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