Scid  4.6.5
opening.tcl
Go to the documentation of this file.
1 ###
2 ### opening.tcl: part of Scid.
3 ### Copyright (C) 2007 Pascal Georges
4 ###
5 namespace eval opening {
6  set repBase -1
7  # list of elements of type fenMovesEvalList (fen move1 nag1 .... movei nagi)
8  set allLinesFenList {}
9  set fenMovesEvalList {}
10  # list of hash lists, one list per game (a game = a line)
11  set allLinesHashList {}
12  set hashList {}
13  # options for repertoire training
14  set playerBestMove 1
15  set opBestMove 1
16  set onlyFlaggedLines 0
17  set repColor "w"
18  set resetStats 0
19 
20  set movesLoaded 0
21  set fenLastUpdate 0
22  set fenLastStatsUpdate 0
23  set cancelLoadRepertoire 0
24  set lastMainLoopFen 0
25  set lastMainLoopFlipped [sc_pos side]
26 
27  # parameters for opening trainer window
28  set displayCM 0
29  set DisplayCMValue 0
30  set tCM ""
31  set lastCMFen ""
32  set lastCM "-1"
33 
34  set displayOpeningStats 1
35  set listStats {} ;# list of {fen x y z t} where x:good move played, y:dubious move, z:move out of rep, t:position played
36 
37  ################################################################################
38  # Configuration
39  ################################################################################
40  proc config {} {
41  global ::opening::playerBestMove ::opening::opBestMove ::opening::repColor
42 
43  set w .openingConfig
44  if { [winfo exists $w] } { focus $w ; return}
45  if { [winfo exists ".openingWin"] } { focus ".openingWin" ; return}
46 
47  toplevel $w
48  wm title $w $::tr(Repertoiretrainingconfiguration)
50  frame $w.f0 -relief groove
51 
52  radiobutton $w.f0.rbRepColorW -value "w" -variable ::opening::repColor -text $::tr(white)
53  radiobutton $w.f0.rbRepColorB -value "b" -variable ::opening::repColor -text $::tr(black)
54  radiobutton $w.f0.rbRepColorWB -value "wb" -variable ::opening::repColor -text $::tr(both)
55  pack $w.f0.rbRepColorW $w.f0.rbRepColorB $w.f0.rbRepColorWB -side left -expand yes -fill both
56 
57  frame $w.f1
58  checkbutton $w.f1.cbPlayerBestMove -text $::tr(PlayerBestMove) -variable ::opening::playerBestMove
59  checkbutton $w.f1.cbOpBestMove -text $::tr(OpponentBestMove) -variable ::opening::opBestMove
60  checkbutton $w.f1.cbOnlyFlaggedLines -text $::tr(OnlyFlaggedLines) -variable ::opening::onlyFlaggedLines
61  checkbutton $w.f1.cbResetStats -text $::tr(resetStats) -variable ::opening::resetStats
62  pack $w.f1.cbPlayerBestMove $w.f1.cbOpBestMove $w.f1.cbOnlyFlaggedLines $w.f1.cbResetStats -anchor w -side top
63 
64  frame $w.f2
65  button $w.f2.ok -text $::tr(Continue) -command " destroy $w ; ::opening::openRep"
66  button $w.f2.cancel -text $::tr(Cancel) -command "focus .; destroy $w"
67  pack $w.f2.ok $w.f2.cancel -expand yes -side left -padx 20 -pady 2
68 
69  pack $w.f0 $w.f1 $w.f2 -side top -fill both
70 
71  bind $w <F1> { helpWindow OpeningTrainer }
72  bind $w <Escape> "destroy $w"
73  bind $w <Destroy> ""
74  bind $w <Configure> "recordWinSize $w"
75  }
76  ################################################################################
77  # Open a repertoire
78  ################################################################################
79  proc openRep {} {
80  global ::windows::switcher::base_types ::opening::repColor ::opening::repBase
81 
82  if {$::opening::resetStats} {
83  set ::opening::listStats {}
84  } else {
85  loadStats
86  }
87 
88  set repBase -1
89  set typeW [lsearch $base_types {Openings for White}]
90  set typeB [lsearch $base_types {Openings for Black}]
91  set typeWB [lsearch $base_types {Openings for either color}]
92 
93  foreach x [sc_base list] {
94  set type [sc_base extra $x type]
95  if {$type == $typeW && $repColor == "w" || $type == $typeB && $repColor == "b" || $type == $typeWB && $repColor == "wb"} {
96  set repBase $x
97  break
98  }
99  }
100 
101  if {$repBase == -1} {
102  tk_messageBox -title $::tr(Repertoirenotfound) -type ok -icon warning \
103  -message $::tr(Openfirstrepertoirewithtype)
104  return
105  }
106 
107  set prevBase [sc_base current]
108  if {$prevBase != $repBase} { sc_base switch $repBase}
109  loadRep "$repBase - [sc_base filename $repBase]" "[sc_base extra $repBase description]"
110  if {$prevBase != $repBase} {
111  sc_base switch $prevBase
112  } else {
113  sc_base switch $::clipbase_db
114  }
115 
116  if { $::opening::movesLoaded == 0 } {
117  tk_messageBox -title $::tr(Repertoirenotfound) -type ok -icon error -message $::tr(ZeroMovesLoaded)
118  }
119 
120  if { ! [sc_base inUse $prevBase] } {
121  # switch to clipboard base if the current base is empty
122  sc_base switch $::clipbase_db
123  }
124 
125  # add a blank game for training in current base, if the current base is opened
126  sc_game new
127  sc_game tags set -event $::tr(Openingtrainer)
128  sc_game save 0
129  updateBoard -pgn -animate
132 
135  }
136  ################################################################################
137  # Loads a repertoire
138  # Go through all games and variations and build a tree of positions encountered
139  #
140  ################################################################################
141  proc loadRep { name desc } {
142  global ::opening::repBase ::opening::fenMovesEvalList ::opening::allLinesFenList \
143  ::opening::allLinesHashList ::opening::hashList ::opening::onlyFlaggedLines \
144  ::opening::movesLoaded ::opening::cancelLoadRepertoire
145 
146  set movesLoaded 0
147  set cancelLoadRepertoire 0
148  set allLinesFenList {}
149  set allLinesHashList {}
150 
151  set ::curr_db [sc_base current]
152  progressWindow "Scid" "$::tr(Loadingrepertoire)..." $::tr(Cancel) "::opening::sc_progressBar"
153  for {set g 1} { $g <= [sc_base numGames $::curr_db]} { incr g} {
154  if {$cancelLoadRepertoire} { break}
155  if {$onlyFlaggedLines && ![sc_base gameflag $::curr_db $g get W] && ![sc_base gameflag $::curr_db $g get B]} {
156  continue
157  }
158  set fenMovesEvalList {}
159  set hashList {}
160  sc_game load $g
161  changeProgressWindow "$::tr(Loadingrepertoire)...\n$name\n$desc\n$::tr(Movesloaded) $movesLoaded"
162  updateProgressWindow $g [sc_base numGames $::curr_db]
163  parseGame
164  lappend allLinesFenList $fenMovesEvalList
165  set hashList [lsort -unique $hashList]
166  lappend allLinesHashList $hashList
167  }
168 
170  }
171  ################################################################################
172  # cancel repertoire loading
173  ################################################################################
174  proc sc_progressBar {} {
175  set ::opening::cancelLoadRepertoire 1
176  }
177  ################################################################################
178  # parse one game and fill the list
179  ################################################################################
180  proc parseGame {} {
181  while {![sc_pos isAt vend]} {
182  fillFen
183  # Go through all variants
184  for {set v 0} {$v<[sc_var count]} {incr v} {
185  # enter each var (beware the first move is played)
186  sc_var enter $v
187  parseVar
188  }
189  # now treat the main line
190  sc_move forward
191  }
192  }
193  ################################################################################
194  # parse recursively variants.
195  ################################################################################
196  proc parseVar {} {
197  while {![sc_pos isAt vend]} {
198  fillFen
199  # Go through all variants
200  for {set v 0} {$v<[sc_var count]} {incr v} {
201  sc_var enter $v
202  fillFen
203  # we are at the start of a var, before the first move : start recursive calls
204  parseVar
205  }
206  sc_move forward
207  }
208  # at the end of a var : exit it
209  sc_var exit
210  }
211  ################################################################################
212  # fill the tree with repertoire information
213  # we are at a given position :
214  # - fill hash list in order to speed up searches
215  # - fill fenMovesEvalList with {fen {move eval} {move eval} .... }
216  ################################################################################
217  proc fillFen {} {
218  global ::opening::fenMovesEvalList ::opening::hashList ::opening::movesLoaded
219 
220  if {[sc_pos isAt vend] && [sc_var count] == 0 } {
221  return
222  }
223 
224  set s [split [sc_pos fen]]
225  set fen "[lindex $s 0] [lindex $s 1] [lindex $s 2] [lindex $s 3]"
226 
227  set newFen {}
228  set moves {}
229  set newIndex -1
230  incr movesLoaded
231 
232  lappend hashList [sc_pos hash]
233 
234  # check if the fen already exists in the list
235  for {set i 0} { $i < [llength $fenMovesEvalList]} {incr i} {
236  set f [lindex $fenMovesEvalList $i]
237  if {[lindex $f 0] == $fen} { set newFen $fen ; set moves [lindex $f 1] ; set newIndex $i ; break}
238  }
239  set newFen $fen
240 
241  # the main move
242  if {! [sc_pos isAt vend] } {
243  set m [sc_game info nextMove]
244  sc_move forward
245  set nag [sc_pos getNags]
246  sc_move back
247  if {[lsearch $moves $m] == -1 } {
248  lappend moves $m $nag
249  } else {
250  # the move already exists : check if NAG values are coherent
251  set lmoves [lsearch -all $moves $m]
252  foreach i $lmoves {
253  if {[lindex $moves [expr $i +1]] != $nag} {
254  puts "redundancy and incoherence $m $nag for $newFen"
255  }
256  }
257  }
258  }
259  # Go through all variants
260  for {set v 0} {$v<[sc_var count]} {incr v} {
261  sc_var enter $v
262  set nag [sc_pos getNags]
263  set m [sc_game info previousMove]
264  if {[lsearch $moves $m] == -1 } {
265  lappend moves $m $nag
266  } else {
267  # the move already exists : check if NAG values are coherent
268  set lmoves [lsearch -all $moves $m]
269  foreach i $lmoves {
270  if {[lindex $moves [expr $i +1]] != $nag} {
271  puts "var redundancy and incoherence $m $nag for $newFen"
272  }
273  }
274  }
275  sc_var exit
276  }
277 
278  # put the newFen in the list
279  if {$newIndex == -1} {
280  lappend fenMovesEvalList [list $fen $moves]
281  } else {
282  lset fenMovesEvalList $newIndex [list $fen $moves]
283  }
284  }
285  ################################################################################
286  # main loop called every second to trigger playing
287  ################################################################################
288  proc mainLoop {} {
289  global ::opening::allLinesHashList ::opening::allLinesFenList tCM
290 
291  after cancel ::opening::mainLoop
292 
293  # Handle case of player's turn (which always plays from bottom of the board)
294  if { [sc_pos side] == "white" && ![::board::isFlipped .main.board] || [sc_pos side] == "black" && [::board::isFlipped .main.board] } {
295  # it is player's turn : update UI
298  after 1000 ::opening::mainLoop
299  return
300  }
301 
302  # check the position has not been treated already
303  if {[sc_pos fen] == $::opening::lastMainLoopFen && [::board::isFlipped .main.board] == $::opening::lastMainLoopFlipped} {
304  after 1000 ::opening::mainLoop
305  return
306  }
307 
308  # the player moved : check if his move was in the repertoire and as good as expected
309  set move_done [sc_game info previousMove]
310  if { $move_done != "" } {
311  sc_move back
312  set cm [ getCm]
313  sc_move forward
314  # No move available : reached the end of a line
315  if { [llength $cm] == 0 } {
318  after 1000 ::opening::mainLoop
319  return
320  }
321 
322  # we know there are some CM
323  set l [lsearch -all $cm $move_done]
324  # move not in repertoire
325  if {[llength $l] == 0} {
326  tk_messageBox -type ok -message $::tr(Movenotinrepertoire) -icon info
327  sc_move back
328  addStats -good 0 -dubious 0 -absent 1 -total 1
331  updateBoard -pgn -animate
332  after 1000 ::opening::mainLoop
333  return
334  }
335 
336  # The move played is in repertoire !
337  set moveOK 1
338 
339  if {$::opening::playerBestMove} {
340  foreach i $l {
341  if {! [ ::opening::isGoodMove [ lindex $cm [expr $i+1]]] } {
342  addStatsPrev -good 0 -dubious 1 -absent 0 -total 1
343  set moveOK 0
344  set nag [ lindex $cm [expr $i+1]]
345  break
346  }
347  }
348 
349  # The move is not good : offer to take back
350  if { ! $moveOK } {
351  # addStatsPrev -good 0 -dubious 0 -absent 1 -total 0
352  set answer [tk_messageBox -icon question -title $::tr(OutOfOpening) -type yesno \
353  -message "$::tr(yourmoveisnotgood) ($nag) \n $::tr(DoYouWantContinue)"]
354  if {$answer == no} {
355  sc_move back
356  updateBoard -pgn
357  after 1000 ::opening::mainLoop
358  return
359  }
360  } else { ;# the move is a good one
361  addStatsPrev -good 1 -dubious 0 -absent 0 -total 1
362  }
363  } else { ;# player is allowed to play bad moves
364  foreach i $l {
365  set goodMove 1
366  if {! [ ::opening::isGoodMove [ lindex $cm [expr $i+1]]] } {
367  set goodMove 0
368  break
369  }
370  }
371  if {$goodMove} {
372  addStatsPrev -good 1 -dubious 0 -absent 0 -total 1
373  } else {
374  addStatsPrev -good 1 -dubious 1 -absent 0 -total 1
375  }
376  }
377 
378  }
379  # end of player's move check
380  # now it is computer's turn
381  set cm [ getCm]
382 
383  if {[llength $cm] != 0} {
384  ::opening::play $cm
385  }
386  set ::opening::lastMainLoopFen [sc_pos fen]
387  set ::opening::lastMainLoopFlipped [::board::isFlipped .main.board]
388 
391  after 1000 ::opening::mainLoop
392  }
393  ################################################################################
394  # isGoodMove : returns true if the nag list in parameter is empty or contains !? ! !!
395  ################################################################################
396  proc isGoodMove { n } {
397  if { [lsearch -exact $n "?"] != -1 || [lsearch -exact $n "?!"] != -1 || [lsearch -exact $n "??"] != -1} {
398  return 0
399  }
400  return 1
401  }
402  ################################################################################
403  # get all candidate moves in the repertoire from current position
404  # the list returned is of the form {move1 nag1 move2 nag2 ....}
405  # the moves are not unique
406  ################################################################################
407  proc getCm { } {
408  global ::opening::allLinesHashList ::opening::allLinesFenList ::opening::lastCMFen
409 
410  set fen [ sc_pos fen]
411  # avoids calculation
412  if {$fen == $lastCMFen } { return $lastCM}
413 
414  set cm {}
415  # First find the position in hash lists to spare time
416  set linesFound {}
417  for {set i 0} {$i<[llength $allLinesHashList]} {incr i} {
418  set res [lsearch -sorted [lindex $allLinesHashList $i] [sc_pos hash]]
419  if {$res != -1} { lappend linesFound $i}
420  }
421 
422  set s [split $fen]
423  set fen "[lindex $s 0] [lindex $s 1] [lindex $s 2] [lindex $s 3]"
424  foreach i $linesFound {
425  set line [lindex $allLinesFenList $i]
426  foreach f $line {
427  if {[lindex $f 0] == $fen} { set cm [concat $cm [lindex $f 1]]}
428  }
429  }
430 
431  set lastCM $cm
432  set lastCMFen $fen
433 
434  return $cm
435  }
436  ################################################################################
437  # play one of the candidate moves
438  ################################################################################
439  proc play { cm } {
440  # addStatsPrev -good 0 -dubious 0 -absent 0 -total 1
441  set r [expr int(rand()*[llength $cm]/2)]
442  set m [ lindex $cm [ expr $r * 2]]
443 
444  if {[sc_pos moveNumber] == 1 && [sc_pos side] == "white"} {
446  }
447 
448  if {![catch {sc_move addSan [::untrans $m]}]} {
449  }
450  updateBoard -pgn -animate
451  }
452  ################################################################################
453  # The window displayed when in opening trainer mode
454  ################################################################################
455  proc openingWin {} {
456  global ::opening::displayCM ::opening::displayCMValue ::opening::tCM ::opening::fenLastUpdate
457 
458  set w ".openingWin"
459  if {[winfo exists $w]} { focus $w ; return}
460 
461  toplevel $w
462  wm title $w $::tr(Openingtrainer)
463  setWinLocation $w
464  frame $w.f1
465  frame $w.f2 -relief raised -bd 2
466  frame $w.f3
467 
468  checkbutton $w.f1.cbDisplayCM -text $::tr(DisplayCM) -variable ::opening::displayCM -relief flat \
469  -command "set fenLastUpdate 0 ; ::opening::update_tCM 1"
470  checkbutton $w.f1.cbDisplayCMValue -text $::tr(DisplayCMValue) -variable ::opening::displayCMValue -relief flat \
471  -command "set fenLastUpdate 0 ; ::opening::update_tCM 1"
472  label $w.f1.lCM -textvariable ::opening::tCM
473  pack $w.f1.cbDisplayCM $w.f1.cbDisplayCMValue -anchor w -side top
474  pack $w.f1.lCM -side top -anchor center
475 
476  checkbutton $w.f2.cbDisplayStats -text $::tr(DisplayOpeningStats) -variable ::opening::displayOpeningStats -relief flat \
477  -command "::opening::updateStats 1"
478  label $w.f2.lStats1 -textvariable ::opening::lStats1 -width 4 -anchor center -background green
479  label $w.f2.lStats2 -textvariable ::opening::lStats2 -width 4 -anchor center -background yellow
480  label $w.f2.lStats3 -textvariable ::opening::lStats3 -width 4 -anchor center -background red
481  label $w.f2.lStats4 -textvariable ::opening::lStats4 -width 4 -anchor center -background white
482 
483  label $w.f2.lStats1exp -text $::tr(NumberOfGoodMovesPlayed)
484  label $w.f2.lStats2exp -text $::tr(NumberOfDubiousMovesPlayed)
485  label $w.f2.lStats3exp -text $::tr(NumberOfMovesPlayedNotInRepertoire)
486  label $w.f2.lStats4exp -text $::tr(NumberOfTimesPositionEncountered)
487 
488  grid $w.f2.cbDisplayStats -row 0 -column 0 -columnspan 2
489  grid $w.f2.lStats4 -row 1 -column 0 -sticky w -padx 5
490  grid $w.f2.lStats1 -row 2 -column 0 -sticky w -padx 5
491  grid $w.f2.lStats2 -row 3 -column 0 -sticky w -padx 5
492  grid $w.f2.lStats3 -row 4 -column 0 -sticky w -padx 5
493 
494 
495  grid $w.f2.lStats4exp -row 1 -column 1 -sticky w -padx 5
496  grid $w.f2.lStats1exp -row 2 -column 1 -sticky w -padx 5
497  grid $w.f2.lStats2exp -row 3 -column 1 -sticky w -padx 5
498  grid $w.f2.lStats3exp -row 4 -column 1 -sticky w -padx 5
499 
500  button $w.f3.report -textvar ::tr(ShowReport) -command ::opening::report
501  button $w.f3.close -textvar ::tr(Abort) -command ::opening::endTraining
502 
503  pack $w.f3.report $w.f3.close -side top -anchor center -fill x
504  pack $w.f1 $w.f2 $w.f3 -fill x
505 
506  bind $w <F1> { helpWindow OpeningTrainer }
507  bind $w <Escape> "destroy $w"
508  bind $w <Destroy> ""
509  bind $w <Configure> "recordWinSize $w"
510  wm minsize $w 45 0
511  }
512  ################################################################################
513  #
514  ################################################################################
515  proc endTraining {} {
516  after cancel ::opening::mainLoop
517  saveStats
518  focus .
519  destroy ".openingWin"
520  }
521  ################################################################################
522  # display the candidate moves list (with NAG values)
523  ################################################################################
524  proc update_tCM { { forceUpdate 0 } } {
525  global ::opening::displayCM ::opening::displayCMValue ::opening::tCM ::opening::fenLastUpdate
526 
527  # If current fen is the same as the one used during latest update call, do nothing
528  if {$fenLastUpdate == [sc_pos fen] && ! $forceUpdate} { return}
529 
530  set cm [ getCm]
531 
532  if { [llength $cm] == 0 } {
533  .openingWin.f1.lCM configure -bg LightCoral
534  set tCM $::tr(EndOfVar)
535  set fenLastUpdate [sc_pos fen]
536  return
537  }
538 
539  if { !$displayCM } { set tCM "" ; set fenLastUpdate 0 ; return}
540 
541  .openingWin.f1.lCM configure -bg linen
542 
543  set tmp ""
544 
545  for {set x 0} {$x<[llength $cm]} { set x [expr $x+2]} {
546  set m [lindex $cm $x]
547  # if the move already found, skip it, even if it has other nags : to be corrected ?
548  if {[string first $m $tmp] != -1} { continue}
549  append tmp $m " "
550  set nlist [lindex $cm [expr $x+1]]
551  if {$nlist == 0} { continue}
552  if {$displayCMValue} {
553  foreach n $nlist {
554  append tmp $n " "
555  }
556  }
557  # go to new line every 3 (moves,nags)
558  if {[expr $x % 3] == 2} { append tmp "\n"}
559  }
560 
561  set fenLastUpdate [sc_pos fen]
562  set tCM $tmp
563  }
564  ################################################################################
565  #
566  ################################################################################
567  proc loadStats {} {
568  set optionsFile [scidConfigFile optrainer]
569  if {[catch {source $optionsFile}]} {
570  ::splash::add "Unable to find the options file: [file tail $optionsFile]"
571  } else {
572  ::splash::add "Your options file \"[file tail $optionsFile]\" was found and loaded."
573  }
574  }
575  ################################################################################
576  #
577  ################################################################################
578  proc saveStats {} {
579  set optrainerFile [scidConfigFile optrainer]
580  if {[catch {open $optrainerFile w} f]} {
581  return 0
582  }
583  puts $f "set ::opening::listStats { $::opening::listStats }"
584  close $f
585  return 1
586  }
587  ################################################################################
588  # getStats
589  # returns a list containing the 4 stats values for current pos
590  # or an empty list if the stats are not available for current position
591  ################################################################################
592  proc getStats {} {
593  set s [split [sc_pos fen]]
594  set fen "[lindex $s 0] [lindex $s 1] [lindex $s 2] [lindex $s 3]"
595  set found 0
596  set idx 0
597  foreach l $::opening::listStats {
598  if {[lindex $l 0] == $fen} {
599  set found 1
600  break
601  }
602  incr idx
603  }
604  if {$found} {
605  return [lindex $l 1]
606  }
607  return {}
608  }
609 
610  ################################################################################
611  # addStats
612  # x = success best moves only, y = success all moves z = failures t = coverage by computer
613  ################################################################################
614  proc addStats { args } {
615  set dx 0
616  set dy 0
617  set dz 0
618  set dt 0
619 
620  for {set i 0} {$i < [llength $args]} {incr i 2} {
621  if {[lindex $args $i] == "-good"} { set dx [lindex $args [expr $i + 1]] ; continue}
622  if {[lindex $args $i] == "-dubious"} { set dy [lindex $args [expr $i + 1]] ; continue}
623  if {[lindex $args $i] == "-absent"} { set dz [lindex $args [expr $i + 1]] ; continue}
624  if {[lindex $args $i] == "-total"} { set dt [lindex $args [expr $i + 1]] ; continue}
625  }
626 
627  set s [split [sc_pos fen]]
628  set fen "[lindex $s 0] [lindex $s 1] [lindex $s 2] [lindex $s 3]"
629  set found 0
630  set idx 0
631  foreach l $::opening::listStats {
632  if {[lindex $l 0] == $fen} {
633  set found 1
634  break
635  }
636  incr idx
637  }
638 
639  if {$found} {
640  set lval [lindex $l 1]
641  set ::opening::listStats [ lreplace $::opening::listStats $idx $idx [list $fen [list \
642  [expr [lindex $lval 0]+$dx] \
643  [expr [lindex $lval 1]+$dy] \
644  [expr [lindex $lval 2]+$dz] \
645  [expr [lindex $lval 3]+$dt]]]]
646  } else {
647  lappend ::opening::listStats [list $fen [list $dx $dy $dz $dt]]
648  }
649  updateStats 1
650  }
651  ################################################################################
652  #
653  ################################################################################
654  proc addStatsPrev { args } {
655  if {[sc_pos isAt vstart] } { return}
656  if { ![catch {sc_move back}]} {
657  eval addStats $args
658  sc_move forward
659  }
660  }
661  ################################################################################
662  #
663  ################################################################################
664  proc updateStats { {force 0} } {
665  global ::opening::fenLastStatsUpdate
666 
667  # If current fen is the same as the one used during latest update call, do nothing
668  if {$fenLastStatsUpdate == [sc_pos fen] && !$force} { return}
669 
670  set fenLastStatsUpdate [sc_pos fen]
671 
672  if { $::opening::displayOpeningStats } {
673  set gs [getStats]
674  set ::opening::lStats1 [lindex $gs 0]
675  set ::opening::lStats2 [lindex $gs 1]
676  set ::opening::lStats3 [lindex $gs 2]
677  set ::opening::lStats4 [lindex $gs 3]
678  } else {
679  set ::opening::lStats1 " "
680  set ::opening::lStats2 " "
681  set ::opening::lStats3 " "
682  set ::opening::lStats4 " "
683  }
684  }
685  ################################################################################
686  # shows a repertoire report (how much of the rep was trained)
687  ################################################################################
688  proc report {} {
689  global ::opening::listStats ::opening::allLinesFenList
690  set w ".openingWin.optrainerreport"
691  if {[winfo exists $w]} { focus $w ; return}
692 
693  toplevel $w
694  wm title $w $::tr(Openingtrainer)
695  setWinLocation $w
696 
697  frame $w.ft
698  text $w.ft.text -height 10 -width 40 -wrap word -background white
699  pack $w.ft.text
700  pack $w.ft
701 
702  frame $w.fclose
703  button $w.fclose.close -textvar ::tr(Close) -command "destroy $w"
704  pack $w.fclose.close
705 
706  # builds stats report
707  set posNotPlayed 0
708  set posTotalPlayed 0
709  set success 0
710  set dubMoves 0
711  set outOfRep 0
712  set totalPos 0
713  foreach line $allLinesFenList {
714  incr totalPos [llength $line]
715  foreach pos $line {
716  set fenLine [lindex $pos 0]
717  set idx 0
718  set found 0
719  foreach l $listStats {
720  if {$fenLine == [lindex $l 0]} {
721  set found 1
722  break
723  }
724  incr idx
725  }
726  if { $found } {
727  set stats [lindex [ lindex $listStats $idx] 1]
728  if { $stats != "" } {
729  incr success [lindex $stats 0]
730  incr dubMoves [lindex $stats 1]
731  incr outOfRep [lindex $stats 2]
732  incr posTotalPlayed [lindex $stats 3]
733  }
734  } else {
735  incr posNotPlayed
736  }
737  }
738  }
739  $w.ft.text insert end "$::tr(PositionsInRepertoire) $totalPos\n"
740  $w.ft.text insert end "$::tr(PositionsNotPlayed) $posNotPlayed\n"
741  $w.ft.text insert end "$::tr(PositionsPlayed) [expr $totalPos - $posNotPlayed]\n"
742  $w.ft.text insert end "$::tr(Success) $success\n"
743  $w.ft.text insert end "$::tr(DubiousMoves) $dubMoves\n"
744  $w.ft.text insert end "$::tr(OutOfRepertoire) $outOfRep\n"
745 
746  $w.ft.text configure -state disabled
747 
748  bind $w <F1> { helpWindow OpeningTrainer }
749  bind $w <Escape> "destroy $w"
750  bind $w <Destroy> ""
751  bind $w <Configure> "recordWinSize $w"
752  # wm minsize $w 45 0
753  }
754  ################################################################################
755  #
756  ################################################################################
757 }
758 ###
759 ### End of file: opening.tcl
760 ###