Ported verbose deserialization.
[tagua/yd.git] / src / hlvariant / chess / serializer.h
blob1c4f46a34a079ab80d6443108948fbc8045fc29c
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"
16 #include "icsverbose.h"
18 namespace HLVariant {
19 namespace Chess {
21 template <typename MoveGenerator>
22 class Serializer {
23 public:
24 /**
25 * Possible string representations for moves.
27 enum {
28 SIMPLE = 0, /// The most direct way of representing a move.
29 COMPACT = 1, /// Compact notation. This corresponds to SAN notation for games that support it.
30 DECORATED = 2, /// Symbolic figurine notation. Figurine names are enclosed in braces.
31 ICS_VERBOSE = 3 /// Verbose notation as defined by ICS.
32 } MoveRepresentation;
33 protected:
34 typedef typename MoveGenerator::LegalityCheck LegalityCheck;
35 typedef typename LegalityCheck::GameState GameState;
36 typedef typename GameState::Move Move;
37 typedef typename GameState::Board::Piece Piece;
39 int m_rep;
41 protected:
42 virtual QString suffix(const Move& move, const GameState& ref);
44 virtual QString san(const Move& move, const GameState& ref);
46 virtual void minimal_notation(SAN& san, const GameState& ref);
48 virtual Move get_san(const SAN& san, const GameState& ref);
50 virtual QChar symbol(typename Piece::Type type) const;
52 virtual Move parse_ics_verbose(const QString& str, const GameState& ref);
53 public:
54 /**
55 * Create a serializer to a given string representation for moves.
56 * \param rep A move representation.
57 * \sa MoveRepresentation.
59 Serializer(int rep);
61 virtual ~Serializer();
63 /**
64 * Serialize a move given a reference game state.
65 * We assume the move has already been tested against @a ref.
66 * Calling this function on an untested or illegal move is safe but its return
67 * value is undefined.
68 * Do not try to call this function on an invalid move.
69 * \param ref The position in which this move shall be executed.
70 * \return A string representation for this move.
72 virtual QString serialize(const Move& move, const GameState& ref);
74 /**
75 * Convert a string representation of a move back to a move object.
76 * \param ref The position in which this move shall be executed.
77 * \return A move corresponding to the given string representation.
79 virtual Move deserialize(const QString& str, const GameState& ref);
83 // IMPLEMENTATION
85 template <typename MoveGenerator>
86 Serializer<MoveGenerator>::Serializer(int rep)
87 : m_rep(rep) { }
89 template <typename MoveGenerator>
90 Serializer<MoveGenerator>::~Serializer() { }
92 template <typename MoveGenerator>
93 QString Serializer<MoveGenerator>::san(const Move& move, const GameState& ref) {
94 if (!move.valid())
95 return "";
97 QString res;
99 Piece piece = ref.board().get(move.from());
100 Piece captured = ref.board().get(move.captureSquare());
102 if (piece == Piece())
103 return ""; // no piece in the initial square
105 if (move.kingSideCastling()) {
106 res = "O-O";
108 else if (move.queenSideCastling()) {
109 res = "O-O-O";
111 else if (piece.type() == Piece::PAWN) {
112 if (captured != Piece())
113 res = move.from().col() + "x";
115 res += move.to().toString(ref.board().size().y);
117 else {
118 if (piece.type() != Piece::PAWN)
119 res = symbol(piece.type()).toUpper();
121 SAN tmp;
122 tmp.from = move.from();
123 tmp.to = move.to();
124 tmp.type = piece.type();
125 tmp.castling = SAN::NoCastling;
126 minimal_notation(tmp, ref);
128 res += tmp.from.toString(ref.board().size().y);
129 if (captured != Piece())
130 res += "x";
131 res += tmp.to.toString(ref.board().size().y);
134 if (move.promoteTo() != -1)
135 res += "=" + QString(symbol(static_cast<typename Piece::Type>(move.promoteTo())).toUpper());
137 res += suffix(move, ref);
139 return res;
142 template <typename MoveGenerator>
143 QString Serializer<MoveGenerator>::serialize(const Move& move, const GameState& ref) {
144 switch (m_rep) {
145 case SIMPLE:
147 int ysize = ref.board().size().y;
148 QString res = move.from().toString(ysize) + move.to().toString(ysize);
149 if (move.promoteTo() != -1)
150 res = res + "=" +
151 symbol(
152 static_cast<typename Piece::Type>(move.promoteTo())
153 ).toUpper();
154 return res;
156 case COMPACT:
157 return san(move, ref);
158 case DECORATED:
160 QString res = san(move, ref);
161 res.replace('K', "{king}");
162 res.replace('Q', "{queen}");
163 res.replace('R', "{rook}");
164 res.replace('N', "{knight}");
165 res.replace('B', "{bishop}");
166 res.replace('P', "{pawn}");
167 return res;
169 case ICS_VERBOSE:
170 default:
171 return "";
175 template <typename MoveGenerator>
176 QString Serializer<MoveGenerator>::suffix(const Move& move, const GameState& ref) {
177 GameState tmp(ref);
178 tmp.move(move);
180 MoveGenerator generator(tmp);
181 if (generator.check(Piece::oppositeColor(ref.turn()))) {
182 if (generator.stalled())
183 return "#";
184 else
185 return "+";
187 else {
188 return "";
192 template <typename MoveGenerator>
193 typename Serializer<MoveGenerator>::Move
194 Serializer<MoveGenerator>::get_san(const SAN& san, const GameState& ref) {
195 Move candidate;
197 if (san.invalid())
198 return candidate;
200 if (san.castling != SAN::NoCastling) {
201 Point from = ref.kingStartingPosition(ref.turn());
202 Point to = from + (san.castling == SAN::KingSide? Point(2,0) : Point(-2,0));
203 Piece king = ref.board().get(from);
204 if (king.type() != Piece::KING)
205 return candidate;
206 else {
207 candidate = Move(from, to);
208 LegalityCheck check(ref);
209 if (check.legal(candidate))
210 return candidate;
211 else
212 return Move();
216 if (san.from.valid()) {
217 candidate = Move(san.from, san.to, static_cast<typename Piece::Type>(san.promotion));
219 else {
220 for (int i = 0; i < ref.board().size().x; i++) {
221 for (int j = 0; j < ref.board().size().y; j++) {
222 Point p(i, j);
223 Piece piece = ref.board().get(p);
225 Move mv(p, san.to, static_cast<typename Piece::Type>(san.promotion));
226 if (p.resembles(san.from) &&
227 piece.type() == san.type &&
228 piece.color() == ref.turn()) {
230 LegalityCheck check(ref);
231 if (check.legal(mv)) {
232 if (candidate.valid()) {
233 // ambiguous!
234 return Move();
236 else {
237 // ok, we have found a candidate move
238 candidate = mv;
246 return candidate;
249 template <typename MoveGenerator>
250 typename Serializer<MoveGenerator>::Move
251 Serializer<MoveGenerator>::deserialize(const QString& str, const GameState& ref) {
252 switch (m_rep) {
253 case COMPACT:
255 SAN tmp;
256 tmp.load(str, ref.board().size().y);
257 return get_san(tmp, ref);
259 case ICS_VERBOSE:
260 return parse_ics_verbose(str, ref);
261 case SIMPLE:
262 case DECORATED:
263 default:
264 // no need to parse decorated moves
265 return Move();
270 #define TRY(x) if(get_san(x, ref).valid()) return;
271 template <typename MoveGenerator>
272 void Serializer<MoveGenerator>::minimal_notation(SAN& san, const GameState& ref) {
273 Point from = san.from;
274 san.castling = SAN::NoCastling;
276 // try notation without starting point
277 san.from = Point::invalid();
278 TRY(san);
280 // add column indication
281 san.from = Point(from.x, -1);
282 TRY(san);
284 // add row indication
285 san.from = Point(-1, from.y);
286 TRY(san);
288 // add complete starting point
289 san.from = from;
291 #undef TRY
293 template <typename MoveGenerator>
294 QChar Serializer<MoveGenerator>::symbol(typename Piece::Type type) const {
295 if (type == Piece::KNIGHT)
296 return 'n';
297 else
298 return Piece::typeName(type)[0];
301 template <typename MoveGenerator>
302 typename Serializer<MoveGenerator>::Move
303 Serializer<MoveGenerator>::parse_ics_verbose(const QString& str, const GameState& ref) {
304 ICSVerbose verbose;
305 verbose.load(str, ref.board().size().y);
307 Point from;
308 Point to;
310 if (verbose.castling == SAN::NoCastling) {
311 from = verbose.from;
312 to = verbose.to;
314 else {
315 from = ref.kingStartingPosition(ref.turn());
316 to = from + (verbose.castling == SAN::KingSide ? Point(2,0) : Point(-2, 0));
319 return Move(from, to, static_cast<typename Piece::Type>(verbose.promotion));
322 } // namespace Chess
323 } // namespace HLVariant
325 #endif // HLVARIANT__CHESS__SERIALIZER_H