Scid  4.7.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
sergame.tcl
Go to the documentation of this file.
1 ###
2 ### sergame.tcl: part of Scid.
3 ### Copyright (C) 2007 Pascal Georges
4 ###
5 ################################################################################
6 # The number used for the engine playing a serious game is 3
7 ################################################################################
8 
9 namespace eval sergame {
10 
11  # DEBUG
12  set ::uci::uciInfo(log_stdout3) 0
13 
14  # if true, follow a specific opening
15  set openingMovesList {}
16  set openingMovesHash {}
17  set openingMoves ""
18  set outOfOpening 0
19  array set engineListBox {}
20  set engineName ""
21  set bookSlot 2
22  set storeEval 0
23 
24  # list of fen positions played to detect 3 fold repetition
25  set lFen {}
26  set lastPlayerMoveUci ""
27 
28  ################################################################################
29  #
30  ################################################################################
31  proc config {} {
32  global ::sergame::configWin ::sergame::chosenOpening
33 
34  set w ".configSerGameWin"
35  if {[winfo exists $w]} {
36  focus $w
37  return
38  }
39 
41  wm title $w "$::tr(configuregame)"
42 
43  bind $w <F1> { helpWindow SeriousGame }
45 
46  ttk::frame $w.fconfig -padding 10
47  ttk::frame $w.fbuttons
48  ttk::labelframe $w.fengines -text $::tr(Engine)
49  ttk::labelframe $w.ftime -text $::tr(TimeMode)
50  ttk::labelframe $w.fopening -text $::tr(Opening)
51 
52  grid $w.fengines -row 0 -column 0 -pady { 0 10 } -sticky we -padx { 0 10 }
53  grid $w.fopening -row 0 -column 1 -pady { 0 10 } -sticky nswe -padx { 10 0 }
54  grid $w.ftime -row 1 -column 0 -pady { 10 0 } -sticky nswe -padx { 0 10 }
55  grid $w.fconfig -row 1 -column 1 -pady { 10 0 } -sticky we -padx { 10 0 }
56  grid $w.fbuttons -row 2 -column 1 -sticky we
57 
58  # builds the list of UCI engines
59  ttk::frame $w.fengines.fEnginesList
60  listbox $w.fengines.fEnginesList.lbEngines -yscrollcommand "$w.fengines.fEnginesList.ybar set" \
61  -height 5 -width 20 -exportselection 0
62  ttk::scrollbar $w.fengines.fEnginesList.ybar -command "$w.fengines.fEnginesList.lbEngines yview"
63  pack $w.fengines.fEnginesList.ybar -side right -fill y
64  pack $w.fengines.fEnginesList.lbEngines -side left -fill x -expand 1
65  pack $w.fengines.fEnginesList -expand yes -fill x -side top
66  set i 0
67  set idx 0
68  foreach e $::engines(list) {
69  if { [lindex $e 7] != 1} { incr idx ; continue}
70  set ::sergame::engineListBox($i) $idx
71  set name [lindex $e 0]
72  $w.fengines.fEnginesList.lbEngines insert end $name
73  incr i
74  incr idx
75  }
76 
77  # Engine configuration (limit strength for example)
78  ttk::button $w.fengines.bEngineConfig -text $::tr(ConfigureUCIengine) -command {
79  set sel [.configSerGameWin.fengines.fEnginesList.lbEngines curselection]
80  set index $::sergame::engineListBox($sel)
81  set engineData [lindex $::engines(list) $index]
82  set name [lindex $engineData 0]
83  set cmd [ toAbsPath [lindex $engineData 1] ]
84  set args [lindex $engineData 2]
85  set dir [ toAbsPath [lindex $engineData 3] ]
86  set options [lindex $engineData 8]
87  set ::uci::autoSaveOptionsIndex $index
88  set ::uci::autoSaveOptions 1
89  ::uci::uciConfig 3 [ toAbsPath $cmd ] $args [ toAbsPath $dir ] $options
90  }
91  pack $w.fengines.bEngineConfig -side top -pady 5 -anchor e -padx 4
92 
93  # if no engines defined, bail out
94  if {$i == 0} {
95  tk_messageBox -type ok -message "No UCI engine defined" -icon error
96  destroy $w
97  return
98  }
99 
100  $w.fengines.fEnginesList.lbEngines selection set $::sergame::chosenEngine
101  $w.fengines.fEnginesList.lbEngines see $::sergame::chosenEngine
102 
103  # load book names
104  ttk::checkbutton $w.fconfig.cbUseBook -text $::tr(UseBook) -variable ::sergame::useBook
105  set bookPath $::scidBooksDir
106  set bookList [ lsort -dictionary [ glob -nocomplain -directory $bookPath *.bin]]
107  if { [llength $bookList] == 0 } {
108  $w.fconfig.cbUseBook configure -state disabled
109  set ::sergame::useBook 0
110  }
111  set i 0
112  set idx 0
113  set tmp {}
114  foreach file $bookList {
115  lappend tmp [ file tail $file]
116  if { $::sergame::bookToUse == [ file tail $file]} {
117  set idx $i
118  }
119  incr i
120  }
121 
122  ttk::combobox $w.fconfig.combo -width 12 -values $tmp
123  catch { ch$w.fconfig.combo current $idx}
124 
125  set row 0
126 
127  # Time bonus frame
128  ttk::frame $w.ftime.timebonus
129  pack $w.ftime.timebonus -side top -fill x
130 
131  ttk::radiobutton $w.ftime.timebonus.rb1 -text $::tr(TimeBonus) -value "timebonus" -variable ::sergame::timeMode
132  grid $w.ftime.timebonus.rb1 -row $row -column 0 -sticky w -rowspan 2
133 
134  ttk::label $w.ftime.timebonus.whitelabel -text $::tr(White)
135  grid $w.ftime.timebonus.whitelabel -row $row -column 1
136  ttk::spinbox $w.ftime.timebonus.whitespminutes -background white -width 2 -from 1 -to 120 -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P }
137  grid $w.ftime.timebonus.whitespminutes -row $row -column 2
138  ttk::label $w.ftime.timebonus.whitelminutes -text $::tr(TimeMin)
139  grid $w.ftime.timebonus.whitelminutes -row $row -column 3
140  ttk::spinbox $w.ftime.timebonus.whitespseconds -background white -width 2 -from 0 -to 60 -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P }
141  grid $w.ftime.timebonus.whitespseconds -row $row -column 4
142  ttk::label $w.ftime.timebonus.whitelseconds -text $::tr(TimeSec)
143  grid $w.ftime.timebonus.whitelseconds -row $row -column 5
144 
145  incr row
146  ttk::label $w.ftime.timebonus.blacklabel -text $::tr(Black)
147  grid $w.ftime.timebonus.blacklabel -row $row -column 1
148  ttk::spinbox $w.ftime.timebonus.blackspminutes -background white -width 2 -from 1 -to 120 -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P }
149  grid $w.ftime.timebonus.blackspminutes -row $row -column 2
150  ttk::label $w.ftime.timebonus.blacklminutes -text $::tr(TimeMin)
151  grid $w.ftime.timebonus.blacklminutes -row $row -column 3
152  ttk::spinbox $w.ftime.timebonus.blackspseconds -background white -width 2 -from 0 -to 60 -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P }
153  grid $w.ftime.timebonus.blackspseconds -row $row -column 4
154  ttk::label $w.ftime.timebonus.blacklseconds -text $::tr(TimeSec)
155  grid $w.ftime.timebonus.blacklseconds -row $row -column 5
156 
157  $w.ftime.timebonus.whitespminutes set [expr $::uci::uciInfo(wtime3) / (60 * 1000)]
158  $w.ftime.timebonus.whitespseconds set [expr $::uci::uciInfo(winc3) / 1000]
159  $w.ftime.timebonus.blackspminutes set [expr $::uci::uciInfo(btime3) / (60 * 1000)]
160  $w.ftime.timebonus.blackspseconds set [expr $::uci::uciInfo(binc3) / 1000]
161 
162  # Fixed depth
163  ttk::frame $w.ftime.depth
164  ttk::radiobutton $w.ftime.depth.button -text $::tr(FixedDepth) -value "depth" -variable ::sergame::timeMode -width 16
165  ttk::spinbox $w.ftime.depth.value -background white -width 3 -from 1 -to 20 -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P }
166  $w.ftime.depth.value set $::sergame::depth
167 
168  pack $w.ftime.depth -side top -fill x
169  pack $w.ftime.depth.button -side left
170  pack $w.ftime.depth.value -side left
171 
172  ttk::frame $w.ftime.nodes
173  ttk::radiobutton $w.ftime.nodes.button -text "$::tr(Nodes) (x1000)" -value "nodes" -variable ::sergame::timeMode -width 16
174  ttk::spinbox $w.ftime.nodes.value -background white -width 3 -from 5 -to 10000 -increment 5 -validate all -validatecommand { regexp {^[0-9]+$} %P }
175  $w.ftime.nodes.value set [ expr $::sergame::nodes /1000]
176 
177  pack $w.ftime.nodes -side top -fill x
178  pack $w.ftime.nodes.button -side left
179  pack $w.ftime.nodes.value -side left
180 
181  ttk::frame $w.ftime.movetime
182  ttk::radiobutton $w.ftime.movetime.button -text $::tr(SecondsPerMove) -value "movetime" -variable ::sergame::timeMode -width 16
183  ttk::spinbox $w.ftime.movetime.value -background white -width 3 -from 1 -to 120 -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P }
184  $w.ftime.movetime.value set [ expr $::sergame::movetime /1000]
185 
186  pack $w.ftime.movetime -side top -fill x
187  pack $w.ftime.movetime.button -side left
188  pack $w.ftime.movetime.value -side left
189 
190  pack $w.fconfig.cbUseBook -side top -anchor w
191  pack $w.fconfig.combo -side top -anchor w -padx 20 -fill x
192 
193  # New game or use current position ?
194  ttk::checkbutton $w.fconfig.cbPosition -text $::tr(StartFromCurrentPosition) -variable ::sergame::startFromCurrent
195  pack $w.fconfig.cbPosition -side top -anchor w
196 
197  # ponder
198  ttk::checkbutton $w.fconfig.cbPonder -text $::tr(Ponder) -variable ::sergame::ponder
199  pack $w.fconfig.cbPonder -side top -anchor w
200 
201  # Warn if the user makes weak/bad moves
202  ttk::checkbutton $w.fconfig.cbCoach -text $::tr(CoachIsWatching) -variable ::sergame::coachIsWatching
203  pack $w.fconfig.cbCoach -side top -anchor w
204  #Should the evaluation of the position stored in the comment?
205  ttk::checkbutton $w.fconfig.storeEval -text $::tr(AddScoreToShortAnnotations) -variable ::sergame::storeEval
206  pack $w.fconfig.storeEval -side top -anchor w
207 
208  # choose a specific opening
209  ttk::checkbutton $w.fopening.cbOpening -text $::tr(SpecificOpening) -variable ::sergame::isOpening
210  ttk::frame $w.fopening.fOpeningList
211  listbox $w.fopening.fOpeningList.lbOpening -yscrollcommand "$w.fopening.fOpeningList.ybar set" \
212  -height 5 -width 36 -list ::tacgame::openingList -exportselection 0
213  $w.fopening.fOpeningList.lbOpening selection set $::sergame::chosenOpening
214  $w.fopening.fOpeningList.lbOpening see $::sergame::chosenOpening
215 
216  ttk::scrollbar $w.fopening.fOpeningList.ybar -command "$w.fopening.fOpeningList.lbOpening yview"
217  pack $w.fopening.fOpeningList.ybar -side right -fill y
218  pack $w.fopening.fOpeningList.lbOpening -side left -fill both -expand 1
219  pack $w.fopening.fOpeningList -fill both -side top
220  pack $w.fopening.cbOpening -fill x -side top
221 
222  ttk::button $w.fbuttons.close -text $::tr(Play) -command {
223  focus .
224  set ::sergame::chosenEngine [.configSerGameWin.fengines.fEnginesList.lbEngines curselection]
225  set ::sergame::engineName [.configSerGameWin.fengines.fEnginesList.lbEngines get $::sergame::chosenEngine]
226  set ::sergame::chosenOpening [.configSerGameWin.fopening.fOpeningList.lbOpening curselection]
227  if {$::sergame::useBook} {
228  set ::sergame::bookToUse [.configSerGameWin.fconfig.combo get]
229  if {$::sergame::bookToUse == "" } {
230  set ::sergame::useBook 0
231  }
232  }
233  set ::uci::uciInfo(wtime3) [expr [.configSerGameWin.ftime.timebonus.whitespminutes get]*1000*60]
234  set ::uci::uciInfo(btime3) [expr [.configSerGameWin.ftime.timebonus.blackspminutes get]*1000*60]
235  set ::uci::uciInfo(winc3) [expr [.configSerGameWin.ftime.timebonus.whitespseconds get]*1000]
236  set ::uci::uciInfo(binc3) [expr [.configSerGameWin.ftime.timebonus.blackspseconds get]*1000]
237  set ::uci::uciInfo(fixeddepth3) [.configSerGameWin.ftime.depth.value get]
238  set ::uci::uciInfo(fixednodes3) [expr [.configSerGameWin.ftime.nodes.value get]*1000]
239  set ::uci::uciInfo(movetime3) [expr [.configSerGameWin.ftime.movetime.value get]*1000]
240 
241  destroy .configSerGameWin
242  ::sergame::play $::sergame::chosenEngine
243  }
244  ttk::button $w.fbuttons.cancel -textvar ::tr(Cancel) -command "focus .; destroy $w"
245 
246  packbuttons right $w.fbuttons.cancel $w.fbuttons.close
247  bind $w <Escape> { .configSerGameWin.fbuttons.cancel invoke }
248  bind $w <Return> { .configSerGameWin.fbuttons.close invoke }
249  bind $w <F1> { helpWindow SeriousGame }
250  bind $w <Destroy> ""
251  bind $w <Configure> "recordWinSize $w"
252  wm resizable $w 0 0
253  }
254 
255  ################################################################################
256  #
257  ################################################################################
258  proc play { engine {n 3} } {
259  global ::sergame::chosenOpening ::sergame::isOpening ::tacgame::openingList ::sergame::openingMovesList \
260  ::sergame::openingMovesHash ::sergame::openingMoves ::sergame::outOfOpening
261 
262  if {$::sergame::isOpening || !$::sergame::startFromCurrent} {
263  if {[::game::Clear] eq "cancel"} { return}
264  }
265 
266  set ::sergame::lFen {}
267 
268  ::uci::startEngine $::sergame::engineListBox($engine) $n
269  set engineData [lindex $::engines(list) $::sergame::engineListBox($engine)]
270  ::uci::sendUCIoptions $n [ lindex $engineData 8]
271 
272  set ::uci::uciInfo(prevscore$n) 0.0
273  set ::uci::uciInfo(score$n) 0.0
274  set ::uci::uciInfo(ponder$n) ""
275 
276  if {$::sergame::startFromCurrent} {
277  set isOpening 0
278  }
279 
280  # ponder
281  if {$::sergame::ponder} {
282  ::sergame::sendToEngine $n "setoption name Ponder value true"
283  } else {
284  ::sergame::sendToEngine $n "setoption name Ponder value false"
285  }
286 
287  # if will follow a specific opening line
288  if {$isOpening} {
289  set fields [split [lindex $openingList $chosenOpening] ":"]
290  set openingName [lindex $fields 0]
291  set openingMoves [string trim [lindex $fields 1]]
292  set openingMovesList ""
293  set openingMovesHash ""
294  set outOfOpening 0
295  foreach m [split $openingMoves] {
296  # in case of multiple adjacent spaces in opening line
297  if {$m =={}} {
298  continue
299  }
300  set p [string trim $m]
301  lappend openingMovesList [string trim [regsub {^[1-9]+\.} $p ""]]
302  }
303 
304  lappend openingMovesHash [sc_pos hash]
305  foreach m $openingMovesList {
306  if {[catch {sc_move addSan $m}]} { }
307  lappend openingMovesHash [sc_pos hash]
308  }
309  }
310 
311  # Engine plays for the upper side
312  if {[::board::isFlipped .main.board]} {
313  set ::sergame::playerColor "black"
314  set ::sergame::engineColor "white"
315  } else {
316  set ::sergame::playerColor "white"
317  set ::sergame::engineColor "black"
318  }
319 
320  if {!$::sergame::startFromCurrent} {
321  # create a new game if a DB is opened
322  sc_game tags set -event "Serious game"
323  sc_game tags set -$::sergame::playerColor "Player"
324  sc_game tags set -$::sergame::engineColor "$::sergame::engineName"
325  sc_game tags set -date [::utils::date::today]
326  }
327 
328  set ::sergame::waitPlayerMove 0
329  set ::sergame::wentOutOfBook 0
330  set ::playMode "::sergame::callback"
332 
333  clocks init $n
334  clocks start
335 
337  }
338 
339  proc callback {cmd} {
340  switch $cmd {
341  stop { ::sergame::abortGame}
342  }
343  return 0
344  }
345 
346  proc abortGame { { n 3 } } {
347  unset ::playMode
348  after cancel ::sergame::engineGo $n
349  clocks stop
350  set ::sergame::lFen {}
351  if { $::uci::uciInfo(pipe$n) != ""} {
353  set ::uci::uciInfo(bestmove$n) "abort"
354  }
356  }
357 
358  proc clocks {cmd {n 3}} {
359  if {$::sergame::timeMode != "timebonus"} { return}
360 
361  switch $cmd {
362  init {
363  ::gameclock::new "" 1
364  ::gameclock::new "" 2
365  ::gameclock::setSec 1 [expr 0 - $::uci::uciInfo(wtime$n)/1000]
366  ::gameclock::setSec 2 [expr 0 - $::uci::uciInfo(btime$n)/1000]
367  }
368  start {
369  if { [sc_pos side] == "white" } {
371  } else {
373  }
374  }
375  stop {
378  }
379  toggle {
380  if {[::gameclock::stop 1]} {
381  ::gameclock::add 1 [expr $::uci::uciInfo(winc$n)/1000]
384  } elseif {[::gameclock::stop 2]} {
385  ::gameclock::add 2 [expr $::uci::uciInfo(binc$n)/1000]
388  }
390  }
391  }
392  }
393 
394  proc takeBack {takebackClockW takebackClockB} {
395  sc_move back 1
396  if {$takebackClockW != ""} {
397  ::gameclock::setSec 1 [expr 0 - $takebackClockW]
398  ::gameclock::setSec 2 [expr 0 - $takebackClockB]
399  clocks start
400  }
402  }
403 
404  ################################################################################
405  #
406  ################################################################################
407  proc sendToEngine {n text} {
408  ::sergame::logEngine $n "Scid : $text"
409  catch {puts $::uci::uciInfo(pipe$n) $text}
410  }
411  ################################################################################
412  # returns true if last move is a mate and stops clocks
413  ################################################################################
414  proc endOfGame {} {
415  set move_done [sc_game info previousMove]
416  if { [string index [sc_game info previousMove] end] == "#"} {
417  clocks stop
418  return 1
419  }
420  return 0
421  }
422  ################################################################################
423  #
424  ################################################################################
425  proc engineGo { n } {
426  global ::sergame::isOpening ::sergame::openingMovesList ::sergame::openingMovesHash ::sergame::openingMoves \
427  ::sergame::timeMode ::sergame::outOfOpening
428 
429  after cancel ::sergame::engineGo $n
430 
431  if { [::sergame::endOfGame] } { return}
432 
433  if { [sc_pos side] != $::sergame::engineColor } {
434  set ::sergame::waitPlayerMove 1
435  after 1000 ::sergame::engineGo $n
436  return
437  }
438 
439  set takebackClockW ""
440  set takebackClockB ""
441  if {$::sergame::waitPlayerMove} {
442  # The player moved
443  set ::sergame::waitPlayerMove 0
444  if {$::sergame::timeMode == "timebonus"} {
445  set takebackClockW [::gameclock::getSec 1]
446  set takebackClockB [::gameclock::getSec 2]
447  clocks toggle $n
448  }
449  repetition
450  }
451 
452  # make a move corresponding to a specific opening, (it is engine's turn)
453  if {$isOpening && !$outOfOpening} {
454  set index 0
455  # Warn if the user went out of the opening line chosen
456  if { !$outOfOpening } {
457  set ply [ expr [sc_pos moveNumber] * 2 - 1]
458  if { [sc_pos side] == "white" } {
459  set ply [expr $ply - 1]
460  }
461 
462  if { [lsearch $openingMovesHash [sc_pos hash]] == -1 && [llength $openingMovesList] >= $ply} {
463  clocks stop
464  set answer [tk_messageBox -icon question -parent .main -title $::tr(OutOfOpening) -type yesno \
465  -message "$::tr(NotFollowedLine) $openingMoves\n $::tr(DoYouWantContinue)"]
466  if {$answer == no} {
467  takeBack $takebackClockW $takebackClockB
468  after 1000 ::sergame::engineGo $n
469  return
470  } else {
471  set outOfOpening 1
472  }
473  clocks start
474  }
475  }
476 
477  set hpos [sc_pos hash]
478  # Find a corresponding position in the opening line
479  set length [llength $openingMovesHash]
480  for {set i 0} { $i < [expr $length-1] } { incr i} {
481  set h [lindex $openingMovesHash $i]
482  if {$h == $hpos} {
483  set index [lsearch $openingMovesHash $h]
484  set move [lindex $openingMovesList $index]
485  # play the move
486  set action "replace"
487  if {![sc_pos isAt vend]} { set action [confirmReplaceMove]}
488  if {$action == "replace"} {
489  if {[catch {sc_move addSan $move}]} {}
490  } elseif {$action == "var"} {
491  sc_var create
492  if {[catch {sc_move addSan $move}]} {}
493  } elseif {$action == "mainline"} {
494  sc_var create
495  if {[catch {sc_move addSan $move}]} {}
496  sc_var promote
497  sc_move forward 1
498  }
499 
500  clocks toggle $n
501  updateBoard -pgn -animate
502  repetition
503  after 1000 ::sergame::engineGo $n
504  return
505  }
506  }
507  }
508  # -------------------------------------------------------------
509  # use a book
510  if {$::sergame::useBook && ! $::sergame::wentOutOfBook} {
511  set move [ ::book::getMove $::sergame::bookToUse [sc_pos fen] $::sergame::bookSlot]
512  if {$move == ""} {
513  set ::sergame::wentOutOfBook 1
514  } else {
515  sc_move addSan $move
517  # we made a book move so assume a score = 0
518  set ::uci::uciInfo(prevscore$n) 0.0
519  clocks toggle $n
520  updateBoard -pgn -animate
521  repetition
522  after 1000 ::sergame::engineGo $n
523  return
524  }
525  }
526  # -------------------------------------------------------------
527  # check if the engine pondered on the right move
528 
529  if { $::sergame::ponder && $::uci::uciInfo(ponder$n) == $::sergame::lastPlayerMoveUci} {
530  ::sergame::sendToEngine $n "ponderhit"
531  } else {
532 
533  if { $::sergame::ponder } {
534  ::sergame::sendToEngine $n "stop"
535  }
536  set ::analysis(waitForReadyOk$n) 1
537  ::sergame::sendToEngine $n "isready"
538  vwait ::analysis(waitForReadyOk$n)
539  ::sergame::sendToEngine $n "position fen [sc_pos fen]"
540  set wtime [expr [::gameclock::getSec 1] * 1000]
541  set btime [expr [::gameclock::getSec 2] * 1000]
542  if {$timeMode == "timebonus"} {
543  ::sergame::sendToEngine $n "go wtime $wtime btime $btime winc $::uci::uciInfo(winc$n) binc $::uci::uciInfo(binc$n)"
544  } elseif {$timeMode == "depth"} {
545  ::sergame::sendToEngine $n "go depth $::uci::uciInfo(fixeddepth$n)"
546  } elseif {$timeMode == "movetime"} {
547  ::sergame::sendToEngine $n "go movetime $::uci::uciInfo(movetime$n)"
548  } elseif {$timeMode == "nodes"} {
549  ::sergame::sendToEngine $n "go nodes $::uci::uciInfo(fixednodes$n)"
550  }
551  }
552 
553  set ::uci::uciInfo(bestmove$n) ""
554  vwait ::uci::uciInfo(bestmove$n)
555 
556  # -------------------------------------------------------------
557  # if weak move detected, propose the user to tack back
558  if { $::sergame::coachIsWatching && $::uci::uciInfo(prevscore$n) != "" } {
559  set blunder 0
560  set delta [expr $::uci::uciInfo(score$n) - $::uci::uciInfo(prevscore$n)]
561  if {$delta > $::informant("?!") && $::sergame::engineColor == "white" ||
562  $delta < [expr 0.0 - $::informant("?!")] && $::sergame::engineColor == "black" } {
563  set blunder 1
564  }
565 
566  if {$delta > $::informant("?") && $::sergame::engineColor == "white" ||
567  $delta < [expr 0.0 - $::informant("?")] && $::sergame::engineColor == "black" } {
568  set blunder 2
569  }
570 
571  if {$delta > $::informant("??") && $::sergame::engineColor == "white" ||
572  $delta < [expr 0.0 - $::informant("??")] && $::sergame::engineColor == "black" } {
573  set blunder 3
574  }
575 
576  if {$blunder == 1} {
577  set tBlunder "DubiousMovePlayedTakeBack"
578  } elseif {$blunder == 2} {
579  set tBlunder "WeakMovePlayedTakeBack"
580  } elseif {$blunder == 3} {
581  set tBlunder "BadMovePlayedTakeBack"
582  }
583 
584  if {$blunder != 0} {
585  clocks stop
586  set answer [tk_messageBox -icon question -parent .main -title "Scid" -type yesno -message $::tr($tBlunder)]
587  if {$answer == yes} {
588  takeBack $takebackClockW $takebackClockB
589  after 1000 ::sergame::engineGo $n
590  return
591  }
592  clocks start
593  }
594  }
595 
596  # -------------------------------------------------------------
597  if { $::uci::uciInfo(bestmove$n) == "abort" } {
598  return
599  }
600 
601  ::uci::sc_move_add $::uci::uciInfo(bestmove$n)
602  ::utils::sound::AnnounceNewMove $::uci::uciInfo(bestmove$n)
603  set ::uci::uciInfo(prevscore$n) $::uci::uciInfo(score$n)
604  if { $::sergame::storeEval == 1 } {
605  storeEvalComment $::uci::uciInfo(score$n)
606  }
607  updateBoard -pgn -animate
608  repetition
609 
610  clocks toggle $n
611 
612  # ponder mode (the engine just played its move)
613  if {$::sergame::ponder && $::uci::uciInfo(ponder$n) != ""} {
614  ::sergame::sendToEngine $n "position fen [sc_pos fen] moves $::uci::uciInfo(ponder$n)"
615  set wtime [expr [::gameclock::getSec 1] * 1000]
616  set btime [expr [::gameclock::getSec 2] * 1000]
617  if {$timeMode == "timebonus"} {
618  ::sergame::sendToEngine $n "go ponder wtime $wtime btime $btime winc $::uci::uciInfo(winc$n) binc $::uci::uciInfo(binc$n)"
619  } elseif {$timeMode == "depth"} {
620  ::sergame::sendToEngine $n "go ponder depth $::uci::uciInfo(fixeddepth$n)"
621  } elseif {$timeMode == "movetime"} {
622  ::sergame::sendToEngine $n "go ponder movetime $::uci::uciInfo(movetime$n)"
623  } elseif {$timeMode == "nodes"} {
624  ::sergame::sendToEngine $n "go ponder nodes $::uci::uciInfo(fixednodes$n)"
625  }
626  }
627 
628  after 1000 ::sergame::engineGo $n
629  }
630  ################################################################################
631  # add current position for 3fold repetition detection and returns 1 if
632  # the position is a repetition
633  ################################################################################
634  proc repetition {} {
635  set elt [lrange [split [sc_pos fen]] 0 2]
636  # append the position only if different from the last element
637  if { $elt != [ lindex $::sergame::lFen end] } {
638  lappend ::sergame::lFen $elt
639  }
640 
641  if { [llength [lsearch -all $::sergame::lFen $elt]] >=3 } {
642  tk_messageBox -type ok -message $::tr(Draw) -parent .main -icon info
643  puts $::sergame::lFen
644  return 1
645  }
646  return 0
647  }
648  ################################################################################
649  #
650  ################################################################################
651  proc logEngine {n text} {
652  if {$::uci::uciInfo(log_stdout$n)} {
653  puts stdout "$n $text"
654  }
655  }
656 
657 }
658 ###
659 ### End of file: sergame.tcl
660 ###