xref: /netbsd-src/games/cgram/cgram.c (revision e6c7e151de239c49d2e38720a061ed9d1fa99309)
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 used[26];
124    for (int i=0; i<26; i++) used[i] = 0;
125 
126    int key[26];
127    int keypos=0;
128    while (keypos < 26) {
129       int c = random()%26;
130       if (used[c]) continue;
131       key[keypos++] = c;
132       used[c] = 1;
133    }
134 
135    for (int y=0; y<lines.num; y++) {
136       for (unsigned x=0; lines.v[y][x]; x++) {
137 	 if (islower((unsigned char)lines.v[y][x])) {
138 	    int q = lines.v[y][x]-'a';
139 	    lines.v[y][x] = 'a'+key[q];
140 	 }
141 	 if (isupper((unsigned char)lines.v[y][x])) {
142 	    int q = lines.v[y][x]-'A';
143 	    lines.v[y][x] = 'A'+key[q];
144 	 }
145       }
146    }
147 }
148 
149 static int substitute(int ch) {
150    assert(cury>=0 && cury<lines.num);
151    if (curx >= strlen(lines.v[cury])) {
152       beep();
153       return -1;
154    }
155 
156    int och = lines.v[cury][curx];
157    if (!isalpha((unsigned char)och)) {
158       beep();
159       return -1;
160    }
161 
162    int loch = tolower((unsigned char)och);
163    int uoch = toupper((unsigned char)och);
164    int lch = tolower((unsigned char)ch);
165    int uch = toupper((unsigned char)ch);
166 
167    for (int y=0; y<lines.num; y++) {
168       for (unsigned x=0; lines.v[y][x]; x++) {
169 	 if (lines.v[y][x]==loch) {
170 	    lines.v[y][x] = lch;
171 	 }
172 	 else if (lines.v[y][x]==uoch) {
173 	    lines.v[y][x] = uch;
174 	 }
175 	 else if (lines.v[y][x]==lch) {
176 	    lines.v[y][x] = loch;
177 	 }
178 	 else if (lines.v[y][x]==uch) {
179 	    lines.v[y][x] = uoch;
180 	 }
181       }
182    }
183    return 0;
184 }
185 
186 ////////////////////////////////////////////////////////////
187 
188 static void redraw(void) {
189    erase();
190    bool won = true;
191    for (int i=0; i<LINES-1; i++) {
192       move(i, 0);
193       int ln = i+scrolldown;
194       if (ln < lines.num) {
195 	 for (unsigned j=0; lines.v[i][j]; j++) {
196 	    int ch = lines.v[i][j];
197 	    if (ch != sollines.v[i][j] && isalpha((unsigned char)ch)) {
198 	       won = false;
199 	    }
200 	    bool bold=false;
201 	    if (hinting && ch==sollines.v[i][j] &&
202 		isalpha((unsigned char)ch)) {
203 	       bold = true;
204 	       attron(A_BOLD);
205 	    }
206 	    addch(lines.v[i][j]);
207 	    if (bold) {
208 	       attroff(A_BOLD);
209 	    }
210 	 }
211       }
212       clrtoeol();
213    }
214 
215    move(LINES-1, 0);
216    if (won) {
217       addstr("*solved* ");
218    }
219    addstr("~ to quit, * to cheat, ^pnfb to move");
220 
221    move(LINES-1, 0);
222 
223    move(cury-scrolldown, curx);
224 
225    refresh();
226 }
227 
228 static void opencurses(void) {
229     initscr();
230     cbreak();
231     noecho();
232 }
233 
234 static void closecurses(void) {
235    endwin();
236 }
237 
238 ////////////////////////////////////////////////////////////
239 
240 static void loop(void) {
241    bool done=false;
242    while (!done) {
243       redraw();
244       int ch = getch();
245       switch (ch) {
246        case 1: /* ^A */
247 	curx=0;
248 	break;
249        case 2: /* ^B */
250 	if (curx > 0) {
251 	   curx--;
252 	}
253 	else if (cury > 0) {
254 	   cury--;
255 	   curx = strlen(lines.v[cury]);
256 	}
257 	break;
258        case 5: /* ^E */
259 	curx = strlen(lines.v[cury]);
260 	break;
261        case 6: /* ^F */
262 	if (curx < strlen(lines.v[cury])) {
263 	   curx++;
264 	}
265 	else if (cury < lines.num - 1) {
266 	   cury++;
267 	   curx = 0;
268 	}
269 	break;
270        case 12: /* ^L */
271 	clear();
272 	break;
273        case 14: /* ^N */
274 	if (cury < lines.num-1) {
275 	   cury++;
276 	}
277 	if (curx > strlen(lines.v[cury])) {
278 	   curx =  strlen(lines.v[cury]);
279 	}
280 	if (scrolldown < cury - (LINES-2)) {
281 	   scrolldown = cury - (LINES-2);
282 	}
283 	break;
284        case 16: /* ^P */
285 	if (cury > 0) {
286 	   cury--;
287 	}
288 	if (curx > strlen(lines.v[cury])) {
289 	   curx = strlen(lines.v[cury]);
290 	}
291 	if (scrolldown > cury) {
292 	   scrolldown = cury;
293 	}
294 	break;
295        case '*':
296 	hinting = !hinting;
297 	break;
298        case '~':
299 	done = true;
300 	break;
301        default:
302 	if (isalpha(ch)) {
303 	   if (!substitute(ch)) {
304 	      if (curx < strlen(lines.v[cury])) {
305 		 curx++;
306 	      }
307 	      if (curx==strlen(lines.v[cury]) && cury < lines.num-1) {
308 		 curx=0;
309 		 cury++;
310 	      }
311 	   }
312 	}
313 	else if (curx < strlen(lines.v[cury]) && ch==lines.v[cury][curx]) {
314 	   curx++;
315 	   if (curx==strlen(lines.v[cury]) && cury < lines.num-1) {
316 	      curx=0;
317 	      cury++;
318 	   }
319 	}
320 	else {
321 	   beep();
322 	}
323 	break;
324       }
325    }
326 }
327 
328 ////////////////////////////////////////////////////////////
329 
330 int main(void) {
331    stringarray_init(&lines);
332    stringarray_init(&sollines);
333    srandom(time(NULL));
334    readquote();
335    encode();
336    opencurses();
337 
338    loop();
339 
340    closecurses();
341    stringarray_cleanup(&sollines);
342    stringarray_cleanup(&lines);
343    return 0;
344 }
345