/*************************************************************************** * Brutal Chess * http://brutalchess.sf.net * * File : brutalplayer.cpp * Authors : Mike Cook, Joe Flint, Neil Pankey **************************************************************************/ #include "board.h" #include "chessplayer.h" #include "options.h" #include #include using namespace std; BrutalPlayer::BrutalPlayer() { m_ply = Options::getInstance()->brutalplayer2ply; m_trustworthy = true; srand(time(NULL)); } void BrutalPlayer::think(const ChessGameState & cgs) { BoardMove move; Board board = cgs.getBoard(); search(board, getColor(), m_ply, -INT_MAX, INT_MAX, move); m_move = move; } int BrutalPlayer::search(Board board, Piece::Color color, int depth, int alpha, int beta, BoardMove& move) { BoardMove testMove; Board testBoard = board; int moveScore, bestScore = -INT_MAX; vector moves = board.possibleMoves(color); bool gotmove = false; for(int i=0; i < moves.size(); i++) { if(!board.isMoveLegal(moves[i])) { continue; } if(!gotmove) { move = moves[i]; gotmove = true; } testBoard = board; testBoard.update(moves[i]); if(depth == 0) { moveScore = evaluateBoard(testBoard, color); } else { moveScore = -search(testBoard, Piece::opposite(color), depth-1, -beta, -alpha, testMove); } if(moveScore > bestScore) { bestScore = moveScore; move = moves[i]; } if(bestScore > alpha) { alpha = bestScore; } if(alpha >= beta) { return beta; } } return bestScore; } int BrutalPlayer::evaluateBoard(const Board & board, Piece::Color turn) { vector< vector > locations(Piece::LAST_TYPE+1); // Raw material balance int balance = 0; int endgamecount = 0; int queenval = 900, rookval = 500, bishopval = 325, knightval = 310, pawnval = 100, kingval = 100000; for(int i=0; i < 64; i++) { BoardPosition bp = BoardPosition(i); if(board.isOccupied(bp)) { Piece *p = board.getPiece(bp); switch(p->type()) { case Piece::QUEEN: locations[Piece::QUEEN].push_back(bp); balance += (p->color() == turn) ? queenval : -queenval; endgamecount += queenval; break; case Piece::ROOK: locations[Piece::ROOK].push_back(bp); balance += (p->color() == turn) ? rookval : -rookval; endgamecount += rookval; break; case Piece::BISHOP: locations[Piece::BISHOP].push_back(bp); balance += (p->color() == turn) ? bishopval : -bishopval; endgamecount += bishopval; break; case Piece::KNIGHT: locations[Piece::KNIGHT].push_back(bp); balance += (p->color() == turn) ? knightval : -knightval; endgamecount += knightval; break; case Piece::PAWN: locations[Piece::PAWN].push_back(bp); balance += (p->color() == turn) ? pawnval : -pawnval; endgamecount += pawnval; break; case Piece::KING: locations[Piece::KING].push_back(bp); if ((p->color() == turn) && !board.isCheckMate(turn)) { balance += kingval; } else if ((p->color() != turn) && !board.isCheckMate(turn)) { balance -= kingval; } break; } } } // Determine end game or not. THIS IS A TWEAKABLE VALUE. bool endgame = false; if (endgamecount < 3500) { endgame = true; } for(int i=0; i < locations[Piece::QUEEN].size(); i++) { balance += queenBonus(locations[Piece::QUEEN][i], board, turn, endgame); } for(int i=0; i < locations[Piece::ROOK].size(); i++) { balance += rookBonus(locations[Piece::ROOK][i], board, turn, endgame); } for(int i=0; i < locations[Piece::BISHOP].size(); i++) { balance += bishopBonus(locations[Piece::BISHOP][i], board, turn, endgame); } for(int i=0; i < locations[Piece::KNIGHT].size(); i++) { balance += knightBonus(locations[Piece::KNIGHT][i], board, turn, endgame); } for(int i=0; i < locations[Piece::PAWN].size(); i++) { balance += pawnBonus(locations[Piece::PAWN][i], board, turn, endgame); } for(int i=0; i < locations[Piece::KING].size(); i++) { balance += kingBonus(locations[Piece::KING][i], board, turn, endgame); } return balance; } // Queen Bonuses are finished, queens get bonuses for being near an opposing king. int BrutalPlayer::queenBonus(const BoardPosition & bp, const Board & board, Piece::Color turn, bool endgame) { if (!endgame) { return 0; } Piece * p = board.getPiece(bp); int bonus = 0; BoardMove dist; if (p->color() == Piece::WHITE) { dist = BoardMove(bp, board.getKing(Piece::BLACK), p); } else { dist = BoardMove(bp, board.getKing(Piece::WHITE), p); } bonus = -2*abs(dist.fileDiff()+dist.rankDiff()); return (turn == p->color()) ? bonus : -bonus; } int BrutalPlayer::rookBonus(const BoardPosition & bp, const Board & board, Piece::Color turn, bool endgame) { int bonus = 0; // Rooks get a bonus of 0 points if blocked, or up to 20 points if // attacking 12 squares or more. unsigned long long rookAttacks = Board::rankAttacks[bp.hash()][board.getRankState(bp)] || Board::fileAttacks[bp.hash()][board.getFileState(bp)]; int numAttacked = numAttackedSquares(rookAttacks); bonus += (numAttacked < 12) ? 2*numAttacked-4 : 20; return bonus; } int BrutalPlayer::bishopBonus(const BoardPosition & bp, const Board & board, Piece::Color turn, bool endgame) { Piece::Color color = board.getPiece(bp)->color(); int bonus = m_bishop[bp.hash()]; return (turn == color) ? bonus: -bonus; } int BrutalPlayer::knightBonus(const BoardPosition & bp, const Board & board, Piece::Color turn, bool endgame) { Piece::Color color = board.getPiece(bp)->color(); int bonus = m_knight[bp.hash()]; bool drivenBonus = false; // Knights are given a bonus for being within 2 squares of each enemy piece. return (turn == color) ? bonus : -bonus; } int BrutalPlayer::pawnBonus(const BoardPosition & bp, const Board & board, Piece::Color turn, bool endgame) { Piece::Color color = board.getPiece(bp)->color(); int bonus = (color == Piece::WHITE) ? m_wpawn[bp.hash()] : m_bpawn[bp.hash()]; if(isIsolatedPawn(bp, board)) { char file = bp.filec(); if(file == 'a' || file == 'h') { bonus -= 12; } else if (file == 'b' || file == 'g') { bonus -= 14; } else if (file == 'c' || file == 'f') { bonus -= 16; } else if (file == 'd' || file == 'e') { bonus -= 20; } } /* if(isDoubledPawn(bp, board)) { } */ return (turn == color) ? bonus : -bonus; } int BrutalPlayer::kingBonus(const BoardPosition & bp, const Board & board, Piece::Color turn, bool endgame) { Piece::Color color = board.getPiece(bp)->color(); int bonus = 0; // Count all pieces attacking the king unsigned long long attacked = board.isAttacked(bp, color); for(int i=0; i < 64; i++) { bonus -= 25*((attacked >> i) & 1); } if(endgame) { bonus = m_end_king[bp.hash()]; } else { bonus = (color == Piece::WHITE) ? m_wking[bp.hash()] : m_bking[bp.hash()]; } return (turn == color) ? bonus : -bonus; } int BrutalPlayer::numAttackedSquares(const unsigned long long & pieceAttacks) { int numSquares = 0; for (int i = 0; i < 64; i++) { if (pieceAttacks && (1LL << i)) { numSquares++; } } return numSquares; } bool BrutalPlayer::isIsolatedPawn(const BoardPosition & bp, const Board & board) { Piece::Color c = board.getPiece(bp)->color(); unsigned long long mask = board.m_color[c] & board.m_pieces[Piece::PAWN]; BoardPosition test = bp.E(); if(test.isValid() && (mask & maskFile(test))) { return false; } test = bp.W(); if(test.isValid() && (mask & maskFile(test))) { return false; } return true; } bool BrutalPlayer::isDoubledPawn(const BoardPosition & bp, const Board & board) { return false; } // Positional Bonuses int BrutalPlayer::m_bishop[64] = { -5,-5,-5,-5,-5,-5,-5,-5, -5,10,5,10,10,5,10,-5, -5,5,3,12,12,3,5,-5, -5,3,12,3,3,12,3,-5, -5,3,12,3,3,12,3,-5, -5,5,3,12,12,3,5,-5, -5,10,5,10,10,5,10,-5, -5,-5,-5,-5,-5,-5,-5,-5 }; int BrutalPlayer::m_knight[64] = { -10,-5,-5,-5,-5,-5,-5,-10, -5,0,0,3,3,0,0,-5, -5,0,5,5,5,5,0,-5, -5,0,5,10,10,5,0,-5, -5,0,5,10,10,5,0,-5, -5,0,5,5,5,5,0,-5, -5,0,0,3,3,0,0,-5, -10,-5,-5,-5,-5,-5,-5,-10 }; int BrutalPlayer::m_wpawn[64] = { 0,0,0,0,0,0,0,0, 0,0,0,-5,-5,0,0,0, 1,2,3,4,4,3,2,1, 2,4,6,8,8,6,4,2, 3,6,9,12,12,9,6,3, 4,8,12,16,16,12,8,4, 5,10,15,20,20,15,10,5, 0,0,0,0,0,0,0,0 }; int BrutalPlayer::m_bpawn[64] = { 0,0,0,0,0,0,0,0, 5,10,15,20,20,15,10,5, 4,8,12,16,16,12,8,4, 3,6,9,12,12,9,6,3, 2,4,6,8,8,6,4,2, 1,2,3,4,4,3,2,1, 0,0,0,-5,-5,0,0,0, 0,0,0,0,0,0,0,0 }; int BrutalPlayer::m_wking[64] = { 2,10,4,0,0,7,10,2, -3,-3,-5,-5,-5,-5,-3,-3, -5,-5,-8,-8,-8,-8,-5,-5, -8,-8,-13,-13,-13,-13,-8,-8, -13,-13,-21,-21,-21,-21,-13,-13, -21,-21,-34,-34,-34,-34,-21,-21, -34,-34,-55,-55,-55,-55,-34,-34, -55,-55,-89,-89,-89,-89,-55,-55}; int BrutalPlayer::m_bking[64] = { -55,-55,-89,-89,-89,-89,-55,-55, -34,-34,-55,-55,-55,-55,-34,-34, -21,-21,-34,-34,-34,-34,-21,-21, -13,-13,-21,-21,-21,-21,-13,-13, -8,-8,-13,-13,-13,-13,-8,-8, -5,-5,-8,-8,-8,-8,-5,-5, -3,-3,-5,-5,-5,-5,-3,-3, 2,10,4,0,0,7,10,2}; int BrutalPlayer::m_end_king[64] = { -5,-3,-1,0,0,-1,-3,-5, -3,5,5,5,5,5,5,-3, -1,5,10,10,10,10,5,-1, 0,5,10,15,15,10,5,0, 0,5,10,15,15,10,5,0, -1,5,10,10,10,10,5,-1, -3,5,5,5,5,5,5,-3, -5,-3,-1,0,0,-1,-3,-5}; // end of file brutalplayer.cpp