# HG changeset patch # User Mike Becker # Date 1402919131 -7200 # Node ID 41017d0a72c512a6124570816cc0bb2bfd391a21 # Parent 02c509a44e98f5e2cc8963d7f6cf9ddef1faa40e added pgn parser and writer (without comment support yet) + minor refactorings diff -r 02c509a44e98 -r 41017d0a72c5 src/chess/Makefile --- a/src/chess/Makefile Wed Jun 11 16:54:20 2014 +0200 +++ b/src/chess/Makefile Mon Jun 16 13:45:31 2014 +0200 @@ -38,6 +38,7 @@ SRC += queen.c SRC += king.c SRC += rules.c +SRC += pgn.c OBJ = $(SRC:%.c=$(BUILDDIR)/release/%$(OBJ_EXT)) OBJ_D = $(SRC:%.c=$(BUILDDIR)/debug/%$(OBJ_EXT)) diff -r 02c509a44e98 -r 41017d0a72c5 src/chess/chess.h --- a/src/chess/chess.h Wed Jun 11 16:54:20 2014 +0200 +++ b/src/chess/chess.h Mon Jun 16 13:45:31 2014 +0200 @@ -34,3 +34,4 @@ #include "bishop.h" #include "queen.h" #include "king.h" +#include "pgn.h" diff -r 02c509a44e98 -r 41017d0a72c5 src/chess/pgn.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/chess/pgn.c Mon Jun 16 13:45:31 2014 +0200 @@ -0,0 +1,196 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 Mike Becker. All rights reserved. + * + * 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 "pgn.h" +#include +#include +#include + +int read_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo) { + int c, i; + + char result[8]; + + char tagkey[32]; + char tagvalue[128]; + + // read tag pairs + _Bool readmoves = 0; + while (!readmoves) { + while (isspace(c = fgetc(stream))); + if (c == '1') { + readmoves = 1; + break; + } + if (c != '[') { + return EXIT_FAILURE; + } + while (isspace(c = fgetc(stream))); + i = 0; + do { + tagkey[i++] = c; + } while (!isspace(c = fgetc(stream))); + tagkey[i] = '\0'; + while (isspace(c = fgetc(stream))); + if (c != '"') { + return EXIT_FAILURE; + } + i = 0; + while ((c = fgetc(stream)) != '"') { + if (c == '\n') { + return EXIT_FAILURE; + } + tagvalue[i++] = c; + } + tagvalue[i] = '\0'; + if (fgetc(stream) != ']') { + return EXIT_FAILURE; + } + + if (strcmp("Result", tagkey) == 0) { + memcpy(result, tagvalue, 8); + } + } + + // read moves + if (fgetc(stream) != '.') { + return EXIT_FAILURE; + } + + char movestr[10]; + Move move; + uint8_t curcol = WHITE; + + while (readmoves) { + // move + while (isspace(c = fgetc(stream))); + i = 0; + do { + movestr[i++] = c; + if (i >= 10) { + return EXIT_FAILURE; + } + } while (!isspace(c = fgetc(stream))); + movestr[i] = '\0'; + if (eval_move(gamestate, movestr, &move, curcol) + != VALID_MOVE_SYNTAX) { + return EXIT_FAILURE; + } + if (validate_move(gamestate, &move) != VALID_MOVE_SEMANTICS) { + return EXIT_FAILURE; + } + apply_move(gamestate, &move); + + // TODO: parse comments + while (isspace(c = fgetc(stream))); + + // end of game data encountered + if (c == EOF) { + break; + } + if (c == '1' || c == '0') { + c = fgetc(stream); + if (c == '-') { + gamestate->resign = !gamestate->checkmate; + break; + } else if (c == '/') { + gamestate->remis = !gamestate->stalemate; + break; + } else { + // oops, it was a move number, go back! + fseek(stream, -1, SEEK_CUR); + } + } + + // we have eaten the next valuable byte, so go back + fseek(stream, -1, SEEK_CUR); + + // skip move number after black move + if (curcol == BLACK) { + while (isdigit(c = fgetc(stream))); + if (c != '.') { + return EXIT_FAILURE; + } + } + curcol = opponent_color(curcol); + } + + return EXIT_SUCCESS; +} + +size_t write_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo) { + // TODO: tag pairs + size_t bytes = 0; + + // Result + char *result; + if (gamestate->stalemate || gamestate->remis) { + result = "1/2-1/2"; + } else if (gamestate->checkmate || gamestate->resign) { + if (gamestate->lastmove) { + result = (gamestate->lastmove->move.piece & COLOR_MASK) == WHITE ? + "1-0" : "0-1"; + } else { + result = "0-1"; + } + } else { + result = "*"; + } + fprintf(stream, "[Result \"%s\"]\n\n", result); + + // moves + int i = 1; + for (MoveList *movelist = gamestate->movelist ; + movelist ; movelist = movelist->next) { + + if (++i % 2 == 0) { + fprintf(stream, "%d. %s", i/2, movelist->move.string); + } else { + fprintf(stream, " %s", movelist->move.string); + } + + // TODO: move time and maybe other comments + + // line break every 10 moves + if (i % 20) { + fputc(' ', stream); + } else { + fputc('\n', stream); + } + } + + if (result[0] == '*') { + fputc('\n', stream); + } else { + fprintf(stream, "%s\n", result); + } + + + return bytes; +} diff -r 02c509a44e98 -r 41017d0a72c5 src/chess/pgn.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/chess/pgn.h Mon Jun 16 13:45:31 2014 +0200 @@ -0,0 +1,50 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 Mike Becker. All rights reserved. + * + * 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. + * + */ + + +#ifndef PGN_H +#define PGN_H + +#include "rules.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int read_pgn(FILE *stream, GameState *gamestate, GameInfo *gameinfo); +size_t write_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo); + + +#ifdef __cplusplus +} +#endif + +#endif /* PGN_H */ + diff -r 02c509a44e98 -r 41017d0a72c5 src/chess/rules.c --- a/src/chess/rules.c Wed Jun 11 16:54:20 2014 +0200 +++ b/src/chess/rules.c Mon Jun 16 13:45:31 2014 +0200 @@ -44,6 +44,22 @@ return simulation; } +void gamestate_init(GameState *gamestate) { + memset(gamestate, 0, sizeof(GameState)); + + Board initboard = { + {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, + {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, + {0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0}, + {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN}, + {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK} + }; + memcpy(gamestate->board, initboard, sizeof(Board)); +} + void gamestate_cleanup(GameState *gamestate) { MoveList *elem; elem = gamestate->movelist; @@ -587,7 +603,7 @@ } } -int eval_move(GameState *gamestate, char *mstr, Move *move) { +int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color) { memset(move, 0, sizeof(Move)); move->fromfile = POS_UNSPECIFIED; move->fromrow = POS_UNSPECIFIED; @@ -612,7 +628,7 @@ if (!move->promotion) { return INVALID_MOVE_SYNTAX; } else { - move->promotion |= gamestate->mycolor; + move->promotion |= color; len -= 2; mstr[len] = 0; } @@ -629,7 +645,7 @@ move->piece = KING; move->fromfile = fileidx('e'); move->tofile = fileidx('g'); - move->fromrow = move->torow = gamestate->mycolor == WHITE ? 0 : 7; + move->fromrow = move->torow = color == WHITE ? 0 : 7; } else { /* move (e.g. "Nf3") */ move->piece = getpiece(mstr[0]); @@ -664,7 +680,7 @@ move->piece = KING; move->fromfile = fileidx('e'); move->tofile = fileidx('c'); - move->fromrow = move->torow = gamestate->mycolor == WHITE ? 0 : 7; + move->fromrow = move->torow = color == WHITE ? 0 : 7; } else { move->piece = getpiece(mstr[0]); if (mstr[2] == 'x') { @@ -701,12 +717,12 @@ if (move->piece) { if (move->piece == PAWN - && move->torow == (gamestate->mycolor==WHITE?7:0) + && move->torow == (color==WHITE?7:0) && !move->promotion) { return NEED_PROMOTION; } - move->piece |= gamestate->mycolor; + move->piece |= color; if (move->fromfile == POS_UNSPECIFIED || move->fromrow == POS_UNSPECIFIED) { return getlocation(gamestate, move); diff -r 02c509a44e98 -r 41017d0a72c5 src/chess/rules.h --- a/src/chess/rules.h Wed Jun 11 16:54:20 2014 +0200 +++ b/src/chess/rules.h Mon Jun 16 13:45:31 2014 +0200 @@ -110,11 +110,12 @@ typedef struct { Board board; - uint8_t mycolor; MoveList* movelist; MoveList* lastmove; _Bool checkmate; _Bool stalemate; + _Bool remis; + _Bool resign; } GameState; #define opponent_color(color) ((color)==WHITE?BLACK:WHITE) @@ -141,6 +142,16 @@ #define fileidx_s(c) (isfile(c)?fileidx(c):POS_UNSPECIFIED) #define rowidx_s(c) (isrow(c)?rowidx(c):POS_UNSPECIFIED) +#define is_game_running(gamestate) !((gamestate)->checkmate || \ + (gamestate)->resign || (gamestate)->stalemate || (gamestate)->remis) + + +/** + * Initializes a game state and prepares the chess board. + * @param gamestate the game state to initialize + */ +void gamestate_init(GameState *gamestate); + /** * Cleans up a game state and frees the memory for the movement list. * @param gamestate the game state to clean up @@ -276,9 +287,10 @@ * @param gamestate the current game state * @param mstr the input string to parse * @param move a pointer to object where the move data shall be stored + * @param color the color of the player to evaluate the move for * @return status code (see macros in this file for the list of codes) */ -int eval_move(GameState *gamestate, char *mstr, Move *move); +int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color); /** * Validates move by applying chess rules. diff -r 02c509a44e98 -r 41017d0a72c5 src/game.c --- a/src/game.c Wed Jun 11 16:54:20 2014 +0200 +++ b/src/game.c Mon Jun 16 13:45:31 2014 +0200 @@ -35,6 +35,8 @@ #include #include #include +#include +#include static const uint8_t boardx = 10, boardy = 10; static int inputy = 21; /* should be overridden on game startup */ @@ -69,7 +71,7 @@ return 0; } -static void draw_board(GameState *gamestate) { +static void draw_board(GameState *gamestate, uint8_t perspective) { for (uint8_t y = 0 ; y < 8 ; y++) { for (uint8_t x = 0 ; x < 8 ; x++) { uint8_t col = gamestate->board[y][x] & COLOR_MASK; @@ -89,8 +91,8 @@ ) ); - int cy = gamestate->mycolor == WHITE ? boardy-y : boardy-7+y; - int cx = gamestate->mycolor == WHITE ? boardx+x*3 : boardx+21-x*3; + int cy = perspective == WHITE ? boardy-y : boardy-7+y; + int cx = perspective == WHITE ? boardx+x*3 : boardx+21-x*3; mvaddch(cy, cx, ' '); mvaddch(cy, cx+1, piecec); mvaddch(cy, cx+2, ' '); @@ -99,8 +101,8 @@ attrset(A_NORMAL); for (uint8_t i = 0 ; i < 8 ; i++) { - int x = gamestate->mycolor == WHITE ? boardx+i*3+1 : boardx+22-i*3; - int y = gamestate->mycolor == WHITE ? boardy-i : boardy-7+i; + int x = perspective == WHITE ? boardx+i*3+1 : boardx+22-i*3; + int y = perspective == WHITE ? boardy-i : boardy-7+i; mvaddch(boardy+1, x, 'a'+i); mvaddch(y, boardx-2, '1'+i); } @@ -122,17 +124,15 @@ printw("%d. ", logi / 2); } - if (logelem) { - addstr(logelem->move.string); - if (!logelem->next) { - if (gamestate->stalemate) { - addstr(" stalemate"); - } + addstr(logelem->move.string); + if (!logelem->next) { + if (gamestate->stalemate) { + addstr(" stalemate"); } - addch(' '); - - logelem = logelem->next; } + addch(' '); + + logelem = logelem->next; } } @@ -167,8 +167,30 @@ } } -#define MOVESTR_BUFLEN 8 -static int domove_singlemachine(GameState *gamestate, GameInfo *gameinfo) { +static void save_pgn(GameState *gamestate, GameInfo *gameinfo) { + printw("Filename: "); + clrtoeol(); + refresh(); + + char filename[64]; + int y = getcury(stdscr); + if (getnstr(filename, 64) == OK) { + move(y, 0); + FILE *file = fopen(filename, "w"); + if (file) { + write_pgn(file, gamestate, gameinfo); + fclose(file); + printw("File saved."); + } else { + printw("Can't write to file (%s).", strerror(errno)); + } + clrtoeol(); + } +} + +#define MOVESTR_BUFLEN 10 +static int domove_singlemachine(GameState *gamestate, + GameInfo *gameinfo, uint8_t curcolor) { size_t bufpos = 0; @@ -183,29 +205,33 @@ move(inputy, 0); printw( "Use chess notation to enter your move.\n" - "Or type 'resign' to resign or 'remis' to end with remis.\n\n" + "Or use a command: remis, resign, savepgn\n\n" "Type your move: "); clrtoeol(); if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { + gamestate->resign = 1; printw("%s resigned!", - gamestate->mycolor==WHITE?"White":"Black"); - clrtoeol(); + curcolor==WHITE?"White":"Black"); + clrtobot(); refresh(); return 1; } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { + gamestate->remis = 1; printw("Game ends remis."); - clrtoeol(); + clrtobot(); refresh(); return 1; + } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { + save_pgn(gamestate, gameinfo); } else { Move move; - int eval_result = eval_move(gamestate, movestr, &move); - switch (eval_result) { + int result = eval_move(gamestate, movestr, &move, curcolor); + switch (result) { case VALID_MOVE_SYNTAX: - eval_result = validate_move(gamestate, &move); - if (eval_result == VALID_MOVE_SEMANTICS) { + result = validate_move(gamestate, &move); + if (result == VALID_MOVE_SEMANTICS) { apply_move(gamestate, &move); if (gamestate->checkmate) { printw("Checkmate!"); @@ -219,11 +245,11 @@ return 0; } } else { - eval_move_failed_msg(eval_result); + eval_move_failed_msg(result); } break; default: - eval_move_failed_msg(eval_result); + eval_move_failed_msg(result); } clrtoeol(); } @@ -231,7 +257,8 @@ } } -static int sendmove(GameState *gamestate, GameInfo *gameinfo, int opponent) { +static int sendmove(GameState *gamestate, GameInfo *gameinfo, + int opponent, uint8_t mycolor) { size_t bufpos = 0; char movestr[MOVESTR_BUFLEN]; @@ -249,23 +276,26 @@ if (remisrejected) { printw( "Use chess notation to enter your move.\n" - "Remis offer rejected - type 'resign' to resign. \n\n" + "Remis offer rejected \n\n" "Type your move: "); } else { printw( "Use chess notation to enter your move.\n" - "Or type 'resign' to resign or 'remis' to offer remis.\n\n" + "Or use a command: remis, resign, savepgn\n\n" "Type your move: "); } clrtoeol(); if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) { if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) { + gamestate->resign = 1; printw("You resigned!"); clrtoeol(); refresh(); net_send_code(opponent, NETCODE_RESIGN); return 1; + } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) { + save_pgn(gamestate, gameinfo); } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) { if (!remisrejected) { net_send_code(opponent, NETCODE_REMIS); @@ -273,6 +303,7 @@ refresh(); code = net_recieve_code(opponent); if (code == NETCODE_ACCEPT) { + gamestate->remis = 1; printw("\rRemis accepted!"); clrtoeol(); refresh(); @@ -288,7 +319,7 @@ } } else { Move move; - int eval_result = eval_move(gamestate, movestr, &move); + int eval_result = eval_move(gamestate, movestr, &move, mycolor); switch (eval_result) { case VALID_MOVE_SYNTAX: net_send_data(opponent, NETCODE_MOVE, &move, @@ -353,6 +384,8 @@ timeout.tv_sec = 0; timeout.tv_usec = 1e5; + // TODO: allow commands + int result = select(opponent+1, &readfds, NULL, NULL, &timeout); if (result == -1) { printw("\rCannot perform asynchronous network IO"); @@ -369,6 +402,7 @@ clrtoeol(); return 1; case NETCODE_RESIGN: + gamestate->resign = 1; printw("\rYour opponent resigned!"); clrtoeol(); return 1; @@ -379,6 +413,7 @@ case NETCODE_REMIS: if (prompt_yesno( "\rYour opponent offers remis - do you accept")) { + gamestate->remis = 1; printw("\rRemis accepted!"); clrtoeol(); net_send_code(opponent, NETCODE_ACCEPT); @@ -421,39 +456,71 @@ } } -static void init_board(GameState *gamestate) { - Board initboard = { - {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, - {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, - {0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, - {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN}, - {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK} - }; - memcpy(gamestate->board, initboard, sizeof(Board)); +static void post_game(GameState *gamestate, GameInfo *gameinfo) { + move(0,0); + draw_board(gamestate, WHITE); + + // TODO: network connection is still open here - think about it! + + mvaddstr(getmaxy(stdscr)-1, 0, + "Press 'q' to quit or 's' to save a PGN file..."); + refresh(); + flushinp(); + + noecho(); + int c; + do { + c = getch(); + if (c == 's') { + addch('\r'); + echo(); + save_pgn(gamestate, gameinfo); + addstr(" Press 'q' to quit..."); + noecho(); + } + } while (c != 'q'); + echo(); + + gamestate_cleanup(gamestate); } void game_start_singlemachine(Settings *settings) { inputy = getmaxy(stdscr) - 6; GameState gamestate; - memset(&gamestate, 0, sizeof(GameState)); - init_board(&gamestate); - gamestate.mycolor = WHITE; + gamestate_init(&gamestate); + uint8_t curcol = WHITE; + + if (settings->continuepgn) { + FILE *pgnfile = fopen(settings->continuepgn, "r"); + if (pgnfile) { + int result = read_pgn(pgnfile, &gamestate, &(settings->gameinfo)); + fclose(pgnfile); + if (result != EXIT_SUCCESS) { + addstr("Invalid PGN file content.\n"); + return; + } + if (!is_game_running(&gamestate)) { + addstr("Game has ended. Use -S to analyze it.\n"); + return; + } + curcol = opponent_color(gamestate.lastmove->move.piece&COLOR_MASK); + } else { + printw("Can't read PGN file (%s)\n", strerror(errno)); + return; + } + } _Bool running; do { clear(); - draw_board(&gamestate); - running = !domove_singlemachine(&gamestate, &(settings->gameinfo)); - gamestate.mycolor = opponent_color(gamestate.mycolor); + draw_board(&gamestate, curcol); + running = !domove_singlemachine(&gamestate, + &(settings->gameinfo), curcol); + curcol = opponent_color(curcol); } while (running); - move(0,0); - draw_board(&gamestate); - gamestate_cleanup(&gamestate); + post_game(&gamestate, &(settings->gameinfo)); } void game_start(Settings *settings, int opponent) { @@ -461,26 +528,23 @@ _Bool myturn = is_server(settings) == (settings->gameinfo.servercolor == WHITE); + uint8_t mycolor = myturn ? WHITE : BLACK; GameState gamestate; - memset(&gamestate, 0, sizeof(GameState)); - init_board(&gamestate); - gamestate.mycolor = myturn ? WHITE:BLACK; + gamestate_init(&gamestate); _Bool running; do { clear(); - draw_board(&gamestate); + draw_board(&gamestate, mycolor); if (myturn) { - running = !sendmove(&gamestate, &(settings->gameinfo), opponent); + running = !sendmove(&gamestate, &(settings->gameinfo), + opponent, mycolor); } else { running = !recvmove(&gamestate, &(settings->gameinfo), opponent); } myturn ^= TRUE; } while (running); - move(0,0); - draw_board(&gamestate); - - gamestate_cleanup(&gamestate); + post_game(&gamestate, &(settings->gameinfo)); } diff -r 02c509a44e98 -r 41017d0a72c5 src/main.c --- a/src/main.c Wed Jun 11 16:54:20 2014 +0200 +++ b/src/main.c Mon Jun 16 13:45:31 2014 +0200 @@ -41,7 +41,7 @@ uint8_t timeunit = 60; size_t len; - for (int opt ; (opt = getopt(argc, argv, "a:bc:hp:rst:")) != -1 ;) { + for (int opt ; (opt = getopt(argc, argv, "a:bc:hp:rsS:t:")) != -1 ;) { switch (opt) { case 'c': settings->continuepgn = optarg; @@ -55,6 +55,9 @@ case 's': settings->singlemachine = 1; break; + case 'S': + settings->analyzepgn = optarg; + break; case 't': case 'a': len = strlen(optarg); @@ -103,6 +106,24 @@ return 1; } + + if (settings->continuepgn) { + if (settings->serverhost) { + fprintf(stderr, "Can't continue a game when joining a server.\n"); + return 1; + } + if (settings->analyzepgn) { + fprintf(stderr, "The options -c and -S are mutually exclusive\n"); + return 1; + } + // TODO: implement + if (!settings->singlemachine) { + fprintf(stderr, "Game continuation currently not supported for " + "network games.\n"); + return 1; + } + } + return 0; } @@ -111,7 +132,6 @@ memset(&settings, 0, sizeof(Settings)); settings.gameinfo.servercolor = WHITE; settings.port = "27015"; - settings.continuepgn = NULL; return settings; } @@ -154,17 +174,16 @@ "Usage: terminal-chess [OPTION]... [HOST]\n" "Starts/joins a network chess game\n" "\nGeneral options\n" +" -c Continue the specified game\n" " -h This help page\n" " -p TCP port to use (default: 27015)\n" +// TODO: implement and activate feature +//" -S Compute and print statistics for the specified game\n" "\nServer options\n" " -a