Skip to content

Commit

Permalink
Random games for chess, many bugs fixed around chess::Move. Progress on
Browse files Browse the repository at this point in the history
  • Loading branch information
oggy22 committed Dec 30, 2023
1 parent cc8c2e2 commit 16b61de
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 42 deletions.
61 changes: 43 additions & 18 deletions BoardGamesEngine/Games/chess.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ namespace chess {
class Square : public SquareBase<8, 8>
{
public:
Square() : SquareBase(0) { }
Square() : SquareBase() { } // invalid square
Square(int n) : SquareBase(n) { }
Square(int x, int y) : SquareBase(y * 8 + x) {}
Square(char letter, char number) : SquareBase(int(letter -'A'), int(number - '1')) { }
Expand Down Expand Up @@ -168,13 +168,20 @@ namespace chess {
class Move
{
public:
bool is_valid() { return int(_from) != -1; }
Move() : _from(-1) { }
bool is_valid() { return _from.is_valid(); }
Move() : _from() { } // invalid move
Move(Square from, Square to, Piece captured = Piece::None, Piece promotion = Piece::None) :
_from(from),
_to(to),
_captured(captured),
_promotion(promotion) {}
_promotion(promotion)
{
DCHECK(captured != Piece::King);
DCHECK(captured != Piece::OtherKing);
DCHECK(from.is_valid());
DCHECK(to.is_valid());
DCHECK(from != to);
}
void flip_rows();
void flip_columns();
void flip_rows_and_columns();
Expand Down Expand Up @@ -364,27 +371,28 @@ namespace chess {
bool is_legal(Move move) { return square(move.to()) == move.captured(); }
bool is_reverse_legal(Move move) { return square(move.from()) == Piece::None && square(move.to()) != Piece::None; }
bool is_legal() { throw ""; }
Piece square(Square sq) const { return table[sq]; }
Piece& square(Square sq) { return table[sq]; }

Piece operator[](Square square) const { return table[square]; }
void operator+=(Move move)
{
DCHECK(square(move.to()) == move.captured());
square(move.to()) = square(move.from());
square(move.to()) = move.promotion() == Piece::None ? square(move.from()) : move.promotion();
square(move.from()) = Piece::None;
if (move.to() == King1) King1 = move.to();
if (move.to() == King2) King2 = move.to();
if (move.from() == King1) King1 = move.to();
if (move.from() == King2) King2 = move.to();
BoardBase::move();
}

void operator-=(Move move)
{
DCHECK(square(move.from()) == 0);
square(move.from()) = square(move.to());
square(move.from()) =
move.promotion() == Piece::None
? square(move.to())
: (move.promotion() > 0 ? Piece::Pawn : Piece::OtherPawn);
square(move.to()) = move.captured();
if (move.from() == King1) King1 = move.from();
if (move.from() == King2) King2 = move.from();
if (move.to() == King1) King1 = move.from();
if (move.to() == King2) King2 = move.from();
BoardBase::reverse_move();
}
std::experimental::generator<Move> all_legal_moves_played()
Expand Down Expand Up @@ -419,7 +427,7 @@ if (sq2.MOVE() && !belongs_to(square(sq2), player) && CONDITION) \
#define MOVE_PAWN(MOVE) MOVE_ONCE_WITH_COND(MOVE, true)

//DCHECK(Player::First)
Square sq;
Square sq(0);
do
{
Piece piece = square(sq);
Expand Down Expand Up @@ -470,24 +478,31 @@ if (sq2.MOVE() && !belongs_to(square(sq2), player) && CONDITION) \
if (piece == Piece::Pawn)
{
Square sqF = sq, sqFF = sq;
Piece queen;
if (this->turn() == Player::First)
{
sqF.move_up();
queen = Piece::Queen;
}
else
{
sqF.move_down();
queen = Piece::OtherQueen;
}
if (square(sqF) == Piece::None)
{
Move move(sq, sqF, Piece::None, sqF.y() == 0 || sqF.y() == 7 ? Piece::Queen : Piece::None); // todo: promotion white and black
Move move(sq, sqF, Piece::None, sqF.y() == 0 || sqF.y() == 7 ? queen : Piece::None);
PLAY_AND_YIELD
}
Square sqL = sqF, sqR = sqF;
if (sqL.move_left() && belongs_to(square(sqL), oponent(player)))
{
Move move(sq, sqL, Piece::None, sqL.y() == 0 || sqL.y() == 7 ? Piece::Queen : Piece::None); // todo: promotion white and black
Move move(sq, sqL, square(sqL), sqL.y() == 0 || sqL.y() == 7 ? queen : Piece::None);
PLAY_AND_YIELD
}
if (sqR.move_right() && belongs_to(square(sqR), oponent(player)))
{
Move move(sq, sqL, Piece::None, sqL.y() == 0 || sqL.y() == 7 ? Piece::Queen : Piece::None); // todo: promotion white and black
Move move(sq, sqR, square(sqR), sqR.y() == 0 || sqR.y() == 7 ? queen : Piece::None);
PLAY_AND_YIELD
}
if (square(sqF) == Piece::None)
Expand Down Expand Up @@ -629,7 +644,18 @@ if (sq.MOVE() && abs(square(sq)) == PIECE && belongs_to(square(sq), player))

void flip_rows()
{
Square bottom(0, 0), top;
Square bottom(0, 0);
do
{
Square top(bottom.x(), 7 - bottom.y());
do
{
std::swap(square(bottom), square(top));
top.move_right();
} while (bottom.move_right());
} while (bottom.y() < 4);
King1 = Square(King1.x(), 7 - King1.y());
King2 = Square(King2.x(), 7 - King2.y());
}

void flip_columns();
Expand Down Expand Up @@ -701,7 +727,6 @@ if (sq.MOVE() && abs(square(sq)) == PIECE && belongs_to(square(sq), player))
return os;
}
private:
Piece table[64];
Square King1, King2;
};

Expand Down
40 changes: 29 additions & 11 deletions BoardGamesEngine/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ template<int W, int H>
class SquareBase
{
public:
SquareBase() : _square(0) {}
SquareBase() : _square(W*H) {} // invalid square
SquareBase(int n) : _square(n)
{
DCHECK(n >= 0 && n < W * H);
Expand Down Expand Up @@ -145,7 +145,7 @@ class BoardBase
void reverse_move()
{
_turn = oponent(_turn);
ply++;
ply--;
DCHECK(ply >= 0);
}

Expand All @@ -162,16 +162,20 @@ class BoardBase
return table[int(sq)];
}

std::experimental::generator<piece_t> get_pieces()
piece_t square(SquareBase<W, H> sq) const { return table[sq]; }

piece_t& square(SquareBase<W, H> sq) { return table[sq]; }

std::experimental::generator<piece_t> get_pieces() const
{
SquareBase<W,H> sq;
SquareBase<W, H> sq(0);
do
{
co_yield (*this)[sq];
} while (++sq);
}

std::experimental::generator<piece_t> get_pieces_reversed()
std::experimental::generator<piece_t> get_pieces_reversed() const
{
SquareBase<W, H> sq(W * H - 1);
do
Expand All @@ -180,7 +184,7 @@ class BoardBase
} while (--sq);
}

std::experimental::generator<SquareBase<W, H>> get_squares()
std::experimental::generator<SquareBase<W, H>> get_squares() const
{
SquareBase<W, H> sq;
do
Expand All @@ -189,14 +193,25 @@ class BoardBase
} while (++sq);
}

std::experimental::generator<SquareBase<W, H>> get_squares_reversed()
std::experimental::generator<SquareBase<W, H>> get_squares_reversed() const
{
SquareBase<W, H> sq(W * H - 1);
do
{
co_yield sq;
} while (--sq);
}

int count_piece(piece_t piece) const
{
int ret = 0;
for (auto p : get_pieces())
{
if (p == piece)
ret++;
}
return ret;
}
};

/// <summary>
Expand Down Expand Up @@ -252,16 +267,19 @@ class TableEntry
};

template <typename Board, typename Move>
Move random_move(Board board)
Move random_move(Board& board, int seed = 0)
{
std::vector<Move> moves;
for (Move move : board.all_legal_moves())
{
moves.push_back(move);
}

std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution dist(0, moves.size() - 1);
if (moves.size() == 0)
return Move();

//std::random_device rd;
std::mt19937 gen(seed);
std::uniform_int_distribution<> dist(0, moves.size() - 1);
return moves[dist(gen)];
}
44 changes: 44 additions & 0 deletions EngineTests/chess_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,47 @@ TEST(chess, square_knight_moves)
EXPECT_TRUE(squares_visited[i])
<< chess::Square(i).chess_notation();
}

TEST(chess, random_game) {
for (int seed = 0; seed < 100; seed++)
{
chess::ChessPosition<true> pos;
for (int i = 0; i < 1500; i++)
{
Player player = pos.turn();
EXPECT_FALSE(pos.is_checked(oponent(player)));

// Get a random move and check that the board isn't affected
chess::ChessPosition<true> pos_backup = pos;
chess::Move move = random_move<chess::ChessPosition<true>, chess::Move>(pos, seed);
EXPECT_EQ(pos, pos_backup);

if (!move.is_valid())
break;

EXPECT_TRUE(pos.is_legal(move));

pos += move;

// No pawns on the 1st and 8th rows
chess::Square first("A1"), last("A8");
do
{
chess::Piece f = abs(pos[first]), l = abs(pos[last]);
EXPECT_NE(chess::Piece::Pawn, f);
EXPECT_NE(chess::Piece::Pawn, l);
} while (first.move_right() && last.move_right());

// Only 2 kings and no check to the player played
EXPECT_EQ(1, pos.count_piece(chess::Piece::King))
<< i << " " << move.chess_notation() << std::endl
<< pos.fen() << std::endl;
EXPECT_EQ(1, pos.count_piece(chess::Piece::OtherKing))
<< i << " " << move.chess_notation() << std::endl
<< pos.fen() << std::endl;
EXPECT_FALSE(pos.is_checked(player))
<< i << " " << move.chess_notation() << std::endl
<< pos.fen() << std::endl;
}
}
}
26 changes: 13 additions & 13 deletions EngineTests/core_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ TEST(Core, oponent) {
}

TEST(Core, SquareBase1_1) {
SquareBase<1, 1> sb1; EXPECT_FALSE(sb1.move_down());
SquareBase<1, 1> sb2; EXPECT_FALSE(sb2.move_up());
SquareBase<1, 1> sb3; EXPECT_FALSE(sb3.move_left());
SquareBase<1, 1> sb4; EXPECT_FALSE(sb4.move_right());

SquareBase<1, 1> sb5; EXPECT_FALSE(sb1.move_upleft());
SquareBase<1, 1> sb6; EXPECT_FALSE(sb1.move_upright());
SquareBase<1, 1> sb7; EXPECT_FALSE(sb1.move_downleft());
SquareBase<1, 1> sb8; EXPECT_FALSE(sb1.move_downright());

SquareBase<1, 1> inc; EXPECT_FALSE(++inc);
SquareBase<1, 1> dec; EXPECT_FALSE(--dec);
SquareBase<1, 1> sb1(0); EXPECT_FALSE(sb1.move_down());
SquareBase<1, 1> sb2(0); EXPECT_FALSE(sb2.move_up());
SquareBase<1, 1> sb3(0); EXPECT_FALSE(sb3.move_left());
SquareBase<1, 1> sb4(0); EXPECT_FALSE(sb4.move_right());

SquareBase<1, 1> sb5(0); EXPECT_FALSE(sb1.move_upleft());
SquareBase<1, 1> sb6(0); EXPECT_FALSE(sb1.move_upright());
SquareBase<1, 1> sb7(0); EXPECT_FALSE(sb1.move_downleft());
SquareBase<1, 1> sb8(0); EXPECT_FALSE(sb1.move_downright());

SquareBase<1, 1> inc(0); EXPECT_FALSE(++inc);
SquareBase<1, 1> dec(0); EXPECT_FALSE(--dec);
}

TEST(Core, SquareBase8_8) {
SquareBase<8, 8> inc;
SquareBase<8, 8> inc(0);
for (int i = 0; i < 63; i++)
EXPECT_TRUE(++inc);
EXPECT_FALSE(++inc);
Expand Down

0 comments on commit 16b61de

Please sign in to comment.