src/game.c

changeset 129
189c7c77aaab
parent 128
ce38ee9bc3af
child 130
3fc6b1d6cbe9
--- a/src/game.c	Tue May 26 15:29:00 2026 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,759 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 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 "game.h"
-#include "network.h"
-#include "input.h"
-#include "colors.h"
-#include "chess/rules.h"
-#include "chess/pgn.h"
-#include <ncurses.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-
-static const uint8_t boardx = 4, boardy = 10;
-static int inputy = 21; /* should be overridden on game startup */
-
-static int timecontrol(GameState *gamestate, GameInfo *gameinfo) {
-    if (gameinfo->timecontrol) {
-        uint16_t white = remaining_movetime(gameinfo, gamestate, WHITE);
-        uint16_t black = remaining_movetime(gameinfo, gamestate, BLACK);
-        char clkstr[16];
-        bool always_hours = gameinfo->time >= 3600;
-        print_clk(white, clkstr, always_hours);
-        mvprintw(boardy+4, boardx-1, "White time: %s", clkstr);
-        print_clk(black, clkstr, always_hours);
-        mvprintw(boardy+5, boardx-1, "Black time: %s", clkstr);
-    
-        if (white == 0) {
-            move(inputy, 0);
-            printw("Time is over - Black wins!");
-            clrtobot();
-            refresh();
-            return 1;
-        }
-        if (black == 0) {
-            move(inputy, 0);
-            printw("Time is over - White wins!");
-            clrtobot();
-            refresh();
-            return 1;
-        }
-    }
-    
-    return 0;
-}
-
-static void draw_board(GameState *gamestate,
-		       uint8_t perspective,
-		       bool unicode) {
-    char fen[90];
-    compute_fen(fen, gamestate);
-    mvaddstr(0, 0, fen);
-    
-    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;
-            uint8_t piece = gamestate->board[y][x];
-            char piecestr[5];
-            if (piece) {
-                if (unicode) {
-                    char* uc = getpieceunicode(piece);
-                    strncpy(piecestr, uc, 5);
-                } else {
-                    piecestr[0] = (piece & PIECE_MASK) == PAWN
-                                      ? 'P' : getpiecechr(piece);
-                    piecestr[1] = '\0';
-                }
-            } else {
-                piecestr[0] = ' ';
-                piecestr[1] = '\0';
-            }
-            
-            bool boardblack = (y&1)==(x&1);
-            attrset((col==WHITE ? A_BOLD : A_DIM)|
-                COLOR_PAIR(col == WHITE ?
-                    (boardblack ? COL_WB : COL_WW) :
-                    (boardblack ? COL_BB : COL_BW)
-                )
-            );
-            
-            int cy = perspective == WHITE ? boardy-y : boardy-7+y;
-            int cx = perspective == WHITE ? boardx+x*3 : boardx+21-x*3;
-            mvprintw(cy, cx, " %s ", piecestr);
-        }
-    }
-    
-    attrset(A_NORMAL);
-    for (uint8_t i = 0 ; i < 8 ; 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);
-    }
-    
-    /* move log */
-    uint8_t logy = 2;
-    const uint8_t logx = boardx + 28;
-    move(logy, logx);
-
-    /* count full moves */
-    unsigned int logi = 0;
-
-    /* wrap log after 45 moves */
-    while (gamestate->movecount/6-logi/3 >= 15) {
-        logi++;
-    }
-    
-    for (unsigned mi = logi*2 ; mi < gamestate->movecount ; mi++) {
-        bool iswhite = mi % 2 == 0;
-        if (iswhite) {
-            logi++;
-            printw("%d. ", logi);
-        }
-
-        addstr(gamestate->moves[mi].string);
-        if (!iswhite && logi%3 == 0) {
-            move(++logy, logx);
-        } else {
-            addch(' ');
-        }
-    }
-}
-
-static void eval_move_failed_msg(int code) {
-    switch (code) {
-    case AMBIGUOUS_MOVE:
-        printw("Ambiguous move - please specify the piece to move.");
-        break;
-    case INVALID_POSITION:
-        printw("No piece can be moved this way.");
-        break;
-    case NEED_PROMOTION:
-        printw("You need to promote the pawn (append \"=Q\" e.g.)!");
-        break;
-    case KING_IN_CHECK:
-        printw("Your king is in check!");
-        break;
-    case PIECE_PINNED:
-        printw("This piece is pinned!");
-        break;
-    case INVALID_MOVE_SYNTAX:
-        printw("Can't interpret move - please use algebraic notation.");
-        break;
-    case RULES_VIOLATED:
-        printw("Move does not comply chess rules.");
-        break;
-    case KING_MOVES_INTO_CHECK:
-        printw("Can't move the king into a check position.");
-        break;
-    default:
-        printw("Unknown move parser error.");
-    }
-}
-
-static void save_pgn(GameState *gamestate, GameInfo *gameinfo) {
-    int y = getcury(stdscr);
-
-    /* ask for player names */
-    {
-        char pname[PLAYER_NAME_BUFLEN];
-        printw("\rWhite's name (%s): ", pgn_player_name(gamestate, WHITE));
-        clrtoeol();
-        if (getnstr(pname, PLAYER_NAME_BUFLEN) == OK && pname[0] != '\0') {
-            strncpy(gamestate->wname, pname, PLAYER_NAME_BUFLEN);
-        }
-        move(y, 0);
-        printw("\rBlack's name (%s): ", pgn_player_name(gamestate, BLACK));
-        clrtoeol();
-        if (getnstr(pname, PLAYER_NAME_BUFLEN) == OK && pname[0] != '\0') {
-            strncpy(gamestate->bname, pname, PLAYER_NAME_BUFLEN);
-        }
-        move(y, 0);
-    }
-
-    bool export_comments = prompt_yesno("Export with comments");
-
-    printw("\rFilename: ");
-    clrtoeol();
-    
-    char filename[64];
-    if (getnstr(filename, 64) == OK && filename[0] != '\0') {
-        move(y, 0);
-        FILE *file = fopen(filename, "w");
-        if (file) {
-            write_pgn(file, gamestate, gameinfo, export_comments);
-            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;
-    char movestr[MOVESTR_BUFLEN];
-    
-    flushinp();
-    while (1) {
-        const char *curcolorstr = curcolor == WHITE ? "White" : "Black";
-        if (timecontrol(gamestate, gameinfo)) {
-            return 1;
-        }
-        move(inputy, 0);
-        printw(
-            "Use chess notation to enter your move.\n"
-            "Or use a command: remis, resign, savepgn\n\n"
-            "%s to move: ", curcolorstr);
-        clrtoeol();
-        
-        if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) {
-            if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) {
-                if (curcolor == WHITE) {
-                    gamestate->wresign = true;
-                } else {
-                    gamestate->bresign = true;
-                }
-                return 1;
-            } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) {
-                gamestate->remis = true;
-                return 1;
-            } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) {
-                save_pgn(gamestate, gameinfo);
-            } else if (movestr[0] == 0) {
-                /* ignore empty move strings and ask again */
-            } else {
-                Move move;
-                int result = eval_move(gamestate, movestr, &move, curcolor);
-                if (result == VALID_MOVE_SYNTAX) {
-                    result = validate_move(gamestate, &move);
-                    if (result == VALID_MOVE_SEMANTICS) {
-                        apply_move(gamestate, &move);
-                        if (gamestate->checkmate) {
-                            return 1;
-                        } else if (gamestate->stalemate) {
-                            return 1;
-                        } else {
-                            return 0;
-                        }
-                    } else {
-                        eval_move_failed_msg(result);
-                    }
-                } else {
-                    eval_move_failed_msg(result);
-                }
-                clrtoeol();
-            }
-        }
-    }
-}
-
-static int sendmove(GameState *gamestate, GameInfo *gameinfo,
-        int opponent, uint8_t mycolor) {
-    
-    size_t bufpos = 0;
-    char movestr[MOVESTR_BUFLEN];
-    bool remis_rejected = false;
-    bool remis_suggested = false;
-    bool resign_suggested = false;
-    bool use_premove = false;
-    uint8_t code;
-
-    if (*gamestate->premove) {
-        use_premove = true;
-        const unsigned mlen = sizeof(gamestate->premove);
-        strncpy(movestr, gamestate->premove, mlen);
-        movestr[mlen] = '\0';
-        memset(gamestate->premove, 0, mlen);
-    }
-    
-    flushinp();
-    while (1) {
-        if (timecontrol(gamestate, gameinfo)) {
-            net_send_code(opponent, NETCODE_TIMEOVER);
-            return 1;
-        }
-        
-        move(inputy, 0);
-        printw("Use chess notation to enter your move.\n");
-        if (resign_suggested) {
-            if (remis_suggested) {
-                printw("The opponent asks you to resign or accept remis. \n\n");
-            } else {
-                printw("The opponent asks you to resign.                 \n\n");
-            }
-        } else if (remis_suggested) {
-            printw("The opponent offers remis. Type remis to accept. \n\n");
-        } else if (remis_rejected) {
-            printw("Remis offer rejected.                            \n\n");
-        } else {
-            printw("Or use a command: remis, resign, savepgn         \n\n");
-        }
-        printw("Type your move: ");
-        clrtoeol();
-
-        /* check if the opponent sent us something */
-        code = net_recieve_code_async(opponent);
-        switch (code) {
-            case NETCODE_REMIS:
-                remis_suggested = true;
-                break;
-            case NETCODE_TAUNT:
-                resign_suggested = true;
-                break;
-            case NETCODE_RESIGN:
-                if (mycolor == WHITE) {
-                    gamestate->bresign = true;
-                } else {
-                    gamestate->wresign = true;
-                }
-                return 1;
-            case NETCODE_CONNLOST:
-                gamestate->ragequit = true;
-                return 1;
-            case NETCODE_ERROR:
-                printw("\rCannot perform asynchronous network IO");
-                cbreak(); getch();
-                exit(EXIT_FAILURE);
-            case NETCODE_AGAIN:
-                /* try again */
-                break;
-            default:
-                printw("\nThe opponent sent an invalid network pacakge.");
-        }
-
-        /* read move */
-        if (use_premove || asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) {
-            bool was_premove = use_premove;
-            use_premove = false;
-            if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) {
-                if (mycolor == WHITE) {
-                    gamestate->wresign = true;
-                } else {
-                    gamestate->bresign = true;
-                }
-                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 (remis_suggested) {
-                    net_send_code(opponent, NETCODE_REMIS);
-                    gamestate->remis = true;
-                    return 1;
-                } if (!remis_rejected) {
-                    net_send_code(opponent, NETCODE_REMIS);
-                    printw("Remis offer sent - waiting for acceptance...");
-                    refresh();
-                    code = net_recieve_code(opponent);
-                    if (code == NETCODE_ACCEPT) {
-                        gamestate->remis = true;
-                        return 1;
-                    } else if (code == NETCODE_CONNLOST) {
-                        gamestate->ragequit = true;
-                        return 1;
-                    } else {
-                        remis_rejected = true;
-                    }
-                }
-            } else if (movestr[0] == 0) {
-                /* ignore empty move strings and ask again */
-            } else {
-                Move move;
-                int eval_result = eval_move(gamestate, movestr, &move, mycolor);
-                switch (eval_result) {
-                case VALID_MOVE_SYNTAX:
-                    net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move));
-                    code = net_recieve_code(opponent);
-                    move.check = code == NETCODE_CHECK ||
-                        code == NETCODE_CHECKMATE;
-                    gamestate->checkmate = code == NETCODE_CHECKMATE;
-                    gamestate->stalemate = code == NETCODE_STALEMATE;
-                    if (code == NETCODE_DECLINE) {
-                        uint32_t reason;
-                        net_recieve_data(opponent, &reason, sizeof(uint32_t));
-                        reason = ntohl(reason);
-                        eval_move_failed_msg(reason);
-                    } else if (code == NETCODE_ACCEPT
-                            || code == NETCODE_CHECK
-                            || code == NETCODE_CHECKMATE
-                            || code == NETCODE_STALEMATE) {
-                        apply_move(gamestate, &move);
-                        if (gamestate->checkmate || gamestate->stalemate) {
-                            return 1;
-                        } else {
-                            return 0;
-                        }
-                    } else if (code == NETCODE_CONNLOST) {
-                        printw("Your opponent left the game.");
-                        return 1;
-                    } else {
-                        printw("Invalid network response.");
-                    }
-                    break;
-                default:
-                    if (was_premove) {
-                        printw("\nThe prepared move could not be executed.");
-                    } else {
-                        eval_move_failed_msg(eval_result);
-                    }
-                }
-                clrtoeol();
-            }
-        }
-    }
-}
-
-static int recvmove(GameState *gamestate, GameInfo *gameinfo,
-        int opponent, uint8_t mycolor) {
-    memset(gamestate->premove, 0, sizeof(gamestate->premove));
-
-    size_t bufpos = 0;
-    char movestr[MOVESTR_BUFLEN];
-    bool remis_suggested = false, resign_suggested = false;
-    while (1) {
-        timecontrol(gamestate, gameinfo);
-        
-        move(inputy, 0);
-        printw("Waiting for opponent. Use chess notation to prepare a move.\n");
-        if (*gamestate->premove) {
-            printw("Current pre-move: %s                             \n\n",
-                gamestate->premove);
-        } else if (remis_suggested && !resign_suggested) {
-            printw("Suggested remis.                                 \n\n");
-        } else if (resign_suggested) {
-            if (remis_suggested) {
-                printw("Suggested to resign or at least to accept remis. \n\n");
-            } else {
-                printw("Suggested to resign.                             \n\n");
-            }
-        } else {
-            printw("Or use a command: remis, resign, taunt, savepgn  \n\n");
-        }
-        printw("Prepare your next move: ");
-        clrtoeol();
-        refresh();
-
-        /* allow the player to prepare a move */
-        if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) {
-            if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) {
-                if (mycolor == WHITE) {
-                    gamestate->wresign = true;
-                } else {
-                    gamestate->bresign = true;
-                }
-                net_send_code(opponent, NETCODE_RESIGN);
-                return 1;
-            } else if (strncmp(movestr, "taunt", MOVESTR_BUFLEN) == 0) {
-                resign_suggested = true;
-                net_send_code(opponent, NETCODE_TAUNT);
-            } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) {
-                remis_suggested = true;
-                net_send_code(opponent, NETCODE_REMIS);
-            } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) {
-                save_pgn(gamestate, gameinfo);
-            } else if (movestr[0] == 0) {
-                memset(gamestate->premove, 0, sizeof(gamestate->premove));
-            } else {
-                int res = check_move(movestr, mycolor);
-                if (res == VALID_MOVE_SYNTAX) {
-                    strncpy(gamestate->premove, movestr, 8);
-                    memset(movestr, 0, MOVESTR_BUFLEN);
-                    bufpos = 0;
-                    clrtobot();
-                } else {
-                    eval_move_failed_msg(res);
-                }
-            }
-        }
-
-        /* read opponent's move */
-        uint8_t code = net_recieve_code_async(opponent);
-        switch (code) {
-        case NETCODE_TIMEOVER:
-            /* redraw the time control */
-            timecontrol(gamestate, gameinfo);
-            return 1;
-        case NETCODE_RESIGN:
-            if (mycolor == WHITE) {
-                gamestate->bresign = true;
-            } else {
-                gamestate->wresign = true;
-            }
-            return 1;
-        case NETCODE_CONNLOST:
-            gamestate->ragequit = true;
-            return 1;
-        case NETCODE_REMIS:
-            if (remis_suggested) {
-                gamestate->remis = true;
-                return 1;
-            } else {
-                if (prompt_yesno(
-                    "\rYour opponent offers remis - do you accept")) {
-                    gamestate->remis = true;
-                    net_send_code(opponent, NETCODE_ACCEPT);
-                    return 1;
-                } else {
-                    net_send_code(opponent, NETCODE_DECLINE);
-                }
-            }
-            break;
-        case NETCODE_MOVE: {
-            Move move;
-            net_recieve_data(opponent, &move, sizeof(Move));
-            code = validate_move(gamestate, &move);
-            if (code == VALID_MOVE_SEMANTICS) {
-                apply_move(gamestate, &move);
-                if (gamestate->checkmate) {
-                    net_send_code(opponent, NETCODE_CHECKMATE);
-                    return 1;
-                } else if (gamestate->stalemate) {
-                    net_send_code(opponent, NETCODE_STALEMATE);
-                    return 1;
-                } else if (move.check) {
-                    net_send_code(opponent, NETCODE_CHECK);
-                } else {
-                    net_send_code(opponent, NETCODE_ACCEPT);
-                }
-                return 0;
-            } else {
-                uint32_t reason = htonl(code);
-                net_send_data(opponent, NETCODE_DECLINE,
-                    &reason, sizeof(uint32_t));
-            }
-            break;
-        }
-        case NETCODE_ERROR:
-            printw("\rCannot perform asynchronous network IO");
-            cbreak(); getch();
-            exit(EXIT_FAILURE);
-        case NETCODE_AGAIN:
-            /* try again */
-            break;
-        default:
-            printw("\nInvalid network request.");
-        }
-    }
-}
-
-void game_review(Settings* settings, GameState *gamestate) {
-    const unsigned page_moves = 10;
-    GameInfo *gameinfo = &(settings->gameinfo);
-    GameState viewedstate = {0};
-    unsigned viewedmove = gamestate->movecount;
-    bool redraw = true;
-
-    noecho();
-    int c;
-    do {
-        if (redraw) {
-            gamestate_cleanup(&viewedstate);
-            gamestate_at_move(gamestate, viewedmove, &viewedstate);
-
-            erase(); /* don't use clear() to avoid flickering */
-            draw_board(&viewedstate, WHITE, settings->unicode);
-            timecontrol(&viewedstate, gameinfo);
-
-            move(getmaxy(stdscr)-5, 0);
-            if (gamestate->wresign) {
-                addstr("White resigned.\n");
-            } else if (gamestate->bresign) {
-                addstr("Black resigned.\n");
-            } else if (gamestate->remis) {
-                addstr("The game ended remis.\n");
-            } else if (gamestate->stalemate) {
-                addstr("The game ended in a stalemate.\n");
-            } else if (gamestate->checkmate) {
-                printw("%s was checkmated.\n",
-                    gamestate->movecount % 2 == 0 ? "White" : "Black");
-            } else if (gamestate->ragequit) {
-                printw("Your opponent disconnected.\n");
-            }
-            addstr("\nPress 'q' to quit, 's' to save the position as PGN, or\n"
-                "arrow keys, home/end, page up/down to review the game.\n");
-            flushinp();
-            redraw = false;
-        }
-        c = getch();
-        if (c == 's') {
-            addch('\r');
-            echo();
-            save_pgn(&viewedstate, gameinfo);
-            noecho();
-            redraw = true;
-        } else if (c == KEY_UP || c == KEY_LEFT) {
-            if (viewedmove > 0) {
-                viewedmove--;
-                redraw = true;
-            }
-        } else if (c == KEY_DOWN || c == KEY_RIGHT) {
-            if (viewedmove < gamestate->movecount) {
-                viewedmove++;
-                redraw = true;
-            }
-        } else if (c == KEY_HOME) {
-            viewedmove = 0;
-            redraw = true;
-        } else if (c == KEY_END) {
-            viewedmove = gamestate->movecount;
-            redraw = true;
-        } else if (c == KEY_PPAGE) {
-            if (viewedmove > page_moves) {
-                viewedmove -= page_moves;
-            } else {
-                viewedmove = 0;
-            }
-            redraw = true;
-        } else if (c == KEY_NPAGE) {
-            viewedmove += page_moves;
-            if (viewedmove > gamestate->movecount) {
-                viewedmove = gamestate->movecount;
-            }
-            redraw = true;
-        }
-    } while (c != 'q');
-    echo();
-    gamestate_cleanup(&viewedstate);
-}
-
-void game_play_singlemachine(Settings *settings) {
-    inputy = getmaxy(stdscr) - 6;
-    
-    GameState gamestate;
-    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));
-            long position = ftell(pgnfile);
-            fclose(pgnfile);
-            if (result) {
-                printw("Invalid PGN file content at position %ld:\n%s\n",
-                        position, pgn_error_str(result));
-                return;
-            }
-            if (!is_game_running(&gamestate)) {
-                addstr("Game has ended. Use -S to analyze it.\n");
-                return;
-            }
-            curcol = opponent_color(last_move(&gamestate).piece&COLOR_MASK);
-        } else {
-            printw("Can't read PGN file (%s)\n", strerror(errno));
-            return;
-        }
-    }
-
-    bool running;
-    do {
-        clear();
-        uint8_t perspective = settings->disableflip ? WHITE : curcol;
-        draw_board(&gamestate, perspective, settings->unicode);
-        running = !domove_singlemachine(&gamestate,
-            &(settings->gameinfo), curcol);
-        curcol = opponent_color(curcol);
-    }  while (running);
-    
-    game_review(settings, &gamestate);
-    gamestate_cleanup(&gamestate);
-}
-
-void game_play(Settings *settings, GameState *gamestate, int opponent) {
-    inputy = getmaxy(stdscr) - 6;
-    
-    uint8_t mycolor = settings->gameinfo.servercolor;
-    if (!settings->ishost) {
-        mycolor = opponent_color(mycolor);
-    }
-    
-    bool myturn = (gamestate->movecount > 0 ?
-        (last_move(gamestate).piece & COLOR_MASK) : BLACK) != mycolor;
-    
-    bool running;
-    do {
-        clear();
-        draw_board(gamestate, mycolor, settings->unicode);
-        if (myturn) {
-            running = !sendmove(gamestate, &(settings->gameinfo),
-                opponent, mycolor);
-        } else {
-            running = !recvmove(gamestate, &(settings->gameinfo),
-                opponent, mycolor);
-        }
-        myturn ^= true;
-    }  while (running);
-}
-
-void dump_gameinfo(GameInfo *gameinfo) {
-    int serverwhite = gameinfo->servercolor == WHITE;
-    attron(A_UNDERLINE);
-    printw("Game details\n");
-    attroff(A_UNDERLINE);
-    printw("  Server:     %s\n  Client:     %s\n",
-        serverwhite?"White":"Black", serverwhite?"Black":"White"
-    );
-    if (gameinfo->timecontrol) {
-        if (gameinfo->time % 60) {
-            printw("  Time limit: %ds + %ds\n",
-                gameinfo->time, gameinfo->addtime);
-        } else {
-            printw("  Time limit: %dm + %ds\n",
-                gameinfo->time/60, gameinfo->addtime);
-        }
-    } else {
-        printw("  No time limit\n");
-    }
-    refresh();
-}
-
-void dump_moveinfo(GameState *gamestate) {
-    for (unsigned i = 0 ; i < gamestate->movecount ; i++) {
-        if (i % 2 == 0) {
-            printw("%d. %s", 1+i/2, gamestate->moves[i].string);
-        } else {
-            printw("%s", gamestate->moves[i].string);
-        }
-        // only five moves reliably fit into one screen row
-        if ((i+1) % 10)  {
-            addch(' ');
-        } else {
-            addch('\n');
-        }
-    }
-    refresh();
-}

mercurial