10 variable newOptions {}
19 set autoSaveOptionsIndex -1
22 set infoToken { depth seldepth time nodes pv multipv score cp mate lowerbound upperbound \
23 currmove currmovenumber hashfull nps tbhits sbhits cpuload string refutation currline }
24 set optionToken {name type default min max var }
25 set optionImportant { MultiPV Hash OwnBook BookFile UCI_LimitStrength UCI_Elo }
26 set optionToKeep { UCI_LimitStrength UCI_Elo UCI_ShredderbasesPath }
31 proc resetUciInfo { { n 1 }} {
33 set uciInfo(depth$n) 0
34 set uciInfo(seldepth$n) 0
36 set uciInfo(nodes$n) 0
38 set uciInfo(multipv$n) ""
41 set uciInfo(tmp_score$n) ""
42 set uciInfo(scoremate$n) 0
43 set uciInfo(currmove$n) ""
44 set uciInfo(currmovenumber$n) 0
45 set uciInfo(hashfull$n) 0
47 set uciInfo(tbhits$n) 0
48 set uciInfo(sbhits$n) 0
49 set uciInfo(cpuload$n) 0
50 set uciInfo(string$n) ""
51 set uciInfo(refutation$n) ""
52 set uciInfo(currline$n) ""
59 proc processAnalysisInput { { n 1 } { analyze 1 } } {
60 global analysis ::uci::uciInfo
63 set pipe $analysis(pipe$n)
66 set analysis(fen$n) ""
67 set pipe $uciInfo(pipe$n)
72 if {! $analysis(seen$n)} {
73 set analysis(seen$n) 1
74 logEngineNote $n {First line from engine seen; sending it initial commands now.}
79 if {! $uciInfo(seen$n)} {
81 logEngineNote $n {First line from engine seen; sending it initial commands now.}
86 after idle "after 1 ::uci::processInput_ $n $analyze"
87 fileevent $pipe readable {}
90 proc processInput_ { {n} {analyze} } {
91 global analysis ::uci::uciInfo ::uci::infoToken ::uci::optionToken
94 set pipe $analysis(pipe$n)
97 set analysis(fen$n) ""
98 set pipe $uciInfo(pipe$n)
103 set line [
gets $pipe]
105 fileevent $pipe readable "::uci::processAnalysisInput $n $analyze"
109 after idle "after 1 ::uci::processInput_ $n $analyze"
118 if {[
string match "bestmove*" $line]} {
119 set data [
split $line]
120 set uciInfo(bestmove$n) [
lindex $data 1]
122 if {[
lindex $data 2] == "ponder"} {
123 set uciInfo(ponder$n) [
lindex $data 3]
125 set uciInfo(ponder$n) ""
127 set analysis(waitForBestMove$n) 0
131 if {[
string match "id *name *" $line]} {
132 set name [ regsub {id[ ]?name[ ]?} $line ""]
134 set analysis(name$n) $name
136 set uciInfo(name$n) $name
140 catch {
wm title .analysisWin$n "Scid: Analysis: $name"}
142 catch {
wm title .analysisWin$n "Scid: Analysis $n: $name"}
148 if {[
string first "info" $line] == 0} {
149 if {$analysis(waitForReadyOk$n)} {
return}
151 set data [
split $line]
152 set length [
llength $data]
153 for {
set i 0} {$i < $length } {
incr i} {
154 set t [
lindex $data $i]
155 if { $t == "info" } { continue}
156 if { $t == "depth" } {
incr i
set uciInfo(depth$n) [
lindex $data $i] continue}
157 if { $t == "seldepth" } {
incr i
set uciInfo(seldepth$n) [
lindex $data $i]
set analysis(seldepth$n) $uciInfo(seldepth$n) continue}
158 if { $t == "time" } {
incr i
set uciInfo(time$n) [
lindex $data $i] continue}
159 if { $t == "nodes" } {
incr i
set uciInfo(nodes$n) [
lindex $data $i] continue}
162 set uciInfo(pv$n) [
lindex $data $i]
164 while { [ lsearch -exact $infoToken [
lindex $data $i]] == -1 && $i < $length } {
165 append uciInfo(pv$n) " " [
lindex $data $i]
172 if { $t == "multipv" } {
incr i
set uciInfo(multipv$n) [
lindex $data $i] continue}
173 if { $t == "score" } {
175 set next [
lindex $data $i]
177 if { $next != "cp" && $next != "mate" } {
180 if { $next == "cp" } {
182 set uciInfo(tmp_score$n) [
lindex $data $i]
184 if { $next == "mate" } {
186 set next [
lindex $data $i]
187 set uciInfo(scoremate$n) $next
189 set uciInfo(tmp_score$n) [
expr {-32767 - 2 * $next}]
191 set uciInfo(tmp_score$n) [
expr {32767 - 2 * $next}]
195 if { $analysis(fen$n) == "" } {
196 set side [
string index [
sc_pos side] 0]
198 set side [
lindex [
split $analysis(fen$n)] 1]
201 set uciInfo(tmp_score$n) [
expr 0.0 - $uciInfo(tmp_score$n)]
202 if { $uciInfo(scoremate$n) } {
203 set uciInfo(scoremate$n) [
expr 0 - $uciInfo(scoremate$n)]
204 if { $uciInfo(tmp_score$n) < 0 } {
205 set uciInfo(tmp_score$n) [
expr {$uciInfo(tmp_score$n) - 1.0}]
208 }
elseif { $uciInfo(scoremate$n) && $uciInfo(tmp_score$n) > 0 } {
209 set uciInfo(tmp_score$n) [
expr {$uciInfo(tmp_score$n) + 1.0}]
211 set uciInfo(tmp_score$n) [
expr {double($uciInfo(tmp_score$n)) / 100.0}]
216 if { $t == "currmove" } {
incr i
set uciInfo(currmove$n) [
lindex $data $i]
set analysis(currmove$n) [
formatPv $uciInfo(currmove$n) $analysis(fen$n)] continue}
217 if { $t == "currmovenumber" } {
incr i
set uciInfo(currmovenumber$n) [
lindex $data $i]
set analysis(currmovenumber$n) $uciInfo(currmovenumber$n) continue}
218 if { $t == "hashfull" } {
incr i
set uciInfo(hashfull$n) [
lindex $data $i]
set analysis(hashfull$n) $uciInfo(hashfull$n) continue}
219 if { $t == "nps" } {
incr i
set uciInfo(nps$n) [
lindex $data $i]
set analysis(nps$n) $uciInfo(nps$n) continue}
220 if { $t == "tbhits" } {
incr i
set uciInfo(tbhits$n) [
lindex $data $i]
set analysis(tbhits$n) $uciInfo(tbhits$n) continue}
221 if { $t == "sbhits" } {
incr i
set uciInfo(sbhits$n) [
lindex $data $i]
set analysis(sbhits$n) $uciInfo(sbhits$n) continue}
222 if { $t == "cpuload" } {
incr i
set uciInfo(cpuload$n) [
lindex $data $i]
set analysis(cpuload$n) $uciInfo(cpuload$n) continue}
223 if { $t == "string" } {
225 while { $i < $length } {
226 append uciInfo(string$n) [
lindex $data $i] " "
232 if { $t == "refutation" } { continue}
233 if { $t == "currline" } { continue}
237 if { $uciInfo(tmp_score$n) == "" || $uciInfo(pv$n) == "" } {
245 if { $uciInfo(multipv$n) == "" } {
set uciInfo(multipv$n) 1}
247 if { $uciInfo(multipv$n) == 1 } {
248 set uciInfo(score$n) $uciInfo(tmp_score$n)
251 if { $uciInfo(multipv$n) == 1 && $analyze} {
253 set analysis(prev_depth$n) $analysis(depth$n)
254 set analysis(depth$n) $uciInfo(depth$n)
255 set analysis(score$n) $uciInfo(score$n)
256 set analysis(scoremate$n) $uciInfo(scoremate$n)
257 set analysis(moves$n) $uciInfo(pv$n)
258 set analysis(time$n) [
expr {double($uciInfo(time$n)) / 1000.0}]
262 set pvRaw $uciInfo(pv$n)
265 if ($toBeFormatted) {
266 set uciInfo(pv$n) [
formatPv $uciInfo(pv$n) $analysis(fen$n)]
270 set idx [
expr $uciInfo(multipv$n) -1]
273 if { $idx < $analysis(multiPVCount$n) } {
274 set tmpTime [
expr {double($uciInfo(time$n)) / 1000.0}]
275 if {$idx < [
llength $analysis(multiPV$n)]} {
276 lset analysis(multiPV$n) $idx "$uciInfo(depth$n) $uciInfo(tmp_score$n) [list $uciInfo(pv$n)] $uciInfo(scoremate$n) $tmpTime"
277 lset analysis(multiPVraw$n) $idx "$uciInfo(depth$n) $uciInfo(tmp_score$n) [list $pvRaw] $uciInfo(scoremate$n) $tmpTime"
279 lappend analysis(multiPV$n) "$uciInfo(depth$n) $uciInfo(tmp_score$n) [list $uciInfo(pv$n)] $uciInfo(scoremate$n) $tmpTime"
280 lappend analysis(multiPVraw$n) "$uciInfo(depth$n) $uciInfo(tmp_score$n) [list $pvRaw] $uciInfo(scoremate$n) $tmpTime"
287 if { $line == "uciok"} {
290 set analysis(uciok$n) 1
292 set uciInfo(uciok$n) 1
294 if {$analysis(onUciOk$n) != ""} { {*}$analysis(onUciOk$n)}
298 if { $line == "readyok"} {
299 set analysis(waitForReadyOk$n) 0
304 if { [
string first "option name" $line] == 0 && $analyze } {
305 set min ""
set max ""
306 set data [
split $line]
307 set length [
llength $data]
308 for {
set i 0} {$i < $length} {
incr i} {
309 set t [
lindex $data $i]
312 set name [
lindex $data $i]
314 while { [ lsearch -exact $optionToken [
lindex $data $i]] == -1 && $i < $length } {
315 append name " " [
lindex $data $i]
321 if {$t == "min"} {
incr i
set min [
lindex $data $i] continue}
322 if {$t == "max"} {
incr i
set max [
lindex $data $i] continue}
324 lappend analysis(uciOptions$n) [ list $name $min $max]
334 global ::uci::uciOptions
336 set line [
string trim [
gets $::uci::uciInfo(pipe$n)]]
338 if {$line == "uciok"} {
344 if { [
string first "option name" $line] == 0 } {
345 lappend uciOptions $line
352 proc uciConfig { n cmd arg dir options } {
353 global ::uci::uciOptions ::uci::oldOptions
355 if {[
info exists ::uci::uciInfo(pipe$n)]} {
356 if {$::uci::uciInfo(pipe$n) != ""} {
357 tk_messageBox -title "Scid" -icon warning -type ok -message "An engine is already running"
361 set oldOptions $options
370 if {[
catch {
set pipe [open "| [list $cmd] $arg" "r+"]} result]} {
371 if {$oldpwd != ""} {
catch {
cd $oldpwd}}
372 tk_messageBox -title "Scid: error starting UCI engine" \
373 -icon warning -type ok -message "Unable to start the program:\n$cmd"
377 set ::uci::uciInfo(pipe$n) $pipe
380 fconfigure $pipe -buffering full -blocking 0
381 fileevent $pipe readable "::uci::readUCI $n"
384 if {$oldpwd != ""} {
catch {
cd $oldpwd}}
393 after 5000 "::uci::closeUCIengine $n 0"
399 proc uciConfigWin {} {
400 global ::uci::uciOptions ::uci::optList ::uci::optionToken ::uci::oldOptions ::uci::optionImportant
403 if { [
winfo exists $w]} {
return}
405 wm title $w $::tr(ConfigureUCIengine)
407 autoscrollframe -bars both $w canvas $w.c -highlightthickness 0 -background [ttk::style lookup Button.label -background]
408 bind $w.c <Configure> {
409 set l [winfo reqwidth %W.f]
410 set h [winfo reqheight %W.f]
411 %W configure -scrollregion [list 0 0 $l $h] -width $l -height $h
413 grid [ttk::frame $w.c.f]
414 $w.c create window 0 0 -window $w.c.f -anchor nw
418 foreach tokeep $::uci::optionToKeep {
419 if { [lsearch $opt $tokeep] != -1 } {
428 foreach opt $uciOptions {
429 set elt(name) ""
set elt(type) ""
set elt(default) ""
set elt(min) ""
set elt(max) ""
set elt(var) ""
430 set data [
split $opt]
434 if { ![
tokeep $opt] && ( [ lsearch -glob $data "UCI_*"] != -1 || [ lsearch $data "Ponder"] != -1 ) } {
438 set length [
llength $data]
440 for {
set i 0} {$i < $length} {
incr i} {
441 set t [
lindex $data $i]
442 if {$t == "option"} { continue}
445 set elt(name) [
lindex $data $i]
447 while { [ lsearch -exact $optionToken [
lindex $data $i]] == -1 && $i < $length } {
448 append elt(name) " " [
lindex $data $i]
454 if {$t == "type"} {
incr i
set elt(type) [
lindex $data $i] continue}
455 if {$t == "default"} { ;
457 set elt(default) [
lindex $data $i]
459 while { [ lsearch -exact $optionToken [
lindex $data $i]] == -1 && $i < $length } {
460 append elt(default) " " [
lindex $data $i]
466 if {$t == "min"} {
incr i
set elt(min) [
lindex $data $i] continue}
467 if {$t == "max"} {
incr i
set elt(max) [
lindex $data $i] continue}
470 set tmp [
lindex $data $i]
472 while { ([ lsearch -exact $optionToken [
lindex $data $i]] == -1 && $i < $length ) \
473 || [
lindex $data $i] == "var" } {
474 if {[
lindex $data $i] != "var" } {
475 append tmp " " [
lindex $data $i]
477 lappend elt(var) [list $tmp]
479 set tmp [
lindex $data $i]
483 lappend elt(var) [list $tmp]
489 lappend optList [
array get elt]
497 if { [ lsearch $optionImportant $elt(name)] != -1 } {
503 if { [ lsearch $optionImportant $elt(name)] == -1 } {
510 ttk::frame $w.fbuttons
514 set isImportantParam 1
518 if { [ lsearch $optionImportant $elt(name)] == -1 && $isImportantParam } {
519 set isImportantParam 0
523 if {$elt(name) == "MultiPV"} {
set name $::tr(MultiPV)}
524 if {$elt(name) == "Hash"} {
set name $::tr(Hash)}
525 if {$elt(name) == "OwnBook"} {
set name $::tr(OwnBook)}
526 if {$elt(name) == "BookFile"} {
set name $::tr(BookFile)}
527 if {$elt(name) == "UCI_LimitStrength"} {
set name $::tr(LimitELO)}
529 if { $col > 3 } {
set col 0
incr row}
530 if {$elt(default) != ""} {
531 set default "\n($elt(default))"
535 set value $elt(default)
537 foreach old $oldOptions {
538 if {[
lindex $old 0] == $elt(name)} {
539 set value [
lindex $old 1]
543 if { $elt(type) == "check"} {
544 ttk::checkbutton $w.fopt.opt$optnbr -text "$name$default" -onvalue true -offvalue false -variable ::uci::check($optnbr)
545 set ::uci::check($optnbr) $value
546 grid $w.fopt.opt$optnbr -row $row -column $col -sticky w
548 if { $elt(type) == "spin"} {
549 ttk::label $w.fopt.label$optnbr -text "$name$default"
550 if { $elt(name) == "UCI_Elo" } {
551 ttk::spinbox $w.fopt.opt$optnbr -from $elt(min) -to $elt(max) -width 5 -increment 50 -validate all -validatecommand { regexp {^[0-9]+$} %P }
553 ttk::spinbox $w.fopt.opt$optnbr -from $elt(min) -to $elt(max) -width 5 -validate all -validatecommand { regexp {^[0-9]+$} %P }
555 $w.fopt.opt$optnbr set $value
556 grid $w.fopt.label$optnbr -row $row -column $col -sticky e
558 grid $w.fopt.opt$optnbr -row $row -column $col -sticky w
560 if { $elt(type) == "combo"} {
561 ttk::label $w.fopt.label$optnbr -text "$name$default"
565 foreach e $elt(var) {
566 lappend tmp [
join $e]
567 if {[
join $e] == $value} {
set idx $i}
570 ttk::combobox $w.fopt.opt$optnbr -values $tmp
572 $w.fopt.opt$optnbr current $idx
573 grid $w.fopt.label$optnbr -row $row -column $col -sticky e
575 grid $w.fopt.opt$optnbr -row $row -column $col -sticky w
577 if { $elt(type) == "button"} {
578 ttk::button $w.fopt.opt$optnbr -text "$name$default"
579 grid $w.fopt.opt$optnbr -row $row -column $col -sticky w
581 if { $elt(type) == "string"} {
582 ttk::label $w.fopt.label$optnbr -text "$name$default"
583 ttk::entry $w.fopt.opt$optnbr
584 $w.fopt.opt$optnbr insert 0 $value
585 grid $w.fopt.label$optnbr -row $row -column $col -sticky e
587 grid $w.fopt.opt$optnbr -row $row -column $col -sticky w
593 ttk::button $w.fbuttons.save -text $::tr(Save) -command {
595 destroy .uciConfigWin
597 ttk::button $w.fbuttons.cancel -text $::tr(Cancel) -command "destroy .uciConfigWin"
598 pack $w.fbuttons.save $w.fbuttons.cancel -side left -expand yes -fill x -padx 20 -pady 2
599 pack $w.fopt -expand 1 -fill both
601 pack $w.fbuttons -expand 1 -fill both
602 bind $w <Return> "$w.fbuttons.save invoke"
603 bind $w <Escape> "destroy .uciConfigWin"
604 catch {grab .uciConfigWin}
610 global ::uci::optList ::uci::newOptions
612 set w .uciConfigWin.c.f
618 if { $elt(type) == "check"} {
619 set value $::uci::check($optnbr)
621 if { $elt(type) == "spin" || $elt(type) == "combo" || $elt(type) == "string" } {
622 set value [$w.fopt.opt$optnbr get]
624 if { $elt(type) != "button" } {
625 lappend newOptions [ list $elt(name) $value]
629 if { $::uci::autoSaveOptions } {
631 set ::uci::autoSaveOptions 0
638 proc writeOptions {} {
639 set elt [
lindex $::engines(list) $::uci::autoSaveOptionsIndex]
640 set elt [
lreplace $elt 8 8 $::uci::newOptions]
641 set ::engines(list) [
lreplace $::engines(list) $::uci::autoSaveOptionsIndex $::uci::autoSaveOptionsIndex $elt]
649 proc sendUCIoptions { n uci_options} {
651 foreach opt $uci_options {
652 set name [
lindex $opt 0]
653 set value [
lindex $opt 1]
654 set analysis(waitForReadyOk$n) 1
656 vwait analysis(waitForReadyOk$n)
658 if { $name == "MultiPV" } {
set analysis(multiPVCount$n) $value}
664 proc startEngine {index n} {
665 global ::uci::uciInfo
667 set uciInfo(pipe$n) ""
668 set uciInfo(seen$n) 0
669 set uciInfo(uciok$n) 0
671 set engineData [
lindex $::engines(list) $index]
672 set analysisName [
lindex $engineData 0]
673 set analysisCommand [
toAbsPath [
lindex $engineData 1]]
674 set analysisArgs [
lindex $engineData 2]
675 set analysisDir [
toAbsPath [
lindex $engineData 3]]
679 if {$analysisDir != "."} {
681 catch {
cd $analysisDir}
685 if {[
catch {
set uciInfo(pipe$n) [open "| [list $analysisCommand] $analysisArgs" "r+"]} result]} {
686 if {$oldpwd != ""} {
catch {
cd $oldpwd}}
687 tk_messageBox -title "Scid: error starting engine" -icon warning -type ok \
688 -message "Unable to start the program:\n$analysisCommand"
692 set ::analysis(index$n) $index
693 set ::analysis(pipe$n) $uciInfo(pipe$n)
696 if {$oldpwd != ""} {
catch {
cd $oldpwd}}
698 fconfigure $uciInfo(pipe$n) -buffering line -blocking 0
699 fileevent $uciInfo(pipe$n) readable "::uci::processAnalysisInput $n 0"
703 while {! $::uci::uciInfo(uciok$n) && $counter < 50 } {
713 proc sendToEngine {n text} {
715 catch {
puts $::uci::uciInfo(pipe$n) $text}
720 proc checkEngineIsAlive { n } {
721 global ::uci::uciInfo
722 if { $uciInfo(pipe$n) == "" } {
return 0}
723 if {[
eof $uciInfo(pipe$n)]} {
724 fileevent $uciInfo(pipe$n) readable {}
726 if {[
catch {
close $uciInfo(pipe$n)} standard_error] != 0} {
728 if {"CHILDSTATUS" == [
lindex $errorCode 0]} {
729 set exit_status [
lindex $errorCode 2]
732 set uciInfo(pipe$n) ""
733 if { $exit_status != 0 } {
734 logEngineNote $n {Engine terminated with exit code $exit_status: "\"$standard_error\""}
735 tk_messageBox -type ok -icon info -parent . -title "Scid" \
736 -message "The analysis engine terminated with exit code $exit_status: \"$standard_error\""
738 logEngineNote $n {Engine terminated without exit code: "\"$standard_error\""}
739 tk_messageBox -type ok -icon info -parent . -title "Scid" \
740 -message "The analysis engine terminated without exist code: \"$standard_error\""
750 proc closeUCIengine { n { uciok 1 } } {
751 global windowsOS ::uci::uciInfo
753 set pipe $uciInfo(pipe$n)
755 if {$pipe == ""} {
return}
757 after cancel "::uci::closeUCIengine $n 0"
758 fileevent $pipe readable {}
761 tk_messageBox -title "Scid: error closing UCI engine" \
762 -icon warning -type ok -message "Not an UCI engine"
767 catch {
puts $pipe "stop"
puts $pipe "quit"}
769 catch {
puts $pipe "exit"
puts $pipe "quit"}
780 set uciInfo(pipe$n) ""
786 proc sc_move_add { moves } {
790 set c [
string index $m 0]
791 if {$c == "K" || $c == "Q" || $c == "R" || $c == "B" || $c == "N"} {
792 set m [
string range $m 1 end]
794 set s1 [
string range $m 0 1]
796 set s2 [
string range $m 2 3]
798 if {[
string length $m] > 4} {
799 set promo [
string range $m 4 end]
807 default {
puts "Promo error $promo for moves $moves"}
809 if { [
catch {
sc_move add $s1 $s2 $p}] } {
return 1}
811 if { [
catch {
sc_move add $s1 $s2 0}] } {
return 1}
819 proc formatPv { moves { fen "" } } {
830 set prev [
sc_game info previousMoveNT]
833 set tmp [
string trim $tmp]
843 proc formatPvAfterMoves { played_moves moves } {
852 set prev [
sc_game info previousMoveNT]
855 set tmp [
string trim $tmp]