Ported crazyhouse MoveFactory and LegalityChecker.
[tagua/yd.git] / src / hlvariant / chess / legalitycheck.h
blob0460a3ac736c0773a29ce4e0453d11eff128cb4d
1 /*
2 Copyright (c) 2007 Paolo Capriotti <p.capriotti@sns.it>
3 (c) 2007 Maurizio Monge <maurizio.monge@kdemail.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 */
11 #ifndef HLVARIANT__CHESS__LEGALITYCHECK_H
12 #define HLVARIANT__CHESS__LEGALITYCHECK_H
14 #include "gamestate.h"
15 #include "interactiontype.h"
16 #include "turnpolicy.h"
18 namespace HLVariant {
19 namespace Chess {
21 template <typename _GameState>
22 class LegalityCheck {
23 public:
24 typedef _GameState GameState;
25 typedef typename GameState::Board Board;
26 typedef typename GameState::Move Move;
27 typedef typename GameState::Piece Piece;
28 protected:
29 const GameState& m_state;
30 public:
31 LegalityCheck(const GameState& state);
32 virtual ~LegalityCheck();
34 virtual typename Move::Type getMoveType(
35 const Piece& piece,
36 const Move& move,
37 const Piece& target = Piece()) const;
38 virtual bool pseudolegal(Move& move) const;
39 virtual bool legal(Move& move) const;
40 virtual bool attacks(
41 typename Piece::Color color,
42 const Point& p,
43 const Piece& target = Piece()) const;
44 virtual bool checkPromotion(typename Piece::Type type) const;
46 virtual typename Piece::Color mover(const Move& move) const;
47 virtual InteractionType movable(const TurnTest&, const Point& x) const;
48 virtual InteractionType droppable(const TurnTest&, int index) const;
51 // IMPLEMENTATION
53 template <typename GameState>
54 LegalityCheck<GameState>::~LegalityCheck() { }
56 template <typename GameState>
57 LegalityCheck<GameState>::LegalityCheck(const GameState& state)
58 : m_state(state) { }
60 template <typename GameState>
61 bool LegalityCheck<GameState>::legal(Move& move) const {
62 if (pseudolegal(move)) {
63 typename Piece::Color turn = mover(move);
65 GameState tmp(m_state);
66 tmp.move(move);
68 Point kingPos = tmp.board().find(Piece(turn, Piece::KING));
70 if (kingPos == Point::invalid())
71 return false;
73 LegalityCheck<GameState> tmpLegality(tmp);
74 if (tmpLegality.attacks(Piece::oppositeColor(turn), kingPos))
75 return false;
77 return true;
79 else {
80 return false;
84 template <typename GameState>
85 bool LegalityCheck<GameState>::pseudolegal(Move& move) const {
86 if (!move.valid()) return false;
88 if (!m_state.board().valid(move.from())) return false;
89 if (!m_state.board().valid(move.to())) return false;
91 Piece piece = m_state.board().get(move.from());
92 if (piece == Piece()) return false;
94 typename Piece::Color thisTurn = piece.color();
95 typename Piece::Color otherTurn = Piece::oppositeColor(thisTurn);
97 if (piece != Piece() && m_state.turn() == thisTurn) {
98 typename Move::Type move_type = getMoveType(piece, move);
99 move.setType(move_type);
100 if (!move.valid())
101 return false;
102 if (!checkPromotion(static_cast<typename Piece::Type>(move.promoteTo())))
103 return false;
104 if (move.kingSideCastling()) {
105 if (attacks(otherTurn, move.from()) ||
106 attacks(otherTurn, move.from() + Point(1, 0), piece))
107 return false;
109 if (move.queenSideCastling()) {
110 if (attacks(otherTurn, move.from()) ||
111 attacks(otherTurn, move.from() + Point(-1, 0), piece))
112 return false;
115 return true;
117 else {
118 return false;
122 template <typename GameState>
123 typename GameState::Move::Type
124 LegalityCheck<GameState>::
125 getMoveType(const Piece& piece, const Move& move, const Piece& _target) const {
126 Piece target = _target == Piece() ? m_state.board().get(move.to()) : _target;
128 switch (piece.type())
130 case Piece::ROOK:
132 if (move.from() == move.to())
133 return Move::INVALID;
134 PathInfo path = m_state.board().path(move.from(), move.to());
135 if (path.parallel() && path.clear() && target.color() != piece.color())
136 return Move::NORMAL;
137 else
138 return Move::INVALID;
141 case Piece::BISHOP:
143 if (move.from() == move.to())
144 return Move::INVALID;
145 PathInfo path = m_state.board().path(move.from(), move.to());
146 if (path.diagonal() && path.clear() && target.color() != piece.color())
147 return Move::NORMAL;
148 else
149 return Move::INVALID;
152 case Piece::KNIGHT:
153 if (target.color() == piece.color())
154 return Move::INVALID;
155 else
157 Point delta = move.from() - move.to();
158 if (abs(delta.x) == 1 && abs(delta.y) == 2)
159 return Move::NORMAL;
160 if (abs(delta.y) == 1 && abs(delta.x) == 2)
161 return Move::NORMAL;
162 return Move::INVALID;
165 case Piece::QUEEN:
167 if (move.from() == move.to())
168 return Move::INVALID;
169 PathInfo path = m_state.board().path(move.from(), move.to());
170 if (path.valid() && path.clear() && target.color() != piece.color())
171 return Move::NORMAL;
172 else
173 return Move::INVALID;
176 case Piece::KING:
178 if (move.from() == move.to())
179 return Move::INVALID;
180 Point delta = move.to() - move.from();
181 if (abs(delta.x) <= 1 && abs(delta.y) <= 1 && target.color() != piece.color()) {
182 return Move::NORMAL;
184 else if (move.from() == m_state.kingStartingPosition(piece.color())) {
185 if (delta == Point(2,0)) {
186 if (m_state.board().get(move.from() + Point(1,0)) == Piece() &&
187 m_state.board().get(move.to()) == Piece() &&
188 m_state.kingCastling(piece.color()))
189 return Move::KING_SIDE_CASTLING;
191 else if (delta == Point(-2,0)) {
192 if (m_state.board().get(move.from() - Point(1, 0)) == Piece() &&
193 m_state.board().get(move.to() + Point(1, 0)) == Piece() &&
194 m_state.board().get(move.to()) == Piece() &&
195 m_state.queenCastling(piece.color()))
196 return Move::QUEEN_SIDE_CASTLING;
199 return Move::INVALID;
202 case Piece::PAWN:
204 Point delta = move.to() - move.from();
205 bool enPassant = m_state.enPassant() == move.to();
207 // moving
208 if (target == Piece() && !enPassant) {
209 if (delta == m_state.direction(piece.color())) {
210 if (move.to().y == m_state.promotionRank(piece.color()))
211 return Move::PROMOTION;
212 else
213 return Move::NORMAL;
216 if (move.from().y == m_state.startingRank(piece.color())
217 + m_state.direction(piece.color()).y &&
218 delta == m_state.direction(piece.color()) * 2 &&
219 m_state.board().get(move.from() + m_state.direction(piece.color())) == Piece())
220 return Move::EN_PASSANT_TRIGGER;
221 else
222 return Move::INVALID;
225 // capturing
226 else if (enPassant || target.color() != piece.color()) {
227 if (delta.y == m_state.direction(piece.color()).y &&
228 abs(delta.x) == 1) {
229 if (enPassant)
230 return Move::EN_PASSANT_CAPTURE;
231 else if (move.to().y == m_state.promotionRank(piece.color()))
232 return Move::PROMOTION;
233 else
234 return Move::NORMAL;
238 return Move::INVALID;
241 default:
242 return Move::INVALID;
246 template <typename GameState>
247 bool LegalityCheck<GameState>::attacks(typename Piece::Color color, const Point& to, const Piece& target) const {
248 for (int i = 0; i < m_state.board().size().x; i++) {
249 for (int j = 0; j < m_state.board().size().y; j++) {
250 Point p(i, j);
251 Piece piece = m_state.board().get(p);
252 Move move(p, to);
253 if (piece != Piece() && piece.color() == color
254 && getMoveType(piece, move, target) != Move::INVALID)
255 return true;
258 return false;
261 template <typename GameState>
262 bool LegalityCheck<GameState>::checkPromotion(typename Piece::Type type) const {
263 return type == -1 ||
264 type == Piece::QUEEN ||
265 type == Piece::ROOK ||
266 type == Piece::BISHOP ||
267 type == Piece::KNIGHT;
270 template <typename GameState>
271 typename LegalityCheck<GameState>::Piece::Color
272 LegalityCheck<GameState>::mover(const Move& move) const {
273 return m_state.board().get(move.from()).color();
276 template <typename GameState>
277 InteractionType LegalityCheck<GameState>::movable(const TurnTest& test, const Point& p) const {
278 Piece piece = m_state.board().get(p);
279 if (piece == Piece() || !test(piece.color()))
280 return NoAction;
282 return piece.color() == m_state.turn() ? Moving : Premoving;
285 template <typename GameState>
286 InteractionType LegalityCheck<GameState>::droppable(const TurnTest& test, int index) const {
287 if (!test(index))
288 return NoAction;
290 typename Piece::Color c = static_cast<typename Piece::Color>(index);
291 return c == m_state.turn() ? Moving : Premoving;
294 } // namespace Chess
295 } // namespace HLVariant
297 #endif // HLVARIANT__CHESS__LEGALITYCHECK_H