aboutsummaryrefslogtreecommitdiff
path: root/nmm.c
diff options
context:
space:
mode:
authorRyan Kavanagh <rak@debian.org>2013-03-07 16:41:02 -0500
committerRyan Kavanagh <rak@debian.org>2013-03-07 17:54:42 -0500
commitb1d02164f2fc3993004b28c17ca6f1fb79998431 (patch)
treee52214a0d5d3ee8e8c094975ae77cf17f3a6ddda /nmm.c
parentAdd install targets to Makefile (diff)
Release nmm version 1.0~beta11.0_beta1
Diffstat (limited to 'nmm.c')
-rw-r--r--nmm.c1366
1 files changed, 981 insertions, 385 deletions
diff --git a/nmm.c b/nmm.c
index 104e2fa..cac1f9b 100644
--- a/nmm.c
+++ b/nmm.c
@@ -29,11 +29,12 @@
#include <curses.h>
#include <err.h>
#include <errno.h>
+#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#define VERSION "0.1"
+#define VERSION "1.0~beta1"
#define EMPTY 'E'
#define WHITEC 'W'
@@ -41,14 +42,30 @@
#define WHITE 0
#define BLACK 1
#define NORTH 0
-#define EAST 1
-#define SOUTH 2
-#define WEST 3
-#define NORTHC 'n'
-#define EASTC 'e'
-#define SOUTHC 's'
-#define WESTC 'w'
-#define PIECECOUNT 8
+#define SOUTH 1
+#define WEST 2
+#define EAST 3
+#define NE 4
+#define SW 5
+#define NW 6
+#define SE 7
+#define MINDIR NORTH
+#define MAXDIR SE
+#define NORTHC "n"
+#define EASTC "e"
+#define SOUTHC "s"
+#define WESTC "w"
+#define NEC "ne"
+#define NWC "nw"
+#define SEC "se"
+#define SWC "sw"
+
+#define ROWS 3
+#define COLS 9
+
+#define NMM 4
+#define TMM 3
+#define TWMM 12
#define sbrow 3 /* score box */
#define sbcol 37
@@ -59,69 +76,98 @@
#define msgcol 7
#define promptcol 16
#define promptrow 7
+#define versrow 17 /* version col calculated based on helpcol */
+#define helpcol 65
+#define helprow 0
/* Board & game construction */
-struct point {
+typedef struct point {
char v; /* Value: what's currently here */
- struct point* n[4]; /* Our neighbours, indexed by NORTH, SOUTH, EAST, WEST */
-};
+ struct point* n[8]; /* Our neighbours, indexed by NORTH, SOUTH, WEST, EAST,
+ NE, SW, NW, SE, see dirc below */
+} point;
-typedef struct point nboard[3][8];
+typedef point* nboard[ROWS][COLS];
-struct game {
+typedef struct game {
nboard board;
int state;
int pieces[2];
int totalpieces;
int phase;
-};
+ int type;
+} game;
-struct scrgame {
+typedef struct scrgame {
struct game *game;
WINDOW *score_w;
WINDOW *board_w;
WINDOW *msg_w;
+} scrgame;
+
+/* Let's make looking up directions -> indices easier */
+struct strkey { char *k; int v; };
+
+static const struct strkey dirc[] = {
+ { NORTHC , NORTH }, { SOUTHC, SOUTH }, { EASTC, EAST }, { WESTC, WEST },
+ { NEC, NE }, { NWC, NW }, { SEC, SE }, { SWC, SW },
+ { 0 } /* GCC will incorrectly complain about this, GCC BUG#53119 */
};
-void joinns(struct point *, struct point *);
-void joinwe(struct point *, struct point *);
-void freepoint(struct point *);
-struct point makepoint(const char, struct point *, struct point *,
- struct point *, struct point *);
-void updatepoint(struct point *, const char, struct point *, struct point *,
- struct point *, struct point *);
-void initboard(nboard);
-void initgame(struct game *);
-int gameend(struct scrgame *);
-/* Rendering functions */
-WINDOW *create_scorebox(const struct game *);
-void update_scorebox(WINDOW *, const struct game *);
-WINDOW *create_board(const struct game *);
-void update_board(WINDOW *, const struct game *);
-WINDOW *create_msgbox(void);
-void update_msgbox(WINDOW *, const char *);
-void initall(struct scrgame *);
-/* During game */
-void printgame(const struct game *);
-int inmill(const struct point *);
-struct point *mill_handler(struct scrgame *, char *, int);
-int validcoords(const char *);
-int dirtoindex(const char);
-char statechar(const struct game *);
-char *getmove(struct scrgame *, char *, int);
-struct point *getpoint(struct game *, const char *);
-struct point *placepiece(struct scrgame *, const char *);
-int checkadjacent(const struct point *, const struct point *);
-struct point *removepiece(struct game *, struct point *);
-struct point *jumppiece(struct point *, struct game *, const char *);
-struct point *movepiece(struct point *, char);
-struct point *phaseone(struct scrgame *);
-struct point *phasetwo(struct scrgame *);
-int checkdir(const char *);
-char* prompt(const char *, char *, const int);
-char* promptcheck(const char *, char *, const int, const int,
- const char *, int (*function)(const char *));
-int main(void);
+int joinpts(const int, point *, point *);
+void joinns(point *, point *);
+void joinwe(point *, point *);
+void joinnesw(point *, point *);
+void joinnwse(point *, point *);
+void freepoint(point *);
+int dirtoindex(const char *);
+point makepoint(const char, point *, point *,
+ point *, point *);
+void updatepoint(point *, const char, point *, point *,
+ point *, point *);
+void init3board(nboard);
+void init9board(nboard);
+void init12board(nboard);
+void initgame(game *, const int);
+void initall(scrgame *, const int);
+/* Rendering functions */
+WINDOW *create_scorebox(const game *);
+void update_scorebox(WINDOW *, const game *);
+WINDOW *create_board(const game *);
+WINDOW *create_3board(const game *);
+WINDOW *create_9board(const game *);
+WINDOW *create_12board(const game *);
+void update_board(WINDOW *, const game *);
+void update_3board(WINDOW *, const game *);
+void update_9board(WINDOW *, const game *);
+WINDOW *create_msgbox(void);
+void update_msgbox(WINDOW *, const char *);
+char *getgname(const game *);
+void full_redraw(scrgame *);
+void printinstrs(scrgame *);
+int gameend(const scrgame *);
+__dead void quit(void);
+/* During game */
+int inmill(const point *);
+point *mill_handler(scrgame *, char *, int);
+char statechar(const game *);
+int validcoords(const game *, const char *);
+int valid3coords(const char *);
+int valid9coords(const char *);
+int checkdir(const char *);
+char *getinput(scrgame *, char *, const int);
+char *getmove(scrgame *, char *, int);
+point *getpoint(const game *, const char *);
+point *get3point(const game *, const char *);
+point *get9point(const game *, const char *);
+point *placepiece(const scrgame *, const char *);
+point *movepiece(point *, const char *);
+point *jumppiece(point *, game *, const char *);
+point *removepiece(game *, point *);
+point *phaseone(scrgame *);
+point *phasetwothree(scrgame *);
+char *lower(char *);
+int main(int, char **);
const char *const instructions[] = {
"The board consists of positions connected with dashed lines, initially\n",
@@ -130,74 +176,143 @@ const char *const instructions[] = {
"reduce the opponent to 2 pieces. Gameplay is split into three phases.\n",
"\n",
"Phase 1. Users alternatingly place a piece on the board, until each\n",
- " player has placed 7 pieces. The location is selected by entering its\n",
- " coordinates, e.g. `a1' or `g7'. A mill can be formed by aligning\n",
+ " player has placed 3/9/12 pieces. The location is selected by entering\n",
+ " its coordinates, e.g. `a1' or `g7'. A mill can be formed by aligning\n",
" three pieces along a dashed line, at which point an opponent's piece\n",
" is removed. This piece is selected by entering its coordinates.\n",
"Phase 2. Users alternatingly slide one of their pieces along dashed line\n",
" to an adjacent empty position. The piece is selected by entering its\n",
- " coordinates and its direction is chosen from n s e w, for North, South,\n",
- " East, West. For example, `a1n' moves the piece at a1 north, `g7s' moves\n",
- " the piece at g7 south. Mills are formed and pieces removed as in\n",
- " phase 1. Phase 2 continues until a player only has 3 pieces remaining.\n",
+ " coordinates and its direction is chosen from n s e w ne nw se sw, for\n",
+ " North, South, East, West, etc. For example, `a1n' moves the piece at\n",
+ " a1 north, `g7sw' moves the piece at g7 south west. Mills are formed\n",
+ " and pieces removed as in phase 1. Phase 2 continues until a player\n",
+ " only has 3 pieces remaining.\n",
"Phase 3. The player with three pieces may move a piece to any empty\n",
" location, e.g. `a1g7' moves the piece at a1 to location g7. The player\n",
" with more than three pieces moves as in phase 2. The first player to\n",
" reduce the opponent to two pieces wins.\n",
"\n",
+ "Press `?' to display these instructions, and `q' to quit.\n",
+ "\n",
"Press any key to continue...",
NULL
};
-/*
+/* **************************
* Board and game creation
+ * ************************** */
+
+/*
+ * Connect two points, with p1 in direction L1 of p2
*/
+int
+joinpts(const int L1, /*@null@*/ point *p1, /*@null@*/ point *p2)
+{
+ /* Check L1 % 2 == 0 to make sure L1 == NORTH,WEST,NE,NW */
+ if (MINDIR <= L1 && L1 <= MAXDIR && L1 % 2 == 0) {
+ if (p1) {
+ p1->n[L1+1] = p2;
+ freepoint(p1);
+ }
+ if (p2) {
+ p2->n[L1] = p1;
+ freepoint(p2);
+ }
+ return 1;
+ }
+ return 0;
+}
+/*
+ * Join the north point n to the south point s
+ */
void
-joinns(/*@null@*/ struct point *n, /*@null@*/ struct point *s)
+joinns(/*@null@*/ point *n, /*@null@*/ point *s)
{
- if (n) {
- n->n[SOUTH] = s;
- freepoint(n);
- }
- if (s) {
- s->n[NORTH] = n;
- freepoint(s);
- }
+ joinpts(NORTH, n, s);
+}
+
+/*
+ * Join the west point w to the east point e
+ */
+void
+joinwe(/*@null@*/ point *w, /*@null@*/ point *e)
+{
+ joinpts(WEST, w, e);
}
+/*
+ * Join the north east point ne to the south west point sw
+ */
void
-joinwe(/*@null@*/ struct point *w, /*@null@*/ struct point *e)
+joinnesw(/*@null@*/ point *ne, /*@null@*/ point *sw)
{
- if (e) {
- e->n[WEST] = w;
- freepoint(e);
+ joinpts(NE, ne, sw);
+}
+
+/*
+ * Join the north west point nw to the south east point se
+ */
+void
+joinnwse(/*@null@*/ point *nw, /*@null@*/ point *se)
+{
+ joinpts(NW, nw, se);
+}
+
+/*
+ * Free the memory allocated to a point if it has no neighbours
+ */
+void
+freepoint(point *p)
+{
+ int i, joined = 0;
+ for (i = 0; i <= MAXDIR; i++) {
+ if (p->n[i]) {
+ joined = 1;
+ break;
+ }
}
- if (w) {
- w->n[EAST] = e;
- freepoint(w);
+ if (!joined) {
+ /* p has no neighbours */
+ /* FIXME: Why does this crash with modified chunk-pointer? */
+ /* free(p); */
}
}
-void freepoint(struct point *p)
+/*
+ * Converts a direction to a neighbour index
+ */
+int
+dirtoindex(const char *dir)
{
- if (!p->n[NORTH] && !p->n[SOUTH] && !p->n[EAST] && !p->n[WEST]) {
- /* free(p); */
+ const struct strkey *ptr;
+ /* dirc is bound by a sentinal with null key */
+ for (ptr = dirc; ptr->k; ptr++) {
+ if (strncmp(ptr->k, dir, 2) == 0) {
+ return ptr->v;
+ }
}
+ return -1;
}
-struct point
-makepoint(const char v, /*@null@*/ struct point *n, /*@null@*/ struct point *s,
- /*@null@*/ struct point *e, /*@null@*/ struct point *w)
+/*
+ * Given a label and four main neighbours, create a point
+ */
+point
+makepoint(const char v, /*@null@*/ point *n, /*@null@*/ point *s,
+ /*@null@*/ point *e, /*@null@*/ point *w)
{
- struct point temp;
+ point temp;
updatepoint(&temp, v, n, s, e, w);
return temp;
}
+/*
+ * Update the value and four main neighbours of a point p
+ */
void
-updatepoint(struct point *p, const char v, struct point *n, struct point *s,
- struct point *e, struct point *w)
+updatepoint(point *p, const char v, point *n, point *s,
+ point *w, point *e)
{
p->v = v;
joinns(p, s);
@@ -206,33 +321,97 @@ updatepoint(struct point *p, const char v, struct point *n, struct point *s,
joinwe(p, e);
}
+/*
+ * Create the board for Three Man Morris
+ */
void
-initboard(nboard b)
+init3board(nboard b)
{
int r = 0;
/* Initialize everything to an empty grid */
for (r = 0; r < 3; r++) {
- updatepoint(&b[r][0], EMPTY, NULL, NULL, NULL, NULL); /* Top mid */
- updatepoint(&b[r][1], EMPTY, NULL, NULL, &b[r][0], NULL); /* Top right */
- updatepoint(&b[r][2], EMPTY, &b[r][1], NULL, NULL, NULL); /* Mid right */
- updatepoint(&b[r][3], EMPTY, &b[r][2], NULL, NULL, NULL); /* Bot right */
- updatepoint(&b[r][4], EMPTY, NULL, NULL, &b[r][3], NULL); /* Bot mid */
- updatepoint(&b[r][5], EMPTY, NULL, NULL, &b[r][4], NULL); /* Bot left */
- updatepoint(&b[r][6], EMPTY, NULL, &b[r][5], NULL, NULL); /* Mid left */
- updatepoint(&b[r][7], EMPTY, NULL, &b[r][6], &b[r][0], NULL); /* Top left */
+ updatepoint(b[r][0], EMPTY, NULL, NULL, NULL, NULL);
+ updatepoint(b[r][1], EMPTY, NULL, NULL, b[r][0], NULL);
+ updatepoint(b[r][2], EMPTY, NULL, NULL, b[r][1], NULL);
+ b[r][3] = NULL;
}
for (r = 0; r < 2; r++) {
- joinns(&b[r][0], &b[r+1][0]); /* Top vertical line */
- joinns(&b[r+1][4], &b[r][4]); /* Bot vertical line */
- joinwe(&b[r][6], &b[r+1][6]); /* Left horiz line */
- joinwe(&b[r+1][2], &b[r][2]); /* Right horiz line */
+ joinns(b[r][0], b[r+1][0]);
+ joinns(b[r][1], b[r+1][1]);
+ joinns(b[r][2], b[r+1][2]);
}
}
+/*
+ * Create the board for Nine Man Morris
+ */
void
-initgame(struct game *g)
+init9board(nboard b)
{
- initboard(g->board);
+ int r = 0;
+ /* Initialize everything to an empty grid */
+ for (r = 0; r < 3; r++) {
+ updatepoint(b[r][0], EMPTY, NULL, NULL, b[r][7], NULL); /* Top mid */
+ updatepoint(b[r][1], EMPTY, NULL, NULL, b[r][0], NULL); /* Top right */
+ updatepoint(b[r][2], EMPTY, b[r][1], NULL, NULL, NULL); /* Mid right */
+ updatepoint(b[r][3], EMPTY, b[r][2], NULL, NULL, NULL); /* Bot right */
+ updatepoint(b[r][4], EMPTY, NULL, NULL, NULL, b[r][3]); /* Bot mid */
+ updatepoint(b[r][5], EMPTY, NULL, NULL, NULL, b[r][4]); /* Bot left */
+ updatepoint(b[r][6], EMPTY, NULL, b[r][5], NULL, NULL); /* Mid left */
+ updatepoint(b[r][7], EMPTY, NULL, b[r][6], NULL, NULL); /* Top left */
+ b[r][8] = NULL;
+ }
+ for (r = 0; r < 2; r++) {
+ printw("\n\n%d\n\n\n", r);
+ joinns(b[r][0], b[r+1][0]); /* Top vertical line */
+ joinns(b[r+1][4], b[r][4]); /* Bot vertical line */
+ joinwe(b[r][6], b[r+1][6]); /* Left horiz line */
+ joinwe(b[r+1][2], b[r][2]); /* Right horiz line */
+ }
+}
+
+/*
+ * Create the board for Twelve Man Morris
+ */
+void
+init12board(nboard b)
+{
+ int r = 0;
+ init9board(b);
+ for (r = 0; r < 2; r++) {
+ /* Top right */
+ joinnesw(b[r][1], b[r+1][1]);
+ /* Bottom right */
+ joinnwse(b[r+1][3], b[r][3]);
+ /* Bottom left */
+ joinnesw(b[r+1][5], b[r][5]);
+ /* Top left */
+ joinnwse(b[r][7], b[r+1][7]);
+ }
+}
+
+/*
+ * Initialize a game with the appropriate boards, reset scores, etc.
+ */
+void
+initgame(game *g, const int type)
+{
+ switch (type)
+ {
+ case TMM:
+ init3board(g->board);
+ g->type = TMM;
+ break;
+
+ case TWMM:
+ init12board(g->board);
+ g->type = TWMM;
+ break;
+
+ default:
+ init9board(g->board);
+ g->type = NMM;
+ }
/* We'll assume that, as in chess, black moves first */
g->state = BLACK;
g->phase = 1;
@@ -241,21 +420,25 @@ initgame(struct game *g)
g->pieces[BLACK] = 0;
}
+/*
+ * Initialize the board and all of the windors in a scrgame
+ */
void
-initall(struct scrgame *sg)
+initall(scrgame *sg, const int type)
{
- initgame(sg->game);
- sg->score_w = create_scorebox(sg->game);
- sg->board_w = create_board(sg->game);
- sg->msg_w = create_msgbox();
+ initgame(sg->game, type);
+ full_redraw(sg);
}
-/*
+/* ********************
* Drawing functions
- */
+ * ******************** */
-WINDOW
-*create_scorebox(const struct game *g)
+/*
+ * Create the scorebox / prompt window
+ */
+WINDOW *
+create_scorebox(const game *g)
{
WINDOW *local_win;
@@ -266,14 +449,28 @@ WINDOW
/* Uncomment the following for the curses box, but I prefer the
"rustic" boxes drawn with pipes, hyphens and plusses */
/* box(local_win, 0, 0); */
- mvwprintw(local_win, 1, 2, "Nine Man Morris");
+ switch (g->type)
+ {
+ case TMM:
+ mvwprintw(local_win, 1, 2, "Three Man Morris");
+ break;
+ case TWMM:
+ mvwprintw(local_win, 1, 2, "Twelve Man Morris");
+ break;
+ default:
+ mvwprintw(local_win, 1, 2, "Nine Man Morris");
+ break;
+ }
/* update will call a refresh for us */
update_scorebox(local_win, g);
return local_win;
}
+/*
+ * Update the score, phase, and current player
+ */
void
-update_scorebox(WINDOW *w, const struct game *g)
+update_scorebox(WINDOW *w, const game *g)
{
mvwprintw(w, 3, 2, "Phase %d", g->phase);
mvwprintw(w, 4, 2, "White pieces: %d", g->pieces[WHITE]);
@@ -286,8 +483,60 @@ update_scorebox(WINDOW *w, const struct game *g)
wrefresh(w);
}
-WINDOW
-*create_board(const struct game *g)
+/*
+ * Determine the type of board to draw and draw it
+ */
+WINDOW *
+create_board(const game *g)
+{
+ switch (g->type)
+ {
+ case TMM:
+ return create_3board(g);
+
+ case TWMM:
+ return create_12board(g);
+
+ default:
+ return create_9board(g);
+ }
+}
+
+/*
+ * Draw board for Three Man Morris
+ */
+WINDOW *
+create_3board(const game *g)
+{
+ WINDOW *local_win;
+ int r = 0;
+ char c = 'a';
+
+ local_win = newwin(13 + legendsep, 21 + legendsep, brdrow, brdcol);
+
+ /* Draw the legend */
+ for (r = 0; r < 3; r++) {
+ mvwprintw(local_win, 6*r, 0, "%d", 3-r);
+ }
+ c = 'a';
+ for (r = 0; r < 21; r += 9) {
+ mvwaddch(local_win, 14, legendsep + r, c++);
+ }
+ /* Draw the board lines */
+ for (r = 0; r < 3; r++) {
+ mvwhline(local_win, 6*r, legendsep, '-', 18);
+ mvwvline(local_win, 1, legendsep + 9*r, '|', 11);
+ }
+
+ update_3board(local_win, g);
+ return local_win;
+}
+
+/*
+ * Draw board for Nine Man Morris
+ */
+WINDOW *
+create_9board(const game *g)
{
WINDOW *local_win;
int r = 0;
@@ -301,8 +550,7 @@ WINDOW
}
c = 'a';
for (r = 0; r < 21; r += 3) {
- mvwaddch(local_win, 14, legendsep + r, c);
- c++;
+ mvwaddch(local_win, 14, legendsep + r, c++);
}
/* Draw the board lines */
@@ -325,33 +573,100 @@ WINDOW
/* RH crossline */
mvwhline(local_win, 6, legendsep + 13, '-', 5);
- update_board(local_win, g);
+ update_9board(local_win, g);
return local_win;
}
+/*
+ * Draw board for Twelve Man Morris
+ */
+WINDOW *
+create_12board(const game *g)
+{
+ WINDOW *local_win;
+ int r = 0;
+ local_win = create_9board(g);
+ /* Draw the diagonal lines */
+ for (r = 0; r < 2; r++) {
+ /* Top left */
+ mvwaddch(local_win, 2*r + 1, legendsep + 3*r + 2, '\\');
+ /* Top right */
+ mvwaddch(local_win, 2*r + 1, legendsep + 16 - 3*r, '/');
+ /* Bottom left */
+ mvwaddch(local_win, 11 - 2*r, legendsep + 3*r + 2, '/');
+ /* Bottom right */
+ mvwaddch(local_win, 11 - 2*r, legendsep + 16 - 3*r, '\\');
+ }
+ wrefresh(local_win);
+ return local_win;
+}
+
+/*
+ * Select the correct function to redraw points
+ */
+void
+update_board(WINDOW *w, const game *g)
+{
+ switch (g->type)
+ {
+ case TMM:
+ update_3board(w, g);
+ break;
+
+ case TWMM:
+ case NMM:
+ default:
+ update_9board(w, g);
+ }
+}
+
+/*
+ * Redraw points for Three Man Morris
+ */
void
-update_board(WINDOW *w, const struct game *g)
+update_3board(WINDOW *w, const game *g)
{
int r = 0;
for (r = 0; r < 3; r++) {
- mvwaddch(w, 2*r, legendsep + 9, g->board[r][0].v);
- mvwaddch(w, 2*r, legendsep + 18 - 3*r, g->board[r][1].v);
- mvwaddch(w, 6, legendsep + 18 - 3*r, g->board[r][2].v);
- mvwaddch(w, 12-2*r, legendsep + 18 - 3*r, g->board[r][3].v);
- mvwaddch(w, 12-2*r, legendsep + 9, g->board[r][4].v);
- mvwaddch(w, 12-2*r, legendsep + 3*r, g->board[r][5].v);
- mvwaddch(w, 6, legendsep + 3*r, g->board[r][6].v);
- mvwaddch(w, 2*r, legendsep + 3*r, g->board[r][7].v);
+ mvwaddch(w, 6*r, legendsep, g->board[r][0]->v);
+ mvwaddch(w, 6*r, legendsep + 9, g->board[r][1]->v);
+ mvwaddch(w, 6*r, legendsep + 18, g->board[r][2]->v);
}
wrefresh(w);
}
-WINDOW
-*create_msgbox(void)
+/*
+ * Redraw points for Nine and Twelve Man Morris
+ */
+void
+update_9board(WINDOW *w, const game *g)
+{
+ int r = 0;
+ for (r = 0; r < 3; r++) {
+ mvwaddch(w, 2*r, legendsep + 9, g->board[r][0]->v);
+ mvwaddch(w, 2*r, legendsep + 18 - 3*r, g->board[r][1]->v);
+ mvwaddch(w, 6, legendsep + 18 - 3*r, g->board[r][2]->v);
+ mvwaddch(w, 12-2*r, legendsep + 18 - 3*r, g->board[r][3]->v);
+ mvwaddch(w, 12-2*r, legendsep + 9, g->board[r][4]->v);
+ mvwaddch(w, 12-2*r, legendsep + 3*r, g->board[r][5]->v);
+ mvwaddch(w, 6, legendsep + 3*r, g->board[r][6]->v);
+ mvwaddch(w, 2*r, legendsep + 3*r, g->board[r][7]->v);
+ }
+ wrefresh(w);
+}
+
+/*
+ * Create window for displaying messages
+ */
+WINDOW *
+create_msgbox(void)
{
return newwin(2, 80-msgcol, msgrow, msgcol);
}
+/*
+ * Change message displayed in window
+ */
void
update_msgbox(WINDOW *w, const char *msg)
{
@@ -360,8 +675,72 @@ update_msgbox(WINDOW *w, const char *msg)
wrefresh(w);
}
+char *
+getgname(const game *g)
+{
+ switch (g->type)
+ {
+ case TMM:
+ return "tmm";
+
+ case TWMM:
+ return "twmm";
+
+ case NMM:
+ default:
+ return "nmm";
+ }
+}
+
+/*
+ * Completely redraw *everything*
+ */
+void
+full_redraw(scrgame *sg)
+{
+ static char vers[25];
+ static size_t vers_len;
+ static const char *helpstr = "? : help";
+ static const size_t helplen = 8;
+ snprintf(vers, 25, "%s version %s", getgname(sg->game), VERSION);
+ vers_len = strnlen(vers, 25);
+ delwin(sg->board_w);
+ delwin(sg->score_w);
+ delwin(sg->msg_w);
+ clear();
+ /* We want the ends of the version string and of
+ * helpstr to line up. */
+ mvprintw(versrow, helpcol + helplen - vers_len, vers);
+ mvprintw(helprow, helpcol, helpstr);
+ refresh();
+ sg->board_w = create_board(sg->game);
+ sg->score_w = create_scorebox(sg->game);
+ sg->msg_w = create_msgbox();
+}
+
+/*
+ * Print instructions to screen and prompt to continue
+ */
+void
+printinstrs(scrgame *sg)
+{
+ /* TODO: Factor out the refresh-everything part */
+ const char *const *instr;
+ clear();
+ for (instr = instructions; *instr; instr++) {
+ printw("%s", *instr);
+ }
+ refresh();
+ getch();
+ full_redraw(sg);
+}
+
+/*
+ * Announce winner, prompt user to play again, non-zero if user wants
+ * to.
+ */
int
-gameend(struct scrgame *sg)
+gameend(const scrgame *sg)
{
char c;
if (sg->game->pieces[WHITE] < sg->game->pieces[BLACK]) {
@@ -379,64 +758,67 @@ gameend(struct scrgame *sg)
}
/*
- * Functions concerning game logic
+ * Properly end curses and exit
*/
+__dead void
+quit(void)
+{
+ refresh();
+ endwin();
+ exit(0);
+}
+/* ********************************
+ * Functions concerning game logic
+ * ******************************** */
+
+/*
+ * Is the point p in a mill?
+ */
int
-inmill(const struct point *p)
+inmill(const point *p)
{
if (p->v == BLACKC || p->v == WHITEC) {
- int steps = 0;
- int hc = 1;
- int vc = 1; /* horizontal count, vertical count */
- struct point *np = p->n[NORTH];
- struct point *sp = p->n[SOUTH];
- struct point *ep = p->n[EAST];
- struct point *wp = p->n[WEST];
-
- /* We make up to three steps in each direction to account for when
- we're on the edge */
- for (steps = 0; steps < 3; steps++) {
- if (np && np->v == p->v) {
- vc++;
- np = np->n[NORTH];
- }
- if (sp && sp->v == p->v) {
- vc++;
- sp = sp->n[SOUTH];
- }
- if (ep && ep->v == p->v) {
- hc++;
- ep = ep->n[EAST];
+ point *neighbours[8];
+ int counts[4] = {1, 1, 1, 1};
+ int i, dir = MINDIR;
+ for (dir = MINDIR; dir <= MAXDIR; dir++) {
+ neighbours[dir] = p->n[dir];
+ }
+
+ /* We should walk at most 2 steps in any direction */
+ for (i = 0; i < 2; i++) {
+ for (dir = MINDIR; dir <= MAXDIR; dir++) {
+ if (neighbours[dir] && neighbours[dir]->v == p->v) {
+ /* Makes use of the fact that directions along the same axis
+ * are 1 apart (e.g. SOUTH = NORTH + 1 = 1), and that
+ * division truncates toward zero */
+ counts[dir / 2]++;
+ neighbours[dir] = neighbours[dir]->n[dir];
+ }
}
- if (wp && wp->v == p->v) {
- hc++;
- wp = wp->n[WEST];
+ }
+ /* If we have three in a row along any axis, we're in a mill */
+ for (i = 0; i < 4; i++) {
+ if (counts[i] == 3) {
+ return 1;
}
}
- return (hc == 3 || vc == 3);
}
/* If we're EMPTY, we're clearly not in a mill */
return 0;
}
-int
-adjacent(const struct point *p1, const struct point *p2)
-{
- return (p2 == p1->n[EAST] || p2 == p1->n[WEST] ||
- p2 == p1->n[NORTH] || p2 == p1->n[SOUTH]);
-}
-
-char
-statechar(const struct game *g)
-{
- return g->state == WHITE ? WHITEC : BLACKC;
-}
-
-struct point*
-mill_handler(struct scrgame *sg, char *move, int count)
+/*
+ * Handle a mill by dealing with the removal of an opponent's piece.
+ * Prompts the user for a piece to remove, checks its legality, and
+ * removes it. Return the removed piece, recursing until a piece is
+ * removed.
+ */
+point *
+mill_handler(scrgame *sg, char *move, int count)
{
- struct point *p = NULL;
+ point *p = NULL;
if (count == 0) {
update_msgbox(sg->msg_w,
@@ -462,7 +844,7 @@ mill_handler(struct scrgame *sg, char *move, int count)
}
for (r = 0; r < 3; r++) {
for (c = 0; c < 7; c++) {
- if (sg->game->board[r][c].v == opp && !inmill(&sg->game->board[r][c])) {
+ if (sg->game->board[r][c]->v == opp && !inmill(sg->game->board[r][c])) {
update_msgbox(sg->msg_w,
"It is possible to remove a piece not in a mill; do so.");
return mill_handler(sg, move, 1);
@@ -492,134 +874,239 @@ mill_handler(struct scrgame *sg, char *move, int count)
}
}
-struct point*
-placepiece(struct scrgame *sg, const char *coords)
+/*
+ * Return the char corresponding to the current state
+ */
+char
+statechar(const game *g)
{
- struct point *p = NULL;
- if ((p = getpoint(sg->game, coords))) {
- if (p->v == EMPTY) {
- p->v = statechar(sg->game);
- return p;
- } else {
- update_msgbox(sg->msg_w, "That location is already occupied, please try again.");
- return NULL;
- }
- } else {
- update_msgbox(sg->msg_w, "Invalid coordinates. Please try again.");
- return NULL;
- }
+ return g->state == WHITE ? WHITEC : BLACKC;
}
+/*
+ * Checks the validity of coordinates for a game
+ * Assumes coords are lower case and of length 2
+ */
int
-checkdir(const char *dir)
+validcoords(const game *g, const char *coords)
{
- if (isalpha(dir[0])) {
- char d;
- d = tolower(dir[0]);
- return (d == NORTHC || d == SOUTHC || d == EASTC || d == WESTC);
+ switch (g->type)
+ {
+ case TMM:
+ return valid3coords(coords);
+
+ case NMM:
+ case TWMM:
+ default:
+ return valid9coords(coords);
}
- return 0;
}
+/*
+ * Checks the validity of coordinates for Three Man Morris
+ * Assumes coords are lower case and of length 2
+ */
int
-validcoords(const char *coords)
+valid3coords(const char *coords)
+{
+ return ('a' <= coords[0] && coords[0] <= 'c' &&
+ '1' <= coords[1] && coords[1] <= '3');
+}
+
+/*
+ * Checks the validity of coordinates for Nine, Twelve Man Morris
+ * Assumes coords are lower case and of length 2
+ */
+int
+valid9coords(const char *coords)
{
- /* TODO: Why doesn't this work? */
- if ((sizeof(coords) / sizeof(*coords)) < 2) {
- return 0;
- }
if (coords[0] < 'a' || 'g' < coords[0] || coords[1] < '0' || '7' < coords[1]) {
return 0;
}
switch (coords[0])
- {
- case 'a': case 'g':
- return (coords[1] == '1' || coords[1] == '4' || coords[1] == '7');
+ {
+ case 'a': case 'g':
+ return (coords[1] == '1' || coords[1] == '4' || coords[1] == '7');
- case 'b': case 'f':
- return (coords[1] == '2' || coords[1] == '4' || coords[1] == '6');
+ case 'b': case 'f':
+ return (coords[1] == '2' || coords[1] == '4' || coords[1] == '6');
- case 'c': case 'e':
- return ('3' <= coords[1] && coords[1] <= '5');
+ case 'c': case 'e':
+ return ('3' <= coords[1] && coords[1] <= '5');
- case 'd':
- return (coords[1] != '4');
+ case 'd':
+ return (coords[1] != '4');
- default:
- /* UNREACHABLE */
- return 0;
- }
+ default:
+ /* UNREACHABLE */
+ return 0;
+ }
}
-char
-*getmove(struct scrgame *sg, char *move, int l)
+/*
+ * Checks the validity of a direction
+ */
+int
+checkdir(const char *dir)
{
- int length = 0;
- int ch = 0;
- int c = 0;
- if (l < 3 || 5 < l) {
- if (sg->game->phase == 1) {
- /* Of form d3 */
- length = 3;
- } else if (sg->game->phase == 2) {
- /* Of form d3s */
- length = 4;
- } else if (sg->game->phase == 3) {
- if (sg->game->pieces[(int) sg->game->state] == 3) {
- /* Of form d3a1 */
- length = 5;
- } else {
- /* Of form d3s */
- length = 4;
- }
+ if (isalpha(dir[0])) {
+ char d[3];
+ int i;
+ for (i = 0; i < 2; i++) {
+ d[i] = tolower(dir[0]);
}
- } else {
- length = l;
+ d[2] = '\0';
+ i = dirtoindex(d);
+ return (MINDIR <= i && i <= MAXDIR);
}
+ return 0;
+}
+
+/*
+ * Prompt user for input, handling backspace, return.
+ * q twice at start of line: quit()
+ * ? at start of line: printinstrs()
+ * Otherwise, read up to length characters from sg->scorer_w into inp
+ * Assume than inp always has room for at least '\0'
+ * Recurse until no errors, return pointer to inp.
+ */
+char *
+getinput(scrgame *sg, char *inp, const int length)
+{
+ int l, ch, i, quitc;
+ quitc = 0;
/* Clear the prompt area */
mvwaddstr(sg->score_w, promptrow, promptcol, " |");
- for (c = 0; c < length - 1; c++) {
- /* Store into int to make sure we don't miss out on special
- ncurses keys, such as KEY_BACKSPACE */
- ch = mvwgetch(sg->score_w, promptrow, promptcol + c);
- if (ch == '\b' || ch == KEY_BACKSPACE) {
- /* We don't want users erasing the whole screen */
- if (c > 0) {
- mvwaddch(sg->score_w, promptrow, promptcol + c, ' ');
- c--;
+ wrefresh(sg->score_w);
+ for (l = 0; l < length - 1; l++) {
+ ch = mvwgetch(sg->score_w, promptrow, promptcol + l);
+ if (ch == 12) {
+ /* We were given a ^L */
+ full_redraw(sg);
+ update_msgbox(sg->msg_w, "");
+ for (i = 0; i < l; i++) {
+ mvwaddch(sg->score_w, promptrow, promptcol + i, inp[i]);
+ }
+ wrefresh(sg->score_w);
+ l--;
+ continue;
+ }
+ if (ch == '\b' || ch == KEY_BACKSPACE ||
+ ch == KEY_DC || ch == 127) {
+ /* We need to retake the current character; l++ will increase
+ * it, so counteract that with an l-- to remove the BACKSPACE. */
+ l--;
+ if (l >= 0) {
+ /* Erase the input char from the screen */
+ mvwaddch(sg->score_w, promptrow, promptcol + l, ' ');
+ /* And another l-- so that we overwrite the input char from
+ * inp on the next iteration of this loop. */
+ l--;
} else {
- mvwaddch(sg->score_w, promptrow, promptcol, ' ');
+ /* We're at the start of the line, so there was no input char
+ * to erase */
}
+ wrefresh(sg->score_w);
+ update_msgbox(sg->msg_w, "");
continue;
- } else if (isascii(ch)) {
- if (ch == '\n') {
- move[c] = ch;
- c++;
- break;
+ } else if (isalnum(ch)) {
+ if (ch == 'q' && l == 0) {
+ if (quitc) {
+ quit();
+ } else {
+ quitc = 1;
+ update_msgbox(sg->msg_w, "Enter 'q' again to quit");
+ mvwaddch(sg->score_w, promptrow, promptcol, ' ');
+ l--;
+ }
+ } else if (ch == '?' && l == 0) {
+ printinstrs(sg);
+ mvwaddch(sg->score_w, promptrow, promptcol, ' ');
+ l--;
} else {
- move[c] = ch;
- mvwaddch(sg->score_w, promptrow, promptcol + c, ch);
+ inp[l] = (char) ch;
+ mvwaddch(sg->score_w, promptrow, promptcol + l, ch);
}
+ wrefresh(sg->score_w);
+ } else if (ch == '\n') {
+ inp[l] = '\0';
+ break;
} else {
update_msgbox(sg->msg_w, "Unexpected non-ASCII input");
- return getmove(sg, move, length);
+ return getinput(sg, inp, length);
}
}
- move[0] = tolower(move[0]);
- move[2] = tolower(move[2]);
- if (!validcoords(move) ||
- (length == 5 && !validcoords(&move[2]))) {
+ /* l <= length-1 */
+ inp[l] = '\0';
+ return inp;
+}
+
+/*
+ * Prompts the user for a move with getinput, sanitizes it, and makes
+ * the move. It stores the move in *move, you can specify the maximum
+ * length l of the move to take, this should be at most the length of
+ * move. If mill is less than three or more than five, move should
+ * hold at least 5 characters. Returns pointer to the move.
+ */
+char *
+getmove(scrgame *sg, char *move, int l)
+{
+ int length, pieces, index, twmm = 0;
+ /* Do we only have 3 pieces, i.e. are we in skipping mode? */
+ pieces = sg->game->pieces[(int) sg->game->state];
+ if (l < 3 || 5 < l) {
+ if (sg->game->type == TWMM) {
+ /* We need to add one to our length to make up for the
+ * possibility of ne/nw/se/sw over n/e/s/w */
+ twmm = 1;
+ }
+ switch (sg->game->phase)
+ {
+ case 1:
+ /* In piece placing mode */
+ length = 3;
+ break;
+
+ case 2:
+ /* Of form d3s or d3sw */
+ length = 4 + twmm;
+ break;
+
+ case 3:
+ if (pieces == 3) {
+ /* Of form d3a1 */
+ length = 5;
+ } else {
+ /* Of form d3s or d3sw */
+ length = 4 + twmm;
+ }
+ break;
+
+ default:
+ length = 0;
+ /* NOTREACHED */
+ }
+ } else {
+ length = l;
+ }
+ move = getinput(sg, move, length);
+ move = lower(move);
+ if (!validcoords(sg->game, move) ||
+ /* Check the length to make sure we're not in mill mode */
+ (pieces == 3 && length != 3 && !validcoords(sg->game, &move[2]))) {
update_msgbox(sg->msg_w, "Invalid coordinates");
return getmove(sg, move, length);
}
- if (length == 4) {
- if (!(move[2] == NORTHC || move[2] == SOUTHC ||
- move[2] == EASTC || move[2] == WESTC)) {
+ /* If we'e in piece sliding stage. The length check makes sure we're
+ not in mill mode. */
+ if (sg->game->phase != 1 && pieces != 3 && length != 3) {
+ index = dirtoindex(&move[2]);
+ if (index == -1) {
update_msgbox(sg->msg_w, "Invalid direction");
return getmove(sg, move, length);
} else if (!(getpoint(sg->game, /* We already checked that move[2]
is safe */
- move)->n[dirtoindex(move[2])])) {
+ move)->n[index])) {
update_msgbox(sg->msg_w, "Impossible to move in that direction");
return getmove(sg, move, length);
}
@@ -628,38 +1115,80 @@ char
return move;
}
-struct point
-*getpoint(struct game *g, const char *coords)
+/*
+ * Given a coordinate array, retrieve the corresponding point by
+ * calling the appropriate function.
+ */
+point *
+getpoint(const game *g, const char *coords)
+{
+ switch (g->type)
+ {
+ case TMM:
+ return get3point(g, coords);
+
+ case NMM:
+ case TWMM:
+ default:
+ return get9point(g, coords);
+ }
+}
+
+/*
+ * Given a coordinate array, retrieve the corresponding point
+ */
+point *
+get3point(const game *g, const char *coords)
+{
+ int r;
+ if (validcoords(g, coords)) {
+ r = 2 + '1' - coords[1];
+ /* = 2 - (coords[0] - '1'); */
+ return g->board[r][coords[0] - 'a'];
+ }
+ return NULL;
+}
+
+
+/*
+ * Given a coordinate array, retrieve the corresponding point
+ */
+point *
+get9point(const game *g, const char *coords)
{
int r, c = 0;
- if (validcoords(coords)) {
+ if (validcoords(g, coords)) {
switch (coords[1])
- {
- case '4':
- if (coords[0] < 'd') {
- r = coords[0] - 'a';
- c = 6;
- } else {
- r = 'g' - coords[0];
- c = 2;
- }
- break;
+ {
+ case '4':
+ if (coords[0] < 'd') {
+ r = coords[0] - 'a';
+ c = 6;
+ } else {
+ r = 'g' - coords[0];
+ c = 2;
+ }
+ break;
- case '1': case '7':
- r = 0;
- break;
+ case '1': case '7':
+ r = 0;
+ break;
- case '2': case '6':
- r = 1;
- break;
+ case '2': case '6':
+ r = 1;
+ break;
- case '3': case '5':
- r = 2;
- break;
+ case '3': case '5':
+ r = 2;
+ break;
- default:
- errx(1, "Reached an unreachable branch in getpoint");
- }
+ default:
+ /* NOTREACHED */
+ endwin();
+ errx(1, "Reached an unreachable branch in getpoint");
+ r = 0; /* Shut up gcc complaining that r may be
+ used unitialized */
+ }
if (coords[1] != '4') {
if (coords[0] < 'd') {
c = coords[1] < '4' ? 5 : 7;
@@ -669,18 +1198,100 @@ struct point
c = coords[1] < '4' ? 3 : 1;
}
}
- return &(g->board[r][c]);
+ return g->board[r][c];
+ }
+ return NULL;
+}
+
+/*
+ * Place a piece corresponding to the current player at the coordinates coords.
+ */
+point *
+placepiece(const scrgame *sg, const char *coords)
+{
+ point *p = NULL;
+ if ((p = getpoint(sg->game, coords))) {
+ if (p->v == EMPTY) {
+ p->v = statechar(sg->game);
+ return p;
+ } else {
+ update_msgbox(sg->msg_w, "That location is already occupied, please try again.");
+ return NULL;
+ }
+ } else {
+ update_msgbox(sg->msg_w, "Invalid coordinates. Please try again.");
+ return NULL;
+ }
+}
+
+
+/*
+ * Move a piece from p in direction dir
+ */
+point *
+movepiece(point *p, const char *dir)
+{
+ int index;
+ index = dirtoindex(dir);
+ if (p->n[index] && p->n[index]->v == EMPTY) {
+ p->n[index]->v = p->v;
+ p->v = EMPTY;
+ return p->n[index];
}
return NULL;
}
+/*
+ * Move point p to the given position
+ */
+point *
+jumppiece(point *p, game *g, const char *position)
+{
+ point *ptr = getpoint(g, position);
+ if (ptr && ptr->v == EMPTY) {
+ ptr->v = p->v;
+ p->v = EMPTY;
+ return ptr;
+ }
+ return NULL;
+}
-struct point*
-phaseone(struct scrgame *sg)
+/*
+ * Remove a point by setting it to empty and decrementing the piece
+ * counts
+ */
+point *
+removepiece(game *g, point *p)
{
- struct point *p = NULL;
+ if (p && g) {
+ /* We let stupid people remove their own pieces */
+ if (p->v == BLACKC) {
+ g->pieces[BLACK]--;
+ } else if (p->v == WHITE) {
+ g->pieces[WHITE]--;
+ } else {
+ errno = ENOTSUP;
+ endwin();
+ errx(ENOTSUP, "Tried removing EMPTY. This should NEVER happen.");
+ }
+ p->v = EMPTY;
+ return p;
+ }
+ return NULL;
+}
+
+/*
+ * Phase one of the game
+ * Returns NULL if one player is guaranteed to have less than 3 pieces
+ * at the end of the phase, thus loosing.
+ */
+point *
+phaseone(scrgame *sg)
+{
+ point *p = NULL;
char coords[4];
- for (; sg->game->totalpieces < PIECECOUNT; sg->game->totalpieces++) {
+ int maxrem = 0;
+ for (; sg->game->totalpieces < 2 * sg->game->type; sg->game->totalpieces++) {
getmove(sg, coords, 0);
if (!(p = placepiece(sg, coords))) {
sg->game->totalpieces--;
@@ -699,16 +1310,30 @@ phaseone(struct scrgame *sg)
update_scorebox(sg->score_w, sg->game);
update_board(sg->board_w, sg->game);
update_msgbox(sg->msg_w, "");
+ maxrem = (2 * sg->game->type - sg->game->totalpieces) / 2;
+ if (sg->game->pieces[0] + maxrem < 3 ||
+ sg->game->pieces[1] + maxrem < 3) {
+ /* It's impossible for a player to place more than 3 pieces, abort */
+ return NULL;
+ }
+ }
+ if (sg->game->type == TMM) {
+ /* Three Man Morris doesn't have a phase 2 */
+ sg->game->phase = 3;
+ } else {
+ sg->game->phase = 2;
}
- sg->game->phase = 2;
update_scorebox(sg->score_w, sg->game);
return p;
}
-struct point*
-phasetwo(struct scrgame *sg)
+/*
+ * Phases two and three of the game
+ */
+point *
+phasetwothree(scrgame *sg)
{
- struct point *p = NULL;
+ point *p = NULL;
char coords[6];
while (sg->game->pieces[WHITE] >= 3 && sg->game->pieces[BLACK] >= 3) {
getmove(sg, coords, 0);
@@ -726,7 +1351,7 @@ phasetwo(struct scrgame *sg)
"That location is already occupied. Please try again");
continue;
}
- } else if (!(p = movepiece(p, coords[2]))) {
+ } else if (!(p = movepiece(p, &coords[2]))) {
update_msgbox(sg->msg_w,
"That location is already occupied. Please try again");
continue;
@@ -750,115 +1375,86 @@ phasetwo(struct scrgame *sg)
return p;
}
-int
-dirtoindex(const char dir)
-{
- switch (dir)
- {
- case NORTHC:
- return NORTH;
-
- case SOUTHC:
- return SOUTH;
-
- case EASTC:
- return EAST;
-
- case WESTC:
- return WEST;
-
- default:
- return -1;
- }
-}
-
-struct point*
-movepiece(struct point *p, const char dir)
+/*
+ * Applies tolower() to every element of a null-terminated string
+ * Will do bad things(TM) if the string is not null-terminated
+ */
+char*
+lower(char *s)
{
- int index;
- index = dirtoindex(dir);
- if (p->n[index] && p->n[index]->v == EMPTY) {
- p->n[index]->v = p->v;
- p->v = EMPTY;
- return p->n[index];
+ char *c = s;
+ for (c = s; *c; c++) {
+ *c = tolower(*c);
}
- return NULL;
+ return s;
}
-struct point*
-jumppiece(struct point *p, struct game *g, const char *position)
+/*
+ * The Big Cheese
+ */
+int
+main(int argc, char *argv[])
{
- struct point *ptr = getpoint(g, position);
- if (ptr && ptr->v == EMPTY) {
- ptr->v = p->v;
- p->v = EMPTY;
- return ptr;
+ scrgame *sg;
+ int c;
+ char *bn = basename(argv[0]);
+ if (!bn || errno) {
+ /* basename can return a NULL pointer, causing a segfault on
+ strncmp below */
+ errx(errno, "Something went wrong in determining the %s",
+ "filename by which nmm was called.");
}
- return NULL;
-}
-
-struct point*
-removepiece(struct game *g, struct point *p)
-{
- if (p && g) {
- /* We let stupid people remove their own pieces */
- if (p->v == BLACKC) {
- g->pieces[BLACK]--;
- } else if (p->v == WHITE) {
- g->pieces[WHITE]--;
+ if (argc != 1) {
+ errx(EINVAL, "%s doesn't take any arguments.", bn);
+ }
+ if ((sg = malloc(sizeof(*sg)))) {
+ if ((sg->game = malloc(sizeof(*sg->game)))) {
+ point *board;
+ if ((board = calloc(ROWS * COLS, sizeof(*board)))) {
+ int r;
+ for (r = 0; r < ROWS; r++) {
+ for (c = 0; c < COLS; c++) {
+ sg->game->board[r][c] = board++;
+ }
+ }
+ } else {
+ errx(errno, "Unable to allocate memory for the board");
+ }
} else {
- errno = ENOTSUP;
- errx(ENOTSUP, "Tried removing EMPTY. This should NEVER happen.");
+ errx(errno, "Unable to allocate memory for the game");
}
- p->v = EMPTY;
- return p;
+ } else {
+ errx(errno, "Unable to allocate memory");
}
- return NULL;
-}
-
-int
-main(void)
-{
- struct scrgame *sg;
- const char *const *instr;
- int c;
initscr();
+ cbreak();
keypad(stdscr, TRUE);
clear();
- refresh();
printw("Display instructions? (y/n) ");
+ refresh();
c = getch();
c = tolower(c);
if (c == 'y') {
- clear();
- for (instr = instructions; *instr; instr++) {
- printw("%s", *instr);
- }
- refresh();
- getch();
+ printinstrs(sg);
}
clear();
- refresh();
noecho();
- sg = malloc(sizeof(struct scrgame));
- if (sg) {
- sg->game = malloc(sizeof(struct game));
- if (sg->game) {
- mvprintw(25, 57, "nmm version %s", VERSION);
- for (;;) {
- initall(sg);
- refresh();
- phaseone(sg);
- phasetwo(sg);
- if (!gameend(sg)) {
- break;
- }
- }
- }
+ refresh();
+ if (strncmp("tmm", bn, 3) == 0) {
+ c = TMM;
+ } else if (strncmp("twmm", bn, 4) == 0) {
+ c = TWMM;
} else {
+ c = NMM;
+ }
+ for (;;) {
+ initall(sg, c);
refresh();
- endwin();
- errx(ENOMEM, "Unable to allocate enough memory.");
+ phaseone(sg);
+ phasetwothree(sg);
+ if (!gameend(sg)) {
+ break;
+ }
}
refresh();
endwin();