First working HLVariant.
[tagua/yd.git] / src / hlvariant / chess / legalitycheck.h
blob97806243b55d07535bd9d162293b63b57838b802
1 #ifndef HLVARIANT__CHESS__LEGALITYCHECK_H
2 #define HLVARIANT__CHESS__LEGALITYCHECK_H
4 #include "gamestate.h"
5 #include "interactiontype.h"
6 #include "turnpolicy.h"
8 namespace HLVariant {
9 namespace Chess {
11 template <typename _GameState>
12 class LegalityCheck {
13 public:
14 typedef _GameState GameState;
15 typedef typename GameState::Board Board;
16 typedef typename GameState::Move Move;
17 typedef typename GameState::Piece Piece;
18 private:
19 const GameState& m_state;
20 public:
21 LegalityCheck(const GameState& state);
22 virtual ~LegalityCheck();
24 virtual typename Move::Type getMoveType(
25 const Piece& piece,
26 const Move& move,
27 const Piece& target = Piece()) const;
28 virtual bool pseudolegal(Move& move) const;
29 virtual bool legal(Move& move) const;
30 virtual bool attacks(
31 typename Piece::Color color,
32 const Point& p,
33 const Piece& target = Piece()) const;
34 virtual bool checkPromotion(typename Piece::Type type) const;
36 virtual InteractionType movable(const TurnTest&, const Point& x) const;
37 virtual InteractionType droppable(const TurnTest&, int index) const;
40 // IMPLEMENTATION
42 template <typename GameState>
43 LegalityCheck<GameState>::~LegalityCheck() { }
45 template <typename GameState>
46 LegalityCheck<GameState>::LegalityCheck(const GameState& state)
47 : m_state(state) { }
49 template <typename GameState>
50 bool LegalityCheck<GameState>::legal(Move& move) const {
51 if (pseudolegal(move)) {
52 Piece piece = m_state.board().get(move.from());
53 typename Piece::Color turn = piece.color();
55 GameState tmp(m_state);
56 tmp.move(move);
58 Point kingPos = tmp.board().find(Piece(turn, Piece::KING));
60 if (kingPos == Point::invalid())
61 return false;
63 LegalityCheck<GameState> tmpLegality(tmp);
64 if (tmpLegality.attacks(Piece::oppositeColor(turn), kingPos))
65 return false;
67 return true;
69 else {
70 return false;
74 template <typename GameState>
75 bool LegalityCheck<GameState>::pseudolegal(Move& move) const {
76 if (!move.valid()) return false;
78 if (!m_state.board().valid(move.from())) return false;
79 if (!m_state.board().valid(move.to())) return false;
81 Piece piece = m_state.board().get(move.from());
82 if (piece == Piece()) return false;
84 typename Piece::Color thisTurn = piece.color();
85 typename Piece::Color otherTurn = Piece::oppositeColor(thisTurn);
87 if (piece != Piece() && m_state.turn() == thisTurn) {
88 typename Move::Type move_type = getMoveType(piece, move);
89 move.setType(move_type);
90 if (!move.valid())
91 return false;
92 if (!checkPromotion(static_cast<typename Piece::Type>(move.promoteTo())))
93 return false;
94 if (move.kingSideCastling()) {
95 if (attacks(otherTurn, move.from()) ||
96 attacks(otherTurn, move.from() + Point(1, 0), piece))
97 return false;
99 if (move.queenSideCastling()) {
100 if (attacks(otherTurn, move.from()) ||
101 attacks(otherTurn, move.from() + Point(-1, 0), piece))
102 return false;
105 return true;
107 else {
108 return false;
112 template <typename GameState>
113 typename GameState::Move::Type
114 LegalityCheck<GameState>::
115 getMoveType(const Piece& piece, const Move& move, const Piece& _target) const {
116 Piece target = _target == Piece() ? m_state.board().get(move.to()) : _target;
118 switch (piece.type())
120 case Piece::ROOK:
122 if (move.from() == move.to())
123 return Move::INVALID;
124 PathInfo path = m_state.board().path(move.from(), move.to());
125 if (path.parallel() && path.clear() && target.color() != piece.color())
126 return Move::NORMAL;
127 else
128 return Move::INVALID;
131 case Piece::BISHOP:
133 if (move.from() == move.to())
134 return Move::INVALID;
135 PathInfo path = m_state.board().path(move.from(), move.to());
136 if (path.diagonal() && path.clear() && target.color() != piece.color())
137 return Move::NORMAL;
138 else
139 return Move::INVALID;
142 case Piece::KNIGHT:
143 if (target.color() == piece.color())
144 return Move::INVALID;
145 else
147 Point delta = move.from() - move.to();
148 if (abs(delta.x) == 1 && abs(delta.y) == 2)
149 return Move::NORMAL;
150 if (abs(delta.y) == 1 && abs(delta.x) == 2)
151 return Move::NORMAL;
152 return Move::INVALID;
155 case Piece::QUEEN:
157 if (move.from() == move.to())
158 return Move::INVALID;
159 PathInfo path = m_state.board().path(move.from(), move.to());
160 if (path.valid() && path.clear() && target.color() != piece.color())
161 return Move::NORMAL;
162 else
163 return Move::INVALID;
166 case Piece::KING:
168 if (move.from() == move.to())
169 return Move::INVALID;
170 Point delta = move.to() - move.from();
171 if (abs(delta.x) <= 1 && abs(delta.y) <= 1 && target.color() != piece.color()) {
172 return Move::NORMAL;
174 else if (move.from() == m_state.kingStartingPosition(piece.color())) {
175 if (delta == Point(2,0)) {
176 if (m_state.board().get(move.from() + Point(1,0)) == Piece() &&
177 m_state.board().get(move.to()) == Piece() &&
178 m_state.kingCastling(piece.color()))
179 return Move::KING_SIDE_CASTLING;
181 else if (delta == Point(-2,0)) {
182 if (m_state.board().get(move.from() - Point(1, 0)) == Piece() &&
183 m_state.board().get(move.to() + Point(1, 0)) == Piece() &&
184 m_state.board().get(move.to()) == Piece() &&
185 m_state.queenCastling(piece.color()))
186 return Move::QUEEN_SIDE_CASTLING;
189 return Move::INVALID;
192 case Piece::PAWN:
194 Point delta = move.to() - move.from();
195 bool enPassant = m_state.enPassant() == move.to();
197 // moving
198 if (target == Piece() && !enPassant) {
199 if (delta == m_state.direction(piece.color())) {
200 if (move.to().y == m_state.promotionRank(piece.color()))
201 return Move::PROMOTION;
202 else
203 return Move::NORMAL;
206 if (move.from().y == m_state.startingRank(piece.color())
207 + m_state.direction(piece.color()).y &&
208 delta == m_state.direction(piece.color()) * 2 &&
209 m_state.board().get(move.from() + m_state.direction(piece.color())) == Piece())
210 return Move::EN_PASSANT_TRIGGER;
211 else
212 return Move::INVALID;
215 // capturing
216 else if (enPassant || target.color() != piece.color()) {
217 if (delta.y == m_state.direction(piece.color()).y &&
218 abs(delta.x) == 1) {
219 if (enPassant)
220 return Move::EN_PASSANT_CAPTURE;
221 else if (move.to().y == m_state.promotionRank(piece.color()))
222 return Move::PROMOTION;
223 else
224 return Move::NORMAL;
228 return Move::INVALID;
231 default:
232 return Move::INVALID;
236 template <typename GameState>
237 bool LegalityCheck<GameState>::attacks(typename Piece::Color color, const Point& to, const Piece& target) const {
238 for (int i = 0; i < m_state.board().size().x; i++) {
239 for (int j = 0; j < m_state.board().size().y; j++) {
240 Point p(i, j);
241 Piece piece = m_state.board().get(p);
242 Move move(p, to);
243 if (piece != Piece() && piece.color() == color
244 && getMoveType(piece, move, target) != Move::INVALID)
245 return true;
248 return false;
251 template <typename GameState>
252 bool LegalityCheck<GameState>::checkPromotion(typename Piece::Type type) const {
253 return type == -1 ||
254 type == Piece::QUEEN ||
255 type == Piece::ROOK ||
256 type == Piece::BISHOP ||
257 type == Piece::KNIGHT;
260 template <typename GameState>
261 InteractionType LegalityCheck<GameState>::movable(const TurnTest& test, const Point& p) const {
262 Piece piece = m_state.board().get(p);
263 if (piece == Piece() || !test(piece.color()))
264 return NoAction;
266 return piece.color() == m_state.turn() ? Moving : Premoving;
269 template <typename GameState>
270 InteractionType LegalityCheck<GameState>::droppable(const TurnTest& test, int index) const {
271 if (!test(index))
272 return NoAction;
274 typename Piece::Color c = static_cast<typename Piece::Color>(index);
275 return c == m_state.turn() ? Moving : Premoving;
278 } // namespace Chess
279 } // namespace HLVariant
281 #endif // HLVARIANT__CHESS__LEGALITYCHECK_H