Continued refactor work.
[tagua.git] / src / variants / shogi.cpp_
blob0ba78de46e7b450608fcbcc7e698c5301bfda186
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 "highlevel.h"
16 #include "moveserializer.impl.h"
17 #include "xchess/animator.impl.h"
18 #include "crazyhouse_p.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
32   };
33   enum Color {
34     BLACK,
35     WHITE,
36     INVALID_COLOR
37   };
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;
62   }
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;
76     }
77     else
78       return !p;
79   }
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;
85   }
87   bool operator!=(const ShogiPiece& p) const {
88     return !(operator==(p));
89   }
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";
140   }
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 "?";
167   }
170 // ------------------------------
172 class ShogiMove {
173   ShogiPiece m_dropped;
174   bool m_promote;
175   template<typename T> friend class MoveSerializer;
176 public:
177   Point from;
178   Point to;
180   ShogiMove();
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()
195 : m_promote(true)
196 , from(Point::invalid())
197 , to(Point::invalid()) { }
199 ShogiMove::ShogiMove(const Point& from, const Point& to, bool promote)
200 : m_promote(promote)
201 , from(from)
202 , to(to) { }
204 ShogiMove::ShogiMove(const ShogiPiece& piece, const Point& to)
205 : m_dropped(piece)
206 , m_promote(false)
207 , from(Point::invalid())
208 , to(to) { }
210 ShogiMove ShogiMove::createDropMove(const ShogiPiece& piece, const Point& to) {
211   return ShogiMove(piece, to);
214 QString ShogiMove::toString(int) const {
215   return "";
218 bool ShogiMove::operator==(const ShogiMove& other) const {
219   if (m_dropped)
220     return m_dropped == other.m_dropped
221         && to == other.to;
222   else
223     return m_promote == other.m_promote
224         && to == other.to
225         && from == other.from;
228 // ------------------------------
230 class ShogiPosition {
231 public:
232   typedef ShogiPiece Piece;
233   typedef ShogiMove Move;
234   typedef std::map<ShogiPiece, int> Pool;
235 private:
236   Piece::Color m_turn;
237   Grid<Piece> m_board;
238   Pool m_pool;
239 public:
240   template<typename T> friend class MoveSerializer;
241   ShogiPosition();
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)
257       m_pool.erase(p);
258   }
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)
294 , m_board(9,9) { }
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&)
302 : m_turn(turn)
303 , m_board(9, 9) { }
305 ShogiPosition::ShogiPosition(const QList<boost::shared_ptr<BaseOpt> >&)
306 : m_turn(ShogiPiece::BLACK)
307 , m_board(9,9) { }
309 QStringList ShogiPosition::borderCoords() const
311   QStringList retv;
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);
316   return retv + retv;
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;
327   if (!m_promoted) {
328     switch (m_type) {
329     case KING:
330       return abs(delta.x) <= 1 && abs(delta.y) <= 1;
331     case GOLD:
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);
335     case SILVER:
336       return (abs(delta.x) == abs(delta.y) && abs(delta.x) == 1)
337           || (delta.y == direction().y && abs(delta.x) <= 1);
338     case ROOK:
339       {
340         PathInfo path = pos.path(from, to);
341         return path.parallel() && path.clear();
342       }
343     case BISHOP:
344       {
345           PathInfo path = pos.path(from, to);
346           return path.diagonal() && path.clear();
347       }
348     case KNIGHT:
349       {
350         return abs(delta.x) == 1 && delta.y == direction().y * 2;
351       }
352     case LANCE:
353       {
354         PathInfo path = pos.path(from, to);
355         return delta.x == 0 && path.clear() && (delta.y * direction().y > 0);
356       }
357     case PAWN:
358       return delta.x == 0 && delta.y == direction().y;
359     default:
360       return false;
361     }
362   }
363   else {
364     switch (m_type) {
365     case SILVER:
366     case PAWN:
367     case LANCE:
368     case KNIGHT:
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);
372     case ROOK:
373       {
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();
377       }
378     case BISHOP:
379       {
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();
383       }
384     default:
385       return false;
386     }
387   }
391 ShogiPosition::Move ShogiPosition::getVerboseMove(Piece::Color, const VerboseNotation&) {
392   return Move();
395 ShogiPosition::Move ShogiPosition::getMove(const AlgebraicNotation&, bool& ok) const {
396   ok = false;
397   return Move();
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;
410   if (piece)
411     m_board[p] = *piece;
412   else
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);
435   }
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;
464 #undef SET_PIECE
466 bool ShogiPosition::testMove(Move& m) const {
467   if (!pseudolegal(m))
468     return false;
470   ShogiPosition tmp(*this);
471   tmp.move(m);
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])
478       if (p == king) {
479         king_pos = i;
480         break;
481       }
482   }
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;
489   }
491   return true;
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))
497       return true;
498   }
499   else if (piece.type() == Piece::KNIGHT) {
500     if (piece.color() == Piece::WHITE) {
501       if (p.y >= 7)
502         return true;
503     }
504     else {
505       if (p.y <= 1)
506         return true;
507     }
508   }
509   return false;
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;
520     }
521     return true;
522   }
523   else {
524     const Piece& p = m_board[m.from];
525     return p && p.canMove(*this, m.from, m.to);
526   }
529 void ShogiPosition::move(const ShogiMove& m) {
530   if (m.dropped()) {
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());
537   }
538   else {
539     if (Piece captured = m_board[m.to]) {
540       addToPool(Piece(Piece::oppositeColor(captured.color()),
541                       captured.type(), false), 1);
542     }
544     m_board[m.to] = m_board[m.from];
545     m_board[m.from] = Piece();
546   }
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();
553     }
554   }
556   switchTurn();
559 #if 0
560 class ShogiAnimator : protected CrazyhouseAnimator {
561 protected:
562   typedef boost::shared_ptr<AnimationGroup> AnimationPtr;
563   virtual boost::shared_ptr<MovementAnimation>
564     createMovementAnimation(const Element& element, const QPoint& destination);
565 public:
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));
578   else
579     return boost::shared_ptr<MovementAnimation>(new MovementAnimation(element.sprite(),
580                                                                 destination, 1.0));
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) {
591   if (move.dropped())
592     return CrazyhouseAnimator::forward(pos, CrazyhouseMove(CrazyhousePiece(WHITE, KING), move.to));
593   else
594     return CrazyhouseAnimator::forward(pos, CrazyhouseMove(move.from, move.to));
597 ShogiAnimator::AnimationPtr ShogiAnimator::back(AbstractPosition::Ptr pos, const ShogiMove& move) {
598   if (move.dropped())
599     return CrazyhouseAnimator::back(pos, CrazyhouseMove(CrazyhousePiece(WHITE, KING), move.to));
600   else
601     return CrazyhouseAnimator::back(pos, CrazyhouseMove(move.from, move.to));
603 #endif
605 class ShogiVariantInfo {
606 public:
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);
611   }
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;
642 template <>
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)
652         continue;
653       ShogiMove mv(i, m_move.to, false);
654       if (m_ref.testMove(mv)) {
655         ambiguous = true;
656         break;
657       }
658     }
659     return ambiguous;
660   }
661 public:
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();
668     QString retv;
669     if (p.promoted())
670       retv += "+";
671     retv += ShogiPiece::typeSymbol(p.type());
672     if (ambiguous) {
673       retv += QString::number(9-m_move.from.x);
674       retv += QString(m_move.from.y+'a');
675     }
676     if (m_move.m_dropped)
677       retv += "*";
678     else if (m_ref.m_board[m_move.to])
679       retv += "x";
680     else
681       retv += "-";
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)
687         retv += "+";
688       else
689         retv += "=";
690     }
691     return retv;
692   }
694   DecoratedMove toDecoratedMove() const {
695     ShogiPiece p = m_move.dropped() ? m_move.dropped() : m_ref.m_board[m_move.from];
696     bool ambiguous = isAmbiguous();
697     DecoratedMove retv;
698     if(p.type() == ShogiPiece::KING)
699       retv += MovePart(p.color() == ShogiPiece::BLACK?"king1":"king2", MovePart::Figurine);
700     else
701       retv += MovePart((p.promoted() ? "p_" : "") + ShogiPiece::typeName(p.type()), MovePart::Figurine);
702     if (ambiguous) {
703       retv += MovePart(QString::number(9-m_move.from.x));
704       retv += MovePart("num_"+QString::number(m_move.from.y+1), MovePart::Figurine);
705     }
706     QString mmm;
707     if (m_move.m_dropped)
708       mmm += "*";
709     else if (m_ref.m_board[m_move.to])
710       mmm += "x";
711     else
712       mmm += "-";
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("+");
720       else
721         retv += MovePart("=");
722     }
723     return retv;
724   }
728 template <>
729 struct MoveFactory<ShogiVariantInfo> {
730   static ShogiMove createNormalMove(const NormalUserMove& move) {
731     return ShogiMove(move.from, move.to, move.promotionType >= 0);
732   }
733   static ShogiMove createDropMove(const ShogiPiece& dropped, const Point& to) {
734     return ShogiMove(dropped, to);
735   }
737   static NormalUserMove toNormal(const ShogiMove& move) {
738     return NormalUserMove(move.from, move.to);
739   }