| 899 } while (c != 'q'); |
899 } while (c != 'q'); |
| 900 echo(); |
900 echo(); |
| 901 gamestate_cleanup(&viewedstate); |
901 gamestate_cleanup(&viewedstate); |
| 902 } |
902 } |
| 903 |
903 |
| 904 static void game_play_singlemachine(void) { |
904 static void game_play_singlemachine(GameState *gamestate) { |
| 905 inputy = getmaxy(stdscr) - 6; |
905 inputy = getmaxy(stdscr) - 6; |
| 906 |
906 |
| 907 GameState gamestate; |
907 uint8_t curcol = current_color(gamestate); |
| 908 gamestate_init(&gamestate); |
908 bool running = is_game_running(gamestate); |
| 909 settings_apply(&gamestate); |
909 while (running) { |
| 910 uint8_t curcol = WHITE; |
|
| 911 |
|
| 912 if (settings.continuepgn) { |
|
| 913 FILE *pgnfile = fopen(settings.continuepgn, "r"); |
|
| 914 if (pgnfile) { |
|
| 915 int result = read_pgn(pgnfile, &gamestate); |
|
| 916 fclose(pgnfile); |
|
| 917 if (result) { |
|
| 918 printw("Invalid PGN file content at position %zu:\n%s\n", |
|
| 919 pgn_error_position(), pgn_error_str(result)); |
|
| 920 gamestate_cleanup(&gamestate); |
|
| 921 return; |
|
| 922 } |
|
| 923 if (!is_game_running(&gamestate)) { |
|
| 924 addstr("Game has ended. Use -S to analyze it.\n"); |
|
| 925 gamestate_cleanup(&gamestate); |
|
| 926 return; |
|
| 927 } |
|
| 928 curcol = current_color(&gamestate); |
|
| 929 } else { |
|
| 930 printw("Can't read PGN file (%s)\n", strerror(errno)); |
|
| 931 gamestate_cleanup(&gamestate); |
|
| 932 return; |
|
| 933 } |
|
| 934 } |
|
| 935 |
|
| 936 bool running; |
|
| 937 do { |
|
| 938 clear(); |
910 clear(); |
| 939 uint8_t perspective = settings.disableflip ? WHITE : curcol; |
911 uint8_t perspective = settings.disableflip ? WHITE : curcol; |
| 940 draw_board(&gamestate, perspective); |
912 draw_board(gamestate, perspective); |
| 941 running = !domove_singlemachine(&gamestate, curcol); |
913 running = !domove_singlemachine(gamestate, curcol); |
| 942 curcol = opponent_color(curcol); |
914 curcol = opponent_color(curcol); |
| 943 } while (running); |
915 } |
| 944 |
916 game_review(gamestate); |
| 945 game_review(&gamestate); |
|
| 946 gamestate_cleanup(&gamestate); |
|
| 947 } |
917 } |
| 948 |
918 |
| 949 static void game_play(GameState *gamestate, int opponent, bool as_client) { |
919 static void game_play(GameState *gamestate, int opponent, bool as_client) { |
| 950 inputy = getmaxy(stdscr) - 6; |
920 inputy = getmaxy(stdscr) - 6; |
| 951 |
921 |
| 1054 refresh(); |
1024 refresh(); |
| 1055 |
1025 |
| 1056 return 0; |
1026 return 0; |
| 1057 } |
1027 } |
| 1058 |
1028 |
| 1059 static int server_run(void) { |
1029 static int server_run(GameState *gamestate) { |
| 1060 Server server; |
1030 Server server; |
| 1061 |
1031 |
| 1062 GameState gamestate; |
1032 dump_gameinfo(gamestate); |
| 1063 gamestate_init(&gamestate); |
|
| 1064 settings_apply(&gamestate); |
|
| 1065 if (settings.continuepgn) { |
|
| 1066 /* preload PGN data before handshake */ |
|
| 1067 bool cancel = false; |
|
| 1068 FILE *pgnfile = fopen(settings.continuepgn, "r"); |
|
| 1069 if (pgnfile) { |
|
| 1070 int result = read_pgn(pgnfile, &gamestate); |
|
| 1071 fclose(pgnfile); |
|
| 1072 if (result) { |
|
| 1073 printw("Invalid PGN file content at position %zu:\n%s\n", |
|
| 1074 pgn_error_position(), pgn_error_str(result)); |
|
| 1075 cancel = true; |
|
| 1076 } else if (!is_game_running(&gamestate)) { |
|
| 1077 addstr("Game has ended. Use -s to analyze it locally.\n"); |
|
| 1078 cancel = true; |
|
| 1079 } |
|
| 1080 } else { |
|
| 1081 printw("Can't read PGN file (%s)\n", strerror(errno)); |
|
| 1082 cancel = true; |
|
| 1083 } |
|
| 1084 if (cancel) { |
|
| 1085 gamestate_cleanup(&gamestate); |
|
| 1086 return 1; |
|
| 1087 } |
|
| 1088 } |
|
| 1089 dump_gameinfo(&gamestate); |
|
| 1090 |
1033 |
| 1091 if (server_open(&server)) { |
1034 if (server_open(&server)) { |
| 1092 net_destroy(&server); |
1035 net_destroy(&server); |
| 1093 gamestate_cleanup(&gamestate); |
|
| 1094 return 1; |
1036 return 1; |
| 1095 } |
1037 } |
| 1096 |
1038 |
| 1097 if (server_handshake(server.client)) { |
1039 if (server_handshake(server.client)) { |
| 1098 net_destroy(&server); |
1040 net_destroy(&server); |
| 1099 gamestate_cleanup(&gamestate); |
|
| 1100 return 1; |
1041 return 1; |
| 1101 } |
1042 } |
| 1102 |
1043 |
| 1103 int fd = server.client->fd; |
1044 int fd = server.client->fd; |
| 1104 if (settings.continuepgn) { |
1045 if (settings.continuepgn) { |
| 1105 /* Continue game, send PGN data */ |
1046 /* Continue game, send PGN data */ |
| 1106 uint16_t mc = gamestate.movecount; |
1047 uint16_t mc = gamestate->movecount; |
| 1107 size_t pgndata_size = sizeof(GameInfo)+sizeof(mc)+mc*sizeof(Move); |
1048 size_t pgndata_size = sizeof(GameInfo)+sizeof(mc)+mc*sizeof(Move); |
| 1108 char *pgndata = malloc(pgndata_size); |
1049 char *pgndata = malloc(pgndata_size); |
| 1109 memcpy(pgndata, &(gamestate.info), sizeof(GameInfo)); |
1050 memcpy(pgndata, &(gamestate->info), sizeof(GameInfo)); |
| 1110 unsigned offset = sizeof(GameInfo); |
1051 unsigned offset = sizeof(GameInfo); |
| 1111 memcpy(pgndata+offset, &mc, sizeof(mc)); |
1052 memcpy(pgndata+offset, &mc, sizeof(mc)); |
| 1112 offset += sizeof(mc); |
1053 offset += sizeof(mc); |
| 1113 memcpy(pgndata+offset, gamestate.moves, mc*sizeof(Move)); |
1054 memcpy(pgndata+offset, gamestate->moves, mc*sizeof(Move)); |
| 1114 net_send_data(fd, NETCODE_PGNDATA, pgndata, pgndata_size); |
1055 net_send_data(fd, NETCODE_PGNDATA, pgndata, pgndata_size); |
| 1115 free(pgndata); |
1056 free(pgndata); |
| 1116 } else { |
1057 } else { |
| 1117 /* Start new game */ |
1058 /* Start new game */ |
| 1118 net_send_data(fd, NETCODE_GAMEINFO, |
1059 net_send_data(fd, NETCODE_GAMEINFO, |
| 1119 &(gamestate.info), sizeof(GameInfo)); |
1060 &(gamestate->info), sizeof(GameInfo)); |
| 1120 } |
1061 } |
| 1121 addstr("\rClient connected - awaiting challenge acceptance..."); |
1062 addstr("\rClient connected - awaiting challenge acceptance..."); |
| 1122 refresh(); |
1063 refresh(); |
| 1123 int code = net_recieve_code(fd); |
1064 int code = net_recieve_code(fd); |
| 1124 int exitcode = 0; |
|
| 1125 if (code == NETCODE_ACCEPT) { |
1065 if (code == NETCODE_ACCEPT) { |
| 1126 addstr("\rClient connected - challenge accepted."); |
1066 addstr("\rClient connected - challenge accepted."); |
| 1127 clrtoeol(); |
1067 clrtoeol(); |
| 1128 game_play(&gamestate, fd, false); |
1068 game_play(gamestate, fd, false); |
| 1129 net_destroy(&server); |
1069 net_destroy(&server); |
| 1130 game_review(&gamestate); |
1070 game_review(gamestate); |
| 1131 } else if (code == NETCODE_DECLINE) { |
1071 } else if (code == NETCODE_DECLINE) { |
| 1132 addstr("\rClient connected - challenge declined."); |
1072 addstr("\rClient connected - challenge declined."); |
| 1133 clrtoeol(); |
1073 clrtoeol(); |
| 1134 net_destroy(&server); |
1074 net_destroy(&server); |
| 1135 } else if (code == NETCODE_CONNLOST) { |
1075 } else if (code == NETCODE_CONNLOST) { |
| 1190 net_destroy(&server); |
1128 net_destroy(&server); |
| 1191 return 1; |
1129 return 1; |
| 1192 } |
1130 } |
| 1193 |
1131 |
| 1194 uint8_t code = net_recieve_code(server.fd); |
1132 uint8_t code = net_recieve_code(server.fd); |
| 1195 GameState gamestate; |
|
| 1196 gamestate_init(&gamestate); |
|
| 1197 bool played = false; |
1133 bool played = false; |
| 1198 if (code == NETCODE_GAMEINFO) { |
1134 if (code == NETCODE_GAMEINFO) { |
| 1199 /* Start new game */ |
1135 /* Start new game */ |
| 1200 net_recieve_data(server.fd, &(gamestate.info), sizeof(GameInfo)); |
1136 net_recieve_data(server.fd, &(gamestate->info), sizeof(GameInfo)); |
| 1201 dump_gameinfo(&gamestate); |
1137 dump_gameinfo(gamestate); |
| 1202 if (prompt_yesno("Accept challenge")) { |
1138 if (prompt_yesno("Accept challenge")) { |
| 1203 net_send_code(server.fd, NETCODE_ACCEPT); |
1139 net_send_code(server.fd, NETCODE_ACCEPT); |
| 1204 game_play(&gamestate, server.fd, true); |
1140 game_play(gamestate, server.fd, true); |
| 1205 played = true; |
1141 played = true; |
| 1206 } else { |
1142 } else { |
| 1207 net_send_code(server.fd, NETCODE_DECLINE); |
1143 net_send_code(server.fd, NETCODE_DECLINE); |
| 1208 } |
1144 } |
| 1209 } else if (code == NETCODE_PGNDATA) { |
1145 } else if (code == NETCODE_PGNDATA) { |
| 1210 net_recieve_data(server.fd, &(gamestate.info), sizeof(GameInfo)); |
1146 net_recieve_data(server.fd, &(gamestate->info), sizeof(GameInfo)); |
| 1211 uint16_t mc; |
1147 uint16_t mc; |
| 1212 net_recieve_data(server.fd, &mc, sizeof(mc)); |
1148 net_recieve_data(server.fd, &mc, sizeof(mc)); |
| 1213 Move *moves = calloc(mc, sizeof(Move)); |
1149 Move *moves = calloc(mc, sizeof(Move)); |
| 1214 net_recieve_data(server.fd, moves, mc*sizeof(Move)); |
1150 net_recieve_data(server.fd, moves, mc*sizeof(Move)); |
| 1215 for (size_t i = 0 ; i < mc ; i++) { |
1151 for (size_t i = 0 ; i < mc ; i++) { |
| 1216 apply_move(&gamestate, &(moves[i])); |
1152 apply_move(gamestate, &(moves[i])); |
| 1217 } |
1153 } |
| 1218 free(moves); |
1154 free(moves); |
| 1219 dump_gameinfo(&gamestate); |
1155 dump_gameinfo(gamestate); |
| 1220 if (prompt_yesno( |
1156 if (prompt_yesno( |
| 1221 "\n\nServer wants to continue a game. Accept challenge")) { |
1157 "\n\nServer wants to continue a game. Accept challenge")) { |
| 1222 net_send_code(server.fd, NETCODE_ACCEPT); |
1158 net_send_code(server.fd, NETCODE_ACCEPT); |
| 1223 game_play(&gamestate, server.fd, true); |
1159 game_play(gamestate, server.fd, true); |
| 1224 played = true; |
1160 played = true; |
| 1225 } else { |
1161 } else { |
| 1226 net_send_code(server.fd, NETCODE_DECLINE); |
1162 net_send_code(server.fd, NETCODE_DECLINE); |
| 1227 } |
1163 } |
| 1228 } else { |
1164 } else { |
| 1259 fprintf(stderr, "Non-colored terminals are not supported yet."); |
1194 fprintf(stderr, "Non-colored terminals are not supported yet."); |
| 1260 endwin(); |
1195 endwin(); |
| 1261 return EXIT_FAILURE; |
1196 return EXIT_FAILURE; |
| 1262 } |
1197 } |
| 1263 atexit(cleanup); |
1198 atexit(cleanup); |
| 1264 |
1199 |
| 1265 int exitcode; |
1200 GameState gamestate; |
| 1266 if (settings.singlemachine) { |
1201 gamestate_init(&gamestate); |
| 1267 game_play_singlemachine(); |
1202 settings_apply(&gamestate); |
| 1268 exitcode = EXIT_SUCCESS; |
1203 |
| 1269 } else { |
1204 int exitcode = EXIT_SUCCESS; |
| 1270 exitcode = settings.ishost ? server_run() : client_run(); |
1205 |
| 1271 } |
1206 if (settings.continuepgn) { |
| |
1207 FILE *pgnfile = fopen(settings.continuepgn, "r"); |
| |
1208 if (pgnfile) { |
| |
1209 int result = read_pgn(pgnfile, &gamestate); |
| |
1210 fclose(pgnfile); |
| |
1211 if (result) { |
| |
1212 printw("Invalid PGN file content at position %zu:\n%s\n", |
| |
1213 pgn_error_position(), pgn_error_str(result)); |
| |
1214 exitcode = EXIT_FAILURE; |
| |
1215 } |
| |
1216 /* clear resignation and remis flags to allow game continuation */ |
| |
1217 gamestate.wresign = gamestate.bresign = gamestate.remis = false; |
| |
1218 } else { |
| |
1219 printw("Can't read PGN file (%s)\n", strerror(errno)); |
| |
1220 exitcode = EXIT_FAILURE; |
| |
1221 } |
| |
1222 } |
| |
1223 |
| |
1224 if (exitcode == EXIT_SUCCESS) { |
| |
1225 if (settings.singlemachine || !is_game_running(&gamestate)) { |
| |
1226 game_play_singlemachine(&gamestate); |
| |
1227 } else if (settings.ishost) { |
| |
1228 exitcode = server_run(&gamestate); |
| |
1229 } else { |
| |
1230 exitcode = client_run(&gamestate); |
| |
1231 } |
| |
1232 } |
| |
1233 |
| |
1234 gamestate_cleanup(&gamestate); |
| 1272 |
1235 |
| 1273 mvaddstr(getmaxy(stdscr)-1, 0, |
1236 mvaddstr(getmaxy(stdscr)-1, 0, |
| 1274 "Game has ended. Press any key to leave..."); |
1237 "Game has ended. Press any key to leave..."); |
| 1275 clrtoeol(); |
1238 clrtoeol(); |
| 1276 refresh(); |
1239 refresh(); |