From 68b01ee4432027f731a05cc1cc1b07229b306cba Mon Sep 17 00:00:00 2001 From: Paolo Capriotti Date: Sun, 29 Jul 2007 22:30:02 +0200 Subject: [PATCH] Added ChessGameState, related classes and tests. --- tests/hlvariants/CMakeLists.txt | 4 + tests/hlvariants/chessgamestatetest.cpp | 100 ++++++++++++++ tests/hlvariants/chessgamestatetest.h | 52 +++++++ tests/hlvariants/chessmovetest.cpp | 34 +++++ tests/hlvariants/chessmovetest.h | 35 +++++ tests/hlvariants/chesspiecetest.cpp | 12 ++ tests/hlvariants/chesspiecetest.h | 2 + tests/hlvariants/prototype/board.h | 2 + tests/hlvariants/prototype/chess/gamestate.cpp | 29 ++++ tests/hlvariants/prototype/chess/gamestate.h | 179 +++++++++++++++++++++++++ tests/hlvariants/prototype/chess/move.cpp | 63 +++++++++ tests/hlvariants/prototype/chess/move.h | 45 +++++++ tests/hlvariants/prototype/chess/piece.cpp | 20 +++ tests/hlvariants/prototype/chess/piece.h | 5 + 14 files changed, 582 insertions(+) create mode 100644 tests/hlvariants/chessgamestatetest.cpp create mode 100644 tests/hlvariants/chessgamestatetest.h create mode 100644 tests/hlvariants/chessmovetest.cpp create mode 100644 tests/hlvariants/chessmovetest.h create mode 100644 tests/hlvariants/prototype/chess/gamestate.cpp create mode 100644 tests/hlvariants/prototype/chess/gamestate.h create mode 100644 tests/hlvariants/prototype/chess/move.cpp create mode 100644 tests/hlvariants/prototype/chess/move.h diff --git a/tests/hlvariants/CMakeLists.txt b/tests/hlvariants/CMakeLists.txt index 91bd04d..eff954e 100644 --- a/tests/hlvariants/CMakeLists.txt +++ b/tests/hlvariants/CMakeLists.txt @@ -2,10 +2,14 @@ set(main_dir "../../src") SET(hl_SRC prototype/chess/piece.cpp + prototype/chess/move.cpp + prototype/chess/gamestate.cpp ../cppunit_main.cpp boardtest.cpp chesspiecetest.cpp + chessgamestatetest.cpp + chessmovetest.cpp ) include_directories( diff --git a/tests/hlvariants/chessgamestatetest.cpp b/tests/hlvariants/chessgamestatetest.cpp new file mode 100644 index 0000000..0802379 --- /dev/null +++ b/tests/hlvariants/chessgamestatetest.cpp @@ -0,0 +1,100 @@ +#include "chessgamestatetest.h" +#include "prototype/chess/gamestate.h" + +CPPUNIT_TEST_SUITE_REGISTRATION(ChessGameStateTest); + +void ChessGameStateTest::setUp() { + m_state = new ChessGameState; + m_state->setup(); +} + +void ChessGameStateTest::tearDown() { + delete m_state; +} + +void ChessGameStateTest::test_setup() { + for (int i = 0; i < 8; i++) { + CPPUNIT_ASSERT_EQUAL(ChessPiece::BLACK, + m_state->board().get(Point(i, 0)).color()); + CPPUNIT_ASSERT_EQUAL(ChessPiece::BLACK, + m_state->board().get(Point(i, 1)).color()); + CPPUNIT_ASSERT_EQUAL(ChessPiece::WHITE, + m_state->board().get(Point(i, 6)).color()); + CPPUNIT_ASSERT_EQUAL(ChessPiece::WHITE, + m_state->board().get(Point(i, 7)).color()); + CPPUNIT_ASSERT(m_state->board().get(Point(i, 4)) == ChessPiece()); + } + + CPPUNIT_ASSERT_EQUAL(ChessPiece::ROOK, + m_state->board().get(Point(0, 0)).type()); + CPPUNIT_ASSERT_EQUAL(ChessPiece::KING, + m_state->board().get(Point(4, 7)).type()); + CPPUNIT_ASSERT_EQUAL(ChessPiece::PAWN, + m_state->board().get(Point(6, 6)).type()); +} + +void ChessGameStateTest::test_simple_move() { + m_state->move(ChessMove(Point(4, 6), Point(4, 5))); // e4 + CPPUNIT_ASSERT(m_state->board().get(Point(4, 6)) == ChessPiece()); + CPPUNIT_ASSERT(m_state->board().get(Point(4, 5)) == + ChessPiece(ChessPiece::WHITE, ChessPiece::PAWN)); +} + +void ChessGameStateTest::test_capture() { + m_state->move(ChessMove(Point(4, 6), Point(4, 4))); // e4 + m_state->move(ChessMove(Point(3, 1), Point(3, 3))); // d5 + m_state->move(ChessMove(Point(4, 4), Point(3, 3))); // exd5 + + CPPUNIT_ASSERT(m_state->board().get(Point(3, 3)) == + ChessPiece(ChessPiece::WHITE, ChessPiece::PAWN)); +} + +void ChessGameStateTest::test_en_passant() { + m_state->move(ChessMove(Point(4, 6), Point(4, 4))); // e4 + m_state->move(ChessMove(Point(7, 1), Point(7, 2))); // h6 + m_state->move(ChessMove(Point(4, 4), Point(4, 3))); // e5 + + ChessMove d5(Point(3, 1), Point(3, 3)); + d5.setType(ChessMove::EN_PASSANT_TRIGGER); + m_state->move(d5); + + ChessMove exd6(Point(4, 3), Point(3, 2)); + exd6.setType(ChessMove::EN_PASSANT_CAPTURE); + m_state->move(exd6); + + CPPUNIT_ASSERT(m_state->board().get(Point(3, 3)) == ChessPiece()); +} + +void ChessGameStateTest::test_kingside_castling() { + ChessMove oo(Point(4, 7), Point(6, 7)); + oo.setType(ChessMove::KING_SIDE_CASTLING); + m_state->move(oo); + + CPPUNIT_ASSERT(m_state->board().get(Point(6, 7)) == + ChessPiece(ChessPiece::WHITE, ChessPiece::KING)); + CPPUNIT_ASSERT(m_state->board().get(Point(5, 7)) == + ChessPiece(ChessPiece::WHITE, ChessPiece::ROOK)); + CPPUNIT_ASSERT(m_state->board().get(Point(7, 7)) == ChessPiece()); +} + +void ChessGameStateTest::test_queenside_castling() { + ChessMove oo(Point(4, 7), Point(2, 7)); + oo.setType(ChessMove::QUEEN_SIDE_CASTLING); + m_state->move(oo); + + CPPUNIT_ASSERT(m_state->board().get(Point(2, 7)) == + ChessPiece(ChessPiece::WHITE, ChessPiece::KING)); + CPPUNIT_ASSERT(m_state->board().get(Point(3, 7)) == + ChessPiece(ChessPiece::WHITE, ChessPiece::ROOK)); + CPPUNIT_ASSERT(m_state->board().get(Point(0, 7)) == ChessPiece()); +} + +void ChessGameStateTest::test_promotion() { + m_state->board().set(Point(7, 1), ChessPiece(ChessPiece::WHITE, ChessPiece::PAWN)); + ChessMove h8B(Point(7, 1), Point(7, 0), ChessPiece::BISHOP); + h8B.setType(ChessMove::PROMOTION); + m_state->move(h8B); + + CPPUNIT_ASSERT(m_state->board().get(Point(7, 0)) == + ChessPiece(ChessPiece::WHITE, ChessPiece::BISHOP)); +} diff --git a/tests/hlvariants/chessgamestatetest.h b/tests/hlvariants/chessgamestatetest.h new file mode 100644 index 0000000..5a84bc9 --- /dev/null +++ b/tests/hlvariants/chessgamestatetest.h @@ -0,0 +1,52 @@ +#ifndef CHESSGAMESTATETEST_H +#define CHESSGAMESTATETEST_H + +#include +#include +#include +#include +#include + +// forward decl +namespace HLVariant { + template class Board; + + namespace Chess { + template class GameState; + class Move; + class Piece; + } +} + +typedef HLVariant::Chess::Move ChessMove; +typedef HLVariant::Chess::Piece ChessPiece; +typedef HLVariant::Board Chessboard; +typedef HLVariant::Chess::GameState ChessGameState; + +class ChessGameStateTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(ChessGameStateTest); + CPPUNIT_TEST(test_setup); + CPPUNIT_TEST(test_simple_move); + CPPUNIT_TEST(test_capture); + CPPUNIT_TEST(test_en_passant); + CPPUNIT_TEST(test_kingside_castling); + CPPUNIT_TEST(test_queenside_castling); + CPPUNIT_TEST(test_promotion); + CPPUNIT_TEST_SUITE_END(); +private: + ChessGameState* m_state; +public: + void setUp(); + void tearDown(); + + void test_setup(); + void test_simple_move(); + void test_capture(); + void test_en_passant(); + void test_kingside_castling(); + void test_queenside_castling(); + void test_promotion(); +}; + +#endif // CHESSGAMESTATETEST_H + diff --git a/tests/hlvariants/chessmovetest.cpp b/tests/hlvariants/chessmovetest.cpp new file mode 100644 index 0000000..c05f24a --- /dev/null +++ b/tests/hlvariants/chessmovetest.cpp @@ -0,0 +1,34 @@ +#include "chessmovetest.h" +#include "prototype/chess/move.h" +#include "prototype/chess/piece.h" + +typedef HLVariant::Chess::Piece ChessPiece; + +CPPUNIT_TEST_SUITE_REGISTRATION(ChessMoveTest); + +void ChessMoveTest::setUp() { } + +void ChessMoveTest::tearDown() { } + +void ChessMoveTest::test_capture_square() { + ChessMove m(Point(4, 3), Point(3, 2)); + CPPUNIT_ASSERT(m.captureSquare() == Point(3, 2)); + + m.setType(ChessMove::EN_PASSANT_CAPTURE); + CPPUNIT_ASSERT(m.captureSquare() == Point(3, 3)); +} + +void ChessMoveTest::test_en_passant_trigger() { + ChessMove m(Point(4, 6), Point(4, 4)); + m.setType(ChessMove::EN_PASSANT_TRIGGER); + CPPUNIT_ASSERT(m.enPassantTrigger() == Point(4, 5)); +} + +void ChessMoveTest::test_promotion() { + ChessMove m(Point(5, 1), Point(5, 0), ChessPiece::ROOK); + + CPPUNIT_ASSERT_EQUAL(-1, m.promoteTo()); + m.setType(ChessMove::PROMOTION); + CPPUNIT_ASSERT_EQUAL((int)ChessPiece::ROOK, m.promoteTo()); +} + diff --git a/tests/hlvariants/chessmovetest.h b/tests/hlvariants/chessmovetest.h new file mode 100644 index 0000000..c5ced7f --- /dev/null +++ b/tests/hlvariants/chessmovetest.h @@ -0,0 +1,35 @@ +#ifndef CHESSMOVETEST_H +#define CHESSMOVETEST_H + +#include +#include +#include +#include +#include + +// forward decl +namespace HLVariant { + namespace Chess { + class Move; + } +} + +typedef HLVariant::Chess::Move ChessMove; + +class ChessMoveTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(ChessMoveTest); + CPPUNIT_TEST(test_capture_square); + CPPUNIT_TEST(test_en_passant_trigger); + CPPUNIT_TEST(test_promotion); + CPPUNIT_TEST_SUITE_END(); +public: + void setUp(); + void tearDown(); + + void test_capture_square(); + void test_en_passant_trigger(); + void test_promotion(); +}; + +#endif // CHESSMOVETEST_H + diff --git a/tests/hlvariants/chesspiecetest.cpp b/tests/hlvariants/chesspiecetest.cpp index abe685f..60a4f4a 100644 --- a/tests/hlvariants/chesspiecetest.cpp +++ b/tests/hlvariants/chesspiecetest.cpp @@ -22,3 +22,15 @@ void ChessPieceTest::test_names() { CPPUNIT_ASSERT(p.name() == "black_bishop"); } +void ChessPieceTest::test_compare() { + ChessPiece a(ChessPiece::BLACK, ChessPiece::KING); + ChessPiece b(ChessPiece::WHITE, ChessPiece::QUEEN); + ChessPiece c(ChessPiece::BLACK, ChessPiece::KING); + + CPPUNIT_ASSERT(a != b); + CPPUNIT_ASSERT(!(a == b)); + CPPUNIT_ASSERT(a == c); + CPPUNIT_ASSERT(c == a); + CPPUNIT_ASSERT(a == a); +} + diff --git a/tests/hlvariants/chesspiecetest.h b/tests/hlvariants/chesspiecetest.h index 8b823c4..d88848c 100644 --- a/tests/hlvariants/chesspiecetest.h +++ b/tests/hlvariants/chesspiecetest.h @@ -13,6 +13,7 @@ class ChessPieceTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ChessPieceTest); CPPUNIT_TEST(test_basic); CPPUNIT_TEST(test_names); + CPPUNIT_TEST(test_compare); CPPUNIT_TEST_SUITE_END(); public: void setUp(); @@ -20,6 +21,7 @@ public: void test_basic(); void test_names(); + void test_compare(); }; #endif // CHESSPIECETEST_H diff --git a/tests/hlvariants/prototype/board.h b/tests/hlvariants/prototype/board.h index 5cb1711..b07013b 100644 --- a/tests/hlvariants/prototype/board.h +++ b/tests/hlvariants/prototype/board.h @@ -1,6 +1,8 @@ #ifndef HLVARIANT__BOARD_H #define HLVARIANT__BOARD_H +#include + #include "point.h" #include "pathinfo.h" diff --git a/tests/hlvariants/prototype/chess/gamestate.cpp b/tests/hlvariants/prototype/chess/gamestate.cpp new file mode 100644 index 0000000..dd00364 --- /dev/null +++ b/tests/hlvariants/prototype/chess/gamestate.cpp @@ -0,0 +1,29 @@ +#include "gamestate.h" + + +namespace HLVariant { +namespace Chess { + +CastlingData::CastlingData() +: wk(true) +, wq(true) +, bk(true) +, bq(true) { } + +CastlingData::CastlingData(bool wk, bool wq, bool bk, bool bq) +: wk(wk) +, wq(wq) +, bk(bk) +, bq(bq) { } + +bool CastlingData::operator==(const CastlingData& other) const { + return wk == other.wk && + wq == other.wq && + bk == other.bk && + bq == other.bq; +} + + +} // namespace Chess +} // namespace HLVariant + diff --git a/tests/hlvariants/prototype/chess/gamestate.h b/tests/hlvariants/prototype/chess/gamestate.h new file mode 100644 index 0000000..56109dd --- /dev/null +++ b/tests/hlvariants/prototype/chess/gamestate.h @@ -0,0 +1,179 @@ +#ifndef HLVARIANT__CHESS__GAMESTATE_H +#define HLVARIANT__CHESS__GAMESTATE_H + +#include "../board.h" +#include "piece.h" +#include "move.h" + +namespace HLVariant { +namespace Chess { + +struct CastlingData { + bool wk : 1; + bool wq : 1; + bool bk : 1; + bool bq : 1; + + CastlingData(); + CastlingData(bool, bool, bool, bool); + bool operator==(const CastlingData& other) const; +}; + +template +class GameState { +public: + typedef _Board Board; + typedef _Move Move; + typedef typename Board::Piece Piece; +private: + Board m_board; + CastlingData m_castling; + Point m_en_passant; + typename Piece::Color m_turn; +public: + GameState(); + virtual ~GameState() { } + + virtual Board& board(); + virtual const Board& board() const; + + virtual void setup(); + + virtual bool operator==(const GameState& other) const; + + virtual void move(const Move& m); + virtual void basicMove(const Move& m); + virtual void handleCastling(const Piece& piece, const Move& m); + virtual void captureOn(const Point& p); + + virtual void switchTurn(); + virtual typename Piece::Color turn() const; +}; + +// IMPLEMENTATION + +template +GameState::GameState() +: m_board(Point(8, 8)) +, m_en_passant(Point::invalid()) +, m_turn(Piece::WHITE) { } + +template +Board& GameState::board() { return m_board; } + +template +const Board& GameState::board() const { return m_board; } + +template +void GameState::setup() { + for (int c = 0; c < 2; c++) { + typename Piece::Color color = static_cast(c); + int row = color == Piece::WHITE ? m_board.size().y - 1: 0; + for (int i = 0; i < m_board.size().x; i++) { + m_board.set(Point(i, color == Piece::WHITE ? row - 1 : row + 1), + Piece(color, Piece::PAWN)); + m_board.set(Point(0, row), Piece(color, Piece::ROOK)); + m_board.set(Point(1, row), Piece(color, Piece::KNIGHT)); + m_board.set(Point(2, row), Piece(color, Piece::BISHOP)); + m_board.set(Point(3, row), Piece(color, Piece::QUEEN)); + m_board.set(Point(4, row), Piece(color, Piece::KING)); + m_board.set(Point(5, row), Piece(color, Piece::BISHOP)); + m_board.set(Point(6, row), Piece(color, Piece::KNIGHT)); + m_board.set(Point(7, row), Piece(color, Piece::ROOK)); + } + } +} + +template +bool GameState::operator==(const GameState& other) const { + return m_board == other.m_board && + m_castling == other.m_castling && + m_en_passant == other.m_en_passant; +} + +template +void GameState::captureOn(const Point& p) { + m_board.set(p, Piece()); +} + +template +void GameState::basicMove(const Move& m) { + if (m.from() != m.to()) { + m_board.set(m.to(), m_board.get(m.from())); + m_board.set(m.from(), Piece()); + } +} + +template +void GameState::handleCastling(const Piece& piece, const Move& m) { + if (m.kingSideCastling()) { + Point rookSquare = m.to() + Point(1,0); + Point rookDestination = m.from() + Point(1,0); + basicMove(Move(rookSquare, rookDestination)); + } + + else if (m.queenSideCastling()) { + Point rookSquare = m.to() - Point(2,0); + Point rookDestination = m.from() - Point(1,0); + basicMove(Move(rookSquare, rookDestination)); + } + + typename Piece::Type type = piece.type(); + typename Piece::Color color = piece.color(); + + if (type == Piece::KING) { + if (color == Piece::WHITE) { + m_castling.wk = false; + m_castling.wq = false; + } + else { + m_castling.bk = false; + m_castling.bq = false; + } + } + + if (m.from() == Point(0,0) || m.to() == Point(0,0)) + m_castling.bq = false; + else if (m.from() == Point(7,0) || m.to() == Point(7,0)) + m_castling.bk = false; + else if (m.from() == Point(0,7) || m.to() == Point(0,7)) + m_castling.wq = false; + else if (m.from() == Point(7,7) || m.to() == Point(7,7)) + m_castling.wk = false; +} + +template +void GameState::switchTurn() { + m_turn = Piece::oppositeColor(m_turn); +} + +template +void GameState::move(const Move& m) { + Piece piece = m_board.get(m.from()); + if (piece == Piece()) return; + + captureOn(m.captureSquare()); + basicMove(m); + + m_en_passant = m.enPassantTrigger(); + + typename Piece::Type promotion_type = + static_cast(m.promoteTo()); + if (promotion_type != Piece::INVALID_TYPE) { + m_board.set(m.to(), Piece(piece.color(), promotion_type)); + } + + handleCastling(piece, m); + switchTurn(); +} + +template +typename Board::Piece::Color GameState::turn() const { + return m_turn; +} + +} // namespace Chess +} // namespace HLVariant + +#endif // HLVARIANT__CHESS__GAMESTATE_H + diff --git a/tests/hlvariants/prototype/chess/move.cpp b/tests/hlvariants/prototype/chess/move.cpp new file mode 100644 index 0000000..8c9f3d4 --- /dev/null +++ b/tests/hlvariants/prototype/chess/move.cpp @@ -0,0 +1,63 @@ +#include "move.h" + +namespace HLVariant { +namespace Chess { + +Move::Move(const Point& from, const Point& to, int promotionType) +: m_from(from) +, m_to(to) +, m_promotion(promotionType) { } + +Move::~Move() { } + +Point Move::enPassantTrigger() const { + if (m_type == EN_PASSANT_TRIGGER) { + return (m_from + m_to) / 2; + } + else { + return Point::invalid(); + } +} + +Point Move::captureSquare() const { + if (m_type == EN_PASSANT_CAPTURE) { + return Point(m_to.x, m_from.y); + } + else { + return m_to; + } +} + +int Move::promoteTo() const { + if (m_type == PROMOTION) { + return m_promotion; + } + else { + return -1; + } +} + +bool Move::kingSideCastling() const { + return m_type == KING_SIDE_CASTLING; +} + +bool Move::queenSideCastling() const { + return m_type == QUEEN_SIDE_CASTLING; +} + +Point Move::from() const { + return m_from; +} + +Point Move::to() const { + return m_to; +} + +void Move::setType(Type type) { + m_type = type; +} + +} // namespace Chess +} // namespace HLVariant + + diff --git a/tests/hlvariants/prototype/chess/move.h b/tests/hlvariants/prototype/chess/move.h new file mode 100644 index 0000000..cb36476 --- /dev/null +++ b/tests/hlvariants/prototype/chess/move.h @@ -0,0 +1,45 @@ +#ifndef HLVARIANT__CHESS__MOVE_H +#define HLVARIANT__CHESS__MOVE_H + +#include "point.h" + +namespace HLVariant { +namespace Chess { + +class Move { +public: + enum Type { + NORMAL, + EN_PASSANT_CAPTURE, + EN_PASSANT_TRIGGER, + KING_SIDE_CASTLING, + QUEEN_SIDE_CASTLING, + PROMOTION + }; +private: + Type m_type; + + Point m_from; + Point m_to; + int m_promotion; +public: + Move(const Point& from, const Point& to, int promotionType = -1); + virtual ~Move(); + + virtual Point enPassantTrigger() const; + virtual Point captureSquare() const; + virtual int promoteTo() const; + virtual bool kingSideCastling() const; + virtual bool queenSideCastling() const; + + virtual Point from() const; + virtual Point to() const; + + virtual void setType(Type type); +}; + +} // namespace Chess +} // namespace HLVariant + +#endif // HLVARIANT__CHESS__MOVE_H + diff --git a/tests/hlvariants/prototype/chess/piece.cpp b/tests/hlvariants/prototype/chess/piece.cpp index a0686c1..4375f19 100644 --- a/tests/hlvariants/prototype/chess/piece.cpp +++ b/tests/hlvariants/prototype/chess/piece.cpp @@ -49,6 +49,26 @@ QString Piece::name() const { return colorName() + '_' + typeName(); } +Piece::Color Piece::oppositeColor(Color color) { + switch (color) { + case WHITE: + return BLACK; + case BLACK: + return WHITE; + default: + return INVALID_COLOR; + } +} + +bool Piece::operator==(const Piece& other) const { + return m_color == other.m_color && + m_type == other.m_type; +} + +bool Piece::operator!=(const Piece& other) const { + return !((*this) == other); +} + } // namespace HLVariant } // namespace Chess diff --git a/tests/hlvariants/prototype/chess/piece.h b/tests/hlvariants/prototype/chess/piece.h index 27088d4..c23ea84 100644 --- a/tests/hlvariants/prototype/chess/piece.h +++ b/tests/hlvariants/prototype/chess/piece.h @@ -36,6 +36,11 @@ public: QString typeName() const; static QString typeName(Type type); QString name() const; + + static Color oppositeColor(Color color); + + bool operator==(const Piece& other) const; + bool operator!=(const Piece& other) const; }; } // namespace Chess -- 2.11.4.GIT