[#68] Fix Tori Shogi legalitycheck to detect in-check condition.
[tagua.git] / src / hlvariant / tori-shogi / legalitycheck.h
blob7f8f81156d3c92f34811a533d9ff5205371d7646
1 /*
2 Copyright (c) 2007 Paolo Capriotti <p.capriotti@sns.it>
3 (c) 2007 Maurizio Monge <maurizio.monge@kdemail.net>
4 (c) 2007 Yann Dirson <ydirson@altern.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
12 #ifndef HLVARIANT__TORISHOGI__LEGALITYCHECK_H
13 #define HLVARIANT__TORISHOGI__LEGALITYCHECK_H
15 #include "interactiontype.h"
16 #include "../shogi/legalitycheck.h"
18 namespace HLVariant {
19 namespace ToriShogi {
21 template <typename _GameState>
22 class LegalityCheck: public Shogi::LegalityCheck<_GameState> {
23 typedef Shogi::LegalityCheck<_GameState> Base;
24 public:
25 typedef _GameState GameState;
26 typedef typename GameState::Board Board;
27 typedef typename Board::Piece Piece;
28 typedef typename GameState::Move Move;
30 LegalityCheck(const GameState& state);
32 virtual bool getMoveType(const Piece& piece, const Move& move) const;
33 bool legal(Move& move) const;
34 bool pseudolegal(Move& move) const;
35 bool canBeCaptured(const GameState& state, const Point& point) const;
36 protected:
37 virtual bool stuckPiece(const Piece& piece, const Point& p) const;
40 // IMPLEMENTATION
42 template <typename GameState>
43 LegalityCheck<GameState>::LegalityCheck(const GameState& state)
44 : Base(state) { }
46 template <typename GameState>
47 bool LegalityCheck<GameState>::getMoveType(const Piece& piece, const Move& move) const {
48 if (!move.valid())
49 return false;
51 if (move.from() == move.to())
52 return false;
54 if (Base::m_state.board().get(move.to()).color() == piece.color())
55 return false;
56 Point delta = move.to() - move.from();
58 if (!piece.promoted()) {
59 switch (piece.type()) {
60 case Piece::PHOENIX:
61 return abs(delta.x) <= 1 && abs(delta.y) <= 1;
62 case Piece::FALCON:
63 return (abs(delta.x) == 1 && abs(delta.y) <= 1)
64 || (delta.x == 0 && delta.y == Base::m_state.direction(piece.color()).y);
65 case Piece::CRANE:
66 return (abs(delta.y) == 1 && abs(delta.x) <= 1);
67 case Piece::PHEASANT:
68 return (delta.x == 0 && delta.y == 2 * Base::m_state.direction(piece.color()).y)
69 || (abs(delta.x) == 1 && delta.y == -Base::m_state.direction(piece.color()).y);
71 case Piece::SWALLOW:
72 return delta == Base::m_state.direction(piece.color());
74 case Piece::RIGHT_QUAIL:
76 if (delta.x == 1 && delta.y == -Base::m_state.direction(piece.color()).y)
77 return true;
78 PathInfo path = Base::m_state.board().path(move.from(), move.to());
79 return ((path.parallel() && delta.y * Base::m_state.direction(piece.color()).y > 0) ||
80 (path.diagonal() && delta.y * Base::m_state.direction(piece.color()).y < 0
81 && delta.x < 0)) && path.clear();
83 case Piece::LEFT_QUAIL:
85 if (delta.x == -1 && delta.y == -Base::m_state.direction(piece.color()).y)
86 return true;
87 PathInfo path = Base::m_state.board().path(move.from(), move.to());
88 return ((path.parallel() && delta.y * Base::m_state.direction(piece.color()).y > 0) ||
89 (path.diagonal() && delta.y * Base::m_state.direction(piece.color()).y < 0
90 && delta.x > 0)) && path.clear();
93 default:
94 return false;
97 else {
98 switch (piece.type()) {
99 case Piece::SWALLOW: // Goose
100 return (delta.x == 0 && delta.y == -2 * Base::m_state.direction(piece.color()).y)
101 || (abs(delta.x) == 2 && delta.y == 2 * Base::m_state.direction(piece.color()).y);
103 case Piece::FALCON: // Eagle
105 if (abs(delta.x) <= 1 && abs(delta.y) <= 1)
106 return true;
107 PathInfo path = Base::m_state.board().path(move.from(), move.to());
108 return ((path.parallel() && delta.y * Base::m_state.direction(piece.color()).y < 0) ||
109 (path.diagonal() && delta.y * Base::m_state.direction(piece.color()).y > 0) ||
110 (path.diagonal() && delta.y * Base::m_state.direction(piece.color()).y == -2)) &&
111 path.clear();
113 default:
114 return false;
119 template <typename GameState>
120 bool LegalityCheck<GameState>::pseudolegal(Move& move) const {
121 if (move.drop() == Piece() &&
122 move.pool() != -1 &&
123 move.index() != -1) {
124 move.setDrop(Base::m_state.pools().pool(move.pool()).get(move.index()));
127 Piece dropped = move.drop();
128 if (dropped != Piece()) {
129 if (Base::m_state.board().get(move.to()) != Piece())
130 return false;
132 if (stuckPiece(dropped, move.to()))
133 return false;
135 if (dropped.type() == Piece::SWALLOW) {
136 int otherswallows = 0;
137 for (int i = 0; i < Base::m_state.board().size().y; i++) {
138 Piece other = Base::m_state.board().get(Point(move.to().x, i));
139 if (other.type() == Piece::SWALLOW &&
140 other.color() == Base::m_state.turn() &&
141 !other.promoted()) {
142 otherswallows++;
143 if (otherswallows >= 2)
144 return false;
149 return true;
151 else {
152 Piece piece = Base::m_state.board().get(move.from());
153 if (piece != Piece() && getMoveType(piece, move)) {
154 if (Base::m_state.canPromote(piece) &&
155 (Base::m_state.promotionZone(piece.color(), move.to()) ||
156 Base::m_state.promotionZone(piece.color(), move.from())))
157 move.setType(Move::PROMOTION);
158 return true;
160 else {
161 return false;
167 // strict copy from Shogi, for template-instantation reasons
168 template <typename GameState>
169 bool LegalityCheck<GameState>::canBeCaptured(const GameState& state, const Point& point) const {
170 for (int i = 0; i < state.board().size().x; i++) {
171 for (int j = 0; j < state.board().size().y; j++) {
172 Point p(i, j);
173 Piece piece = state.board().get(p);
174 LegalityCheck<GameState> check(state);
175 if (piece.color() == state.turn() && check.getMoveType(piece, Move(p, point))) {
176 kDebug() << state.board().get(point).name() << " can be captured";
177 return true;
181 return false;
184 template <typename GameState>
185 bool LegalityCheck<GameState>::legal(Move& move) const {
186 if (!pseudolegal(move))
187 return false;
189 GameState tmp(Base::m_state);
190 tmp.move(move);
192 // find king position
193 Point king_pos = tmp.board().find(Piece(Base::m_state.turn(), Piece::PHOENIX));
194 if (!king_pos.valid())
195 return false;
197 // check if the king can be captured
198 if (canBeCaptured(tmp, king_pos))
199 return false;
201 return true;
204 template <typename GameState>
205 bool LegalityCheck<GameState>::stuckPiece(const Piece& piece, const Point& p) const {
206 if (piece.type() == Piece::SWALLOW) {
207 return p.y == Base::m_state.startingRank(Piece::oppositeColor(piece.color()));
209 else {
210 return false;
214 } // namespace ToriShogi
215 } // namespace HLVariant
217 #endif // HLVARIANT__TORISHOGI__LEGALITYCHECK_H