xref: /dragonfly/games/cgram/cgram.c (revision 564d7699)
1*564d7699SAaron LI /* $NetBSD: cgram.c,v 1.11 2021/02/21 22:21:56 rillig Exp $ */
2*564d7699SAaron LI 
31efcf41bSAaron LI /*-
4*564d7699SAaron LI  * Copyright (c) 2013, 2021 The NetBSD Foundation, Inc.
51efcf41bSAaron LI  * All rights reserved.
61efcf41bSAaron LI  *
71efcf41bSAaron LI  * This code is derived from software contributed to The NetBSD Foundation
8*564d7699SAaron LI  * by David A. Holland and Roland Illig.
91efcf41bSAaron LI  *
101efcf41bSAaron LI  * Redistribution and use in source and binary forms, with or without
111efcf41bSAaron LI  * modification, are permitted provided that the following conditions
121efcf41bSAaron LI  * are met:
131efcf41bSAaron LI  * 1. Redistributions of source code must retain the above copyright
141efcf41bSAaron LI  *    notice, this list of conditions and the following disclaimer.
151efcf41bSAaron LI  * 2. Redistributions in binary form must reproduce the above copyright
161efcf41bSAaron LI  *    notice, this list of conditions and the following disclaimer in the
171efcf41bSAaron LI  *    documentation and/or other materials provided with the distribution.
181efcf41bSAaron LI  *
191efcf41bSAaron LI  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
201efcf41bSAaron LI  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
211efcf41bSAaron LI  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
221efcf41bSAaron LI  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
231efcf41bSAaron LI  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
241efcf41bSAaron LI  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
251efcf41bSAaron LI  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
261efcf41bSAaron LI  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
271efcf41bSAaron LI  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
281efcf41bSAaron LI  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
291efcf41bSAaron LI  * POSSIBILITY OF SUCH DAMAGE.
301efcf41bSAaron LI  */
311efcf41bSAaron LI 
321efcf41bSAaron LI #include <assert.h>
33a22b106bSAaron LI #include <ctype.h>
341efcf41bSAaron LI #include <curses.h>
35*564d7699SAaron LI #include <err.h>
36*564d7699SAaron LI #include <stdbool.h>
37a22b106bSAaron LI #include <stdio.h>
38a22b106bSAaron LI #include <stdlib.h>
39a22b106bSAaron LI #include <string.h>
40a22b106bSAaron LI #include <time.h>
41a22b106bSAaron LI 
421efcf41bSAaron LI #include "pathnames.h"
431efcf41bSAaron LI 
441efcf41bSAaron LI ////////////////////////////////////////////////////////////
451efcf41bSAaron LI 
46*564d7699SAaron LI static char
47*564d7699SAaron LI ch_toupper(char ch)
48a22b106bSAaron LI {
49*564d7699SAaron LI 	return (char)toupper((unsigned char)ch);
50*564d7699SAaron LI }
511efcf41bSAaron LI 
52*564d7699SAaron LI static char
53*564d7699SAaron LI ch_tolower(char ch)
54*564d7699SAaron LI {
55*564d7699SAaron LI 	return (char)tolower((unsigned char)ch);
56*564d7699SAaron LI }
57a22b106bSAaron LI 
58*564d7699SAaron LI static bool
59*564d7699SAaron LI ch_isalpha(char ch)
60*564d7699SAaron LI {
61*564d7699SAaron LI 	return isalpha((unsigned char)ch) != 0;
62*564d7699SAaron LI }
63*564d7699SAaron LI 
64*564d7699SAaron LI static bool
65*564d7699SAaron LI ch_islower(char ch)
66*564d7699SAaron LI {
67*564d7699SAaron LI 	return islower((unsigned char)ch) != 0;
68*564d7699SAaron LI }
69*564d7699SAaron LI 
70*564d7699SAaron LI static bool
71*564d7699SAaron LI ch_isupper(char ch)
72*564d7699SAaron LI {
73*564d7699SAaron LI 	return isupper((unsigned char)ch) != 0;
74*564d7699SAaron LI }
75*564d7699SAaron LI 
76*564d7699SAaron LI static int
77*564d7699SAaron LI imax(int a, int b)
78*564d7699SAaron LI {
79*564d7699SAaron LI 	return a > b ? a : b;
80*564d7699SAaron LI }
81*564d7699SAaron LI 
82*564d7699SAaron LI static int
83*564d7699SAaron LI imin(int a, int b)
84*564d7699SAaron LI {
85*564d7699SAaron LI 	return a < b ? a : b;
861efcf41bSAaron LI }
871efcf41bSAaron LI 
881efcf41bSAaron LI ////////////////////////////////////////////////////////////
891efcf41bSAaron LI 
90*564d7699SAaron LI struct string {
91*564d7699SAaron LI 	char *s;
92*564d7699SAaron LI 	size_t len;
93*564d7699SAaron LI 	size_t cap;
941efcf41bSAaron LI };
951efcf41bSAaron LI 
96*564d7699SAaron LI struct stringarray {
97*564d7699SAaron LI 	struct string *v;
98*564d7699SAaron LI 	size_t num;
99*564d7699SAaron LI };
100*564d7699SAaron LI 
101*564d7699SAaron LI static void
102*564d7699SAaron LI string_init(struct string *s)
103*564d7699SAaron LI {
104*564d7699SAaron LI 	s->s = NULL;
105*564d7699SAaron LI 	s->len = 0;
106*564d7699SAaron LI 	s->cap = 0;
107*564d7699SAaron LI }
108*564d7699SAaron LI 
109*564d7699SAaron LI static void
110*564d7699SAaron LI string_add(struct string *s, char ch)
111*564d7699SAaron LI {
112*564d7699SAaron LI 	if (s->len >= s->cap) {
113*564d7699SAaron LI 		s->cap = 2 * s->cap + 16;
114*564d7699SAaron LI 		s->s = realloc(s->s, s->cap);
115*564d7699SAaron LI 		if (s->s == NULL)
116*564d7699SAaron LI 			errx(1, "Out of memory");
117*564d7699SAaron LI 	}
118*564d7699SAaron LI 	s->s[s->len++] = ch;
119*564d7699SAaron LI }
120*564d7699SAaron LI 
121*564d7699SAaron LI static void
122*564d7699SAaron LI string_finish(struct string *s)
123*564d7699SAaron LI {
124*564d7699SAaron LI 	string_add(s, '\0');
125*564d7699SAaron LI 	s->len--;
126*564d7699SAaron LI }
127*564d7699SAaron LI 
128a22b106bSAaron LI static void
129a22b106bSAaron LI stringarray_init(struct stringarray *a)
130a22b106bSAaron LI {
1311efcf41bSAaron LI 	a->v = NULL;
1321efcf41bSAaron LI 	a->num = 0;
1331efcf41bSAaron LI }
1341efcf41bSAaron LI 
135a22b106bSAaron LI static void
136a22b106bSAaron LI stringarray_cleanup(struct stringarray *a)
137a22b106bSAaron LI {
138*564d7699SAaron LI 	for (size_t i = 0; i < a->num; i++)
139*564d7699SAaron LI 		free(a->v[i].s);
1401efcf41bSAaron LI 	free(a->v);
1411efcf41bSAaron LI }
1421efcf41bSAaron LI 
143a22b106bSAaron LI static void
144*564d7699SAaron LI stringarray_add(struct stringarray *a, struct string *s)
145a22b106bSAaron LI {
146*564d7699SAaron LI 	size_t num = a->num++;
147*564d7699SAaron LI 	a->v = realloc(a->v, a->num * sizeof a->v[0]);
148a22b106bSAaron LI 	if (a->v == NULL)
1491efcf41bSAaron LI 		errx(1, "Out of memory");
150*564d7699SAaron LI 	a->v[num] = *s;
151*564d7699SAaron LI }
152a22b106bSAaron LI 
153*564d7699SAaron LI static void
154*564d7699SAaron LI stringarray_dup(struct stringarray *dst, const struct stringarray *src)
155*564d7699SAaron LI {
156*564d7699SAaron LI 	assert(dst->num == 0);
157*564d7699SAaron LI 	for (size_t i = 0; i < src->num; i++) {
158*564d7699SAaron LI 		struct string str;
159*564d7699SAaron LI 		string_init(&str);
160*564d7699SAaron LI 		for (const char *p = src->v[i].s; *p != '\0'; p++)
161*564d7699SAaron LI 			string_add(&str, *p);
162*564d7699SAaron LI 		string_finish(&str);
163*564d7699SAaron LI 		stringarray_add(dst, &str);
164*564d7699SAaron LI 	}
1651efcf41bSAaron LI }
1661efcf41bSAaron LI 
1671efcf41bSAaron LI ////////////////////////////////////////////////////////////
1681efcf41bSAaron LI 
1691efcf41bSAaron LI static struct stringarray lines;
1701efcf41bSAaron LI static struct stringarray sollines;
1711efcf41bSAaron LI static bool hinting;
172*564d7699SAaron LI static int extent_x;
173*564d7699SAaron LI static int extent_y;
174*564d7699SAaron LI static int offset_x;
175*564d7699SAaron LI static int offset_y;
176*564d7699SAaron LI static int cursor_x;
177*564d7699SAaron LI static int cursor_y;
178*564d7699SAaron LI 
179*564d7699SAaron LI static int
180*564d7699SAaron LI cur_max_x(void)
181*564d7699SAaron LI {
182*564d7699SAaron LI 	return (int)lines.v[cursor_y].len;
183*564d7699SAaron LI }
184*564d7699SAaron LI 
185*564d7699SAaron LI static int
186*564d7699SAaron LI cur_max_y(void)
187*564d7699SAaron LI {
188*564d7699SAaron LI 	return extent_y - 1;
189*564d7699SAaron LI }
1901efcf41bSAaron LI 
191a22b106bSAaron LI static void
192a22b106bSAaron LI readquote(void)
193a22b106bSAaron LI {
1941efcf41bSAaron LI 	FILE *f = popen(_PATH_FORTUNE, "r");
195*564d7699SAaron LI 	if (f == NULL)
1961efcf41bSAaron LI 		err(1, "%s", _PATH_FORTUNE);
1971efcf41bSAaron LI 
198*564d7699SAaron LI 	struct string line;
199*564d7699SAaron LI 	string_init(&line);
2001efcf41bSAaron LI 
201*564d7699SAaron LI 	int ch;
202*564d7699SAaron LI 	while ((ch = fgetc(f)) != EOF) {
203*564d7699SAaron LI 		if (ch == '\n') {
204*564d7699SAaron LI 			string_finish(&line);
205*564d7699SAaron LI 			stringarray_add(&lines, &line);
206*564d7699SAaron LI 			string_init(&line);
207*564d7699SAaron LI 		} else if (ch == '\t') {
208*564d7699SAaron LI 			string_add(&line, ' ');
209*564d7699SAaron LI 			while (line.len % 8 != 0)
210*564d7699SAaron LI 				string_add(&line, ' ');
211*564d7699SAaron LI 		} else if (ch == '\b') {
212*564d7699SAaron LI 			if (line.len > 0)
213*564d7699SAaron LI 				line.len--;
214a22b106bSAaron LI 		} else {
215*564d7699SAaron LI 			string_add(&line, (char)ch);
2161efcf41bSAaron LI 		}
2171efcf41bSAaron LI 	}
2181efcf41bSAaron LI 
219*564d7699SAaron LI 	stringarray_dup(&sollines, &lines);
220*564d7699SAaron LI 
221*564d7699SAaron LI 	extent_y = (int)lines.num;
222*564d7699SAaron LI 	for (int i = 0; i < extent_y; i++)
223*564d7699SAaron LI 		extent_x = imax(extent_x, (int)lines.v[i].len);
2241efcf41bSAaron LI 
2251efcf41bSAaron LI 	pclose(f);
2261efcf41bSAaron LI }
2271efcf41bSAaron LI 
228a22b106bSAaron LI static void
229a22b106bSAaron LI encode(void)
230a22b106bSAaron LI {
2311efcf41bSAaron LI 	int key[26];
232a22b106bSAaron LI 
233a22b106bSAaron LI 	for (int i = 0; i < 26; i++)
234a22b106bSAaron LI 		key[i] = i;
235*564d7699SAaron LI 
2361efcf41bSAaron LI 	for (int i = 26; i > 1; i--) {
237*564d7699SAaron LI 		int c = (int)(random() % i);
2381efcf41bSAaron LI 		int t = key[i - 1];
2391efcf41bSAaron LI 		key[i - 1] = key[c];
2401efcf41bSAaron LI 		key[c] = t;
2411efcf41bSAaron LI 	}
2421efcf41bSAaron LI 
243*564d7699SAaron LI 	for (int y = 0; y < extent_y; y++) {
244*564d7699SAaron LI 		for (char *p = lines.v[y].s; *p != '\0'; p++) {
245*564d7699SAaron LI 			if (ch_islower(*p))
246*564d7699SAaron LI 				*p = (char)('a' + key[*p - 'a']);
247*564d7699SAaron LI 			if (ch_isupper(*p))
248*564d7699SAaron LI 				*p = (char)('A' + key[*p - 'A']);
2491efcf41bSAaron LI 		}
2501efcf41bSAaron LI 	}
2511efcf41bSAaron LI }
2521efcf41bSAaron LI 
253*564d7699SAaron LI static bool
254*564d7699SAaron LI substitute(char ch)
255a22b106bSAaron LI {
256*564d7699SAaron LI 	assert(cursor_x >= 0 && cursor_x < extent_x);
257*564d7699SAaron LI 	assert(cursor_y >= 0 && cursor_y < extent_y);
258*564d7699SAaron LI 	if (cursor_x >= cur_max_x()) {
2591efcf41bSAaron LI 		beep();
260*564d7699SAaron LI 		return false;
2611efcf41bSAaron LI 	}
2621efcf41bSAaron LI 
263*564d7699SAaron LI 	char och = lines.v[cursor_y].s[cursor_x];
264*564d7699SAaron LI 	if (!ch_isalpha(och)) {
2651efcf41bSAaron LI 		beep();
266*564d7699SAaron LI 		return false;
2671efcf41bSAaron LI 	}
2681efcf41bSAaron LI 
269*564d7699SAaron LI 	char loch = ch_tolower(och);
270*564d7699SAaron LI 	char uoch = ch_toupper(och);
271*564d7699SAaron LI 	char lch = ch_tolower(ch);
272*564d7699SAaron LI 	char uch = ch_toupper(ch);
2731efcf41bSAaron LI 
274*564d7699SAaron LI 	for (int y = 0; y < (int)lines.num; y++) {
275*564d7699SAaron LI 		for (char *p = lines.v[y].s; *p != '\0'; p++) {
276*564d7699SAaron LI 			if (*p == loch)
277*564d7699SAaron LI 				*p = lch;
278*564d7699SAaron LI 			else if (*p == uoch)
279*564d7699SAaron LI 				*p = uch;
280*564d7699SAaron LI 			else if (*p == lch)
281*564d7699SAaron LI 				*p = loch;
282*564d7699SAaron LI 			else if (*p == uch)
283*564d7699SAaron LI 				*p = uoch;
2841efcf41bSAaron LI 		}
2851efcf41bSAaron LI 	}
286*564d7699SAaron LI 	return true;
2871efcf41bSAaron LI }
2881efcf41bSAaron LI 
2891efcf41bSAaron LI ////////////////////////////////////////////////////////////
2901efcf41bSAaron LI 
291*564d7699SAaron LI static bool
292*564d7699SAaron LI is_solved(void)
293*564d7699SAaron LI {
294*564d7699SAaron LI 	for (size_t i = 0; i < lines.num; i++)
295*564d7699SAaron LI 		if (strcmp(lines.v[i].s, sollines.v[i].s) != 0)
296*564d7699SAaron LI 			return false;
297*564d7699SAaron LI 	return true;
298*564d7699SAaron LI }
299*564d7699SAaron LI 
300a22b106bSAaron LI static void
301a22b106bSAaron LI redraw(void)
302a22b106bSAaron LI {
3031efcf41bSAaron LI 	erase();
304a22b106bSAaron LI 
305*564d7699SAaron LI 	int max_y = imin(LINES - 1, extent_y - offset_y);
306*564d7699SAaron LI 	for (int y = 0; y < max_y; y++) {
307*564d7699SAaron LI 		move(y, 0);
308d60ecdb4SAaron LI 
309*564d7699SAaron LI 		int len = (int)lines.v[offset_y + y].len;
310*564d7699SAaron LI 		int max_x = imin(COLS - 1, len - offset_x);
311*564d7699SAaron LI 		const char *line = lines.v[offset_y + y].s;
312*564d7699SAaron LI 		const char *solline = sollines.v[offset_y + y].s;
313d60ecdb4SAaron LI 
314*564d7699SAaron LI 		for (int x = 0; x < max_x; x++) {
315*564d7699SAaron LI 			char ch = line[offset_x + x];
316*564d7699SAaron LI 			bool bold = hinting &&
317*564d7699SAaron LI 			    ch == solline[offset_x + x] &&
318*564d7699SAaron LI 			    ch_isalpha(ch);
319*564d7699SAaron LI 
320*564d7699SAaron LI 			if (bold)
321*564d7699SAaron LI 				attron(A_BOLD);
322*564d7699SAaron LI 			addch(ch);
323*564d7699SAaron LI 			if (bold)
324*564d7699SAaron LI 				attroff(A_BOLD);
3251efcf41bSAaron LI 		}
3261efcf41bSAaron LI 		clrtoeol();
3271efcf41bSAaron LI 	}
3281efcf41bSAaron LI 
3291efcf41bSAaron LI 	move(LINES - 1, 0);
330*564d7699SAaron LI 	if (is_solved())
3311efcf41bSAaron LI 		addstr("*solved* ");
3321efcf41bSAaron LI 	addstr("~ to quit, * to cheat, ^pnfb to move");
3331efcf41bSAaron LI 
334*564d7699SAaron LI 	move(cursor_y - offset_y, cursor_x - offset_x);
3351efcf41bSAaron LI 
3361efcf41bSAaron LI 	refresh();
3371efcf41bSAaron LI }
3381efcf41bSAaron LI 
339a22b106bSAaron LI static void
340a22b106bSAaron LI opencurses(void)
341a22b106bSAaron LI {
3421efcf41bSAaron LI 	initscr();
3431efcf41bSAaron LI 	cbreak();
3441efcf41bSAaron LI 	noecho();
345*564d7699SAaron LI 	keypad(stdscr, true);
3461efcf41bSAaron LI }
3471efcf41bSAaron LI 
348a22b106bSAaron LI static void
349a22b106bSAaron LI closecurses(void)
350a22b106bSAaron LI {
3511efcf41bSAaron LI 	endwin();
3521efcf41bSAaron LI }
3531efcf41bSAaron LI 
3541efcf41bSAaron LI ////////////////////////////////////////////////////////////
3551efcf41bSAaron LI 
356a22b106bSAaron LI static void
357*564d7699SAaron LI saturate_cursor(void)
358a22b106bSAaron LI {
359*564d7699SAaron LI 	cursor_y = imax(cursor_y, 0);
360*564d7699SAaron LI 	cursor_y = imin(cursor_y, cur_max_y());
361a22b106bSAaron LI 
362*564d7699SAaron LI 	assert(cursor_x >= 0);
363*564d7699SAaron LI 	cursor_x = imin(cursor_x, cur_max_x());
364*564d7699SAaron LI }
365a22b106bSAaron LI 
366*564d7699SAaron LI static void
367*564d7699SAaron LI scroll_into_view(void)
368*564d7699SAaron LI {
369*564d7699SAaron LI 	if (cursor_x < offset_x)
370*564d7699SAaron LI 		offset_x = cursor_x;
371*564d7699SAaron LI 	if (cursor_x > offset_x + COLS - 1)
372*564d7699SAaron LI 		offset_x = cursor_x - (COLS - 1);
373*564d7699SAaron LI 
374*564d7699SAaron LI 	if (cursor_y < offset_y)
375*564d7699SAaron LI 		offset_y = cursor_y;
376*564d7699SAaron LI 	if (cursor_y > offset_y + LINES - 2)
377*564d7699SAaron LI 		offset_y = cursor_y - (LINES - 2);
378*564d7699SAaron LI }
379*564d7699SAaron LI 
380*564d7699SAaron LI static void
381*564d7699SAaron LI handle_char_input(int ch)
382*564d7699SAaron LI {
383*564d7699SAaron LI 	if (isascii(ch) && ch_isalpha((char)ch)) {
384*564d7699SAaron LI 		if (substitute((char)ch)) {
385*564d7699SAaron LI 			if (cursor_x < cur_max_x())
386*564d7699SAaron LI 				cursor_x++;
387*564d7699SAaron LI 			if (cursor_x == cur_max_x() &&
388*564d7699SAaron LI 			    cursor_y < cur_max_y()) {
389*564d7699SAaron LI 				cursor_x = 0;
390*564d7699SAaron LI 				cursor_y++;
391*564d7699SAaron LI 			}
392*564d7699SAaron LI 		}
393*564d7699SAaron LI 	} else if (cursor_x < cur_max_x() &&
394*564d7699SAaron LI 	    ch == lines.v[cursor_y].s[cursor_x]) {
395*564d7699SAaron LI 		cursor_x++;
396*564d7699SAaron LI 		if (cursor_x == cur_max_x() &&
397*564d7699SAaron LI 		    cursor_y < cur_max_y()) {
398*564d7699SAaron LI 			cursor_x = 0;
399*564d7699SAaron LI 			cursor_y++;
400*564d7699SAaron LI 		}
401*564d7699SAaron LI 	} else {
402*564d7699SAaron LI 		beep();
403*564d7699SAaron LI 	}
404*564d7699SAaron LI }
405*564d7699SAaron LI 
406*564d7699SAaron LI static bool
407*564d7699SAaron LI handle_key(void)
408*564d7699SAaron LI {
4091efcf41bSAaron LI 	int ch = getch();
410*564d7699SAaron LI 
4111efcf41bSAaron LI 	switch (ch) {
4121efcf41bSAaron LI 	case 1:			/* ^A */
413359c8e75SAaron LI 	case KEY_HOME:
414*564d7699SAaron LI 		cursor_x = 0;
4151efcf41bSAaron LI 		break;
4161efcf41bSAaron LI 	case 2:			/* ^B */
4171efcf41bSAaron LI 	case KEY_LEFT:
418*564d7699SAaron LI 		if (cursor_x > 0) {
419*564d7699SAaron LI 			cursor_x--;
420*564d7699SAaron LI 		} else if (cursor_y > 0) {
421*564d7699SAaron LI 			cursor_y--;
422*564d7699SAaron LI 			cursor_x = cur_max_x();
4231efcf41bSAaron LI 		}
4241efcf41bSAaron LI 		break;
4251efcf41bSAaron LI 	case 5:			/* ^E */
4261efcf41bSAaron LI 	case KEY_END:
427*564d7699SAaron LI 		cursor_x = cur_max_x();
4281efcf41bSAaron LI 		break;
4291efcf41bSAaron LI 	case 6:			/* ^F */
4301efcf41bSAaron LI 	case KEY_RIGHT:
431*564d7699SAaron LI 		if (cursor_x < cur_max_x()) {
432*564d7699SAaron LI 			cursor_x++;
433*564d7699SAaron LI 		} else if (cursor_y < cur_max_y()) {
434*564d7699SAaron LI 			cursor_y++;
435*564d7699SAaron LI 			cursor_x = 0;
4361efcf41bSAaron LI 		}
4371efcf41bSAaron LI 		break;
4381efcf41bSAaron LI 	case 12:		/* ^L */
4391efcf41bSAaron LI 		clear();
4401efcf41bSAaron LI 		break;
4411efcf41bSAaron LI 	case 14:		/* ^N */
4421efcf41bSAaron LI 	case KEY_DOWN:
443*564d7699SAaron LI 		cursor_y++;
4441efcf41bSAaron LI 		break;
4451efcf41bSAaron LI 	case 16:		/* ^P */
4461efcf41bSAaron LI 	case KEY_UP:
447*564d7699SAaron LI 		cursor_y--;
448*564d7699SAaron LI 		break;
449*564d7699SAaron LI 	case KEY_PPAGE:
450*564d7699SAaron LI 		cursor_y -= LINES - 2;
451*564d7699SAaron LI 		break;
452*564d7699SAaron LI 	case KEY_NPAGE:
453*564d7699SAaron LI 		cursor_y += LINES - 2;
4541efcf41bSAaron LI 		break;
4551efcf41bSAaron LI 	case '*':
4561efcf41bSAaron LI 		hinting = !hinting;
4571efcf41bSAaron LI 		break;
4581efcf41bSAaron LI 	case '~':
459*564d7699SAaron LI 		return false;
4601efcf41bSAaron LI 	default:
461*564d7699SAaron LI 		handle_char_input(ch);
4621efcf41bSAaron LI 		break;
4631efcf41bSAaron LI 	}
464*564d7699SAaron LI 	return true;
4651efcf41bSAaron LI }
466*564d7699SAaron LI 
467*564d7699SAaron LI static void
468*564d7699SAaron LI init(void)
469*564d7699SAaron LI {
470*564d7699SAaron LI 	stringarray_init(&lines);
471*564d7699SAaron LI 	stringarray_init(&sollines);
472*564d7699SAaron LI 	srandom((unsigned int)time(NULL));
473*564d7699SAaron LI 	readquote();
474*564d7699SAaron LI 	encode();
475*564d7699SAaron LI 	opencurses();
476*564d7699SAaron LI }
477*564d7699SAaron LI 
478*564d7699SAaron LI static void
479*564d7699SAaron LI loop(void)
480*564d7699SAaron LI {
481*564d7699SAaron LI 	for (;;) {
482*564d7699SAaron LI 		redraw();
483*564d7699SAaron LI 		if (!handle_key())
484*564d7699SAaron LI 			break;
485*564d7699SAaron LI 		saturate_cursor();
486*564d7699SAaron LI 		scroll_into_view();
487*564d7699SAaron LI 	}
488*564d7699SAaron LI }
489*564d7699SAaron LI 
490*564d7699SAaron LI static void
491*564d7699SAaron LI clean_up(void)
492*564d7699SAaron LI {
493*564d7699SAaron LI 	closecurses();
494*564d7699SAaron LI 	stringarray_cleanup(&sollines);
495*564d7699SAaron LI 	stringarray_cleanup(&lines);
4961efcf41bSAaron LI }
4971efcf41bSAaron LI 
4981efcf41bSAaron LI ////////////////////////////////////////////////////////////
4991efcf41bSAaron LI 
500a22b106bSAaron LI int
501a22b106bSAaron LI main(void)
502a22b106bSAaron LI {
503*564d7699SAaron LI 	init();
5041efcf41bSAaron LI 	loop();
505*564d7699SAaron LI 	clean_up();
5061efcf41bSAaron LI }
507