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
);