Initial porting to the new component API.
[tagua/yd.git] / src / game.cpp
bloba55e09f01a34013473e1e9fc37dd453636d33a1e
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@gmail.com>
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
22 #include <core/moveserializer.h>
23 #include <core/state.h>
24 #include <core/statefactory.h>
25 #include <core/validator.h>
26 #include <core/variant.h>
28 #include "variants.h"
29 #include "pgnparser.h"
30 #include "game.h"
31 #include "game_p.h"
34 using namespace GamePrivate;
37 Game::Game(Variant* variant)
38 : current(-1)
39 , undo_pos(0) {
40 m_validator = requestComponent<IValidator>(variant, "validator");
41 m_serializer = requestComponent<IMoveSerializer>(
42 variant, "moveserializer/compact");
45 Game::~Game() {
48 void Game::onAdded(const Index&) {
51 void Game::onRemoved(const Index&) {
54 void Game::onEntryChanged(const Index&, int) {
57 void Game::onPromoteVariation(const Index&, int) {
60 void Game::onSetComment(const Index&, const QString&) {
63 void Game::onSetVComment(const Index&, int, const QString&) {
66 void Game::onCurrentIndexChanged(const Index&) {
69 void Game::onAvailableUndo(bool) {
72 void Game::onAvailableRedo(bool) {
75 Entry* Game::fetch(const Index& ix) {
76 int at;
77 History *vec = fetchRef(ix, &at);
78 return vec ? &(*vec)[at] : NULL;
81 const Entry* Game::fetch(const Index& ix) const {
82 int at;
83 const History *vec = fetchRef(ix, &at);
84 return vec ? &(*vec)[at] : NULL;
87 History* Game::fetchRef(const Index& ix, int* idx) {
88 if(ix.num_moves >= (int)history.size() || ix.num_moves < 0 )
89 return NULL;
91 History* aretv = &history;
92 Entry* retv = &history[ix.num_moves];
93 if(idx) *idx = ix.num_moves;
95 for(int i=0; i<(int)ix.nested.size();i++) {
96 Variations::iterator it = retv->variations.find(ix.nested[i].variation);
97 if(it == retv->variations.end() || ix.nested[i].num_moves >= (int)it->second.size()
98 || ix.nested[i].num_moves < 0 )
99 return NULL;
101 aretv = &it->second;
102 retv = &it->second[ix.nested[i].num_moves];
103 if(idx) *idx = ix.nested[i].num_moves;
105 return aretv;
108 const History* Game::fetchRef(const Index& ix, int* idx) const {
109 return const_cast<const History*>(const_cast<Game*>(this)->fetchRef(ix, idx));
112 void Game::testMove(const Index& ix) {
113 if (ix != Index(0)) {
114 Entry *e1 = fetch(ix.prev());
115 Entry *e2 = fetch(ix);
116 if (!e1 || !e2 || !e1->position || e2->move == Move())
117 return;
119 if (!m_validator->legal(e1->position.get(), e2->move))
120 ERROR("invalid move added to game history!");
124 void Game::testMove() {
125 testMove(current);
128 void Game::saveUndo(const UndoOp& op) {
129 bool redo = undo_pos < (int)undo_history.size();
131 while(undo_pos < (int)undo_history.size())
132 undo_history.pop_back();
133 undo_history.push_back(op);
134 undo_pos++;
136 if(undo_pos == 1)
137 onAvailableUndo(true);
138 if(redo)
139 onAvailableRedo(false);
143 Index Game::index() const {
144 return current;
147 Index Game::lastMainlineIndex() const {
148 return Index(history.size()-1);
151 bool Game::containsIndex(const Index& index) const {
152 return !!fetch(index);
155 Move Game::move() const {
156 return move(current);
159 Move Game::move(const Index& index) const {
160 Entry *e = (Entry*)fetch(index);
161 if(!e) {
162 ERROR("Index out of range!");
163 return Move();
165 return e->move;
168 StatePtr Game::position() const {
169 return position(current);
172 StatePtr Game::position(const Index& index) const {
173 Entry *e = (Entry*)fetch(index);
174 if(!e) {
175 ERROR("Index " << index << " out of range!");
176 return StatePtr();
178 return e->position;
181 QString Game::comment() const {
182 return comment(current);
185 QString Game::comment(const Index& index) const {
186 const Entry *e = fetch(index);
187 if(!e) {
188 ERROR("Index out of range!");
189 return QString();
191 return e->comment;
194 void Game::reset(StatePtr pos) {
195 Q_ASSERT(pos);
197 undo_pos = 0;
198 undo_history.clear();
199 history.clear();
200 history.push_back( Entry(Move(), pos) );
201 current = Index(0);
202 onCurrentIndexChanged();
205 void Game::undo() {
206 if(undo_pos <= 0) {
207 ERROR("Cannot undo at the beginning of the undo history!");
208 return;
211 bool last_undo = undo_pos == 1;
212 bool now_redo = undo_pos == (int)undo_history.size();
214 undo_pos--;
215 UndoOp* op = &(undo_history[undo_pos]);
217 if(boost::get<UndoAdd>(op)) {
218 UndoAdd *a = boost::get<UndoAdd>(op);
220 if(a->index.atVariationStart() ) {
221 Entry* e = fetch(a->index.prev());
222 Q_ASSERT(e);
224 int v = a->index.nested[a->index.nested.size()-1].variation;
225 Q_ASSERT(e->variations.count(v) == 1);
226 Q_ASSERT(e->variations[v].size() == 1);
228 e->variations.erase(v);
230 else {
231 int at;
232 std::vector<Entry>* vec = fetchRef(a->index, &at);
233 Q_ASSERT(vec);
234 Q_ASSERT((int)vec->size() == at+1);
236 vec->pop_back();
239 if(current == a->index) {
240 current = current.prev();
241 onCurrentIndexChanged();
244 onRemoved(a->index);
246 else if(boost::get<UndoPromote>(op)) {
247 UndoPromote *p = boost::get<UndoPromote>(op);
249 int at;
250 std::vector<Entry>* vec = fetchRef(p->index, &at);
251 Q_ASSERT(vec);
252 Q_ASSERT((*vec)[at].variations.count(p->variation)==1);
254 History vold = (*vec)[at].variations[p->variation];
255 History vnew;
256 for(int i=at+1; i<(int)vec->size(); i++)
257 vnew.push_back((*vec)[i]);
258 while((int)vec->size()>at+1)
259 vec->pop_back();
260 for(int i=0; i<(int)vold.size(); i++)
261 vec->push_back(vold[i]);
262 (*vec)[at].variations[p->variation] = vnew;
264 current = current.flipVariation(p->index, p->variation);
265 onPromoteVariation(p->index, p->variation);
266 //onCurrentIndexChanged();
268 else if(boost::get<UndoTruncate>(op)) {
269 UndoTruncate *t = boost::get<UndoTruncate>(op);
271 int at;
272 std::vector<Entry>* vec = fetchRef(t->index, &at);
273 Q_ASSERT(vec);
274 Q_ASSERT((int)vec->size() == at+1);
275 Q_ASSERT((*vec)[at].variations.empty());
277 for(int i=0;i<(int)t->history.size();i++)
278 vec->push_back(t->history[i]);
279 (*vec)[at].variations = t->variations;
280 (*vec)[at].vcomments = t->vcomments;
282 if(t->history.size())
283 onAdded(t->index.next());
284 for(Variations::iterator it = t->variations.begin(); it != t->variations.end(); ++it)
285 onAdded(t->index.next(it->first));
286 for(VComments::iterator it = t->vcomments.begin(); it != t->vcomments.end(); ++it)
287 onSetVComment(t->index, it->first, it->second);
289 else if(boost::get<UndoRemove>(op)) {
290 UndoRemove *r = boost::get<UndoRemove>(op);
292 Entry *e = fetch(r->index);
293 e->variations[r->variation] = r->history;
294 onAdded(r->index.next(r->variation));
295 if(!r->vcomment.isEmpty()) {
296 e->vcomments[r->variation] = r->vcomment;
297 onSetVComment(r->index, r->variation, r->vcomment);
300 else if(boost::get<UndoClear>(op)) {
301 UndoClear *c = boost::get<UndoClear>(op);
303 Entry *e = fetch(c->index);
304 e->variations = c->variations;
305 e->vcomments = c->vcomments;
306 for(Variations::iterator it = c->variations.begin(); it != c->variations.end(); ++it)
307 onAdded(c->index.next(it->first));
308 for(VComments::iterator it = c->vcomments.begin(); it != c->vcomments.end(); ++it)
309 onSetVComment(c->index, it->first, it->second);
311 else if(boost::get<UndoSetComment>(op)) {
312 UndoSetComment *sc = boost::get<UndoSetComment>(op);
313 Entry *e = fetch(sc->index);
314 Q_ASSERT(e);
316 if(sc->variation == -1) {
317 e->comment = sc->old_comment;
318 onSetComment(sc->index, sc->old_comment);
320 else {
321 if(sc->old_comment.isEmpty())
322 e->vcomments.erase(sc->variation);
323 else
324 e->vcomments[sc->variation] = sc->old_comment;
325 onSetVComment(sc->index, sc->variation, sc->old_comment);
329 if(last_undo)
330 onAvailableUndo(false);
331 if(now_redo)
332 onAvailableRedo(true);
335 void Game::redo() {
336 if(undo_pos >= (int)undo_history.size()) {
337 ERROR("Cannot redo at the end of the undo history!");
338 return;
341 bool now_undo = undo_pos == 0;
342 bool last_redo = undo_pos == (int)undo_history.size()-1;
344 UndoOp* op = &(undo_history[undo_pos]);
345 undo_pos++;
347 if(boost::get<UndoAdd>(op)) {
348 UndoAdd *a = boost::get<UndoAdd>(op);
350 if(a->index.atVariationStart() ) {
351 Entry* e = fetch(a->index.prev());
352 Q_ASSERT(e);
354 int v = a->index.nested[a->index.nested.size()-1].variation;
355 Q_ASSERT(e->variations.count(v) == 0);
357 History h;
358 h.push_back(a->entry);
359 e->variations[v] = h;
361 else {
362 int at;
363 std::vector<Entry>* vec = fetchRef(a->index.prev(), &at);
364 Q_ASSERT(vec);
365 Q_ASSERT((int)vec->size() == at+1);
367 vec->push_back(a->entry);
370 onAdded(a->index);
372 else if(boost::get<UndoPromote>(op)) {
373 UndoPromote *p = boost::get<UndoPromote>(op);
375 int at;
376 std::vector<Entry>* vec = fetchRef(p->index, &at);
378 Q_ASSERT(vec);
379 Q_ASSERT((*vec)[at].variations.count(p->variation)==1);
380 History vold = (*vec)[at].variations[p->variation];
381 History vnew;
382 for(int i=at+1; i<(int)vec->size(); i++)
383 vnew.push_back((*vec)[i]);
384 while((int)vec->size()>at+1)
385 vec->pop_back();
386 for(int i=0; i<(int)vold.size(); i++)
387 vec->push_back(vold[i]);
388 (*vec)[at].variations[p->variation] = vnew;
390 current = current.flipVariation(p->index, p->variation);
391 onPromoteVariation(p->index, p->variation);
392 //onCurrentIndexChanged();
394 else if(boost::get<UndoTruncate>(op)) {
395 UndoTruncate *t = boost::get<UndoTruncate>(op);
397 int at;
398 std::vector<Entry>* vec = fetchRef(t->index, &at);
399 Q_ASSERT(vec);
400 Q_ASSERT((int)vec->size() == at+1+(int)t->history.size());
402 while((int)vec->size() > at+1)
403 vec->pop_back();
404 (*vec)[at].variations.clear();
405 (*vec)[at].vcomments.clear();
407 if(current > t->index) {
408 current = t->index;
409 onCurrentIndexChanged();
412 if(t->history.size())
413 onRemoved(t->index.next());
414 for(Variations::iterator it = t->variations.begin(); it != t->variations.end(); ++it)
415 onRemoved(t->index.next(it->first));
417 else if(boost::get<UndoRemove>(op)) {
418 UndoRemove *r = boost::get<UndoRemove>(op);
420 Entry *e = fetch(r->index);
421 e->variations.erase(r->variation);
422 e->vcomments.erase(r->variation);
423 onRemoved(r->index.next(r->variation));
425 else if(boost::get<UndoClear>(op)) {
426 UndoClear *c = boost::get<UndoClear>(op);
428 Entry *e = fetch(c->index);
429 e->variations.clear();
430 e->vcomments.clear();
431 for(Variations::iterator it = c->variations.begin(); it != c->variations.end(); ++it)
432 onRemoved(c->index.next(it->first));
434 else if(boost::get<UndoSetComment>(op)) {
435 UndoSetComment *sc = boost::get<UndoSetComment>(op);
436 Entry *e = fetch(sc->index);
437 Q_ASSERT(e);
439 if(sc->variation == -1) {
440 e->comment = sc->new_comment;
441 onSetComment(sc->index, sc->new_comment);
443 else {
444 if(sc->new_comment.isEmpty())
445 e->vcomments.erase(sc->variation);
446 else
447 e->vcomments[sc->variation] = sc->new_comment;
448 onSetVComment(sc->index, sc->variation, sc->new_comment);
452 if(now_undo)
453 onAvailableUndo(true);
454 if(last_redo)
455 onAvailableRedo(false);
458 void Game::setComment(const QString& c) {
459 setComment(current, c);
462 void Game::setComment(const Index& ix, const QString& c) {
463 Entry* e = fetch(ix);
464 if(!e) {
465 ERROR("Invalid index!");
466 return;
468 if(e->comment == c)
469 return;
471 saveUndo(UndoSetComment(ix, -1, e->comment, c));
472 e->comment = c;
473 onSetComment(ix, c);
476 void Game::setVComment(const Index& ix, int v, const QString& c) {
477 Entry* e = fetch(ix);
478 if(!e) {
479 ERROR("Invalid index!");
480 return;
482 QString oc = e->vcomments.count(v) ? e->vcomments[v] : QString();
483 if(oc == c)
484 return;
486 saveUndo(UndoSetComment(ix, v, oc, c));
487 if(c.isEmpty())
488 e->vcomments.erase(v);
489 else
490 e->vcomments[v] = c;
491 onSetVComment(ix, v, c);
494 void Game::promoteVariation() {
495 promoteVariation(current);
498 void Game::promoteVariation(const Index& _ix) {
499 if(_ix.nested.size()==0) {
500 ERROR("Cannot promote main line!");
501 return;
503 Index ix = _ix;
504 int v = ix.nested[ix.nested.size()-1].variation;
505 ix.nested.pop_back();
507 promoteVariation(ix, v);
510 void Game::promoteVariation(const Index& ix, int v) {
511 int at;
512 std::vector<Entry>* vec = fetchRef(ix, &at);
513 Q_ASSERT(vec);
514 Q_ASSERT((*vec)[at].variations.count(v)==1);
516 History vold = (*vec)[at].variations[v];
517 History vnew;
518 for(int i=at+1; i<(int)vec->size(); i++)
519 vnew.push_back((*vec)[i]);
520 while((int)vec->size()>at+1)
521 vec->pop_back();
522 for(int i=0; i<(int)vold.size(); i++)
523 vec->push_back(vold[i]);
524 (*vec)[at].variations[v] = vnew;
526 saveUndo(UndoPromote(ix, v));
527 current = current.flipVariation(ix, v);
528 onPromoteVariation(ix, v);
529 //don't call onCurrentIndexChanged(), as the position did not change actually
532 void Game::removeVariation(int v) {
533 removeVariation(current, v);
536 void Game::removeVariation(const Index& _ix) {
537 if(_ix.nested.size()==0) {
538 ERROR("Cannot remove main line!");
539 return;
541 Index ix = _ix;
542 int v = ix.nested[ix.nested.size()-1].variation;
543 ix.nested.pop_back();
545 removeVariation(ix, v);
548 void Game::removeVariation(const Index& ix, int v) {
549 Entry* e = fetch(ix);
551 saveUndo(UndoRemove(ix, v, e->variations[v],
552 e->vcomments.count(v) ? e->vcomments[v] : QString() ));
553 e->variations.erase(v);
554 e->vcomments.erase(v);
556 onRemoved(ix.next(v));
557 if(current >= ix.next(v)) {
558 current = ix;
559 onCurrentIndexChanged();
563 void Game::clearVariations() {
564 clearVariations(current);
567 void Game::clearVariations(const Index& ix) {
568 Entry* e = fetch(ix);
570 UndoClear uc(ix, e->variations, e->vcomments);
571 saveUndo(uc);
572 e->variations.clear();
573 e->vcomments.clear();
575 for(Variations::iterator it = uc.variations.begin(); it != uc.variations.end(); ++it)
576 onRemoved(ix.next(it->first));
577 if(current > ix && !(current >= ix.next())) {
578 current = ix;
579 onCurrentIndexChanged();
583 void Game::truncate() {
584 truncate(current);
587 void Game::truncate(const Index& ix) {
588 int at;
589 History* vec = fetchRef(ix, &at);
590 if(!vec) {
591 ERROR("Truncating at an unexisting index!");
592 return;
595 Entry *e = &(*vec)[at];
596 UndoTruncate undo(ix);
597 for(int i=at+1; i<(int)vec->size();i++)
598 undo.history.push_back((*vec)[i]);
599 while((int)vec->size()>at+1)
600 vec->pop_back();
602 undo.variations = e->variations;
603 undo.vcomments = e->vcomments;
604 saveUndo(undo);
605 e->variations.clear();
606 e->vcomments.clear();
608 if(undo.history.size())
609 onRemoved(undo.index.next());
610 for(Variations::iterator it = undo.variations.begin(); it != undo.variations.end(); ++it)
611 onRemoved(undo.index.next(it->first));
613 if(current > ix) {
614 current = ix;
615 onCurrentIndexChanged();
619 void Game::add(const Move& m, const StatePtr& pos) {
620 Q_ASSERT(pos);
622 Index old_c = current;
623 int at;
624 std::vector<Entry>* vec = fetchRef(current, &at);
625 Q_ASSERT(vec);
627 /* add the move on the mainline */
628 if((int)vec->size() <= at+1 ) {
629 Q_ASSERT((int)vec->size() == at+1);
630 vec->push_back(Entry(m, pos));
631 current = current.next();
632 testMove();
633 saveUndo(UndoAdd(current, Entry(m, pos)));
634 onAdded(current);
635 onCurrentIndexChanged(old_c);
637 /* we are playing the move that is already next in the mainline */
638 else if( (*vec)[at+1].position && (*vec)[at+1].position->equals(pos.get())) {
639 current = current.next();
640 onCurrentIndexChanged(old_c);
641 /* no need to test the move */
643 else {
644 Entry *e = fetch(current);
645 Q_ASSERT(e);
647 /* check if a variations with this move already exists. */
648 for(Variations::iterator it = e->variations.begin(); it != e->variations.end(); ++it)
649 if(it->second.size() > 0 && it->second[0].position
650 && it->second[0].position->equals(pos.get()) ) {
651 current = current.next(it->first);
652 onCurrentIndexChanged(old_c);
654 return;
657 int var_id = e->last_var_id++;
658 e->variations[var_id].push_back(Entry(m, pos));
659 current = current.next(var_id);
660 testMove();
661 saveUndo(UndoAdd(current, Entry(m, pos)));
662 onAdded(current);
663 onCurrentIndexChanged(old_c);
667 bool Game::insert(const Move& m, const StatePtr& pos, const Index& at) {
668 Entry *e = fetch(at);
670 if(!e) {
671 if(at.nested.size() == 0) {
672 if(undo_history.size()) {
673 undo_pos = 0;
674 undo_history.clear();
676 int hs = history.size();
677 history.resize(at.num_moves + 1);
678 history[at.num_moves] = Entry(m, pos);
679 testMove(at);
680 onAdded(Index(hs));
681 return true;
683 else {
684 ERROR("Index out if range!");
685 return false;
689 if(undo_history.size()) {
690 undo_pos = 0;
691 undo_history.clear();
693 bool res = e->position && e->position->equals(pos.get());
694 e->move = m;
695 e->position = pos;
696 testMove(at);
697 testMove(at.next());
698 for (Variations::const_iterator it = e->variations.begin();
699 it != e->variations.end(); ++it)
700 testMove(at.next(it->first));
701 onEntryChanged(at);
702 return res;
705 bool Game::lastPosition() const {
706 return !fetch(current.next());
709 bool Game::back() {
710 if (current <= 0) return false; // first entry or uninitialized
711 Index old_c = current;
712 Index new_c = current.prev();
714 Entry *e = fetch(new_c);
715 if(!e || e->position == 0) return false; // gap immediately before current
716 current = new_c;
717 onCurrentIndexChanged(old_c);
719 return true;
722 bool Game::forward() {
723 Index old_c = current;
724 Index new_c = current.next();
726 Entry *e = fetch(new_c);
727 if(!e || e->position == 0) {
728 return false; // gap immediately before current
730 current = new_c;
731 onCurrentIndexChanged(old_c);
733 return true;
736 void Game::gotoFirst() {
737 Index old_c = current;
738 current = Index(0);
739 onCurrentIndexChanged(old_c);
742 void Game::gotoLast() {
743 int at;
744 std::vector<Entry>* vec = fetchRef(current, &at);
745 Q_ASSERT(vec);
746 Q_ASSERT((int)vec->size() > at);
748 if((int)vec->size() > at+1) {
749 Index old_c = current;
750 current = current.next(-1, vec->size()-1-at);
751 onCurrentIndexChanged(old_c);
755 bool Game::goTo(const Index& index) {
756 if (fetch(index)) {
757 Index old_c = current;
758 current = index;
759 onCurrentIndexChanged(old_c);
760 return true;
762 return false;
765 QString Game::variationPgn(const History& vec, const Entry& e,
766 int start, const Index& _ix) const {
767 Index ix = _ix;
768 QString res;
770 for (int i = start; i < static_cast<int>(vec.size()); i++) {
771 const Entry& preve = (i > start) ? vec[i-1] : e;
774 QString mv = (vec[i].move != Move() && preve.position)
775 ? m_serializer->serialize(vec[i].move, preve.position.get())
776 : "???";
777 #if 0
778 if (ix == current)
779 mv = "[[" + mv + "]]";
780 #endif
782 int n = ix.totalNumMoves()+1;
783 if(i==start || n%2==0)
784 mv = QString::number(n/2)+(n%2==1 ? ". ... " : ". ") + mv;
785 if (i > start)
786 mv = " " + mv;
788 res += mv;
790 if(!vec[i].comment.isEmpty())
791 res += " {" + vec[i].comment + "}";
793 if(i > 0) {
794 for(Variations::const_iterator it = vec[i-1].variations.begin();
795 it != vec[i-1].variations.end(); ++it) {
796 res += " (";
797 if(vec[i-1].vcomments.count(it->first))
798 res += "{" + vec[i-1].vcomments.find(it->first)->second + "} ";
799 res += variationPgn(it->second, vec[i - 1], 0,
800 ix.prev().next(it->first)) + ")";
804 ix = ix.next();
806 return res;
809 QString Game::pgn() const {
810 return variationPgn(history, history[0], 1, Index(1));
813 void Game::load(const PGN& pgn) {
814 std::map<QString, QString>::const_iterator var = pgn.m_tags.find("Variant");
815 Variant* vi;
817 if (var == pgn.m_tags.end()) {
818 vi = Variants::instance().get("chess");
820 else if (!(vi = Variants::instance().get(var->second))) {
821 ERROR("No such variant " << var->second);
822 return;
825 std::map<QString, QString>::const_iterator fen = pgn.m_tags.find("FEN");
826 StatePtr pos;
828 if(var == pgn.m_tags.end()) {
829 IStateFactory* fact = requestComponent<IStateFactory>(vi, "state_factory");
830 pos = StatePtr(fact->createState());
831 pos->setup();
833 #if 0 // BROKEN
834 else if( !(pos = vi->createPositionFromFEN(fen->second))) {
835 ERROR("Wrong fen " << fen->second);
836 return;
838 #endif
840 //TODO: what about options? FEN rules?
842 load(pos, pgn);
845 void Game::load(StatePtr pos, const PGN& pgn) {
846 current = Index(0);
847 undo_history.clear();
848 undo_pos = 0;
850 if(history.size()) {
851 Entry* fe = &history[0];
852 int old_history_size = history.size();
853 std::vector<int> v_ids;
855 while(history.size()>1)
856 history.pop_back();
857 for(Variations::const_iterator it = fe->variations.begin();
858 it != fe->variations.end(); ++it)
859 v_ids.push_back(it->first);
860 fe->variations.clear();
861 fe->vcomments.clear();
863 for(int i=0;i<(int)v_ids.size();i++)
864 onRemoved(Index(0).next(v_ids[i]));
865 if(old_history_size>1)
866 onRemoved(Index(1));
867 v_ids.clear();
868 history[0].position = pos;
870 else
871 history.push_back( Entry(Move(), pos) );
873 QString vcomment;
874 std::vector<Index> var_stack;
875 bool var_start = false;
877 for (uint i = 0; i < pgn.m_entries.size(); i++) {
878 if(boost::get<QString>(pgn[i])) {
879 if(var_start)
880 vcomment += *boost::get<QString>(pgn[i]);
881 else {
882 Entry *e = fetch(current);
883 Q_ASSERT(e);
885 e->comment += *boost::get<QString>(pgn[i]);
888 else if(boost::get<PGN::BeginVariation>(pgn[i])) {
889 var_stack.push_back(current);
890 var_start = true;
892 else if(boost::get<PGN::EndVariation>(pgn[i])) {
893 if(var_stack.size() == 0) {
894 ERROR("Unexpected end variation!");
895 break;
897 current = var_stack[var_stack.size()-1];
898 var_stack.pop_back();
900 else if(boost::get<PGN::Move>(pgn[i])) {
901 const PGN::Move *pm = boost::get<PGN::Move>(pgn[i]);
903 int n = current.totalNumMoves()+1;
904 if(var_start) {
905 if(!pm->m_number)
906 current = current.prev();
907 else if(pm->m_number>n+1)
908 ERROR("Too far variation!");
909 else {
910 if(pm->m_number<n)
911 ERROR("Too near variation!");
912 current = current.prev(n + 1 - pm->m_number);
915 else if(pm->m_number && pm->m_number!=n+1)
916 ERROR("Move number mismatch!");
918 StatePtr pos = position();
919 Move m = m_serializer->deserialize(pm->m_move, pos.get());
921 if (m == Move() || !m_validator->legal(pos.get(), m))
922 break;
924 StatePtr newPos(pos->clone());
925 newPos->move(m);
927 int at;
928 History *vec = fetchRef(current, &at);
929 Q_ASSERT(vec);
931 if(var_start) {
932 Entry *e = &(*vec)[at];
933 int var_id = e->last_var_id++;
934 e->variations[var_id].push_back(Entry(m, newPos));
935 if(!vcomment.isEmpty()) {
936 e->vcomments[var_id] = vcomment;
937 vcomment = QString();
939 /* this is a hack, but the mainline should NEVER
940 be empty if there is a variation*/
941 if((int)vec->size() - 1 == at)
942 vec->push_back(Entry(m, newPos));
944 current = current.next(var_id);
946 else {
947 if((int)vec->size() - 1 == at)
948 vec->push_back(Entry(m, newPos));
949 else
950 (*vec)[at] = Entry(m, newPos);
952 current = current.next();
955 var_start = false;
959 if(history.size()>1)
960 onAdded(Index(1));
961 Entry* e = fetch(Index(0));
962 for(Variations::const_iterator it = e->variations.begin();
963 it != e->variations.end(); ++it)
964 onAdded(Index(0).next(it->first));
965 for(VComments::const_iterator it = e->vcomments.begin();
966 it != e->vcomments.end(); ++it)
967 onSetVComment(Index(0), it->first, it->second);
969 current = Index(0);
970 onCurrentIndexChanged();