>----Messaggio originale----
>Da: stevena...@yahoo.com
>Data: 20/02/2011 2.37
>A: "Scid Users List"<scid-users@lists.sourceforge.net>
>Ogg: Re: [Scid-users] I hate to wait [PATCH]
>
>Hmmm... my idea is interesting, but it needs some work.

multipv is difficult to handle too.
The attached uci.tcl show what i had in mind.
However I think that Joost is right: a really fast procedure can't be written 
in tcl.
Bye,
Fulvio
###
### uci.tcl: part of Scid.
### Copyright (C) 2007  Pascal Georges
###
######################################################################
### add UCI engine support

namespace eval uci {
    # will contain the UCI engine options saved
    variable newOptions {}
    
    # set pipe ""
    set uciOptions {}
    set optList {}
    set oldOptions ""
    array set check ""
    
    set autoSaveOptions 0 ; # UCI options are saved as soon as the options dialog is closed
    set autoSaveOptionsIndex -1
    
    # The list of token that comes with info
    set infoToken { depth seldepth time nodes pv multipv score cp mate lowerbound upperbound \
                currmove currmovenumber hashfull nps tbhits sbhits cpuload string refutation currline }
    set optionToken {name type default min max var }
    set optionImportant { MultiPV Hash OwnBook BookFile UCI_LimitStrength UCI_Elo }
    set optionToKeep { UCI_LimitStrength UCI_Elo UCI_ShredderbasesPath }
    array set uciInfo {}
    ################################################################################
    #
    ################################################################################
    proc resetUciInfo { { n 1 }} {
        global ::uci::uciInfo
        set uciInfo(depth$n) 0
        set uciInfo(seldepth$n) 0
        set uciInfo(time$n) 0
        set uciInfo(nodes$n) 0
        set uciInfo(pv$n) ""
        set uciInfo(multipv$n) ""
        # set uciInfo(pvlist$n) {}
        # set uciInfo(score$n) ""
        set uciInfo(tmp_score$n) ""
        set uciInfo(scoremate$n) 0
        set uciInfo(currmove$n) ""
        set uciInfo(currmovenumber$n) 0
        set uciInfo(hashfull$n) 0
        set uciInfo(nps$n) 0
        set uciInfo(tbhits$n) 0
        set uciInfo(sbhits$n) 0
        set uciInfo(cpuload$n) 0
        set uciInfo(string$n) ""
        set uciInfo(refutation$n) ""
        set uciInfo(currline$n) ""
        # set uciInfo(bestmove$n) ""
    }
    ################################################################################
    # if analyze = 0 -> engine mode
    # if analyze = 1 -> analysis mode
    ################################################################################
    proc  processAnalysisInput { { n 1 } { analyze 1 } } {
        global analysis ::uci::uciInfo ::uci::infoToken ::uci::optionToken
        
        if {$analyze} {
            set pipe $analysis(pipe$n)
            if { ! [ ::checkEngineIsAlive $n ] } { return }
        } else  {
            set analysis(fen$n) ""
            set pipe $uciInfo(pipe$n)
            if { ! [ ::uci::checkEngineIsAlive $n ] } { return }
        }
        
        if {$analyze} {
            if {! $analysis(seen$n)} {
                set analysis(seen$n) 1
                logEngineNote $n {First line from engine seen; sending it initial commands now.}
                # in order to get options, engine should end reply with "uciok"
                ::sendToEngine $n "uci"
            }
        } else  {
            if {! $uciInfo(seen$n)} {
                set uciInfo(seen$n) 1
                logEngineNote $n {First line from engine seen; sending it initial commands now.}
                ::uci::sendToEngine $n "uci"
            }
        }

        set cur_depth 0
        set info_n 0
        while {1} {
            # Get one line from the engine:
            set line [gets $pipe]
            if {$line == ""} { break }

            logEngine $n "Engine: $line"

            if {[string first "info" $line ] == 0} {
                if {$analysis(waitForReadyOk$n)} { continue }
            
                set search_depth [string first {depth } $line]
                if {$search_depth != -1} {
                    set depth [string range $line [expr $search_depth + 6] end]
                    set next_space [string first { } $depth]
                    if {$next_space != -1} {
                        incr next_space -1
                        set depth [string range $depth 0 $next_space]
                    }
                    if {$depth > [expr $cur_depth +1]} {
                        if {$info_n > 0} { logEngine $n "Scid: discarded $info_n infos" }
                        set cur_depth $depth
                        set info_n 0
				    }
			    }
                incr info_n
			    set info_lines($info_n) $line
                continue
            }

            if {[string match "bestmove*" $line]} {
                set data [split $line]
                set uciInfo(bestmove$n) [lindex $data 1]
                # get ponder move
                if {[lindex $data 2] == "ponder"} {
                    set uciInfo(ponder$n) [lindex $data 3]
                } else {
                    set uciInfo(ponder$n) ""
                }
                set analysis(waitForBestMove$n) 0
                continue
            }

            # the UCI engine answers to <isready> command
            if { $line == "readyok"} {
                set analysis(waitForReadyOk$n) 0
                continue
            }

            # the UCI engine answers to <uci> command
            if { $line == "uciok"} {
                set analysis(waitForUciOk$n) 0
                resetUciInfo $n
                sendUCIoptions $n
                if {$analyze} {
                    set analysis(uciok$n) 1
                    startAnalyzeMode $n
                } else {
                    set uciInfo(uciok$n) 1
                }
                continue
            }

            # get options and save only part of data
            if { [string first "option name" $line] == 0 && $analyze } {
                set min "" ; set max ""
                set data [split $line]
                set length [llength $data]
                for {set i 0} {$i < $length} {incr i} {
                    set t [lindex $data $i]
                    if {$t == "name"} {
                        incr i
                        set name [ lindex $data $i ]
                        incr i
                        while { [ lsearch -exact $optionToken [ lindex $data $i ] ] == -1 && $i < $length } {
                            append name " " [ lindex $data $i ]
                            incr i
                        }
                        incr i -1
                        continue
                    }
                    if {$t == "min"} { incr i ; set min [ lindex $data $i ] ; continue }
                    if {$t == "max"} {incr i ; set max [ lindex $data $i ] ; continue }
                }
                lappend analysis(uciOptions$n) [ list $name $min $max ]
                continue
            }

            if {[string match "id *name *" $line]} {
                set name [ regsub {id[ ]?name[ ]?} $line "" ]
                if {$analyze} {
                    set analysis(name$n) $name
                } else  {
                    set uciInfo(name$n) $name
                }
            
                if {$n == 1} {
                    catch {wm title .analysisWin$n "Scid: Analysis: $name"}
                } else {
                    catch {wm title .analysisWin$n "Scid: Analysis $n: $name"}
                }
            }
        }

        if {$info_n == 0 || $analysis(waitForReadyOk$n)} { return }

        # parse info lines
        resetUciInfo $n        
        set toBeFormatted 0
        for {set i_info 1} {$i_info <= $info_n} {incr i_info} {
            set data [split $info_lines($i_info)]
            set length [llength $data]
            for {set i 0} {$i < $length } {incr i} {
                set t [lindex $data $i]
                if { $t == "info" } { continue }
                if { $t == "depth" } { incr i ; set uciInfo(depth$n) [ lindex $data $i ] ; continue }
                if { $t == "seldepth" } { incr i ; set uciInfo(seldepth$n) [ lindex $data $i ] ; set analysis(seldepth$n) $uciInfo(seldepth$n) ; continue }
                if { $t == "time" } { incr i ; set uciInfo(time$n) [ lindex $data $i ] ; continue }
                if { $t == "nodes" } { incr i ; set uciInfo(nodes$n) [ lindex $data $i ] ; continue }
                if { $t == "pv" } {
                    incr i
                    set uciInfo(pv$n) [ lindex $data $i ]
                    incr i
                    while { [ lsearch -exact $infoToken [ lindex $data $i ] ] == -1 && $i < $length } {
                        append uciInfo(pv$n) " " [ lindex $data $i ]
                        incr i
                    }
                    set toBeFormatted 1
                    incr i -1
                    continue
                }
                if { $t == "multipv" } { incr i ; set uciInfo(multipv$n) [ lindex $data $i ] ; continue }
                if { $t == "score" } {
                    incr i
                    set next [ lindex $data $i ]
                    # Needed for Prodeo, which is not UCI compliant
                    if { $next != "cp" && $next != "mate" } {
                        return
                    }
                    if { $next == "cp" } {
                        incr i
                        set uciInfo(tmp_score$n) [ lindex $data $i ]
                    }
                    if { $next == "mate" } {
                        incr i
                        set next [ lindex $data $i ]
                        set uciInfo(scoremate$n) $next
                        if { $next < 0} {
                            set uciInfo(tmp_score$n) [expr {-32767 - 2 * $next}]
                        } else  {
                            set uciInfo(tmp_score$n) [expr {32767 - 2 * $next}]
                        }
                    }
                    # convert the score to white's perspective (not engine's one)
                    if { $analysis(fen$n) == "" } {
                        set side [string index [sc_pos side] 0]
                    } else {
                        set side [lindex [split $analysis(fen$n)] 1]
                    }
                    if { $side == "b"} {
                        set uciInfo(tmp_score$n) [ expr 0.0 - $uciInfo(tmp_score$n) ]
                        if { $uciInfo(scoremate$n) } {
                            set uciInfo(scoremate$n) [ expr 0 - $uciInfo(scoremate$n) ]
                            if { $uciInfo(tmp_score$n) < 0 } {
                                set uciInfo(tmp_score$n) [ expr {$uciInfo(tmp_score$n) - 1.0} ]
                            }
                        }
                    } elseif { $uciInfo(scoremate$n) && $uciInfo(tmp_score$n) > 0 } {
                        set uciInfo(tmp_score$n) [ expr {$uciInfo(tmp_score$n) + 1.0} ]
                    }
                    set uciInfo(tmp_score$n) [expr {double($uciInfo(tmp_score$n)) / 100.0} ]
                    
                    # don't consider lowerbound & upperbound score info
                    continue
                }
                if { $t == "currmove" } { incr i ; set uciInfo(currmove$n) [ lindex $data $i ] ; set analysis(currmove$n) [formatPv $uciInfo(currmove$n) $analysis(fen$n)] ; continue}
                if { $t == "currmovenumber" } { incr i ; set uciInfo(currmovenumber$n) [ lindex $data $i ] ; set analysis(currmovenumber$n) $uciInfo(currmovenumber$n) ; continue}
                if { $t == "hashfull" } { incr i ; set uciInfo(hashfull$n) [ lindex $data $i ] ; set analysis(hashfull$n) $uciInfo(hashfull$n) ; continue}
                if { $t == "nps" } { incr i ; set uciInfo(nps$n) [ lindex $data $i ] ; set analysis(nps$n) $uciInfo(nps$n) ; continue}
                if { $t == "tbhits" } { incr i ; set uciInfo(tbhits$n) [ lindex $data $i ] ; set analysis(tbhits$n) $uciInfo(tbhits$n) ; continue}
                if { $t == "sbhits" } { incr i ; set uciInfo(sbhits$n) [ lindex $data $i ] ; set analysis(sbhits$n) $uciInfo(sbhits$n) ; continue}
                if { $t == "cpuload" } { incr i ; set uciInfo(cpuload$n) [ lindex $data $i ] ; set analysis(cpuload$n) $uciInfo(cpuload$n) ; continue}
                if { $t == "string" } {
                    incr i
                    while { $i < $length } {
                        append uciInfo(string$n) [ lindex $data $i ] " "
                        incr i
                    }
                    break
                }
                # TODO parse following tokens if necessary  : refutation currline
                if { $t == "refutation" } { continue }
                if { $t == "currline" } { continue }
            };# end for data loop
        }
                
        if { $uciInfo(tmp_score$n) != "" && $uciInfo(pv$n) != "" } {            
            # handle the case an UCI engine does not send multiPV
            if { $uciInfo(multipv$n) == "" } { set uciInfo(multipv$n) 1 }
            
            if { $uciInfo(multipv$n) == 1 } {
                set uciInfo(score$n) $uciInfo(tmp_score$n)
            }
            
            if { $uciInfo(multipv$n) == 1 && $analyze} {
                # this is the best line
                set analysis(prev_depth$n) $analysis(depth$n)
                set analysis(depth$n) $uciInfo(depth$n)
                set analysis(score$n) $uciInfo(score$n)
                set analysis(scoremate$n) $uciInfo(scoremate$n)
                set analysis(moves$n) $uciInfo(pv$n)
                set analysis(time$n) [expr {double($uciInfo(time$n)) / 1000.0} ]
                set analysis(nodes$n) [calculateNodes $uciInfo(nodes$n) ]
            }
            
            set pvRaw $uciInfo(pv$n)
            
            # convert to something more readable
            if ($toBeFormatted) {
                set uciInfo(pv$n) [formatPv $uciInfo(pv$n) $analysis(fen$n)]
                set toBeFormatted 0
            }
            
            set idx [ expr $uciInfo(multipv$n) -1 ]
            
            # was if $analyze etc..
            if { $idx < $analysis(multiPVCount$n) } {
                set tmpTime [expr {double($uciInfo(time$n)) / 1000.0}]
                if {$idx < [llength $analysis(multiPV$n)]} {
                    lset analysis(multiPV$n) $idx "$uciInfo(depth$n) $uciInfo(tmp_score$n) [list $uciInfo(pv$n)] $uciInfo(scoremate$n) $tmpTime"
                    lset analysis(multiPVraw$n) $idx "$uciInfo(depth$n) $uciInfo(tmp_score$n) [list $pvRaw] $uciInfo(scoremate$n) $tmpTime"
                } else  {
                    lappend analysis(multiPV$n) "$uciInfo(depth$n) $uciInfo(tmp_score$n) [list $uciInfo(pv$n)] $uciInfo(scoremate$n) $tmpTime"
                    lappend analysis(multiPVraw$n) "$uciInfo(depth$n) $uciInfo(tmp_score$n) [list $pvRaw] $uciInfo(scoremate$n) $tmpTime"
                }
            }
            
        }        
        
        if {$analyze} {
            updateAnalysisText $n
            #Show the processed info even if system is busy
            update idletasks
        }
    }
    ################################################################################
    #
    ################################################################################
    proc readUCI { n } {
        global ::uci::uciOptions
        
        set line [string trim [gets $::uci::uciInfo(pipe$n)] ]
        # end of options
        if {$line == "uciok"} {
            # we got all options, stop engine
            closeUCIengine $n 1
            uciConfigWin
        }
        # get options
        if { [string first "option name" $line] == 0 } {
            lappend uciOptions $line
        }
    }
    ################################################################################
    # build a dialog with UCI options published by the engine
    # and available in analysis(uciOptions)
    ################################################################################
    proc uciConfig { n cmd arg dir options } {
        global ::uci::uciOptions ::uci::oldOptions
        
        if {[info exists ::uci::uciInfo(pipe$n)]} {
            if {$::uci::uciInfo(pipe$n) != ""} {
                tk_messageBox -title "Scid" -icon warning -type ok -message "An engine is already running"
                return
            }
        }
        set oldOptions $options
        
        # If the analysis directory is not current dir, cd to it:
        set oldpwd ""
        if {$dir != "."} {
            set oldpwd [pwd]
            catch {cd $dir}
        }
        # Try to execute the analysis program:
        if {[catch {set pipe [open "| [list $cmd] $arg" "r+"]} result]} {
            if {$oldpwd != ""} { catch {cd $oldpwd} }
            tk_messageBox -title "Scid: error starting UCI engine" \
                    -icon warning -type ok -message "Unable to start the program:\n$cmd"
            return
        }
        
        set ::uci::uciInfo(pipe$n) $pipe
        
        # Configure pipe for line buffering and non-blocking mode:
        fconfigure $pipe -buffering full -blocking 0
        fileevent $pipe readable "::uci::readUCI $n"
        
        # Return to original dir if necessary:
        if {$oldpwd != ""} { catch {cd $oldpwd} }
        
        set uciOptions {}
        
        puts $pipe "uci"
        flush $pipe
        
        # give a few seconds for the engine to output its options, then automatically kill it
        # (to handle xboard engines)
        after 5000  "::uci::closeUCIengine $n 0"
    }
    
    ################################################################################
    #   builds the dialog for UCI engine configuration
    ################################################################################
    proc uciConfigWin {} {
        global ::uci::uciOptions ::uci::optList ::uci::optionToken ::uci::oldOptions ::uci::optionImportant
        
        set w .uciConfigWin
        if { [winfo exists $w]} { return }
        toplevel $w
        wm title $w $::tr(ConfigureUCIengine)
        ::scrolledframe::scrolledframe .uciConfigWin.sf -xscrollcommand {.uciConfigWin.hs set} -yscrollcommand {.uciConfigWin.vs set} \
                -fill both -width 1000 -height 600
        ttk::scrollbar .uciConfigWin.vs -command {.uciConfigWin.sf yview}
        ttk::scrollbar .uciConfigWin.hs -command {.uciConfigWin.sf xview} -orient horizontal
        grid .uciConfigWin.sf -row 0 -column 0 -sticky nsew
        grid .uciConfigWin.vs -row 0 -column 1 -sticky ns
        grid .uciConfigWin.hs -row 1 -column 0 -sticky ew
        grid rowconfigure .uciConfigWin 0 -weight 1
        grid columnconfigure .uciConfigWin 0 -weight 1
        set w .uciConfigWin.sf.scrolled
        
        proc tokeep {opt} {
            foreach tokeep $::uci::optionToKeep {
                if { [lsearch $opt $tokeep] != -1 } {
                    return 1
                }
            }
            return 0
        }
        
        set optList ""
        array set elt {}
        foreach opt $uciOptions {
            set elt(name) "" ; set elt(type) "" ; set elt(default) "" ; set elt(min) "" ; set elt(max) "" ; set elt(var) ""
            set data [split $opt]
            # skip options starting with UCI_ and Ponder
            # some engines like shredder use UCI_* options that should not be ignored
            
            if { ![tokeep $opt] && ( [ lsearch -glob $data "UCI_*" ] != -1 || [ lsearch $data "Ponder" ] != -1 ) } {
                continue
            }
            
            set length [llength $data]
            # parse one option
            for {set i 0} {$i < $length} {incr i} {
                set t [lindex $data $i]
                if {$t == "option"} { continue }
                if {$t == "name"} {
                    incr i
                    set elt(name) [ lindex $data $i ]
                    incr i
                    while { [ lsearch -exact $optionToken [ lindex $data $i ] ] == -1 && $i < $length } {
                        append elt(name) " " [ lindex $data $i ]
                        incr i
                    }
                    incr i -1
                    continue
                }
                if {$t == "type"} { incr i ; set elt(type) [ lindex $data $i ] ; continue }
                if {$t == "default"} { ;# Glaurung uses a default value that is > one word
                    incr i
                    set elt(default) [ lindex $data $i ]
                    incr i
                    while { [ lsearch -exact $optionToken [ lindex $data $i ] ] == -1 && $i < $length } {
                        append elt(default) " " [ lindex $data $i ]
                        incr i
                    }
                    incr i -1
                    continue
                }
                if {$t == "min"} { incr i ; set elt(min) [ lindex $data $i ] ; continue }
                if {$t == "max"} { incr i ; set elt(max) [ lindex $data $i ] ; continue }
                if {$t == "var"} {
                    incr i
                    set tmp [ lindex $data $i ]
                    incr i
                    while { ([ lsearch -exact $optionToken [ lindex $data $i ] ] == -1 && $i < $length ) \
                                || [ lindex $data $i ] == "var" } {
                        if {[ lindex $data $i ] != "var" } {
                            append tmp " " [ lindex $data $i ]
                        } else  {
                            lappend elt(var) [list $tmp]
                            incr i
                            set tmp [ lindex $data $i ]
                        }
                        incr i
                    }
                    lappend elt(var) [list $tmp]
                    
                    incr i -1
                    continue
                }
            }
            lappend optList [array get elt]
        }
        
        # sort list of options so that important ones come first
        set tmp $optList
        set optList {}
        foreach l $tmp {
            array set elt $l
            if { [ lsearch $optionImportant $elt(name) ] != -1 } {
                lappend optList $l
            }
        }
        foreach l $tmp {
            array set elt $l
            if { [ lsearch $optionImportant $elt(name) ] == -1 } {
                lappend optList $l
            }
        }
        
        set optnbr 0
        ttk::frame $w.fopt
        ttk::frame $w.fbuttons
        
        set row 0
        set col 0
        set isImportantParam 1
        foreach l $optList {
            array set elt $l
            set name $elt(name)
            if { [ lsearch $optionImportant $elt(name) ] == -1 && $isImportantParam } {
                set isImportantParam 0
                incr row
                set col 0
            }
            if {$elt(name) == "MultiPV"} { set name $::tr(MultiPV) }
            if {$elt(name) == "Hash"} { set name $::tr(Hash) }
            if {$elt(name) == "OwnBook"} { set name $::tr(OwnBook) }
            if {$elt(name) == "BookFile"} { set name $::tr(BookFile) }
            if {$elt(name) == "UCI_LimitStrength"} { set name $::tr(LimitELO) }
            
            if { $col > 3 } { set col 0 ; incr row}
            if {$elt(default) != ""} {
                set default "\n($elt(default))"
            } else  {
                set default ""
            }
            set value $elt(default)
            # find the name in oldOptions (the previously saved data)
            foreach old $oldOptions {
                if {[lindex $old 0] == $elt(name)} {
                    set value [lindex $old 1]
                    break
                }
            }
            if { $elt(type) == "check"} {
                ttk::checkbutton $w.fopt.opt$optnbr -text "$name$default" -onvalue true -offvalue false -variable ::uci::check($optnbr)
                set ::uci::check($optnbr) $value
                grid $w.fopt.opt$optnbr -row $row -column $col -sticky w
            }
            if { $elt(type) == "spin"} {
                ttk::label $w.fopt.label$optnbr -text "$name$default"
                if { $elt(name) == "UCI_Elo" } {
                    spinbox $w.fopt.opt$optnbr -from $elt(min) -to $elt(max) -width 5 -increment 50 -validate all -vcmd { regexp {^[0-9]+$} %P }
                } else  {
                    spinbox $w.fopt.opt$optnbr -from $elt(min) -to $elt(max) -width 5 -validate all -vcmd { regexp {^[0-9]+$} %P }
                }
                $w.fopt.opt$optnbr set $value
                grid $w.fopt.label$optnbr -row $row -column $col -sticky e
                incr col
                grid $w.fopt.opt$optnbr -row $row -column $col -sticky w
            }
            if { $elt(type) == "combo"} {
                ttk::label $w.fopt.label$optnbr -text "$name$default"
                set idx 0
                set i 0
                set tmp {}
                foreach e $elt(var) {
                    lappend tmp [join $e]
                    if {[join $e] == $value} { set idx $i }
                    incr i
                }
                ttk::combobox $w.fopt.opt$optnbr -values $tmp
                
                $w.fopt.opt$optnbr current $idx
                grid $w.fopt.label$optnbr -row $row -column $col -sticky e
                incr col
                grid $w.fopt.opt$optnbr -row $row -column $col -sticky w
            }
            if { $elt(type) == "button"} {
                ttk::button $w.fopt.opt$optnbr -text "$name$default"
                grid $w.fopt.opt$optnbr -row $row -column $col -sticky w
            }
            if { $elt(type) == "string"} {
                ttk::label $w.fopt.label$optnbr -text "$name$default"
                ttk::entry $w.fopt.opt$optnbr
                $w.fopt.opt$optnbr insert 0 $value
                grid $w.fopt.label$optnbr -row $row -column $col -sticky e
                incr col
                grid $w.fopt.opt$optnbr -row $row -column $col -sticky w
            }
            incr col
            incr optnbr
        }
        
        ttk::button $w.fbuttons.save -text $::tr(Save) -command {
            ::uci::saveConfig
            destroy .uciConfigWin
        }
        ttk::button $w.fbuttons.cancel -text $::tr(Cancel) -command "destroy .uciConfigWin"
        pack $w.fbuttons.save $w.fbuttons.cancel -side left -expand yes -fill x -padx 20 -pady 2
        pack $w.fopt -expand 1 -fill both
        addHorizontalRule $w
        pack $w.fbuttons -expand 1 -fill both
        bind $w <Return> "$w.fbuttons.save invoke"
        bind $w <Escape> "destroy .uciConfigWin"
        catch {grab .uciConfigWin}
    }
    ################################################################################
    # will generate a list of list {{name}/value} pairs
    ################################################################################
    proc saveConfig {} {
        global ::uci::optList ::uci::newOptions
        set newOptions {}
        set w .uciConfigWin.sf.scrolled
        set optnbr 0
        
        foreach l $optList {
            array set elt $l
            set value ""
            if { $elt(type) == "check"} {
                set value $::uci::check($optnbr)
            }
            if { $elt(type) == "spin" || $elt(type) == "combo" || $elt(type) == "string" } {
                set value [$w.fopt.opt$optnbr get]
            }
            if { $elt(type) == "button" } { set value "" }
            
            lappend newOptions [ list $elt(name)  $value ]
            incr optnbr
        }
        if { $::uci::autoSaveOptions } {
            writeOptions
            set ::uci::autoSaveOptions 0
        }
    }
    ################################################################################
    # If the config window is called outside the engine dialog, save UCI options
    # (only the UCI options dialog box is called
    ################################################################################
    proc writeOptions {} {
        set elt [lindex $::engines(list) $::uci::autoSaveOptionsIndex]
        set elt [ lreplace $elt 8 8 $::uci::newOptions]
        set ::engines(list) [lreplace $::engines(list) $::uci::autoSaveOptionsIndex $::uci::autoSaveOptionsIndex $elt]
        
        ::enginelist::write
    }
    ################################################################################
    # The engine replied readyok, so it's time to configure it (sends the options to the engine)
    # It seems necessary to ask first if engine is ready
    ################################################################################
    proc sendUCIoptions { n } {
        global analysis
        set engineData [ lindex $::engines(list) $analysis(index$n) ]
        set options [ lindex $engineData 8 ]
        foreach opt $options {
            set name [lindex $opt 0]
            set value [lindex $opt 1]
            set analysis(waitForReadyOk$n) 1
            ::sendToEngine $n "isready"
            vwait analysis(waitForReadyOk$n)
            ::sendToEngine $n "setoption name $name value $value"
        }
    }
    ################################################################################
    # will start an engine for playing (not analysis)
    ################################################################################
    proc startEngine {index n} {
        global ::uci::uciInfo
        resetUciInfo $n
        set uciInfo(pipe$n) ""
        set uciInfo(seen$n) 0
        set uciInfo(uciok$n) 0
        ::resetEngine $n
        set engineData [lindex $::engines(list) $index]
        set analysisName [lindex $engineData 0]
        set analysisCommand [ toAbsPath [lindex $engineData 1] ]
        set analysisArgs [lindex $engineData 2]
        set analysisDir [ toAbsPath [lindex $engineData 3] ]
        
        # If the analysis directory is not current dir, cd to it:
        set oldpwd ""
        if {$analysisDir != "."} {
            set oldpwd [pwd]
            catch {cd $analysisDir}
        }
        
        # Try to execute the analysis program:
        if {[catch {set uciInfo(pipe$n) [open "| [list $analysisCommand] $analysisArgs" "r+"]} result]} {
            if {$oldpwd != ""} { catch {cd $oldpwd} }
            tk_messageBox -title "Scid: error starting engine" -icon warning -type ok \
                    -message "Unable to start the program:\n$analysisCommand"
            return
        }
        
        set ::analysis(index$n) $index
        set ::analysis(pipe$n) $uciInfo(pipe$n)
        
        # Return to original dir if necessary:
        if {$oldpwd != ""} { catch {cd $oldpwd} }
        
        fconfigure $uciInfo(pipe$n) -buffering line -blocking 0
        fileevent $uciInfo(pipe$n) readable "::uci::processAnalysisInput $n 0"
        
        # wait a few seconds to be sure the engine had time to start
        set counter 0
        while {! $::uci::uciInfo(uciok$n) && $counter < 50 } {
            incr counter
            update
            after 100
        }
    }
    ################################################################################
    #
    ################################################################################
    proc sendToEngine {n text} {
        logEngine $n "Scid  : $text"
        catch {puts $::uci::uciInfo(pipe$n) $text}
    }
    ################################################################################
    # returns 0 if engine died abruptly or 1 otherwise
    ################################################################################
    proc checkEngineIsAlive { n } {
        global ::uci::uciInfo
        if {[eof $uciInfo(pipe$n)]} {
            fileevent $uciInfo(pipe$n) readable {}
            set exit_status 0
            if {[catch {close $uciInfo(pipe$n)} standard_error] != 0} {
                global errorCode
                if {"CHILDSTATUS" == [lindex $errorCode 0]} {
                    set exit_status [lindex $errorCode 2]
                }
            }
            set uciInfo(pipe$n) ""
            if { $exit_status != 0 } {
                logEngineNote $n {Engine terminated with exit code $exit_status: "\"$standard_error\""}
                tk_messageBox -type ok -icon info -parent . -title "Scid" \
                              -message "The analysis engine terminated with exit code $exit_status: \"$standard_error\""
            } else {
                logEngineNote $n {Engine terminated without exit code: "\"$standard_error\""}
                tk_messageBox -type ok -icon info -parent . -title "Scid" \
                              -message "The analysis engine terminated without exist code: \"$standard_error\""
            }
            return 0
        }
        return 1
    }
    ################################################################################
    # close the engine
    # It may be not an UCI one (if the user made an error, trying to configure an xboard engine)
    ################################################################################
    proc closeUCIengine { n { uciok 1 } } {
        global windowsOS ::uci::uciInfo
        
        set pipe $uciInfo(pipe$n)
        # Check the pipe is not already closed:
        if {$pipe == ""} { return }
        
        after cancel "::uci::closeUCIengine $n 0"
        fileevent $pipe readable {}
        
        if {! $uciok } {
            tk_messageBox -title "Scid: error closing UCI engine" \
                    -icon warning -type ok -message "Not an UCI engine"
        }
        
        # Some engines in analyze mode may not react as expected to "quit"
        # so ensure the engine exits analyze mode first:
        catch { puts $pipe "stop" ; puts $pipe "quit" }
        #in case an xboard engine
        catch { puts $pipe "exit" ; puts $pipe "quit" }
        
        # last resort : try to kill the engine (TODO if Windows : no luck, welcome zombies !)
        # No longer try to kill the engine as :
        # - it does not work on Windows
        # - Rybka MP uses processes instead of threads : killing the main process will leave the children processes running
        # - engines should normally exit
        # if { ! $windowsOS } { catch { exec -- kill -s INT [ pid $pipe ] }  }
        
        catch { flush $pipe }
        catch { close $pipe }
        set uciInfo(pipe$n) ""
    }
    ################################################################################
    # UCI moves use long notation
    # returns 1 if an error occured when entering a move
    ################################################################################
    proc sc_move_add { moves } {
        
        foreach m $moves {
            # get rid of leading piece
            set c [string index $m 0]
            if {$c == "K" || $c == "Q" || $c == "R" || $c == "B" || $c == "N"} {
                set m [string range $m 1 end]
            }
            set s1 [string range $m 0 1]
            set s1 [::board::sq $s1]
            set s2 [string range $m 2 3]
            set s2 [::board::sq $s2]
            if {[string length $m] > 4} {
                set promo [string range $m 4 end]
                # inverse transformation : const char PIECE_CHAR [] = "xKQRBNP.xkqrbnpxMm";
                # it seems capitalisation does not matter (see addMove proc in main.tcl)
                switch -- $promo {
                    q { set p 2}
                    r { set p 3}
                    b { set p 4}
                    n { set p 5}
                    default {puts "Promo error $promo for moves $moves"}
                }
                if { [catch { sc_move add $s1 $s2 $p } ] } { return 1 }
            } else  {
                if { [catch { sc_move add $s1 $s2 0 } ] } { return 1 }
            }
        }
        return 0
    }
    ################################################################################
    #make UCI output more readable (b1c3 -> Nc3)
    ################################################################################
    proc formatPv { moves { fen "" } } {
        sc_info preMoveCmd {}
        # Push a temporary copy of the current game:
        if {$fen != ""} {
            sc_game push
            sc_game startBoard $fen
        } else  {
            sc_game push copyfast
        }
        set tmp ""
        foreach m $moves {
            if { [sc_move_add $m] == 1 } { break }
            set prev [sc_game info previousMoveNT]
            append tmp " $prev"
        }
        set tmp [string trim $tmp]
        
        # Pop the temporary game:
        sc_game pop
        # Restore pre-move command:
        sc_info preMoveCmd preMoveCommand
        
        return $tmp
    }
    ################################################################################
    #   Format a line starting at current position where <moves> were played (in San notation)
    ################################################################################
    proc formatPvAfterMoves { played_moves moves } {
        sc_info preMoveCmd {}
        sc_game push copyfast
        sc_move addSan $played_moves
        
        set tmp ""
        foreach m $moves {
            if { [sc_move_add $m] == 1 } {
                break
            }
            set prev [sc_game info previousMoveNT]
            append tmp " $prev"
        }
        set tmp [string trim $tmp]
        
        # Pop the temporary game:
        sc_game pop
        # Restore pre-move command:
        sc_info preMoveCmd preMoveCommand
        
        return $tmp
    }
}
###
### End of file: uci.tcl
###
------------------------------------------------------------------------------
The ultimate all-in-one performance toolkit: Intel(R) Parallel Studio XE:
Pinpoint memory and threading errors before they happen.
Find and fix more than 250 security defects in the development cycle.
Locate bottlenecks in serial and parallel code that limit performance.
http://p.sf.net/sfu/intel-dev2devfeb
_______________________________________________
Scid-users mailing list
Scid-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/scid-users

Reply via email to