Fixed DecoratedMove and serialization bug.
[tagua/yd.git] / src / hlvariant / chess / serializer.h
blobc8037b79db9d54af9063540b9270de5739823a1e
1 /*
2 Copyright (c) 2007 Paolo Capriotti <p.capriotti@sns.it>
3 (c) 2007 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 #ifndef HLVARIANT__CHESS__SERIALIZER_H
12 #define HLVARIANT__CHESS__SERIALIZER_H
14 #include "legalitycheck.h"
15 #include "san.h"
17 namespace HLVariant {
18 namespace Chess {
20 template <typename MoveGenerator>
21 class Serializer {
22 public:
23 /**
24 * Possible string representations for moves.
26 enum {
27 SIMPLE = 0, /// The most direct way of representing a move.
28 COMPACT = 1, /// Compact notation. This corresponds to SAN notation for games that support it.
29 DECORATED = 2 /// Symbolic figurine notation. Figurine names are enclosed in braces.
30 } MoveRepresentation;
31 private:
32 typedef typename MoveGenerator::LegalityCheck LegalityCheck;
33 typedef typename LegalityCheck::GameState GameState;
34 typedef typename GameState::Move Move;
35 typedef typename GameState::Board::Piece Piece;
37 int m_rep;
39 protected:
40 virtual QString suffix(const Move& move, const GameState& ref);
42 virtual QString san(const Move& move, const GameState& ref);
44 virtual void minimal_notation(SAN& san, const GameState& ref);
46 virtual Move get_san(const SAN& san, const GameState& ref);
48 virtual QChar symbol(typename Piece::Type type) const;
49 public:
50 /**
51 * Create a serializer to a given string representation for moves.
52 * \param rep A move representation.
53 * \sa MoveRepresentation.
55 Serializer(int rep);
57 virtual ~Serializer();
59 /**
60 * Serialize a move given a reference game state.
61 * We assume the move has already been tested against @a ref.
62 * Calling this function on an untested or illegal move is safe but its return
63 * value is undefined.
64 * Do not try to call this function on an invalid move.
65 * \param ref The position in which this move shall be executed.
66 * \return A string representation for this move.
68 virtual QString serialize(const Move& move, const GameState& ref);
70 /**
71 * Convert a string representation of a move back to a move object.
72 * \param ref The position in which this move shall be executed.
73 * \return A move corresponding to the given string representation.
75 virtual Move deserialize(const QString& str, const GameState& ref);
79 // IMPLEMENTATION
81 template <typename MoveGenerator>
82 Serializer<MoveGenerator>::Serializer(int rep)
83 : m_rep(rep) { }
85 template <typename MoveGenerator>
86 Serializer<MoveGenerator>::~Serializer() { }
88 template <typename MoveGenerator>
89 QString Serializer<MoveGenerator>::san(const Move& move, const GameState& ref) {
90 if (!move.valid())
91 return "";
93 QString res;
95 Piece piece = ref.board().get(move.from());
96 Piece captured = ref.board().get(move.captureSquare());
98 if (piece == Piece())
99 return ""; // no piece in the initial square
101 if (move.kingSideCastling()) {
102 res = "O-O";
104 else if (move.queenSideCastling()) {
105 res = "O-O-O";
107 else if (piece.type() == Piece::PAWN) {
108 if (captured != Piece())
109 res = move.from().col() + "x";
111 res += move.to().toString(ref.board().size().y);
113 else {
114 if (piece.type() != Piece::PAWN)
115 res = symbol(piece.type()).toUpper();
117 SAN tmp;
118 tmp.from = move.from();
119 tmp.to = move.to();
120 tmp.type = piece.type();
121 tmp.castling = SAN::NoCastling;
122 minimal_notation(tmp, ref);
124 res += tmp.from.toString(ref.board().size().y);
125 if (captured != Piece())
126 res += "x";
127 res += tmp.to.toString(ref.board().size().y);
130 if (move.promoteTo() != -1)
131 res += "=" + QString(symbol(static_cast<typename Piece::Type>(move.promoteTo())).toUpper());
133 res += suffix(move, ref);
135 return res;
138 template <typename MoveGenerator>
139 QString Serializer<MoveGenerator>::serialize(const Move& move, const GameState& ref) {
140 switch (m_rep) {
141 case SIMPLE:
143 int ysize = ref.board().size().y;
144 QString res = move.from().toString(ysize) + move.to().toString(ysize);
145 if (move.promoteTo() != -1)
146 res = res + "=" +
147 symbol(
148 static_cast<typename Piece::Type>(move.promoteTo())
149 ).toUpper();
150 return res;
152 case COMPACT:
153 return san(move, ref);
154 case DECORATED:
156 QString res = san(move, ref);
157 res.replace('K', "{king}");
158 res.replace('Q', "{queen}");
159 res.replace('R', "{rook}");
160 res.replace('N', "{knight}");
161 res.replace('B', "{bishop}");
162 res.replace('P', "{pawn}");
163 return res;
165 default:
166 return "";
170 template <typename MoveGenerator>
171 QString Serializer<MoveGenerator>::suffix(const Move& move, const GameState& ref) {
172 GameState tmp(ref);
173 tmp.move(move);
175 MoveGenerator generator(tmp);
176 if (generator.check(Piece::oppositeColor(ref.turn()))) {
177 if (generator.stalled())
178 return "#";
179 else
180 return "+";
182 else {
183 return "";
187 template <typename MoveGenerator>
188 typename Serializer<MoveGenerator>::Move
189 Serializer<MoveGenerator>::get_san(const SAN& san, const GameState& ref) {
190 Move candidate;
192 if (san.invalid())
193 return candidate;
195 if (san.castling != SAN::NoCastling) {
196 Point from = ref.kingStartingPosition(ref.turn());
197 Point to = from + (san.castling == SAN::KingSide? Point(2,0) : Point(-2,0));
198 Piece king = ref.board().get(from);
199 if (king.type() != Piece::KING)
200 return candidate;
201 else {
202 candidate = Move(from, to);
203 LegalityCheck check(ref);
204 if (check.legal(candidate))
205 return candidate;
206 else
207 return Move();
211 if (san.from.valid()) {
212 candidate = Move(san.from, san.to, static_cast<typename Piece::Type>(san.promotion));
214 else {
215 for (int i = 0; i < ref.board().size().x; i++) {
216 for (int j = 0; j < ref.board().size().y; j++) {
217 Point p(i, j);
218 Piece piece = ref.board().get(p);
220 Move mv(p, san.to, static_cast<typename Piece::Type>(san.promotion));
221 if (p.resembles(san.from) &&
222 piece.type() == san.type &&
223 piece.color() == ref.turn()) {
225 LegalityCheck check(ref);
226 if (check.legal(mv)) {
227 if (candidate.valid()) {
228 // ambiguous!
229 return Move();
231 else {
232 // ok, we have found a candidate move
233 candidate = mv;
241 return candidate;
244 template <typename MoveGenerator>
245 typename Serializer<MoveGenerator>::Move
246 Serializer<MoveGenerator>::deserialize(const QString& str, const GameState& ref) {
247 switch (m_rep) {
248 case SIMPLE:
249 return Move(); // BROKEN
250 case COMPACT:
252 SAN tmp;
253 tmp.load(str, ref.board().size().y);
254 return get_san(tmp, ref);
256 case DECORATED:
257 default:
258 // no need to parse decorated moves
259 return Move();
264 #define TRY(x) if(get_san(x, ref).valid()) return;
265 template <typename MoveGenerator>
266 void Serializer<MoveGenerator>::minimal_notation(SAN& san, const GameState& ref) {
267 Point from = san.from;
268 san.castling = SAN::NoCastling;
270 // try notation without starting point
271 san.from = Point::invalid();
272 TRY(san);
274 // add column indication
275 san.from = Point(from.x, -1);
276 TRY(san);
278 // add row indication
279 san.from = Point(-1, from.y);
280 TRY(san);
282 // add complete starting point
283 san.from = from;
285 #undef TRY
287 template <typename MoveGenerator>
288 QChar Serializer<MoveGenerator>::symbol(typename Piece::Type type) const {
289 if (type == Piece::KNIGHT)
290 return 'n';
291 else
292 return Piece::typeName(type)[0];
295 } // namespace Chess
296 } // namespace HLVariant
298 #endif // HLVARIANT__CHESS__SERIALIZER_H