Mon, 31 Mar 2014 11:16:32 +0200
moved chess rules to separate lib
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_code(opponent, NETCODE_MOVE);
163 net_send_data(opponent, &move, sizeof(Move));
164 code = net_recieve_code(opponent);
165 move.check = code == NETCODE_CHECK;
166 move.checkmate = code == NETCODE_CHECKMATE;
167 addmove(movelist, &move);
168 if (code == NETCODE_DECLINE) {
169 printw("Invalid move.");
170 } else {
171 apply_move(board, &move);
172 if (move.checkmate) {
173 printw("Checkmate!");
174 clrtoeol();
175 return 1;
176 } else {
177 return 0;
178 }
179 }
180 break;
181 case AMBIGUOUS_MOVE:
182 printw("Ambiguous move - please specify the piece to move.");
183 break;
184 case INVALID_POSITION:
185 printw("Cannot find the piece that shall be moved.");
186 break;
187 case NEED_PROMOTION:
188 printw("You need to promote the pawn (append \"=Q\" e.g.)!");
189 break;
190 default:
191 printw("Can't interpret move - please use algebraic notation.");
192 }
193 clrtoeol();
194 }
195 }
196 }
198 static int recvmove(Board board, MoveListRoot *movelist, int opponent) {
200 int inputy = getmaxy(tchess_window) - 6;
201 while (1) {
202 move(inputy, 0);
203 printw("Awaiting opponent move...");
204 clrtoeol();
205 refresh();
207 // TODO: nonblocking
208 uint32_t code = net_recieve_code(opponent);
210 Move move;
211 switch (code) {
212 case NETCODE_SURRENDER:
213 printw("\rYour opponent surrendered!");
214 clrtoeol();
215 return 1;
216 case NETCODE_REMIS:
217 if (prompt_yesno(
218 "\rYour opponent offers remis - do you accept")) {
219 printw("\rRemis accepted!");
220 clrtoeol();
221 net_send_code(opponent, NETCODE_ACCEPT);
222 return 1;
223 } else {
224 net_send_code(opponent, NETCODE_DECLINE);
225 }
226 break;
227 case NETCODE_MOVE:
228 net_recieve_data(opponent, &move, sizeof(Move));
229 if (validate_move(board, &move)) {
230 apply_move(board, &move);
231 addmove(movelist, &move);
232 if (move.check) {
233 net_send_code(opponent, NETCODE_CHECK);
234 } else if (move.checkmate) {
235 net_send_code(opponent, NETCODE_CHECKMATE);
236 } else {
237 net_send_code(opponent, NETCODE_ACCEPT);
238 }
239 return 0;
240 } else {
241 net_send_code(opponent, NETCODE_DECLINE);
242 }
243 }
244 }
245 }
247 void freemovelist(MoveListRoot* list) {
248 MoveList *elem;
249 elem = list->first;
250 while (elem) {
251 MoveList *cur = elem;
252 elem = elem->next;
253 free(cur);
254 };
255 free(list);
256 }
258 void addmove(MoveListRoot* list, Move *move) {
259 MoveList *elem = malloc(sizeof(MoveList));
260 elem->next = NULL;
261 elem->move = *move;
263 if (list->last) {
264 list->last->next = elem;
265 list->last = elem;
266 } else {
267 list->first = list->last = elem;
268 }
269 }
271 void game_start(Settings *settings, int opponent) {
272 _Bool myturn = is_server(settings) ==
273 (settings->gameinfo.servercolor == WHITE);
274 uint8_t mycolor = myturn ? WHITE:BLACK;
276 _Bool running;
278 MoveListRoot* movelist = calloc(1, sizeof(MoveListRoot));
280 Board board = {
281 {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
282 {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN},
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 {0, 0, 0, 0, 0, 0, 0, 0},
287 {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN},
288 {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
289 };
291 do {
292 clear();
293 draw_board(board, movelist, mycolor);
294 if (myturn) {
295 running = !sendmove(board, movelist, mycolor, opponent);
296 } else {
297 running = !recvmove(board, movelist, opponent);
298 flushinp(); // flush any input the user hacked in while waiting
299 }
300 myturn ^= TRUE;
301 } while (running);
303 freemovelist(movelist);
305 mvaddstr(getmaxy(tchess_window)-1, 0,
306 "Game has ended. Press any key to leave...");
307 getch();
308 }