|
@@ -1,27 +1,75 @@
|
|
|
#include "Minimax.h"
|
|
|
#include "ChessGame.h"
|
|
|
|
|
|
+#include <limits>
|
|
|
+
|
|
|
using namespace chessy;
|
|
|
|
|
|
+template MiniMax::BestMove MiniMax::minimax<WHITE_SIDE>(int);
|
|
|
+template MiniMax::BestMove MiniMax::minimax<BLACK_SIDE>(int);
|
|
|
+
|
|
|
+#include <iostream>
|
|
|
+Move MiniMax::calculateBest(int depth)
|
|
|
+{
|
|
|
+ if (game.getTurn() == ChessGame::Turn::WHITE) {
|
|
|
+ BestMove bm = minimax<WHITE_SIDE>(depth);
|
|
|
+ return bm.move;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ return minimax<BLACK_SIDE>(depth).move;
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
template<Side side>
|
|
|
-float chessy::MiniMax::evaluate(void) const
|
|
|
+float MiniMax::evaluate(void) const
|
|
|
{
|
|
|
- return 0.0f;
|
|
|
+ int piecePoints = 0;
|
|
|
+ Board& bd = game.getBoard();
|
|
|
+ Bitboard p = bd.getPawns<side>();
|
|
|
+ Bitboard n = bd.getKnights<side>();
|
|
|
+ Bitboard b = bd.getBishops<side>();
|
|
|
+ Bitboard r = bd.getRooks<side>();
|
|
|
+ Bitboard q = bd.getQueens<side>();
|
|
|
+ Bitboard k = bd.getKing<side>();
|
|
|
+ piecePoints += 1 * p.popcount();
|
|
|
+ piecePoints += 3 * n.popcount();
|
|
|
+ piecePoints += 3 * b.popcount();
|
|
|
+ piecePoints += 4 * r.popcount();
|
|
|
+ piecePoints += 6 * q.popcount();
|
|
|
+ if (k == Bitboard(0ULL))
|
|
|
+ piecePoints -= 100000;
|
|
|
+
|
|
|
+ const Side other = otherSide(side);
|
|
|
+ p = bd.getPawns<other>();
|
|
|
+ n = bd.getKnights<other>();
|
|
|
+ b = bd.getBishops<other>();
|
|
|
+ r = bd.getRooks<other>();
|
|
|
+ q = bd.getQueens<other>();
|
|
|
+ k = bd.getKing<other>();
|
|
|
+ piecePoints -= 1 * p.popcount();
|
|
|
+ piecePoints -= 3 * n.popcount();
|
|
|
+ piecePoints -= 3 * b.popcount();
|
|
|
+ piecePoints -= 4 * r.popcount();
|
|
|
+ piecePoints -= 6 * q.popcount();
|
|
|
+ if (k == Bitboard(0ULL))
|
|
|
+ piecePoints += 100000;
|
|
|
+
|
|
|
+ return piecePoints;
|
|
|
}
|
|
|
|
|
|
|
|
|
template<Side side>
|
|
|
-float MiniMax::minimax(int depth)
|
|
|
+MiniMax::BestMove MiniMax::minimax(int depth)
|
|
|
{
|
|
|
if (depth == 0) {
|
|
|
- return { {0, 0}, evaluate() };
|
|
|
+ return { {0, 0}, -evaluate<side>() };
|
|
|
}
|
|
|
- BestMove bestMove = { {0, 0}, 0.0f };
|
|
|
|
|
|
+ BestMove bestMove = { {0, 0}, -std::numeric_limits<float>::infinity() };
|
|
|
+
|
|
|
+ Board& board = game.getBoard();
|
|
|
Bitboard friends;
|
|
|
Bitboard enemies;
|
|
|
- Board& board = game.getBoard();
|
|
|
if (side == WHITE_SIDE) {
|
|
|
friends = board.getWhites();
|
|
|
enemies = board.getBlacks();
|
|
@@ -31,47 +79,123 @@ float MiniMax::minimax(int depth)
|
|
|
enemies = board.getWhites();
|
|
|
}
|
|
|
|
|
|
+ const Board temp = board;
|
|
|
PawnPushGenerator<side> mg{ game };
|
|
|
for (Move push : mg) {
|
|
|
Bitboard& pawns = board.getPawns<side>();
|
|
|
- Bitboard temp = pawns;
|
|
|
- pawns &= ~(Bitboard::fromIndex(push.origin));
|
|
|
- pawns |= Bitboard::fromIndex(push.destination);
|
|
|
- bestMove.overwriteIfBetter(minmax<!side>(depth - 1));
|
|
|
+ Bitboard pTemp = pawns;
|
|
|
+ pawns.applyMove(push);
|
|
|
+ BestMove m = minimax<otherSide(side)>(depth - 1);
|
|
|
+ m.move = push;
|
|
|
+ bestMove.overwriteIfBetter(m);
|
|
|
+ pawns = pTemp;
|
|
|
}
|
|
|
-
|
|
|
- PositionSet knights = game.getBoard().getBlackKnights();
|
|
|
+
|
|
|
+ PawnDoublePushGenerator<side> dp{ game };
|
|
|
+ for (Move push : dp) {
|
|
|
+ Bitboard& pawns = board.getPawns<side>();
|
|
|
+ Bitboard pTemp = pawns;
|
|
|
+ pawns.applyMove(push);
|
|
|
+ BestMove m = minimax<otherSide(side)>(depth - 1);
|
|
|
+ m.move = push;
|
|
|
+ bestMove.overwriteIfBetter(m);
|
|
|
+ pawns = pTemp;
|
|
|
+ }
|
|
|
+
|
|
|
+ PawnCaptureGenerator<side, LEFT> pl{ game };
|
|
|
+ for (Move capture : pl) {
|
|
|
+ Bitboard& pawns = board.getPawns<side>();
|
|
|
+ board.removeAt(capture.destination);
|
|
|
+ pawns.applyMove(capture);
|
|
|
+ BestMove m = minimax<otherSide(side)>(depth - 1);
|
|
|
+ m.move = capture;
|
|
|
+ bestMove.overwriteIfBetter(m);
|
|
|
+ board = temp;
|
|
|
+ }
|
|
|
+
|
|
|
+ PawnCaptureGenerator<side, RIGHT> pr{ game };
|
|
|
+ for (Move capture : pr) {
|
|
|
+ Bitboard& pawns = board.getPawns<side>();
|
|
|
+ board.removeAt(capture.destination);
|
|
|
+ pawns.applyMove(capture);
|
|
|
+ BestMove m = minimax<otherSide(side)>(depth - 1);
|
|
|
+ m.move = capture;
|
|
|
+ bestMove.overwriteIfBetter(m);
|
|
|
+ board = temp;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ Bitboard& ns = board.getKnights<side>();
|
|
|
+ PositionSet knights { ns };
|
|
|
for (auto knight : knights) {
|
|
|
- for (auto pos : KnightMoveGenerator{ knight, blacks }) {
|
|
|
- ret.push_back(Move{ knight, pos });
|
|
|
+ for (auto pos : KnightMoveGenerator{ knight, friends }) {
|
|
|
+ Move move = { knight, pos };
|
|
|
+ board.removeAt(move.destination);
|
|
|
+ ns.applyMove(move);
|
|
|
+ BestMove m = minimax<otherSide(side)>(depth - 1);
|
|
|
+ m.move = move;
|
|
|
+ bestMove.overwriteIfBetter(m);
|
|
|
+ board = temp;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- PositionSet bishops = cg.getBoard().getBlackBishops();
|
|
|
+ Bitboard& bs = board.getBishops<side>();
|
|
|
+ PositionSet bishops { bs };
|
|
|
for (auto bishop : bishops) {
|
|
|
- for (auto pos : PrimitiveBishopMoveGenerator{ bishop, whites, blacks }) {
|
|
|
- ret.push_back(Move{ bishop, pos });
|
|
|
+ for (auto pos : PrimitiveBishopMoveGenerator{ bishop, enemies, friends }) {
|
|
|
+ Move move = { bishop, pos };
|
|
|
+ board.removeAt(move.destination);
|
|
|
+ bs.applyMove(move);
|
|
|
+ BestMove m = minimax<otherSide(side)>(depth - 1);
|
|
|
+ m.move = move;
|
|
|
+ bestMove.overwriteIfBetter(m);
|
|
|
+ board = temp;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- PositionSet rooks = cg.getBoard().getBlackRooks();
|
|
|
+ Bitboard& rs = board.getRooks<side>();
|
|
|
+ PositionSet rooks { rs };
|
|
|
for (auto rook : rooks) {
|
|
|
- for (auto pos : PrimitiveRookMoveGenerator{ rook, whites, blacks }) {
|
|
|
- //cout << "rook: " << Move(rook, pos).asString() << endl;
|
|
|
+ for (auto pos : PrimitiveRookMoveGenerator{ rook, enemies, friends }) {
|
|
|
+ Move move = { rook, pos };
|
|
|
+ board.removeAt(move.destination);
|
|
|
+ rs.applyMove(move);
|
|
|
+ BestMove m = minimax<otherSide(side)>(depth - 1);
|
|
|
+ m.move = move;
|
|
|
+ bestMove.overwriteIfBetter(m);
|
|
|
+ board = temp;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- PositionSet queens = cg.getBoard().getBlackQueens();
|
|
|
+ Bitboard& qs = board.getQueens<side>();
|
|
|
+ PositionSet queens { qs };
|
|
|
for (auto queen : queens) {
|
|
|
- for (auto pos : PrimitiveQueenMoveGenerator{ queen, whites, blacks }) {
|
|
|
- //cout << "queen: " << Move(queen, pos).asString() << endl;
|
|
|
+ for (auto pos : PrimitiveQueenMoveGenerator{ queen, enemies, friends }) {
|
|
|
+ Move move = { queen, pos };
|
|
|
+ board.removeAt(move.destination);
|
|
|
+ qs.applyMove(move);
|
|
|
+ BestMove m = minimax<otherSide(side)>(depth - 1);
|
|
|
+ m.move = move;
|
|
|
+ bestMove.overwriteIfBetter(m);
|
|
|
+ board = temp;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- Bitboard king = cg.getBoard().getBlackKings();
|
|
|
+ Bitboard& king = board.getKing<side>();
|
|
|
Index kingIndex = king.getLeastSignificantBit();
|
|
|
- for (auto pos : KingMoveGenerator{ king, blacks }) {
|
|
|
- //cout << "king: " << Move(kingIndex, pos).asString() << endl;
|
|
|
+ for (auto pos : KingMoveGenerator{ king, friends }) {
|
|
|
+ Move move = { kingIndex, pos };
|
|
|
+ board.removeAt(pos);
|
|
|
+ king.applyMove(move);
|
|
|
+ BestMove m = minimax<otherSide(side)>(depth - 1);
|
|
|
+ m.move = move;
|
|
|
+ //if (depth >= 3)
|
|
|
+ // std::cout << m.move.asString() << " " << bestMove.value << " -> " << m.value << std::endl;
|
|
|
+ bestMove.overwriteIfBetter(m);
|
|
|
+ board = temp;
|
|
|
}
|
|
|
- return {0, 0};
|
|
|
+ float v = evaluate<side>();
|
|
|
+ bestMove.value *= 0.9f;
|
|
|
+ bestMove.value += v * 0.2f;
|
|
|
+ return -bestMove;
|
|
|
}
|