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]