Mon, 31 Mar 2014 14:08:00 +0200
improved network performance
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2014 Mike Becker. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
30 #include "game.h"
31 #include "network.h"
32 #include "input.h"
33 #include <ncurses.h>
34 #include <string.h>
36 static const uint8_t boardx = 10, boardy = 10;
38 static void draw_board(Board board, MoveListRoot *movelist, uint8_t mycolor) {
40 for (uint8_t y = 0 ; y < 8 ; y++) {
41 for (uint8_t x = 0 ; x < 8 ; x++) {
42 uint8_t col = board[y][x] & COLOR_MASK;
43 uint8_t piece = board[y][x] & PIECE_MASK;
44 char piecec;
45 if (piece) {
46 piecec = piece == PAWN ? 'P' : getpiecechr(piece);
47 } else {
48 piecec = ' ';
49 }
51 attrset((col == WHITE ? A_BOLD : A_DIM) |
52 COLOR_PAIR((y&1)==(x&1) ? COL_WB : COL_BW));
54 int cy = mycolor == WHITE ? boardy-y : boardy-7+y;
55 int cx = mycolor == WHITE ? boardx+x*3 : boardx+21-x*3;
56 mvaddch(cy, cx, ' ');
57 mvaddch(cy, cx+1, piecec);
58 mvaddch(cy, cx+2, ' ');
59 }
60 }
62 attrset(A_NORMAL);
63 for (uint8_t i = 0 ; i < 8 ; i++) {
64 int x = mycolor == WHITE ? boardx+i*3+1 : boardx+22-i*3;
65 int y = mycolor == WHITE ? boardy-i : boardy-7+i;
66 mvaddch(boardy+1, x, 'a'+i);
67 mvaddch(y, boardx-2, '1'+i);
68 }
70 /* move log */
71 // TODO: introduce window to avoid bugs with a long move log
72 uint8_t logy = 0;
73 const uint8_t logx = boardx + 30;
74 int logi = 1;
75 MoveList *logelem = movelist->first;
77 while (logelem) {
78 logi++;
79 if (logi % 2 == 0) {
80 if ((logi - 2) % 4 == 0) {
81 logy++;
82 wmove(tchess_window, logy, logx);
83 }
84 printw("%d. ", logi / 2);
85 }
87 if (logelem) {
88 Move move = logelem->move;
89 char logstr[] = {
90 getpiecechr(move.piece),
91 filechr(move.fromfile), rowchr(move.fromrow),
92 move.capture ? 'x':'\0',
93 filechr(move.tofile), rowchr(move.torow),
94 move.check ? '+' : (move.checkmate ? '#' :
95 (move.promotion ? '=' : '\0')),
96 move.promotion ? getpiecechr(move.promotion) : '\0',
97 ' '
98 };
99 for (int stri = 0 ; stri < sizeof(logstr) ; stri++) {
100 if (logstr[stri]) {
101 addch(logstr[stri]);
102 }
103 }
105 logelem = logelem->next;
106 }
107 }
108 }
111 static int sendmove(Board board, MoveListRoot *movelist,
112 uint8_t mycolor, int opponent) {
114 const size_t buflen = 8;
115 char movestr[buflen];
116 _Bool remisrejected = FALSE;
117 uint8_t code;
119 int inputy = getmaxy(tchess_window) - 6;
120 while (1) {
121 move(inputy, 0);
122 if (remisrejected) {
123 printw(
124 "Use chess notation to enter your move.\n"
125 "Remis offer rejected - type 'surr' to surrender. \n\n"
126 "Type your move: ");
127 } else {
128 printw(
129 "Use chess notation to enter your move.\n"
130 "Or type 'surr' to surrender or 'remis' to offer remis.\n\n"
131 "Type your move: ");
132 }
133 clrtoeol();
134 refresh();
135 getnstr(movestr, buflen);
137 if (strncmp(movestr, "surr", buflen) == 0) {
138 printw("You surrendered!");
139 clrtoeol();
140 refresh();
141 net_send_code(opponent, NETCODE_SURRENDER);
142 return 1;
143 } else if (strncmp(movestr, "remis", buflen) == 0) {
144 if (!remisrejected) {
145 net_send_code(opponent, NETCODE_REMIS);
146 printw("Remis offer sent - waiting for acceptance...");
147 refresh();
148 if (net_recieve_code(opponent) == NETCODE_ACCEPT) {
149 printw("\rRemis accepted!");
150 clrtoeol();
151 refresh();
152 return 1;
153 } else {
154 remisrejected = TRUE;
155 }
156 }
157 } else {
158 Move move;
159 int eval_result = eval_move(board, mycolor, movestr, &move);
160 switch (eval_result) {
161 case VALID_MOVE_SYNTAX:
162 net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move));
163 code = net_recieve_code(opponent);
164 move.check = code == NETCODE_CHECK;
165 move.checkmate = code == NETCODE_CHECKMATE;
166 if (code == NETCODE_DECLINE) {
167 printw("Invalid move.");
168 } else {
169 apply_move(board, &move);
170 addmove(movelist, &move);
171 if (move.checkmate) {
172 printw("Checkmate!");
173 clrtoeol();
174 return 1;
175 } else {
176 return 0;
177 }
178 }
179 break;
180 case AMBIGUOUS_MOVE:
181 printw("Ambiguous move - please specify the piece to move.");
182 break;
183 case INVALID_POSITION:
184 printw("Cannot find the piece that shall be moved.");
185 break;
186 case NEED_PROMOTION:
187 printw("You need to promote the pawn (append \"=Q\" e.g.)!");
188 break;
189 default:
190 printw("Can't interpret move - please use algebraic notation.");
191 }
192 clrtoeol();
193 }
194 }
195 }
197 static int recvmove(Board board, MoveListRoot *movelist, int opponent) {
199 int inputy = getmaxy(tchess_window) - 6;
200 while (1) {
201 move(inputy, 0);
202 printw("Awaiting opponent move...");
203 clrtoeol();
204 refresh();
206 // TODO: nonblocking
207 uint32_t code = net_recieve_code(opponent);
209 Move move;
210 switch (code) {
211 case NETCODE_SURRENDER:
212 printw("\rYour opponent surrendered!");
213 clrtoeol();
214 return 1;
215 case NETCODE_REMIS:
216 if (prompt_yesno(
217 "\rYour opponent offers remis - do you accept")) {
218 printw("\rRemis accepted!");
219 clrtoeol();
220 net_send_code(opponent, NETCODE_ACCEPT);
221 return 1;
222 } else {
223 net_send_code(opponent, NETCODE_DECLINE);
224 }
225 break;
226 case NETCODE_MOVE:
227 net_recieve_data(opponent, &move, sizeof(Move));
228 if (validate_move(board, &move)) {
229 apply_move(board, &move);
230 addmove(movelist, &move);
231 if (move.check) {
232 net_send_code(opponent, NETCODE_CHECK);
233 } else if (move.checkmate) {
234 net_send_code(opponent, NETCODE_CHECKMATE);
235 } else {
236 net_send_code(opponent, NETCODE_ACCEPT);
237 }
238 return 0;
239 } else {
240 net_send_code(opponent, NETCODE_DECLINE);
241 }
242 }
243 }
244 }
246 void freemovelist(MoveListRoot* list) {
247 MoveList *elem;
248 elem = list->first;
249 while (elem) {
250 MoveList *cur = elem;
251 elem = elem->next;
252 free(cur);
253 };
254 free(list);
255 }
257 void addmove(MoveListRoot* list, Move *move) {
258 MoveList *elem = malloc(sizeof(MoveList));
259 elem->next = NULL;
260 elem->move = *move;
262 if (list->last) {
263 list->last->next = elem;
264 list->last = elem;
265 } else {
266 list->first = list->last = elem;
267 }
268 }
270 void game_start(Settings *settings, int opponent) {
271 _Bool myturn = is_server(settings) ==
272 (settings->gameinfo.servercolor == WHITE);
273 uint8_t mycolor = myturn ? WHITE:BLACK;
275 _Bool running;
277 MoveListRoot* movelist = calloc(1, sizeof(MoveListRoot));
279 Board board = {
280 {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
281 {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN},
282 {0, 0, 0, 0, 0, 0, 0, 0},
283 {0, 0, 0, 0, 0, 0, 0, 0},
284 {0, 0, 0, 0, 0, 0, 0, 0},
285 {0, 0, 0, 0, 0, 0, 0, 0},
286 {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN},
287 {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
288 };
290 do {
291 clear();
292 draw_board(board, movelist, mycolor);
293 if (myturn) {
294 running = !sendmove(board, movelist, mycolor, opponent);
295 } else {
296 running = !recvmove(board, movelist, opponent);
297 flushinp(); // flush any input the user hacked in while waiting
298 }
299 myturn ^= TRUE;
300 } while (running);
302 freemovelist(movelist);
304 mvaddstr(getmaxy(tchess_window)-1, 0,
305 "Game has ended. Press any key to leave...");
306 getch();
307 }