ICSAPI::parseVerbose is now used instead of VariantInfo::getVerboseMove.
[tagua/yd.git] / src / variants / xiangqi.cpp_
blob36fd20f9d56da7afef4306f74ecb610e707e2a0f
1 #include "xiangqi.h"
2 #include "xchess/animator.impl.h"
3 #include "xchess/piece.h"
4 #include "xchess/move.h"
5 #include "highlevel.h"
6 #include "moveserializer.impl.h"
7 #include "crazyhouse_p.h"
9 class XiangQiPiece {
10 public:
11   enum Type {
12     GENERAL,
13     HORSE,
14     CHARIOT,
15     ADVISOR,
16     CANNON,
17     SOLDIER,
18     ELEPHANT,
19     INVALID_TYPE
20   };
21   enum Color {
22     RED,
23     BLACK,
24     INVALID_COLOR
25   };
26   typedef bool PromotionType;
27 private:
28   Color m_color;
29   Type m_type;
30   bool m_promoted;
31 public:
32   XiangQiPiece()
33   : m_color(INVALID_COLOR)
34   , m_type(INVALID_TYPE)
35   , m_promoted(false) { }
36   XiangQiPiece(XiangQiPiece::Color color, XiangQiPiece::Type type, bool promoted = false);
37   XiangQiPiece(const XiangQiPiece& other);
39   bool inPalace(const Point& p) const;
41   void promote() { m_promoted = true; }
42   bool promoted() { return m_promoted; }
44   bool operator<(const XiangQiPiece& p) const {
45     if (m_promoted == p.m_promoted)
46       if (m_color == p.m_color)
47         return m_type < p.m_type;
48       else
49         return m_color < p.m_color;
50     else
51       return m_promoted < p.m_promoted;
52   }
54   QString name() const;
55   static QString typeName(XiangQiPiece::Type t);
56   bool valid() const { return m_color != INVALID_COLOR && m_type != INVALID_TYPE; }
57   operator bool() const { return valid(); }
58   bool operator!() const { return !valid(); }
60   bool equals(const XiangQiPiece* p) const {
61     if (valid()) {
62       if (p) 
63         return (*this) == (*p);
64       else
65         return false;
66     }
67     else
68       return !p;
69   }
71   bool operator==(const XiangQiPiece& p) const {
72     return m_promoted == p.m_promoted &&
73            m_color == p.m_color &&
74            m_type == p.m_type;
75   }
77   bool operator!=(const XiangQiPiece& p) const {
78     return !(operator==(p));
79   }
81   static Type getType(const QString& t);
82   static QString typeSymbol(XiangQiPiece::Type t);
84   bool canMove(const class XiangQiPosition&, const Point&, const Point&) const;
85   Color color() const { return m_color; }
86   Type type() const { return m_type; }
88   static Color oppositeColor(Color c) { return c == RED ? BLACK : BLACK ? RED : INVALID_COLOR; }
89   Point direction() const { return Point(0, m_color == RED ? -1 : 1); }
92 XiangQiPiece::XiangQiPiece(XiangQiPiece::Color color, XiangQiPiece::Type type, bool promoted)
93 : m_color(color)
94 , m_type(type)
95 , m_promoted(promoted) { }
97 XiangQiPiece::XiangQiPiece(const XiangQiPiece& other)
98 : m_color(other.m_color)
99 , m_type(other.m_type)
100 , m_promoted(other.m_promoted) { }
102 bool XiangQiPiece::inPalace(const Point& p) const {
103   if(m_color == RED)
104     return p.x > 2 && p.x < 6 && p.y > 6;
105   else
106     return p.x > 2 && p.x < 6 && p.y < 3;
109 QString XiangQiPiece::name() const {
110   QString res = m_color == RED ? "red_" : "black_";
111   if (m_promoted)
112     res += "p_";
113   res += typeName(m_type);
114   return res;
117 QString XiangQiPiece::typeName(XiangQiPiece::Type t) {
118   switch (t) {
119     case GENERAL:
120       return "general";
121     case HORSE:
122       return "horse";
123     case CHARIOT:
124       return "chariot";
125     case ADVISOR:
126       return "advisor";
127     case CANNON:
128       return "cannon";
129     case SOLDIER:
130       return "soldier";
131     case ELEPHANT:
132       return "elephant";
133     default:
134       return "unknown";
135   }
138 XiangQiPiece::Type XiangQiPiece::getType(const QString&) {
139   return GENERAL; // FIXME
142 QString XiangQiPiece::typeSymbol(XiangQiPiece::Type t) {
143   switch (t) {
144     case GENERAL:
145       return "G";
146     case HORSE:
147       return "H";
148     case CHARIOT:
149       return "R";
150     case ADVISOR:
151       return "A";
152     case CANNON:
153       return "C";
154     case SOLDIER:
155       return "S";
156     case ELEPHANT:
157       return "E";
158     default:
159       return "?";
160   }
163 // ------------------------------
165 class XiangQiMove {
166   XiangQiPiece m_dropped;
167   bool m_promote;
168   template<typename T> friend class MoveSerializer;
169 public:
170   Point from;
171   Point to;
173   XiangQiMove();
174   XiangQiMove(const Point& from, const Point& to, bool promote);
175   XiangQiMove(const XiangQiPiece& piece, const Point& to);
177   static XiangQiMove createDropMove(const XiangQiPiece& piece, const Point& to);
178   QString toString(int) const;
180   bool operator==(const XiangQiMove& other) const;
182   const XiangQiPiece& dropped() const { return m_dropped; }
183   bool promote() const { return m_promote; }
184   bool valid() const { return to.valid(); }
187 XiangQiMove::XiangQiMove()
188 : m_promote(true)
189 , from(Point::invalid())
190 , to(Point::invalid()) { }
192 XiangQiMove::XiangQiMove(const Point& from, const Point& to, bool promote)
193 : m_promote(promote)
194 , from(from)
195 , to(to) { }
197 XiangQiMove::XiangQiMove(const XiangQiPiece& piece, const Point& to)
198 : m_dropped(piece)
199 , m_promote(false)
200 , from(Point::invalid())
201 , to(to) { }
203 XiangQiMove XiangQiMove::createDropMove(const XiangQiPiece& piece, const Point& to) {
204   return XiangQiMove(piece, to);
207 QString XiangQiMove::toString(int) const {
208   return "";
211 bool XiangQiMove::operator==(const XiangQiMove& other) const {
212   if (m_dropped)
213     return m_dropped == other.m_dropped
214         && to == other.to;
215   else
216     return m_promote == other.m_promote
217         && to == other.to
218         && from == other.from;
221 // ------------------------------
223 class XiangQiPosition {
224 public:
225   typedef XiangQiPiece Piece;
226   typedef XiangQiMove Move;
227   typedef std::map<XiangQiPiece, int> Pool;
228 private:
229   Piece::Color m_turn;
230   Grid<Piece> m_board;
231   Pool m_pool;
232 public:
233   template<typename T> friend class MoveSerializer;
234   XiangQiPosition();
235   XiangQiPosition(const XiangQiPosition&);
236   XiangQiPosition(Piece::Color turn, bool wk, bool wq,
237                                           bool bk, bool bq, const Point& ep);
238   XiangQiPosition(const QList<boost::shared_ptr<BaseOpt> >& opts);
239   virtual XiangQiPosition* clone() const { return new XiangQiPosition(*this); }
240   virtual ~XiangQiPosition() { }
242   virtual void setup();
244   bool testMove(Move&) const;
245   bool pseudolegal(Move& m) const;
247   virtual void addToPool(const Piece&, int) { }
248   virtual void removeFromPool(const Piece&, int) {  }
249   Pool& pool() { return m_pool; }
250   const Pool& pool() const { return m_pool; }
252   const XiangQiPiece* get(const Point& p) const;
253   XiangQiPiece* get(const Point& p);
254   void set(const Point& p, Piece* piece);
256   XiangQiPiece operator[](const Point& p) const { return m_board[p]; }
258   Piece::Color turn() const { return m_turn; }
259   void setTurn(Piece::Color turn) { m_turn = turn; }
260   Piece::Color previousTurn() const { return Piece::oppositeColor(m_turn); }
261   void switchTurn() { m_turn = Piece::oppositeColor(m_turn); }
263   void move(const XiangQiMove& m);
265   void fromFEN(const QString&, bool& ok) { ok = false; }
266   QString fen(int, int) const { return ""; }
267   bool operator==(const XiangQiPosition& p) const;
269   static Move getVerboseMove(Piece::Color turn, const VerboseNotation& m);
270   Move getMove(const AlgebraicNotation&, bool& ok) const;
271   boost::shared_ptr<XiangQiPiece> moveHint(const XiangQiMove& m) const;
273   Point size() const { return Point(9,10); }
274   void dump() const { }
276   PathInfo path(const Point& from, const Point& to) const { return m_board.path(from, to); }
278   QStringList borderCoords() const;
281 XiangQiPosition::XiangQiPosition()
282 : m_turn(XiangQiPiece::BLACK)
283 , m_board(9,10) { }
285 XiangQiPosition::XiangQiPosition(const XiangQiPosition& other)
286 : m_turn(other.m_turn)
287 , m_board(other.m_board)
288 , m_pool(other.m_pool) { }
290 XiangQiPosition::XiangQiPosition(Piece::Color turn, bool, bool, bool, bool, const Point&)
291 : m_turn(turn)
292 , m_board(9,10) { }
294 XiangQiPosition::XiangQiPosition(const QList<boost::shared_ptr<BaseOpt> >&)
295 : m_turn(XiangQiPiece::BLACK)
296 , m_board(9,10) { }
298 QStringList XiangQiPosition::borderCoords() const
300   QStringList retv;
301   for(int i=1; i<=9; i++)
302     retv += QString::number(i);
303   for(int i=0; i<10; i++)
304     retv += QString();
305   retv << QChar(0x4e5d)  << QChar(0x516b) << QChar(0x4e03) << QChar(0x516d)
306     << QChar(0x4e94) << QChar(0x56db) << QChar(0x4e09) << QChar(0x4e8c) << QChar(0x4e00);
307   for(int i=0; i<10; i++)
308     retv += QString();
309   return retv;
312 bool XiangQiPiece::canMove(const XiangQiPosition& pos,
313                          const Point& from, const Point& to) const {
314   if (!from.valid()) return false;
315   if (!to.valid()) return false;
316   if (from == to) return false;
317   if (pos[to].color() == m_color) return false;
318   Point delta = to - from;
320   switch (m_type) {
321   case GENERAL:
322     return (abs(delta.x) + abs(delta.y) == 1)
323           && inPalace(to);
324   case ADVISOR:
325     return (abs(delta.x) == 1) && (abs(delta.y) == 1)
326             && inPalace(to);
327   case ELEPHANT:
328     return (abs(delta.x) == 2) && (abs(delta.y) == 2)
329             && !pos[Point((to.x+from.x)/2, (to.y+from.y)/2)]
330             && (m_color == RED ? to.y > 4 : to.y < 5);
331   case HORSE:
332     return (delta.x*delta.x + delta.y*delta.y) == 5
333             && !pos[Point((to.x+from.x*2+1)/3, (to.y+from.y*2+1)/3)];
334   case CHARIOT:
335     {
336       PathInfo path = pos.path(from, to);
337       return path.parallel() && path.clear();
338     }
339   case CANNON:
340     {
341       PathInfo path = pos.path(from, to);
342       return path.parallel() &&
343               (pos[to] ? path.numObstacles() == 1 : path.clear());
344     }
345   case SOLDIER:
346     return (delta.x == 0 && delta.y == direction().y)
347           || (abs(delta.x) == 1 && delta.y == 0 &&
348              (m_color == RED ? to.y < 5 : to.y > 4));
349   default:
350     return false;
351   }
355 XiangQiPosition::Move XiangQiPosition::getVerboseMove(Piece::Color, const VerboseNotation&) {
356   return Move();
359 XiangQiPosition::Move XiangQiPosition::getMove(const AlgebraicNotation&, bool& ok) const {
360   ok = false;
361   return Move();
364 const XiangQiPiece* XiangQiPosition::get(const Point& p) const {
365   return m_board.valid(p) && m_board[p] ? &m_board[p] : 0;
368 XiangQiPiece* XiangQiPosition::get(const Point& p) {
369   return m_board.valid(p) && m_board[p] ? &m_board[p] : 0;
372 void XiangQiPosition::set(const Point& p, XiangQiPiece* piece) {
373   if (!m_board.valid(p)) return;
374   if (piece)
375     m_board[p] = *piece;
376   else
377     m_board[p] = Piece();
380 bool XiangQiPosition::operator==(const XiangQiPosition& p) const {
381   return m_turn == p.m_turn
382       && m_board == p.m_board;
385 boost::shared_ptr<XiangQiPiece> XiangQiPosition::moveHint(const XiangQiMove& m) const {
386   if (m.dropped()) return boost::shared_ptr<XiangQiPiece>(new XiangQiPiece(m.dropped()));
387   else return boost::shared_ptr<XiangQiPiece>();
390 #define SET_PIECE(i,j, color, type) m_board[Point(i,j)] = Piece(XiangQiPiece::color, XiangQiPiece::type)
391 void XiangQiPosition::setup() {
392   SET_PIECE(0,0, BLACK, CHARIOT);
393   SET_PIECE(1,0, BLACK, HORSE);
394   SET_PIECE(2,0, BLACK, ELEPHANT);
395   SET_PIECE(3,0, BLACK, ADVISOR);
396   SET_PIECE(4,0, BLACK, GENERAL);
397   SET_PIECE(5,0, BLACK, ADVISOR);
398   SET_PIECE(6,0, BLACK, ELEPHANT);
399   SET_PIECE(7,0, BLACK, HORSE);
400   SET_PIECE(8,0, BLACK, CHARIOT);
401   SET_PIECE(1,2, BLACK, CANNON);
402   SET_PIECE(7,2, BLACK, CANNON);
403   for(int i=0;i<=8;i+=2)
404     SET_PIECE(i, 3, BLACK, SOLDIER);
406   SET_PIECE(0,9, RED, CHARIOT);
407   SET_PIECE(1,9, RED, HORSE);
408   SET_PIECE(2,9, RED, ELEPHANT);
409   SET_PIECE(3,9, RED, ADVISOR);
410   SET_PIECE(4,9, RED, GENERAL);
411   SET_PIECE(5,9, RED, ADVISOR);
412   SET_PIECE(6,9, RED, ELEPHANT);
413   SET_PIECE(7,9, RED, HORSE);
414   SET_PIECE(8,9, RED, CHARIOT);
415   SET_PIECE(1,7, RED, CANNON);
416   SET_PIECE(7,7, RED, CANNON);
417   for(int i=0;i<=8;i+=2)
418     SET_PIECE(i, 6, RED, SOLDIER);
420   m_turn = XiangQiPiece::RED;
422 #undef SET_PIECE
424 bool XiangQiPosition::testMove(Move& m) const {
425   return pseudolegal(m);
428 bool XiangQiPosition::pseudolegal(Move& m) const {
429   const Piece& p = m_board[m.from];
430   return p.canMove(*this, m.from, m.to);
433 void XiangQiPosition::move(const XiangQiMove& m) {
434   if (Piece captured = m_board[m.to]) {
435     addToPool(Piece(Piece::oppositeColor(captured.color()),
436                     captured.type(), false), 1);
437   }
439   m_board[m.to] = m_board[m.from];
440   m_board[m.from] = Piece();
442   switchTurn();
445 template <>
446 class MoveSerializer<XiangQiPosition> {
447   const XiangQiMove&     m_move;
448   const XiangQiPosition& m_ref;
449 public:
450   MoveSerializer(const XiangQiMove& m, const XiangQiPosition& r)
451     : m_move(m), m_ref(r) { }
452   QString SAN() const {
453     XiangQiPiece p = m_ref[m_move.from];
454     int order = 0;
455     int num = 1;
456     for(int i=0;i<10;i++) {
457       Point f(m_move.from.x, i);
458       Point t(m_move.to-m_move.from+f);
459       if(f != m_move.from && m_ref[f] == p && m_ref.m_board.valid(t)) {
460         XiangQiMove m(f, m_move.to-m_move.from+f, false);
461         if(m_ref.testMove(m)) {
462           order = ((i < m_move.to.y) != (p.color() == XiangQiPiece::RED)) ? -1 : 1;
463           num++;
464         }
465       }
466     }
468     QString retv;
469     if(num >= 4 && (order == 0 || order == num-1)) //4/5 pawns case
470       retv += (order==0) ? "-" : "+";
471     else
472       retv += XiangQiPiece::typeSymbol(p.type());
474     if(num==1 || (num==3 && order==1) || (num==5 && order==2))
475       retv += QString::number((p.color() == XiangQiPiece::RED) ? 9-m_move.from.x : m_move.from.x+1);
476     else
477       retv += (order<num/2) ? "-" : "+";
479     if(m_move.from.x == m_move.to.x) {
480       retv += ((m_move.to.y>m_move.from.y) != (p.color() == XiangQiPiece::RED)) ? "+" : "-";
481       retv += QString::number(abs(m_move.to.y-m_move.from.y));
482     }
483     else {
484       retv += (m_move.to.y == m_move.from.y) ? "." :
485             ((m_move.to.y>m_move.from.y) != (p.color() == XiangQiPiece::RED)) ? "+" : "-";
486       retv += QString::number((p.color() == XiangQiPiece::RED) ? 9-m_move.to.x : m_move.to.x+1);
487     }
488     return retv;
489   }
491   DecoratedMove toDecoratedMove() const {
492     XiangQiPiece p = m_ref[m_move.from];
493     int order = 0;
494     int num = 1;
495     for(int i=0;i<10;i++) {
496       Point f(m_move.from.x, i);
497       Point t(m_move.to-m_move.from+f);
498       if(f != m_move.from && m_ref[f] == p && m_ref.m_board.valid(t)) {
499         XiangQiMove m(f, m_move.to-m_move.from+f, false);
500         if(m_ref.testMove(m)) {
501           order = ((i < m_move.to.y) != (p.color() == XiangQiPiece::RED)) ? -1 : 1;
502           num++;
503         }
504       }
505     }
507     DecoratedMove retv;
508     MovePart piece = QString();
510     std::cout << "num = " << num << "  order = " << order << std::endl;
511     if(num >= 4 && (order == 0 || order == num-1)) //4/5 pawns case
512       piece = MovePart((order==0) ? "front" : "rear", MovePart::Figurine);
513     else
514       piece = MovePart(p.name(), MovePart::Figurine);
516     if(num==1 || (num==3 && order==1) || (num==5 && order==2)) {
517       retv += piece;
518       retv += MovePart(((p.color() == XiangQiPiece::RED) ? "rn_" : "bn_")
519                 + QString::number((p.color() == XiangQiPiece::RED) ?
520                                                     9-m_move.from.x : m_move.from.x+1),
521                                                                         MovePart::Figurine);
522     }
523     else {
524       retv += MovePart((order<num/2) ? "front" : "rear", MovePart::Figurine);
525       retv += piece;
526     }
528     if(m_move.from.x == m_move.to.x) {
529       retv += MovePart(((m_move.to.y>m_move.from.y) != (p.color() == XiangQiPiece::RED))
530                                 ? "advances" : "retreats", MovePart::Figurine);
531       retv += MovePart(((p.color() == XiangQiPiece::RED) ? "rn_" : "bn_")
532                 + QString::number(abs(m_move.to.y-m_move.from.y)), MovePart::Figurine);
533     }
534     else {
535       retv += MovePart((m_move.to.y == m_move.from.y) ? "traverses" :
536             ((m_move.to.y>m_move.from.y) != (p.color() == XiangQiPiece::RED)) ? "advances" :
537                                                                 "retreats", MovePart::Figurine);
538       retv += MovePart(((p.color() == XiangQiPiece::RED) ? "rn_" : "bn_")
539                 + QString::number((p.color() == XiangQiPiece::RED) ? 9-m_move.to.x : m_move.to.x+1),
540                                                                                MovePart::Figurine);
541     }
542     return retv;
543   }
546 #if 0
547 class XiangQiAnimator : protected CrazyhouseAnimator {
548 protected:
549   typedef boost::shared_ptr<AnimationGroup> AnimationPtr;
550   virtual boost::shared_ptr<MovementAnimation>
551     createMovementAnimation(const Element& element, const QPoint& destination);
552 public:
553   XiangQiAnimator(PointConverter* converter, GraphicalPosition* position);
554   virtual ~XiangQiAnimator(){}
555   virtual AnimationPtr warp(AbstractPosition::Ptr);
556   virtual AnimationPtr forward(AbstractPosition::Ptr, const XiangQiMove& move);
557   virtual AnimationPtr back(AbstractPosition::Ptr, const XiangQiMove& move);
560 boost::shared_ptr<MovementAnimation>
561 XiangQiAnimator::createMovementAnimation(const Element& element, const QPoint& destination) {
562   if (element.piece()->type() == static_cast<int>(XiangQiPiece::HORSE))
563     return boost::shared_ptr<MovementAnimation>(new KnightMovementAnimation(element.sprite(),
564                                                        destination, m_anim_rotate, 1.0));
565   else
566     return boost::shared_ptr<MovementAnimation>(new MovementAnimation(element.sprite(),
567                                                                 destination, 1.0));
570 XiangQiAnimator::XiangQiAnimator(PointConverter* converter, GraphicalPosition* position)
571 : CrazyhouseAnimator(converter, position) { }
573 XiangQiAnimator::AnimationPtr XiangQiAnimator::warp(AbstractPosition::Ptr pos) {
574   return CrazyhouseAnimator::warp(pos);
577 XiangQiAnimator::AnimationPtr XiangQiAnimator::forward(AbstractPosition::Ptr pos, const XiangQiMove& move) {
578   if (move.dropped())
579     return CrazyhouseAnimator::forward(pos, CrazyhouseMove(CrazyhousePiece(WHITE, KING), move.to));
580   else
581     return CrazyhouseAnimator::forward(pos, CrazyhouseMove(move.from, move.to));
584 XiangQiAnimator::AnimationPtr XiangQiAnimator::back(AbstractPosition::Ptr pos, const XiangQiMove& move) {
585   if (move.dropped())
586     return CrazyhouseAnimator::back(pos, CrazyhouseMove(CrazyhousePiece(WHITE, KING), move.to));
587   else
588     return CrazyhouseAnimator::back(pos, CrazyhouseMove(move.from, move.to));
590 #endif
593 class XiangQiVariantInfo {
594 public:
595   typedef XiangQiPosition Position;
596   typedef Position::Move Move;
597   typedef Position::Piece Piece;
598   typedef SimpleAnimator<XiangQiVariantInfo> Animator;
599   static const bool m_simple_moves = false;
600   static void forallPieces(PieceFunction& f);
601   static QStringList borderCoords(){
602     return QStringList() << "i" << "h" << "g" << "f" << "e" << "d" << "c" << "b" << "a"
603                          << "10" << "9" << "8" << "7" << "6" << "5" << "4" << "3" << "2" << "1";
604   }
605   static int moveListLayout() { return 0; }
606   static OptList positionOptions() { return OptList(); }
607   static const char *m_name;
608   static const char *m_theme_proxy;
611 const char *XiangQiVariantInfo::m_name = "XiangQi";
612 const char *XiangQiVariantInfo::m_theme_proxy = "XiangQi";
615 void XiangQiVariantInfo::forallPieces(PieceFunction& f) {
616   ChessVariant::forallPieces(f);
619 VariantInfo* XiangQiVariant::static_xiangqi_variant = 0;
621 VariantInfo* XiangQiVariant::info() {
622   if (!static_xiangqi_variant)
623     static_xiangqi_variant = new WrappedVariantInfo<XiangQiVariantInfo>;
624   return static_xiangqi_variant;
628 template <>
629 struct MoveFactory<XiangQiVariantInfo> {
630   static XiangQiMove createNormalMove(const NormalUserMove& move) {
631     return XiangQiMove(move.from, move.to, move.promotionType >= 0);
632   }
633   static XiangQiMove createDropMove(const XiangQiPiece& dropped, const Point& to) {
634     return XiangQiMove(dropped, to);
635   }
637   static NormalUserMove toNormal(const XiangQiMove& move) {
638     return NormalUserMove(move.from, move.to);
639   }