removed clock pref widget. clock prefs are definitively part of the lua theme.
[kboard.git] / src / variants / shogi.cpp
blob04ffc59df6a6c9af628f737816d5e673a3999907
1 /*
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.
9 */
11 #include "shogi.h"
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"
20 class ShogiPiece {
21 public:
22 enum Type {
23 KING,
24 GOLD,
25 SILVER,
26 KNIGHT,
27 LANCE,
28 ROOK,
29 BISHOP,
30 PAWN,
31 INVALID_TYPE
33 enum Color {
34 BLACK,
35 WHITE,
36 INVALID_COLOR
38 typedef bool PromotionType;
39 private:
40 Color m_color;
41 Type m_type;
42 bool m_promoted;
43 public:
44 ShogiPiece()
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;
58 else
59 return m_color < p.m_color;
60 else
61 return m_promoted < p.m_promoted;
64 QString name() const;
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 {
71 if (valid()) {
72 if (p)
73 return (*this) == (*p);
74 else
75 return false;
77 else
78 return !p;
81 bool operator==(const ShogiPiece& p) const {
82 return m_promoted == p.m_promoted &&
83 m_color == p.m_color &&
84 m_type == p.m_type;
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)
103 : m_color(color)
104 , m_type(type)
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_";
114 if (m_promoted)
115 res += "p_";
116 res += typeName(m_type);
117 return res;
120 QString ShogiPiece::typeName(ShogiPiece::Type t) {
121 switch (t) {
122 case KING:
123 return "king";
124 case GOLD:
125 return "gold";
126 case SILVER:
127 return "silver";
128 case KNIGHT:
129 return "knight";
130 case LANCE:
131 return "lance";
132 case ROOK:
133 return "rook";
134 case BISHOP:
135 return "bishop";
136 case PAWN:
137 return "pawn";
138 default:
139 return "unknown";
143 ShogiPiece::Type ShogiPiece::getType(const QString&) {
144 return KING; // FIXME
147 QString ShogiPiece::typeSymbol(ShogiPiece::Type t) {
148 switch (t) {
149 case KING:
150 return "K";
151 case GOLD:
152 return "G";
153 case SILVER:
154 return "S";
155 case KNIGHT:
156 return "N";
157 case LANCE:
158 return "L";
159 case ROOK:
160 return "R";
161 case BISHOP:
162 return "B";
163 case PAWN:
164 return "P";
165 default:
166 return "?";
170 // ------------------------------
172 class ShogiMove {
173 ShogiPiece m_drop;
175 int m_pool;
176 int m_pool_index;
178 bool m_promote;
179 template<typename T> friend class MoveSerializer;
180 public:
181 Point from;
182 Point to;
184 ShogiMove();
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()
202 : m_pool(-1)
203 , m_pool_index(-1)
204 , m_promote(true)
205 , from(Point::invalid())
206 , to(Point::invalid()) { }
208 ShogiMove::ShogiMove(const Point& from, const Point& to, bool promote)
209 : m_pool(-1)
210 , m_pool_index(-1)
211 , m_promote(promote)
212 , from(from)
213 , to(to) { }
215 ShogiMove::ShogiMove(int pool, int pool_index, const Point& to)
216 : m_pool(pool)
217 , m_pool_index(pool_index)
218 , m_promote(false)
219 , from(Point::invalid())
220 , to(to) { }
222 QString ShogiMove::toString(int) const {
223 return "";
226 bool ShogiMove::operator==(const ShogiMove& other) const {
227 if (m_drop)
228 return m_drop == other.m_drop
229 && to == other.to;
230 else
231 return m_promote == other.m_promote
232 && to == other.to
233 && from == other.from;
236 // ------------------------------
238 class ShogiPosition {
239 public:
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;
247 private:
248 Piece::Color m_turn;
249 Grid<Piece> m_board;
250 Pool m_pool;
251 public:
252 template<typename T> friend class MoveSerializer;
253 ShogiPosition();
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])
289 return NoAction;
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)
317 , m_board(9,9) { }
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&)
325 : m_turn(turn)
326 , m_board(9, 9) { }
328 ShogiPosition::ShogiPosition(const QList<boost::shared_ptr<BaseOpt> >&)
329 : m_turn(ShogiPiece::BLACK)
330 , m_board(9,9) { }
332 QStringList ShogiPosition::borderCoords() const
334 QStringList retv;
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);
339 return retv + retv;
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;
350 if (!m_promoted) {
351 switch (m_type) {
352 case KING:
353 return abs(delta.x) <= 1 && abs(delta.y) <= 1;
354 case GOLD:
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);
358 case SILVER:
359 return (abs(delta.x) == abs(delta.y) && abs(delta.x) == 1)
360 || (delta.y == direction().y && abs(delta.x) <= 1);
361 case ROOK:
363 PathInfo path = pos.path(from, to);
364 return path.parallel() && path.clear();
366 case BISHOP:
368 PathInfo path = pos.path(from, to);
369 return path.diagonal() && path.clear();
371 case KNIGHT:
373 return abs(delta.x) == 1 && delta.y == direction().y * 2;
375 case LANCE:
377 PathInfo path = pos.path(from, to);
378 return delta.x == 0 && path.clear() && (delta.y * direction().y > 0);
380 case PAWN:
381 return delta.x == 0 && delta.y == direction().y;
382 default:
383 return false;
386 else {
387 switch (m_type) {
388 case SILVER:
389 case PAWN:
390 case LANCE:
391 case KNIGHT:
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);
395 case ROOK:
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();
401 case BISHOP:
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();
407 default:
408 return false;
414 ShogiPosition::Move ShogiPosition::getVerboseMove(Piece::Color, const VerboseNotation&) {
415 return Move();
418 ShogiPosition::Move ShogiPosition::getMove(const AlgebraicNotation&, bool& ok) const {
419 ok = false;
420 return Move();
423 ShogiPiece ShogiPosition::get(const Point& p) const {
424 if (m_board.valid(p)) {
425 return m_board[p];
427 else {
428 return ShogiPiece();
432 void ShogiPosition::set(const Point& p, const ShogiPiece& piece) {
433 if (m_board.valid(p)) {
434 m_board[p] = piece;
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;
496 #undef SET_PIECE
498 bool ShogiPosition::testMove(Move& m) const {
499 if (!pseudolegal(m))
500 return false;
502 ShogiPosition tmp(*this);
503 tmp.move(m);
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])
510 if (p == king) {
511 king_pos = i;
512 break;
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;
523 return true;
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))
529 return true;
531 else if (piece.type() == Piece::KNIGHT) {
532 if (piece.color() == Piece::WHITE) {
533 if (p.y >= 7)
534 return true;
536 else {
537 if (p.y <= 1)
538 return true;
541 return false;
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;
557 return true;
559 else {
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());
571 else {
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();
588 switchTurn();
591 class ShogiAnimatorBase;
593 class ShogiVariantInfo {
594 public:
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;
605 typedef NoPool Pool;
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> {
631 public:
632 typedef ShogiVariantInfo Variant;
633 protected:
634 typedef BaseAnimator<ShogiVariantInfo> Base;
635 typedef Base::API API;
636 public:
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);
647 if (piece)
648 res.addPreAnimation(Animate::move(piece, move.to));
650 if (captured)
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);
656 if (promoted) {
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));
665 return res;
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));
678 if (!piece) {
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);
687 if (old_piece) {
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);
693 piece = old;
697 res.addPreAnimation(Animate::move(piece, move.from));
699 return res;
704 template <>
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;
711 if (!m_move.drop())
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)
714 continue;
715 ShogiMove mv(i, m_move.to, false);
716 if (m_ref.testMove(mv)) {
717 ambiguous = true;
718 break;
721 return ambiguous;
723 public:
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();
730 QString retv;
731 if (p.promoted())
732 retv += "+";
733 retv += ShogiPiece::typeSymbol(p.type());
734 if (ambiguous) {
735 retv += QString::number(9-m_move.from.x);
736 retv += QString(m_move.from.y+'a');
738 if (m_move.drop())
739 retv += "*";
740 else if (m_ref.m_board[m_move.to])
741 retv += "x";
742 else
743 retv += "-";
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)
749 retv += "+";
750 else
751 retv += "=";
753 return retv;
756 DecoratedMove toDecoratedMove() const {
757 ShogiPiece p = m_move.drop() ? m_move.drop() : m_ref.m_board[m_move.from];
758 bool ambiguous = isAmbiguous();
759 DecoratedMove retv;
760 if(p.type() == ShogiPiece::KING)
761 retv += MovePart(p.color() == ShogiPiece::BLACK?"king1":"king2", MovePart::Figurine);
762 else
763 retv += MovePart((p.promoted() ? "p_" : "") + ShogiPiece::typeName(p.type()), MovePart::Figurine);
764 if (ambiguous) {
765 retv += MovePart(QString::number(9-m_move.from.x));
766 retv += MovePart("num_"+QString::number(m_move.from.y+1), MovePart::Figurine);
768 QString mmm;
769 if (m_move.drop())
770 mmm += "*";
771 else if (m_ref.m_board[m_move.to])
772 mmm += "x";
773 else
774 mmm += "-";
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("+");
782 else
783 retv += MovePart("=");
785 return retv;
790 template <>
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);