Initial implementation of the new abstraction architecture.
[tagua.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
21 enum Color {
22 RED,
23 BLACK,
24 INVALID_COLOR
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;
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;
67 else
68 return !p;
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;
77 bool operator!=(const XiangQiPiece& p) const {
78 return !(operator==(p));
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";
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 "?";
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:
336 PathInfo path = pos.path(from, to);
337 return path.parallel() && path.clear();
339 case CANNON:
341 PathInfo path = pos.path(from, to);
342 return path.parallel() &&
343 (pos[to] ? path.numObstacles() == 1 : path.clear());
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;
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);
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++;
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));
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);
488 return retv;
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++;
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);
523 else {
524 retv += MovePart((order<num/2) ? "front" : "rear", MovePart::Figurine);
525 retv += piece;
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);
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);
542 return retv;
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";
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);
633 static XiangQiMove createDropMove(const XiangQiPiece& dropped, const Point& to) {
634 return XiangQiMove(dropped, to);
637 static NormalUserMove toNormal(const XiangQiMove& move) {
638 return NormalUserMove(move.from, move.to);