xref: /dragonfly/games/cgram/cgram.c (revision 1efcf41b)
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 }
349