]> git.cameronkatri.com Git - bsdgames-darwin.git/blob - cgram/cgram.c
cgram: WARNS=6, use int for all coordinates
[bsdgames-darwin.git] / cgram / cgram.c
1 /* $NetBSD: cgram.c,v 1.9 2021/02/21 17:16:00 rillig Exp $ */
2
3 /*-
4 * Copyright (c) 2013 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by David A. Holland.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <assert.h>
33 #include <ctype.h>
34 #include <curses.h>
35 #include <err.h>
36 #include <stdbool.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <time.h>
41
42 #include "pathnames.h"
43
44 ////////////////////////////////////////////////////////////
45
46 static char *
47 xstrdup(const char *s)
48 {
49 char *ret;
50
51 ret = malloc(strlen(s) + 1);
52 if (ret == NULL) {
53 errx(1, "Out of memory");
54 }
55 strcpy(ret, s);
56 return ret;
57 }
58
59 static char
60 ch_toupper(char ch)
61 {
62 return (char)toupper((unsigned char)ch);
63 }
64
65 static char
66 ch_tolower(char ch)
67 {
68 return (char)tolower((unsigned char)ch);
69 }
70
71 static bool
72 ch_isalpha(char ch)
73 {
74 return isalpha((unsigned char)ch);
75 }
76
77 static bool
78 ch_islower(char ch)
79 {
80 return islower((unsigned char)ch);
81 }
82
83 static bool
84 ch_isupper(char ch)
85 {
86 return isupper((unsigned char)ch);
87 }
88
89 ////////////////////////////////////////////////////////////
90
91 struct stringarray {
92 char **v;
93 size_t num;
94 };
95
96 static void
97 stringarray_init(struct stringarray *a)
98 {
99 a->v = NULL;
100 a->num = 0;
101 }
102
103 static void
104 stringarray_cleanup(struct stringarray *a)
105 {
106 free(a->v);
107 }
108
109 static void
110 stringarray_add(struct stringarray *a, const char *s)
111 {
112 a->v = realloc(a->v, (a->num + 1) * sizeof(a->v[0]));
113 if (a->v == NULL) {
114 errx(1, "Out of memory");
115 }
116 a->v[a->num] = xstrdup(s);
117 a->num++;
118 }
119
120 ////////////////////////////////////////////////////////////
121
122 static struct stringarray lines;
123 static struct stringarray sollines;
124 static bool hinting;
125 static int scrolldown;
126 static int curx;
127 static int cury;
128
129 static void
130 readquote(void)
131 {
132 FILE *f = popen(_PATH_FORTUNE, "r");
133 if (f == NULL) {
134 err(1, "%s", _PATH_FORTUNE);
135 }
136
137 char buf[128], buf2[8 * sizeof(buf)];
138 while (fgets(buf, sizeof buf, f) != NULL) {
139 char *s = strrchr(buf, '\n');
140 assert(s != NULL);
141 assert(strlen(s) == 1);
142 *s = '\0';
143
144 int i, j;
145 for (i = j = 0; buf[i] != '\0'; i++) {
146 if (buf[i] == '\t') {
147 buf2[j++] = ' ';
148 while (j % 8 != 0)
149 buf2[j++] = ' ';
150 } else if (buf[i] == '\b') {
151 if (j > 0)
152 j--;
153 } else {
154 buf2[j++] = buf[i];
155 }
156 }
157 buf2[j] = '\0';
158
159 stringarray_add(&lines, buf2);
160 stringarray_add(&sollines, buf2);
161 }
162
163 pclose(f);
164 }
165
166 static void
167 encode(void)
168 {
169 int key[26];
170
171 for (int i = 0; i < 26; i++)
172 key[i] = i;
173
174 for (int i = 26; i > 1; i--) {
175 int c = (int)(random() % i);
176 int t = key[i - 1];
177 key[i - 1] = key[c];
178 key[c] = t;
179 }
180
181 for (int y = 0; y < (int)lines.num; y++) {
182 for (char *p = lines.v[y]; *p != '\0'; p++) {
183 if (ch_islower(*p))
184 *p = (char)('a' + key[*p - 'a']);
185 if (ch_isupper(*p))
186 *p = (char)('A' + key[*p - 'A']);
187 }
188 }
189 }
190
191 static bool
192 substitute(char ch)
193 {
194 assert(cury >= 0 && cury < (int)lines.num);
195 if (curx >= (int)strlen(lines.v[cury])) {
196 beep();
197 return false;
198 }
199
200 char och = lines.v[cury][curx];
201 if (!ch_isalpha(och)) {
202 beep();
203 return false;
204 }
205
206 char loch = ch_tolower(och);
207 char uoch = ch_toupper(och);
208 char lch = ch_tolower(ch);
209 char uch = ch_toupper(ch);
210
211 for (int y = 0; y < (int)lines.num; y++) {
212 for (char *p = lines.v[y]; *p != '\0'; p++) {
213 if (*p == loch)
214 *p = lch;
215 else if (*p == uoch)
216 *p = uch;
217 else if (*p == lch)
218 *p = loch;
219 else if (*p == uch)
220 *p = uoch;
221 }
222 }
223 return true;
224 }
225
226 ////////////////////////////////////////////////////////////
227
228 static void
229 redraw(void)
230 {
231 erase();
232 bool won = true;
233 for (int i = 0; i < LINES - 1; i++) {
234 move(i, 0);
235 int ln = i + scrolldown;
236 if (ln < (int)lines.num) {
237 for (unsigned j = 0; lines.v[i][j] != '\0'; j++) {
238 char ch = lines.v[i][j];
239 if (ch != sollines.v[i][j] && ch_isalpha(ch)) {
240 won = false;
241 }
242 bool bold = false;
243 if (hinting && ch == sollines.v[i][j] &&
244 ch_isalpha(ch)) {
245 bold = true;
246 attron(A_BOLD);
247 }
248 addch(lines.v[i][j]);
249 if (bold) {
250 attroff(A_BOLD);
251 }
252 }
253 }
254 clrtoeol();
255 }
256
257 move(LINES - 1, 0);
258 if (won) {
259 addstr("*solved* ");
260 }
261 addstr("~ to quit, * to cheat, ^pnfb to move");
262
263 move(LINES - 1, 0);
264
265 move(cury - scrolldown, curx);
266
267 refresh();
268 }
269
270 static void
271 opencurses(void)
272 {
273 initscr();
274 cbreak();
275 noecho();
276 }
277
278 static void
279 closecurses(void)
280 {
281 endwin();
282 }
283
284 ////////////////////////////////////////////////////////////
285
286 static void
287 loop(void)
288 {
289 bool done = false;
290 while (!done) {
291 redraw();
292 int ch = getch();
293 switch (ch) {
294 case 1: /* ^A */
295 case KEY_HOME:
296 curx = 0;
297 break;
298 case 2: /* ^B */
299 case KEY_LEFT:
300 if (curx > 0) {
301 curx--;
302 } else if (cury > 0) {
303 cury--;
304 curx = (int)strlen(lines.v[cury]);
305 }
306 break;
307 case 5: /* ^E */
308 case KEY_END:
309 curx = (int)strlen(lines.v[cury]);
310 break;
311 case 6: /* ^F */
312 case KEY_RIGHT:
313 if (curx < (int)strlen(lines.v[cury])) {
314 curx++;
315 } else if (cury < (int)lines.num - 1) {
316 cury++;
317 curx = 0;
318 }
319 break;
320 case 12: /* ^L */
321 clear();
322 break;
323 case 14: /* ^N */
324 case KEY_DOWN:
325 if (cury < (int)lines.num - 1) {
326 cury++;
327 }
328 if (curx > (int)strlen(lines.v[cury])) {
329 curx = (int)strlen(lines.v[cury]);
330 }
331 if (scrolldown < cury - (LINES - 2)) {
332 scrolldown = cury - (LINES - 2);
333 }
334 break;
335 case 16: /* ^P */
336 case KEY_UP:
337 if (cury > 0) {
338 cury--;
339 }
340 if (curx > (int)strlen(lines.v[cury])) {
341 curx = (int)strlen(lines.v[cury]);
342 }
343 if (scrolldown > cury) {
344 scrolldown = cury;
345 }
346 break;
347 case '*':
348 hinting = !hinting;
349 break;
350 case '~':
351 done = true;
352 break;
353 default:
354 if (isascii(ch) && ch_isalpha((char)ch)) {
355 if (substitute((char)ch)) {
356 if (curx < (int)strlen(lines.v[cury])) {
357 curx++;
358 }
359 if (curx == (int)strlen(lines.v[cury]) &&
360 cury < (int)lines.num - 1) {
361 curx = 0;
362 cury++;
363 }
364 }
365 } else if (curx < (int)strlen(lines.v[cury]) &&
366 ch == lines.v[cury][curx]) {
367 curx++;
368 if (curx == (int)strlen(lines.v[cury]) &&
369 cury < (int)lines.num - 1) {
370 curx = 0;
371 cury++;
372 }
373 } else {
374 beep();
375 }
376 break;
377 }
378 }
379 }
380
381 ////////////////////////////////////////////////////////////
382
383 int
384 main(void)
385 {
386
387 stringarray_init(&lines);
388 stringarray_init(&sollines);
389 srandom((unsigned int)time(NULL));
390 readquote();
391 encode();
392 opencurses();
393
394 keypad(stdscr, true);
395 loop();
396
397 closecurses();
398 stringarray_cleanup(&sollines);
399 stringarray_cleanup(&lines);
400 }