Ver código fonte

added more rules

nicolaswinkler 7 anos atrás
pai
commit
18c2beba36
13 arquivos alterados com 564 adições e 106 exclusões
  1. 46 8
      src/BitBoard.h
  2. 12 0
      src/BitOperations.h
  3. 42 9
      src/Board.cpp
  4. 34 13
      src/Board.h
  5. 15 0
      src/ChessGame.cpp
  6. 8 4
      src/ChessGame.h
  7. 151 27
      src/Minimax.cpp
  8. 9 7
      src/Minimax.h
  9. 71 6
      src/MoveGeneration.cpp
  10. 74 21
      src/MoveGeneration.h
  11. 86 10
      src/UciParser.cpp
  12. 15 0
      src/UciParser.h
  13. 1 1
      src/makefile

+ 46 - 8
src/BitBoard.h

@@ -10,7 +10,9 @@
 
 namespace chessy
 {
-    union Index;
+    struct Index;
+
+    struct Move;
 
     struct Bitboard;
 
@@ -27,14 +29,14 @@ namespace chessy
 /*!
  * data structure to index one field on a chess board
  */
-union chessy::Index
+struct chessy::Index
 {
     int8_t index;
 
     Index(void) = default;
     inline constexpr Index(int8_t ind) : index{ ind } {}
     inline Index(const std::string& name) :
-        Index{ name[0] - '1', name[1] - 'a' } {}
+        Index{ name[1] - '1', name[0] - 'a' } {}
     inline constexpr Index(int row, int column) :
         index{int8_t(((row & 0x7) << 3) + (column & 0x7))} {}
 
@@ -51,6 +53,36 @@ union chessy::Index
 };
 
 
+struct chessy::Move
+{
+    Index origin;
+    Index destination;
+
+    Move            (void)          = default;
+    Move            (const Move&)   = default;
+    Move            (Move&&)        = default;
+    ~Move           (void)          = default;
+    Move& operator= (const Move&)   = default;
+    Move& operator= (Move&&)        = default;
+
+    inline Move(const std::string& move)
+    {
+        if (move.length() < 4)
+            return;
+        origin = Index { move.substr(0, 2) };
+        destination = Index { move.substr(2, 2) };
+    }
+
+    inline Move(Index origin, Index destination) :
+        origin{ origin }, destination{ destination } {}
+
+    inline std::string asString(void) const
+    {
+        return origin.getName() + destination.getName();
+    }
+};
+
+
 struct chessy::Bitboard
 {
     U64 bits;
@@ -84,10 +116,10 @@ struct chessy::Bitboard
     inline Bitboard northOne    (void)      { return bits << 8; }
     inline void     moveSouthOne(void)      { bits >>= 8; }
     inline Bitboard southOne    (void)      { return bits >> 8; }
-    inline void     moveWestOne (void)      { bits = (bits & ~aColumn) >> 1; }
-    inline Bitboard westOne     (void)      { return (bits & ~aColumn) >> 1; }
-    inline void     moveEastOne (void)      { bits = (bits & ~hColumn) << 1; }
-    inline Bitboard eastOne     (void)      { return (bits & ~hColumn) << 1; }
+    inline void     moveEastOne (void)      { bits = (bits & ~aColumn) >> 1; }
+    inline Bitboard eastOne     (void)      { return (bits & ~aColumn) >> 1; }
+    inline void     moveWestOne (void)      { bits = (bits & ~hColumn) << 1; }
+    inline Bitboard westOne     (void)      { return (bits & ~hColumn) << 1; }
     inline void     moveNWOne   (void)      { bits = (bits << 7) & ~hColumn; }
     inline Bitboard nwOne       (void)      { return (bits << 7) & ~hColumn; }
     inline void     moveNEOne   (void)      { bits = (bits << 9) & ~aColumn; }
@@ -110,8 +142,15 @@ struct chessy::Bitboard
     inline          operator U64(void) const                { return bits; }
     inline explicit operator bool(void) const               { return bits != 0; }
 
+    inline void applyMove(Move move)
+    {
+        bits &= ~fromIndex(move.origin);
+        bits |= fromIndex(move.destination);
+    }
+
     inline Bitboard mirror(void) const                      { return byteswap(bits); }
     inline Index    getLeastSignificantBit (void) const     { return trailingZeroes(bits); }
+    inline int      popcount    (void) const                { return chessy::popcount(bits); }
 };
 
 
@@ -124,4 +163,3 @@ static_assert(sizeof(chessy::Bitboard) == sizeof(uint64_t),
 
 
 #endif // CHESSY_BITBOARD_H
-

+ 12 - 0
src/BitOperations.h

@@ -65,6 +65,18 @@ namespace chessy
         return 0;
 #endif
     }
+
+    inline int popcount(U64 x) {
+#if __GNUC__ > 4
+        return __builtin_popcount(x);
+#else
+        int result = 0;
+        for (int i = 0; i < 64; i++)
+            if (x & (1ULL << i))
+                result++;
+        return result;
+#endif
+    }
 }
 
 

+ 42 - 9
src/Board.cpp

@@ -11,19 +11,19 @@ using namespace std;
 
 void Board::resetBoard(void)
 {
-    whites[PieceType::PAWN]   = 0x0000000000000081;
+    whites[PieceType::PAWN]   = 0x000000000000FF00;
+    whites[PieceType::ROOK]   = 0x0000000000000081;
     whites[PieceType::KNIGHT] = 0x0000000000000042;
     whites[PieceType::BISHOP] = 0x0000000000000024;
-    whites[PieceType::ROOK]   = 0x0000000000000010;
     whites[PieceType::QUEEN]  = 0x0000000000000008;
-    whites[PieceType::KING]   = 0x000000000000FF00;
+    whites[PieceType::KING]   = 0x0000000000000010;
 
     blacks[PieceType::PAWN]   = 0x00FF000000000000;
-    blacks[PieceType::KNIGHT] = 0x8100000000000000;
-    blacks[PieceType::BISHOP] = 0x4200000000000000;
-    blacks[PieceType::ROOK]   = 0x2400000000000000;
-    blacks[PieceType::QUEEN]  = 0x1000000000000000;
-    blacks[PieceType::KING]   = 0x0800000000000000;
+    blacks[PieceType::ROOK]   = 0x8100000000000000;
+    blacks[PieceType::KNIGHT] = 0x4200000000000000;
+    blacks[PieceType::BISHOP] = 0x2400000000000000;
+    blacks[PieceType::QUEEN]  = 0x0800000000000000;
+    blacks[PieceType::KING]   = 0x1000000000000000;
 }
 
 
@@ -53,6 +53,16 @@ bool Board::tryToMove(Bitboard start, Bitboard end, Bitboard& b)
 }
 
 
+void Board::removeAt(Index i)
+{
+    Bitboard mask = ~Bitboard::fromIndex(i);
+    for (Bitboard& b : whites)
+        b &= mask;
+    for (Bitboard& b : blacks)
+        b &= mask;
+}
+
+
 void Board::setBoard(const std::string& fenPosition)
 {
     int row = 7, column = 0;
@@ -163,10 +173,33 @@ PieceType Board::getBlackAtPosition(Index i) const
 }
 
 
-void Board::applyMove(const Move& move)
+PieceType Board::getAtPosition(Index i) const
+{
+    PieceType b = getWhiteAtPosition(i);
+    if (b != PieceType::EMPTY)
+        return b;
+    return getBlackAtPosition(i);;
+}
+
+
+void Board::move(const Move& move)
 {
     Bitboard start = Bitboard::fromIndex(move.origin);
     Bitboard end = Bitboard::fromIndex(move.destination);
+    for (Bitboard& b : whites) {
+        if (b & start) {
+            b &= ~start;
+            b |= end;
+            return;
+        }
+    }
+    for (Bitboard& b : blacks) {
+        if (b & start) {
+            b &= ~start;
+            b |= end;
+            return;
+        }
+    }
 }
 
 

+ 34 - 13
src/Board.h

@@ -35,6 +35,7 @@ public:
     Board(void) = default;
     Board(const Board&) = default;
     ~Board(void) = default;
+    Board& operator=(const Board&) = default;
 
     /*!
      * resets the board to the chess starting position.
@@ -47,19 +48,36 @@ private:
     bool tryToMove(Bitboard start, Bitboard end, Bitboard& b);
 public:
 
-    Bitboard getWhitePawns  (void) const    { return whites[PAWN]; }
-    Bitboard getWhiteKnights(void) const    { return whites[KNIGHT]; }
-    Bitboard getWhiteBishops(void) const    { return whites[BISHOP]; }
-    Bitboard getWhiteRooks  (void) const    { return whites[ROOK]; }
-    Bitboard getWhiteQueens (void) const    { return whites[QUEEN]; }
-    Bitboard getWhiteKings  (void) const    { return whites[KING]; }
+    inline Bitboard getWhitePawns  (void) const    { return whites[PAWN]; }
+    inline Bitboard getWhiteKnights(void) const    { return whites[KNIGHT]; }
+    inline Bitboard getWhiteBishops(void) const    { return whites[BISHOP]; }
+    inline Bitboard getWhiteRooks  (void) const    { return whites[ROOK]; }
+    inline Bitboard getWhiteQueens (void) const    { return whites[QUEEN]; }
+    inline Bitboard getWhiteKings  (void) const    { return whites[KING]; }
+
+    inline Bitboard getBlackPawns  (void) const    { return blacks[PAWN]; }
+    inline Bitboard getBlackKnights(void) const    { return blacks[KNIGHT]; }
+    inline Bitboard getBlackBishops(void) const    { return blacks[BISHOP]; }
+    inline Bitboard getBlackRooks  (void) const    { return blacks[ROOK]; }
+    inline Bitboard getBlackQueens (void) const    { return blacks[QUEEN]; }
+    inline Bitboard getBlackKings  (void) const    { return blacks[KING]; }
+
+    template<Side side>
+    inline Bitboard* getBitboards  (void) { return side == WHITE_SIDE ? whites : blacks; }
+
+    template<Side side>
+    inline Bitboard& getPawns      (void) { return getBitboards<side>()[PAWN]; }
+    template<Side side>
+    inline Bitboard& getKnights    (void) { return getBitboards<side>()[KNIGHT]; }
+    template<Side side>
+    inline Bitboard& getBishops    (void) { return getBitboards<side>()[BISHOP]; }
+    template<Side side>
+    inline Bitboard& getRooks      (void) { return getBitboards<side>()[ROOK]; }
+    template<Side side>
+    inline Bitboard& getQueens     (void) { return getBitboards<side>()[QUEEN]; }
+    template<Side side>
+    inline Bitboard& getKing       (void) { return getBitboards<side>()[KING]; }
 
-    Bitboard getBlackPawns  (void) const    { return blacks[PAWN]; }
-    Bitboard getBlackKnights(void) const    { return blacks[KNIGHT]; }
-    Bitboard getBlackBishops(void) const    { return blacks[BISHOP]; }
-    Bitboard getBlackRooks  (void) const    { return blacks[ROOK]; }
-    Bitboard getBlackQueens (void) const    { return blacks[QUEEN]; }
-    Bitboard getBlackKings  (void) const    { return blacks[KING]; }
 
     /*!
      * parses the first part of a FEN string and sets the board
@@ -70,11 +88,14 @@ public:
 
     PieceType getWhiteAtPosition(Index i) const;
     PieceType getBlackAtPosition(Index i) const;
+    PieceType getAtPosition(Index i) const;
 
     /*!
      * applies a move to the board
      */
-    void applyMove(const Move& move);
+    void move(const Move& move);
+
+    void removeAt(Index index);
 
     /*!
      * \return

+ 15 - 0
src/ChessGame.cpp

@@ -4,6 +4,11 @@
 
 using namespace chessy;
 
+ChessGame::ChessGame(void)
+{
+    board.resetBoard();
+}
+
 
 ChessGame::ChessGame(const std::string& fenString)
 {
@@ -24,6 +29,16 @@ std::vector<Move> ChessGame::getValidMoves(void) const
 }
 
 
+void ChessGame::move(Move move)
+{
+    board.move(move);
+    if (turn == Turn::BLACK) {
+        moveCount++;
+    }
+    turn = turn == Turn::WHITE ? Turn::BLACK : Turn::WHITE;
+}
+
+
 void ChessGame::loadFromFen(const std::string& fenString)
 {
     using namespace std;

+ 8 - 4
src/ChessGame.h

@@ -26,11 +26,12 @@ class chessy::ChessGame
     //! -1 for no en passant possible
     Index enPassant =              -1;
 
+public:
     enum class Turn { WHITE, BLACK };
-    //! 0 for white, 1 for black
+private:
     Turn turn =                     Turn::WHITE;
 
-    int moveCount =                 0;
+    int moveCount =                 1;
 
     const static char WHITE =       0;
     const static char BLACK =       1;
@@ -38,9 +39,8 @@ class chessy::ChessGame
 
 public:
 
+    ChessGame(void);
     ChessGame(const std::string& fenString);
-
-    ChessGame(void) = default;
     ChessGame(const ChessGame&) = default;
     ~ChessGame(void) = default;
 
@@ -48,6 +48,10 @@ public:
     std::vector<Move> getValidMoves(void) const;
 
     inline const Board& getBoard(void) const { return board; }
+    inline       Board& getBoard(void)       { return board; }
+
+    void move(Move move);
+    inline Turn getTurn(void) const { return turn; }
 
     void loadFromFen(const std::string& fenString);
     std::string generateFen(void) const;

+ 151 - 27
src/Minimax.cpp

@@ -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;
 }

+ 9 - 7
src/Minimax.h

@@ -1,5 +1,6 @@
-#ifndef CHESSY_MINMAX_H
+#ifndef CHESSY_MINIMAX_H
 #define CHESSY_MINIMAX_H
+
 #include "MoveGeneration.h"
 
 
@@ -22,24 +23,25 @@ class chessy::MiniMax
             if (other.value > value)
                 *this = other;
         }
+
+        inline BestMove operator - (void) const
+        {
+            return { move, -value };
+        }
     };
 
 public:
     inline MiniMax(ChessGame& game) :
         game{ game } {}
 
-    Move calculateBest(int depth)
-    {
-        minimax<WHITE_SIDE>(depth);
-        return {0, 0};
-    }
+    Move calculateBest(int depth);
 
     template<Side side>
     float evaluate(void) const;
 
 private:
     template<Side side>
-    float minimax(int depth);
+    BestMove minimax(int depth);
 };
 
 

+ 71 - 6
src/MoveGeneration.cpp

@@ -3,13 +3,18 @@
 
 using namespace chessy;
 
-template class PawnPushGenerator<WHITE_SIDE>;
-template class PawnPushGenerator<BLACK_SIDE>;
+namespace chessy
+{
+    template class PawnPushGenerator<WHITE_SIDE>;
+    template class PawnPushGenerator<BLACK_SIDE>;
 
+    template class PawnDoublePushGenerator<WHITE_SIDE>;
+    template class PawnDoublePushGenerator<BLACK_SIDE>;
 
-std::string Move::asString(void) const
-{
-    return origin.getName() + destination.getName();
+    template class PawnCaptureGenerator<WHITE_SIDE, LEFT>;
+    template class PawnCaptureGenerator<WHITE_SIDE, RIGHT>;
+    template class PawnCaptureGenerator<BLACK_SIDE, LEFT>;
+    template class PawnCaptureGenerator<BLACK_SIDE, RIGHT>;
 }
 
 
@@ -25,7 +30,8 @@ typename PawnPushGenerator<side>::MoveIterator PawnPushGenerator<side>::begin(vo
     else
         movedPieces.moveSouthOne();
     
-    movedPieces &= ~(side == WHITE_SIDE ? board.getBlacks() : board.getWhites());
+    movedPieces &= ~board.getOccupied();
+    //movedPieces &= Bitboard(~0xFF000000000000FFULL);
 
     return MoveIterator{ movedPieces };
 }
@@ -38,6 +44,65 @@ typename PawnPushGenerator<side>::MoveIterator PawnPushGenerator<side>::end(void
 }
 
 
+template<Side side>
+typename PawnDoublePushGenerator<side>::MoveIterator
+    PawnDoublePushGenerator<side>::begin(void) const
+{
+    const Board& board = chessGame.getBoard();
+    Bitboard movedPieces = side == WHITE_SIDE ?
+        board.getWhitePawns() & Bitboard(0xFFULL << 8) :
+        board.getBlackPawns() & Bitboard(0xFFULL << 48);
+    if (side == WHITE_SIDE)
+        movedPieces.moveNorth(2);
+    else
+        movedPieces.moveSouth(2);
+    
+    movedPieces &= ~board.getOccupied();
+
+    return MoveIterator{ movedPieces };
+}
+
+
+template<Side side>
+typename PawnDoublePushGenerator<side>::MoveIterator
+    PawnDoublePushGenerator<side>::end(void) const
+{
+    return MoveIterator{ 0 };
+}
+
+
+template<Side side, Leftright leftright>
+typename PawnCaptureGenerator<side, leftright>::MoveIterator
+    PawnCaptureGenerator<side, leftright>::begin(void) const
+{
+    const Board& board = chessGame.getBoard();
+    Bitboard movedPieces = side == WHITE_SIDE ?
+        board.getWhitePawns() :
+        board.getBlackPawns();
+    if (side == WHITE_SIDE)
+        movedPieces.moveNorthOne();
+    else
+        movedPieces.moveSouthOne();
+    
+    if (leftright == LEFT)
+        movedPieces.moveEastOne();
+    else
+        movedPieces.moveWestOne();
+    
+    movedPieces &= (side == BLACK_SIDE ? board.getWhites() : board.getBlacks());
+    //movedPieces &= Bitboard(~0xFF000000000000FFULL);
+
+    return MoveIterator{ movedPieces };
+}
+
+
+template<Side side, Leftright leftright>
+typename PawnCaptureGenerator<side, leftright>::MoveIterator
+    PawnCaptureGenerator<side, leftright>::end(void) const
+{
+    return MoveIterator{ 0 };
+}
+
 const std::array<Bitboard, 64> KnightMoveGenerator::moveSets = generateKnightMoves();
 
 Bitboard KnightMoveGenerator::generateFromIndex(Index i) {

+ 74 - 21
src/MoveGeneration.h

@@ -11,11 +11,17 @@ namespace chessy
     // forward declaration
     class ChessGame;
 
-    struct Move;
+    using Leftright = int;
+    const Leftright LEFT = -1;
+    const Leftright RIGHT = 1;
 
     class PositionSet;
-    template<int side>
+    template<Side side>
     class PawnPushGenerator;
+    template<Side side>
+    class PawnDoublePushGenerator;
+    template<Side side, Leftright leftright>
+    class PawnCaptureGenerator;
     class KnightMoveGenerator;
     class PrimitiveQueenMoveGenerator;
     class PrimitiveRookMoveGenerator;
@@ -24,25 +30,6 @@ namespace chessy
 }
 
 
-struct chessy::Move
-{
-    Index origin;
-    Index destination;
-
-    Move            (void)          = default;
-    Move            (const Move&)   = default;
-    Move            (Move&&)        = default;
-    ~Move           (void)          = default;
-    Move& operator= (const Move&)   = default;
-    Move& operator= (Move&&)        = default;
-
-    inline Move(Index origin, Index destination) :
-        origin{ origin }, destination{ destination } {}
-
-    std::string asString(void) const;
-};
-
-
 class chessy::PositionSet
 {
     Bitboard bitboard;
@@ -114,6 +101,72 @@ public:
 };
 
 
+template<chessy::Side side>
+class chessy::PawnDoublePushGenerator
+{
+    const ChessGame& chessGame;
+
+    struct MoveIterator
+    {
+        PositionSet::PositionSetIterator pawnPushes;
+        inline Move operator *(void) const
+        {
+            Index pp = *pawnPushes;
+            return Move{ int8_t(pp + (side != WHITE_SIDE ? 16 : -16)), pp };
+        }
+
+        inline void operator ++(void)
+        {
+            ++pawnPushes;
+        }
+
+        inline bool operator !=(const MoveIterator& psi) const
+        {
+            return pawnPushes != psi.pawnPushes;
+        }
+    };
+public:
+    inline PawnDoublePushGenerator(const ChessGame& cg) : chessGame{ cg } {}
+
+    MoveIterator begin(void) const;
+    MoveIterator end(void) const;
+};
+
+
+template<chessy::Side side, chessy::Leftright leftright>
+class chessy::PawnCaptureGenerator
+{
+    const ChessGame& chessGame;
+
+    struct MoveIterator
+    {
+        PositionSet::PositionSetIterator pawnPushes;
+        inline Move operator *(void) const
+        {
+            Index pp = *pawnPushes;
+            return Move{
+                int8_t(pp + (side != WHITE_SIDE ? 8 : -8) - leftright), pp
+            };
+        }
+
+        inline void operator ++(void)
+        {
+            ++pawnPushes;
+        }
+
+        inline bool operator !=(const MoveIterator& psi) const
+        {
+            return pawnPushes != psi.pawnPushes;
+        }
+    };
+public:
+    inline PawnCaptureGenerator(const ChessGame& cg) : chessGame{ cg } {}
+
+    MoveIterator begin(void) const;
+    MoveIterator end(void) const;
+};
+
+
 /*!
  * extends \link PositionSet so that all possible destinations for
  * a knight can be iterated over.

+ 86 - 10
src/UciParser.cpp

@@ -1,28 +1,38 @@
 #include "UciParser.h"
 #include <sstream>
+#include <thread>
 
 #include "ChessGame.h"
 
 using namespace std;
 
-
-const std::map<std::string, UciParser::CommandHandler> UciParser::commandHandlers = {
+const map<std::string, UciParser::CommandHandler> UciParser::commandHandlers = {
     {"uci",         &UciParser::uci },
     {"debug",       &UciParser::debug },
     {"isready",     &UciParser::isready },
     {"setoption",   &UciParser::setoption },
     {"register",    &UciParser::doNothing },
     {"ucinewgame",  &UciParser::ucinewgame },
+    {"position",    &UciParser::position },
+    {"go",          &UciParser::go },
+    {"quit",        &UciParser::quit },
+    {"getfen",      &UciParser::getfen},
 };
 
 
+#include<fstream>
 int UciParser::parse(istream& in, ostream& out)
 {
-    while (!in.eof()) {
+    ofstream log{ "log.log" };
+    while (!in.eof() && !quitting) {
         string line;
         getline(in, line);
-        executeLine(line);
+        if (!line.empty()) {
+            executeLine(line);
+            log << line << endl;
+        }
     }
+    log.close();
     return 0;
 }
 
@@ -51,7 +61,11 @@ int UciParser::executeCommand(const string& command,
     }
     catch (out_of_range& oor) {
         // no handler for command -> invalid command
-        
+        out << "unknown command: " << command << endl;
+        return 1;
+    }
+    catch (...) {
+        out << "unknown error occurred" << endl;
         return 1;
     }
 }
@@ -68,23 +82,26 @@ void UciParser::sendCommand(const std::string& command,
 
 void UciParser::uci(const std::vector<std::string>& args)
 {
+    sendCommand("id", { "name", "Chessy", "1.0" });
+    sendCommand("id", { "author", "N. Winkler" });
     sendCommand("uciok");
 }
 
 
-void UciParser::debug(const std::vector<std::string>& args)
+void UciParser::debug(const vector<string>& args)
 {
     // not yet implemented
 }
 
 
-void UciParser::isready(const std::vector<std::string>& args)
+void UciParser::isready(const vector<string>& args)
 {
     sendCommand("readyok");
+    int a = this->cg.getBoard().getWhitePawns().popcount();
 }
 
 
-void UciParser::setoption(const std::vector<std::string>& args)
+void UciParser::setoption(const vector<string>& args)
 {
     Option o;
     
@@ -92,13 +109,72 @@ void UciParser::setoption(const std::vector<std::string>& args)
 }
 
 
-void UciParser::ucinewgame(const std::vector<std::string>& args)
+void UciParser::ucinewgame(const vector<string>& args)
 {
     return;
 }
 
 
-void UciParser::doNothing(const std::vector<std::string>& args)
+void UciParser::position(const vector<string>& args)
+{
+    try {
+        int movesIndex = 0;
+        if (args.at(0) == "fen") {
+            string fenString = args.at(1) + " " + args.at(2) + " " +
+                args.at(3) + " " + args.at(4) + " " + args.at(5) + " " +
+                args.at(6);
+            cg.loadFromFen(fenString);
+            movesIndex = 6;
+        }
+        else if (args.at(0) == "startpos") {
+            cg = chessy::ChessGame();
+            movesIndex = 1;
+        }
+        if (args.size() > movesIndex + 1 && args.at(movesIndex) == "moves") {
+            for (int i = movesIndex + 1; i < args.size(); i++) {
+                using chessy::Move;
+                Move move = Move{ args[i] };
+                cg.move(move);
+            }
+        }
+    }
+    catch(out_of_range& oor) {
+        out << "invalid arguments for command 'position'" << endl;
+    }
+    catch(runtime_error& re) {
+        out << "" << endl;
+    }
+}
+
+
+void UciParser::go(const vector<string>& args)
+{
+    chessy::Move m = minimax.calculateBest(4);
+
+    // TODO: hack!
+    string suffix;
+    if (cg.getBoard().getAtPosition(m.origin) == chessy::PieceType::PAWN &&
+        (m.destination.index < 8 || m.destination.index >= 56)) {
+            suffix = "Q";
+    }
+
+    sendCommand("bestmove", { m.asString() + suffix });
+}
+
+
+void UciParser::quit(const vector<string>& args)
+{
+    quitting = true;
+}
+
+
+void UciParser::getfen(const vector<string>& args)
+{
+    out << cg.generateFen() << endl;
+}
+
+
+void UciParser::doNothing(const vector<string>& args)
 {
     // explicitly do nothing
     return;

+ 15 - 0
src/UciParser.h

@@ -7,6 +7,9 @@
 #include <algorithm>
 #include <map>
 
+#include "ChessGame.h"
+#include "Minimax.h"
+
 
 struct Option
 {
@@ -23,6 +26,7 @@ class OptionsManager
     std::map<std::string, Option> options = {
         {"", {}}
     };
+
 public:
     OptionsManager(void) = default;
 
@@ -41,6 +45,11 @@ class UciParser
     std::istream& in;
     std::ostream& out;
     OptionsManager optionsManager;
+
+    bool quitting = false;
+
+    chessy::ChessGame cg;
+    chessy::MiniMax minimax { cg };
 public:
     inline UciParser(std::istream& in = std::cin, std::ostream& out = std::cout) :
         in{ in }, out{ out } {}
@@ -61,6 +70,12 @@ public:
     virtual void isready        (const std::vector<std::string>& args);
     virtual void setoption      (const std::vector<std::string>& args);
     virtual void ucinewgame     (const std::vector<std::string>& args);
+    virtual void position       (const std::vector<std::string>& args);
+    virtual void go             (const std::vector<std::string>& args);
+    virtual void quit           (const std::vector<std::string>& args);
+
+    // engine-specific extension commands
+    virtual void getfen         (const std::vector<std::string>& args);
 
     /*!
      * Is needed for commands that should not trigger any action, but are

+ 1 - 1
src/makefile

@@ -6,7 +6,7 @@ STRIP=      strip
 CXXFLAGS=   -std=c++14
 LNFLAGS=    
 DEPS=       Bitfield.h
-OBJ=        main.o Board.o ChessGame.o UciParser.o MoveGeneration.o
+OBJ=        $(patsubst %.cpp,%.o,$(wildcard *.cpp))
 
 
 %.s: %.cpp