Thu Jul 2 19:11:01 2020 UTC ()
cgram(6): use standard cursor keys, use standard shuffle algorithm

The previous shuffle algorithm asked for 100 random numbers, on average.
The new algorithm asks exactly for 26 random numbers.

Curses predefines numeric constants for keys, and there is no apparent
reason not to use these standard keys for cursor movement.


(rillig)
diff -r1.1 -r1.2 src/games/cgram/cgram.c

cvs diff -r1.1 -r1.2 src/games/cgram/cgram.c (switch to unified diff)

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