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)); }