]> git.cameronkatri.com Git - bsdgames-darwin.git/blob - cgram/cgram.c
cgram: adjust style to survive lint's strict bool mode
[bsdgames-darwin.git] / cgram / cgram.c
1 /* $NetBSD */
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 ////////////////////////////////////////////////////////////
60
61 struct stringarray {
62 char **v;
63 int num;
64 };
65
66 static void
67 stringarray_init(struct stringarray *a)
68 {
69 a->v = NULL;
70 a->num = 0;
71 }
72
73 static void
74 stringarray_cleanup(struct stringarray *a)
75 {
76 free(a->v);
77 }
78
79 static void
80 stringarray_add(struct stringarray *a, const char *s)
81 {
82 a->v = realloc(a->v, (a->num + 1) * sizeof(a->v[0]));
83 if (a->v == NULL) {
84 errx(1, "Out of memory");
85 }
86 a->v[a->num] = xstrdup(s);
87 a->num++;
88 }
89
90 ////////////////////////////////////////////////////////////
91
92 static struct stringarray lines;
93 static struct stringarray sollines;
94 static bool hinting;
95 static int scrolldown;
96 static unsigned curx;
97 static int cury;
98
99 static void
100 readquote(void)
101 {
102 FILE *f = popen(_PATH_FORTUNE, "r");
103 if (f == NULL) {
104 err(1, "%s", _PATH_FORTUNE);
105 }
106
107 char buf[128], buf2[8 * sizeof(buf)];
108 while (fgets(buf, sizeof buf, f) != NULL) {
109 char *s = strrchr(buf, '\n');
110 assert(s != NULL);
111 assert(strlen(s) == 1);
112 *s = '\0';
113
114 int i, j;
115 for (i = j = 0; buf[i] != '\0'; i++) {
116 if (buf[i] == '\t') {
117 buf2[j++] = ' ';
118 while (j % 8 != 0)
119 buf2[j++] = ' ';
120 } else if (buf[i] == '\b') {
121 if (j > 0)
122 j--;
123 } else {
124 buf2[j++] = buf[i];
125 }
126 }
127 buf2[j] = '\0';
128
129 stringarray_add(&lines, buf2);
130 stringarray_add(&sollines, buf2);
131 }
132
133 pclose(f);
134 }
135
136 static void
137 encode(void)
138 {
139 int key[26];
140 for (int i = 0; i < 26; i++)
141 key[i] = i;
142 for (int i = 26; i > 1; i--) {
143 int c = random() % i;
144 int t = key[i - 1];
145 key[i - 1] = key[c];
146 key[c] = t;
147 }
148
149 for (int y = 0; y < lines.num; y++) {
150 for (unsigned x = 0; lines.v[y][x] != '\0'; x++) {
151 if (islower((unsigned char)lines.v[y][x])) {
152 int q = lines.v[y][x] - 'a';
153 lines.v[y][x] = 'a' + key[q];
154 }
155 if (isupper((unsigned char)lines.v[y][x])) {
156 int q = lines.v[y][x] - 'A';
157 lines.v[y][x] = 'A' + key[q];
158 }
159 }
160 }
161 }
162
163 static bool
164 substitute(int ch)
165 {
166 assert(cury >= 0 && cury < lines.num);
167 if (curx >= strlen(lines.v[cury])) {
168 beep();
169 return false;
170 }
171
172 int och = lines.v[cury][curx];
173 if (!isalpha((unsigned char)och)) {
174 beep();
175 return false;
176 }
177
178 int loch = tolower((unsigned char)och);
179 int uoch = toupper((unsigned char)och);
180 int lch = tolower((unsigned char)ch);
181 int uch = toupper((unsigned char)ch);
182
183 for (int y = 0; y < lines.num; y++) {
184 for (unsigned x = 0; lines.v[y][x] != '\0'; x++) {
185 if (lines.v[y][x] == loch) {
186 lines.v[y][x] = lch;
187 } else if (lines.v[y][x] == uoch) {
188 lines.v[y][x] = uch;
189 } else if (lines.v[y][x] == lch) {
190 lines.v[y][x] = loch;
191 } else if (lines.v[y][x] == uch) {
192 lines.v[y][x] = uoch;
193 }
194 }
195 }
196 return true;
197 }
198
199 ////////////////////////////////////////////////////////////
200
201 static void
202 redraw(void)
203 {
204 erase();
205 bool won = true;
206 for (int i = 0; i < LINES - 1; i++) {
207 move(i, 0);
208 int ln = i + scrolldown;
209 if (ln < lines.num) {
210 for (unsigned j = 0; lines.v[i][j] != '\0'; j++) {
211 int ch = lines.v[i][j];
212 if (ch != sollines.v[i][j] &&
213 isalpha((unsigned char)ch)) {
214 won = false;
215 }
216 bool bold = false;
217 if (hinting && ch == sollines.v[i][j] &&
218 isalpha((unsigned char)ch)) {
219 bold = true;
220 attron(A_BOLD);
221 }
222 addch(lines.v[i][j]);
223 if (bold) {
224 attroff(A_BOLD);
225 }
226 }
227 }
228 clrtoeol();
229 }
230
231 move(LINES - 1, 0);
232 if (won) {
233 addstr("*solved* ");
234 }
235 addstr("~ to quit, * to cheat, ^pnfb to move");
236
237 move(LINES - 1, 0);
238
239 move(cury - scrolldown, curx);
240
241 refresh();
242 }
243
244 static void
245 opencurses(void)
246 {
247 initscr();
248 cbreak();
249 noecho();
250 }
251
252 static void
253 closecurses(void)
254 {
255 endwin();
256 }
257
258 ////////////////////////////////////////////////////////////
259
260 static void
261 loop(void)
262 {
263 bool done = false;
264 while (!done) {
265 redraw();
266 int ch = getch();
267 switch (ch) {
268 case 1: /* ^A */
269 case KEY_HOME:
270 curx = 0;
271 break;
272 case 2: /* ^B */
273 case KEY_LEFT:
274 if (curx > 0) {
275 curx--;
276 } else if (cury > 0) {
277 cury--;
278 curx = strlen(lines.v[cury]);
279 }
280 break;
281 case 5: /* ^E */
282 case KEY_END:
283 curx = strlen(lines.v[cury]);
284 break;
285 case 6: /* ^F */
286 case KEY_RIGHT:
287 if (curx < strlen(lines.v[cury])) {
288 curx++;
289 } else if (cury < lines.num - 1) {
290 cury++;
291 curx = 0;
292 }
293 break;
294 case 12: /* ^L */
295 clear();
296 break;
297 case 14: /* ^N */
298 case KEY_DOWN:
299 if (cury < lines.num - 1) {
300 cury++;
301 }
302 if (curx > strlen(lines.v[cury])) {
303 curx = strlen(lines.v[cury]);
304 }
305 if (scrolldown < cury - (LINES - 2)) {
306 scrolldown = cury - (LINES - 2);
307 }
308 break;
309 case 16: /* ^P */
310 case KEY_UP:
311 if (cury > 0) {
312 cury--;
313 }
314 if (curx > strlen(lines.v[cury])) {
315 curx = strlen(lines.v[cury]);
316 }
317 if (scrolldown > cury) {
318 scrolldown = cury;
319 }
320 break;
321 case '*':
322 hinting = !hinting;
323 break;
324 case '~':
325 done = true;
326 break;
327 default:
328 if (isalpha(ch)) {
329 if (substitute(ch)) {
330 if (curx < strlen(lines.v[cury])) {
331 curx++;
332 }
333 if (curx == strlen(lines.v[cury]) &&
334 cury < lines.num - 1) {
335 curx = 0;
336 cury++;
337 }
338 }
339 } else if (curx < strlen(lines.v[cury]) &&
340 ch == lines.v[cury][curx]) {
341 curx++;
342 if (curx == strlen(lines.v[cury]) &&
343 cury < lines.num - 1) {
344 curx = 0;
345 cury++;
346 }
347 } else {
348 beep();
349 }
350 break;
351 }
352 }
353 }
354
355 ////////////////////////////////////////////////////////////
356
357 int
358 main(void)
359 {
360
361 stringarray_init(&lines);
362 stringarray_init(&sollines);
363 srandom(time(NULL));
364 readquote();
365 encode();
366 opencurses();
367
368 keypad(stdscr, true);
369 loop();
370
371 closecurses();
372 stringarray_cleanup(&sollines);
373 stringarray_cleanup(&lines);
374 }