summaryrefslogtreecommitdiffstats
path: root/cgram
diff options
context:
space:
mode:
authordholland <dholland@NetBSD.org>2013-08-04 05:42:47 +0000
committerdholland <dholland@NetBSD.org>2013-08-04 05:42:47 +0000
commitedae42d03385ad6cc80974f04021637d74f329af (patch)
treebc1e3c92b9916a0a0a0b078f96684df0266a5f06 /cgram
parent13562cc5cbeb4bdc784aeb88bb957c1764e90f19 (diff)
downloadbsdgames-darwin-edae42d03385ad6cc80974f04021637d74f329af.tar.gz
bsdgames-darwin-edae42d03385ad6cc80974f04021637d74f329af.tar.zst
bsdgames-darwin-edae42d03385ad6cc80974f04021637d74f329af.zip
Add a curses gizmo for solving Sunday-paper-type cryptograms based on
substitution ciphers. It gets the cleartext from fortune. I wrote this some years ago for my own amusement; a couple people have suggested that I should import it. Approved only by groo, so I'm going to wait a couple days to hook it to the build in case anyone demands it be removed again...
Diffstat (limited to 'cgram')
-rw-r--r--cgram/Makefile10
-rw-r--r--cgram/cgram.664
-rw-r--r--cgram/cgram.c344
-rw-r--r--cgram/pathnames.h30
4 files changed, 448 insertions, 0 deletions
diff --git a/cgram/Makefile b/cgram/Makefile
new file mode 100644
index 00000000..70e01d50
--- /dev/null
+++ b/cgram/Makefile
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.1 2013/08/04 05:42:47 dholland Exp $
+
+PROG=cgram
+DPADD=${LIBCURSES} ${LIBTERMINFO}
+LDADD=-lcurses -lterminfo
+SRCS=cgram.c
+MAN=cgram.6
+HIDEGAME=hidegame
+
+.include <bsd.prog.mk>
diff --git a/cgram/cgram.6 b/cgram/cgram.6
new file mode 100644
index 00000000..36178d8d
--- /dev/null
+++ b/cgram/cgram.6
@@ -0,0 +1,64 @@
+.\"
+.\" Copyright (c) 2004, 2013 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by David A. Holland.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd August 3, 2013
+.Dt CGRAM 6
+.Os
+.Sh NAME
+.Nm cgram
+.Nd solve Sunday-paper cryptograms
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+is a curses-based widget for solving Sunday-paper-type cryptograms
+based on substitution ciphers.
+A random cleartext is chosen using
+.Xr fortune 6
+and a random substitution key is generated.
+.Pp
+The ciphertext is displayed.
+Typing a letter changes the key so that the letter under the cursor
+maps to the newly typed letter, and updates the display accordingly.
+Use Emacs-type cursor commands to move around.
+Enter a tilde
+.Pq ~
+to quit.
+Press asterisk
+.Pq *
+to enter an easier mode where correct letters are displayed in
+boldface.
+.Sh SEE ALSO
+.Xr caesar 6
+.Sh HISTORY
+.Nm
+was written circa 2004.
+It was imported into
+.Nx
+in 2013 and first appeared in
+.Nx 7.0 .
diff --git a/cgram/cgram.c b/cgram/cgram.c
new file mode 100644
index 00000000..76ea55fb
--- /dev/null
+++ b/cgram/cgram.c
@@ -0,0 +1,344 @@
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <time.h>
+#include <err.h>
+#include <assert.h>
+#include <curses.h>
+#include "pathnames.h"
+
+////////////////////////////////////////////////////////////
+
+static char *xstrdup(const char *s) {
+ char *ret;
+
+ ret = malloc(strlen(s) + 1);
+ if (ret == NULL) {
+ errx(1, "Out of memory");
+ }
+ strcpy(ret, s);
+ return ret;
+}
+
+////////////////////////////////////////////////////////////
+
+struct stringarray {
+ char **v;
+ int num;
+};
+
+static void stringarray_init(struct stringarray *a) {
+ a->v = NULL;
+ a->num = 0;
+}
+
+static void stringarray_cleanup(struct stringarray *a) {
+ free(a->v);
+}
+
+static void stringarray_add(struct stringarray *a, const char *s) {
+ a->v = realloc(a->v, (a->num + 1) * sizeof(a->v[0]));
+ if (a->v == NULL) {
+ errx(1, "Out of memory");
+ }
+ a->v[a->num] = xstrdup(s);
+ a->num++;
+}
+
+////////////////////////////////////////////////////////////
+
+static struct stringarray lines;
+static struct stringarray sollines;
+static bool hinting;
+static int scrolldown;
+static unsigned curx;
+static int cury;
+
+static void readquote(void) {
+ FILE *f = popen(_PATH_FORTUNE, "r");
+ if (!f) {
+ err(1, "%s", _PATH_FORTUNE);
+ }
+
+ char buf[128], buf2[8*sizeof(buf)];
+ while (fgets(buf, sizeof(buf), f)) {
+ char *s = strrchr(buf, '\n');
+ assert(s);
+ assert(strlen(s)==1);
+ *s = 0;
+
+ int i,j;
+ for (i=j=0; buf[i]; i++) {
+ if (buf[i]=='\t') {
+ buf2[j++] = ' ';
+ while (j%8) buf2[j++] = ' ';
+ }
+ else if (buf[i]=='\b') {
+ if (j>0) j--;
+ }
+ else {
+ buf2[j++] = buf[i];
+ }
+ }
+ buf2[j] = 0;
+
+ stringarray_add(&lines, buf2);
+ stringarray_add(&sollines, buf2);
+ }
+
+ pclose(f);
+}
+
+static void encode(void) {
+ int used[26];
+ for (int i=0; i<26; i++) used[i] = 0;
+
+ int key[26];
+ int keypos=0;
+ while (keypos < 26) {
+ int c = random()%26;
+ if (used[c]) continue;
+ key[keypos++] = c;
+ used[c] = 1;
+ }
+
+ for (int y=0; y<lines.num; y++) {
+ for (unsigned x=0; lines.v[y][x]; x++) {
+ if (islower((unsigned char)lines.v[y][x])) {
+ int q = lines.v[y][x]-'a';
+ lines.v[y][x] = 'a'+key[q];
+ }
+ if (isupper((unsigned char)lines.v[y][x])) {
+ int q = lines.v[y][x]-'A';
+ lines.v[y][x] = 'A'+key[q];
+ }
+ }
+ }
+}
+
+static int substitute(int ch) {
+ assert(cury>=0 && cury<lines.num);
+ if (curx >= strlen(lines.v[cury])) {
+ beep();
+ return -1;
+ }
+
+ int och = lines.v[cury][curx];
+ if (!isalpha((unsigned char)och)) {
+ beep();
+ return -1;
+ }
+
+ int loch = tolower((unsigned char)och);
+ int uoch = toupper((unsigned char)och);
+ int lch = tolower((unsigned char)ch);
+ int uch = toupper((unsigned char)ch);
+
+ for (int y=0; y<lines.num; y++) {
+ for (unsigned x=0; lines.v[y][x]; x++) {
+ if (lines.v[y][x]==loch) {
+ lines.v[y][x] = lch;
+ }
+ else if (lines.v[y][x]==uoch) {
+ lines.v[y][x] = uch;
+ }
+ else if (lines.v[y][x]==lch) {
+ lines.v[y][x] = loch;
+ }
+ else if (lines.v[y][x]==uch) {
+ lines.v[y][x] = uoch;
+ }
+ }
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////
+
+static void redraw(void) {
+ erase();
+ bool won = true;
+ for (int i=0; i<LINES-1; i++) {
+ move(i, 0);
+ int ln = i+scrolldown;
+ if (ln < lines.num) {
+ for (unsigned j=0; lines.v[i][j]; j++) {
+ int ch = lines.v[i][j];
+ if (ch != sollines.v[i][j] && isalpha((unsigned char)ch)) {
+ won = false;
+ }
+ bool bold=false;
+ if (hinting && ch==sollines.v[i][j] &&
+ isalpha((unsigned char)ch)) {
+ bold = true;
+ attron(A_BOLD);
+ }
+ addch(lines.v[i][j]);
+ if (bold) {
+ attroff(A_BOLD);
+ }
+ }
+ }
+ clrtoeol();
+ }
+
+ move(LINES-1, 0);
+ if (won) {
+ addstr("*solved* ");
+ }
+ addstr("~ to quit, * to cheat, ^pnfb to move");
+
+ move(LINES-1, 0);
+
+ move(cury-scrolldown, curx);
+
+ refresh();
+}
+
+static void opencurses(void) {
+ initscr();
+ cbreak();
+ noecho();
+}
+
+static void closecurses(void) {
+ endwin();
+}
+
+////////////////////////////////////////////////////////////
+
+static void loop(void) {
+ bool done=false;
+ while (!done) {
+ redraw();
+ int ch = getch();
+ switch (ch) {
+ case 1: /* ^A */
+ curx=0;
+ break;
+ case 2: /* ^B */
+ if (curx > 0) {
+ curx--;
+ }
+ else if (cury > 0) {
+ cury--;
+ curx = strlen(lines.v[cury]);
+ }
+ break;
+ case 5: /* ^E */
+ curx = strlen(lines.v[cury]);
+ break;
+ case 6: /* ^F */
+ if (curx < strlen(lines.v[cury])) {
+ curx++;
+ }
+ else if (cury < lines.num - 1) {
+ cury++;
+ curx = 0;
+ }
+ break;
+ case 12: /* ^L */
+ clear();
+ break;
+ case 14: /* ^N */
+ if (cury < lines.num-1) {
+ cury++;
+ }
+ if (curx > strlen(lines.v[cury])) {
+ curx = strlen(lines.v[cury]);
+ }
+ if (scrolldown < cury - (LINES-2)) {
+ scrolldown = cury - (LINES-2);
+ }
+ break;
+ case 16: /* ^P */
+ if (cury > 0) {
+ cury--;
+ }
+ if (curx > strlen(lines.v[cury])) {
+ curx = strlen(lines.v[cury]);
+ }
+ if (scrolldown > cury) {
+ scrolldown = cury;
+ }
+ break;
+ case '*':
+ hinting = !hinting;
+ break;
+ case '~':
+ done = true;
+ break;
+ default:
+ if (isalpha(ch)) {
+ if (!substitute(ch)) {
+ if (curx < strlen(lines.v[cury])) {
+ curx++;
+ }
+ if (curx==strlen(lines.v[cury]) && cury < lines.num-1) {
+ curx=0;
+ cury++;
+ }
+ }
+ }
+ else if (curx < strlen(lines.v[cury]) && ch==lines.v[cury][curx]) {
+ curx++;
+ if (curx==strlen(lines.v[cury]) && cury < lines.num-1) {
+ curx=0;
+ cury++;
+ }
+ }
+ else {
+ beep();
+ }
+ break;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////
+
+int main(void) {
+ stringarray_init(&lines);
+ stringarray_init(&sollines);
+ srandom(time(NULL));
+ readquote();
+ encode();
+ opencurses();
+
+ loop();
+
+ closecurses();
+ stringarray_cleanup(&sollines);
+ stringarray_cleanup(&lines);
+ return 0;
+}
diff --git a/cgram/pathnames.h b/cgram/pathnames.h
new file mode 100644
index 00000000..51da95c9
--- /dev/null
+++ b/cgram/pathnames.h
@@ -0,0 +1,30 @@
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _PATH_FORTUNE "/usr/games/fortune"