Update the board after the animator has done its work.
[tagua/yd.git] / src / game.cpp
blob3a48e1b3b824eba44258216a3de071b3e82ecf1d
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 <iostream>
12 #include <map>
13 #ifdef Q_CC_MSVC
14 #pragma warning( push )
15 #pragma warning( disable : 4100 )
16 #include <boost/variant.hpp>
17 #pragma warning( pop )
18 #else
19 #include <boost/variant.hpp>
20 #endif
21 #ifndef NO_PGN
22 #include "variants/variants.h"
23 #include "pgnparser.h"
24 #include "kboard.h"
25 #endif //NO_PGN
26 #include "game.h"
27 #include "game_p.h"
30 using namespace GamePrivate;
32 /**
33 \class Game game.h <game.h>
34 \brief A game with history and variations.
36 This template class encapsulates an editable game with history and undo editing.
39 /** Constructor, creates an empty game*/
40 Game::Game()
41 : current(-1)
42 , undo_pos(0) {
45 /** destructor */
46 Game::~Game() {
49 void Game::onAdded(const Index&) {
52 void Game::onRemoved(const Index&) {
55 void Game::onEntryChanged(const Index&, int) {
58 void Game::onPromoteVariation(const Index&, int) {
61 void Game::onSetComment(const Index&, const QString&) {
64 void Game::onSetVComment(const Index&, int, const QString&) {
67 void Game::onCurrentIndexChanged(const Index&) {
70 void Game::onAvailableUndo(bool) {
73 void Game::onAvailableRedo(bool) {
76 Entry* Game::fetch(const Index& ix) {
77 int at;
78 History *vec = fetchRef(ix, &at);
79 return vec ? &(*vec)[at] : NULL;
82 const Entry* Game::fetch(const Index& ix) const {
83 int at;
84 const History *vec = fetchRef(ix, &at);
85 return vec ? &(*vec)[at] : NULL;
88 History* Game::fetchRef(const Index& ix, int* idx) {
89 if(ix.num_moves >= (int)history.size() || ix.num_moves < 0 )
90 return NULL;
92 History* aretv = &history;
93 Entry* retv = &history[ix.num_moves];
94 if(idx) *idx = ix.num_moves;
96 for(int i=0; i<(int)ix.nested.size();i++) {
97 Variations::iterator it = retv->variations.find(ix.nested[i].variation);
98 if(it == retv->variations.end() || ix.nested[i].num_moves >= (int)it->second.size()
99 || ix.nested[i].num_moves < 0 )
100 return NULL;
102 aretv = &it->second;
103 retv = &it->second[ix.nested[i].num_moves];
104 if(idx) *idx = ix.nested[i].num_moves;
106 return aretv;
109 const History* Game::fetchRef(const Index& ix, int* idx) const {
110 return const_cast<const History*>(const_cast<Game*>(this)->fetchRef(ix, idx));
113 void Game::testMove(const Index& ix) {
114 if (ix != Index(0)) {
115 Entry *e1 = fetch(ix.prev());
116 Entry *e2 = fetch(ix);
117 if(!e1 || !e2 || !e1->position || !e2->move)
118 return;
120 if (!e1->position->testMove(e2->move)) {
121 std::cout << "--> warning, invalid move added to game history!" << std::endl;
122 //e2->move = MovePtr();
127 void Game::testMove() {
128 testMove(current);
131 void Game::saveUndo(const UndoOp& op) {
132 bool redo = undo_pos < (int)undo_history.size();
134 while(undo_pos < (int)undo_history.size())
135 undo_history.pop_back();
136 undo_history.push_back(op);
137 undo_pos++;
139 if(undo_pos == 1)
140 onAvailableUndo(true);
141 if(redo)
142 onAvailableRedo(false);
146 /** returns the index of the current position */
147 Index Game::index() const {
148 return current;
151 /** returns an index opinting to the last position in the main line */
152 Index Game::lastMainlineIndex() const {
153 return Index(history.size()-1);
156 /** true if the game contains the index */
157 bool Game::containsIndex(const Index& index) const {
158 return !!fetch(index);
161 /** returns the current move */
162 MovePtr Game::move() const {
163 return move(current);
166 /** returns the move at the given index */
167 MovePtr Game::move(const Index& index) const {
168 Entry *e = (Entry*)fetch(index);
169 if(!e) {
170 std::cout << "--> Error in Game::position! Index out of range!" << std::endl;
171 return MovePtr();
173 return e->move;
176 /** returns the current position */
177 PositionPtr Game::position() const {
178 return position(current);
181 /** returns the position at the given index */
182 PositionPtr Game::position(const Index& index) const {
183 Entry *e = (Entry*)fetch(index);
184 if(!e) {
185 std::cout << "--> Error in Game::position! Index out of range!" << std::endl;
186 return PositionPtr();
188 return e->position;
191 /** returns the current comment */
192 QString Game::comment() const {
193 return comment(current);
196 /** returns the comment at the given index */
197 QString Game::comment(const Index& index) const {
198 const Entry *e = fetch(index);
199 if(!e) {
200 std::cout << "--> Error in Game::comment! Index out of range!" << std::endl;
201 return QString();
203 return e->comment;
206 void Game::reset(PositionPtr pos) {
207 Q_ASSERT(pos);
209 undo_pos = 0;
210 undo_history.clear();
211 history.clear();
212 history.push_back( Entry(MovePtr(), pos) );
213 current = Index(0);
214 onCurrentIndexChanged();
217 /** undo */
218 void Game::undo() {
219 if(undo_pos <= 0) {
220 std::cout << "--> Info: Cannot undo at the beginning of the undo history!" << std::endl;
221 return;
224 bool last_undo = undo_pos == 1;
225 bool now_redo = undo_pos == (int)undo_history.size();
227 undo_pos--;
228 UndoOp* op = &(undo_history[undo_pos]);
230 if(boost::get<UndoAdd>(op)) {
231 UndoAdd *a = boost::get<UndoAdd>(op);
233 if(a->index.atVariationStart() ) {
234 Entry* e = fetch(a->index.prev());
235 Q_ASSERT(e);
237 int v = a->index.nested[a->index.nested.size()-1].variation;
238 Q_ASSERT(e->variations.count(v) == 1);
239 Q_ASSERT(e->variations[v].size() == 1);
241 e->variations.erase(v);
243 else {
244 int at;
245 std::vector<Entry>* vec = fetchRef(a->index, &at);
246 Q_ASSERT(vec);
247 Q_ASSERT((int)vec->size() == at+1);
249 vec->pop_back();
252 if(current == a->index) {
253 current = current.prev();
254 onCurrentIndexChanged();
257 onRemoved(a->index);
259 else if(boost::get<UndoPromote>(op)) {
260 UndoPromote *p = boost::get<UndoPromote>(op);
262 int at;
263 std::vector<Entry>* vec = fetchRef(p->index, &at);
264 Q_ASSERT(vec);
265 Q_ASSERT((*vec)[at].variations.count(p->variation)==1);
267 History vold = (*vec)[at].variations[p->variation];
268 History vnew;
269 for(int i=at+1; i<(int)vec->size(); i++)
270 vnew.push_back((*vec)[i]);
271 while((int)vec->size()>at+1)
272 vec->pop_back();
273 for(int i=0; i<(int)vold.size(); i++)
274 vec->push_back(vold[i]);
275 (*vec)[at].variations[p->variation] = vnew;
277 current = current.flipVariation(p->index, p->variation);
278 onPromoteVariation(p->index, p->variation);
279 //onCurrentIndexChanged();
281 else if(boost::get<UndoTruncate>(op)) {
282 UndoTruncate *t = boost::get<UndoTruncate>(op);
284 int at;
285 std::vector<Entry>* vec = fetchRef(t->index, &at);
286 Q_ASSERT(vec);
287 Q_ASSERT((int)vec->size() == at+1);
288 Q_ASSERT((*vec)[at].variations.empty());
290 for(int i=0;i<(int)t->history.size();i++)
291 vec->push_back(t->history[i]);
292 (*vec)[at].variations = t->variations;
293 (*vec)[at].vcomments = t->vcomments;
295 if(t->history.size())
296 onAdded(t->index.next());
297 for(Variations::iterator it = t->variations.begin(); it != t->variations.end(); ++it)
298 onAdded(t->index.next(it->first));
299 for(VComments::iterator it = t->vcomments.begin(); it != t->vcomments.end(); ++it)
300 onSetVComment(t->index, it->first, it->second);
302 else if(boost::get<UndoRemove>(op)) {
303 UndoRemove *r = boost::get<UndoRemove>(op);
305 Entry *e = fetch(r->index);
306 e->variations[r->variation] = r->history;
307 onAdded(r->index.next(r->variation));
308 if(!r->vcomment.isEmpty()) {
309 e->vcomments[r->variation] = r->vcomment;
310 onSetVComment(r->index, r->variation, r->vcomment);
313 else if(boost::get<UndoClear>(op)) {
314 UndoClear *c = boost::get<UndoClear>(op);
316 Entry *e = fetch(c->index);
317 e->variations = c->variations;
318 e->vcomments = c->vcomments;
319 for(Variations::iterator it = c->variations.begin(); it != c->variations.end(); ++it)
320 onAdded(c->index.next(it->first));
321 for(VComments::iterator it = c->vcomments.begin(); it != c->vcomments.end(); ++it)
322 onSetVComment(c->index, it->first, it->second);
324 else if(boost::get<UndoSetComment>(op)) {
325 UndoSetComment *sc = boost::get<UndoSetComment>(op);
326 Entry *e = fetch(sc->index);
327 Q_ASSERT(e);
329 if(sc->variation == -1) {
330 e->comment = sc->old_comment;
331 onSetComment(sc->index, sc->old_comment);
333 else {
334 if(sc->old_comment.isEmpty())
335 e->vcomments.erase(sc->variation);
336 else
337 e->vcomments[sc->variation] = sc->old_comment;
338 onSetVComment(sc->index, sc->variation, sc->old_comment);
342 if(last_undo)
343 onAvailableUndo(false);
344 if(now_redo)
345 onAvailableRedo(true);
348 /** redo */
349 void Game::redo() {
350 if(undo_pos >= (int)undo_history.size()) {
351 std::cout << "--> Info: Cannot redo at the end of the undo history!" << std::endl;
352 return;
355 bool now_undo = undo_pos == 0;
356 bool last_redo = undo_pos == (int)undo_history.size()-1;
358 UndoOp* op = &(undo_history[undo_pos]);
359 undo_pos++;
361 if(boost::get<UndoAdd>(op)) {
362 UndoAdd *a = boost::get<UndoAdd>(op);
364 if(a->index.atVariationStart() ) {
365 Entry* e = fetch(a->index.prev());
366 Q_ASSERT(e);
368 int v = a->index.nested[a->index.nested.size()-1].variation;
369 Q_ASSERT(e->variations.count(v) == 0);
371 History h;
372 h.push_back(a->entry);
373 e->variations[v] = h;
375 else {
376 int at;
377 std::vector<Entry>* vec = fetchRef(a->index.prev(), &at);
378 Q_ASSERT(vec);
379 Q_ASSERT((int)vec->size() == at+1);
381 vec->push_back(a->entry);
384 onAdded(a->index);
386 else if(boost::get<UndoPromote>(op)) {
387 UndoPromote *p = boost::get<UndoPromote>(op);
389 int at;
390 std::vector<Entry>* vec = fetchRef(p->index, &at);
392 Q_ASSERT(vec);
393 Q_ASSERT((*vec)[at].variations.count(p->variation)==1);
394 History vold = (*vec)[at].variations[p->variation];
395 History vnew;
396 for(int i=at+1; i<(int)vec->size(); i++)
397 vnew.push_back((*vec)[i]);
398 while((int)vec->size()>at+1)
399 vec->pop_back();
400 for(int i=0; i<(int)vold.size(); i++)
401 vec->push_back(vold[i]);
402 (*vec)[at].variations[p->variation] = vnew;
404 current = current.flipVariation(p->index, p->variation);
405 onPromoteVariation(p->index, p->variation);
406 //onCurrentIndexChanged();
408 else if(boost::get<UndoTruncate>(op)) {
409 UndoTruncate *t = boost::get<UndoTruncate>(op);
411 int at;
412 std::vector<Entry>* vec = fetchRef(t->index, &at);
413 Q_ASSERT(vec);
414 Q_ASSERT((int)vec->size() == at+1+(int)t->history.size());
416 while((int)vec->size() > at+1)
417 vec->pop_back();
418 (*vec)[at].variations.clear();
419 (*vec)[at].vcomments.clear();
421 if(current > t->index) {
422 current = t->index;
423 onCurrentIndexChanged();
426 if(t->history.size())
427 onRemoved(t->index.next());
428 for(Variations::iterator it = t->variations.begin(); it != t->variations.end(); ++it)
429 onRemoved(t->index.next(it->first));
431 else if(boost::get<UndoRemove>(op)) {
432 UndoRemove *r = boost::get<UndoRemove>(op);
434 Entry *e = fetch(r->index);
435 e->variations.erase(r->variation);
436 e->vcomments.erase(r->variation);
437 onRemoved(r->index.next(r->variation));
439 else if(boost::get<UndoClear>(op)) {
440 UndoClear *c = boost::get<UndoClear>(op);
442 Entry *e = fetch(c->index);
443 e->variations.clear();
444 e->vcomments.clear();
445 for(Variations::iterator it = c->variations.begin(); it != c->variations.end(); ++it)
446 onRemoved(c->index.next(it->first));
448 else if(boost::get<UndoSetComment>(op)) {
449 UndoSetComment *sc = boost::get<UndoSetComment>(op);
450 Entry *e = fetch(sc->index);
451 Q_ASSERT(e);
453 if(sc->variation == -1) {
454 e->comment = sc->new_comment;
455 onSetComment(sc->index, sc->new_comment);
457 else {
458 if(sc->new_comment.isEmpty())
459 e->vcomments.erase(sc->variation);
460 else
461 e->vcomments[sc->variation] = sc->new_comment;
462 onSetVComment(sc->index, sc->variation, sc->new_comment);
466 if(now_undo)
467 onAvailableUndo(true);
468 if(last_redo)
469 onAvailableRedo(false);
472 /** sets the comment in the current index */
473 void Game::setComment(const QString& c) {
474 setComment(current, c);
477 /** sets the comment in the given index */
478 void Game::setComment(const Index& ix, const QString& c) {
479 Entry* e = fetch(ix);
480 if(!e) {
481 std::cout << "--> Error in Game::setComment! Invalid index!" << std::endl;
482 return;
484 if(e->comment == c)
485 return;
487 saveUndo(UndoSetComment(ix, -1, e->comment, c));
488 e->comment = c;
489 onSetComment(ix, c);
492 /** sets the variation comment in the given index/variation */
493 void Game::setVComment(const Index& ix, int v, const QString& c) {
494 Entry* e = fetch(ix);
495 if(!e) {
496 std::cout << "--> Error in Game::setComment! Invalid index!" << std::endl;
497 return;
499 QString oc = e->vcomments.count(v) ? e->vcomments[v] : QString();
500 if(oc == c)
501 return;
503 saveUndo(UndoSetComment(ix, v, oc, c));
504 if(c.isEmpty())
505 e->vcomments.erase(v);
506 else
507 e->vcomments[v] = c;
508 onSetVComment(ix, v, c);
511 /** promotes the current position in the upper main line */
512 void Game::promoteVariation() {
513 promoteVariation(current);
516 /** promotes the given position in the upper main line */
517 void Game::promoteVariation(const Index& _ix) {
518 if(_ix.nested.size()==0) {
519 std::cout << "--> Error in Game::promoteVariation! cannot promote main line!" << std::endl;
520 return;
522 Index ix = _ix;
523 int v = ix.nested[ix.nested.size()-1].variation;
524 ix.nested.pop_back();
526 promoteVariation(ix, v);
529 /** promotes the given variation in the upper main line */
530 void Game::promoteVariation(const Index& ix, int v) {
531 int at;
532 std::vector<Entry>* vec = fetchRef(ix, &at);
533 Q_ASSERT(vec);
534 Q_ASSERT((*vec)[at].variations.count(v)==1);
536 History vold = (*vec)[at].variations[v];
537 History vnew;
538 for(int i=at+1; i<(int)vec->size(); i++)
539 vnew.push_back((*vec)[i]);
540 while((int)vec->size()>at+1)
541 vec->pop_back();
542 for(int i=0; i<(int)vold.size(); i++)
543 vec->push_back(vold[i]);
544 (*vec)[at].variations[v] = vnew;
546 saveUndo(UndoPromote(ix, v));
547 current = current.flipVariation(ix, v);
548 onPromoteVariation(ix, v);
549 //don't call onCurrentIndexChanged(), as the position did not change actually
552 /** removes the given variation in the current index */
553 void Game::removeVariation(int v) {
554 removeVariation(current, v);
557 /** removes the given variation in the given index */
558 void Game::removeVariation(const Index& _ix) {
559 if(_ix.nested.size()==0) {
560 std::cout << "--> Error in Game::removeVariation! cannot remove main line!" << std::endl;
561 return;
563 Index ix = _ix;
564 int v = ix.nested[ix.nested.size()-1].variation;
565 ix.nested.pop_back();
567 removeVariation(ix, v);
570 /** removes the given variation in the given index */
571 void Game::removeVariation(const Index& ix, int v) {
572 Entry* e = fetch(ix);
574 saveUndo(UndoRemove(ix, v, e->variations[v],
575 e->vcomments.count(v) ? e->vcomments[v] : QString() ));
576 e->variations.erase(v);
577 e->vcomments.erase(v);
579 onRemoved(ix.next(v));
580 if(current >= ix.next(v)) {
581 current = ix;
582 onCurrentIndexChanged();
586 /** removes the given variation in the current index */
587 void Game::clearVariations() {
588 clearVariations(current);
591 /** removes the given variation in the given index */
592 void Game::clearVariations(const Index& ix) {
593 Entry* e = fetch(ix);
595 UndoClear uc(ix, e->variations, e->vcomments);
596 saveUndo(uc);
597 e->variations.clear();
598 e->vcomments.clear();
600 for(Variations::iterator it = uc.variations.begin(); it != uc.variations.end(); ++it)
601 onRemoved(ix.next(it->first));
602 if(current > ix && !(current >= ix.next())) {
603 current = ix;
604 onCurrentIndexChanged();
608 /** removes all the successors of the current position */
609 void Game::truncate() {
610 truncate(current);
613 /** removes all the successors of the given position */
614 void Game::truncate(const Index& ix) {
615 int at;
616 History* vec = fetchRef(ix, &at);
617 if(!vec) {
618 std::cout << "--> Error in Game::truncate! Truncating at an unexisting index!" << std::endl;
619 return;
622 Entry *e = &(*vec)[at];
623 UndoTruncate undo(ix);
624 for(int i=at+1; i<(int)vec->size();i++)
625 undo.history.push_back((*vec)[i]);
626 while((int)vec->size()>at+1)
627 vec->pop_back();
629 undo.variations = e->variations;
630 undo.vcomments = e->vcomments;
631 saveUndo(undo);
632 e->variations.clear();
633 e->vcomments.clear();
635 if(undo.history.size())
636 onRemoved(undo.index.next());
637 for(Variations::iterator it = undo.variations.begin(); it != undo.variations.end(); ++it)
638 onRemoved(undo.index.next(it->first));
640 if(current > ix) {
641 current = ix;
642 onCurrentIndexChanged();
646 /** adds a new move+position after the current one, on the main
647 line if possible, or else in a new variation */
648 void Game::add(MovePtr m, PositionPtr pos) {
649 Q_ASSERT(pos);
651 Index old_c = current;
652 int at;
653 std::vector<Entry>* vec = fetchRef(current, &at);
654 Q_ASSERT(vec);
656 /* add the move on the mainline */
657 if((int)vec->size() <= at+1 ) {
658 Q_ASSERT((int)vec->size() == at+1);
659 vec->push_back(Entry(m, pos));
660 current = current.next();
661 testMove();
662 saveUndo(UndoAdd(current, Entry(m, pos)));
663 onAdded(current);
664 onCurrentIndexChanged(old_c);
666 /* we are playing the move that is already next in the mainline */
667 else if( (*vec)[at+1].position && (*vec)[at+1].position->equals(pos)) {
668 current = current.next();
669 onCurrentIndexChanged(old_c);
670 /* no need to test the move */
672 else {
673 Entry *e = fetch(current);
674 Q_ASSERT(e);
676 /* check if a variations with this move already exists. */
677 for(Variations::iterator it = e->variations.begin(); it != e->variations.end(); ++it)
678 if(it->second.size() > 0 && it->second[0].position
679 && it->second[0].position->equals(pos) ) {
680 current = current.next(it->first);
681 onCurrentIndexChanged(old_c);
683 return;
686 int var_id = e->last_var_id++;
687 e->variations[var_id].push_back(Entry(m, pos));
688 current = current.next(var_id);
689 testMove();
690 saveUndo(UndoAdd(current, Entry(m, pos)));
691 onAdded(current);
692 onCurrentIndexChanged(old_c);
696 /** forces a move+position at in certain index */
697 bool Game::insert(MovePtr m, PositionPtr pos, const Index& at) {
698 Entry *e = fetch(at);
700 if(!e) {
701 if(at.nested.size() == 0) {
702 if(undo_history.size()) {
703 undo_pos = 0;
704 undo_history.clear();
706 int hs = history.size();
707 history.resize(at.num_moves + 1);
708 history[at.num_moves] = Entry(m, pos);
709 testMove(at);
710 onAdded(Index(hs));
711 return true;
713 else {
714 std::cout << "--> Error in Game::insert! Index out of range!" << std::endl;
715 return false;
719 if(undo_history.size()) {
720 undo_pos = 0;
721 undo_history.clear();
723 bool res = e->position && e->position->equals(pos);
724 //*e = Entry(m, pos);
725 e->move = m;
726 e->position = pos;
727 testMove(at);
728 testMove(at.next());
729 for (Variations::const_iterator it = e->variations.begin();
730 it != e->variations.end(); ++it)
731 testMove(at.next(it->first));
732 onEntryChanged(at);
733 return res;
736 /** returns true if we cannot go forward */
737 bool Game::lastPosition() const {
738 return !fetch(current.next());
741 /** go back */
742 bool Game::back() {
743 if (current <= 0) return false; // first entry or uninitialized
744 Index old_c = current;
745 Index new_c = current.prev();
747 Entry *e = fetch(new_c);
748 if(!e || e->position == 0) return false; // gap immediately before current
749 current = new_c;
750 onCurrentIndexChanged(old_c);
752 return true;
755 /** go forward (in the current mainline) */
756 bool Game::forward() {
757 Index old_c = current;
758 Index new_c = current.next();
760 Entry *e = fetch(new_c);
761 if(!e || e->position == 0) {
762 return false; // gap immediately before current
764 current = new_c;
765 onCurrentIndexChanged(old_c);
767 return true;
770 /** go to the root position */
771 void Game::gotoFirst() {
772 Index old_c = current;
773 current = Index(0);
774 onCurrentIndexChanged(old_c);
777 /** go to the last position (in the current mainline) */
778 void Game::gotoLast() {
779 int at;
780 std::vector<Entry>* vec = fetchRef(current, &at);
781 Q_ASSERT(vec);
782 Q_ASSERT((int)vec->size() > at);
784 if((int)vec->size() > at+1) {
785 Index old_c = current;
786 current = current.next(-1, vec->size()-1-at);
787 onCurrentIndexChanged(old_c);
791 /** go to a specified index */
792 bool Game::goTo(const Index& index) {
793 if (fetch(index)) {
794 Index old_c = current;
795 current = index;
796 onCurrentIndexChanged(old_c);
797 return true;
799 return false;
802 QString Game::variationPgn(const History& vec, const Entry& e,
803 int start, const Index& _ix) const {
804 Index ix = _ix;
805 QString res;
807 for (int i = start; i < static_cast<int>(vec.size()); i++) {
808 const Entry& preve = (i > start) ? vec[i-1] : e;
810 QString mv = (vec[i].move && preve.position) ?
811 vec[i].move->SAN( preve.position ) : "???";
812 #if 0
813 if (ix == current)
814 mv = "[[" + mv + "]]";
815 #endif
817 int n = ix.totalNumMoves()+1;
818 if(i==start || n%2==0)
819 mv = QString::number(n/2)+(n%2==1 ? ". ... " : ". ") + mv;
820 if (i > start)
821 mv = " " + mv;
823 res += mv;
825 if(!vec[i].comment.isEmpty())
826 res += " {" + vec[i].comment + "}";
828 if(i > 0) {
829 for(Variations::const_iterator it = vec[i-1].variations.begin();
830 it != vec[i-1].variations.end(); ++it) {
831 res += " (";
832 if(vec[i-1].vcomments.count(it->first))
833 res += "{" + vec[i-1].vcomments.find(it->first)->second + "} ";
834 res += variationPgn(it->second, vec[i - 1], 0,
835 ix.prev().next(it->first)) + ")";
839 ix = ix.next();
841 return res;
844 /** returns a pgn containing the whole game (with variations) */
845 QString Game::pgn() const {
846 return variationPgn(history, history[0], 1, Index(1));
849 #ifndef NO_PGN
850 /** loads a pgn in the current game */
851 void Game::load(const PGN& pgn) {
852 std::map<QString, QString>::const_iterator var = pgn.m_tags.find("Variant");
853 VariantInfo *vi;
855 if(var == pgn.m_tags.end())
856 vi = Variant::variant("Chess");
857 else if(!(vi = Variant::variant(var->second))) {
858 std::cout << " --> Error, no such variant " << var->second << std::endl;
859 return;
861 std::cout << "Fine, loaded variant " << vi->name() << std::endl;
863 std::map<QString, QString>::const_iterator fen = pgn.m_tags.find("FEN");
864 PositionPtr pos;
866 if(var == pgn.m_tags.end()) {
867 pos = vi->createPosition();
868 pos->setup();
870 else if( !(pos = vi->createPositionFromFEN(fen->second))) {
871 std::cout << " --> Error, wrong fen " << fen->second << std::endl;
872 return;
875 //TODO: what about options? FEN rules?
877 load(pos, pgn);
880 /** loads a pgn in the current game */
881 void Game::load(PositionPtr pos, const PGN& pgn) {
882 current = Index(0);
883 undo_history.clear();
884 undo_pos = 0;
886 if(history.size()) {
887 Entry* fe = &history[0];
888 int old_history_size = history.size();
889 std::vector<int> v_ids;
891 while(history.size()>1)
892 history.pop_back();
893 for(Variations::const_iterator it = fe->variations.begin();
894 it != fe->variations.end(); ++it)
895 v_ids.push_back(it->first);
896 fe->variations.clear();
897 fe->vcomments.clear();
899 for(int i=0;i<(int)v_ids.size();i++)
900 onRemoved(Index(0).next(v_ids[i]));
901 if(old_history_size>1)
902 onRemoved(Index(1));
903 v_ids.clear();
904 history[0].position = pos;
906 else
907 history.push_back( Entry(MovePtr(), pos) );
909 QString vcomment;
910 std::vector<Index> var_stack;
911 bool var_start = false;
913 for (uint i = 0; i < pgn.m_entries.size(); i++) {
914 if(boost::get<QString>(pgn[i])) {
915 if(var_start)
916 vcomment += *boost::get<QString>(pgn[i]);
917 else {
918 Entry *e = fetch(current);
919 Q_ASSERT(e);
921 e->comment += *boost::get<QString>(pgn[i]);
924 else if(boost::get<PGN::BeginVariation>(pgn[i])) {
925 var_stack.push_back(current);
926 var_start = true;
928 else if(boost::get<PGN::EndVariation>(pgn[i])) {
929 if(var_stack.size() == 0) {
930 std::cout << " --> Error, unexpected end variation!!!" << std::endl;
931 break;
933 current = var_stack[var_stack.size()-1];
934 var_stack.pop_back();
936 else if(boost::get<PGN::Move>(pgn[i])) {
937 const PGN::Move *pm = boost::get<PGN::Move>(pgn[i]);
939 int n = current.totalNumMoves()+1;
940 if(var_start) {
941 if(!pm->m_number)
942 current = current.prev();
943 else if(pm->m_number>n+1)
944 std::cout << " --> Error, too far variation!!!" << std::endl;
945 else {
946 if(pm->m_number<n)
947 std::cout << " --> Warning, too near variation..." << std::endl;
948 current = current.prev(n + 1 - pm->m_number);
951 else if(pm->m_number && pm->m_number!=n+1)
952 std::cout << " --> Warning, move number mismatch..." << std::endl;
954 PositionPtr pos = position();
955 MovePtr m = pos->getMove(pm->m_move);
957 if(!m || !pos->testMove(m))
958 break;
960 PositionPtr newPos = pos->clone();
961 newPos->move(m);
963 int at;
964 History *vec = fetchRef(current, &at);
965 Q_ASSERT(vec);
967 if(var_start) {
968 Entry *e = &(*vec)[at];
969 int var_id = e->last_var_id++;
970 e->variations[var_id].push_back(Entry(m, newPos));
971 if(!vcomment.isEmpty()) {
972 e->vcomments[var_id] = vcomment;
973 vcomment = QString();
975 /* this is a hack, but the mainline should NEVER
976 be empty if there is a variation*/
977 if((int)vec->size() - 1 == at)
978 vec->push_back(Entry(m, newPos));
980 current = current.next(var_id);
982 else {
983 if((int)vec->size() - 1 == at)
984 vec->push_back(Entry(m, newPos));
985 else
986 (*vec)[at] = Entry(m, newPos);
988 current = current.next();
991 var_start = false;
995 if(history.size()>1)
996 onAdded(Index(1));
997 Entry* e = fetch(Index(0));
998 for(Variations::const_iterator it = e->variations.begin();
999 it != e->variations.end(); ++it)
1000 onAdded(Index(0).next(it->first));
1001 for(VComments::const_iterator it = e->vcomments.begin();
1002 it != e->vcomments.end(); ++it)
1003 onSetVComment(Index(0), it->first, it->second);
1005 current = Index(0);
1006 onCurrentIndexChanged();
1008 #endif //NO_PGN