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.
14 #pragma warning( push )
15 #pragma warning( disable : 4100 )
16 #include <boost/variant.hpp>
17 #pragma warning( pop )
19 #include <boost/variant.hpp>
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>
29 #include "pgnparser.h"
34 using namespace GamePrivate
;
37 Game::Game(Variant
* variant
)
40 m_validator
= requestComponent
<IValidator
>(variant
, "validator");
41 m_serializer
= requestComponent
<IMoveSerializer
>(
42 variant
, "moveserializer/compact");
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
) {
77 History
*vec
= fetchRef(ix
, &at
);
78 return vec
? &(*vec
)[at
] : NULL
;
81 const Entry
* Game::fetch(const Index
& ix
) const {
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 )
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 )
102 retv
= &it
->second
[ix
.nested
[i
].num_moves
];
103 if(idx
) *idx
= ix
.nested
[i
].num_moves
;
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())
119 if (!m_validator
->legal(e1
->position
.get(), e2
->move
))
120 ERROR("invalid move added to game history!");
124 void Game::testMove() {
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
);
137 onAvailableUndo(true);
139 onAvailableRedo(false);
143 Index
Game::index() const {
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
);
162 ERROR("Index out of range!");
168 StatePtr
Game::position() const {
169 return position(current
);
172 StatePtr
Game::position(const Index
& index
) const {
173 Entry
*e
= (Entry
*)fetch(index
);
175 ERROR("Index " << index
<< " out of range!");
181 QString
Game::comment() const {
182 return comment(current
);
185 QString
Game::comment(const Index
& index
) const {
186 const Entry
*e
= fetch(index
);
188 ERROR("Index out of range!");
194 void Game::reset(StatePtr pos
) {
198 undo_history
.clear();
200 history
.push_back( Entry(Move(), pos
) );
202 onCurrentIndexChanged();
207 ERROR("Cannot undo at the beginning of the undo history!");
211 bool last_undo
= undo_pos
== 1;
212 bool now_redo
= undo_pos
== (int)undo_history
.size();
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());
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
);
232 std::vector
<Entry
>* vec
= fetchRef(a
->index
, &at
);
234 Q_ASSERT((int)vec
->size() == at
+1);
239 if(current
== a
->index
) {
240 current
= current
.prev();
241 onCurrentIndexChanged();
246 else if(boost::get
<UndoPromote
>(op
)) {
247 UndoPromote
*p
= boost::get
<UndoPromote
>(op
);
250 std::vector
<Entry
>* vec
= fetchRef(p
->index
, &at
);
252 Q_ASSERT((*vec
)[at
].variations
.count(p
->variation
)==1);
254 History vold
= (*vec
)[at
].variations
[p
->variation
];
256 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
257 vnew
.push_back((*vec
)[i
]);
258 while((int)vec
->size()>at
+1)
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
);
272 std::vector
<Entry
>* vec
= fetchRef(t
->index
, &at
);
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
);
316 if(sc
->variation
== -1) {
317 e
->comment
= sc
->old_comment
;
318 onSetComment(sc
->index
, sc
->old_comment
);
321 if(sc
->old_comment
.isEmpty())
322 e
->vcomments
.erase(sc
->variation
);
324 e
->vcomments
[sc
->variation
] = sc
->old_comment
;
325 onSetVComment(sc
->index
, sc
->variation
, sc
->old_comment
);
330 onAvailableUndo(false);
332 onAvailableRedo(true);
336 if(undo_pos
>= (int)undo_history
.size()) {
337 ERROR("Cannot redo at the end of the undo history!");
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
]);
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());
354 int v
= a
->index
.nested
[a
->index
.nested
.size()-1].variation
;
355 Q_ASSERT(e
->variations
.count(v
) == 0);
358 h
.push_back(a
->entry
);
359 e
->variations
[v
] = h
;
363 std::vector
<Entry
>* vec
= fetchRef(a
->index
.prev(), &at
);
365 Q_ASSERT((int)vec
->size() == at
+1);
367 vec
->push_back(a
->entry
);
372 else if(boost::get
<UndoPromote
>(op
)) {
373 UndoPromote
*p
= boost::get
<UndoPromote
>(op
);
376 std::vector
<Entry
>* vec
= fetchRef(p
->index
, &at
);
379 Q_ASSERT((*vec
)[at
].variations
.count(p
->variation
)==1);
380 History vold
= (*vec
)[at
].variations
[p
->variation
];
382 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
383 vnew
.push_back((*vec
)[i
]);
384 while((int)vec
->size()>at
+1)
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
);
398 std::vector
<Entry
>* vec
= fetchRef(t
->index
, &at
);
400 Q_ASSERT((int)vec
->size() == at
+1+(int)t
->history
.size());
402 while((int)vec
->size() > at
+1)
404 (*vec
)[at
].variations
.clear();
405 (*vec
)[at
].vcomments
.clear();
407 if(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
);
439 if(sc
->variation
== -1) {
440 e
->comment
= sc
->new_comment
;
441 onSetComment(sc
->index
, sc
->new_comment
);
444 if(sc
->new_comment
.isEmpty())
445 e
->vcomments
.erase(sc
->variation
);
447 e
->vcomments
[sc
->variation
] = sc
->new_comment
;
448 onSetVComment(sc
->index
, sc
->variation
, sc
->new_comment
);
453 onAvailableUndo(true);
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
);
465 ERROR("Invalid index!");
471 saveUndo(UndoSetComment(ix
, -1, e
->comment
, c
));
476 void Game::setVComment(const Index
& ix
, int v
, const QString
& c
) {
477 Entry
* e
= fetch(ix
);
479 ERROR("Invalid index!");
482 QString oc
= e
->vcomments
.count(v
) ? e
->vcomments
[v
] : QString();
486 saveUndo(UndoSetComment(ix
, v
, oc
, c
));
488 e
->vcomments
.erase(v
);
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!");
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
) {
512 std::vector
<Entry
>* vec
= fetchRef(ix
, &at
);
514 Q_ASSERT((*vec
)[at
].variations
.count(v
)==1);
516 History vold
= (*vec
)[at
].variations
[v
];
518 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
519 vnew
.push_back((*vec
)[i
]);
520 while((int)vec
->size()>at
+1)
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!");
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
)) {
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
);
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())) {
579 onCurrentIndexChanged();
583 void Game::truncate() {
587 void Game::truncate(const Index
& ix
) {
589 History
* vec
= fetchRef(ix
, &at
);
591 ERROR("Truncating at an unexisting index!");
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)
602 undo
.variations
= e
->variations
;
603 undo
.vcomments
= e
->vcomments
;
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
));
615 onCurrentIndexChanged();
619 void Game::add(const Move
& m
, const StatePtr
& pos
) {
622 Index old_c
= current
;
624 std::vector
<Entry
>* vec
= fetchRef(current
, &at
);
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();
633 saveUndo(UndoAdd(current
, Entry(m
, pos
)));
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 */
644 Entry
*e
= fetch(current
);
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
);
657 int var_id
= e
->last_var_id
++;
658 e
->variations
[var_id
].push_back(Entry(m
, pos
));
659 current
= current
.next(var_id
);
661 saveUndo(UndoAdd(current
, Entry(m
, pos
)));
663 onCurrentIndexChanged(old_c
);
667 bool Game::insert(const Move
& m
, const StatePtr
& pos
, const Index
& at
) {
668 Entry
*e
= fetch(at
);
671 if(at
.nested
.size() == 0) {
672 if(undo_history
.size()) {
674 undo_history
.clear();
676 int hs
= history
.size();
677 history
.resize(at
.num_moves
+ 1);
678 history
[at
.num_moves
] = Entry(m
, pos
);
684 ERROR("Index out if range!");
689 if(undo_history
.size()) {
691 undo_history
.clear();
693 bool res
= e
->position
&& e
->position
->equals(pos
.get());
698 for (Variations::const_iterator it
= e
->variations
.begin();
699 it
!= e
->variations
.end(); ++it
)
700 testMove(at
.next(it
->first
));
705 bool Game::lastPosition() const {
706 return !fetch(current
.next());
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
717 onCurrentIndexChanged(old_c
);
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
731 onCurrentIndexChanged(old_c
);
736 void Game::gotoFirst() {
737 Index old_c
= current
;
739 onCurrentIndexChanged(old_c
);
742 void Game::gotoLast() {
744 std::vector
<Entry
>* vec
= fetchRef(current
, &at
);
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
) {
757 Index old_c
= current
;
759 onCurrentIndexChanged(old_c
);
765 QString
Game::variationPgn(const History
& vec
, const Entry
& e
,
766 int start
, const Index
& _ix
) const {
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())
779 mv
= "[[" + mv
+ "]]";
782 int n
= ix
.totalNumMoves()+1;
783 if(i
==start
|| n
%2==0)
784 mv
= QString::number(n
/2)+(n
%2==1 ? ". ... " : ". ") + mv
;
790 if(!vec
[i
].comment
.isEmpty())
791 res
+= " {" + vec
[i
].comment
+ "}";
794 for(Variations::const_iterator it
= vec
[i
-1].variations
.begin();
795 it
!= vec
[i
-1].variations
.end(); ++it
) {
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
)) + ")";
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");
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
);
825 std::map
<QString
, QString
>::const_iterator fen
= pgn
.m_tags
.find("FEN");
828 if(var
== pgn
.m_tags
.end()) {
829 IStateFactory
* fact
= requestComponent
<IStateFactory
>(vi
, "state_factory");
830 pos
= StatePtr(fact
->createState());
834 else if( !(pos
= vi
->createPositionFromFEN(fen
->second
))) {
835 ERROR("Wrong fen " << fen
->second
);
840 //TODO: what about options? FEN rules?
845 void Game::load(StatePtr pos
, const PGN
& pgn
) {
847 undo_history
.clear();
851 Entry
* fe
= &history
[0];
852 int old_history_size
= history
.size();
853 std::vector
<int> v_ids
;
855 while(history
.size()>1)
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)
868 history
[0].position
= pos
;
871 history
.push_back( Entry(Move(), pos
) );
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
])) {
880 vcomment
+= *boost::get
<QString
>(pgn
[i
]);
882 Entry
*e
= fetch(current
);
885 e
->comment
+= *boost::get
<QString
>(pgn
[i
]);
888 else if(boost::get
<PGN::BeginVariation
>(pgn
[i
])) {
889 var_stack
.push_back(current
);
892 else if(boost::get
<PGN::EndVariation
>(pgn
[i
])) {
893 if(var_stack
.size() == 0) {
894 ERROR("Unexpected end variation!");
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;
906 current
= current
.prev();
907 else if(pm
->m_number
>n
+1)
908 ERROR("Too far variation!");
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
))
924 StatePtr
newPos(pos
->clone());
928 History
*vec
= fetchRef(current
, &at
);
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
);
947 if((int)vec
->size() - 1 == at
)
948 vec
->push_back(Entry(m
, newPos
));
950 (*vec
)[at
] = Entry(m
, newPos
);
952 current
= current
.next();
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
);
970 onCurrentIndexChanged();