]> git.sesse.net Git - stupid/blob - position.c
Initial checkin for move to Git (no prior version history available).
[stupid] / position.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <assert.h>
4 #include "position.h"
5 #include "evaluator.h"
6
7 void place_piece(struct position *pos, unsigned file, unsigned rank, unsigned char piece)
8 {
9         struct piece *ptr = IS_WHITE(piece) ? pos->white_pieces : pos->black_pieces;
10         unsigned i;
11
12         for (i = 0; i < 16; ++i) {
13                 if (ptr[i].type == PIECE_EMPTY) {
14                         ptr[i].type = MAKE_COLORLESS(piece);
15                         ptr[i].square = SQUARE_TO_NUM(file, rank);
16         
17                         pos->board[ptr[i].square] = piece | (i << 4);
18
19                         return;
20                 }
21         }
22
23         assert(0);
24 }
25
26 void init_position(struct position *pos)
27 {
28         unsigned i, y, x;
29
30         // clear the board
31         for (i = 0; i < NUM_SQUARES; ++i) {
32                 pos->board[i] = PIECE_EMPTY;
33         }
34         for (i = 0; i < 16; ++i) {
35                 pos->white_pieces[i].type = pos->black_pieces[i].type = PIECE_EMPTY;
36         }
37
38         // mark off the invalid squares
39         for (x = 0; x < 10; ++x) {
40                 pos->board[x +  0 * 10] = PIECE_INVALID;
41                 pos->board[x +  1 * 10] = PIECE_INVALID;
42                 pos->board[x + 10 * 10] = PIECE_INVALID;
43                 pos->board[x + 11 * 10] = PIECE_INVALID;
44         }
45         for (y = 2; y < 10; ++y) {
46                 pos->board[0 +  y * 10] = PIECE_INVALID;
47                 pos->board[9 +  y * 10] = PIECE_INVALID;
48         }
49         
50         // kings
51         place_piece(pos, 4, 0, MAKE_WHITE(PIECE_KING));
52         place_piece(pos, 4, 7, MAKE_BLACK(PIECE_KING));
53         
54         // queens
55         place_piece(pos, 3, 0, MAKE_WHITE(PIECE_QUEEN));
56         place_piece(pos, 3, 7, MAKE_BLACK(PIECE_QUEEN));
57         
58         // rooks
59         place_piece(pos, 0, 0, MAKE_WHITE(PIECE_ROOK));
60         place_piece(pos, 7, 0, MAKE_WHITE(PIECE_ROOK));
61         place_piece(pos, 0, 7, MAKE_BLACK(PIECE_ROOK));
62         place_piece(pos, 7, 7, MAKE_BLACK(PIECE_ROOK));
63         
64         // bishops
65         place_piece(pos, 2, 0, MAKE_WHITE(PIECE_BISHOP));
66         place_piece(pos, 5, 0, MAKE_WHITE(PIECE_BISHOP));
67         place_piece(pos, 2, 7, MAKE_BLACK(PIECE_BISHOP));
68         place_piece(pos, 5, 7, MAKE_BLACK(PIECE_BISHOP));
69         
70         // knights
71         place_piece(pos, 1, 0, MAKE_WHITE(PIECE_KNIGHT));
72         place_piece(pos, 6, 0, MAKE_WHITE(PIECE_KNIGHT));
73         place_piece(pos, 1, 7, MAKE_BLACK(PIECE_KNIGHT));
74         place_piece(pos, 6, 7, MAKE_BLACK(PIECE_KNIGHT));
75
76         // pawns
77         for (x = 0; x < 8; ++x) {
78                 place_piece(pos, x, 1, MAKE_WHITE(PIECE_PAWN));
79                 place_piece(pos, x, 6, MAKE_BLACK(PIECE_PAWN));
80         }
81         
82         // non-piece information
83         pos->wc_short = pos->wc_long = pos->bc_short = pos->bc_long = true;
84         pos->ep_square = 0;
85         pos->white_to_move = true;
86 }
87
88 // checks various internal structures -- not a complete check, but useful
89 // to safeguard against corruption
90 void validate_position(const struct position * const pos)
91 {
92         unsigned i, j, k;
93
94 #if 0
95         // both sides need kings
96         assert(pos->white_pieces[0].type == PIECE_KING);
97         assert(pos->black_pieces[0].type == PIECE_KING);
98 #endif
99
100         // check that all material is on the board, with correct indexes
101         for (i = 0; i < 2; ++i) {
102                 bool white = (i == 0);
103                 const struct piece * const pieces = (white ? pos->white_pieces : pos->black_pieces);
104
105                 for (j = 0; j < 16; ++j) {
106                         if (pieces[j].type == PIECE_EMPTY)
107                                 continue;
108
109                         // valid piece type
110                         assert(pieces[j].type >= PIECE_PAWN);
111                         assert(pieces[j].type <= PIECE_QUEEN);
112                         assert(pieces[j].type != PIECE_INVALID);
113
114                         // on a valid square
115                         assert(pieces[j].square >= SQUARE_TO_NUM(0, 0));
116                         assert(pieces[j].square <= SQUARE_TO_NUM(7, 7));
117                         assert(NUM_TO_FILE(pieces[j].square) >= 0);
118                         assert(NUM_TO_FILE(pieces[j].square) <= 7);
119                         assert(NUM_TO_RANK(pieces[j].square) >= 0);
120                         assert(NUM_TO_RANK(pieces[j].square) <= 7);
121
122                         // the board matches
123                         assert(MAKE_COLORLESS(pos->board[pieces[j].square]) == pieces[j].type);
124
125                         if (white)
126                                 assert(IS_WHITE(pos->board[pieces[j].square]));
127                         else
128                                 assert(IS_BLACK(pos->board[pieces[j].square]));
129
130                         assert(FIND_INDEX(pos->board[pieces[j].square]) == j);
131                 }
132         }
133
134         // check that there's no phantom material
135         for (i = 0; i < 8; ++i) {
136                 for (j = 0; j < 8; ++j) {
137                         unsigned square = SQUARE_TO_NUM(i, j);
138                 
139                         if (MAKE_COLORLESS(pos->board[square]) == PIECE_EMPTY)
140                                 continue;
141
142                         if (IS_WHITE(pos->board[square])) {
143                                 assert(pos->white_pieces[FIND_INDEX(pos->board[square])].type == MAKE_COLORLESS(pos->board[square]));
144                                 assert(pos->white_pieces[FIND_INDEX(pos->board[square])].square == square);
145                         } else {
146                                 assert(pos->black_pieces[FIND_INDEX(pos->board[square])].type == MAKE_COLORLESS(pos->board[square]));
147                                 assert(pos->black_pieces[FIND_INDEX(pos->board[square])].square == square);
148                         }
149                 }
150         }
151
152         // TODO: check castling sanity
153 }
154
155 char piece_to_char(int piece)
156 {
157         switch (piece & 0x0f) {
158         case PIECE_EMPTY:
159                 return ' ';
160         case MAKE_WHITE(PIECE_PAWN):
161                 return 'P';
162         case MAKE_BLACK(PIECE_PAWN):
163                 return 'p';
164         case MAKE_WHITE(PIECE_ROOK):
165                 return 'R';
166         case MAKE_BLACK(PIECE_ROOK):
167                 return 'r';
168         case MAKE_WHITE(PIECE_KNIGHT):
169                 return 'N';
170         case MAKE_BLACK(PIECE_KNIGHT):
171                 return 'n';
172         case MAKE_WHITE(PIECE_BISHOP):
173                 return 'B';
174         case MAKE_BLACK(PIECE_BISHOP):
175                 return 'b';
176         case MAKE_WHITE(PIECE_QUEEN):
177                 return 'Q';
178         case MAKE_BLACK(PIECE_QUEEN):
179                 return 'q';
180         case MAKE_WHITE(PIECE_KING):
181                 return 'K';
182         case MAKE_BLACK(PIECE_KING):
183                 return 'k';
184         default:
185                 return '?';
186         }
187 }       
188
189 void print_position(struct position *pos)
190 {
191         unsigned x, y;
192         
193         printf("Position:\n\n");
194
195         for (y = 8; y --> 0; ) {
196                 printf("  ");
197                 for (x = 0; x < 8; ++x) {
198                         int piece = pos->board[SQUARE_TO_NUM(x, y)];
199
200                         if (piece == PIECE_EMPTY) {
201                                 if (SQUARE_TO_NUM(x, y) == pos->ep_square) {
202                                         printf("x");
203                                 } else {
204                                         printf(" ");
205                                 }
206                         } else {
207                                 putchar(piece_to_char(pos->board[SQUARE_TO_NUM(x, y)]));
208                         }
209                 }
210                 printf("\n");
211         }
212         
213         if (pos->white_to_move) {
214                 printf("White to move\n");
215         } else {
216                 printf("Black to move\n");
217         }
218         
219 #if 0
220         {
221                 unsigned i;
222                 printf("Black pieces:");
223                 for (i = 0; i < 16; ++i) {
224                         int type = pos->black_pieces[i].type;
225                         int square = pos->black_pieces[i].square;
226                         if (type != PIECE_EMPTY)
227                                 printf(" %c%u(%c)", "abcdefgh"[NUM_TO_FILE(square)], NUM_TO_RANK(square) + 1, piece_to_char(type));
228                 }
229                 printf("\n");
230         }
231 #endif
232
233         printf("\nStatic evaluation: %.2f\n", 0.01f * evaluate(pos));
234 }
235
236 void real_make_move(struct position *pos, unsigned from, unsigned to, struct piece *from_ptr, struct piece *to_ptr)
237 {
238         unsigned piece = pos->board[from];
239         unsigned new_ep_square = 0;
240         
241         validate_position(pos);
242
243         if (pos->board[to] != PIECE_EMPTY) {
244                 // capture
245                 to_ptr = &to_ptr[FIND_INDEX(pos->board[to])];
246                 to_ptr->type = PIECE_EMPTY;
247                 to_ptr->square = 0;
248                         
249                 // see if the rook was captured 
250                 if (to == 28)
251                         pos->wc_short = false;
252                 else if (to == 21)
253                         pos->wc_long = false;
254                 else if (to == 98)
255                         pos->bc_short = false;
256                 else if (to == 91)
257                         pos->bc_long = false;
258         }
259
260         // update castling, and possibly castle
261         if (MAKE_COLORLESS(piece) == PIECE_KING) {
262                 if (IS_WHITE(piece)) {
263                         // whoa, hardcoded =)
264                         if (from == 25 && to == 27) {
265                                 assert(from_ptr[3].square == 28);
266                                 from_ptr[3].square = 26;
267                                 pos->board[26] = pos->board[28];
268                                 pos->board[28] = PIECE_EMPTY;
269                         } else if (from == 25 && to == 23) {
270                                 assert(from_ptr[2].square == 21);
271                                 from_ptr[2].square = 24;
272                                 pos->board[24] = pos->board[21];
273                                 pos->board[21] = PIECE_EMPTY;
274                         }
275                         
276                         pos->wc_short = pos->wc_long = false;
277                 } else {
278                         if (from == 95 && to == 97) {
279                                 assert(from_ptr[3].square == 98);
280                                 from_ptr[3].square = 96;
281                                 pos->board[96] = pos->board[98];
282                                 pos->board[98] = PIECE_EMPTY;
283                         } else if (from == 95 && to == 93) {
284                                 assert(from_ptr[2].square == 91);
285                                 from_ptr[2].square = 94;
286                                 pos->board[94] = pos->board[91];
287                                 pos->board[91] = PIECE_EMPTY;
288                         }
289                         
290                         pos->bc_short = pos->bc_long = false;
291                 }
292         } else if (MAKE_COLORLESS(piece) == PIECE_ROOK) {
293                 if (IS_WHITE(piece)) {
294                         if (from == 28)
295                                 pos->wc_short = false;
296                         else if (from == 21)
297                                 pos->wc_long = false;
298                 } else {
299                         if (from == 98)
300                                 pos->bc_short = false;
301                         else if (from == 91)
302                                 pos->bc_long = false;
303                 }
304         }
305
306         // en passant and promotion handling
307         if (MAKE_COLORLESS(piece) == PIECE_PAWN) {
308                 if (to == pos->ep_square) {
309                         // en passant capture
310                         if (IS_WHITE(piece)) {
311                                 to_ptr = &to_ptr[FIND_INDEX(pos->board[to - 10])];
312                                 pos->board[to - 10] = PIECE_EMPTY;
313                         } else {
314                                 to_ptr = &to_ptr[FIND_INDEX(pos->board[to + 10])];
315                                 pos->board[to + 10] = PIECE_EMPTY;
316                         }
317                         to_ptr->type = PIECE_EMPTY;
318                         to_ptr->square = 0;
319                 } else if (abs(to - from) == 20) {
320                         // double move
321                         if (IS_WHITE(piece)) {
322                                 new_ep_square = to - 10;
323                         } else {
324                                 new_ep_square = to + 10;
325                         }
326                 } else if (NUM_TO_RANK(to) == 0 || NUM_TO_RANK(to) == 7) {
327                         // promotion
328                         from_ptr->type = PIECE_QUEEN;
329                         piece = (piece & 0xf8) | PIECE_QUEEN;
330                 }
331         }
332         
333         // move the piece 
334         from_ptr->square = to;
335         
336         // and update the board
337         pos->board[to] = piece;
338         pos->board[from] = PIECE_EMPTY;
339         pos->ep_square = new_ep_square;
340
341         pos->white_to_move = !pos->white_to_move;
342         
343         validate_position(pos);
344 }
345
346 // not used in the move generator, real_make_move() is what's used there
347 void make_move(struct position *pos, unsigned from, unsigned to)
348 {
349         struct piece *from_ptr, *to_ptr;
350
351         validate_position(pos);
352
353         if (IS_WHITE(pos->board[from])) {
354                 from_ptr = pos->white_pieces;
355                 to_ptr = pos->black_pieces;
356         } else {
357                 from_ptr = pos->black_pieces;
358                 to_ptr = pos->white_pieces;
359         }
360
361         from_ptr = &from_ptr[FIND_INDEX(pos->board[from])];
362         real_make_move(pos, from, to, from_ptr, to_ptr);
363 }
364