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>
30 #include "pgnparser.h"
31 #include "components.h"
34 using namespace GamePrivate
;
37 Game::Game(Components
* components
)
40 , m_components(components
) { }
45 void Game::onAdded(const Index
&) {
48 void Game::onRemoved(const Index
&) {
51 void Game::onEntryChanged(const Index
&, int) {
54 void Game::onPromoteVariation(const Index
&, int) {
57 void Game::onSetComment(const Index
&, const QString
&) {
60 void Game::onSetVComment(const Index
&, int, const QString
&) {
63 void Game::onCurrentIndexChanged(const Index
&) {
66 void Game::onAvailableUndo(bool) {
69 void Game::onAvailableRedo(bool) {
72 Entry
* Game::fetch(const Index
& ix
) {
74 History
*vec
= fetchRef(ix
, &at
);
75 return vec
? &(*vec
)[at
] : NULL
;
78 const Entry
* Game::fetch(const Index
& ix
) const {
80 const History
*vec
= fetchRef(ix
, &at
);
81 return vec
? &(*vec
)[at
] : NULL
;
84 History
* Game::fetchRef(const Index
& ix
, int* idx
) {
85 if(ix
.num_moves
>= (int)history
.size() || ix
.num_moves
< 0 )
88 History
* aretv
= &history
;
89 Entry
* retv
= &history
[ix
.num_moves
];
90 if(idx
) *idx
= ix
.num_moves
;
92 for(int i
=0; i
<(int)ix
.nested
.size();i
++) {
93 Variations::iterator it
= retv
->variations
.find(ix
.nested
[i
].variation
);
94 if(it
== retv
->variations
.end() || ix
.nested
[i
].num_moves
>= (int)it
->second
.size()
95 || ix
.nested
[i
].num_moves
< 0 )
99 retv
= &it
->second
[ix
.nested
[i
].num_moves
];
100 if(idx
) *idx
= ix
.nested
[i
].num_moves
;
105 const History
* Game::fetchRef(const Index
& ix
, int* idx
) const {
106 return const_cast<const History
*>(const_cast<Game
*>(this)->fetchRef(ix
, idx
));
109 void Game::testMove(const Index
& ix
) {
110 if (ix
!= Index(0)) {
111 Entry
*e1
= fetch(ix
.prev());
112 Entry
*e2
= fetch(ix
);
113 if (!e1
|| !e2
|| !e1
->position
|| e2
->move
== Move())
116 if (!m_components
->validator()->legal(e1
->position
.get(), e2
->move
))
117 kError() << "invalid move added to game history";
121 void Game::testMove() {
125 void Game::saveUndo(const UndoOp
& op
) {
126 bool redo
= undo_pos
< (int)undo_history
.size();
128 while(undo_pos
< (int)undo_history
.size())
129 undo_history
.pop_back();
130 undo_history
.push_back(op
);
134 onAvailableUndo(true);
136 onAvailableRedo(false);
140 Index
Game::index() const {
144 Index
Game::lastMainlineIndex() const {
145 return Index(history
.size()-1);
148 bool Game::containsIndex(const Index
& index
) const {
149 return !!fetch(index
);
152 Move
Game::move() const {
153 return move(current
);
156 Move
Game::move(const Index
& index
) const {
157 Entry
*e
= (Entry
*)fetch(index
);
159 kError() << "Index out of range";
165 StatePtr
Game::position() const {
166 return position(current
);
169 StatePtr
Game::position(const Index
& index
) const {
170 Entry
*e
= (Entry
*)fetch(index
);
172 kError() << "Index" << index
<< "out of range";
178 QString
Game::comment() const {
179 return comment(current
);
182 QString
Game::comment(const Index
& index
) const {
183 const Entry
*e
= fetch(index
);
185 kError() << "Index out of range";
191 void Game::reset(StatePtr pos
) {
195 undo_history
.clear();
197 history
.push_back( Entry(Move(), pos
) );
199 onCurrentIndexChanged();
204 kError() << "Nothing to undo";
208 bool last_undo
= undo_pos
== 1;
209 bool now_redo
= undo_pos
== (int)undo_history
.size();
212 UndoOp
* op
= &(undo_history
[undo_pos
]);
214 if(boost::get
<UndoAdd
>(op
)) {
215 UndoAdd
*a
= boost::get
<UndoAdd
>(op
);
217 if(a
->index
.atVariationStart() ) {
218 Entry
* e
= fetch(a
->index
.prev());
221 int v
= a
->index
.nested
.back().variation
;
222 Q_ASSERT(e
->variations
.count(v
) == 1);
223 Q_ASSERT(e
->variations
[v
].size() == 1);
225 e
->variations
.erase(v
);
229 std::vector
<Entry
>* vec
= fetchRef(a
->index
, &at
);
231 Q_ASSERT((int)vec
->size() == at
+1);
236 if(current
== a
->index
) {
237 current
= current
.prev();
238 onCurrentIndexChanged();
243 else if(boost::get
<UndoPromote
>(op
)) {
244 UndoPromote
*p
= boost::get
<UndoPromote
>(op
);
247 std::vector
<Entry
>* vec
= fetchRef(p
->index
, &at
);
249 Q_ASSERT((*vec
)[at
].variations
.count(p
->variation
)==1);
251 History vold
= (*vec
)[at
].variations
[p
->variation
];
253 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
254 vnew
.push_back((*vec
)[i
]);
255 while((int)vec
->size()>at
+1)
257 for(int i
=0; i
<(int)vold
.size(); i
++)
258 vec
->push_back(vold
[i
]);
259 (*vec
)[at
].variations
[p
->variation
] = vnew
;
261 current
= current
.flipVariation(p
->index
, p
->variation
);
262 onPromoteVariation(p
->index
, p
->variation
);
263 //onCurrentIndexChanged();
265 else if(boost::get
<UndoTruncate
>(op
)) {
266 UndoTruncate
*t
= boost::get
<UndoTruncate
>(op
);
269 std::vector
<Entry
>* vec
= fetchRef(t
->index
, &at
);
271 Q_ASSERT((int)vec
->size() == at
+1);
272 Q_ASSERT((*vec
)[at
].variations
.empty());
274 for(int i
=0;i
<(int)t
->history
.size();i
++)
275 vec
->push_back(t
->history
[i
]);
276 (*vec
)[at
].variations
= t
->variations
;
277 (*vec
)[at
].vcomments
= t
->vcomments
;
279 if(t
->history
.size())
280 onAdded(t
->index
.next());
281 for(Variations::iterator it
= t
->variations
.begin(); it
!= t
->variations
.end(); ++it
)
282 onAdded(t
->index
.next(it
->first
));
283 for(VComments::iterator it
= t
->vcomments
.begin(); it
!= t
->vcomments
.end(); ++it
)
284 onSetVComment(t
->index
, it
->first
, it
->second
);
286 else if(boost::get
<UndoRemove
>(op
)) {
287 UndoRemove
*r
= boost::get
<UndoRemove
>(op
);
289 Entry
*e
= fetch(r
->index
);
290 e
->variations
[r
->variation
] = r
->history
;
291 onAdded(r
->index
.next(r
->variation
));
292 if(!r
->vcomment
.isEmpty()) {
293 e
->vcomments
[r
->variation
] = r
->vcomment
;
294 onSetVComment(r
->index
, r
->variation
, r
->vcomment
);
297 else if(boost::get
<UndoClear
>(op
)) {
298 UndoClear
*c
= boost::get
<UndoClear
>(op
);
300 Entry
*e
= fetch(c
->index
);
301 e
->variations
= c
->variations
;
302 e
->vcomments
= c
->vcomments
;
303 for(Variations::iterator it
= c
->variations
.begin(); it
!= c
->variations
.end(); ++it
)
304 onAdded(c
->index
.next(it
->first
));
305 for(VComments::iterator it
= c
->vcomments
.begin(); it
!= c
->vcomments
.end(); ++it
)
306 onSetVComment(c
->index
, it
->first
, it
->second
);
308 else if(boost::get
<UndoSetComment
>(op
)) {
309 UndoSetComment
*sc
= boost::get
<UndoSetComment
>(op
);
310 Entry
*e
= fetch(sc
->index
);
313 if(sc
->variation
== -1) {
314 e
->comment
= sc
->old_comment
;
315 onSetComment(sc
->index
, sc
->old_comment
);
318 if(sc
->old_comment
.isEmpty())
319 e
->vcomments
.erase(sc
->variation
);
321 e
->vcomments
[sc
->variation
] = sc
->old_comment
;
322 onSetVComment(sc
->index
, sc
->variation
, sc
->old_comment
);
326 kError() << "Unexpected type in boost::variant";
329 onAvailableUndo(false);
331 onAvailableRedo(true);
335 if(undo_pos
>= (int)undo_history
.size()) {
336 kError() << "Nothing to redo";
340 bool now_undo
= undo_pos
== 0;
341 bool last_redo
= undo_pos
== (int)undo_history
.size()-1;
343 UndoOp
* op
= &(undo_history
[undo_pos
]);
346 if(boost::get
<UndoAdd
>(op
)) {
347 UndoAdd
*a
= boost::get
<UndoAdd
>(op
);
349 if(a
->index
.atVariationStart() ) {
350 Entry
* e
= fetch(a
->index
.prev());
353 int v
= a
->index
.nested
.back().variation
;
354 Q_ASSERT(e
->variations
.count(v
) == 0);
357 h
.push_back(a
->entry
);
358 e
->variations
[v
] = h
;
362 std::vector
<Entry
>* vec
= fetchRef(a
->index
.prev(), &at
);
364 Q_ASSERT((int)vec
->size() == at
+1);
366 vec
->push_back(a
->entry
);
371 onCurrentIndexChanged();
373 else if(boost::get
<UndoPromote
>(op
)) {
374 UndoPromote
*p
= boost::get
<UndoPromote
>(op
);
377 std::vector
<Entry
>* vec
= fetchRef(p
->index
, &at
);
380 Q_ASSERT((*vec
)[at
].variations
.count(p
->variation
)==1);
381 History vold
= (*vec
)[at
].variations
[p
->variation
];
383 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
384 vnew
.push_back((*vec
)[i
]);
385 while((int)vec
->size()>at
+1)
387 for(int i
=0; i
<(int)vold
.size(); i
++)
388 vec
->push_back(vold
[i
]);
389 (*vec
)[at
].variations
[p
->variation
] = vnew
;
391 current
= current
.flipVariation(p
->index
, p
->variation
);
392 onPromoteVariation(p
->index
, p
->variation
);
393 //onCurrentIndexChanged();
395 else if(boost::get
<UndoTruncate
>(op
)) {
396 UndoTruncate
*t
= boost::get
<UndoTruncate
>(op
);
399 std::vector
<Entry
>* vec
= fetchRef(t
->index
, &at
);
401 Q_ASSERT((int)vec
->size() == at
+1+(int)t
->history
.size());
403 while((int)vec
->size() > at
+1)
405 (*vec
)[at
].variations
.clear();
406 (*vec
)[at
].vcomments
.clear();
408 if(current
> t
->index
) {
410 onCurrentIndexChanged();
413 if(t
->history
.size())
414 onRemoved(t
->index
.next());
415 for(Variations::iterator it
= t
->variations
.begin(); it
!= t
->variations
.end(); ++it
)
416 onRemoved(t
->index
.next(it
->first
));
418 else if(boost::get
<UndoRemove
>(op
)) {
419 UndoRemove
*r
= boost::get
<UndoRemove
>(op
);
421 Entry
*e
= fetch(r
->index
);
422 e
->variations
.erase(r
->variation
);
423 e
->vcomments
.erase(r
->variation
);
424 onRemoved(r
->index
.next(r
->variation
));
426 else if(boost::get
<UndoClear
>(op
)) {
427 UndoClear
*c
= boost::get
<UndoClear
>(op
);
429 Entry
*e
= fetch(c
->index
);
430 e
->variations
.clear();
431 e
->vcomments
.clear();
432 for(Variations::iterator it
= c
->variations
.begin(); it
!= c
->variations
.end(); ++it
)
433 onRemoved(c
->index
.next(it
->first
));
435 else if(boost::get
<UndoSetComment
>(op
)) {
436 UndoSetComment
*sc
= boost::get
<UndoSetComment
>(op
);
437 Entry
*e
= fetch(sc
->index
);
440 if(sc
->variation
== -1) {
441 e
->comment
= sc
->new_comment
;
442 onSetComment(sc
->index
, sc
->new_comment
);
445 if(sc
->new_comment
.isEmpty())
446 e
->vcomments
.erase(sc
->variation
);
448 e
->vcomments
[sc
->variation
] = sc
->new_comment
;
449 onSetVComment(sc
->index
, sc
->variation
, sc
->new_comment
);
453 kError() << "Unexpected type in boost::variant";
456 onAvailableUndo(true);
458 onAvailableRedo(false);
461 void Game::setComment(const QString
& c
) {
462 setComment(current
, c
);
465 void Game::setComment(const Index
& ix
, const QString
& c
) {
466 Entry
* e
= fetch(ix
);
468 kError() << "Invalid index";
474 saveUndo(UndoSetComment(ix
, -1, e
->comment
, c
));
479 void Game::setVComment(const Index
& ix
, int v
, const QString
& c
) {
480 Entry
* e
= fetch(ix
);
482 kError() << "Invalid index";
485 QString oc
= e
->vcomments
.count(v
) ? e
->vcomments
[v
] : QString();
489 saveUndo(UndoSetComment(ix
, v
, oc
, c
));
491 e
->vcomments
.erase(v
);
494 onSetVComment(ix
, v
, c
);
497 void Game::promoteVariation() {
498 promoteVariation(current
);
501 void Game::promoteVariation(const Index
& _ix
) {
502 if(_ix
.nested
.size()==0) {
503 kError() << "Cannot promote main line";
507 int v
= ix
.nested
.back().variation
;
508 ix
.nested
.pop_back();
510 promoteVariation(ix
, v
);
513 void Game::promoteVariation(const Index
& ix
, int v
) {
515 std::vector
<Entry
>* vec
= fetchRef(ix
, &at
);
517 Q_ASSERT((*vec
)[at
].variations
.count(v
)==1);
519 History vold
= (*vec
)[at
].variations
[v
];
521 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
522 vnew
.push_back((*vec
)[i
]);
523 while((int)vec
->size()>at
+1)
525 for(int i
=0; i
<(int)vold
.size(); i
++)
526 vec
->push_back(vold
[i
]);
527 (*vec
)[at
].variations
[v
] = vnew
;
529 saveUndo(UndoPromote(ix
, v
));
530 current
= current
.flipVariation(ix
, v
);
531 onPromoteVariation(ix
, v
);
532 //don't call onCurrentIndexChanged(), as the position did not change actually
535 void Game::removeVariation(int v
) {
536 removeVariation(current
, v
);
539 void Game::removeVariation(const Index
& _ix
) {
540 if(_ix
.nested
.size()==0) {
541 kError() << "Cannot remove main line";
545 int v
= ix
.nested
.back().variation
;
546 ix
.nested
.pop_back();
548 removeVariation(ix
, v
);
551 void Game::removeVariation(const Index
& ix
, int v
) {
552 Entry
* e
= fetch(ix
);
554 saveUndo(UndoRemove(ix
, v
, e
->variations
[v
],
555 e
->vcomments
.count(v
) ? e
->vcomments
[v
] : QString() ));
556 e
->variations
.erase(v
);
557 e
->vcomments
.erase(v
);
559 onRemoved(ix
.next(v
));
560 if(current
>= ix
.next(v
)) {
562 onCurrentIndexChanged();
566 void Game::clearVariations() {
567 clearVariations(current
);
570 void Game::clearVariations(const Index
& ix
) {
571 Entry
* e
= fetch(ix
);
573 UndoClear
uc(ix
, e
->variations
, e
->vcomments
);
575 e
->variations
.clear();
576 e
->vcomments
.clear();
578 for(Variations::iterator it
= uc
.variations
.begin(); it
!= uc
.variations
.end(); ++it
)
579 onRemoved(ix
.next(it
->first
));
580 if(current
> ix
&& !(current
>= ix
.next())) {
582 onCurrentIndexChanged();
586 void Game::truncate() {
590 void Game::truncate(const Index
& ix
) {
592 History
* vec
= fetchRef(ix
, &at
);
594 kError() << "Truncating at an unexisting index";
598 Entry
*e
= &(*vec
)[at
];
599 UndoTruncate
undo(ix
);
600 for(int i
=at
+1; i
<(int)vec
->size();i
++)
601 undo
.history
.push_back((*vec
)[i
]);
602 while((int)vec
->size()>at
+1)
605 undo
.variations
= e
->variations
;
606 undo
.vcomments
= e
->vcomments
;
608 e
->variations
.clear();
609 e
->vcomments
.clear();
611 if(undo
.history
.size())
612 onRemoved(undo
.index
.next());
613 for(Variations::iterator it
= undo
.variations
.begin(); it
!= undo
.variations
.end(); ++it
)
614 onRemoved(undo
.index
.next(it
->first
));
618 onCurrentIndexChanged();
622 void Game::add(const Move
& m
, const StatePtr
& pos
) {
625 Index old_c
= current
;
627 std::vector
<Entry
>* vec
= fetchRef(current
, &at
);
630 /* add the move on the mainline */
631 if((int)vec
->size() <= at
+1 ) {
632 Q_ASSERT((int)vec
->size() == at
+1);
633 vec
->push_back(Entry(m
, pos
));
634 current
= current
.next();
636 saveUndo(UndoAdd(current
, Entry(m
, pos
)));
638 onCurrentIndexChanged(old_c
);
640 /* we are playing the move that is already next in the mainline */
641 else if( (*vec
)[at
+1].position
&& (*vec
)[at
+1].position
->equals(pos
.get())) {
642 current
= current
.next();
643 onCurrentIndexChanged(old_c
);
644 /* no need to test the move */
647 Entry
*e
= fetch(current
);
650 /* check if a variations with this move already exists. */
651 for(Variations::iterator it
= e
->variations
.begin(); it
!= e
->variations
.end(); ++it
)
652 if(it
->second
.size() > 0 && it
->second
[0].position
653 && it
->second
[0].position
->equals(pos
.get()) ) {
654 current
= current
.next(it
->first
);
655 onCurrentIndexChanged(old_c
);
660 int var_id
= e
->last_var_id
++;
661 e
->variations
[var_id
].push_back(Entry(m
, pos
));
662 current
= current
.next(var_id
);
664 saveUndo(UndoAdd(current
, Entry(m
, pos
)));
666 onCurrentIndexChanged(old_c
);
670 bool Game::insert(const Move
& m
, const StatePtr
& pos
, const Index
& at
) {
671 Entry
*e
= fetch(at
);
674 if(at
.nested
.size() == 0) {
675 if(undo_history
.size()) {
677 undo_history
.clear();
679 int hs
= history
.size();
680 history
.resize(at
.num_moves
+ 1);
681 history
[at
.num_moves
] = Entry(m
, pos
);
687 kError() << "Index out if range";
692 if(undo_history
.size()) {
694 undo_history
.clear();
696 bool res
= e
->position
&& e
->position
->equals(pos
.get());
701 for (Variations::const_iterator it
= e
->variations
.begin();
702 it
!= e
->variations
.end(); ++it
)
703 testMove(at
.next(it
->first
));
708 bool Game::lastPosition() const {
709 return !fetch(current
.next());
713 if (current
<= 0) return false; // first entry or uninitialized
714 Index old_c
= current
;
715 Index new_c
= current
.prev();
717 Entry
*e
= fetch(new_c
);
718 if(!e
|| e
->position
== 0) return false; // gap immediately before current
720 onCurrentIndexChanged(old_c
);
725 bool Game::forward() {
726 Index old_c
= current
;
727 Index new_c
= current
.next();
729 Entry
*e
= fetch(new_c
);
730 if(!e
|| e
->position
== 0) {
731 return false; // gap immediately before current
734 onCurrentIndexChanged(old_c
);
739 void Game::gotoFirst() {
740 Index old_c
= current
;
742 onCurrentIndexChanged(old_c
);
745 void Game::gotoLast() {
747 std::vector
<Entry
>* vec
= fetchRef(current
, &at
);
749 Q_ASSERT((int)vec
->size() > at
);
751 if((int)vec
->size() > at
+1) {
752 Index old_c
= current
;
753 current
= current
.next(-1, vec
->size()-1-at
);
754 onCurrentIndexChanged(old_c
);
758 bool Game::goTo(const Index
& index
) {
760 Index old_c
= current
;
762 onCurrentIndexChanged(old_c
);
768 QString
Game::variationPgn(const History
& vec
, const Entry
& e
,
769 int start
, const Index
& _ix
) const {
773 for (int i
= start
; i
< static_cast<int>(vec
.size()); i
++) {
774 const Entry
& preve
= (i
> start
) ? vec
[i
-1] : e
;
777 QString mv
= (vec
[i
].move
!= Move() && preve
.position
)
778 ? m_components
->moveSerializer("compact")->
779 serialize(vec
[i
].move
, preve
.position
.get())
783 mv
= "[[" + mv
+ "]]";
786 int n
= ix
.totalNumMoves()+1;
787 if(i
==start
|| n
%2==0)
788 mv
= QString::number(n
/2)+(n
%2==1 ? ". ... " : ". ") + mv
;
794 if(!vec
[i
].comment
.isEmpty())
795 res
+= " {" + vec
[i
].comment
+ "}";
798 for(Variations::const_iterator it
= vec
[i
-1].variations
.begin();
799 it
!= vec
[i
-1].variations
.end(); ++it
) {
801 if(vec
[i
-1].vcomments
.count(it
->first
))
802 res
+= "{" + vec
[i
-1].vcomments
.find(it
->first
)->second
+ "} ";
803 res
+= variationPgn(it
->second
, vec
[i
- 1], 0,
804 ix
.prev().next(it
->first
)) + ")";
813 QString
Game::pgn() const {
814 return variationPgn(history
, history
[0], 1, Index(1));
817 void Game::load(const PGN
& pgn
) {
818 std::map
<QString
, QString
>::const_iterator var
= pgn
.m_tags
.find("Variant");
821 // FIXME do not create a variant here
822 if (var
== pgn
.m_tags
.end()) {
823 if (!(vi
= Variants::self().create("chess"))) {
824 kError() << "No such variant 'chess'";
828 else if (!(vi
= Variants::self().create(var
->second
))) {
829 kError() << "No such variant" << var
->second
;
833 std::map
<QString
, QString
>::const_iterator fen
= pgn
.m_tags
.find("FEN");
836 //if(var == pgn.m_tags.end()) {
837 pos
= StatePtr(m_components
->createState());
841 else if( !(pos
= vi
->createPositionFromFEN(fen
->second
))) {
842 kError() << "Wrong fen " << fen
->second
;
847 //TODO: what about options? FEN rules?
852 void Game::load(StatePtr pos
, const PGN
& pgn
) {
854 undo_history
.clear();
857 // setup an empty history, clear as needed
860 Entry
* fe
= &history
[0];
861 int old_history_size
= history
.size();
862 std::vector
<int> v_ids
;
864 while(history
.size()>1)
866 for(Variations::const_iterator it
= fe
->variations
.begin();
867 it
!= fe
->variations
.end(); ++it
)
868 v_ids
.push_back(it
->first
);
869 fe
->variations
.clear();
870 fe
->vcomments
.clear();
872 for(int i
=0;i
<(int)v_ids
.size();i
++)
873 onRemoved(Index(0).next(v_ids
[i
]));
874 if(old_history_size
>1)
877 history
[0].position
= pos
;
880 history
.push_back( Entry(Move(), pos
) );
882 // apply moves from PGN, one by one
885 std::vector
<Index
> var_stack
;
886 bool var_start
= false;
888 for (uint i
= 0; i
< pgn
.m_entries
.size(); i
++) {
889 if(boost::get
<QString
>(pgn
[i
])) {
891 vcomment
+= *boost::get
<QString
>(pgn
[i
]);
893 Entry
*e
= fetch(current
);
896 e
->comment
+= *boost::get
<QString
>(pgn
[i
]);
899 else if(boost::get
<PGN::BeginVariation
>(pgn
[i
])) {
900 var_stack
.push_back(current
);
903 else if(boost::get
<PGN::EndVariation
>(pgn
[i
])) {
904 if(var_stack
.size() == 0) {
905 kError() << "Unexpected end variation";
908 current
= var_stack
.back();
909 var_stack
.pop_back();
911 else if(boost::get
<PGN::Move
>(pgn
[i
])) {
912 const PGN::Move
*pm
= boost::get
<PGN::Move
>(pgn
[i
]);
914 int n
= current
.totalNumMoves()+1;
916 if(!pm
->m_number
) // not all moves get numbered in PGN, usually only 1st player ones
917 current
= current
.prev();
918 else if(pm
->m_number
>n
+1)
919 kError() << "Too far variation";
922 kError() << "Too close variation";
923 current
= current
.prev(n
+ 1 - pm
->m_number
);
926 else if(pm
->m_number
&& pm
->m_number
!=n
+1)
927 kError() << "Move number mismatch";
929 StatePtr pos
= position();
930 Move m
= m_components
->moveSerializer("compact")->
931 deserialize(pm
->m_move
, pos
.get());
933 if (m
== Move() || !m_components
->validator()->legal(pos
.get(), m
))
936 StatePtr
newPos(pos
->clone());
940 History
*vec
= fetchRef(current
, &at
);
944 Entry
*e
= &(*vec
)[at
];
945 int var_id
= e
->last_var_id
++;
946 e
->variations
[var_id
].push_back(Entry(m
, newPos
));
947 if(!vcomment
.isEmpty()) {
948 e
->vcomments
[var_id
] = vcomment
;
949 vcomment
= QString();
951 /* this is a hack, but the mainline should NEVER
952 be empty if there is a variation*/
953 if((int)vec
->size() - 1 == at
)
954 vec
->push_back(Entry(m
, newPos
));
956 current
= current
.next(var_id
);
959 if((int)vec
->size() - 1 == at
)
960 vec
->push_back(Entry(m
, newPos
));
962 (*vec
)[at
] = Entry(m
, newPos
);
964 current
= current
.next();
970 kError() << "Unexpected type in boost::variant";
975 Entry
* e
= fetch(Index(0));
976 for(Variations::const_iterator it
= e
->variations
.begin();
977 it
!= e
->variations
.end(); ++it
)
978 onAdded(Index(0).next(it
->first
));
979 for(VComments::const_iterator it
= e
->vcomments
.begin();
980 it
!= e
->vcomments
.end(); ++it
)
981 onSetVComment(Index(0), it
->first
, it
->second
);
984 onCurrentIndexChanged();