Final AI tweaks.
[NALCG.git] / src / ais / daniel / position.cpp
blobefc7f0852ed57824dcfc07bdd5ecd572013f018f
1 #include "position.h"
4 Position::Position(MovementGenerator *mg) : generator(mg), gameOver(false), isCheck(false), noisiness(0) {
5 reset();
6 mg->setPosition(this);
7 mg->getAllLegalMoves(&legalMoves);
10 Position::Position(Position *p, int mv) {
11 // Dampen the noisiness.
12 noisiness = (int)(p->getNoisiness() * 0.5f);
14 legalMoves.reserve(64);
15 int x;
16 p->getBoard(&board[0], &x, &x, &x, &x);
17 whiteToMove = p->isWhiteToMove();
18 generator = p->getMovementGenerator();
19 move(p->getLegalMoves()->at(mv),0);
20 gameOver = testLeafNode();
23 Position::Position(MovementGenerator *mg, char b[8][8], bool white) : generator(mg), whiteToMove(white), noisiness(0) {
24 for (int i = 0; i < 8; ++i) {
25 for (int j = 0; j < 8; ++j) {
26 board[i][j] = b[i][j];
29 mg->setPosition(this);
30 mg->getAllLegalMoves(&legalMoves);
31 isCheck = !generator->isKingSafe(0,0,0,0);
32 gameOver = testLeafNode();
35 // Is the game over
36 inline bool Position::testLeafNode() {
37 // Checkmate or stalemate
38 if (legalMoves.empty()) {
39 return true;
41 for (int i = 0; i < 8; ++i) {
42 for (int j = 0; j < 8; ++j) {
43 switch(board[i][j]) {
44 case ' ':
45 case 'K':
46 case 'X':
47 case 'k':
48 case 'x':
49 break;
50 // Game is still alive
51 default:
52 return false;
56 // Nothing but kings on the board
57 return true;
60 inline char Position::translate(char piece) {
61 switch(piece) {
62 case 'T':
63 return 'R';
64 case 't':
65 return 'r';
66 case 'X':
67 return 'K';
68 case 'x':
69 return 'k';
70 case 'E':
71 return 'P';
72 case 'e':
73 return 'p';
75 return piece;
78 inline int Position::pawnIsolationPenalty(int j, bool white) {
79 char p = 'P';
80 char e = 'E';
81 if (white) {
82 p += 32;
83 e += 32;
85 int k = j - 1;
86 if (j > 0) {
87 for (int i = 0; i < 8; ++i) {
88 if (board[i][k] == p || board[i][k] == e) {
89 return 0;
93 if (j < 7) {
94 k = j + 1;
95 for (int i = 0; i < 8; ++i) {
96 if (board[i][k] == p || board[i][k] == e) {
97 return 0;
101 switch(j) {
102 case 0:
103 case 7:
104 return 12;
105 case 1:
106 case 6:
107 return 14;
108 case 2:
109 case 5:
110 return 16;
112 return 20;
115 inline int Position::pawnBackwardPenalty(int i, int j, bool white) {
116 int y = i + 1;
117 int x1 = j - 1;
118 int x2 = j + 1;
119 char p = 'P';
120 char e = 'E';
121 if (white) {
122 y = i - 1;
123 p += 32;
124 e += 32;
126 if ((x1 >= 0 && (board[i][x1] == p || board[i][x1] == e || board[y][x1] == p || board[y][x1] == e)) || (x2 < 8 && (board[i][x2] == p || board[i][x2] == e || board[y][x2] == p || board[y][x2] == e))) {
127 return 0;
129 else {
130 // Check that the pawn isn't on a half-open lane (which will cause
131 // a higher penalty.)
132 if (white) {
133 for (int k = i - 1; k >= 0; --k) {
134 if (board[k][j] == 'P' || board[k][j] == 'E') {
135 return 6;
139 else {
140 for (int k = i + 1; k < 8; ++k) {
141 if (board[k][j] == 'p' || board[k][j] == 'e') {
142 return 6;
147 return 10;
150 inline int Position::pawnAdvancementBonus(int i, int j, bool white) {
151 int advancement;
152 if (white) {
153 advancement = 6 - i;
155 else {
156 advancement = i - 1;
158 // Penalty for unadvanced king & queen pawns
159 if (advancement == 0 && (j == 3 || j == 4)) {
160 return -10;
162 // Big fat bonus for pawn about to queen
163 if (advancement == 5) {
164 return 200;
166 if (advancement == 4) {
167 return 50;
169 // Bigger bonus for centre pawns
170 switch (j) {
171 case 0:
172 case 7:
173 return advancement * 2;
174 case 3:
175 case 4:
176 return advancement * 4;
178 return advancement * 3;
181 inline int Position::pawnProximityToKingBonus(int i, int j, bool white) {
182 int bonus = 0;
183 char p = 'P';
184 char e = 'E';
185 if (white) {
186 p = 'p';
187 e = 'e';
189 for (int y = i - 1; y <= i + 1; ++y) {
190 for (int x = j - 1; x <= j + 1; ++x) {
191 if (x >= 0 && x < 8 && y >= 0 && y < 8 && (board[y][x] == p || board[y][x] == e)) {
192 bonus += 6;
196 // Having no pawns at all in the vincinity is bad. Penalty!
197 if (bonus == 0) {
198 return -8;
200 return bonus;
203 inline int Position::centreBonus(int x) {
204 switch (x) {
205 case 0:
206 case 7:
207 return 0;
208 case 1:
209 case 6:
210 return 5;
211 case 2:
212 case 5:
213 return 10;
214 default:
215 return 15;
219 inline int Position::bishopControlBonus(int i, int j, bool white) {
220 int bonus = 0;
221 // Check upwards to the left
222 int x = j - 1;
223 int y = i - 1;
224 while (x >= 0 && y >= 0) {
225 switch(board[y][x]) {
226 case 'p':
227 case 'P':
228 case 'e':
229 case 'E':
230 goto upright;
231 default:
232 bonus += xrayControlBonus(y,x,white);
234 --x;
235 --y;
238 upright:
239 // Check upwards to the right
240 x = j + 1;
241 y = i - 1;
242 while (x < 8 && y >= 0) {
243 switch(board[y][x]) {
244 case 'p':
245 case 'P':
246 case 'e':
247 case 'E':
248 goto downleft;
249 default:
250 bonus += xrayControlBonus(y,x,white);
252 ++x;
253 --y;
255 downleft:
256 // Check downwards to the left
257 x = j - 1;
258 y = i + 1;
259 while (x >= 0 && y < 8) {
260 switch(board[y][x]) {
261 case 'p':
262 case 'P':
263 case 'e':
264 case 'E':
265 goto downright;
266 default:
267 bonus += xrayControlBonus(y,x,white);
269 --x;
270 ++y;
272 downright:
273 // Check downwards to the right
274 x = j + 1;
275 y = i + 1;
276 while (x < 8 && y < 8) {
277 switch(board[y][x]) {
278 case 'p':
279 case 'P':
280 case 'e':
281 case 'E':
282 return bonus;
283 default:
284 bonus += xrayControlBonus(y,x,white);
286 ++x;
287 ++y;
289 return bonus;
292 inline int Position::rookControlBonus(int i, int j, bool white) {
293 int bonus = 0;
294 // Check to the left
295 int x = j - 1;
296 while (x >= 0) {
297 switch(board[i][x]) {
298 case 'p':
299 case 'P':
300 case 'e':
301 case 'E':
302 goto right;
303 default:
304 bonus += xrayControlBonus(i,x,white);
306 --x;
308 right:
309 // Check to the right
310 x = j + 1;
311 while (x < 8) {
312 switch(board[i][x]) {
313 case 'p':
314 case 'P':
315 case 'e':
316 case 'E':
317 goto up;
318 default:
319 bonus += xrayControlBonus(i,x,white);
321 ++x;
324 // Check upwards
325 int y = i - 1;
326 while (y >= 0) {
327 switch(board[y][j]) {
328 case 'p':
329 case 'P':
330 case 'e':
331 case 'E':
332 goto down;
333 default:
334 bonus += xrayControlBonus(y,j,white);
336 --y;
338 down:
339 // Check downwards
340 y = i + 1;
341 while (y < 8) {
342 switch(board[y][j]) {
343 case 'p':
344 case 'P':
345 case 'e':
346 case 'E':
347 return bonus;
348 default:
349 bonus += xrayControlBonus(y,j,white);
351 ++y;
353 return bonus;
356 inline int Position::xrayControlBonus(int i, int j, bool white) {
357 int bonus = 0;
358 bool whiteSide = true;
359 if (i < 4) {
360 whiteSide = false;
362 switch(board[i][j]) {
363 case 'b':
364 case 'B':
365 case 'n':
366 case 'N':
367 case ' ':
368 break;
369 default:
370 // Threatening a valuable (r, q or k) piece even as an "x-ray" attack
371 // is worth a bonus.
372 if ((white && board[i][j] < 'Z') || (!white && board[i][j] > 'a')) {
373 bonus += 8;
376 // If white and whiteSide are the same, increment bonus by 1, otherwise by 2.
377 // (Control of the enemy's squares is more valuable.)
378 if (white == whiteSide) {
379 ++bonus;
381 else {
382 bonus += 2;
384 return bonus;
387 inline void Position::reset() {
388 whiteToMove = true;
389 isCheck = false;
390 for (int i = 0; i < 8; ++i) {
391 for (int j = 0; j < 8; ++j) {
392 board[i][j] = ' ';
395 board[0][0] = 'T';
396 board[0][1] = 'N';
397 board[0][2] = 'B';
398 board[0][3] = 'Q';
399 board[0][4] = 'X';
400 board[0][5] = 'B';
401 board[0][6] = 'N';
402 board[0][7] = 'T';
403 board[7][0] = 't';
404 board[7][1] = 'n';
405 board[7][2] = 'b';
406 board[7][3] = 'q';
407 board[7][4] = 'x';
408 board[7][5] = 'b';
409 board[7][6] = 'n';
410 board[7][7] = 't';
411 for (int i = 0; i < 8; ++i) {
412 board[1][i] = 'P';
413 board[6][i] = 'p';
417 inline void Position::move(int mv, int promotion) {
418 // If a piece is eaten by the move, the position isn't quiet
419 if (board[UNPACK_I2(mv)][UNPACK_J2(mv)] != ' ') {
420 noisiness += 50;
423 // Move the piece
424 board[UNPACK_I2(mv)][UNPACK_J2(mv)] = board[UNPACK_I1(mv)][UNPACK_J1(mv)];
425 board[UNPACK_I1(mv)][UNPACK_J1(mv)] = ' ';
426 // Check for castling
427 if (board[UNPACK_I2(mv)][UNPACK_J2(mv)] == 'x') {
428 if (UNPACK_J1(mv) - UNPACK_J2(mv) == 2) {
429 board[7][3] = 'r';
430 board[7][0] = ' ';
432 else if (UNPACK_J1(mv) - UNPACK_J2(mv) == -2) {
433 board[7][5] = 'r';
434 board[7][7] = ' ';
437 if (board[UNPACK_I2(mv)][UNPACK_J2(mv)] == 'X') {
438 if (UNPACK_J1(mv) - UNPACK_J2(mv) == 2) {
439 board[0][3] = 'R';
440 board[0][0] = ' ';
442 else if (UNPACK_J1(mv) - UNPACK_J2(mv) == -2) {
443 board[0][5] = 'R';
444 board[0][7] = ' ';
448 if (whiteToMove) {
449 // Check for en passant
450 if (board[UNPACK_I2(mv)][UNPACK_J2(mv)] == 'p' && board[UNPACK_I2(mv) + 1][UNPACK_J2(mv)] == 'E') {
451 board[UNPACK_I2(mv) + 1][UNPACK_J2(mv)] = ' ';
453 // The right to en passant is one-turn-only. Set all E's to P's.
454 for (int j = 0; j < 8; ++j) {
455 if (board[3][j] == 'E') {
456 board[3][j] = 'P';
459 // Check for pawn promotion
460 if (UNPACK_I2(mv) == 0 && board[UNPACK_I2(mv)][UNPACK_J2(mv)] == 'p') {
461 switch (promotion) {
462 case 1:
463 board[UNPACK_I2(mv)][UNPACK_J2(mv)] = 'r';
464 break;
465 case 2:
466 board[UNPACK_I2(mv)][UNPACK_J2(mv)] = 'b';
467 break;
468 case 3:
469 board[UNPACK_I2(mv)][UNPACK_J2(mv)] = 'n';
470 break;
471 default:
472 board[UNPACK_I2(mv)][UNPACK_J2(mv)] = 'q';
476 else {
477 // Same checks for black
478 if (board[UNPACK_I2(mv)][UNPACK_J2(mv)] == 'P' && board[UNPACK_I2(mv) - 1][UNPACK_J2(mv)] == 'e') {
479 board[UNPACK_I2(mv) - 1][UNPACK_J2(mv)] = ' ';
482 for (int j = 0; j < 8; ++j) {
483 if (board[4][j] == 'e') {
484 board[4][j] = 'p';
487 // Check for pawn promotion
488 if (UNPACK_I2(mv) == 7 && board[UNPACK_I2(mv)][UNPACK_J2(mv)] == 'P') {
489 switch (promotion) {
490 case 1:
491 board[UNPACK_I2(mv)][UNPACK_J2(mv)] = 'R';
492 break;
493 case 2:
494 board[UNPACK_I2(mv)][UNPACK_J2(mv)] = 'B';
495 break;
496 case 3:
497 board[UNPACK_I2(mv)][UNPACK_J2(mv)] = 'N';
498 break;
499 default:
500 board[UNPACK_I2(mv)][UNPACK_J2(mv)] = 'Q';
504 // Check whether a piece was moved that requires a change of status
505 // due to the movement
506 switch(board[UNPACK_I2(mv)][UNPACK_J2(mv)]) {
507 case 'x':
508 case 'X':
509 board[UNPACK_I2(mv)][UNPACK_J2(mv)] -= 13; // X -> K, x -> k
510 break;
511 case 't':
512 case 'T':
513 board[UNPACK_I2(mv)][UNPACK_J2(mv)] -= 2; // T -> R, t -> r
514 break;
515 case 'p':
516 case 'P':
517 if (std::abs(UNPACK_I1(mv) - UNPACK_I2(mv)) > 1) {
518 board[UNPACK_I2(mv)][UNPACK_J2(mv)] -= 11; // P -> E, p -> e
520 break;
522 whiteToMove = !whiteToMove;
523 legalMoves.clear();
524 generator->setPosition(this);
525 generator->getAllLegalMoves(&legalMoves);
526 isCheck = !generator->isKingSafe(0,0,0,0);
529 inline void Position::print() {
530 for (int i = 0; i < 8; ++i) {
531 std::cout << " +---+---+---+---+---+---+---+---+" << std::endl;
532 std::cout << 8 - i;
533 for (int j = 0; j < 8; ++j) {
534 std::cout << " | " << translate(board[i][j]);
536 std::cout << " |" << std::endl;
538 std::cout << " +---+---+---+---+---+---+---+---+" << std::endl;
539 std::cout << " A B C D E F G H" << std::endl << std::endl;
542 inline void Position::getBoard(char b[8][8], int *wkx, int *wky, int *bkx, int *bky) {
543 for (int i = 0; i < 8; ++i) {
544 for (int j = 0; j < 8; ++j) {
545 if (board[i][j] == 'K' || board[i][j] == 'X') {
546 *bkx = j;
547 *bky = i;
549 if (board[i][j] == 'k' || board[i][j] == 'x') {
550 *wkx = j;
551 *wky = i;
553 b[i][j] = board[i][j];
558 inline bool Position::isWhiteToMove() {
559 return whiteToMove;
562 bool Position::isGameOver() {
563 return gameOver;
566 int Position::evaluate() {
567 if (gameOver) {
568 if (isCheck) {
569 if (whiteToMove) {
570 // White is checkmated
571 return std::numeric_limits<int>::min();
573 else {
574 // Black is checkmated
575 return std::numeric_limits<int>::max();
578 else {
579 // Stalemate or two kings butting heads
580 return 0;
583 int white = 0;
584 int black = 0;
585 int wkx = 0, wky = 0, bkx = 0, bky = 0;
586 for (int i = 0; i < 8; ++i) {
587 for (int j = 0; j < 8; ++j) {
588 switch(board[i][j]) {
589 case 'R':
590 case 'T':
591 black += 520;
592 black += rookControlBonus(i,j,false);
593 break;
594 case 'B':
595 black += 330;
596 black += bishopControlBonus(i,j,false);
597 break;
598 case 'N':
599 black += 330;
600 black += centreBonus(i);
601 black += centreBonus(j);
602 break;
603 case 'Q':
604 black += 950;
605 break;
606 case 'P':
607 case 'E':
608 black += 100;
609 black -= pawnIsolationPenalty(j,false);
610 // black -= pawnBackwardPenalty(i,j,false);
611 black += pawnAdvancementBonus(i,j,false);
612 break;
613 case 'K':
614 case 'X':
615 bkx = j;
616 bky = i;
617 break;
618 case 'r':
619 case 't':
620 white += 520;
621 white += rookControlBonus(i,j,true);
622 break;
623 case 'b':
624 white += 330;
625 white += bishopControlBonus(i,j,true);
626 break;
627 case 'n':
628 white += 330;
629 white += centreBonus(i);
630 white += centreBonus(j);
631 break;
632 case 'q':
633 white += 950;
634 break;
635 case 'p':
636 case 'e':
637 white += 100;
638 white -= pawnIsolationPenalty(j,true);
639 // white -= pawnBackwardPenalty(i,j,true);
640 white += pawnAdvancementBonus(i,j,true);
641 break;
642 case 'k':
643 case 'x':
644 wkx = j;
645 wky = i;
646 break;
650 // Before the endgame we don't want the king in the centre of the board
651 if (white + black > 1700) {
652 black -= centreBonus(bkx);
653 black -= centreBonus(bky);
654 white -= centreBonus(wkx);
655 white -= centreBonus(wky);
656 black += pawnProximityToKingBonus(bky,bkx,false);
657 white += pawnProximityToKingBonus(wky,wkx,true);
659 // But during the endgame he's welcome.
660 else {
661 black += centreBonus(bkx);
662 black += centreBonus(bky);
663 white += centreBonus(wkx);
664 white += centreBonus(wky);
666 return white - black;
669 inline std::vector<int>* Position::getLegalMoves() {
670 return &legalMoves;
673 inline MovementGenerator* Position::getMovementGenerator() {
674 return generator;
677 long Position::getHash() {
678 long hash = 0;
679 // Hash piece positions
680 for (int i = 0; i < 8; ++i) {
681 for (int j = 0; j < 8; ++j) {
682 switch (board[i][j]) {
683 case 'P':
684 hash = hash ^ (1 << (i * 8 + j)) ^ 1;
685 break;
686 case 'E':
687 hash = hash ^ (1 << (i * 8 + j)) ^ 2;
688 break;
689 case 'T':
690 case 'R':
691 hash = hash ^ (1 << (i * 8 + j)) ^ 4;
692 break;
693 case 'N':
694 hash = hash ^ (1 << (i * 8 + j)) ^ 8;
695 break;
696 case 'B':
697 hash = hash ^ (1 << (i * 8 + j)) ^ 16;
698 break;
699 case 'Q':
700 hash = hash ^ (1 << (i * 8 + j)) ^ 32;
701 break;
702 case 'K':
703 case 'X':
704 hash = hash ^ (1 << (i * 8 + j)) ^ 64;
705 break;
706 case 'p':
707 hash = hash ^ (1 << (i * 8 + j)) ^ 128;
708 break;
709 case 'e':
710 hash = hash ^ (1 << (i * 8 + j)) ^ 256;
711 break;
712 case 't':
713 case 'r':
714 hash = hash ^ (1 << (i * 8 + j)) ^ 512;
715 break;
716 case 'n':
717 hash = hash ^ (1 << (i * 8 + j)) ^ 1024;
718 break;
719 case 'b':
720 hash = hash ^ (1 << (i * 8 + j)) ^ 2048;
721 break;
722 case 'q':
723 hash = hash ^ (1 << (i * 8 + j)) ^ 4096;
724 break;
725 case 'k':
726 case 'x':
727 hash = hash ^ (1 << (i * 8 + j)) ^ 8192;
728 break;
732 // Hash castling rights
733 if (board[0][4] == 'X') {
734 if (board[0][0] == 'T') {
735 hash = hash ^ 16384;
737 if (board[0][7] == 'T') {
738 hash = hash ^ 32768;
741 if (board[7][4] == 'x') {
742 if (board[7][0] == 't') {
743 hash = hash ^ 65536;
745 if (board[7][7] == 't') {
746 hash = hash ^ 131072;
749 // Hash turn
750 if (whiteToMove) {
751 hash = hash ^ 262144;
753 return hash;