Subversion Repositories Games.Chess Giants

Rev

Rev 177 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
// board.cpp
2
 
3
#include "common.h"
4
 
5
 
6
// initial position of a chess game :
7
//
8
//  +-----------------+
9
// 8| R K B Q K B K R | - black
10
// 7| P P P P P P P P |
11
// 6|                 |
12
// 5|                 |
13
// 4|                 |
14
// 3|                 |
15
// 2| P P P P P P P P |
16
// 1| R K B Q K B K R | - white
17
//  +-----------------+
18
//    a b c d e f g h
19
 
20
 
172 pmbaty 21
bool Board_Init (board_t *board, int white_playertype, int black_playertype, wchar_t *game_rules, wchar_t *fen_string)
1 pmbaty 22
{
23
   // this function initializes a new chess game
24
 
25
   bool is_success;
26
 
27
   // initialize the moves array and blank out the first move's structure
28
   board->moves = (boardmove_t *) SAFE_malloc (1, sizeof (boardmove_t), false);
29
   memset (&board->moves[0], 0, sizeof (boardmove_t));
30
 
31
   board->viewed_move = 0;
32
   board->move_count = 1;
33
   board->game_state = STATE_UNKNOWN; // game has not started yet
34
   board->lastmove_time = 0.0f;
35
 
36
   // initialize this board's players
37
   Player_Init (&board->players[COLOR_WHITE], COLOR_WHITE, white_playertype);
38
   Player_Init (&board->players[COLOR_BLACK], COLOR_BLACK, black_playertype);
39
 
40
   // can we NOT successfully initialize from the given FEN string ?
41
   is_success = Move_SetupFromFEN (&board->moves[0], fen_string);
42
   if (!is_success)
43
      Move_SetupFromFEN (&board->moves[0], FENSTARTUP_STANDARDCHESS); // if so, fallback to standard chess table
44
 
45
   // reset all selection and hovering information
46
   Board_SetSelectedAndHovered (board, -1, -1, -1, -1);
47
 
172 pmbaty 48
   // set the game rules to the specified one
49
   wcscpy_s (board->game_rules, WCHAR_SIZEOF (board->game_rules), game_rules);
50
 
193 pmbaty 51
   // if white is human AND black is computer AND white prefers to play black, swap sides
52
   if ((white_playertype == PLAYER_HUMAN) && (black_playertype == PLAYER_COMPUTER) && options.want_playblackside)
53
      board->want_playerswap = true; // remember to swap sides
54
 
1 pmbaty 55
   // notify that board was just set up
56
   board->was_setup = true;
171 pmbaty 57
   board->players[COLOR_WHITE].should_wakeup = true;
58
   board->players[COLOR_BLACK].should_wakeup = true;
124 pmbaty 59
   board->reevaluate = true;
1 pmbaty 60
 
61
   return (is_success); // finished
62
}
63
 
64
 
65
void Board_Shutdown (board_t *board)
66
{
67
   // this function ends a chess game
68
 
69
   boardmove_t *move;
70
   int move_index;
71
 
72
   // reset the moves array
73
   for (move_index = 0; move_index < board->move_count; move_index++)
74
   {
75
      move = &board->moves[move_index]; // quick access to move
76
      SAFE_free ((void **) &move->sides[COLOR_WHITE].takenparts); // free white's taken parts
77
      SAFE_free ((void **) &move->sides[COLOR_BLACK].takenparts); // free black's taken parts
78
      SAFE_free ((void **) &move->comment); // free each move comment
79
   }
80
   SAFE_free ((void **) &board->moves); // and the moves array itself
81
   board->viewed_move = -1;
82
   board->move_count = 0;
83
 
84
   // release players
85
   Player_Shutdown (&board->players[COLOR_WHITE]);
86
   Player_Shutdown (&board->players[COLOR_BLACK]);
87
 
88
   // game has not started yet
89
   board->game_state = STATE_UNKNOWN;
90
 
91
   return; // that's all there is
92
}
93
 
94
 
95
bool Board_Reset (board_t *board, wchar_t *fen_string)
96
{
97
   // this function initializes a chess game according to a FEN string, WITHOUT changing the players
98
 
99
   boardmove_t *move;
100
   int move_index;
101
   bool is_success;
102
 
103
   // reset the moves array
104
   for (move_index = 0; move_index < board->move_count; move_index++)
105
   {
106
      move = &board->moves[move_index]; // quick access to move
107
      SAFE_free ((void **) &move->sides[COLOR_WHITE].takenparts); // free white's taken parts
108
      SAFE_free ((void **) &move->sides[COLOR_BLACK].takenparts); // free black's taken parts
109
      SAFE_free ((void **) &move->comment); // free each move comment
110
   }
111
 
112
   // resize the moves array and blank out the first move's structure
113
   board->moves = (boardmove_t *) SAFE_realloc (board->moves, board->move_count, 1, sizeof (boardmove_t), false);
114
   memset (&board->moves[0], 0, sizeof (boardmove_t));
115
 
116
   board->viewed_move = 0;
117
   board->move_count = 1;
118
   board->game_state = STATE_UNKNOWN; // game has not started yet
119
   board->lastmove_time = 0.0f;
120
 
121
   // reset the players' view angles
122
   Player_ResetView (&board->players[COLOR_BLACK]);
123
   Player_ResetView (&board->players[COLOR_WHITE]);
124
 
125
   // can we NOT successfully initialize from the given FEN string ?
126
   is_success = Move_SetupFromFEN (&board->moves[0], fen_string);
127
   if (!is_success)
128
      Move_SetupFromFEN (&board->moves[0], FENSTARTUP_STANDARDCHESS); // if so, fallback to standard chess table
129
 
130
   // reset all selection and hovering information
131
   Board_SetSelectedAndHovered (board, -1, -1, -1, -1);
132
 
133
   // notify that board was just set up
134
   board->was_setup = true;
171 pmbaty 135
   board->players[COLOR_WHITE].should_wakeup = true;
136
   board->players[COLOR_BLACK].should_wakeup = true;
1 pmbaty 137
 
138
   return (is_success); // finished
139
}
140
 
141
 
142
bool Board_Think (board_t *board)
143
{
144
   // helper function to make both a board's players think
145
 
146
   bool do_update;
147
 
148
   do_update = false; // don't update scene until told otherwise
149
 
150
   do_update |= Player_Think (&board->players[COLOR_WHITE]); // make white player think
151
   do_update |= Player_Think (&board->players[COLOR_BLACK]); // make black player think
152
 
153
   // do the board sides need to be swapped ?
154
   if (board->want_playerswap)
155
   {
156
      Board_SwapSides (board); // if so, swap board sides
157
      board->want_playerswap = false; // don't do this all day long
158
      do_update |= true; // and update the scene
159
   }
160
 
171 pmbaty 161
   // clear board setup notification
1 pmbaty 162
   board->was_setup = false;
163
 
164
   return (do_update); // finished, return whether we update the scene or not
165
}
166
 
167
 
168
void Board_SwapSides (board_t *board)
169
{
170
   // helper function to swap a board's sides
171
 
172
   player_t player_to_swap;
173
 
174
   // THREAD-SAFE: wait until all buffers are unlocked
175
   while (board->players[COLOR_BLACK].sendbuffer_locked
176
          || board->players[COLOR_WHITE].sendbuffer_locked)
177
      Sleep (10); // test again in 10 milliseconds
178
 
179
   // lock all the buffers at once
180
   board->players[COLOR_BLACK].sendbuffer_locked = true;
181
   board->players[COLOR_WHITE].sendbuffer_locked = true;
182
 
183
   // swap players structures
184
   memcpy (&player_to_swap, &board->players[COLOR_BLACK], sizeof (player_t));
185
   memcpy (&board->players[COLOR_BLACK], &board->players[COLOR_WHITE], sizeof (player_t));
186
   memcpy (&board->players[COLOR_WHITE], &player_to_swap, sizeof (player_t));
187
 
188
   // restore correct colors
189
   board->players[COLOR_BLACK].color = COLOR_BLACK;
190
   board->players[COLOR_WHITE].color = COLOR_WHITE;
191
 
192
   // turn their point of view 180 degrees
193
   board->players[COLOR_BLACK].view_yaw = WrapAngle (board->players[COLOR_BLACK].view_yaw + 180.0f);
194
   board->players[COLOR_WHITE].view_yaw = WrapAngle (board->players[COLOR_WHITE].view_yaw + 180.0f);
195
 
171 pmbaty 196
   // now unlock all the players' buffers and have them sleep for 2 seconds
1 pmbaty 197
   board->players[COLOR_BLACK].sendbuffer_locked = false;
198
   board->players[COLOR_WHITE].sendbuffer_locked = false;
171 pmbaty 199
   board->players[COLOR_BLACK].sleep_time = current_time + 2.0f;
200
   board->players[COLOR_WHITE].sleep_time = current_time + 2.0f;
1 pmbaty 201
 
202
   board->reevaluate = true; // remember to reevaluate this board
203
 
204
   return; // finished
205
}
206
 
207
 
136 pmbaty 208
void Board_EnterSetupPosition (board_t *board)
209
{
210
   // helper function to switch to board setup position mode
211
 
212
   memset (&board->moves[0].slots, 0, sizeof (board->moves[0].slots)); // erase all of start move's slots
213
 
214
   the_board.game_state = STATE_SETUPPOSITION; // game not started yet, enter board setup mode
215
   the_board.reevaluate = true; // evaluate board again
216
 
217
   // display the "please choose start position" phrase in the middle of the screen
140 pmbaty 218
   Scene_UpdateText (&the_scene.gui.central_text, RGBA_TO_RGBACOLOR (255, 255, 255, 191), DURATION_INFINITE, true, L"\n\n\n\n\n\n%s", LOCALIZE (L"SetupMode"));
136 pmbaty 219
   the_scene.gui.partspick_selectedpart = ' '; // no selected part yet
220
   the_scene.update = true; // and update the 3D scene
221
 
222
   return; // finished
223
}
224
 
225
 
1 pmbaty 226
void Board_SetSelectedAndHovered (board_t *board, int selected_line, int selected_column, int hovered_line, int hovered_column)
227
{
228
   // helper function to forcibly set a board's selected and hovered slots (respectively move source and destination)
229
 
230
   board->selected_position[0] = selected_line;
231
   board->selected_position[1] = selected_column;
232
   board->hovered_position[0] = hovered_line;
233
   board->hovered_position[1] = hovered_column;
234
 
235
   return; // finished
236
}
237
 
238
 
239
char Board_ColorToMove (board_t *board)
240
{
241
   // helper function that returns the current color to move for the given board
242
   // NOTE: since the return value may be used to address an array, DO NOT return an invalid value !
243
 
244
   if ((board->moves == NULL) || (board->move_count < 1))
245
      return (COLOR_WHITE); // consistency check
246
 
247
   return (1 - board->moves[the_board.move_count - 1].color); // return the opposite color of the last move's color
248
}
249
 
250
 
251
void Board_AppendMove (board_t *board, int from_line, int from_column, int to_line, int to_column, char promotion_type, wchar_t *comment)
252
{
253
   // this function processes a part movement on the specified board.
254
 
255
   boardmove_t *last_move;
256
   boardmove_t new_move;
257
   int side_index;
258
   boardside_t *playing_side;
259
   boardside_t *opposing_side;
260
 
261
   // get a quick access to board's last move
262
   last_move = &board->moves[board->move_count - 1];
263
 
264
   // prepare the new move
193 pmbaty 265
   memset (&new_move, 0, sizeof (new_move));
1 pmbaty 266
   new_move.color = 1 - last_move->color; // switch colors
267
   new_move.part = last_move->slots[from_line][from_column].part; // save the move part type
268
   new_move.promotion_type = promotion_type; // save the promotion type
269
   new_move.has_captured = false; // assume there's no capture until told otherwise
270
   new_move.is_enpassant = false; // assume no en passant coup is played until told otherwise
271
   new_move.is_check = false; // assume no check until told otherwise
272
   new_move.is_stalemate = false; // assume no stalemate until told otherwise
273
   new_move.source[0] = from_line; // save the move source line and column
274
   new_move.source[1] = from_column;
275
   new_move.target[0] = to_line; // save the move target line and column
276
   new_move.target[1] = to_column;
277
   new_move.pgntext[0] = 0; // will be evaluated at the end of this function
278
   new_move.comment = NULL; // assume no comment until told otherwise
279
   new_move.comment_size = 0;
280
   for (side_index = 0; side_index < 2; side_index++)
281
   {
282
      new_move.sides[side_index].takenparts = (unsigned char *) SAFE_malloc (last_move->sides[side_index].takenpart_count, sizeof (unsigned char), false);
283
      memcpy (new_move.sides[side_index].takenparts, last_move->sides[side_index].takenparts, last_move->sides[side_index].takenpart_count * sizeof (unsigned char));
284
      new_move.sides[side_index].takenpart_count = last_move->sides[side_index].takenpart_count; // copy side's taken parts
285
      new_move.sides[side_index].shortcastle_allowed = last_move->sides[side_index].shortcastle_allowed; // copy whether side can castle short
286
      new_move.sides[side_index].longcastle_allowed = last_move->sides[side_index].longcastle_allowed; // copy whether side can castle long
287
   }
288
   memcpy (&new_move.slots, last_move->slots, sizeof (last_move->slots)); // copy table disposition
289
   new_move.fen_string[0] = 0; // will be evaluated at the end of this function
290
 
291
   // get a quick access to current and opposing sides
292
   playing_side = &new_move.sides[new_move.color];
293
   opposing_side = &new_move.sides[1 - new_move.color];
294
 
295
   // is a piece being taken ?
296
   if (new_move.slots[to_line][to_column].part != PART_NONE)
297
   {
298
      // was the piece being taken a rook ?
299
      if (new_move.slots[to_line][to_column].part == PART_ROOK)
300
      {
301
         // was it a white tower in A1, a white tower in A7, a black tower in H1 or a black tower in H7 ?
302
         if ((new_move.slots[to_line][to_column].color == COLOR_WHITE) && (to_line == 0) && (to_column == 0))
303
            new_move.sides[COLOR_WHITE].longcastle_allowed = false; // our opponent can no longer castle queenside
304
         else if ((new_move.slots[to_line][to_column].color == COLOR_WHITE) && (to_line == 0) && (to_column == 7))
305
            new_move.sides[COLOR_WHITE].shortcastle_allowed = false; // our opponent can no longer castle bishopside
306
         else if ((new_move.slots[to_line][to_column].color == COLOR_BLACK) && (to_line == 7) && (to_column == 0))
307
            new_move.sides[COLOR_BLACK].longcastle_allowed = false; // our opponent can no longer castle queenside
308
         else if ((new_move.slots[to_line][to_column].color == COLOR_BLACK) && (to_line == 7) && (to_column == 7))
309
            new_move.sides[COLOR_BLACK].shortcastle_allowed = false; // our opponent can no longer castle bishopside
310
      }
311
 
312
      // resize this players' taken parts array and move the taken piece in it
313
      playing_side->takenparts = (unsigned char *) SAFE_realloc (playing_side->takenparts, playing_side->takenpart_count, playing_side->takenpart_count + 1, sizeof (unsigned char), true);
314
      playing_side->takenparts[playing_side->takenpart_count] = new_move.slots[to_line][to_column].part;
315
      playing_side->takenpart_count++; // player has now taken one piece more
316
      new_move.has_captured = true; // remember a part has just been captured
317
   }
318
 
319
   // else is it an "en passant" coup ?
320
   else if ((new_move.slots[from_line][from_column].part == PART_PAWN) // we're a pawn
321
            && (abs (to_column - from_column) == 1) // we moved diagonally
322
            && (new_move.slots[to_line][to_column].part == PART_NONE) // no part on destination
323
            && (new_move.slots[from_line][to_column].part == PART_PAWN)) // but a pawn next to us
324
   {
325
      // resize this players' taken parts array and move the taken piece in it
326
      playing_side->takenparts = (unsigned char *) SAFE_realloc (playing_side->takenparts, playing_side->takenpart_count, playing_side->takenpart_count + 1, sizeof (unsigned char), true);
327
      playing_side->takenparts[playing_side->takenpart_count] = new_move.slots[from_line][to_column].part;
328
      memset (&new_move.slots[from_line][to_column], 0, sizeof (boardslot_t)); // "en passant" coup
329
      playing_side->takenpart_count++; // player has now taken one piece more
330
      new_move.has_captured = true; // remember a part has just been captured
331
      new_move.is_enpassant = true; // and that it's an en passant coup
332
   }
333
 
334
   // has a promotion been specified ?
335
   if (promotion_type != PART_NONE)
336
      Move_SetSlot (&new_move, from_line, from_column, new_move.color, promotion_type); // promote the pawn to the desired type
337
 
338
   // was the moved part the king ? if so, player can't castle anymore
339
   if (new_move.slots[from_line][from_column].part == PART_KING)
340
   {
341
      // did the king castle queenside ? or did it castle bishopside ?
342
      if ((from_column == 4) && (to_column == 2))
343
      {
344
         // move the rook from A to D (0 -> 3) and wipe its previous location
345
         memcpy (&new_move.slots[to_line][3], &new_move.slots[from_line][0], sizeof (boardslot_t));
346
         memset (&new_move.slots[from_line][0], 0, sizeof (boardslot_t));
347
      }
348
      else if ((from_column == 4) && (to_column == 6))
349
      {
350
         // move the rook from H to F (7 -> 5) and wipe its previous location
351
         memcpy (&new_move.slots[to_line][5], &new_move.slots[from_line][7], sizeof (boardslot_t));
352
         memset (&new_move.slots[from_line][7], 0, sizeof (boardslot_t));
353
      }
354
 
355
      // no matter whether it castled or not, remember this king cannot castle anymore
356
      playing_side->shortcastle_allowed = false;
357
      playing_side->longcastle_allowed = false;
358
   }
359
 
360
   // else was it the rook ?
361
   else if (new_move.slots[from_line][from_column].part == PART_ROOK)
362
   {
363
      // was the tower in A ? or was it in H ?
364
      if (from_column == 0)
365
         playing_side->longcastle_allowed = false; // player can no longer castle queenside
366
      else if (from_column == 7)
367
         playing_side->shortcastle_allowed = false; // player can no longer castle bishopside
368
   }
369
 
370
   // move the part and erase the starting slot
371
   memcpy (&new_move.slots[to_line][to_column], &new_move.slots[from_line][from_column], sizeof (boardslot_t));
372
   memset (&new_move.slots[from_line][from_column], 0, sizeof (boardslot_t));
373
 
374
   // does the new move have a comment ? if so, save it
375
   if ((comment != NULL) && (comment[0] != 0))
376
   {
377
      new_move.comment_size = wcslen (comment) + 1; // save this move's comment and the size of the mallocated space
378
      new_move.comment = (wchar_t *) SAFE_malloc (new_move.comment_size, sizeof (wchar_t), false);
379
      wcscpy_s (new_move.comment, new_move.comment_size, comment); // copy comment
380
   }
381
 
382
   // evaluate check and stalemate status
383
   new_move.is_check = Move_IsCheck (&new_move, 1 - new_move.color); // save whether opponent is in check or not
384
   new_move.is_stalemate = Move_IsStaleMate (&new_move, 1 - new_move.color); // save whether opponent is stalemate
385
 
386
   // describe our move in Standard Abbreviated Notation and describe the resulting table in Forsyth-Edwards Notation
193 pmbaty 387
   Move_DescribeInSAN (&new_move, last_move, new_move.pgntext, WCHAR_SIZEOF (new_move.pgntext), false); // don't use the localized part abbreviations
1 pmbaty 388
   Move_DescribeInFEN (&new_move);
389
 
390
   // resize the previous moves array and insert our new move at the end of it
391
   board->moves = (boardmove_t *) SAFE_realloc (board->moves, board->move_count, board->move_count + 1, sizeof (boardmove_t), false);
392
   memcpy (&board->moves[board->move_count], &new_move, sizeof (boardmove_t));
393
   board->move_count++; // we know now one previous move more
394
 
395
   board->viewed_move = board->move_count - 1; // set the move to be viewed to be the last move
396
 
397
   // if the game hadn't started yet, remember it has now
398
   if (board->game_state == STATE_UNKNOWN)
399
      board->game_state = STATE_PLAYING;
400
 
401
   // remember the last move time
402
   board->lastmove_time = current_time;
403
 
75 pmbaty 404
   // reset stoppage time
405
   stoppage_time = 0;
406
 
1 pmbaty 407
   // remember the game state should be reevaluated
408
   board->reevaluate = true;
409
 
410
   return; // finished
411
}