2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@sns.it>
3 (c) 2006 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.
12 #include "xchess/animator.h"
13 #include "xchess/piece.h"
14 #include "xchess/move.h"
15 #include "highlevel.h"
16 #include "moveserializer.impl.h"
17 #include "xchess/animator.impl.h"
18 #include "crazyhouse_p.h"
38 typedef bool PromotionType;
45 : m_color(INVALID_COLOR)
46 , m_type(INVALID_TYPE)
47 , m_promoted(false) { }
48 ShogiPiece(ShogiPiece::Color color, ShogiPiece::Type type, bool promoted = false);
49 ShogiPiece(const ShogiPiece& other);
51 void promote() { m_promoted = true; }
52 bool promoted() { return m_promoted; }
54 bool operator<(const ShogiPiece& p) const {
55 if (m_promoted == p.m_promoted)
56 if (m_color == p.m_color)
57 return m_type < p.m_type;
59 return m_color < p.m_color;
61 return m_promoted < p.m_promoted;
65 static QString typeName(ShogiPiece::Type t);
66 bool valid() const { return m_color != INVALID_COLOR && m_type != INVALID_TYPE; }
67 operator bool() const { return valid(); }
68 bool operator!() const { return !valid(); }
70 bool equals(const ShogiPiece* p) const {
73 return (*this) == (*p);
81 bool operator==(const ShogiPiece& p) const {
82 return m_promoted == p.m_promoted &&
83 m_color == p.m_color &&
87 bool operator!=(const ShogiPiece& p) const {
88 return !(operator==(p));
91 static Type getType(const QString& t);
92 static QString typeSymbol(ShogiPiece::Type t);
94 bool canMove(const class ShogiPosition&, const Point&, const Point&) const;
95 Color color() const { return m_color; }
96 Type type() const { return m_type; }
98 static Color oppositeColor(Color c) { return c == WHITE ? BLACK : WHITE; }
99 Point direction() const { return Point(0, m_color == WHITE ? 1 : -1); }
102 ShogiPiece::ShogiPiece(ShogiPiece::Color color, ShogiPiece::Type type, bool promoted)
105 , m_promoted(promoted) { }
107 ShogiPiece::ShogiPiece(const ShogiPiece& other)
108 : m_color(other.m_color)
109 , m_type(other.m_type)
110 , m_promoted(other.m_promoted) { }
112 QString ShogiPiece::name() const {
113 QString res = m_color == WHITE ? "white_" : "black_";
116 res += typeName(m_type);
120 QString ShogiPiece::typeName(ShogiPiece::Type t) {
143 ShogiPiece::Type ShogiPiece::getType(const QString&) {
144 return KING; // FIXME
147 QString ShogiPiece::typeSymbol(ShogiPiece::Type t) {
170 // ------------------------------
173 ShogiPiece m_dropped;
175 template<typename T> friend class MoveSerializer;
181 ShogiMove(const Point& from, const Point& to, bool promote);
182 ShogiMove(const ShogiPiece& piece, const Point& to);
184 static ShogiMove createDropMove(const ShogiPiece& piece, const Point& to);
185 QString toString(int) const;
187 bool operator==(const ShogiMove& other) const;
189 const ShogiPiece& dropped() const { return m_dropped; }
190 bool promote() const { return m_promote; }
191 bool valid() const { return to.valid(); }
194 ShogiMove::ShogiMove()
196 , from(Point::invalid())
197 , to(Point::invalid()) { }
199 ShogiMove::ShogiMove(const Point& from, const Point& to, bool promote)
204 ShogiMove::ShogiMove(const ShogiPiece& piece, const Point& to)
207 , from(Point::invalid())
210 ShogiMove ShogiMove::createDropMove(const ShogiPiece& piece, const Point& to) {
211 return ShogiMove(piece, to);
214 QString ShogiMove::toString(int) const {
218 bool ShogiMove::operator==(const ShogiMove& other) const {
220 return m_dropped == other.m_dropped
223 return m_promote == other.m_promote
225 && from == other.from;
228 // ------------------------------
230 class ShogiPosition {
232 typedef ShogiPiece Piece;
233 typedef ShogiMove Move;
234 typedef std::map<ShogiPiece, int> Pool;
240 template<typename T> friend class MoveSerializer;
242 ShogiPosition(const ShogiPosition&);
243 ShogiPosition(Piece::Color turn, bool wk, bool wq,
244 bool bk, bool bq, const Point& ep);
245 ShogiPosition(const QList<boost::shared_ptr<BaseOpt> >& opts);
246 virtual ShogiPosition* clone() const { return new ShogiPosition(*this); }
247 virtual ~ShogiPosition() { }
249 virtual void setup();
251 bool testMove(Move&) const;
252 bool pseudolegal(Move& m) const;
254 virtual void addToPool(const Piece& p, int n) { m_pool[p] += n; }
255 virtual void removeFromPool(const Piece& p, int n) {
256 if((m_pool[p] -= n) <= 0)
259 Pool& pool() { return m_pool; }
260 const Pool& pool() const { return m_pool; }
262 const ShogiPiece* get(const Point& p) const;
263 ShogiPiece* get(const Point& p);
264 void set(const Point& p, Piece* piece);
266 ShogiPiece operator[](const Point& p) const { return m_board[p]; }
268 Piece::Color turn() const { return m_turn; }
269 void setTurn(Piece::Color turn) { m_turn = turn; }
270 Piece::Color previousTurn() const { return Piece::oppositeColor(m_turn); }
271 void switchTurn() { m_turn = Piece::oppositeColor(m_turn); }
273 void move(const ShogiMove& m);
275 void fromFEN(const QString&, bool& ok) { ok = false; }
276 QString fen(int, int) const { return ""; }
277 bool operator==(const ShogiPosition& p) const;
279 static Move getVerboseMove(Piece::Color turn, const VerboseNotation& m);
280 Move getMove(const AlgebraicNotation&, bool& ok) const;
281 boost::shared_ptr<ShogiPiece> moveHint(const ShogiMove& m) const;
283 Point size() const { return Point(9,9); }
284 void dump() const { }
286 static bool promotionZone(Piece::Color color, const Point& p);
287 static bool stuckPiece(const Piece& piece, const Point& to);
288 PathInfo path(const Point& from, const Point& to) const { return m_board.path(from, to); }
289 QStringList borderCoords() const;
292 ShogiPosition::ShogiPosition()
293 : m_turn(ShogiPiece::BLACK)
296 ShogiPosition::ShogiPosition(const ShogiPosition& other)
297 : m_turn(other.m_turn)
298 , m_board(other.m_board)
299 , m_pool(other.m_pool) { }
301 ShogiPosition::ShogiPosition(Piece::Color turn, bool, bool, bool, bool, const Point&)
305 ShogiPosition::ShogiPosition(const QList<boost::shared_ptr<BaseOpt> >&)
306 : m_turn(ShogiPiece::BLACK)
309 QStringList ShogiPosition::borderCoords() const
312 for(int i=9; i>0; i--)
313 retv += QString::number(i);
314 retv << QChar(0x4e5d) << QChar(0x516b) << QChar(0x4e03) << QChar(0x516d)
315 << QChar(0x4e94) << QChar(0x56db) << QChar(0x4e09) << QChar(0x4e8c) << QChar(0x4e00);
319 bool ShogiPiece::canMove(const ShogiPosition& pos,
320 const Point& from, const Point& to) const {
321 if (!from.valid()) return false;
322 if (!to.valid()) return false;
323 if (from == to) return false;
324 if (pos[to].color() == m_color) return false;
325 Point delta = to - from;
330 return abs(delta.x) <= 1 && abs(delta.y) <= 1;
332 return (delta.x == 0 && abs(delta.y) == 1)
333 || (delta.y == 0 && abs(delta.x) == 1)
334 || (delta.y == direction().y && abs(delta.x) <= 1);
336 return (abs(delta.x) == abs(delta.y) && abs(delta.x) == 1)
337 || (delta.y == direction().y && abs(delta.x) <= 1);
340 PathInfo path = pos.path(from, to);
341 return path.parallel() && path.clear();
345 PathInfo path = pos.path(from, to);
346 return path.diagonal() && path.clear();
350 return abs(delta.x) == 1 && delta.y == direction().y * 2;
354 PathInfo path = pos.path(from, to);
355 return delta.x == 0 && path.clear() && (delta.y * direction().y > 0);
358 return delta.x == 0 && delta.y == direction().y;
369 return (delta.x == 0 && abs(delta.y) == 1)
370 || (delta.y == 0 && abs(delta.x) == 1)
371 || (delta.y == direction().y && abs(delta.x) <= 1);
374 if (abs(delta.x) <= 1 && abs(delta.y) <= 1) return true;
375 PathInfo path = pos.path(from, to);
376 return path.parallel() && path.clear();
380 if (abs(delta.x) <= 1 && abs(delta.y) <= 1) return true;
381 PathInfo path = pos.path(from, to);
382 return path.diagonal() && path.clear();
391 ShogiPosition::Move ShogiPosition::getVerboseMove(Piece::Color, const VerboseNotation&) {
395 ShogiPosition::Move ShogiPosition::getMove(const AlgebraicNotation&, bool& ok) const {
400 const ShogiPiece* ShogiPosition::get(const Point& p) const {
401 return m_board.valid(p) && m_board[p] ? &m_board[p] : 0;
404 ShogiPiece* ShogiPosition::get(const Point& p) {
405 return m_board.valid(p) && m_board[p] ? &m_board[p] : 0;
408 void ShogiPosition::set(const Point& p, ShogiPiece* piece) {
409 if (!m_board.valid(p)) return;
413 m_board[p] = Piece();
416 bool ShogiPosition::operator==(const ShogiPosition& p) const {
417 return m_turn == p.m_turn
418 && m_board == p.m_board;
421 boost::shared_ptr<ShogiPiece> ShogiPosition::moveHint(const ShogiMove& m) const {
422 if (m.dropped()) return boost::shared_ptr<ShogiPiece>(new ShogiPiece(m.dropped()));
423 else return boost::shared_ptr<ShogiPiece>();
426 bool ShogiPosition::promotionZone(Piece::Color color, const Point& p) {
427 return color == ShogiPiece::WHITE ? p.y >= 6 : p.y <= 2;
430 #define SET_PIECE(i,j, color, type) m_board[Point(i,j)] = Piece(ShogiPiece::color, ShogiPiece::type)
431 void ShogiPosition::setup() {
432 for (int i = 0; i < 9; i++) {
433 SET_PIECE(i, 2, WHITE, PAWN);
434 SET_PIECE(i, 6, BLACK, PAWN);
437 SET_PIECE(0,0, WHITE, LANCE);
438 SET_PIECE(1,0, WHITE, KNIGHT);
439 SET_PIECE(2,0, WHITE, SILVER);
440 SET_PIECE(3,0, WHITE, GOLD);
441 SET_PIECE(4,0, WHITE, KING);
442 SET_PIECE(5,0, WHITE, GOLD);
443 SET_PIECE(6,0, WHITE, SILVER);
444 SET_PIECE(7,0, WHITE, KNIGHT);
445 SET_PIECE(8,0, WHITE, LANCE);
446 SET_PIECE(1,1, WHITE, ROOK);
447 SET_PIECE(7,1, WHITE, BISHOP);
449 SET_PIECE(0,8, BLACK, LANCE);
450 SET_PIECE(1,8, BLACK, KNIGHT);
451 SET_PIECE(2,8, BLACK, SILVER);
452 SET_PIECE(3,8, BLACK, GOLD);
453 SET_PIECE(4,8, BLACK, KING);
454 SET_PIECE(5,8, BLACK, GOLD);
455 SET_PIECE(6,8, BLACK, SILVER);
456 SET_PIECE(7,8, BLACK, KNIGHT);
457 SET_PIECE(8,8, BLACK, LANCE);
458 SET_PIECE(1,7, BLACK, BISHOP);
459 SET_PIECE(7,7, BLACK, ROOK);
462 m_turn = ShogiPiece::BLACK;
466 bool ShogiPosition::testMove(Move& m) const {
470 ShogiPosition tmp(*this);
473 // find king position
474 Point king_pos = Point::invalid();
475 ShogiPiece king(m_turn, ShogiPiece::KING);
476 for (Point i = tmp.m_board.first(); i <= tmp.m_board.last(); i = tmp.m_board.next(i)) {
477 if (ShogiPiece p = tmp[i])
483 if (!king_pos.valid()) return false;
485 // check if the king can be captured
486 for (Point i = tmp.m_board.first(); i <= tmp.m_board.last(); i = tmp.m_board.next(i)) {
487 if (ShogiPiece p = tmp[i])
488 if (p.color() == tmp.turn() && p.canMove(tmp, i, king_pos)) return false;
494 bool ShogiPosition::stuckPiece(const ShogiPiece& piece, const Point& p) {
495 if (piece.type() == Piece::PAWN) {
496 if (p.y == (piece.color() == Piece::WHITE ? 8 : 0))
499 else if (piece.type() == Piece::KNIGHT) {
500 if (piece.color() == Piece::WHITE) {
512 bool ShogiPosition::pseudolegal(Move& m) const {
513 if (ShogiPiece dropped = m.dropped()) {
514 if (m_board[m.to]) return false;
515 if (stuckPiece(dropped, m.to)) return false;
516 if (dropped.type() == Piece::PAWN) {
517 for (int i = 0; i < 9; i++)
518 if (ShogiPiece other = m_board[Point(m.to.x, i)])
519 if (other.color() == m_turn && other.type() == Piece::PAWN && !other.promoted()) return false;
524 const Piece& p = m_board[m.from];
525 return p && p.canMove(*this, m.from, m.to);
529 void ShogiPosition::move(const ShogiMove& m) {
531 Q_ASSERT(m_pool.count(m.dropped()));
532 Q_ASSERT(!m_board[m.to]);
534 m_board[m.to] = m.dropped();
535 if(!--m_pool[m.dropped()])
536 m_pool.erase(m.dropped());
539 if (Piece captured = m_board[m.to]) {
540 addToPool(Piece(Piece::oppositeColor(captured.color()),
541 captured.type(), false), 1);
544 m_board[m.to] = m_board[m.from];
545 m_board[m.from] = Piece();
548 if (promotionZone(m_turn, m.to) | promotionZone(m_turn, m.from)) {
549 if (m.promote() || stuckPiece(m_board[m.to], m.to)) {
550 Piece::Type type = m_board[m.to].type();
551 if (type != ShogiPiece::KING && type != ShogiPiece::GOLD)
552 m_board[m.to].promote();
560 class ShogiAnimator : protected CrazyhouseAnimator {
562 typedef boost::shared_ptr<AnimationGroup> AnimationPtr;
563 virtual boost::shared_ptr<MovementAnimation>
564 createMovementAnimation(const Element& element, const QPoint& destination);
566 ShogiAnimator(PointConverter* converter, GraphicalPosition* position);
567 virtual ~ShogiAnimator(){}
568 virtual AnimationPtr warp(AbstractPosition::Ptr);
569 virtual AnimationPtr forward(AbstractPosition::Ptr, const ShogiMove& move);
570 virtual AnimationPtr back(AbstractPosition::Ptr, const ShogiMove& move);
573 boost::shared_ptr<MovementAnimation>
574 ShogiAnimator::createMovementAnimation(const Element& element, const QPoint& destination) {
575 if (element.piece()->type() == static_cast<int>(ShogiPiece::KNIGHT))
576 return boost::shared_ptr<MovementAnimation>(new KnightMovementAnimation(element.sprite(),
577 destination, m_anim_rotate, 1.0));
579 return boost::shared_ptr<MovementAnimation>(new MovementAnimation(element.sprite(),
583 ShogiAnimator::ShogiAnimator(PointConverter* converter, GraphicalPosition* position)
584 : CrazyhouseAnimator(converter, position) { }
586 ShogiAnimator::AnimationPtr ShogiAnimator::warp(AbstractPosition::Ptr pos) {
587 return CrazyhouseAnimator::warp(pos);
590 ShogiAnimator::AnimationPtr ShogiAnimator::forward(AbstractPosition::Ptr pos, const ShogiMove& move) {
592 return CrazyhouseAnimator::forward(pos, CrazyhouseMove(CrazyhousePiece(WHITE, KING), move.to));
594 return CrazyhouseAnimator::forward(pos, CrazyhouseMove(move.from, move.to));
597 ShogiAnimator::AnimationPtr ShogiAnimator::back(AbstractPosition::Ptr pos, const ShogiMove& move) {
599 return CrazyhouseAnimator::back(pos, CrazyhouseMove(CrazyhousePiece(WHITE, KING), move.to));
601 return CrazyhouseAnimator::back(pos, CrazyhouseMove(move.from, move.to));
605 class ShogiVariantInfo {
607 static QStringList getNumbers() {
608 return QStringList() << QChar(0x4e5d) << QChar(0x516b) << QChar(0x4e03)
609 << QChar(0x516d) << QChar(0x4e94) << QChar(0x56db)
610 << QChar(0x4e09) << QChar(0x4e8c) << QChar(0x4e00);
613 typedef ShogiPosition Position;
614 typedef Position::Move Move;
615 typedef Position::Piece Piece;
616 typedef SimpleAnimator<ShogiVariantInfo> Animator;
617 static const bool m_simple_moves = false;
618 static void forallPieces(PieceFunction& f);
619 static int moveListLayout() { return 0; }
620 static OptList positionOptions() { return OptList(); }
621 static const char *m_name;
622 static const char *m_theme_proxy;
625 const char *ShogiVariantInfo::m_name = "Shogi";
626 const char *ShogiVariantInfo::m_theme_proxy = "Shogi";
629 void ShogiVariantInfo::forallPieces(PieceFunction& f) {
630 ChessVariant::forallPieces(f);
633 VariantInfo* ShogiVariant::static_shogi_variant = 0;
635 VariantInfo* ShogiVariant::info() {
636 if (!static_shogi_variant)
637 static_shogi_variant = new WrappedVariantInfo<ShogiVariantInfo>;
638 return static_shogi_variant;
643 class MoveSerializer<ShogiPosition> {
644 const ShogiMove& m_move;
645 const ShogiPosition& m_ref;
646 bool isAmbiguous() const {
647 ShogiPiece p = m_move.dropped() ? m_move.dropped() : m_ref.m_board[m_move.from];
648 bool ambiguous = false;
649 if (!m_move.m_dropped)
650 for (Point i = m_ref.m_board.first(); i <= m_ref.m_board.last(); i = m_ref.m_board.next(i) ) {
651 if (i==m_move.from || m_ref.m_board[i] != p)
653 ShogiMove mv(i, m_move.to, false);
654 if (m_ref.testMove(mv)) {
662 MoveSerializer(const ShogiMove& m, const ShogiPosition& r)
663 : m_move(m), m_ref(r) { }
665 QString SAN() const {
666 ShogiPiece p = m_move.dropped() ? m_move.dropped() : m_ref.m_board[m_move.from];
667 bool ambiguous = isAmbiguous();
671 retv += ShogiPiece::typeSymbol(p.type());
673 retv += QString::number(9-m_move.from.x);
674 retv += QString(m_move.from.y+'a');
676 if (m_move.m_dropped)
678 else if (m_ref.m_board[m_move.to])
682 retv += QString::number(9-m_move.to.x);
683 retv += QString(m_move.to.y+'a');
684 if (!p.promoted() && !m_move.dropped() &&
685 ShogiPosition::promotionZone(m_ref.turn(), m_move.to)) {
686 if (m_move.m_promote)
694 DecoratedMove toDecoratedMove() const {
695 ShogiPiece p = m_move.dropped() ? m_move.dropped() : m_ref.m_board[m_move.from];
696 bool ambiguous = isAmbiguous();
698 if(p.type() == ShogiPiece::KING)
699 retv += MovePart(p.color() == ShogiPiece::BLACK?"king1":"king2", MovePart::Figurine);
701 retv += MovePart((p.promoted() ? "p_" : "") + ShogiPiece::typeName(p.type()), MovePart::Figurine);
703 retv += MovePart(QString::number(9-m_move.from.x));
704 retv += MovePart("num_"+QString::number(m_move.from.y+1), MovePart::Figurine);
707 if (m_move.m_dropped)
709 else if (m_ref.m_board[m_move.to])
713 mmm += QString::number(9-m_move.to.x);
714 retv += MovePart(mmm);
715 retv += MovePart("num_"+QString::number(m_move.to.y+1), MovePart::Figurine);
716 if (!p.promoted() && !m_move.dropped() &&
717 ShogiPosition::promotionZone(m_ref.turn(), m_move.to)) {
718 if (m_move.m_promote)
719 retv += MovePart("+");
721 retv += MovePart("=");
729 struct MoveFactory<ShogiVariantInfo> {
730 static ShogiMove createNormalMove(const NormalUserMove& move) {
731 return ShogiMove(move.from, move.to, move.promotionType >= 0);
733 static ShogiMove createDropMove(const ShogiPiece& dropped, const Point& to) {
734 return ShogiMove(dropped, to);
737 static NormalUserMove toNormal(const ShogiMove& move) {
738 return NormalUserMove(move.from, move.to);