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