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 "moveserializer.impl.h"
16 #include "xchess/dropanimator.impl.h"
17 #include "crazyhouse_p.h"
18 #include "kboard_wrapped.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 // ------------------------------
179 template<typename T
> friend class MoveSerializer
;
185 ShogiMove(const Point
& from
, const Point
& to
, bool promote
);
186 ShogiMove(int pool
, int pool_index
, const Point
& to
);
188 QString
toString(int) const;
190 bool operator==(const ShogiMove
& other
) const;
192 const ShogiPiece
& drop() const { return m_drop
; }
193 void setDrop(const ShogiPiece
& piece
) { m_drop
= piece
; }
194 int pool() const { return m_pool
; }
195 int poolIndex() const { return m_pool_index
; }
197 bool promote() const { return m_promote
; }
198 bool valid() const { return to
.valid(); }
201 ShogiMove::ShogiMove()
205 , from(Point::invalid())
206 , to(Point::invalid()) { }
208 ShogiMove::ShogiMove(const Point
& from
, const Point
& to
, bool promote
)
215 ShogiMove::ShogiMove(int pool
, int pool_index
, const Point
& to
)
217 , m_pool_index(pool_index
)
219 , from(Point::invalid())
222 QString
ShogiMove::toString(int) const {
226 bool ShogiMove::operator==(const ShogiMove
& other
) const {
228 return m_drop
== other
.m_drop
231 return m_promote
== other
.m_promote
233 && from
== other
.from
;
236 // ------------------------------
238 class ShogiPosition
{
240 typedef ShogiPiece Piece
;
241 typedef ShogiMove Move
;
243 typedef PoolReference
<ShogiPosition
> PoolReference
;
244 typedef PoolConstReference
<ShogiPosition
> PoolConstReference
;
245 typedef PoolReference::Pool Pool
;
246 typedef PoolReference::PlayerPool PlayerPool
;
252 template<typename T
> friend class MoveSerializer
;
254 ShogiPosition(const ShogiPosition
&);
255 ShogiPosition(Piece::Color turn
, bool wk
, bool wq
,
256 bool bk
, bool bq
, const Point
& ep
);
257 ShogiPosition(const QList
<boost::shared_ptr
<BaseOpt
> >& opts
);
258 virtual ShogiPosition
* clone() const { return new ShogiPosition(*this); }
259 virtual ~ShogiPosition() { }
261 virtual void setup();
263 bool testMove(Move
&) const;
264 bool pseudolegal(Move
& m
) const;
266 PoolReference
pool(int);
267 PoolConstReference
pool(int) const;
269 PlayerPool
& rawPool(Piece::Color color
) { return m_pool
[color
]; }
270 const PlayerPool
& rawPool(Piece::Color color
) const { return const_cast<Pool
&>(m_pool
)[color
]; }
272 virtual const Point
first() const { return m_board
.first(); }
273 virtual const Point
last() const { return m_board
.last(); }
274 virtual Point
next(const Point
& p
) const { return m_board
.next(p
); }
275 inline bool valid(const Point
& p
) const { return m_board
.valid(p
); }
277 ShogiPiece
get(const Point
& p
) const;
278 void set(const Point
& p
, const Piece
& piece
);
280 ShogiPiece
operator[](const Point
& p
) const { return m_board
[p
]; }
282 Piece::Color
turn() const { return m_turn
; }
283 void setTurn(Piece::Color turn
) { m_turn
= turn
; }
284 Piece::Color
previousTurn() const { return Piece::oppositeColor(m_turn
); }
285 void switchTurn() { m_turn
= Piece::oppositeColor(m_turn
); }
287 InteractionType
movable(const Point
& p
) const {
288 if(!valid(p
) || !m_board
[p
])
290 return m_board
[p
].color() == m_turn
? Moving
: Premoving
;
292 InteractionType
droppable(int p
) const {
293 ShogiPiece::Color c
= static_cast<ShogiPiece::Color
>(p
);
294 return c
== m_turn
? Moving
: Premoving
;
296 void move(const ShogiMove
& m
);
298 void fromFEN(const QString
&, bool& ok
) { ok
= false; }
299 QString
fen(int, int) const { return ""; }
300 bool operator==(const ShogiPosition
& p
) const;
302 static Move
getVerboseMove(Piece::Color turn
, const VerboseNotation
& m
);
303 Move
getMove(const AlgebraicNotation
&, bool& ok
) const;
304 boost::shared_ptr
<ShogiPiece
> moveHint(const ShogiMove
& m
) const;
306 Point
size() const { return Point(9,9); }
307 void dump() const { }
309 static bool promotionZone(Piece::Color color
, const Point
& p
);
310 static bool stuckPiece(const Piece
& piece
, const Point
& to
);
311 PathInfo
path(const Point
& from
, const Point
& to
) const { return m_board
.path(from
, to
); }
312 QStringList
borderCoords() const;
315 ShogiPosition::ShogiPosition()
316 : m_turn(ShogiPiece::BLACK
)
319 ShogiPosition::ShogiPosition(const ShogiPosition
& other
)
320 : m_turn(other
.m_turn
)
321 , m_board(other
.m_board
)
322 , m_pool(other
.m_pool
) { }
324 ShogiPosition::ShogiPosition(Piece::Color turn
, bool, bool, bool, bool, const Point
&)
328 ShogiPosition::ShogiPosition(const QList
<boost::shared_ptr
<BaseOpt
> >&)
329 : m_turn(ShogiPiece::BLACK
)
332 QStringList
ShogiPosition::borderCoords() const
335 for(int i
=9; i
>0; i
--)
336 retv
+= QString::number(i
);
337 retv
<< QChar(0x4e5d) << QChar(0x516b) << QChar(0x4e03) << QChar(0x516d)
338 << QChar(0x4e94) << QChar(0x56db) << QChar(0x4e09) << QChar(0x4e8c) << QChar(0x4e00);
342 bool ShogiPiece::canMove(const ShogiPosition
& pos
,
343 const Point
& from
, const Point
& to
) const {
344 if (!from
.valid()) return false;
345 if (!to
.valid()) return false;
346 if (from
== to
) return false;
347 if (pos
[to
].color() == m_color
) return false;
348 Point delta
= to
- from
;
353 return abs(delta
.x
) <= 1 && abs(delta
.y
) <= 1;
355 return (delta
.x
== 0 && abs(delta
.y
) == 1)
356 || (delta
.y
== 0 && abs(delta
.x
) == 1)
357 || (delta
.y
== direction().y
&& abs(delta
.x
) <= 1);
359 return (abs(delta
.x
) == abs(delta
.y
) && abs(delta
.x
) == 1)
360 || (delta
.y
== direction().y
&& abs(delta
.x
) <= 1);
363 PathInfo path
= pos
.path(from
, to
);
364 return path
.parallel() && path
.clear();
368 PathInfo path
= pos
.path(from
, to
);
369 return path
.diagonal() && path
.clear();
373 return abs(delta
.x
) == 1 && delta
.y
== direction().y
* 2;
377 PathInfo path
= pos
.path(from
, to
);
378 return delta
.x
== 0 && path
.clear() && (delta
.y
* direction().y
> 0);
381 return delta
.x
== 0 && delta
.y
== direction().y
;
392 return (delta
.x
== 0 && abs(delta
.y
) == 1)
393 || (delta
.y
== 0 && abs(delta
.x
) == 1)
394 || (delta
.y
== direction().y
&& abs(delta
.x
) <= 1);
397 if (abs(delta
.x
) <= 1 && abs(delta
.y
) <= 1) return true;
398 PathInfo path
= pos
.path(from
, to
);
399 return path
.parallel() && path
.clear();
403 if (abs(delta
.x
) <= 1 && abs(delta
.y
) <= 1) return true;
404 PathInfo path
= pos
.path(from
, to
);
405 return path
.diagonal() && path
.clear();
414 ShogiPosition::Move
ShogiPosition::getVerboseMove(Piece::Color
, const VerboseNotation
&) {
418 ShogiPosition::Move
ShogiPosition::getMove(const AlgebraicNotation
&, bool& ok
) const {
423 ShogiPiece
ShogiPosition::get(const Point
& p
) const {
424 if (m_board
.valid(p
)) {
432 void ShogiPosition::set(const Point
& p
, const ShogiPiece
& piece
) {
433 if (m_board
.valid(p
)) {
438 ShogiPosition::PoolReference
ShogiPosition::pool(int index
) {
439 ShogiPiece::Color color
= static_cast<ShogiPiece::Color
>(index
);
440 return PoolReference(&m_pool
[color
], color
);
443 ShogiPosition::PoolConstReference
ShogiPosition::pool(int index
) const {
444 ShogiPiece::Color color
= static_cast<ShogiPiece::Color
>(index
);
445 return PoolConstReference(&m_pool
.find(color
)->second
, color
);
448 bool ShogiPosition::operator==(const ShogiPosition
& p
) const {
449 return m_turn
== p
.m_turn
450 && m_board
== p
.m_board
;
453 boost::shared_ptr
<ShogiPiece
> ShogiPosition::moveHint(const ShogiMove
& m
) const {
454 if (m
.drop()) return boost::shared_ptr
<ShogiPiece
>(new ShogiPiece(m
.drop()));
455 else return boost::shared_ptr
<ShogiPiece
>();
458 bool ShogiPosition::promotionZone(Piece::Color color
, const Point
& p
) {
459 return color
== ShogiPiece::WHITE
? p
.y
>= 6 : p
.y
<= 2;
462 #define SET_PIECE(i,j, color, type) m_board[Point(i,j)] = Piece(ShogiPiece::color, ShogiPiece::type)
463 void ShogiPosition::setup() {
464 for (int i
= 0; i
< 9; i
++) {
465 SET_PIECE(i
, 2, WHITE
, PAWN
);
466 SET_PIECE(i
, 6, BLACK
, PAWN
);
469 SET_PIECE(0,0, WHITE
, LANCE
);
470 SET_PIECE(1,0, WHITE
, KNIGHT
);
471 SET_PIECE(2,0, WHITE
, SILVER
);
472 SET_PIECE(3,0, WHITE
, GOLD
);
473 SET_PIECE(4,0, WHITE
, KING
);
474 SET_PIECE(5,0, WHITE
, GOLD
);
475 SET_PIECE(6,0, WHITE
, SILVER
);
476 SET_PIECE(7,0, WHITE
, KNIGHT
);
477 SET_PIECE(8,0, WHITE
, LANCE
);
478 SET_PIECE(1,1, WHITE
, ROOK
);
479 SET_PIECE(7,1, WHITE
, BISHOP
);
481 SET_PIECE(0,8, BLACK
, LANCE
);
482 SET_PIECE(1,8, BLACK
, KNIGHT
);
483 SET_PIECE(2,8, BLACK
, SILVER
);
484 SET_PIECE(3,8, BLACK
, GOLD
);
485 SET_PIECE(4,8, BLACK
, KING
);
486 SET_PIECE(5,8, BLACK
, GOLD
);
487 SET_PIECE(6,8, BLACK
, SILVER
);
488 SET_PIECE(7,8, BLACK
, KNIGHT
);
489 SET_PIECE(8,8, BLACK
, LANCE
);
490 SET_PIECE(1,7, BLACK
, BISHOP
);
491 SET_PIECE(7,7, BLACK
, ROOK
);
494 m_turn
= ShogiPiece::BLACK
;
498 bool ShogiPosition::testMove(Move
& m
) const {
502 ShogiPosition
tmp(*this);
505 // find king position
506 Point king_pos
= Point::invalid();
507 ShogiPiece
king(m_turn
, ShogiPiece::KING
);
508 for (Point i
= tmp
.m_board
.first(); i
<= tmp
.m_board
.last(); i
= tmp
.m_board
.next(i
)) {
509 if (ShogiPiece p
= tmp
[i
])
515 if (!king_pos
.valid()) return false;
517 // check if the king can be captured
518 for (Point i
= tmp
.m_board
.first(); i
<= tmp
.m_board
.last(); i
= tmp
.m_board
.next(i
)) {
519 if (ShogiPiece p
= tmp
[i
])
520 if (p
.color() == tmp
.turn() && p
.canMove(tmp
, i
, king_pos
)) return false;
526 bool ShogiPosition::stuckPiece(const ShogiPiece
& piece
, const Point
& p
) {
527 if (piece
.type() == Piece::PAWN
) {
528 if (p
.y
== (piece
.color() == Piece::WHITE
? 8 : 0))
531 else if (piece
.type() == Piece::KNIGHT
) {
532 if (piece
.color() == Piece::WHITE
) {
544 bool ShogiPosition::pseudolegal(Move
& m
) const {
545 if (!m
.drop() && m
.pool() != -1 && m
.poolIndex() != -1) {
546 m
.setDrop(pool(m
.pool()).get(m
.poolIndex()));
549 if (ShogiPiece dropped
= m
.drop()) {
550 if (m_board
[m
.to
]) return false;
551 if (stuckPiece(dropped
, m
.to
)) return false;
552 if (dropped
.type() == Piece::PAWN
) {
553 for (int i
= 0; i
< 9; i
++)
554 if (ShogiPiece other
= m_board
[Point(m
.to
.x
, i
)])
555 if (other
.color() == m_turn
&& other
.type() == Piece::PAWN
&& !other
.promoted()) return false;
560 const Piece
& p
= m_board
[m
.from
];
561 return p
&& p
.canMove(*this, m
.from
, m
.to
);
565 void ShogiPosition::move(const ShogiMove
& m
) {
566 if (Piece dropped
= m
.drop()) {
567 m_board
[m
.to
] = dropped
;
568 if (!--rawPool(dropped
.color())[dropped
.type()])
569 rawPool(dropped
.color()).erase(dropped
.type());
572 if (Piece captured
= m_board
[m
.to
]) {
573 rawPool(Piece::oppositeColor(captured
.color()))[captured
.type()]++;
576 m_board
[m
.to
] = m_board
[m
.from
];
577 m_board
[m
.from
] = Piece();
580 if (promotionZone(m_turn
, m
.to
) || promotionZone(m_turn
, m
.from
)) {
581 if (m
.promote() || stuckPiece(m_board
[m
.to
], m
.to
)) {
582 Piece::Type type
= m_board
[m
.to
].type();
583 if (type
!= ShogiPiece::KING
&& type
!= ShogiPiece::GOLD
)
584 m_board
[m
.to
].promote();
591 class ShogiAnimatorBase
;
593 class ShogiVariantInfo
{
595 static QStringList
getNumbers() {
596 return QStringList() << QChar(0x4e5d) << QChar(0x516b) << QChar(0x4e03)
597 << QChar(0x516d) << QChar(0x4e94) << QChar(0x56db)
598 << QChar(0x4e09) << QChar(0x4e8c) << QChar(0x4e00);
601 typedef ShogiPosition Position
;
602 typedef Position::Move Move
;
603 typedef Position::Piece Piece
;
604 typedef DropAnimatorMixin
<ShogiAnimatorBase
> Animator
;
606 static const bool m_simple_moves
= false;
607 static void forallPieces(PieceFunction
& f
);
608 static int moveListLayout() { return 0; }
609 static OptList
positionOptions() { return OptList(); }
610 static const char *m_name
;
611 static const char *m_theme_proxy
;
614 const char *ShogiVariantInfo::m_name
= "Shogi";
615 const char *ShogiVariantInfo::m_theme_proxy
= "Shogi";
618 void ShogiVariantInfo::forallPieces(PieceFunction
& f
) {
619 ChessVariant::forallPieces(f
);
622 VariantInfo
* ShogiVariant::static_shogi_variant
= 0;
624 VariantInfo
* ShogiVariant::info() {
625 if (!static_shogi_variant
)
626 static_shogi_variant
= new WrappedVariantInfo
<ShogiVariantInfo
>;
627 return static_shogi_variant
;
630 class ShogiAnimatorBase
: public BaseAnimator
<ShogiVariantInfo
> {
632 typedef ShogiVariantInfo Variant
;
634 typedef BaseAnimator
<ShogiVariantInfo
> Base
;
635 typedef Base::API API
;
637 ShogiAnimatorBase(API cinterface
)
638 : Base(cinterface
) { }
640 virtual AnimationGroupPtr
forward(const ShogiPosition
& final
, const ShogiMove
& move
) {
641 AnimationFactory
res(m_cinterface
->inner());
643 NamedSprite piece
= m_cinterface
->takeSprite(move
.from
);
644 NamedSprite captured
= m_cinterface
->takeSprite(move
.to
);
645 m_cinterface
->setSprite(move
.to
, piece
);
648 res
.addPreAnimation(Animate::move(piece
, move
.to
));
651 res
.addPostAnimation(Animate::destroy(captured
));
653 if (final
.get(move
.to
) != m_cinterface
->position()->get(move
.from
)) {
654 Piece promoted
= final
.get(move
.to
);
657 QPoint real
= m_cinterface
->converter()->toReal(move
.to
);
658 NamedSprite old_sprite
= m_cinterface
->getSprite(move
.to
);
659 NamedSprite new_sprite
= m_cinterface
->setPiece(move
.to
, promoted
, false);
661 res
.addPostAnimation(Animate::morph(old_sprite
, new_sprite
));
668 virtual AnimationGroupPtr
back(const ShogiPosition
& final
, const ShogiMove
& move
) {
669 AnimationFactory
res(m_cinterface
->inner());
671 NamedSprite piece
= m_cinterface
->takeSprite(move
.to
);
672 NamedSprite captured
;
673 if (Piece captured_piece
= final
.get(move
.to
)) {
674 captured
= m_cinterface
->setPiece(move
.to
, captured_piece
, false);
675 res
.addPreAnimation(Animate::appear(captured
));
679 piece
= m_cinterface
->createPiece(move
.to
, final
.get(move
.from
), false);
680 res
.addPreAnimation(Animate::appear(piece
));
683 m_cinterface
->setSprite(move
.from
, piece
);
685 if (final
.get(move
.from
) != m_cinterface
->position()->get(move
.to
)) {
686 Piece old_piece
= final
.get(move
.from
);
688 NamedSprite old
= m_cinterface
->createPiece(move
.to
, old_piece
, false);
689 res
.addPreAnimation(Animate::morph(piece
, old
));
691 // replace piece with pawn
692 m_cinterface
->setSprite(move
.from
, old
);
697 res
.addPreAnimation(Animate::move(piece
, move
.from
));
705 class MoveSerializer
<ShogiPosition
> {
706 const ShogiMove
& m_move
;
707 const ShogiPosition
& m_ref
;
708 bool isAmbiguous() const {
709 ShogiPiece p
= m_move
.drop() ? m_move
.drop() : m_ref
.m_board
[m_move
.from
];
710 bool ambiguous
= false;
712 for (Point i
= m_ref
.m_board
.first(); i
<= m_ref
.m_board
.last(); i
= m_ref
.m_board
.next(i
) ) {
713 if (i
==m_move
.from
|| m_ref
.m_board
[i
] != p
)
715 ShogiMove
mv(i
, m_move
.to
, false);
716 if (m_ref
.testMove(mv
)) {
724 MoveSerializer(const ShogiMove
& m
, const ShogiPosition
& r
)
725 : m_move(m
), m_ref(r
) { }
727 QString
SAN() const {
728 ShogiPiece p
= m_move
.drop() ? m_move
.drop() : m_ref
.m_board
[m_move
.from
];
729 bool ambiguous
= isAmbiguous();
733 retv
+= ShogiPiece::typeSymbol(p
.type());
735 retv
+= QString::number(9-m_move
.from
.x
);
736 retv
+= QString(m_move
.from
.y
+'a');
740 else if (m_ref
.m_board
[m_move
.to
])
744 retv
+= QString::number(9-m_move
.to
.x
);
745 retv
+= QString(m_move
.to
.y
+'a');
746 if (!p
.promoted() && !m_move
.drop() &&
747 ShogiPosition::promotionZone(m_ref
.turn(), m_move
.to
)) {
748 if (m_move
.m_promote
)
756 DecoratedMove
toDecoratedMove() const {
757 ShogiPiece p
= m_move
.drop() ? m_move
.drop() : m_ref
.m_board
[m_move
.from
];
758 bool ambiguous
= isAmbiguous();
760 if(p
.type() == ShogiPiece::KING
)
761 retv
+= MovePart(p
.color() == ShogiPiece::BLACK
?"king1":"king2", MovePart::Figurine
);
763 retv
+= MovePart((p
.promoted() ? "p_" : "") + ShogiPiece::typeName(p
.type()), MovePart::Figurine
);
765 retv
+= MovePart(QString::number(9-m_move
.from
.x
));
766 retv
+= MovePart("num_"+QString::number(m_move
.from
.y
+1), MovePart::Figurine
);
771 else if (m_ref
.m_board
[m_move
.to
])
775 mmm
+= QString::number(9-m_move
.to
.x
);
776 retv
+= MovePart(mmm
);
777 retv
+= MovePart("num_"+QString::number(m_move
.to
.y
+1), MovePart::Figurine
);
778 if (!p
.promoted() && !m_move
.drop() &&
779 ShogiPosition::promotionZone(m_ref
.turn(), m_move
.to
)) {
780 if (m_move
.m_promote
)
781 retv
+= MovePart("+");
783 retv
+= MovePart("=");
791 struct MoveFactory
<ShogiVariantInfo
> {
792 static ShogiMove
createNormalMove(const NormalUserMove
& move
) {
793 return ShogiMove(move
.from
, move
.to
, move
.promotionType
>= 0);
795 static ShogiMove
createDropMove(const DropUserMove
& move
) {
796 return ShogiMove(move
.m_pool
, move
.m_piece_index
, move
.m_to
);
799 static NormalUserMove
toNormal(const ShogiMove
& move
) {
800 return NormalUserMove(move
.from
, move
.to
);