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.
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 "variants/variants.h"
23 #include "pgnparser.h"
30 using namespace GamePrivate
;
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*/
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
) {
78 History
*vec
= fetchRef(ix
, &at
);
79 return vec
? &(*vec
)[at
] : NULL
;
82 const Entry
* Game::fetch(const Index
& ix
) const {
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 )
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 )
103 retv
= &it
->second
[ix
.nested
[i
].num_moves
];
104 if(idx
) *idx
= ix
.nested
[i
].num_moves
;
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
)
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() {
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
);
140 onAvailableUndo(true);
142 onAvailableRedo(false);
146 /** returns the index of the current position */
147 Index
Game::index() const {
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
);
170 std::cout
<< "--> Error in Game::position! Index out of range!" << std::endl
;
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
);
185 std::cout
<< "--> Error in Game::position! Index out of range!" << std::endl
;
186 return PositionPtr();
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
);
200 std::cout
<< "--> Error in Game::comment! Index out of range!" << std::endl
;
206 void Game::reset(PositionPtr pos
) {
210 undo_history
.clear();
212 history
.push_back( Entry(MovePtr(), pos
) );
214 onCurrentIndexChanged();
220 std::cout
<< "--> Info: Cannot undo at the beginning of the undo history!" << std::endl
;
224 bool last_undo
= undo_pos
== 1;
225 bool now_redo
= undo_pos
== (int)undo_history
.size();
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());
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
);
245 std::vector
<Entry
>* vec
= fetchRef(a
->index
, &at
);
247 Q_ASSERT((int)vec
->size() == at
+1);
252 if(current
== a
->index
) {
253 current
= current
.prev();
254 onCurrentIndexChanged();
259 else if(boost::get
<UndoPromote
>(op
)) {
260 UndoPromote
*p
= boost::get
<UndoPromote
>(op
);
263 std::vector
<Entry
>* vec
= fetchRef(p
->index
, &at
);
265 Q_ASSERT((*vec
)[at
].variations
.count(p
->variation
)==1);
267 History vold
= (*vec
)[at
].variations
[p
->variation
];
269 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
270 vnew
.push_back((*vec
)[i
]);
271 while((int)vec
->size()>at
+1)
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
);
285 std::vector
<Entry
>* vec
= fetchRef(t
->index
, &at
);
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
);
329 if(sc
->variation
== -1) {
330 e
->comment
= sc
->old_comment
;
331 onSetComment(sc
->index
, sc
->old_comment
);
334 if(sc
->old_comment
.isEmpty())
335 e
->vcomments
.erase(sc
->variation
);
337 e
->vcomments
[sc
->variation
] = sc
->old_comment
;
338 onSetVComment(sc
->index
, sc
->variation
, sc
->old_comment
);
343 onAvailableUndo(false);
345 onAvailableRedo(true);
350 if(undo_pos
>= (int)undo_history
.size()) {
351 std::cout
<< "--> Info: Cannot redo at the end of the undo history!" << std::endl
;
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
]);
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());
368 int v
= a
->index
.nested
[a
->index
.nested
.size()-1].variation
;
369 Q_ASSERT(e
->variations
.count(v
) == 0);
372 h
.push_back(a
->entry
);
373 e
->variations
[v
] = h
;
377 std::vector
<Entry
>* vec
= fetchRef(a
->index
.prev(), &at
);
379 Q_ASSERT((int)vec
->size() == at
+1);
381 vec
->push_back(a
->entry
);
386 else if(boost::get
<UndoPromote
>(op
)) {
387 UndoPromote
*p
= boost::get
<UndoPromote
>(op
);
390 std::vector
<Entry
>* vec
= fetchRef(p
->index
, &at
);
393 Q_ASSERT((*vec
)[at
].variations
.count(p
->variation
)==1);
394 History vold
= (*vec
)[at
].variations
[p
->variation
];
396 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
397 vnew
.push_back((*vec
)[i
]);
398 while((int)vec
->size()>at
+1)
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
);
412 std::vector
<Entry
>* vec
= fetchRef(t
->index
, &at
);
414 Q_ASSERT((int)vec
->size() == at
+1+(int)t
->history
.size());
416 while((int)vec
->size() > at
+1)
418 (*vec
)[at
].variations
.clear();
419 (*vec
)[at
].vcomments
.clear();
421 if(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
);
453 if(sc
->variation
== -1) {
454 e
->comment
= sc
->new_comment
;
455 onSetComment(sc
->index
, sc
->new_comment
);
458 if(sc
->new_comment
.isEmpty())
459 e
->vcomments
.erase(sc
->variation
);
461 e
->vcomments
[sc
->variation
] = sc
->new_comment
;
462 onSetVComment(sc
->index
, sc
->variation
, sc
->new_comment
);
467 onAvailableUndo(true);
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
);
481 std::cout
<< "--> Error in Game::setComment! Invalid index!" << std::endl
;
487 saveUndo(UndoSetComment(ix
, -1, e
->comment
, 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
);
496 std::cout
<< "--> Error in Game::setComment! Invalid index!" << std::endl
;
499 QString oc
= e
->vcomments
.count(v
) ? e
->vcomments
[v
] : QString();
503 saveUndo(UndoSetComment(ix
, v
, oc
, c
));
505 e
->vcomments
.erase(v
);
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
;
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
) {
532 std::vector
<Entry
>* vec
= fetchRef(ix
, &at
);
534 Q_ASSERT((*vec
)[at
].variations
.count(v
)==1);
536 History vold
= (*vec
)[at
].variations
[v
];
538 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
539 vnew
.push_back((*vec
)[i
]);
540 while((int)vec
->size()>at
+1)
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
;
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
)) {
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
);
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())) {
604 onCurrentIndexChanged();
608 /** removes all the successors of the current position */
609 void Game::truncate() {
613 /** removes all the successors of the given position */
614 void Game::truncate(const Index
& ix
) {
616 History
* vec
= fetchRef(ix
, &at
);
618 std::cout
<< "--> Error in Game::truncate! Truncating at an unexisting index!" << std::endl
;
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)
629 undo
.variations
= e
->variations
;
630 undo
.vcomments
= e
->vcomments
;
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
));
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
) {
651 Index old_c
= current
;
653 std::vector
<Entry
>* vec
= fetchRef(current
, &at
);
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();
662 saveUndo(UndoAdd(current
, Entry(m
, pos
)));
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 */
673 Entry
*e
= fetch(current
);
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
);
686 int var_id
= e
->last_var_id
++;
687 e
->variations
[var_id
].push_back(Entry(m
, pos
));
688 current
= current
.next(var_id
);
690 saveUndo(UndoAdd(current
, Entry(m
, pos
)));
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
);
701 if(at
.nested
.size() == 0) {
702 if(undo_history
.size()) {
704 undo_history
.clear();
706 int hs
= history
.size();
707 history
.resize(at
.num_moves
+ 1);
708 history
[at
.num_moves
] = Entry(m
, pos
);
714 std::cout
<< "--> Error in Game::insert! Index out of range!" << std::endl
;
719 if(undo_history
.size()) {
721 undo_history
.clear();
723 bool res
= e
->position
&& e
->position
->equals(pos
);
724 //*e = Entry(m, pos);
729 for (Variations::const_iterator it
= e
->variations
.begin();
730 it
!= e
->variations
.end(); ++it
)
731 testMove(at
.next(it
->first
));
736 /** returns true if we cannot go forward */
737 bool Game::lastPosition() const {
738 return !fetch(current
.next());
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
750 onCurrentIndexChanged(old_c
);
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
765 onCurrentIndexChanged(old_c
);
770 /** go to the root position */
771 void Game::gotoFirst() {
772 Index old_c
= current
;
774 onCurrentIndexChanged(old_c
);
777 /** go to the last position (in the current mainline) */
778 void Game::gotoLast() {
780 std::vector
<Entry
>* vec
= fetchRef(current
, &at
);
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
) {
794 Index old_c
= current
;
796 onCurrentIndexChanged(old_c
);
802 QString
Game::variationPgn(const History
& vec
, const Entry
& e
,
803 int start
, const Index
& _ix
) const {
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
) : "???";
814 mv
= "[[" + mv
+ "]]";
817 int n
= ix
.totalNumMoves()+1;
818 if(i
==start
|| n
%2==0)
819 mv
= QString::number(n
/2)+(n
%2==1 ? ". ... " : ". ") + mv
;
825 if(!vec
[i
].comment
.isEmpty())
826 res
+= " {" + vec
[i
].comment
+ "}";
829 for(Variations::const_iterator it
= vec
[i
-1].variations
.begin();
830 it
!= vec
[i
-1].variations
.end(); ++it
) {
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
)) + ")";
844 /** returns a pgn containing the whole game (with variations) */
845 QString
Game::pgn() const {
846 return variationPgn(history
, history
[0], 1, Index(1));
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");
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
;
861 std::cout
<< "Fine, loaded variant " << vi
->name() << std::endl
;
863 std::map
<QString
, QString
>::const_iterator fen
= pgn
.m_tags
.find("FEN");
866 if(var
== pgn
.m_tags
.end()) {
867 pos
= vi
->createPosition();
870 else if( !(pos
= vi
->createPositionFromFEN(fen
->second
))) {
871 std::cout
<< " --> Error, wrong fen " << fen
->second
<< std::endl
;
875 //TODO: what about options? FEN rules?
880 /** loads a pgn in the current game */
881 void Game::load(PositionPtr pos
, const PGN
& pgn
) {
883 undo_history
.clear();
887 Entry
* fe
= &history
[0];
888 int old_history_size
= history
.size();
889 std::vector
<int> v_ids
;
891 while(history
.size()>1)
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)
904 history
[0].position
= pos
;
907 history
.push_back( Entry(MovePtr(), pos
) );
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
])) {
916 vcomment
+= *boost::get
<QString
>(pgn
[i
]);
918 Entry
*e
= fetch(current
);
921 e
->comment
+= *boost::get
<QString
>(pgn
[i
]);
924 else if(boost::get
<PGN::BeginVariation
>(pgn
[i
])) {
925 var_stack
.push_back(current
);
928 else if(boost::get
<PGN::EndVariation
>(pgn
[i
])) {
929 if(var_stack
.size() == 0) {
930 std::cout
<< " --> Error, unexpected end variation!!!" << std::endl
;
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;
942 current
= current
.prev();
943 else if(pm
->m_number
>n
+1)
944 std::cout
<< " --> Error, too far variation!!!" << std::endl
;
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
))
960 PositionPtr newPos
= pos
->clone();
964 History
*vec
= fetchRef(current
, &at
);
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
);
983 if((int)vec
->size() - 1 == at
)
984 vec
->push_back(Entry(m
, newPos
));
986 (*vec
)[at
] = Entry(m
, newPos
);
988 current
= current
.next();
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
);
1006 onCurrentIndexChanged();