cgram.c revision 1.1
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 42static 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 55struct stringarray { 56 char **v; 57 int num; 58}; 59 60static void stringarray_init(struct stringarray *a) { 61 a->v = NULL; 62 a->num = 0; 63} 64 65static void stringarray_cleanup(struct stringarray *a) { 66 free(a->v); 67} 68 69static 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 80static struct stringarray lines; 81static struct stringarray sollines; 82static bool hinting; 83static int scrolldown; 84static unsigned curx; 85static int cury; 86 87static 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 122static 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 149static 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 188static 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 228static void opencurses(void) { 229 initscr(); 230 cbreak(); 231 noecho(); 232} 233 234static void closecurses(void) { 235 endwin(); 236} 237 238//////////////////////////////////////////////////////////// 239 240static 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 330int 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