]> thyrsus.com Git - galaxis.git/commitdiff
Initial revision
authorEric S. Raymond <esr@thyrsus.com>
Sun, 23 Oct 1994 19:58:54 +0000 (19:58 +0000)
committerEric S. Raymond <esr@thyrsus.com>
Sun, 23 Oct 1994 19:58:54 +0000 (19:58 +0000)
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
galaxis.c [new file with mode: 0644]
galaxis.xml [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..ab795e8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+#      Makefile for the galaxis game
+
+# Flags for use with the Linux ncurses package (recommended)
+CFLAGS = -g -DNDEBUG  -I/usr/local/include -L/usr/local/lib
+TERMLIB = -lncurses
+CC = gcc
+
+# Flags for use with stock curses
+#CFLAGS = -DNDEBUG
+#TERMLIB = -lcurses
+#CC = gcc
+
+galaxis: galaxis.c
+       $(CC) $(CFLAGS) -o galaxis galaxis.c $(TERMLIB)
+
+clean:
+       rm -f galaxis galaxis.shar *~
+
+shar:
+       shar READ.ME galaxis.c Makefile galaxis.6 >galaxis.shar
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..2138110
--- /dev/null
+++ b/README
@@ -0,0 +1,17 @@
+                       GALAXIS for UNIX
+       by Eric S. Raymond <esr@snark.thyrsus.com>, Sun Oct 23 1994
+
+This directory contains the source for a UNIX-hosted, curses-based clone of the
+nifty little Macintosh freeware game Galaxis.  The code is POSIX-conforming and
+was written under Linux using the ncurses library; it should part readily to
+any System V.  BSD sites using the inferior native BSD curses will lose some
+features (no color, nor arrow-key support).
+
+Files in this distribution:
+
+READ.ME                -- this file
+Makefile       -- how to make the game
+galaxis.c      -- the game itself
+galaxis.6      -- a trivial man page
+
+Send bug reports to esr@snark.thyrsus.com.
diff --git a/galaxis.c b/galaxis.c
new file mode 100644 (file)
index 0000000..207c71f
--- /dev/null
+++ b/galaxis.c
@@ -0,0 +1,632 @@
+/* 
+ * galaxis.c - a curses clone of the Macintosh game, Galaxis
+ *
+ * version 1.0 by Eric S. Raymond <esr@snark.thyrsus.com> Sun Oct 23 1994
+ */
+#define _POSIX_SOURCE
+
+#include <stdio.h>
+#include <termios.h>
+#include <ncurses.h>   /* change this to curses.h if you don't have ncurses */
+#include <signal.h>
+#include <ctype.h>
+#include <assert.h>
+
+#ifndef A_UNDERLINE    /* BSD curses */
+#define        beep()  write(1,"\007",1);
+#define        cbreak  crmode
+#define        saveterm savetty
+#define        resetterm resetty
+#define        nocbreak nocrmode
+#define strchr index
+#endif /* !A_UNDERLINE */
+
+#ifdef isxdigit                /* aha, must be an AT&T system... */
+#define srand(n)       srand48(n)
+#define rand()         lrand48()
+extern long lrand48();
+extern void srand48();
+#define bzero(s, n)    (void)memset((char *)(s), '\0', n)
+extern char *memset();
+/*
+ * Try this if ungetch() fails to resolve.
+ *
+ * #define ungetch ungetc
+ */
+#endif /* isxdigit */
+
+/* miscellaneous constants */
+#define CTRLC          '\003'  /* used as terminate command */
+#define FF             '\014'  /* used as redraw command */
+#define ONBOARD(y, x)  (x >= 0 && x < BWIDTH && y >= 0 && y < BDEPTH)
+
+/* coordinate handling */
+#define BWIDTH         20
+#define BDEPTH         9
+#define LIFEBOATS      4
+
+/* board state flags */
+#define S_LIFEBOAT     0x01    /* boat is here */
+#define S_BOATSEEN     0x02    /* boat has been seen */
+#define S_MARKED       0x04    /* place has been marked empty by player */
+#define S_BLANKED      0x08    /* probe has been marked empty by player */
+#define S_PROBED       0x10    /* probe has been dropped here */
+#define S_REVERSE      0x20    /* this is a ray, mark it in rv */
+
+/* display symbols */
+#ifdef A_COLOR
+#define LIFEBOAT       ('L' | COLOR_PAIR(COLOR_RED))
+#define UNKNOWN                ('.' | COLOR_PAIR(COLOR_BLUE))
+#define MARKED         ('M' | COLOR_PAIR(COLOR_GREEN))
+#define BLANKED                ('o' | COLOR_PAIR(COLOR_GREEN))
+#define HIDDEN         ('X' | COLOR_PAIR(COLOR_MAGENTA))
+#else
+#define LIFEBOAT       'L'
+#define UNKNOWN                '.'
+#define MARKED         'M'
+#define BLANKED                'o'
+#define HIDDEN         'X'
+#endif /* A_COLOR */
+
+/* how to position us on player board */
+#define PYBASE 3
+#define PXBASE 7
+#define PY(y)  (PYBASE + (y))
+#define PX(x)  (PXBASE + ((x+1)*3))
+#define pgoto(y, x)    (void)move(PY(y), PX(x))
+
+/* other board locations */
+#define COLWIDTH       80
+#define PROMPTLINE     23                      /* prompt line */
+#define SYBASE         PYBASE + BDEPTH + 2     /* move key diagram */
+#define SXBASE         54
+#define MYBASE         SYBASE - 1              /* diagram caption */
+#define MXBASE         SXBASE
+#define HYBASE         SYBASE - 1              /* help area */
+#define HXBASE         PXBASE + 3
+#define POSYBASE       PYBASE + BDEPTH/2
+
+/* direction constants */
+#define E      0
+#define SE     1
+#define S      2
+#define SW     3
+#define W      4
+#define NW     5
+#define N      6
+#define NE     7
+static int xincr[8] = {1,  1,  0, -1, -1, -1,  0,  1};
+static int yincr[8] = {0,  1,  1,  1,  0, -1, -1, -1};
+
+static unsigned char board[BDEPTH][BWIDTH], probes[BDEPTH][BWIDTH];
+static int curx = (BWIDTH / 2);
+static int cury = (BDEPTH / 2);
+
+static int turn;                       /* how many tries we've made */
+static int boatsfound;                 /* number of boats found */
+static bool revealed;                  /* display hidden lifeboats */
+static bool dohilite;                  /* highlight those rays */
+
+#define        PR      (void)addstr
+
+static void outro(sig)
+/* end the game, either normally or due to signal */
+int    sig;
+{
+    clear();
+    (void)refresh();
+    (void)resetterm();
+    (void)echo();
+    (void)endwin();
+    exit(0);
+}
+
+static void intro()
+/* initialize curses */
+{
+    srand(time(0L)+getpid());  /* Kick the random number generator */
+
+    (void) signal(SIGINT,outro);
+    (void) signal(SIGINT,outro);
+    (void) signal(SIGIOT,outro);               /* for assert(3) */
+    if(signal(SIGQUIT,SIG_IGN) != SIG_IGN)
+       (void)signal(SIGQUIT,outro);
+
+    (void)initscr();
+#ifdef KEY_MIN
+    keypad(stdscr, TRUE);
+#endif /* KEY_MIN */
+    (void)saveterm();
+    (void)nonl();
+    (void)cbreak();
+    (void)noecho();
+
+#ifdef A_COLOR
+    start_color();
+
+    init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
+    init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
+    init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
+    init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
+    init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
+    init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
+    init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
+    init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
+#endif /* A_COLOR */
+}                  
+
+/* VARARGS1 */
+static void prompt(f, s)
+/* print a message at the prompt line */
+char *f, *s;
+{
+    (void) move(PROMPTLINE, 0);
+    (void) clrtoeol();
+    (void) printw(f, s);
+    (void) refresh();
+}
+
+static void error(s)
+char *s;
+{
+    (void) move(PROMPTLINE + 2, 0);
+    (void) clrtoeol();
+    if (s)
+    {
+       (void) addstr(s);
+       (void) beep();
+    }
+}
+
+static int rnd(n)
+int n;
+{
+    return(((rand() & 0x7FFF) % n));
+}
+
+static void initgame()
+{
+    int boatsplaced;
+
+    bzero(board, sizeof(char) * BWIDTH * BDEPTH);
+    bzero(probes, sizeof(char) * BWIDTH * BDEPTH);
+
+    /* first, place the lifeboats */
+    boatsplaced = 0;
+    while (boatsplaced < LIFEBOATS)
+    {
+       int x = rnd(BWIDTH);
+       int y = rnd(BDEPTH);
+
+       if (!(board[y][x] & S_LIFEBOAT))
+       {
+           board[y][x] |= S_LIFEBOAT;
+           boatsplaced++;
+       }       
+    }
+
+    turn = 0;
+    revealed = FALSE;
+    dohilite = TRUE;
+}
+
+
+static int show(int i, int j)
+{
+    int state = board[i][j];
+    int hilite = ((state & S_REVERSE) ? A_REVERSE : A_NORMAL);
+
+    if ((state & S_BOATSEEN))
+       return(hilite | LIFEBOAT);
+    else if (revealed && (state & S_LIFEBOAT))
+       return(hilite | HIDDEN);
+    else if ((state & S_MARKED))
+       return(hilite | MARKED);
+    else if ((state & S_BLANKED))
+       return(hilite | BLANKED);
+    else if ((state & S_PROBED))
+       return(hilite | ('0' + probes[i][j]));
+    else
+       return(hilite | UNKNOWN);
+}
+
+static void showmoves(int cury, int curx)
+/* reverse-highlight or unhighlight the rays extending from a loxcation */
+{
+    int n;
+    int i, j;
+
+    for (i = 0; i < BDEPTH; i++)
+       for (j = 0; j < BWIDTH; j++)
+           board[i][j] &=~ S_REVERSE;
+
+    if (!dohilite)
+       return;
+
+    for (n = 0; n < 8; n++)
+    {
+       int ny = cury;
+       int nx = curx;
+
+       for (;;)
+       {
+           ny += yincr[n];
+           nx += xincr[n];
+
+           if (!ONBOARD(ny, nx) || board[ny][nx] == S_BOATSEEN)
+               break;
+
+           board[ny][nx] |= S_REVERSE;
+       }
+    }
+}
+
+static void redisplay(bool force)
+/* redraw the main board */ 
+{
+    int        i, j;
+
+    if (force)
+    {
+       (void) clear();
+       (void) mvaddstr(0,35,"GALAXIS");
+       (void) mvaddstr(1,2, "by Eric S. Raymond, based on Christian Franz's original for the Macintosh"); 
+
+       (void) mvprintw(HYBASE,   HXBASE, "?       -- detailed help");
+       (void) mvprintw(HYBASE+1, HXBASE, "p       -- drop or re-query a probe");
+       (void) mvprintw(HYBASE+2, HXBASE, "<space> -- toggle the empty mark");
+       (void) mvprintw(HYBASE+3, HXBASE, "o       -- toggle ray highlighting");
+       (void) mvprintw(HYBASE+4, HXBASE, "r       -- refresh screen"); 
+       (void) mvprintw(HYBASE+5,HXBASE, "q       -- quit this game");
+
+       (void) mvaddstr(MYBASE,   MXBASE, "Movement keys:");
+       (void) mvaddstr(SYBASE,   SXBASE, "y k u    7 8 9");
+       (void) mvaddstr(SYBASE+1, SXBASE, " \\|/      \\|/ ");
+       (void) mvaddstr(SYBASE+2, SXBASE, "h-+-l    4-+-6");
+       (void) mvaddstr(SYBASE+3, SXBASE, " /|\\      /|\\ ");
+       (void) mvaddstr(SYBASE+4, SXBASE, "b j n    1 2 3");
+#ifdef A_UNDERLINE
+       (void) mvaddstr(SYBASE+5, SXBASE, "Arrow keys work.");
+#endif /* A_UNDERLINE */
+    }
+ 
+    (void) mvprintw(POSYBASE + 1, 0, "Tries: %d", turn);
+    (void) mvprintw(POSYBASE + 2, 0, "Found: %d", boatsfound);
+
+    for (i = 0; i < BDEPTH; i++)
+       for (j = 0; j < BWIDTH; j++)
+       {
+           pgoto(i, j);
+           addch(show(i,j));
+       }
+
+    pgoto(cury, curx);
+    refresh();
+}
+
+static void help()
+{
+    clear();
+
+       (void) mvaddstr(0,35,"GALAXIS\n\n");
+    (void) printw(
+           "%d lifeboats from a crippled interstellar liner are adrift in this starfield.\n",
+           LIFEBOATS);
+    (void) printw(
+           "To find them, you can place probes that look in all eight compass directions\n");
+    (void) printw(
+           "and tell you how many lifeboats they see.  If you drop a probe directly on a\n");
+    (void) printw(
+           "lifeboat it will be revealed immediately.  Your objective: find the lifeboats\n");
+    (void) printw(
+           "as quickly as possible, before the stranded passengers run out of oxygen!\n\n");
+
+
+    (void) printw(
+           "To drop a probe, move the cursor to a cell and press the `p' key.  You can mark\n");
+    (void) printw(
+           "any cell to show that you believe there is no lifeboat there by pressing the\n");
+    (void) printw(
+           "spacebar.  You can unmark a cell in the same way.  Finally, you can quit with\n");
+    (void) printw(
+           "`q', which will reveal all lifeboats.\n\n");
+
+    (void) printw(
+           "When the game starts, all the locations you can see from a given spot are\n");
+    (void) printw(
+           "reverse-video highlighted.  As you mark more of the board, this may become\n");
+    (void) printw(
+           "visually confusing.  You can toggle the highlighting with the `o' key.\n\n");
+
+    (void) printw(
+           "Sometimes, after you have found a lifeboat, doing `p' on a cell you've already\n");
+    (void) printw(
+           "probed will place more known-empty marks (because you can see all the boats the\n");
+    (void) printw(
+           "probe counted).  This second query will not cost you a turn.\n\n"); 
+
+    (void) printw(
+           "Yes, this should have an X interface more like the Mac game.  So write one!");
+
+    prompt("Press any key to continue...");
+    getch();
+    redisplay(TRUE);
+}
+
+static int getcoord(void)
+/* keep taking keystrokes till we see a player move */
+{
+    int ny, nx, c;
+
+    for (;;)
+    {
+       (void) mvprintw(POSYBASE,     0, "(%d, %c) ", curx, 'A'+cury);
+       pgoto(cury, curx);
+       curs_set(1);
+       showmoves(cury, curx);
+       redisplay(FALSE);
+
+       switch(c = getch())
+       {
+       case 'k': case '8':
+#ifdef KEY_MIN
+       case KEY_UP:
+#endif /* KEY_MIN */
+           ny = cury+BDEPTH-1; nx = curx;
+           break;
+       case 'j': case '2':
+#ifdef KEY_MIN
+       case KEY_DOWN:
+#endif /* KEY_MIN */
+           ny = cury+1;        nx = curx;
+           break;
+       case 'h': case '4':
+#ifdef KEY_MIN
+       case KEY_LEFT:
+#endif /* KEY_MIN */
+           ny = cury;          nx = curx+BWIDTH-1;
+           break;
+       case 'l': case '6':
+#ifdef KEY_MIN
+       case KEY_RIGHT:
+#endif /* KEY_MIN */
+           ny = cury;          nx = curx+1;
+           break;
+       case 'y': case '7':
+#ifdef KEY_MIN
+       case KEY_A1:
+#endif /* KEY_MIN */
+           ny = cury+BDEPTH-1; nx = curx+BWIDTH-1;
+           break;
+       case 'b': case '1':
+#ifdef KEY_MIN
+       case KEY_C1:
+#endif /* KEY_MIN */
+           ny = cury+1;        nx = curx+BWIDTH-1;
+           break;
+       case 'u': case '9':
+#ifdef KEY_MIN
+       case KEY_A3:
+#endif /* KEY_MIN */
+           ny = cury+BDEPTH-1; nx = curx+1;
+           break;
+       case 'n': case '3':
+#ifdef KEY_MIN
+       case KEY_C3:
+#endif /* KEY_MIN */
+           ny = cury+1;        nx = curx+1;
+           break;
+       case 'r':
+       case FF:        /* undocumented */
+           nx = curx; ny = cury;
+           (void)clearok(stdscr, TRUE);
+           (void)refresh();
+           break;
+       default:
+           return(c);
+       }
+
+       curx = nx % BWIDTH;
+       cury = ny % BDEPTH;
+    }
+}
+
+static void count_lifeboats(int cury, int curx, int *total, int *visible)
+/* count lifeboats visible from here */
+{
+    register int       n, tot, vis;
+
+    *total = *visible = tot = vis = 0;
+    for (n = 0; n < 8; n++)
+    {
+       int ny = cury;
+       int nx = curx;
+
+       for (;;)
+       {
+           ny += yincr[n];
+           nx += xincr[n];
+
+           if (!ONBOARD(ny, nx))
+               break;
+ 
+           if (board[ny][nx] & S_BOATSEEN)
+           {
+               ++tot;
+               ++vis;
+               break;          /* stop counting in this direction */
+           }
+ 
+           if (board[ny][nx] & S_LIFEBOAT)
+           {
+               ++tot;
+               break;          /* stop counting in this direction */
+           }
+       }
+    }
+
+    *total = tot;
+    *visible = vis;
+}
+
+static void auto_mark(cury, curx)
+/* automatically place marks on all rays */
+{
+    int n;
+
+    for (n = 0; n < 8; n++)
+    {
+       int ny = cury;
+       int nx = curx;
+
+       for (;;)
+       {
+           ny += yincr[n];
+           nx += xincr[n];
+
+           if (!ONBOARD(ny, nx) || (board[ny][nx] & S_BOATSEEN))
+               break;
+
+           if (!(board[ny][nx] & ~S_REVERSE))
+               board[ny][nx] |= S_MARKED;
+       }
+    }
+
+    board[cury][curx] |= S_BLANKED;
+}
+
+static void drop_probe(int cury, int curx)
+/* drop a probe on the given spot */
+{
+    if (board[cury][curx] & S_LIFEBOAT)
+    {
+       board[cury][curx] |= S_BOATSEEN;
+       prompt("You found a lifeboat!");
+       beep();
+       turn++;
+       boatsfound++;
+    }
+    else if (board[cury][curx] & S_BOATSEEN)
+    {
+       prompt("This lifeboat is already visible.");
+       beep();
+    }
+    else
+    {
+       int visibles, total;
+
+       count_lifeboats(cury, curx, &total, &visibles);
+
+       if (!(board[cury][curx] & S_PROBED))
+       {
+           board[cury][curx] |= S_PROBED; 
+           probes[cury][curx] = total;
+           turn++;
+       }
+       else
+           prompt("You have already probed this location.");
+
+       /* auto-mark all rays when all lifeboats on them are visible */
+       if (probes[cury][curx] == visibles)
+           auto_mark(cury, curx);
+    }
+}
+
+static bool domove(void)
+/* accept a player move */
+{
+    switch(getcoord())
+    {
+    case '?':
+       help();
+       break;
+
+    case ' ':
+       if (board[cury][curx] & S_PROBED)
+           board[cury][curx] ^= S_BLANKED;
+       else
+           board[cury][curx] ^= S_MARKED;
+       break;
+
+    case 'o':
+       dohilite = !dohilite;
+       redisplay(FALSE);
+       break;
+
+    case 'p':
+       drop_probe(cury, curx);
+       break;
+
+    case 'q':
+       revealed = TRUE;
+       redisplay(FALSE);
+       return(FALSE);
+
+    default:
+       prompt("Sorry, I don't recognize that commamd.");
+       break;
+    }
+
+    return(TRUE);
+}
+
+static bool allmarked(void)
+/* does user believe he knowa the whole board state */
+{
+    bool yes = TRUE;
+    int i, j;
+
+    for (i = 0; i < BDEPTH; i++)
+       for (j = 0; j < BWIDTH; j++)
+           if (!(board[i][j] || probes[i][j]))
+               yes = FALSE;
+    return(FALSE);
+}
+
+static int sgetc(s)
+/* get a character, restricted to a string */
+char *s;
+{
+    char *s1;
+    int ch;
+
+    (void)refresh();
+    for(;;)
+    {
+       ch = getch();
+       if (islower(ch))
+           ch = toupper(ch);
+       if (ch == CTRLC)
+           outro();
+       for (s1=s; *s1 && ch != *s1; ++s1)
+           continue;
+       if (*s1)
+       {
+           (void) addch((chtype)ch);
+           (void)refresh();
+           return(ch);
+       }
+    }
+}
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+    intro();
+    do {
+       initgame();
+       redisplay(TRUE);
+
+       while (boatsfound < LIFEBOATS && !allmarked() && domove())
+           continue;
+
+       redisplay(FALSE);       /* force found count to be updated */
+       prompt("Want to play again [yn]? ");
+    } while
+       (sgetc("YN") == 'Y');
+    outro();
+    /*NOTREACHED*/
+}
+
+/* galaxis.c ends here */
diff --git a/galaxis.xml b/galaxis.xml
new file mode 100644 (file)
index 0000000..02e91a8
--- /dev/null
@@ -0,0 +1,25 @@
+.TH GALAXIS 6 "Oct 23 1994"
+.SH NAME
+galaxis \- rescue lifeboats lost in interstellar space
+.SH SYNOPSIS
+galaxis
+.SH DESCRIPTION
+Lifeboats from a crippled interstellar liner are adrift in a starfield.  To
+find them, you can place probes that look in all eight compass directions and
+tell you how many lifeboats they see.  If you drop a probe directly on a
+lifeboat it will be revealed immediately.  Your objective: find the lifeboats
+as quickly as possible, before the stranded passengers run out of oxygen!
+.P
+This is a UNIX-hosted, curses-based clone of the nifty little Macintosh
+freeware game Galaxis.  It doesn't have the super-simple, point-and-click
+interface of the original, but compensates by automating away some of the
+game's simpler deductions.
+.P
+To run it, simply type `galaxis'.  The game screen includes a description
+of all commands, including `?' for detailed help.
+.SH AUTHORS
+The Macintosh game was designed and written by Christian Franz
+<cfranz@iiic.ethz.ch>.  This version was cloned by Eric S. Raymond
+<esr@snark.thyrsus.com>.
+
+