]> git.cameronkatri.com Git - bsdgames-darwin.git/blob - cgram/cgram.c
- avoid multipling a boolean value, use &&.
[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 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 }