src/game.c

changeset 50
41017d0a72c5
parent 49
02c509a44e98
child 51
84f2e380a434
     1.1 --- a/src/game.c	Wed Jun 11 16:54:20 2014 +0200
     1.2 +++ b/src/game.c	Mon Jun 16 13:45:31 2014 +0200
     1.3 @@ -35,6 +35,8 @@
     1.4  #include <string.h>
     1.5  #include <inttypes.h>
     1.6  #include <sys/select.h>
     1.7 +#include <stdio.h>
     1.8 +#include <errno.h>
     1.9  
    1.10  static const uint8_t boardx = 10, boardy = 10;
    1.11  static int inputy = 21; /* should be overridden on game startup */
    1.12 @@ -69,7 +71,7 @@
    1.13      return 0;
    1.14  }
    1.15  
    1.16 -static void draw_board(GameState *gamestate) {
    1.17 +static void draw_board(GameState *gamestate, uint8_t perspective) {
    1.18      for (uint8_t y = 0 ; y < 8 ; y++) {
    1.19          for (uint8_t x = 0 ; x < 8 ; x++) {
    1.20              uint8_t col = gamestate->board[y][x] & COLOR_MASK;
    1.21 @@ -89,8 +91,8 @@
    1.22                  )
    1.23              );
    1.24              
    1.25 -            int cy = gamestate->mycolor == WHITE ? boardy-y : boardy-7+y;
    1.26 -            int cx = gamestate->mycolor == WHITE ? boardx+x*3 : boardx+21-x*3;
    1.27 +            int cy = perspective == WHITE ? boardy-y : boardy-7+y;
    1.28 +            int cx = perspective == WHITE ? boardx+x*3 : boardx+21-x*3;
    1.29              mvaddch(cy, cx, ' ');
    1.30              mvaddch(cy, cx+1, piecec);
    1.31              mvaddch(cy, cx+2, ' ');
    1.32 @@ -99,8 +101,8 @@
    1.33      
    1.34      attrset(A_NORMAL);
    1.35      for (uint8_t i = 0 ; i < 8 ; i++) {
    1.36 -        int x = gamestate->mycolor == WHITE ? boardx+i*3+1 : boardx+22-i*3;
    1.37 -        int y = gamestate->mycolor == WHITE ? boardy-i : boardy-7+i;
    1.38 +        int x = perspective == WHITE ? boardx+i*3+1 : boardx+22-i*3;
    1.39 +        int y = perspective == WHITE ? boardy-i : boardy-7+i;
    1.40          mvaddch(boardy+1, x, 'a'+i);
    1.41          mvaddch(y, boardx-2, '1'+i);
    1.42      }
    1.43 @@ -122,17 +124,15 @@
    1.44              printw("%d. ", logi / 2);
    1.45          }
    1.46  
    1.47 -        if (logelem) {
    1.48 -            addstr(logelem->move.string);
    1.49 -            if (!logelem->next) {
    1.50 -                if (gamestate->stalemate) {
    1.51 -                    addstr(" stalemate");
    1.52 -                }
    1.53 +        addstr(logelem->move.string);
    1.54 +        if (!logelem->next) {
    1.55 +            if (gamestate->stalemate) {
    1.56 +                addstr(" stalemate");
    1.57              }
    1.58 -            addch(' ');
    1.59 -            
    1.60 -            logelem = logelem->next;
    1.61          }
    1.62 +        addch(' ');
    1.63 +
    1.64 +        logelem = logelem->next;
    1.65      }
    1.66  }
    1.67  
    1.68 @@ -167,8 +167,30 @@
    1.69      }
    1.70  }
    1.71  
    1.72 -#define MOVESTR_BUFLEN 8
    1.73 -static int domove_singlemachine(GameState *gamestate, GameInfo *gameinfo) {
    1.74 +static void save_pgn(GameState *gamestate, GameInfo *gameinfo) {
    1.75 +    printw("Filename: ");
    1.76 +    clrtoeol();
    1.77 +    refresh();
    1.78 +    
    1.79 +    char filename[64];
    1.80 +    int y = getcury(stdscr);
    1.81 +    if (getnstr(filename, 64) == OK) {
    1.82 +        move(y, 0);
    1.83 +        FILE *file = fopen(filename, "w");
    1.84 +        if (file) {
    1.85 +            write_pgn(file, gamestate, gameinfo);
    1.86 +            fclose(file);
    1.87 +            printw("File saved.");
    1.88 +        } else {
    1.89 +            printw("Can't write to file (%s).", strerror(errno));
    1.90 +        }
    1.91 +        clrtoeol();
    1.92 +    }
    1.93 +}
    1.94 +
    1.95 +#define MOVESTR_BUFLEN 10
    1.96 +static int domove_singlemachine(GameState *gamestate,
    1.97 +        GameInfo *gameinfo, uint8_t curcolor) {
    1.98      
    1.99  
   1.100      size_t bufpos = 0;
   1.101 @@ -183,29 +205,33 @@
   1.102          move(inputy, 0);
   1.103          printw(
   1.104              "Use chess notation to enter your move.\n"
   1.105 -            "Or type 'resign' to resign or 'remis' to end with remis.\n\n"
   1.106 +            "Or use a command: remis, resign, savepgn\n\n"
   1.107              "Type your move: ");
   1.108          clrtoeol();
   1.109          
   1.110          if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) {
   1.111              if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) {
   1.112 +                gamestate->resign = 1;
   1.113                  printw("%s resigned!",
   1.114 -                    gamestate->mycolor==WHITE?"White":"Black");
   1.115 -                clrtoeol();
   1.116 +                    curcolor==WHITE?"White":"Black");
   1.117 +                clrtobot();
   1.118                  refresh();
   1.119                  return 1;
   1.120              } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) {
   1.121 +                gamestate->remis = 1;
   1.122                  printw("Game ends remis.");
   1.123 -                clrtoeol();
   1.124 +                clrtobot();
   1.125                  refresh();
   1.126                  return 1;
   1.127 +            } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) {
   1.128 +                save_pgn(gamestate, gameinfo);
   1.129              } else {
   1.130                  Move move;
   1.131 -                int eval_result = eval_move(gamestate, movestr, &move);
   1.132 -                switch (eval_result) {
   1.133 +                int result = eval_move(gamestate, movestr, &move, curcolor);
   1.134 +                switch (result) {
   1.135                  case VALID_MOVE_SYNTAX:
   1.136 -                    eval_result = validate_move(gamestate, &move);
   1.137 -                    if (eval_result == VALID_MOVE_SEMANTICS) {
   1.138 +                    result = validate_move(gamestate, &move);
   1.139 +                    if (result == VALID_MOVE_SEMANTICS) {
   1.140                          apply_move(gamestate, &move);
   1.141                          if (gamestate->checkmate) {
   1.142                              printw("Checkmate!");
   1.143 @@ -219,11 +245,11 @@
   1.144                              return 0;
   1.145                          }
   1.146                      } else {
   1.147 -                        eval_move_failed_msg(eval_result);
   1.148 +                        eval_move_failed_msg(result);
   1.149                      }
   1.150                      break;
   1.151                  default:
   1.152 -                    eval_move_failed_msg(eval_result);
   1.153 +                    eval_move_failed_msg(result);
   1.154                  }
   1.155                  clrtoeol();
   1.156              }
   1.157 @@ -231,7 +257,8 @@
   1.158      }
   1.159  }
   1.160  
   1.161 -static int sendmove(GameState *gamestate, GameInfo *gameinfo, int opponent) {
   1.162 +static int sendmove(GameState *gamestate, GameInfo *gameinfo,
   1.163 +        int opponent, uint8_t mycolor) {
   1.164      
   1.165      size_t bufpos = 0;
   1.166      char movestr[MOVESTR_BUFLEN];
   1.167 @@ -249,23 +276,26 @@
   1.168          if (remisrejected) {
   1.169              printw(
   1.170                  "Use chess notation to enter your move.\n"
   1.171 -                "Remis offer rejected - type 'resign' to resign.      \n\n"
   1.172 +                "Remis offer rejected                    \n\n"
   1.173                  "Type your move: ");
   1.174          } else {
   1.175              printw(
   1.176                  "Use chess notation to enter your move.\n"
   1.177 -                "Or type 'resign' to resign or 'remis' to offer remis.\n\n"
   1.178 +                "Or use a command: remis, resign, savepgn\n\n"
   1.179                  "Type your move: ");
   1.180          }
   1.181          clrtoeol();
   1.182          
   1.183          if (asyncgetnstr(movestr, &bufpos, MOVESTR_BUFLEN)) {
   1.184              if (strncmp(movestr, "resign", MOVESTR_BUFLEN) == 0) {
   1.185 +                gamestate->resign = 1;
   1.186                  printw("You resigned!");
   1.187                  clrtoeol();
   1.188                  refresh();
   1.189                  net_send_code(opponent, NETCODE_RESIGN);
   1.190                  return 1;
   1.191 +            } else if (strncmp(movestr, "savepgn", MOVESTR_BUFLEN) == 0) {
   1.192 +                save_pgn(gamestate, gameinfo);
   1.193              } else if (strncmp(movestr, "remis", MOVESTR_BUFLEN) == 0) {
   1.194                  if (!remisrejected) {
   1.195                      net_send_code(opponent, NETCODE_REMIS);
   1.196 @@ -273,6 +303,7 @@
   1.197                      refresh();
   1.198                      code = net_recieve_code(opponent);
   1.199                      if (code == NETCODE_ACCEPT) {
   1.200 +                        gamestate->remis = 1;
   1.201                          printw("\rRemis accepted!");
   1.202                          clrtoeol();
   1.203                          refresh();
   1.204 @@ -288,7 +319,7 @@
   1.205                  }
   1.206              } else {
   1.207                  Move move;
   1.208 -                int eval_result = eval_move(gamestate, movestr, &move);
   1.209 +                int eval_result = eval_move(gamestate, movestr, &move, mycolor);
   1.210                  switch (eval_result) {
   1.211                  case VALID_MOVE_SYNTAX:
   1.212                      net_send_data(opponent, NETCODE_MOVE, &move,
   1.213 @@ -353,6 +384,8 @@
   1.214          timeout.tv_sec = 0;
   1.215          timeout.tv_usec = 1e5;
   1.216          
   1.217 +        // TODO: allow commands
   1.218 +        
   1.219          int result = select(opponent+1, &readfds, NULL, NULL, &timeout);
   1.220          if (result == -1) {
   1.221              printw("\rCannot perform asynchronous network IO");
   1.222 @@ -369,6 +402,7 @@
   1.223                  clrtoeol();
   1.224                  return 1;
   1.225              case NETCODE_RESIGN:
   1.226 +                gamestate->resign = 1;
   1.227                  printw("\rYour opponent resigned!");
   1.228                  clrtoeol();
   1.229                  return 1;
   1.230 @@ -379,6 +413,7 @@
   1.231              case NETCODE_REMIS:
   1.232                  if (prompt_yesno(
   1.233                      "\rYour opponent offers remis - do you accept")) {
   1.234 +                    gamestate->remis = 1;
   1.235                      printw("\rRemis accepted!");
   1.236                      clrtoeol();
   1.237                      net_send_code(opponent, NETCODE_ACCEPT);
   1.238 @@ -421,39 +456,71 @@
   1.239      }
   1.240  }
   1.241  
   1.242 -static void init_board(GameState *gamestate) {
   1.243 -    Board initboard = {
   1.244 -        {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
   1.245 -        {WPAWN, WPAWN,   WPAWN,   WPAWN,  WPAWN, WPAWN,   WPAWN,   WPAWN},
   1.246 -        {0,     0,       0,       0,      0,     0,       0,       0},
   1.247 -        {0,     0,       0,       0,      0,     0,       0,       0},
   1.248 -        {0,     0,       0,       0,      0,     0,       0,       0},
   1.249 -        {0,     0,       0,       0,      0,     0,       0,       0},
   1.250 -        {BPAWN, BPAWN,   BPAWN,   BPAWN,  BPAWN, BPAWN,   BPAWN,   BPAWN},
   1.251 -        {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
   1.252 -    };
   1.253 -    memcpy(gamestate->board, initboard, sizeof(Board));
   1.254 +static void post_game(GameState *gamestate, GameInfo *gameinfo) {
   1.255 +    move(0,0);
   1.256 +    draw_board(gamestate, WHITE);
   1.257 +    
   1.258 +    // TODO: network connection is still open here - think about it!
   1.259 +    
   1.260 +    mvaddstr(getmaxy(stdscr)-1, 0,
   1.261 +        "Press 'q' to quit or 's' to save a PGN file...");
   1.262 +    refresh();
   1.263 +    flushinp();
   1.264 +    
   1.265 +    noecho();
   1.266 +    int c;
   1.267 +    do {
   1.268 +        c = getch();
   1.269 +        if (c == 's') {
   1.270 +            addch('\r');
   1.271 +            echo();
   1.272 +            save_pgn(gamestate, gameinfo);
   1.273 +            addstr(" Press 'q' to quit...");
   1.274 +            noecho();
   1.275 +        }
   1.276 +    } while (c != 'q');
   1.277 +    echo();
   1.278 +    
   1.279 +    gamestate_cleanup(gamestate);
   1.280  }
   1.281  
   1.282  void game_start_singlemachine(Settings *settings) {
   1.283      inputy = getmaxy(stdscr) - 6;
   1.284      
   1.285      GameState gamestate;
   1.286 -    memset(&gamestate, 0, sizeof(GameState));
   1.287 -    init_board(&gamestate);
   1.288 -    gamestate.mycolor = WHITE;
   1.289 +    gamestate_init(&gamestate);
   1.290 +    uint8_t curcol = WHITE;
   1.291 +    
   1.292 +    if (settings->continuepgn) {
   1.293 +        FILE *pgnfile = fopen(settings->continuepgn, "r");
   1.294 +        if (pgnfile) {
   1.295 +            int result = read_pgn(pgnfile, &gamestate, &(settings->gameinfo));
   1.296 +            fclose(pgnfile);
   1.297 +            if (result != EXIT_SUCCESS) {
   1.298 +                addstr("Invalid PGN file content.\n");
   1.299 +                return;
   1.300 +            }
   1.301 +            if (!is_game_running(&gamestate)) {
   1.302 +                addstr("Game has ended. Use -S to analyze it.\n");
   1.303 +                return;
   1.304 +            }
   1.305 +            curcol = opponent_color(gamestate.lastmove->move.piece&COLOR_MASK);
   1.306 +        } else {
   1.307 +            printw("Can't read PGN file (%s)\n", strerror(errno));
   1.308 +            return;
   1.309 +        }
   1.310 +    }
   1.311  
   1.312      _Bool running;
   1.313      do {
   1.314          clear();
   1.315 -        draw_board(&gamestate);
   1.316 -        running = !domove_singlemachine(&gamestate, &(settings->gameinfo));
   1.317 -        gamestate.mycolor = opponent_color(gamestate.mycolor);
   1.318 +        draw_board(&gamestate, curcol);
   1.319 +        running = !domove_singlemachine(&gamestate,
   1.320 +            &(settings->gameinfo), curcol);
   1.321 +        curcol = opponent_color(curcol);
   1.322      }  while (running);
   1.323 -    move(0,0);
   1.324 -    draw_board(&gamestate);
   1.325      
   1.326 -    gamestate_cleanup(&gamestate);
   1.327 +    post_game(&gamestate, &(settings->gameinfo));
   1.328  }
   1.329  
   1.330  void game_start(Settings *settings, int opponent) {
   1.331 @@ -461,26 +528,23 @@
   1.332      
   1.333      _Bool myturn = is_server(settings) ==
   1.334          (settings->gameinfo.servercolor == WHITE);
   1.335 +    uint8_t mycolor = myturn ? WHITE : BLACK;
   1.336      
   1.337      GameState gamestate;
   1.338 -    memset(&gamestate, 0, sizeof(GameState));
   1.339 -    init_board(&gamestate);
   1.340 -    gamestate.mycolor = myturn ? WHITE:BLACK;
   1.341 +    gamestate_init(&gamestate);
   1.342      
   1.343      _Bool running;
   1.344      do {
   1.345          clear();
   1.346 -        draw_board(&gamestate);
   1.347 +        draw_board(&gamestate, mycolor);
   1.348          if (myturn) {
   1.349 -            running = !sendmove(&gamestate, &(settings->gameinfo), opponent);
   1.350 +            running = !sendmove(&gamestate, &(settings->gameinfo),
   1.351 +                opponent, mycolor);
   1.352          } else {
   1.353              running = !recvmove(&gamestate, &(settings->gameinfo), opponent);
   1.354          }
   1.355          myturn ^= TRUE;
   1.356      }  while (running);
   1.357      
   1.358 -    move(0,0);
   1.359 -    draw_board(&gamestate);
   1.360 -    
   1.361 -    gamestate_cleanup(&gamestate);
   1.362 +    post_game(&gamestate, &(settings->gameinfo));
   1.363  }

mercurial