From 436dc7ab3ee052df470d108dcb3bfcd0627d2a8d Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 26 Jan 2025 17:06:08 +0100 Subject: [PATCH 01/57] Annotate game with new Engine-Interface First running version. Code used from analysis.tcl --- tcl/menus.tcl | 1 + tcl/start.tcl | 1 + tcl/tools/annotate.tcl | 648 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 650 insertions(+) create mode 100644 tcl/tools/annotate.tcl diff --git a/tcl/menus.tcl b/tcl/menus.tcl index e66623ef3..d350ef1ed 100644 --- a/tcl/menus.tcl +++ b/tcl/menus.tcl @@ -250,6 +250,7 @@ $m add command -label ToolsStartEngine1 \ -command "::enginewin::start 1" -accelerator "F2" $m add command -label ToolsStartEngine2 \ -command "::enginewin::start 2" -accelerator "F3" +$m add command -label "Partie analysieren" -command "::annotation::doAnnotate" $m add command -label ToolsAnalysis -command "makeAnalysisWin 1" $m add separator $m add checkbutton -label ToolsFilterGraph \ diff --git a/tcl/start.tcl b/tcl/start.tcl index 51cae61a3..813f4e9a0 100644 --- a/tcl/start.tcl +++ b/tcl/start.tcl @@ -744,6 +744,7 @@ tools/optable.tcl tools/preport.tcl tools/pinfo.tcl tools/analysis.tcl +tools/annotate.tcl tools/wbdetect.tcl tools/graphs.tcl tools/ptracker.tcl diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl new file mode 100644 index 000000000..17768c96a --- /dev/null +++ b/tcl/tools/annotate.tcl @@ -0,0 +1,648 @@ +### +### annotate.tcl: part of Scid. +### This file is part of Scid (Shane's Chess Information Database). +### Copyright (C) 2025 Uwe Klimmek +### uses code from Fulvio Benini https://github.com/benini/chess_accuracy +###################################################################### +### Annotate Dialog: uses a chess engine to analyze and annotate a chess game. + +#TODO +#check Tactical Exercise only for uci und multipv 4 +#use analyse depth, actual ignored +namespace eval ::annotation { + + set ::annotate(movetime) 1000 + set ::annotate(time) 1 + set ::annotate(depth) 20 + # Typ may be "movetime": time per move or "depth": analyse till depth is reached + set ::annotate(typ) "movetime" + set ::annotate(engine) "" + set ::annotate(progress) 25 + set ::annotate(blunderThreshold) 0.5 + set ::annotate(annotateMoves) all + set ::annotate(annotateBlunders) blundersonly + set ::annotate(scoreAllMoves) 1 + set ::annotate(annotateMode) 0 + set ::annotate(useAnalysisBook) 0 + set ::annotate(BookSlot) 1 + set ::annotate(tacticalExercises) 0 + set ::annotate(addAnnotatorTag) 1 + set ::annotate(OpeningErrors) 0 + set ::annotate(OpeningMoves) 0 + set ::annotate(prevdepth) 0 + set ::annotate(annotateVar) 0 + set ::annotate(annotateShort) 1 + set ::annotate(addScoreToShortAnnotations) 1 + set ::annotate(msg) "" + set ::annotate(prevscore) 0 + set ::annotate(prevmoves) "" + set ::annotate(score) 0 + set ::annotate(moves) "" + set ::annotate(scoremate) 0 + set ::annotate(prevscoremate) 0 + + proc doAnnotate {} { + set w .annotationDialog + # Do not do anything if the window exists + if { [winfo exists $w] } { + raise $w + focus $w + return + } + + #Workaround for error in trace var for arrays + set ::annotateBlunderThreshold $::annotate(blunderThreshold) + set ::annotateTime $::annotate(time) + trace variable ::annotateBlunderThreshold w {::utils::validate::Regexp {^[0-9]*\.?[0-9]*$}} + trace variable ::annotateTime w {::utils::validate::Regexp {^[0-9]*\.?[0-9]*$}} + + win::createDialog $w + ::setTitle $w "Scid: $::tr(Annotate)" + catch {grab $w} + wm resizable $w 0 0 + set f [ttk::frame $w.f] + pack $f -expand 1 + + ttk::labelframe $f.annotate -text $::tr(GameReview) + ttk::frame $f.annotate.typ + ttk::radiobutton $f.annotate.typ.label -text $::tr(AnnotateTime) -variable ::annotate(typ) -value "movetime" + ttk::radiobutton $f.annotate.typ.ldepth -text "Depth per move" -variable ::annotate(typ) -value "depth" + ttk::spinbox $f.annotate.typ.spDelay -width 5 -textvariable ::annotateTime -from 0.1 -to 999 \ + -validate key -justify right + ttk::spinbox $f.annotate.typ.depth -width 5 -textvariable ::annotate(depth) -from 2 -to 999 \ + -validate key -justify right + ttk::radiobutton $f.annotate.allmoves -text $::tr(AnnotateAllMoves) -variable ::annotate(annotateBlunders) -value allmoves + ttk::radiobutton $f.annotate.blundersonly -text $::tr(AnnotateBlundersOnly) -variable ::annotate(annotateBlunders) -value blundersonly + ttk::frame $f.annotate.blunderbox + ttk::label $f.annotate.blunderbox.label -text $::tr(BlundersThreshold:) + ttk::spinbox $f.annotate.blunderbox.spBlunder -width 4 -textvariable ::annotateBlunderThreshold \ + -from 0.1 -to 3.0 -increment 0.1 -justify right + ttk::checkbutton $f.annotate.cbBook -text $::tr(UseBook) -variable ::annotate(useAnalysisBook) + set engList [::enginecfg::names ] + if { $::annotate(engine) eq "" } { set ::annotate(engine) [lindex $engList 0] } + ttk::combobox $f.annotate.engine -width 30 -state readonly -values $engList -textvariable annotate(engine) + # choose a book for analysis + # load book names + set bookPath $::scidBooksDir + set bookList [ lsort -dictionary [ glob -nocomplain -directory $bookPath *.bin ] ] + + # No book found + if { [llength $bookList] == 0 } { + set ::annotate(useAnalysisBook) 0 + $f.annotate.cbBook configure -state disabled + } + set tmp {} + set idx 0 + set i 0 + foreach file $bookList { + lappend tmp [ file tail $file ] + if {$::book::lastBook == [ file tail $file ] } { + set idx $i + } + incr i + } + ttk::combobox $f.annotate.comboBooks -width 12 -values $tmp + catch { $f.annotate.comboBooks current $idx } + pack $f.annotate.comboBooks -side bottom -anchor w -padx 20 + pack $f.annotate.cbBook -side bottom -anchor w + pack $f.annotate.blunderbox.label -side left -padx { 20 0 } + pack $f.annotate.blunderbox.spBlunder -side left -anchor w + pack $f.annotate.blunderbox -side bottom -anchor w + pack $f.annotate.blundersonly -side bottom -anchor w + pack $f.annotate.allmoves -side bottom -anchor w + pack $f.annotate.engine -side bottom -anchor w + pack $f.annotate.typ -side bottom -anchor w + grid $f.annotate.typ.label -row 0 -column 0 -sticky w + grid $f.annotate.typ.ldepth -row 1 -column 0 -sticky w + grid $f.annotate.typ.spDelay -row 0 -column 1 -sticky w + grid $f.annotate.typ.depth -row 1 -column 1 -sticky w + bind $w { .configAnnotation.f.buttons.cancel invoke } + bind $w { .configAnnotation.f.buttons.ok invoke } + + ttk::labelframe $f.av -text $::tr(AnnotateWhich) + ttk::radiobutton $f.av.all -text $::tr(AnnotateAll) -variable ::annotate(annotateMoves) -value all + ttk::radiobutton $f.av.white -text $::tr(AnnotateWhite) -variable ::annotate(annotateMoves) -value white + ttk::radiobutton $f.av.black -text $::tr(AnnotateBlack) -variable ::annotate(annotateMoves) -value black + pack $f.av.all $f.av.white $f.av.black -side top -fill x -anchor w + + ttk::labelframe $f.comment -text $::tr(Comments) + ttk::checkbutton $f.comment.cbAnnotateVar -text $::tr(AnnotateVariations) -variable ::annotate(annotateVar) + ttk::checkbutton $f.comment.cbShortAnnotation -text $::tr(ShortAnnotations) -variable ::annotate(annotateShort) + ttk::checkbutton $f.comment.cbAddScore -text $::tr(AddScoreToShortAnnotations) -variable ::annotate(addScoreToShortAnnotations) + ttk::checkbutton $f.comment.cbAddAnnotatorTag -text $::tr(addAnnotatorTag) -variable ::annotate(addAnnotatorTag) + # Checkmark to enable all-move-scoring + ttk::checkbutton $f.comment.scoreAll -text $::tr(ScoreAllMoves) -variable ::annotate(scoreAllMoves) + ttk::checkbutton $f.comment.cbMarkTactics -text $::tr(MarkTacticalExercises) -variable ::annotate(tacticalExercises) + # if {! $::analysis(uci1)} { + # set ::markTacticalExercises 0 + # $f.comment.cbMarkTactics configure -state disabled + # } + pack $f.comment.scoreAll $f.comment.cbAnnotateVar $f.comment.cbShortAnnotation $f.comment.cbAddScore \ + $f.comment.cbAddAnnotatorTag $f.comment.cbMarkTactics -fill x -anchor w + # batch annotation of consecutive games, and optional opening errors finder + ttk::labelframe $f.batch -text "Batch Annotation" + ttk::frame $f.buttons + ttk::frame $f.running + ttk::label $f.running.line -textvariable ::annotate(msg) -width 50 + ttk::progressbar $f.running.progress -variable annotate(progress) -orient horizontal -length 300 + pack $f.running.line $f.running.progress -side top -anchor w + grid $f.annotate -row 0 -column 0 -pady { 0 10 } -sticky nswe -padx { 0 10 } + grid $f.comment -row 0 -column 1 -pady { 0 10 } -sticky nswe -padx { 10 0 } + grid $f.av -row 1 -column 0 -pady { 10 0 } -sticky nswe -padx { 0 10 } + grid $f.batch -row 1 -column 1 -pady { 10 0 } -sticky nswe -padx { 10 0 } + grid $f.buttons -row 3 -column 1 -sticky we + + set to [sc_base numGames $::curr_db] + if {$to <1} { set to 1} + ttk::checkbutton $f.batch.cbBatch -text $::tr(AnnotateSeveralGames) -variable ::isBatch + ttk::spinbox $f.batch.spBatchEnd -width 8 -textvariable ::batchEnd \ + -from 1 -to $to -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } + ttk::checkbutton $f.batch.cbBatchOpening -text $::tr(FindOpeningErrors) -variable ::annotate(OpeningErrors) + ttk::spinbox $f.batch.spBatchOpening -width 2 -textvariable ::annotate(OpeningMoves) \ + -from 10 -to 20 -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } + ttk::label $f.batch.lBatchOpening -text $::tr(moves) + pack $f.batch.cbBatch -side top -anchor w -pady { 0 0 } + pack $f.batch.spBatchEnd -side top -padx 20 -anchor w + pack $f.batch.cbBatchOpening -side top -anchor w + pack $f.batch.spBatchOpening -side left -anchor w -padx { 20 4 } + pack $f.batch.lBatchOpening -side left + set ::batchEnd $to + + ttk::button $f.buttons.cancel -text $::tr(Cancel) -command { + if { $::autoplayMode } { + set ::autoplayMode 0 + } else { + destroy .annotationDialog + } + } + ttk::button $f.buttons.ok -text "Annotate" -command { + if {$::annotateTime < 0.1} { set ::annotateTime 0.1 } + set ::annotate(movetime) [expr {int($::annotateTime * 1000.0)}] + set ::annotate(blunderThreshold) $::annotateBlunderThreshold + set ::annotate(time) $::annotateTime + ::annotation::runAnnotation + } + pack $f.buttons.cancel $f.buttons.ok -side right -padx 5 -pady 5 + focus $f.annotate.typ.spDelay + bind $w { focus . } + } + + proc runAnnotation { } { + set f .annotationDialog.f + grid $f.running -row 2 -column 0 -columnspan 2 -sticky we + grid forget $f.annotate + grid forget $f.comment + grid forget $f.av + grid forget $f.batch + $f.buttons.ok configure -state disabled + set ::autoplayMode 1 + + set ::annotate(AnalysisBookName) [.annotationDialog.f.annotate.comboBooks get] + set ::book::lastBook $::annotate(AnalysisBookName) + # tactical positions is selected, must be in multipv mode +# if {$::annotate(tacticalExercises)} { +# if { $::analysis(multiPVCount1) < 2} { + # TODO: Why not put it at the (apparent) minimum of 2? + # +# set ::analysis(multiPVCount1) 4 +# changePVSize 1 +# } +# } + + # Open the engine + set config [::enginecfg::get $::annotate(engine)] + lassign $config name cmd args wdir elo time url ::annotate(uci) options + ::engine::setLogCmd AnnoEngine {} + ::engine::connect AnnoEngine ::annotation::eng_messages $cmd {} + ::engine::send AnnoEngine SetOptions $options + ::engine::send AnnoEngine NewGame [list analysis post_pv post_wdl] + set ::annotate(progress) 0 + set ::annotate(msg) "" + set ::annotate(prevscore) 0 + set ::annotate(prevmoves) "" + set ::annotate(score) 0 + set ::annotate(moves) "" + set ::annotate(scoremate) 0 + set ::annotate(prevscoremate) 0 + if { $::annotate(addAnnotatorTag) } { + appendAnnotator "$::annotate(engine) $::annotate(typ) $::annotate($::annotate(typ))" + } + bookAnnotation + if { $::annotate(OpeningErrors) && ([sc_pos moveNumber] < $::annotate(OpeningMoves) ) } { + appendAnnotator "opBlunder [sc_pos moveNumber] ([sc_pos side])" + } + set ::autoplayMode 1 + + while 1 { + set ::annotate(PV1) [list 0 cp ""] + ::engine::send AnnoEngine Go [list [sc_game UCI_currentPos] [list $::annotate(typ) $::annotate($::annotate(typ))]] + vwait ::engine_done + addAnnotation + incr ::annotate(progress) + if {[sc_pos isAt end]} break + sc_move forward + ::notify::PosChanged -pgn + if { ! $::autoplayMode } { break } + } + set ::autoplayMode 0 + ::engine::close AnnoEngine + set ::annotate(progress) 99 + destroy .annotationDialog + } + + ################################################################################ + # Part of annotation process : will check the moves if they are in te book, and add a comment + # when going out of it + ################################################################################ + proc bookAnnotation { } { + if {$::annotate(useAnalysisBook)} { + set prevbookmoves "" + set bn [ file join $::scidBooksDir $::annotate(AnalysisBookName) ] + sc_book load $bn $::annotate(BookSlot) + + lassign [sc_book moves $::annotate(BookSlot)] bookmoves + while {[string length $bookmoves] != 0 && ![sc_pos isAt vend]} { + # we are in book, so move immediately forward + ::move::Forward + set prevbookmoves $bookmoves + lassign [sc_book moves $::annotate(BookSlot)] bookmoves + } + sc_book close $::annotate(BookSlot) + set ::wentOutOfBook 1 + + set verboseMoveOutOfBook " $::tr(MoveOutOfBook)" + set verboseLastBookMove " $::tr(LastBookMove)" + + set theCatch 0 + if { [ string match -nocase "*[sc_game info previousMoveNT]*" $prevbookmoves ] != 1 } { + if {$prevbookmoves != ""} { + sc_pos setComment "[sc_pos getComment]$verboseMoveOutOfBook [::trans $prevbookmoves]" + } else { + sc_pos setComment "[sc_pos getComment]$verboseMoveOutOfBook" + } + # last move was out of book: it needs to be analyzed, so take back + set theCatch [catch {sc_move back 1}] + } else { + sc_pos setComment "[sc_pos getComment]$verboseLastBookMove" + } +#TODO is this needed? +# if { ! $theCatch } { +# resetAnalysis +# updateBoard -pgn +# } +# set analysis(prevscore$n) $analysis(score$n) +# set analysis(prevmoves$n) $analysis(moves$n) +# set analysis(prevscoremate$n) $analysis(scoremate$n) +# set analysis(prevdepth$n) $analysis(depth$n) + } + } + + ################################################################################ + # will append arg to current game Annotator tag + ################################################################################ + proc appendAnnotator { s } { + # Get the current collection of extra tags + set extra [sc_game tags get "Extra"] + set annot 0 + set other "" + set nExtra {} + # Walk through the extra tags, just copying the crap we do not need + # If we meet the existing annotator tag, add our name to the list + foreach line $extra { + if { $annot == 1 } { + lappend nExtra "Annotator \"$line, $s\"\n" + set annot 2 + } elseif { $other != "" } { + lappend nExtra "$other \"$line\"\n" + set other "" + } elseif {[string match "Annotator" $line]} { + set annot 1 + } else { + set other $line + } + } + # First annotator: Create a tag + if { $annot == 0 } { + lappend nExtra "Annotator \"$s\"\n" + } + # Put the extra tags back to the game + sc_game tags set -extra $nExtra + } + + proc addAnnotation { } { + # Let's try to assess the situation: + # We are here, now that the engine has analyzed the position reached by + # our last move. Currently it is the opponent to move: + set tomove [sc_pos side] + + #TODO + set skipEngineLine 0 + # And this is his best line: + lassign $::annotate(PV1) score score_type ::annotate(moves) + set moves $::annotate(moves) + set bestMoveIsMate 0 + if { $score_type eq "mate" } { + # We do not want to insert a best-line variation into the game + # if we did play along that line. Even not when annotating all moves. + # It simply makes no sense to do so (unless we are debugging the engine!) + # Sooner or later the game will deviate anyway; a variation at that point will + # do nicely and is probably more accurate as well. + set bestMoveIsMate 1 + set ::annotate(scoremate) $score + set score [expr { $tomove eq "black" ? 127 : -127 }] + set ::annotate(score) $score + } else { + set ::annotate(score) $score + set ::annotate(scoremate) 0 + } + # For non-uci lines, trim space characters in .[ *][...] + set moves [regsub -all {\. *} $moves {.}] + + # The best line we could have followed, and the game move we just played instead, are here: + set prevmoves $::annotate(prevmoves) + # For non-uci lines, trim space characters in .[ *][...] + set prevmoves [regsub -all {\. *} $prevmoves {.}] + set gamemove [sc_game info previousMoveUCI] + + # We will add a closing line at the end of variation or game + set addClosingLine 0 + if { [sc_pos isAt vend] } { + set addClosingLine 1 + } + + # This is the score we could have had if we had played our best move + set prevscore $::annotate(prevscore) + + # Note that the engine's judgement is in absolute terms, a negative score + # being favorable to black, a positive score favorable to white + # Looking primarily for blunders, we are interested in the score decay, + # which, for white, is (previous-current) + set deltamove [expr {$prevscore + $score}] + # and whether the game was already lost for us + set gameIsLost [expr {$prevscore < (0.0 - $::informant("+--"))}] + + # Invert this logic for black + if { $tomove == "white" } { + set gameIsLost [expr {$prevscore > $::informant("+--")}] + } + + # Set an "isBlunder" filter. + # Let's mark moves with a decay greater than the threshold. + set isBlunder 0 + if { $deltamove > $::annotate(blunderThreshold) } { + set isBlunder 2 + } elseif { $deltamove > 0 } { + set isBlunder 1 + } + set absdeltamove [expr { abs($deltamove) } ] + + # to parse scores if the engine's name contains - or + chars (see sc_game_scores) + set engine_name [string map {"-" " " "+" " "} $::annotate(engine)] + + # Prepare score strings for the opponent + if { $::annotate(scoremate) != 0 } { + set text [format "M%d" [expr abs($::annotate(scoremate))]] + } else { + set wscore $score + if { $tomove eq "black" } {set wscore [expr 0.0 - $score] } + set text "\[%eval [format "%+.2f" $wscore]\]" + } + # And for the my (missed?) chance +puts "$::annotate(progress) $gamemove $tomove priv $::annotate(prevscore) score $::annotate(score) Lost $gameIsLost" + if { $::annotate(prevscoremate) != 0 } { + set prevtext [format "M%d" [expr abs($::annotate(prevscoremate))]] + } else { + set wprevscore $prevscore + if { $tomove eq "black" } {set wprevscore [expr 0.0 - $prevscore] } + set prevtext "\[%eval [format "%+.2f" $wprevscore]\]" + } + + # Must we annotate our own moves? If no, we bail out unless + # - we must add a closing line + if { ( $::annotate(annotateMoves) == "white" && $tomove == "white" || + $::annotate(annotateMoves) == "black" && $tomove == "black" ) && ! $addClosingLine } { + set ::annotate(prevscore) $::annotate(score) + set ::annotate(prevmoves) $::annotate(moves) + set ::annotate(prevscoremate) $::annotate(scoremate) + set ::annotate(prevdepth) $::annotate(depth) + updateBoard -pgn + } + + # See if we have the threshold filter activated. + # If so, take only bad moves and missed mates until the position is lost anyway + # Or that we must annotate all moves + if { ( $::annotate(annotateBlunders) == "blundersonly" + && ($isBlunder > 1 || ($isBlunder > 0 && [expr abs($score)] >= 327.0)) + && ! $gameIsLost) + || ($::annotate(annotateBlunders) == "allmoves") } { + if { $isBlunder > 0 } { + # Add move score nag, and possibly an exercise + if { $absdeltamove > $::informant("??") } { + markExercise $prevscore $score "??" + } elseif { $absdeltamove > $::informant("?") } { + markExercise $prevscore $score "?" + } elseif { $absdeltamove > $::informant("?!") } { + sc_pos addNag "?!" + } + } elseif { $absdeltamove > $::informant("!?") } { + sc_pos addNag "!?" + } + + # Add score comment and engine name if needed + if { ! $::annotate(annotateShort) } { + sc_pos setComment "[sc_pos getComment] $engine_name: $text" + } elseif { $::annotate(addScoreToShortAnnotations) || $::annotate(scoreAllMoves) } { + sc_pos setComment "[sc_pos getComment] $text" + } + + # Add position score nag + sc_pos addNag [scoreToNag $score] + + # Add the variation + if { $skipEngineLine == 0 } { + sc_move back + if { $::annotate(annotateBlunders) == "blundersonly" } { + # Add a diagram tag, but avoid doubles + # + if { [string first "D" "[sc_pos getNags]"] == -1 } { + sc_pos addNag "D" + } + } + if { $prevmoves != "" && ( $::annotate(annotateMoves) == "all" || $::annotate(annotateMoves) == "white" && $tomove == "black" || + $::annotate(annotateMoves) == "black" && $tomove == "white" )} { + sc_var create + # Add the starting move + sc_move_add [lrange $prevmoves 0 0] + # Add its score + if { ! $bestMoveIsMate } { + if { ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations) } { + sc_pos setComment "$prevtext" + } + } + # Add remaining moves + sc_move_add [lrange $prevmoves 1 end] + # Add position NAG, unless the line ends in mate + if { $::annotate(prevscoremate) == 0 } { + sc_pos addNag [scoreToNag $prevscore] + } + sc_var exit + } + sc_move forward + } + } else { + if { $isBlunder == 0 && $absdeltamove > $::informant("!?") } { + sc_pos addNag "!?" + } + if { $::annotate(scoreAllMoves) } { + # Add a score mark anyway + sc_pos setComment "[sc_pos getComment] $text" + } + } + + if { $addClosingLine } { + sc_move back + sc_var create + sc_move addSan $gamemove + if { ($::annotate(scoremate) == 0) && ( ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations)) } { + sc_pos setComment "$text" + } + sc_move_add $moves + if { $::annotate(scoremate) == 0 } { + sc_pos addNag [scoreToNag $score] + } + sc_var exit + # Now up to the end of the game + ::move::Forward + } + + set ::annotate(prevscore) $::annotate(score) + set ::annotate(prevmoves) $::annotate(moves) + set ::annotate(prevscoremate) $::annotate(scoremate) + set ::annotate(prevdepth) $::annotate(depth) + updateBoard -pgn + } + + ################################################################################ + # Will add **** to any position considered as a tactical shot + # returns 1 if an exercise was marked, 0 if for some reason it was not (obvious move for example) + ################################################################################ + proc markExercise { prevscore score nag} { + sc_pos addNag $nag + if {!$::annotate(tacticalExercises)} { return 0 } + +puts "prev $prevscore Score $score" + # check at which depth the tactical shot is found + # this assumes analysis by an UCI engine + if {! $::annotate(uci)} { return 0 } + + set deltamove [expr {$score + $prevscore}] + # filter tactics so only those with high gains are kept + if { [expr abs($deltamove)] < $::informant("+/-") } { return 0 } + # dismiss games where the result is already clear (high score,and we continue in the same way) + if { [expr $prevscore * $score] >= 0} { + if { [expr abs($prevscore) ] > $::informant("+--") } { return 0 } + if { [expr abs($prevscore)] > $::informant("+-") && [expr abs($score) ] < [expr 2 * abs($prevscore)]} { return 0 } + } + + # The best move is much better than others. + set sc2 [lindex $::annotate(PV2) 0] + if { [expr abs( $score - $sc2 )] < 1.5 } { return 0 } + + # There is no other winning moves (the best move may not win, of course, but + # I reject exercises when there are e.g. moves leading to +9, +7 and +5 scores) + if { [expr $score * $sc2] > 0.0 && [expr abs($score)] > $::informant("+-") && [expr abs($sc2)] > $::informant("+-") } { + puts diffscore + return 0 + } + + # The best move does not lose position. + if {[sc_pos side] == "black" && $score < [expr 0.0 - $::informant("+/-")] } { return 0 } + if {[sc_pos side] == "white" && $score > $::informant("+/-") } { return 0} + + # Move is not obvious: check that it is not the first move guessed at low depths + set pv [ lindex [ lindex $::annotate(PV1) 2 ] 0 ] + set bm0 [lindex $pv 0] + foreach depth {1 2 3} { + set res [ sc_pos analyze -time 1000 -hashkb 32 -pawnkb 1 -searchdepth $depth ] + set bm$depth [lindex $res 1] + } + #TODO Bm0 is UCI a2c4 not Bc4, reurn from sc_pos is pgn +puts "BM $bm0 $bm1 $bm2 $bm3 $::annotate(PV1)" + if { $bm0 == $bm1 && $bm0 == $bm2 && $bm0 == $bm3 } { + return 0 + } + + # find what time is needed to get the solution (use internal analyze function) + set timer {1 2 5 10 50 100 200 1000} + set movelist {} + for {set t 0} {$t < [llength $timer]} { incr t} { + set res [sc_pos analyze -time [lindex $timer $t] -hashkb 1 -pawnkb 1 -mindepth 0] + set move_analyze [lindex $res 1] + lappend movelist $move_analyze + } + + # find at what timing the right move was reliably found + # only the move is checked, not if the score is close to the expected one + for {set t [expr [llength $timer] -1]} {$t >= 0} { incr t -1} { + if { [lindex $movelist $t] != $bm0 } { + break + } + } + set difficulty [expr $t +2] + + # If the base opened is read only, like a PGN file, avoids an exception + catch { sc_base gameflag [sc_base current] [sc_game number] set T } + sc_pos setComment "****D${difficulty} [format %.1f $prevscore]->[format %.1f $score] [sc_pos getComment]" + updateBoard + return 1 + } + + proc ::annotation::eng_messages {msg} { + lassign $msg msgType msgData + if {$msgType eq "InfoPV"} { + lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv + if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } + set ::annotate(PV$multipv) [list $score $score_type $pv] + if { $multipv == 1 } { + set ::annotate(msg) [string range "Analyse Move $::annotate(progress) Score: $score\nLine: $pv" 0 70] + } + } elseif {$msgType eq "InfoBestMove"} { + lassign $msgData ::engineBestMove + set ::engine_done 1 + } + } + # Informant index strings + array set ana_informantList { 0 "+=" 1 "+/-" 2 "+-" 3 "+--" } + # Nags. Note the slight inconsistency for the "crushing" symbol (see game.cpp) + array set ana_nagList { 0 "=" 1 "+=" 2 "+/-" 3 "+-" 4 "+--" 5 "=" 6 "=+" 7 "-/+" 8 "-+" 9 "--+" } + ################################################################################ + # + ################################################################################ + proc scoreToNag {score} { + global ana_informantList ana_nagList + # Find the score in the informant map + set tmp [expr { abs( $score ) }] + for { set i 0 } { $i < 4 } { incr i } { + if { $tmp < $::informant("$ana_informantList($i)") } { + break + } + } + # Jump into negative counterpart + if { $score < 0.0 } { + set i [expr {$i + 5}] + } + return $ana_nagList($i) + } + + ################################################################################ + # If UCI engine, add move through a dedicated function in uci namespace + # returns the error caught by catch + ################################################################################ + proc sc_move_add { moves } { + if { $::annotate(uci) } { + return [::uci::sc_move_add $moves] + } else { + return [ catch { sc_move addSan $moves } ] + } + } +} From 11654e9a1efae82e7a371a84c5f4889d5ddec058 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 26 Jan 2025 18:26:18 +0100 Subject: [PATCH 02/57] Batchmode added --- tcl/menus.tcl | 2 +- tcl/tools/annotate.tcl | 103 ++++++++++++++++++++++++++--------------- 2 files changed, 67 insertions(+), 38 deletions(-) diff --git a/tcl/menus.tcl b/tcl/menus.tcl index d350ef1ed..1d3fbc406 100644 --- a/tcl/menus.tcl +++ b/tcl/menus.tcl @@ -250,7 +250,7 @@ $m add command -label ToolsStartEngine1 \ -command "::enginewin::start 1" -accelerator "F2" $m add command -label ToolsStartEngine2 \ -command "::enginewin::start 2" -accelerator "F3" -$m add command -label "Partie analysieren" -command "::annotation::doAnnotate" +$m add command -label "Annotate Game(s)" -command "::annotation::doAnnotate" $m add command -label ToolsAnalysis -command "makeAnalysisWin 1" $m add separator $m add checkbutton -label ToolsFilterGraph \ diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 17768c96a..d53b1610c 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -33,7 +33,10 @@ namespace eval ::annotation { set ::annotate(annotateVar) 0 set ::annotate(annotateShort) 1 set ::annotate(addScoreToShortAnnotations) 1 - set ::annotate(msg) "" + set ::annotate(batchMode) 0 + set ::annotate(batchEnd) 0 + set ::annotate(msg1) "" + set ::annotate(msg2) "" set ::annotate(prevscore) 0 set ::annotate(prevmoves) "" set ::annotate(score) 0 @@ -57,7 +60,7 @@ namespace eval ::annotation { trace variable ::annotateTime w {::utils::validate::Regexp {^[0-9]*\.?[0-9]*$}} win::createDialog $w - ::setTitle $w "Scid: $::tr(Annotate)" + ::setTitle $w "Scid: $::tr(Annotate) Game" catch {grab $w} wm resizable $w 0 0 set f [ttk::frame $w.f] @@ -143,9 +146,10 @@ namespace eval ::annotation { ttk::labelframe $f.batch -text "Batch Annotation" ttk::frame $f.buttons ttk::frame $f.running - ttk::label $f.running.line -textvariable ::annotate(msg) -width 50 - ttk::progressbar $f.running.progress -variable annotate(progress) -orient horizontal -length 300 - pack $f.running.line $f.running.progress -side top -anchor w + ttk::label $f.running.line1 -textvariable ::annotate(msg1) -width 50 -anchor center + ttk::label $f.running.line2 -textvariable ::annotate(msg2) -width 50 + ttk::progressbar $f.running.progress -variable annotate(progress) -orient horizontal -length 600 + pack $f.running.line1 $f.running.line2 $f.running.progress -side top -anchor w grid $f.annotate -row 0 -column 0 -pady { 0 10 } -sticky nswe -padx { 0 10 } grid $f.comment -row 0 -column 1 -pady { 0 10 } -sticky nswe -padx { 10 0 } grid $f.av -row 1 -column 0 -pady { 10 0 } -sticky nswe -padx { 0 10 } @@ -154,8 +158,8 @@ namespace eval ::annotation { set to [sc_base numGames $::curr_db] if {$to <1} { set to 1} - ttk::checkbutton $f.batch.cbBatch -text $::tr(AnnotateSeveralGames) -variable ::isBatch - ttk::spinbox $f.batch.spBatchEnd -width 8 -textvariable ::batchEnd \ + ttk::checkbutton $f.batch.cbBatch -text $::tr(AnnotateSeveralGames) -variable ::annotate(batchMode) + ttk::spinbox $f.batch.spBatchEnd -width 8 -textvariable ::annotate(batchEnd) \ -from 1 -to $to -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } ttk::checkbutton $f.batch.cbBatchOpening -text $::tr(FindOpeningErrors) -variable ::annotate(OpeningErrors) ttk::spinbox $f.batch.spBatchOpening -width 2 -textvariable ::annotate(OpeningMoves) \ @@ -166,7 +170,7 @@ namespace eval ::annotation { pack $f.batch.cbBatchOpening -side top -anchor w pack $f.batch.spBatchOpening -side left -anchor w -padx { 20 4 } pack $f.batch.lBatchOpening -side left - set ::batchEnd $to + set ::annotate(batchEnd) $to ttk::button $f.buttons.cancel -text $::tr(Cancel) -command { if { $::autoplayMode } { @@ -216,33 +220,60 @@ namespace eval ::annotation { ::engine::connect AnnoEngine ::annotation::eng_messages $cmd {} ::engine::send AnnoEngine SetOptions $options ::engine::send AnnoEngine NewGame [list analysis post_pv post_wdl] - set ::annotate(progress) 0 - set ::annotate(msg) "" - set ::annotate(prevscore) 0 - set ::annotate(prevmoves) "" - set ::annotate(score) 0 - set ::annotate(moves) "" - set ::annotate(scoremate) 0 - set ::annotate(prevscoremate) 0 - if { $::annotate(addAnnotatorTag) } { - appendAnnotator "$::annotate(engine) $::annotate(typ) $::annotate($::annotate(typ))" - } - bookAnnotation - if { $::annotate(OpeningErrors) && ([sc_pos moveNumber] < $::annotate(OpeningMoves) ) } { - appendAnnotator "opBlunder [sc_pos moveNumber] ([sc_pos side])" - } - set ::autoplayMode 1 - while 1 { - set ::annotate(PV1) [list 0 cp ""] - ::engine::send AnnoEngine Go [list [sc_game UCI_currentPos] [list $::annotate(typ) $::annotate($::annotate(typ))]] - vwait ::engine_done - addAnnotation - incr ::annotate(progress) - if {[sc_pos isAt end]} break - sc_move forward - ::notify::PosChanged -pgn - if { ! $::autoplayMode } { break } + while { 1 } { + set firstmove [llength [sc_game moves]] + sc_game push copyfast + catch { sc_move forward 300 } + set anz [expr {[llength [sc_game moves]] - $firstmove}] + sc_game pop + $f.running.progress configure -maximum $anz + set ::annotate(progress) 0 + set ::annotate(msg1) "[sc_game info white] - [sc_game info black]" + set ::annotate(msg2) "" + set ::annotate(prevscore) 0 + set ::annotate(prevmoves) "" + set ::annotate(score) 0 + set ::annotate(moves) "" + set ::annotate(scoremate) 0 + set ::annotate(prevscoremate) 0 + if { $::annotate(addAnnotatorTag) } { + appendAnnotator "$::annotate(engine) $::annotate(typ) $::annotate($::annotate(typ))" + } + bookAnnotation + if { $::annotate(OpeningErrors) && ([sc_pos moveNumber] < $::annotate(OpeningMoves) ) } { + appendAnnotator "opBlunder [sc_pos moveNumber] ([sc_pos side])" + } + set ::autoplayMode 1 + + # Annotate all remaining moves of the game + while 1 { + set ::annotate(PV1) [list 0 cp ""] + ::engine::send AnnoEngine Go [list [sc_game UCI_currentPos] [list $::annotate(typ) $::annotate($::annotate(typ))]] + vwait ::engine_done + addAnnotation + incr ::annotate(progress) + if {[sc_pos isAt end]} break + sc_move forward + ::notify::PosChanged -pgn + if { ! $::autoplayMode } { break } + } + if { $::annotate(batchMode) && $::autoplayMode } { + #Batchmode: save game and load next game + set gameNo [sc_game number] + if { $gameNo != 0 } { + sc_game save $gameNo + } + # See if we must advance to the next game + if { $gameNo < $::annotate(batchEnd) } { + incr gameNo + sc_game load $gameNo + } else { + break + } + } else { + break + } } set ::autoplayMode 0 ::engine::close AnnoEngine @@ -408,7 +439,6 @@ namespace eval ::annotation { set text "\[%eval [format "%+.2f" $wscore]\]" } # And for the my (missed?) chance -puts "$::annotate(progress) $gamemove $tomove priv $::annotate(prevscore) score $::annotate(score) Lost $gameIsLost" if { $::annotate(prevscoremate) != 0 } { set prevtext [format "M%d" [expr abs($::annotate(prevscoremate))]] } else { @@ -530,7 +560,6 @@ puts "$::annotate(progress) $gamemove $tomove priv $::annotate(prevscore) score sc_pos addNag $nag if {!$::annotate(tacticalExercises)} { return 0 } -puts "prev $prevscore Score $score" # check at which depth the tactical shot is found # this assumes analysis by an UCI engine if {! $::annotate(uci)} { return 0 } @@ -604,7 +633,7 @@ puts "BM $bm0 $bm1 $bm2 $bm3 $::annotate(PV1)" if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } set ::annotate(PV$multipv) [list $score $score_type $pv] if { $multipv == 1 } { - set ::annotate(msg) [string range "Analyse Move $::annotate(progress) Score: $score\nLine: $pv" 0 70] + set ::annotate(msg2) [string range "Move $::annotate(progress) Score: $score\nLine: $pv" 0 70] } } elseif {$msgType eq "InfoBestMove"} { lassign $msgData ::engineBestMove From 7c385c53582e51acab2a861085ceb64f48138020 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 27 Jan 2025 18:33:54 +0100 Subject: [PATCH 03/57] AnnotateVar removed, show game number in progress minor Bugfixes --- tcl/tools/annotate.tcl | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index d53b1610c..c5ea4f4d2 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -9,6 +9,7 @@ #TODO #check Tactical Exercise only for uci und multipv 4 #use analyse depth, actual ignored +#finish game function namespace eval ::annotation { set ::annotate(movetime) 1000 @@ -30,7 +31,6 @@ namespace eval ::annotation { set ::annotate(OpeningErrors) 0 set ::annotate(OpeningMoves) 0 set ::annotate(prevdepth) 0 - set ::annotate(annotateVar) 0 set ::annotate(annotateShort) 1 set ::annotate(addScoreToShortAnnotations) 1 set ::annotate(batchMode) 0 @@ -83,7 +83,7 @@ namespace eval ::annotation { ttk::checkbutton $f.annotate.cbBook -text $::tr(UseBook) -variable ::annotate(useAnalysisBook) set engList [::enginecfg::names ] if { $::annotate(engine) eq "" } { set ::annotate(engine) [lindex $engList 0] } - ttk::combobox $f.annotate.engine -width 30 -state readonly -values $engList -textvariable annotate(engine) + ttk::combobox $f.annotate.engine -width 25 -state readonly -values $engList -textvariable annotate(engine) # choose a book for analysis # load book names set bookPath $::scidBooksDir @@ -129,18 +129,17 @@ namespace eval ::annotation { pack $f.av.all $f.av.white $f.av.black -side top -fill x -anchor w ttk::labelframe $f.comment -text $::tr(Comments) - ttk::checkbutton $f.comment.cbAnnotateVar -text $::tr(AnnotateVariations) -variable ::annotate(annotateVar) + # Checkmark to enable all-move-scoring + ttk::checkbutton $f.comment.scoreAll -text $::tr(ScoreAllMoves) -variable ::annotate(scoreAllMoves) ttk::checkbutton $f.comment.cbShortAnnotation -text $::tr(ShortAnnotations) -variable ::annotate(annotateShort) ttk::checkbutton $f.comment.cbAddScore -text $::tr(AddScoreToShortAnnotations) -variable ::annotate(addScoreToShortAnnotations) ttk::checkbutton $f.comment.cbAddAnnotatorTag -text $::tr(addAnnotatorTag) -variable ::annotate(addAnnotatorTag) - # Checkmark to enable all-move-scoring - ttk::checkbutton $f.comment.scoreAll -text $::tr(ScoreAllMoves) -variable ::annotate(scoreAllMoves) ttk::checkbutton $f.comment.cbMarkTactics -text $::tr(MarkTacticalExercises) -variable ::annotate(tacticalExercises) # if {! $::analysis(uci1)} { # set ::markTacticalExercises 0 # $f.comment.cbMarkTactics configure -state disabled # } - pack $f.comment.scoreAll $f.comment.cbAnnotateVar $f.comment.cbShortAnnotation $f.comment.cbAddScore \ + pack $f.comment.scoreAll $f.comment.cbShortAnnotation $f.comment.cbAddScore \ $f.comment.cbAddAnnotatorTag $f.comment.cbMarkTactics -fill x -anchor w # batch annotation of consecutive games, and optional opening errors finder ttk::labelframe $f.batch -text "Batch Annotation" @@ -219,9 +218,9 @@ namespace eval ::annotation { ::engine::setLogCmd AnnoEngine {} ::engine::connect AnnoEngine ::annotation::eng_messages $cmd {} ::engine::send AnnoEngine SetOptions $options - ::engine::send AnnoEngine NewGame [list analysis post_pv post_wdl] while { 1 } { + ::engine::send AnnoEngine NewGame [list analysis post_pv post_wdl] set firstmove [llength [sc_game moves]] sc_game push copyfast catch { sc_move forward 300 } @@ -229,7 +228,7 @@ namespace eval ::annotation { sc_game pop $f.running.progress configure -maximum $anz set ::annotate(progress) 0 - set ::annotate(msg1) "[sc_game info white] - [sc_game info black]" + set ::annotate(msg1) "Game [sc_game number]: [sc_game info white] - [sc_game info black]" set ::annotate(msg2) "" set ::annotate(prevscore) 0 set ::annotate(prevmoves) "" @@ -248,7 +247,7 @@ namespace eval ::annotation { # Annotate all remaining moves of the game while 1 { - set ::annotate(PV1) [list 0 cp ""] + set ::annotate(PV1) [list "" "" ""] ::engine::send AnnoEngine Go [list [sc_game UCI_currentPos] [list $::annotate(typ) $::annotate($::annotate(typ))]] vwait ::engine_done addAnnotation @@ -365,11 +364,13 @@ namespace eval ::annotation { # We are here, now that the engine has analyzed the position reached by # our last move. Currently it is the opponent to move: set tomove [sc_pos side] + set gamemove [sc_game info previousMoveUCI] #TODO set skipEngineLine 0 # And this is his best line: lassign $::annotate(PV1) score score_type ::annotate(moves) + if { $gamemove eq "" || $score eq "" } { return } set moves $::annotate(moves) set bestMoveIsMate 0 if { $score_type eq "mate" } { @@ -380,7 +381,7 @@ namespace eval ::annotation { # do nicely and is probably more accurate as well. set bestMoveIsMate 1 set ::annotate(scoremate) $score - set score [expr { $tomove eq "black" ? 127 : -127 }] + set score [expr { $score < 0 ? -127 : 127 }] set ::annotate(score) $score } else { set ::annotate(score) $score @@ -393,7 +394,6 @@ namespace eval ::annotation { set prevmoves $::annotate(prevmoves) # For non-uci lines, trim space characters in .[ *][...] set prevmoves [regsub -all {\. *} $prevmoves {.}] - set gamemove [sc_game info previousMoveUCI] # We will add a closing line at the end of variation or game set addClosingLine 0 @@ -426,6 +426,7 @@ namespace eval ::annotation { set isBlunder 1 } set absdeltamove [expr { abs($deltamove) } ] +puts "$tomove $gamemove $prevscore $score $absdeltamove $::annotate(PV1)" # to parse scores if the engine's name contains - or + chars (see sc_game_scores) set engine_name [string map {"-" " " "+" " "} $::annotate(engine)] @@ -438,12 +439,12 @@ namespace eval ::annotation { if { $tomove eq "black" } {set wscore [expr 0.0 - $score] } set text "\[%eval [format "%+.2f" $wscore]\]" } - # And for the my (missed?) chance + # And for the (missed?) chance if { $::annotate(prevscoremate) != 0 } { set prevtext [format "M%d" [expr abs($::annotate(prevscoremate))]] } else { set wprevscore $prevscore - if { $tomove eq "black" } {set wprevscore [expr 0.0 - $prevscore] } + if { $tomove eq "white" } {set wprevscore [expr 0.0 - $prevscore] } set prevtext "\[%eval [format "%+.2f" $wprevscore]\]" } From 63f210193e76de1ddb793ee632a25386d8392d68 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 27 Jan 2025 22:26:54 +0100 Subject: [PATCH 04/57] use proc initAnnotation for every new game --- tcl/tools/annotate.tcl | 128 +++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 69 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index c5ea4f4d2..5658b2ce7 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -83,7 +83,7 @@ namespace eval ::annotation { ttk::checkbutton $f.annotate.cbBook -text $::tr(UseBook) -variable ::annotate(useAnalysisBook) set engList [::enginecfg::names ] if { $::annotate(engine) eq "" } { set ::annotate(engine) [lindex $engList 0] } - ttk::combobox $f.annotate.engine -width 25 -state readonly -values $engList -textvariable annotate(engine) + ttk::combobox $f.annotate.engine -width 26 -state readonly -values $engList -textvariable annotate(engine) # choose a book for analysis # load book names set bookPath $::scidBooksDir @@ -135,10 +135,6 @@ namespace eval ::annotation { ttk::checkbutton $f.comment.cbAddScore -text $::tr(AddScoreToShortAnnotations) -variable ::annotate(addScoreToShortAnnotations) ttk::checkbutton $f.comment.cbAddAnnotatorTag -text $::tr(addAnnotatorTag) -variable ::annotate(addAnnotatorTag) ttk::checkbutton $f.comment.cbMarkTactics -text $::tr(MarkTacticalExercises) -variable ::annotate(tacticalExercises) - # if {! $::analysis(uci1)} { - # set ::markTacticalExercises 0 - # $f.comment.cbMarkTactics configure -state disabled - # } pack $f.comment.scoreAll $f.comment.cbShortAnnotation $f.comment.cbAddScore \ $f.comment.cbAddAnnotatorTag $f.comment.cbMarkTactics -fill x -anchor w # batch annotation of consecutive games, and optional opening errors finder @@ -190,6 +186,30 @@ namespace eval ::annotation { bind $w { focus . } } + proc initAnnotation { } { + #reset engine + ::engine::send AnnoEngine NewGame [list analysis post_pv post_wdl] + # calc amount of moves to analyze + set firstmove [llength [sc_game moves]] + sc_game push copyfast + catch { sc_move forward 300 } + set anz [expr {[llength [sc_game moves]] - $firstmove}] + sc_game pop + .annotationDialog.f.running.progress configure -maximum $anz + set ::annotate(progress) 0 + set ::annotate(msg1) "Game [sc_game number]: [sc_game info white] - [sc_game info black]" + set ::annotate(msg2) "" + set ::annotate(prevscore) 0 + set ::annotate(prevmoves) "" + set ::annotate(score) 0 + set ::annotate(moves) "" + set ::annotate(scoremate) 0 + set ::annotate(prevscoremate) 0 + if { $::annotate(addAnnotatorTag) } { + appendAnnotator "$::annotate(engine) $::annotate(typ) $::annotate($::annotate(typ))" + } + } + proc runAnnotation { } { set f .annotationDialog.f grid $f.running -row 2 -column 0 -columnspan 2 -sticky we @@ -202,43 +222,22 @@ namespace eval ::annotation { set ::annotate(AnalysisBookName) [.annotationDialog.f.annotate.comboBooks get] set ::book::lastBook $::annotate(AnalysisBookName) - # tactical positions is selected, must be in multipv mode -# if {$::annotate(tacticalExercises)} { -# if { $::analysis(multiPVCount1) < 2} { - # TODO: Why not put it at the (apparent) minimum of 2? - # -# set ::analysis(multiPVCount1) 4 -# changePVSize 1 -# } -# } # Open the engine set config [::enginecfg::get $::annotate(engine)] lassign $config name cmd args wdir elo time url ::annotate(uci) options ::engine::setLogCmd AnnoEngine {} ::engine::connect AnnoEngine ::annotation::eng_messages $cmd {} + # tactical positions is selected, must be in multipv mode + if {$::annotate(tacticalExercises)} { + # make sure to use Multipv + lappend options "MultiPV 4" + } ::engine::send AnnoEngine SetOptions $options +puts "$options" while { 1 } { - ::engine::send AnnoEngine NewGame [list analysis post_pv post_wdl] - set firstmove [llength [sc_game moves]] - sc_game push copyfast - catch { sc_move forward 300 } - set anz [expr {[llength [sc_game moves]] - $firstmove}] - sc_game pop - $f.running.progress configure -maximum $anz - set ::annotate(progress) 0 - set ::annotate(msg1) "Game [sc_game number]: [sc_game info white] - [sc_game info black]" - set ::annotate(msg2) "" - set ::annotate(prevscore) 0 - set ::annotate(prevmoves) "" - set ::annotate(score) 0 - set ::annotate(moves) "" - set ::annotate(scoremate) 0 - set ::annotate(prevscoremate) 0 - if { $::annotate(addAnnotatorTag) } { - appendAnnotator "$::annotate(engine) $::annotate(typ) $::annotate($::annotate(typ))" - } + annotation::initAnnotation bookAnnotation if { $::annotate(OpeningErrors) && ([sc_pos moveNumber] < $::annotate(OpeningMoves) ) } { appendAnnotator "opBlunder [sc_pos moveNumber] ([sc_pos side])" @@ -366,8 +365,6 @@ namespace eval ::annotation { set tomove [sc_pos side] set gamemove [sc_game info previousMoveUCI] - #TODO - set skipEngineLine 0 # And this is his best line: lassign $::annotate(PV1) score score_type ::annotate(moves) if { $gamemove eq "" || $score eq "" } { return } @@ -426,7 +423,7 @@ namespace eval ::annotation { set isBlunder 1 } set absdeltamove [expr { abs($deltamove) } ] -puts "$tomove $gamemove $prevscore $score $absdeltamove $::annotate(PV1)" +puts "$tomove $gamemove $prevscore $score [string range $absdeltamove 0 6]$::annotate(PV1)" # to parse scores if the engine's name contains - or + chars (see sc_game_scores) set engine_name [string map {"-" " " "+" " "} $::annotate(engine)] @@ -488,38 +485,34 @@ puts "$tomove $gamemove $prevscore $score $absdeltamove $::annotate(PV1)" # Add position score nag sc_pos addNag [scoreToNag $score] - # Add the variation - if { $skipEngineLine == 0 } { - sc_move back - if { $::annotate(annotateBlunders) == "blundersonly" } { - # Add a diagram tag, but avoid doubles - # - if { [string first "D" "[sc_pos getNags]"] == -1 } { - sc_pos addNag "D" - } + sc_move back + if { $::annotate(annotateBlunders) == "blundersonly" } { + # Add a diagram tag, but avoid doubles + if { [string first "D" "[sc_pos getNags]"] == -1 } { + sc_pos addNag "D" } - if { $prevmoves != "" && ( $::annotate(annotateMoves) == "all" || $::annotate(annotateMoves) == "white" && $tomove == "black" || - $::annotate(annotateMoves) == "black" && $tomove == "white" )} { - sc_var create - # Add the starting move - sc_move_add [lrange $prevmoves 0 0] - # Add its score - if { ! $bestMoveIsMate } { - if { ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations) } { - sc_pos setComment "$prevtext" - } - } - # Add remaining moves - sc_move_add [lrange $prevmoves 1 end] - # Add position NAG, unless the line ends in mate - if { $::annotate(prevscoremate) == 0 } { - sc_pos addNag [scoreToNag $prevscore] + } + if { $prevmoves != "" && ( $::annotate(annotateMoves) == "all" || $::annotate(annotateMoves) == "white" && $tomove == "black" || + $::annotate(annotateMoves) == "black" && $tomove == "white" )} { + sc_var create + # Add the starting move + sc_move_add [lrange $prevmoves 0 0] + # Add its score + if { ! $bestMoveIsMate } { + if { ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations) } { + sc_pos setComment "$prevtext" } - sc_var exit } - sc_move forward + # Add remaining moves + sc_move_add [lrange $prevmoves 1 end] + # Add position NAG, unless the line ends in mate + if { $::annotate(prevscoremate) == 0 } { + sc_pos addNag [scoreToNag $prevscore] + } + sc_var exit } + sc_move forward } else { if { $isBlunder == 0 && $absdeltamove > $::informant("!?") } { sc_pos addNag "!?" @@ -535,7 +528,7 @@ puts "$tomove $gamemove $prevscore $score $absdeltamove $::annotate(PV1)" sc_var create sc_move addSan $gamemove if { ($::annotate(scoremate) == 0) && ( ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations)) } { - sc_pos setComment "$text" + sc_pos setComment "$text" } sc_move_add $moves if { $::annotate(scoremate) == 0 } { @@ -555,15 +548,12 @@ puts "$tomove $gamemove $prevscore $score $absdeltamove $::annotate(PV1)" ################################################################################ # Will add **** to any position considered as a tactical shot - # returns 1 if an exercise was marked, 0 if for some reason it was not (obvious move for example) + # check at which depth the tactical shot is found ################################################################################ proc markExercise { prevscore score nag} { sc_pos addNag $nag - if {!$::annotate(tacticalExercises)} { return 0 } - - # check at which depth the tactical shot is found # this assumes analysis by an UCI engine - if {! $::annotate(uci)} { return 0 } + if { ! $::annotate(uci) || ! $::annotate(tacticalExercises)} { return 0 } set deltamove [expr {$score + $prevscore}] # filter tactics so only those with high gains are kept From 368aa800933b25c9e3f74ff52345193eec901531 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 28 Jan 2025 09:32:48 +0100 Subject: [PATCH 05/57] Support for uci-engines only --- tcl/tools/annotate.tcl | 100 +++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 54 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 5658b2ce7..1195581b2 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -179,71 +179,75 @@ namespace eval ::annotation { set ::annotate(movetime) [expr {int($::annotateTime * 1000.0)}] set ::annotate(blunderThreshold) $::annotateBlunderThreshold set ::annotate(time) $::annotateTime - ::annotation::runAnnotation + set ::annotate(AnalysisBookName) [.annotationDialog.f.annotate.comboBooks get] + set msg [::annotation::initAnnotationEngine] + if { $msg eq "ok" } { + ::annotation::runAnnotation + } else { + tk_messageBox -title Scid -icon info -type ok -message $msg + } } pack $f.buttons.cancel $f.buttons.ok -side right -padx 5 -pady 5 focus $f.annotate.typ.spDelay bind $w { focus . } } - proc initAnnotation { } { + proc initGameAnnotation { } { #reset engine ::engine::send AnnoEngine NewGame [list analysis post_pv post_wdl] - # calc amount of moves to analyze + # calc amount of moves to analyze for progressbar set firstmove [llength [sc_game moves]] sc_game push copyfast catch { sc_move forward 300 } set anz [expr {[llength [sc_game moves]] - $firstmove}] sc_game pop .annotationDialog.f.running.progress configure -maximum $anz + #reset values set ::annotate(progress) 0 - set ::annotate(msg1) "Game [sc_game number]: [sc_game info white] - [sc_game info black]" - set ::annotate(msg2) "" set ::annotate(prevscore) 0 set ::annotate(prevmoves) "" set ::annotate(score) 0 set ::annotate(moves) "" set ::annotate(scoremate) 0 set ::annotate(prevscoremate) 0 + set ::annotate(msg2) "" + set ::annotate(msg1) "Game [sc_game number]: [sc_game info white] - [sc_game info black]" if { $::annotate(addAnnotatorTag) } { appendAnnotator "$::annotate(engine) $::annotate(typ) $::annotate($::annotate(typ))" } } - proc runAnnotation { } { - set f .annotationDialog.f - grid $f.running -row 2 -column 0 -columnspan 2 -sticky we - grid forget $f.annotate - grid forget $f.comment - grid forget $f.av - grid forget $f.batch - $f.buttons.ok configure -state disabled - set ::autoplayMode 1 - - set ::annotate(AnalysisBookName) [.annotationDialog.f.annotate.comboBooks get] - set ::book::lastBook $::annotate(AnalysisBookName) - + proc initAnnotationEngine { } { # Open the engine set config [::enginecfg::get $::annotate(engine)] - lassign $config name cmd args wdir elo time url ::annotate(uci) options + lassign $config name cmd args wdir elo time url uci options + if { ! $uci } { return "Only UCI-Engines are supported!" } ::engine::setLogCmd AnnoEngine {} ::engine::connect AnnoEngine ::annotation::eng_messages $cmd {} # tactical positions is selected, must be in multipv mode if {$::annotate(tacticalExercises)} { - # make sure to use Multipv lappend options "MultiPV 4" } ::engine::send AnnoEngine SetOptions $options -puts "$options" + return "ok" + } - while { 1 } { - annotation::initAnnotation - bookAnnotation - if { $::annotate(OpeningErrors) && ([sc_pos moveNumber] < $::annotate(OpeningMoves) ) } { - appendAnnotator "opBlunder [sc_pos moveNumber] ([sc_pos side])" - } - set ::autoplayMode 1 + proc runAnnotation { } { + set f .annotationDialog.f + grid forget $f.annotate + grid forget $f.comment + grid forget $f.av + grid forget $f.batch + # show progressbar and game infos + grid $f.running -row 2 -column 0 -columnspan 2 -sticky we + $f.buttons.ok configure -state disabled + set ::autoplayMode 1 + + set ::autoplayMode 1 + while { 1 } { + initGameAnnotation + makeBookAnnotation # Annotate all remaining moves of the game while 1 { set ::annotate(PV1) [list "" "" ""] @@ -259,9 +263,7 @@ puts "$options" if { $::annotate(batchMode) && $::autoplayMode } { #Batchmode: save game and load next game set gameNo [sc_game number] - if { $gameNo != 0 } { - sc_game save $gameNo - } + if { $gameNo != 0 } { sc_game save $gameNo } # See if we must advance to the next game if { $gameNo < $::annotate(batchEnd) } { incr gameNo @@ -275,7 +277,6 @@ puts "$options" } set ::autoplayMode 0 ::engine::close AnnoEngine - set ::annotate(progress) 99 destroy .annotationDialog } @@ -283,7 +284,7 @@ puts "$options" # Part of annotation process : will check the moves if they are in te book, and add a comment # when going out of it ################################################################################ - proc bookAnnotation { } { + proc makeBookAnnotation { } { if {$::annotate(useAnalysisBook)} { set prevbookmoves "" set bn [ file join $::scidBooksDir $::annotate(AnalysisBookName) ] @@ -314,6 +315,9 @@ puts "$options" } else { sc_pos setComment "[sc_pos getComment]$verboseLastBookMove" } + if { $::annotate(OpeningErrors) && ([sc_pos moveNumber] < $::annotate(OpeningMoves) ) } { + appendAnnotator "opBlunder [sc_pos moveNumber] ([sc_pos side])" + } #TODO is this needed? # if { ! $theCatch } { # resetAnalysis @@ -385,12 +389,12 @@ puts "$options" set ::annotate(scoremate) 0 } # For non-uci lines, trim space characters in .[ *][...] - set moves [regsub -all {\. *} $moves {.}] +# set moves [regsub -all {\. *} $moves {.}] # The best line we could have followed, and the game move we just played instead, are here: set prevmoves $::annotate(prevmoves) # For non-uci lines, trim space characters in .[ *][...] - set prevmoves [regsub -all {\. *} $prevmoves {.}] +# set prevmoves [regsub -all {\. *} $prevmoves {.}] # We will add a closing line at the end of variation or game set addClosingLine 0 @@ -497,7 +501,7 @@ puts "$tomove $gamemove $prevscore $score [string range $absdeltamove 0 6]$::ann $::annotate(annotateMoves) == "black" && $tomove == "white" )} { sc_var create # Add the starting move - sc_move_add [lrange $prevmoves 0 0] + ::uci::sc_move_add [lrange $prevmoves 0 0] # Add its score if { ! $bestMoveIsMate } { if { ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations) } { @@ -505,7 +509,7 @@ puts "$tomove $gamemove $prevscore $score [string range $absdeltamove 0 6]$::ann } } # Add remaining moves - sc_move_add [lrange $prevmoves 1 end] + ::uci::sc_move_add [lrange $prevmoves 1 end] # Add position NAG, unless the line ends in mate if { $::annotate(prevscoremate) == 0 } { sc_pos addNag [scoreToNag $prevscore] @@ -530,7 +534,7 @@ puts "$tomove $gamemove $prevscore $score [string range $absdeltamove 0 6]$::ann if { ($::annotate(scoremate) == 0) && ( ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations)) } { sc_pos setComment "$text" } - sc_move_add $moves + ::uci::sc_move_add $moves if { $::annotate(scoremate) == 0 } { sc_pos addNag [scoreToNag $score] } @@ -552,8 +556,7 @@ puts "$tomove $gamemove $prevscore $score [string range $absdeltamove 0 6]$::ann ################################################################################ proc markExercise { prevscore score nag} { sc_pos addNag $nag - # this assumes analysis by an UCI engine - if { ! $::annotate(uci) || ! $::annotate(tacticalExercises)} { return 0 } + if { ! $::annotate(tacticalExercises)} { return 0 } set deltamove [expr {$score + $prevscore}] # filter tactics so only those with high gains are kept @@ -576,8 +579,8 @@ puts "$tomove $gamemove $prevscore $score [string range $absdeltamove 0 6]$::ann } # The best move does not lose position. - if {[sc_pos side] == "black" && $score < [expr 0.0 - $::informant("+/-")] } { return 0 } - if {[sc_pos side] == "white" && $score > $::informant("+/-") } { return 0} +# if {[sc_pos side] == "black" && $score < [expr 0.0 - $::informant("+/-")] } { return 0 } +# if {[sc_pos side] == "white" && $score > $::informant("+/-") } { return 0} # Move is not obvious: check that it is not the first move guessed at low depths set pv [ lindex [ lindex $::annotate(PV1) 2 ] 0 ] @@ -591,6 +594,7 @@ puts "BM $bm0 $bm1 $bm2 $bm3 $::annotate(PV1)" if { $bm0 == $bm1 && $bm0 == $bm2 && $bm0 == $bm3 } { return 0 } +puts "prev $prevscore pv1 $score pv2 $sc2" # find what time is needed to get the solution (use internal analyze function) set timer {1 2 5 10 50 100 200 1000} @@ -653,16 +657,4 @@ puts "BM $bm0 $bm1 $bm2 $bm3 $::annotate(PV1)" } return $ana_nagList($i) } - - ################################################################################ - # If UCI engine, add move through a dedicated function in uci namespace - # returns the error caught by catch - ################################################################################ - proc sc_move_add { moves } { - if { $::annotate(uci) } { - return [::uci::sc_move_add $moves] - } else { - return [ catch { sc_move addSan $moves } ] - } - } } From 9a3886609da1ff877d5b693e670d59fed1ede6d7 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 28 Jan 2025 16:32:30 +0100 Subject: [PATCH 06/57] new proc annotateGame, check for engine connection and minor code ajustments --- tcl/tools/annotate.tcl | 152 ++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 79 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 1195581b2..ff01cde0b 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -12,11 +12,11 @@ #finish game function namespace eval ::annotation { + # Typ may be "movetime": time per move or "depth": analyse till depth is reached + set ::annotate(typ) "movetime" set ::annotate(movetime) 1000 set ::annotate(time) 1 set ::annotate(depth) 20 - # Typ may be "movetime": time per move or "depth": analyse till depth is reached - set ::annotate(typ) "movetime" set ::annotate(engine) "" set ::annotate(progress) 25 set ::annotate(blunderThreshold) 0.5 @@ -25,6 +25,7 @@ namespace eval ::annotation { set ::annotate(scoreAllMoves) 1 set ::annotate(annotateMode) 0 set ::annotate(useAnalysisBook) 0 + set ::annotate(AnalysisBookName) "" set ::annotate(BookSlot) 1 set ::annotate(tacticalExercises) 0 set ::annotate(addAnnotatorTag) 1 @@ -83,12 +84,11 @@ namespace eval ::annotation { ttk::checkbutton $f.annotate.cbBook -text $::tr(UseBook) -variable ::annotate(useAnalysisBook) set engList [::enginecfg::names ] if { $::annotate(engine) eq "" } { set ::annotate(engine) [lindex $engList 0] } - ttk::combobox $f.annotate.engine -width 26 -state readonly -values $engList -textvariable annotate(engine) + ttk::combobox $f.annotate.engine -width 26 -state readonly -values $engList -textvariable ::annotate(engine) # choose a book for analysis # load book names set bookPath $::scidBooksDir set bookList [ lsort -dictionary [ glob -nocomplain -directory $bookPath *.bin ] ] - # No book found if { [llength $bookList] == 0 } { set ::annotate(useAnalysisBook) 0 @@ -97,14 +97,15 @@ namespace eval ::annotation { set tmp {} set idx 0 set i 0 - foreach file $bookList { + foreach file $bookList { lappend tmp [ file tail $file ] if {$::book::lastBook == [ file tail $file ] } { set idx $i } incr i } - ttk::combobox $f.annotate.comboBooks -width 12 -values $tmp + if { $::annotate(AnalysisBookName) eq "" } { set ::annotate(AnalysisBookName) [lindex $tmp $idx] } + ttk::combobox $f.annotate.comboBooks -width 12 -values $tmp -textvariable ::annotate(AnalysisBookName) catch { $f.annotate.comboBooks current $idx } pack $f.annotate.comboBooks -side bottom -anchor w -padx 20 pack $f.annotate.cbBook -side bottom -anchor w @@ -131,9 +132,9 @@ namespace eval ::annotation { ttk::labelframe $f.comment -text $::tr(Comments) # Checkmark to enable all-move-scoring ttk::checkbutton $f.comment.scoreAll -text $::tr(ScoreAllMoves) -variable ::annotate(scoreAllMoves) - ttk::checkbutton $f.comment.cbShortAnnotation -text $::tr(ShortAnnotations) -variable ::annotate(annotateShort) - ttk::checkbutton $f.comment.cbAddScore -text $::tr(AddScoreToShortAnnotations) -variable ::annotate(addScoreToShortAnnotations) - ttk::checkbutton $f.comment.cbAddAnnotatorTag -text $::tr(addAnnotatorTag) -variable ::annotate(addAnnotatorTag) + ttk::checkbutton $f.comment.cbShortAnnotation -text $::tr(ShortAnnotations) -variable ::annotate(annotateShort) + ttk::checkbutton $f.comment.cbAddScore -text $::tr(AddScoreToShortAnnotations) -variable ::annotate(addScoreToShortAnnotations) + ttk::checkbutton $f.comment.cbAddAnnotatorTag -text $::tr(addAnnotatorTag) -variable ::annotate(addAnnotatorTag) ttk::checkbutton $f.comment.cbMarkTactics -text $::tr(MarkTacticalExercises) -variable ::annotate(tacticalExercises) pack $f.comment.scoreAll $f.comment.cbShortAnnotation $f.comment.cbAddScore \ $f.comment.cbAddAnnotatorTag $f.comment.cbMarkTactics -fill x -anchor w @@ -151,11 +152,11 @@ namespace eval ::annotation { grid $f.batch -row 1 -column 1 -pady { 10 0 } -sticky nswe -padx { 10 0 } grid $f.buttons -row 3 -column 1 -sticky we - set to [sc_base numGames $::curr_db] - if {$to <1} { set to 1} + set ::annotate(batchEnd) [sc_base numGames $::curr_db] + if {$::annotate(batchEnd) <1} { set ::annotate(batchEnd) 1 } ttk::checkbutton $f.batch.cbBatch -text $::tr(AnnotateSeveralGames) -variable ::annotate(batchMode) ttk::spinbox $f.batch.spBatchEnd -width 8 -textvariable ::annotate(batchEnd) \ - -from 1 -to $to -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } + -from 1 -to $::annotate(batchEnd) -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } ttk::checkbutton $f.batch.cbBatchOpening -text $::tr(FindOpeningErrors) -variable ::annotate(OpeningErrors) ttk::spinbox $f.batch.spBatchOpening -width 2 -textvariable ::annotate(OpeningMoves) \ -from 10 -to 20 -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } @@ -165,7 +166,6 @@ namespace eval ::annotation { pack $f.batch.cbBatchOpening -side top -anchor w pack $f.batch.spBatchOpening -side left -anchor w -padx { 20 4 } pack $f.batch.lBatchOpening -side left - set ::annotate(batchEnd) $to ttk::button $f.buttons.cancel -text $::tr(Cancel) -command { if { $::autoplayMode } { @@ -179,7 +179,6 @@ namespace eval ::annotation { set ::annotate(movetime) [expr {int($::annotateTime * 1000.0)}] set ::annotate(blunderThreshold) $::annotateBlunderThreshold set ::annotate(time) $::annotateTime - set ::annotate(AnalysisBookName) [.annotationDialog.f.annotate.comboBooks get] set msg [::annotation::initAnnotationEngine] if { $msg eq "ok" } { ::annotation::runAnnotation @@ -192,6 +191,7 @@ namespace eval ::annotation { bind $w { focus . } } + # reset values for every game proc initGameAnnotation { } { #reset engine ::engine::send AnnoEngine NewGame [list analysis post_pv post_wdl] @@ -217,21 +217,36 @@ namespace eval ::annotation { } } + # Open the engine and configure it proc initAnnotationEngine { } { - # Open the engine set config [::enginecfg::get $::annotate(engine)] lassign $config name cmd args wdir elo time url uci options if { ! $uci } { return "Only UCI-Engines are supported!" } ::engine::setLogCmd AnnoEngine {} ::engine::connect AnnoEngine ::annotation::eng_messages $cmd {} # tactical positions is selected, must be in multipv mode - if {$::annotate(tacticalExercises)} { - lappend options "MultiPV 4" - } + if {$::annotate(tacticalExercises)} { lappend options "MultiPV 4" } ::engine::send AnnoEngine SetOptions $options return "ok" } + proc annotateGame { } { + initGameAnnotation + makeBookAnnotation + # Annotate all remaining moves of the game + while 1 { + set ::annotate(PV1) [list "" "" ""] + ::engine::send AnnoEngine Go [list [sc_game UCI_currentPos] [list $::annotate(typ) $::annotate($::annotate(typ))]] + vwait ::annotate(move_done) + addAnnotation + incr ::annotate(progress) + if {[sc_pos isAt end]} break + sc_move forward + ::notify::PosChanged -pgn + if { ! $::autoplayMode } { break } + } + } + proc runAnnotation { } { set f .annotationDialog.f grid forget $f.annotate @@ -241,38 +256,16 @@ namespace eval ::annotation { # show progressbar and game infos grid $f.running -row 2 -column 0 -columnspan 2 -sticky we $f.buttons.ok configure -state disabled - set ::autoplayMode 1 - set ::autoplayMode 1 - while { 1 } { - initGameAnnotation - makeBookAnnotation - # Annotate all remaining moves of the game - while 1 { - set ::annotate(PV1) [list "" "" ""] - ::engine::send AnnoEngine Go [list [sc_game UCI_currentPos] [list $::annotate(typ) $::annotate($::annotate(typ))]] - vwait ::engine_done - addAnnotation - incr ::annotate(progress) - if {[sc_pos isAt end]} break - sc_move forward - ::notify::PosChanged -pgn - if { ! $::autoplayMode } { break } - } - if { $::annotate(batchMode) && $::autoplayMode } { - #Batchmode: save game and load next game + annotateGame + if { $::annotate(batchMode)} { + while { $::autoplayMode && ([sc_game number] < $::annotate(batchEnd)) } { set gameNo [sc_game number] if { $gameNo != 0 } { sc_game save $gameNo } - # See if we must advance to the next game - if { $gameNo < $::annotate(batchEnd) } { - incr gameNo - sc_game load $gameNo - } else { - break - } - } else { - break + incr gameNo + sc_game load $gameNo + annotateGame } } set ::autoplayMode 0 @@ -388,13 +381,8 @@ namespace eval ::annotation { set ::annotate(score) $score set ::annotate(scoremate) 0 } - # For non-uci lines, trim space characters in .[ *][...] -# set moves [regsub -all {\. *} $moves {.}] - # The best line we could have followed, and the game move we just played instead, are here: set prevmoves $::annotate(prevmoves) - # For non-uci lines, trim space characters in .[ *][...] -# set prevmoves [regsub -all {\. *} $prevmoves {.}] # We will add a closing line at the end of variation or game set addClosingLine 0 @@ -405,8 +393,8 @@ namespace eval ::annotation { # This is the score we could have had if we had played our best move set prevscore $::annotate(prevscore) - # Note that the engine's judgement is in absolute terms, a negative score - # being favorable to black, a positive score favorable to white + # Note that the engine's judgement is in relative terms, a negative score + # being favorable to opponent, a positive score favorable to player # Looking primarily for blunders, we are interested in the score decay, # which, for white, is (previous-current) set deltamove [expr {$prevscore + $score}] @@ -427,7 +415,7 @@ namespace eval ::annotation { set isBlunder 1 } set absdeltamove [expr { abs($deltamove) } ] -puts "$tomove $gamemove $prevscore $score [string range $absdeltamove 0 6]$::annotate(PV1)" + puts "$tomove $gamemove $prevscore $score [string range $absdeltamove 0 6]$::annotate(PV1)" # to parse scores if the engine's name contains - or + chars (see sc_game_scores) set engine_name [string map {"-" " " "+" " "} $::annotate(engine)] @@ -436,17 +424,17 @@ puts "$tomove $gamemove $prevscore $score [string range $absdeltamove 0 6]$::ann if { $::annotate(scoremate) != 0 } { set text [format "M%d" [expr abs($::annotate(scoremate))]] } else { - set wscore $score - if { $tomove eq "black" } {set wscore [expr 0.0 - $score] } - set text "\[%eval [format "%+.2f" $wscore]\]" + set wscore [format "%+.2f" $score] + if { $tomove eq "black" } {set wscore [expr 0.0 - $wscore] } + set text "\[%eval $wscore\]" } # And for the (missed?) chance if { $::annotate(prevscoremate) != 0 } { set prevtext [format "M%d" [expr abs($::annotate(prevscoremate))]] } else { - set wprevscore $prevscore - if { $tomove eq "white" } {set wprevscore [expr 0.0 - $prevscore] } - set prevtext "\[%eval [format "%+.2f" $wprevscore]\]" + set wprevscore [format "%+.2f" $prevscore] + if { $tomove eq "white" } {set wprevscore [expr 0.0 - $wprevscore] } + set prevtext "\[%eval $wprevscore\]" } # Must we annotate our own moves? If no, we bail out unless @@ -497,16 +485,15 @@ puts "$tomove $gamemove $prevscore $score [string range $absdeltamove 0 6]$::ann sc_pos addNag "D" } } - if { $prevmoves != "" && ( $::annotate(annotateMoves) == "all" || $::annotate(annotateMoves) == "white" && $tomove == "black" || + if { $prevmoves != "" && ( $::annotate(annotateMoves) == "all" || + $::annotate(annotateMoves) == "white" && $tomove == "black" || $::annotate(annotateMoves) == "black" && $tomove == "white" )} { sc_var create # Add the starting move ::uci::sc_move_add [lrange $prevmoves 0 0] # Add its score - if { ! $bestMoveIsMate } { - if { ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations) } { - sc_pos setComment "$prevtext" - } + if { ! $bestMoveIsMate && ( ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations) ) } { + sc_pos setComment "$prevtext" } # Add remaining moves ::uci::sc_move_add [lrange $prevmoves 1 end] @@ -542,7 +529,6 @@ puts "$tomove $gamemove $prevscore $score [string range $absdeltamove 0 6]$::ann # Now up to the end of the game ::move::Forward } - set ::annotate(prevscore) $::annotate(score) set ::annotate(prevmoves) $::annotate(moves) set ::annotate(prevscoremate) $::annotate(scoremate) @@ -575,7 +561,7 @@ puts "$tomove $gamemove $prevscore $score [string range $absdeltamove 0 6]$::ann # I reject exercises when there are e.g. moves leading to +9, +7 and +5 scores) if { [expr $score * $sc2] > 0.0 && [expr abs($score)] > $::informant("+-") && [expr abs($sc2)] > $::informant("+-") } { puts diffscore - return 0 +# return 0 } # The best move does not lose position. @@ -623,16 +609,26 @@ puts "prev $prevscore pv1 $score pv2 $sc2" proc ::annotation::eng_messages {msg} { lassign $msg msgType msgData - if {$msgType eq "InfoPV"} { - lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv - if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } - set ::annotate(PV$multipv) [list $score $score_type $pv] - if { $multipv == 1 } { - set ::annotate(msg2) [string range "Move $::annotate(progress) Score: $score\nLine: $pv" 0 70] + + switch $msgType { + "InfoPV" { + lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv + if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } + set ::annotate(PV$multipv) [list $score $score_type $pv] + if { $multipv == 1 } { + set ::annotate(msg2) [string range "Move $::annotate(progress) Score: $score\nLine: $pv" 0 70] + } + } + "InfoBestMove" { + lassign $msgData ::engineBestMove + set ::annotate(move_done) 1 + } + "InfoDisconnected" { + lassign $msgData errorMsg + if {$errorMsg eq ""} { set errorMsg "The connection with the engine terminated unexpectedly." } + tk_messageBox -icon warning -type ok -parent . -message $errorMsg + set ::autoplayMode 0 } - } elseif {$msgType eq "InfoBestMove"} { - lassign $msgData ::engineBestMove - set ::engine_done 1 } } # Informant index strings @@ -647,9 +643,7 @@ puts "prev $prevscore pv1 $score pv2 $sc2" # Find the score in the informant map set tmp [expr { abs( $score ) }] for { set i 0 } { $i < 4 } { incr i } { - if { $tmp < $::informant("$ana_informantList($i)") } { - break - } + if { $tmp < $::informant("$ana_informantList($i)") } { break } } # Jump into negative counterpart if { $score < 0.0 } { From dcaa67d4613bdf4058beeda889f2738adb051afb Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 28 Jan 2025 19:34:01 +0100 Subject: [PATCH 07/57] code cleanup, show SAN in progress window --- tcl/tools/annotate.tcl | 47 +++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index ff01cde0b..522074e63 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -2,14 +2,15 @@ ### annotate.tcl: part of Scid. ### This file is part of Scid (Shane's Chess Information Database). ### Copyright (C) 2025 Uwe Klimmek -### uses code from Fulvio Benini https://github.com/benini/chess_accuracy -###################################################################### +### uses code from Fulvio Benini https://github.com/benini/chess_accuracy and analysis.tcl +########################################################################################## ### Annotate Dialog: uses a chess engine to analyze and annotate a chess game. #TODO -#check Tactical Exercise only for uci und multipv 4 +#improve Tactical Exercise #use analyse depth, actual ignored -#finish game function +#"finish game" function +#accuracy function namespace eval ::annotation { # Typ may be "movetime": time per move or "depth": analyse till depth is reached @@ -31,7 +32,6 @@ namespace eval ::annotation { set ::annotate(addAnnotatorTag) 1 set ::annotate(OpeningErrors) 0 set ::annotate(OpeningMoves) 0 - set ::annotate(prevdepth) 0 set ::annotate(annotateShort) 1 set ::annotate(addScoreToShortAnnotations) 1 set ::annotate(batchMode) 0 @@ -61,7 +61,7 @@ namespace eval ::annotation { trace variable ::annotateTime w {::utils::validate::Regexp {^[0-9]*\.?[0-9]*$}} win::createDialog $w - ::setTitle $w "Scid: $::tr(Annotate) Game" + ::setTitle $w "Scid: $::tr(Annotate)" catch {grab $w} wm resizable $w 0 0 set f [ttk::frame $w.f] @@ -291,35 +291,21 @@ namespace eval ::annotation { lassign [sc_book moves $::annotate(BookSlot)] bookmoves } sc_book close $::annotate(BookSlot) - set ::wentOutOfBook 1 - set verboseMoveOutOfBook " $::tr(MoveOutOfBook)" - set verboseLastBookMove " $::tr(LastBookMove)" - - set theCatch 0 if { [ string match -nocase "*[sc_game info previousMoveNT]*" $prevbookmoves ] != 1 } { if {$prevbookmoves != ""} { - sc_pos setComment "[sc_pos getComment]$verboseMoveOutOfBook [::trans $prevbookmoves]" + sc_pos setComment "[sc_pos getComment] $::tr(LastBookMove) [::trans $prevbookmoves]" } else { - sc_pos setComment "[sc_pos getComment]$verboseMoveOutOfBook" + sc_pos setComment "[sc_pos getComment] $::tr(LastBookMove)" } # last move was out of book: it needs to be analyzed, so take back - set theCatch [catch {sc_move back 1}] + sc_move back } else { - sc_pos setComment "[sc_pos getComment]$verboseLastBookMove" + sc_pos setComment "[sc_pos getComment] $::tr(MoveOutOfBook)" } if { $::annotate(OpeningErrors) && ([sc_pos moveNumber] < $::annotate(OpeningMoves) ) } { appendAnnotator "opBlunder [sc_pos moveNumber] ([sc_pos side])" } -#TODO is this needed? -# if { ! $theCatch } { -# resetAnalysis -# updateBoard -pgn -# } -# set analysis(prevscore$n) $analysis(score$n) -# set analysis(prevmoves$n) $analysis(moves$n) -# set analysis(prevscoremate$n) $analysis(scoremate$n) -# set analysis(prevdepth$n) $analysis(depth$n) } } @@ -444,7 +430,6 @@ namespace eval ::annotation { set ::annotate(prevscore) $::annotate(score) set ::annotate(prevmoves) $::annotate(moves) set ::annotate(prevscoremate) $::annotate(scoremate) - set ::annotate(prevdepth) $::annotate(depth) updateBoard -pgn } @@ -532,7 +517,6 @@ namespace eval ::annotation { set ::annotate(prevscore) $::annotate(score) set ::annotate(prevmoves) $::annotate(moves) set ::annotate(prevscoremate) $::annotate(scoremate) - set ::annotate(prevdepth) $::annotate(depth) updateBoard -pgn } @@ -565,8 +549,8 @@ namespace eval ::annotation { } # The best move does not lose position. -# if {[sc_pos side] == "black" && $score < [expr 0.0 - $::informant("+/-")] } { return 0 } -# if {[sc_pos side] == "white" && $score > $::informant("+/-") } { return 0} + if {[sc_pos side] == "black" && $score < [expr 0.0 - $::informant("+/-")] } { return 0 } + if {[sc_pos side] == "white" && $score > $::informant("+/-") } { return 0} # Move is not obvious: check that it is not the first move guessed at low depths set pv [ lindex [ lindex $::annotate(PV1) 2 ] 0 ] @@ -609,20 +593,23 @@ puts "prev $prevscore pv1 $score pv2 $sc2" proc ::annotation::eng_messages {msg} { lassign $msg msgType msgData - switch $msgType { "InfoPV" { lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } set ::annotate(PV$multipv) [list $score $score_type $pv] if { $multipv == 1 } { - set ::annotate(msg2) [string range "Move $::annotate(progress) Score: $score\nLine: $pv" 0 70] + set pv [sc_pos coordToSAN $::annotate(position) $pv] + set ::annotate(msg2) [string range "Depth: $depth/$seldepth Move $::annotate(progress) Score: $score\nLine: $pv" 0 70] } } "InfoBestMove" { lassign $msgData ::engineBestMove set ::annotate(move_done) 1 } + "InfoGo" { + lassign $msgData ::annotate(position) + } "InfoDisconnected" { lassign $msgData errorMsg if {$errorMsg eq ""} { set errorMsg "The connection with the engine terminated unexpectedly." } From 771e2acbf53fb0a6224cbe95ea8e0aa9f43d12dc Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 28 Jan 2025 21:49:59 +0100 Subject: [PATCH 08/57] use sc_move addSAN --- tcl/tools/annotate.tcl | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 522074e63..0fc77ba2a 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -401,7 +401,6 @@ namespace eval ::annotation { set isBlunder 1 } set absdeltamove [expr { abs($deltamove) } ] - puts "$tomove $gamemove $prevscore $score [string range $absdeltamove 0 6]$::annotate(PV1)" # to parse scores if the engine's name contains - or + chars (see sc_game_scores) set engine_name [string map {"-" " " "+" " "} $::annotate(engine)] @@ -475,13 +474,13 @@ namespace eval ::annotation { $::annotate(annotateMoves) == "black" && $tomove == "white" )} { sc_var create # Add the starting move - ::uci::sc_move_add [lrange $prevmoves 0 0] + sc_move addSan [lrange $prevmoves 0 0] # Add its score if { ! $bestMoveIsMate && ( ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations) ) } { sc_pos setComment "$prevtext" } # Add remaining moves - ::uci::sc_move_add [lrange $prevmoves 1 end] + sc_move addSan [lrange $prevmoves 1 end] # Add position NAG, unless the line ends in mate if { $::annotate(prevscoremate) == 0 } { sc_pos addNag [scoreToNag $prevscore] @@ -506,7 +505,7 @@ namespace eval ::annotation { if { ($::annotate(scoremate) == 0) && ( ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations)) } { sc_pos setComment "$text" } - ::uci::sc_move_add $moves + sc_move addSan $moves if { $::annotate(scoremate) == 0 } { sc_pos addNag [scoreToNag $score] } @@ -549,22 +548,24 @@ namespace eval ::annotation { } # The best move does not lose position. - if {[sc_pos side] == "black" && $score < [expr 0.0 - $::informant("+/-")] } { return 0 } - if {[sc_pos side] == "white" && $score > $::informant("+/-") } { return 0} +# if {([sc_pos side] == "black") && ($score < [expr 0.0 - $::informant("+/-")]) } { return 0 } +# if {([sc_pos side] == "white") && ($score > $::informant("+/-")) } { return 0} # Move is not obvious: check that it is not the first move guessed at low depths set pv [ lindex [ lindex $::annotate(PV1) 2 ] 0 ] - set bm0 [lindex $pv 0] + # bm0 must SAN, pv is UCI: convert + set bm0 [string range [lindex $pv 0] 0 3] + set bm0 [sc_pos coordToSAN $::annotate(position) $bm0] + set bm0 [string range $bm0 [expr [string first "." $bm0] + 1] end] + foreach depth {1 2 3} { set res [ sc_pos analyze -time 1000 -hashkb 32 -pawnkb 1 -searchdepth $depth ] set bm$depth [lindex $res 1] } - #TODO Bm0 is UCI a2c4 not Bc4, reurn from sc_pos is pgn -puts "BM $bm0 $bm1 $bm2 $bm3 $::annotate(PV1)" if { $bm0 == $bm1 && $bm0 == $bm2 && $bm0 == $bm3 } { return 0 } -puts "prev $prevscore pv1 $score pv2 $sc2" + puts "prev $prevscore pv1 $score pv2 $sc2 BM $bm0 $bm1 $bm2 $bm3 [string range $::annotate(PV1) 0 40]" # find what time is needed to get the solution (use internal analyze function) set timer {1 2 5 10 50 100 200 1000} From bcc715f3f627e9b7e978991753fe9610dcbd03ab Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 29 Jan 2025 17:28:14 +0100 Subject: [PATCH 09/57] bachmode optimized --- tcl/tools/annotate.tcl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 0fc77ba2a..8942045cc 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -258,15 +258,15 @@ namespace eval ::annotation { $f.buttons.ok configure -state disabled set ::autoplayMode 1 + set gameNo [sc_game number] + if { $gameNo == 0 } { return } annotateGame - if { $::annotate(batchMode)} { - while { $::autoplayMode && ([sc_game number] < $::annotate(batchEnd)) } { - set gameNo [sc_game number] - if { $gameNo != 0 } { sc_game save $gameNo } - incr gameNo - sc_game load $gameNo - annotateGame - } + while { $::annotate(batchMode)} { + sc_game save $gameNo + incr gameNo + if { ! $::autoplayMode || $gameNo > $::annotate(batchEnd) } { break } + sc_game load $gameNo + annotateGame } set ::autoplayMode 0 ::engine::close AnnoEngine From 8f0920644fc40a117d30d4fc7ee0b69c01c713c3 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 29 Jan 2025 18:29:54 +0100 Subject: [PATCH 10/57] add second progressbar for batchmode gui optimized --- tcl/tools/annotate.tcl | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 8942045cc..c7791dfa1 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -19,7 +19,7 @@ namespace eval ::annotation { set ::annotate(time) 1 set ::annotate(depth) 20 set ::annotate(engine) "" - set ::annotate(progress) 25 + set ::annotate(progress) 0 set ::annotate(blunderThreshold) 0.5 set ::annotate(annotateMoves) all set ::annotate(annotateBlunders) blundersonly @@ -38,6 +38,7 @@ namespace eval ::annotation { set ::annotate(batchEnd) 0 set ::annotate(msg1) "" set ::annotate(msg2) "" + set ::annotate(msg3) "" set ::annotate(prevscore) 0 set ::annotate(prevmoves) "" set ::annotate(score) 0 @@ -142,10 +143,16 @@ namespace eval ::annotation { ttk::labelframe $f.batch -text "Batch Annotation" ttk::frame $f.buttons ttk::frame $f.running - ttk::label $f.running.line1 -textvariable ::annotate(msg1) -width 50 -anchor center - ttk::label $f.running.line2 -textvariable ::annotate(msg2) -width 50 - ttk::progressbar $f.running.progress -variable annotate(progress) -orient horizontal -length 600 - pack $f.running.line1 $f.running.line2 $f.running.progress -side top -anchor w + ttk::label $f.running.line1 -textvariable ::annotate(msg1) -width 60 + ttk::label $f.running.line2 -textvariable ::annotate(msg2) -width 10 + ttk::label $f.running.line3 -textvariable ::annotate(msg3) -width 10 + ttk::progressbar $f.running.progress -variable ::annotate(progress) -orient horizontal -length 600 + ttk::progressbar $f.running.games -variable ::annotate(games) -orient horizontal -length 600 + grid $f.running.line1 -row 0 -column 1 -sticky w -pady { 0 10 } + grid $f.running.line2 -row 1 -column 0 -sticky w + grid $f.running.line3 -row 2 -column 0 -sticky w + grid $f.running.games -row 1 -column 1 -sticky w + grid $f.running.progress -row 2 -column 1 -sticky w grid $f.annotate -row 0 -column 0 -pady { 0 10 } -sticky nswe -padx { 0 10 } grid $f.comment -row 0 -column 1 -pady { 0 10 } -sticky nswe -padx { 10 0 } grid $f.av -row 1 -column 0 -pady { 10 0 } -sticky nswe -padx { 0 10 } @@ -210,8 +217,8 @@ namespace eval ::annotation { set ::annotate(moves) "" set ::annotate(scoremate) 0 set ::annotate(prevscoremate) 0 - set ::annotate(msg2) "" - set ::annotate(msg1) "Game [sc_game number]: [sc_game info white] - [sc_game info black]" + set ::annotate(msg1) "$::tr(game) [sc_game number]: [sc_game info white] - [sc_game info black]" + set ::annotate(msg3) "$::tr(move)" if { $::annotate(addAnnotatorTag) } { appendAnnotator "$::annotate(engine) $::annotate(typ) $::annotate($::annotate(typ))" } @@ -234,12 +241,13 @@ namespace eval ::annotation { initGameAnnotation makeBookAnnotation # Annotate all remaining moves of the game - while 1 { + while { 1 } { set ::annotate(PV1) [list "" "" ""] ::engine::send AnnoEngine Go [list [sc_game UCI_currentPos] [list $::annotate(typ) $::annotate($::annotate(typ))]] vwait ::annotate(move_done) addAnnotation incr ::annotate(progress) + set ::annotate(msg3) "$::tr(move) $::annotate(progress)" if {[sc_pos isAt end]} break sc_move forward ::notify::PosChanged -pgn @@ -249,11 +257,14 @@ namespace eval ::annotation { proc runAnnotation { } { set f .annotationDialog.f - grid forget $f.annotate - grid forget $f.comment - grid forget $f.av - grid forget $f.batch + grid forget $f.annotate $f.comment $f.av $f.batch + pack forget $f.buttons.ok # show progressbar and game infos + set ::annotate(games) 1 + set ::annotate(msg2) "$::tr(game) 1" + set gameNo [sc_game number] + $f.running.games configure -maximum [expr {$::annotate(batchEnd) - $gameNo + 1}] + if {!$::annotate(batchMode)} { grid forget $f.running.games $f.running.line2 } grid $f.running -row 2 -column 0 -columnspan 2 -sticky we $f.buttons.ok configure -state disabled @@ -261,9 +272,11 @@ namespace eval ::annotation { set gameNo [sc_game number] if { $gameNo == 0 } { return } annotateGame - while { $::annotate(batchMode)} { + while {$::annotate(batchMode)} { sc_game save $gameNo incr gameNo + incr ::annotate(games) + set ::annotate(msg2) "$::tr(game) $::annotate(games)" if { ! $::autoplayMode || $gameNo > $::annotate(batchEnd) } { break } sc_game load $gameNo annotateGame @@ -601,7 +614,6 @@ namespace eval ::annotation { set ::annotate(PV$multipv) [list $score $score_type $pv] if { $multipv == 1 } { set pv [sc_pos coordToSAN $::annotate(position) $pv] - set ::annotate(msg2) [string range "Depth: $depth/$seldepth Move $::annotate(progress) Score: $score\nLine: $pv" 0 70] } } "InfoBestMove" { From 45e4bd637dbc61f6079e5d46a9822d85f567c759 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 29 Jan 2025 21:03:12 +0100 Subject: [PATCH 11/57] code cleanup --- tcl/tools/annotate.tcl | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index c7791dfa1..5e1d3d3e8 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -8,7 +8,6 @@ #TODO #improve Tactical Exercise -#use analyse depth, actual ignored #"finish game" function #accuracy function namespace eval ::annotation { @@ -212,12 +211,13 @@ namespace eval ::annotation { #reset values set ::annotate(progress) 0 set ::annotate(prevscore) 0 - set ::annotate(prevmoves) "" set ::annotate(score) 0 - set ::annotate(moves) "" set ::annotate(scoremate) 0 set ::annotate(prevscoremate) 0 + set ::annotate(prevmoves) "" + set ::annotate(moves) "" set ::annotate(msg1) "$::tr(game) [sc_game number]: [sc_game info white] - [sc_game info black]" + set ::annotate(msg2) "$::tr(game) $::annotate(games)" set ::annotate(msg3) "$::tr(move)" if { $::annotate(addAnnotatorTag) } { appendAnnotator "$::annotate(engine) $::annotate(typ) $::annotate($::annotate(typ))" @@ -248,10 +248,9 @@ namespace eval ::annotation { addAnnotation incr ::annotate(progress) set ::annotate(msg3) "$::tr(move) $::annotate(progress)" - if {[sc_pos isAt end]} break + if {[sc_pos isAt end] || ! $::autoplayMode } break sc_move forward ::notify::PosChanged -pgn - if { ! $::autoplayMode } { break } } } @@ -259,14 +258,12 @@ namespace eval ::annotation { set f .annotationDialog.f grid forget $f.annotate $f.comment $f.av $f.batch pack forget $f.buttons.ok + if {!$::annotate(batchMode)} { grid forget $f.running.games $f.running.line2 } # show progressbar and game infos set ::annotate(games) 1 - set ::annotate(msg2) "$::tr(game) 1" set gameNo [sc_game number] $f.running.games configure -maximum [expr {$::annotate(batchEnd) - $gameNo + 1}] - if {!$::annotate(batchMode)} { grid forget $f.running.games $f.running.line2 } grid $f.running -row 2 -column 0 -columnspan 2 -sticky we - $f.buttons.ok configure -state disabled set ::autoplayMode 1 set gameNo [sc_game number] @@ -276,7 +273,6 @@ namespace eval ::annotation { sc_game save $gameNo incr gameNo incr ::annotate(games) - set ::annotate(msg2) "$::tr(game) $::annotate(games)" if { ! $::autoplayMode || $gameNo > $::annotate(batchEnd) } { break } sc_game load $gameNo annotateGame From b14a2c5bcaa7984af3e9fda559f802170adbf4e0 Mon Sep 17 00:00:00 2001 From: Uwe Date: Thu, 30 Jan 2025 17:31:55 +0100 Subject: [PATCH 12/57] code optimized --- tcl/tools/annotate.tcl | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 5e1d3d3e8..03ffa7d47 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -205,20 +205,20 @@ namespace eval ::annotation { set firstmove [llength [sc_game moves]] sc_game push copyfast catch { sc_move forward 300 } - set anz [expr {[llength [sc_game moves]] - $firstmove}] + set anz [expr {[llength [sc_game moves]] - $firstmove + 1}] sc_game pop .annotationDialog.f.running.progress configure -maximum $anz #reset values - set ::annotate(progress) 0 set ::annotate(prevscore) 0 set ::annotate(score) 0 set ::annotate(scoremate) 0 set ::annotate(prevscoremate) 0 set ::annotate(prevmoves) "" set ::annotate(moves) "" + set ::annotate(progress) 1 set ::annotate(msg1) "$::tr(game) [sc_game number]: [sc_game info white] - [sc_game info black]" set ::annotate(msg2) "$::tr(game) $::annotate(games)" - set ::annotate(msg3) "$::tr(move)" + set ::annotate(msg3) "$::tr(move) 1" if { $::annotate(addAnnotatorTag) } { appendAnnotator "$::annotate(engine) $::annotate(typ) $::annotate($::annotate(typ))" } @@ -359,7 +359,7 @@ namespace eval ::annotation { # And this is his best line: lassign $::annotate(PV1) score score_type ::annotate(moves) - if { $gamemove eq "" || $score eq "" } { return } + if { $gamemove eq "" || $score eq "" } { set ::annotate(prevscore) $score; return } set moves $::annotate(moves) set bestMoveIsMate 0 if { $score_type eq "mate" } { @@ -431,16 +431,6 @@ namespace eval ::annotation { set prevtext "\[%eval $wprevscore\]" } - # Must we annotate our own moves? If no, we bail out unless - # - we must add a closing line - if { ( $::annotate(annotateMoves) == "white" && $tomove == "white" || - $::annotate(annotateMoves) == "black" && $tomove == "black" ) && ! $addClosingLine } { - set ::annotate(prevscore) $::annotate(score) - set ::annotate(prevmoves) $::annotate(moves) - set ::annotate(prevscoremate) $::annotate(scoremate) - updateBoard -pgn - } - # See if we have the threshold filter activated. # If so, take only bad moves and missed mates until the position is lost anyway # Or that we must annotate all moves @@ -613,7 +603,7 @@ namespace eval ::annotation { } } "InfoBestMove" { - lassign $msgData ::engineBestMove + lassign $msgData ::annotate(bestmove) set ::annotate(move_done) 1 } "InfoGo" { From 84b9d3a5f70fb0e225a46906bd08cdc70a85902d Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 31 Jan 2025 19:51:00 +0100 Subject: [PATCH 13/57] option to store two engine variations --- tcl/tools/annotate.tcl | 77 ++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 03ffa7d47..ce26b84ad 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -38,12 +38,15 @@ namespace eval ::annotation { set ::annotate(msg1) "" set ::annotate(msg2) "" set ::annotate(msg3) "" - set ::annotate(prevscore) 0 - set ::annotate(prevmoves) "" + set ::annotate(prevscore1) 0 + set ::annotate(prevscore2) 0 + set ::annotate(prevmoves1) "" + set ::annotate(prevmoves2) "" set ::annotate(score) 0 set ::annotate(moves) "" set ::annotate(scoremate) 0 set ::annotate(prevscoremate) 0 + set ::annotate(anzVariant) 1 proc doAnnotate {} { set w .annotationDialog @@ -127,7 +130,8 @@ namespace eval ::annotation { ttk::radiobutton $f.av.all -text $::tr(AnnotateAll) -variable ::annotate(annotateMoves) -value all ttk::radiobutton $f.av.white -text $::tr(AnnotateWhite) -variable ::annotate(annotateMoves) -value white ttk::radiobutton $f.av.black -text $::tr(AnnotateBlack) -variable ::annotate(annotateMoves) -value black - pack $f.av.all $f.av.white $f.av.black -side top -fill x -anchor w + ttk::checkbutton $f.av.vars -text "Store two variants" -variable ::annotate(anzVariant) -onvalue 2 -offvalue 1 + pack $f.av.all $f.av.white $f.av.black $f.av.vars -side top -fill x -anchor w ttk::labelframe $f.comment -text $::tr(Comments) # Checkmark to enable all-move-scoring @@ -209,11 +213,13 @@ namespace eval ::annotation { sc_game pop .annotationDialog.f.running.progress configure -maximum $anz #reset values - set ::annotate(prevscore) 0 + set ::annotate(prevscore1) 0 + set ::annotate(prevscore2) 0 set ::annotate(score) 0 set ::annotate(scoremate) 0 set ::annotate(prevscoremate) 0 - set ::annotate(prevmoves) "" + set ::annotate(prevmoves1) "" + set ::annotate(prevmoves2) "" set ::annotate(moves) "" set ::annotate(progress) 1 set ::annotate(msg1) "$::tr(game) [sc_game number]: [sc_game info white] - [sc_game info black]" @@ -279,6 +285,7 @@ namespace eval ::annotation { } set ::autoplayMode 0 ::engine::close AnnoEngine + ::notify::PosChanged -pgn destroy .annotationDialog } @@ -359,7 +366,7 @@ namespace eval ::annotation { # And this is his best line: lassign $::annotate(PV1) score score_type ::annotate(moves) - if { $gamemove eq "" || $score eq "" } { set ::annotate(prevscore) $score; return } + if { $gamemove eq "" || $score eq "" } { set ::annotate(prevscore1) $score; return } set moves $::annotate(moves) set bestMoveIsMate 0 if { $score_type eq "mate" } { @@ -376,8 +383,6 @@ namespace eval ::annotation { set ::annotate(score) $score set ::annotate(scoremate) 0 } - # The best line we could have followed, and the game move we just played instead, are here: - set prevmoves $::annotate(prevmoves) # We will add a closing line at the end of variation or game set addClosingLine 0 @@ -386,7 +391,7 @@ namespace eval ::annotation { } # This is the score we could have had if we had played our best move - set prevscore $::annotate(prevscore) + set prevscore $::annotate(prevscore1) # Note that the engine's judgement is in relative terms, a negative score # being favorable to opponent, a positive score favorable to player @@ -422,14 +427,6 @@ namespace eval ::annotation { if { $tomove eq "black" } {set wscore [expr 0.0 - $wscore] } set text "\[%eval $wscore\]" } - # And for the (missed?) chance - if { $::annotate(prevscoremate) != 0 } { - set prevtext [format "M%d" [expr abs($::annotate(prevscoremate))]] - } else { - set wprevscore [format "%+.2f" $prevscore] - if { $tomove eq "white" } {set wprevscore [expr 0.0 - $wprevscore] } - set prevtext "\[%eval $wprevscore\]" - } # See if we have the threshold filter activated. # If so, take only bad moves and missed mates until the position is lost anyway @@ -468,23 +465,36 @@ namespace eval ::annotation { sc_pos addNag "D" } } - if { $prevmoves != "" && ( $::annotate(annotateMoves) == "all" || + + if { $::annotate(prevmoves1) != "" && ( $::annotate(annotateMoves) == "all" || $::annotate(annotateMoves) == "white" && $tomove == "black" || $::annotate(annotateMoves) == "black" && $tomove == "white" )} { - sc_var create - # Add the starting move - sc_move addSan [lrange $prevmoves 0 0] - # Add its score - if { ! $bestMoveIsMate && ( ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations) ) } { - sc_pos setComment "$prevtext" - } - # Add remaining moves - sc_move addSan [lrange $prevmoves 1 end] - # Add position NAG, unless the line ends in mate - if { $::annotate(prevscoremate) == 0 } { - sc_pos addNag [scoreToNag $prevscore] + set n 1 + while { $n <= $::annotate(anzVariant) && $::annotate(prevmoves$n) ne "" } { + sc_var create + # Add the starting move + sc_move addSan [lrange $::annotate(prevmoves$n) 0 0] + # Add its score + if { ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations) } { + # And for the (missed?) chance + if { $::annotate(prevscoremate) != 0 } { + set prevtext [format "M%d" [expr abs($::annotate(prevscoremate))]] + } else { + set wprevscore [format "%+.2f" $::annotate(prevscore$n)] + if { $tomove eq "white" } {set wprevscore [expr 0.0 - $wprevscore] } + set prevtext "\[%eval $wprevscore\]" + } + sc_pos setComment "$prevtext" + } + # Add remaining moves + sc_move addSan [lrange $::annotate(prevmoves$n) 1 end] + # Add position NAG, unless the line ends in mate + if { $n == 1 && $::annotate(prevscoremate) == 0 } { + sc_pos addNag [scoreToNag $prevscore] + } + sc_var exit + incr n } - sc_var exit } sc_move forward } else { @@ -512,8 +522,9 @@ namespace eval ::annotation { # Now up to the end of the game ::move::Forward } - set ::annotate(prevscore) $::annotate(score) - set ::annotate(prevmoves) $::annotate(moves) + set ::annotate(prevscore1) $::annotate(score) + set ::annotate(prevmoves1) $::annotate(moves) + lassign $::annotate(PV2) ::annotate(prevscore2) score_type ::annotate(prevmoves2) set ::annotate(prevscoremate) $::annotate(scoremate) updateBoard -pgn } From 2954654fd8a38454a631a19ee5b501439276387b Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 31 Jan 2025 19:58:32 +0100 Subject: [PATCH 14/57] rename variable --- tcl/tools/annotate.tcl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index ce26b84ad..b9bb17049 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -46,7 +46,7 @@ namespace eval ::annotation { set ::annotate(moves) "" set ::annotate(scoremate) 0 set ::annotate(prevscoremate) 0 - set ::annotate(anzVariant) 1 + set ::annotate(anzVariation) 1 proc doAnnotate {} { set w .annotationDialog @@ -130,7 +130,7 @@ namespace eval ::annotation { ttk::radiobutton $f.av.all -text $::tr(AnnotateAll) -variable ::annotate(annotateMoves) -value all ttk::radiobutton $f.av.white -text $::tr(AnnotateWhite) -variable ::annotate(annotateMoves) -value white ttk::radiobutton $f.av.black -text $::tr(AnnotateBlack) -variable ::annotate(annotateMoves) -value black - ttk::checkbutton $f.av.vars -text "Store two variants" -variable ::annotate(anzVariant) -onvalue 2 -offvalue 1 + ttk::checkbutton $f.av.vars -text "Store two variations" -variable ::annotate(anzVariation) -onvalue 2 -offvalue 1 pack $f.av.all $f.av.white $f.av.black $f.av.vars -side top -fill x -anchor w ttk::labelframe $f.comment -text $::tr(Comments) @@ -470,7 +470,7 @@ namespace eval ::annotation { $::annotate(annotateMoves) == "white" && $tomove == "black" || $::annotate(annotateMoves) == "black" && $tomove == "white" )} { set n 1 - while { $n <= $::annotate(anzVariant) && $::annotate(prevmoves$n) ne "" } { + while { $n <= $::annotate(anzVariation) && $::annotate(prevmoves$n) ne "" } { sc_var create # Add the starting move sc_move addSan [lrange $::annotate(prevmoves$n) 0 0] From 1e144c0932ed0573dc7c8e8817bb31ab13d65ce9 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sat, 1 Feb 2025 09:44:03 +0100 Subject: [PATCH 15/57] code cleanup --- tcl/tools/annotate.tcl | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index b9bb17049..15e31aba7 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -550,21 +550,10 @@ namespace eval ::annotation { set sc2 [lindex $::annotate(PV2) 0] if { [expr abs( $score - $sc2 )] < 1.5 } { return 0 } - # There is no other winning moves (the best move may not win, of course, but - # I reject exercises when there are e.g. moves leading to +9, +7 and +5 scores) - if { [expr $score * $sc2] > 0.0 && [expr abs($score)] > $::informant("+-") && [expr abs($sc2)] > $::informant("+-") } { - puts diffscore -# return 0 - } - - # The best move does not lose position. -# if {([sc_pos side] == "black") && ($score < [expr 0.0 - $::informant("+/-")]) } { return 0 } -# if {([sc_pos side] == "white") && ($score > $::informant("+/-")) } { return 0} - # Move is not obvious: check that it is not the first move guessed at low depths set pv [ lindex [ lindex $::annotate(PV1) 2 ] 0 ] # bm0 must SAN, pv is UCI: convert - set bm0 [string range [lindex $pv 0] 0 3] + set bm0 [string range [lindex $pv 0] 0 4] set bm0 [sc_pos coordToSAN $::annotate(position) $bm0] set bm0 [string range $bm0 [expr [string first "." $bm0] + 1] end] @@ -575,7 +564,6 @@ namespace eval ::annotation { if { $bm0 == $bm1 && $bm0 == $bm2 && $bm0 == $bm3 } { return 0 } - puts "prev $prevscore pv1 $score pv2 $sc2 BM $bm0 $bm1 $bm2 $bm3 [string range $::annotate(PV1) 0 40]" # find what time is needed to get the solution (use internal analyze function) set timer {1 2 5 10 50 100 200 1000} From b132b9d2d6bced53ec18fe9b24310cd4d956988b Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 2 Feb 2025 09:44:02 +0100 Subject: [PATCH 16/57] revert last commit partially --- tcl/tools/annotate.tcl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 15e31aba7..715b440ac 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -550,6 +550,10 @@ namespace eval ::annotation { set sc2 [lindex $::annotate(PV2) 0] if { [expr abs( $score - $sc2 )] < 1.5 } { return 0 } + # The best move does not lose position. + if {([sc_pos side] == "black") && ($score < [expr 0.0 - $::informant("+/-")]) } { return 0 } + if {([sc_pos side] == "white") && ($score > $::informant("+/-")) } { return 0} + # Move is not obvious: check that it is not the first move guessed at low depths set pv [ lindex [ lindex $::annotate(PV1) 2 ] 0 ] # bm0 must SAN, pv is UCI: convert From 11c06f9dbb800052e2c0fdf41ddb4ad1b0fea703 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 2 Feb 2025 21:19:37 +0100 Subject: [PATCH 17/57] Finish game with new Engine-Interface First running version. Code used from analysis.tcl --- tcl/menus.tcl | 1 + tcl/start.tcl | 1 + tcl/tools/finishgame.tcl | 188 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 tcl/tools/finishgame.tcl diff --git a/tcl/menus.tcl b/tcl/menus.tcl index e66623ef3..9fa59e80c 100644 --- a/tcl/menus.tcl +++ b/tcl/menus.tcl @@ -250,6 +250,7 @@ $m add command -label ToolsStartEngine1 \ -command "::enginewin::start 1" -accelerator "F2" $m add command -label ToolsStartEngine2 \ -command "::enginewin::start 2" -accelerator "F3" +$m add command -label "Fishish Game" -command "::finishgame::finishGameDialog" $m add command -label ToolsAnalysis -command "makeAnalysisWin 1" $m add separator $m add checkbutton -label ToolsFilterGraph \ diff --git a/tcl/start.tcl b/tcl/start.tcl index 51cae61a3..cae4365cc 100644 --- a/tcl/start.tcl +++ b/tcl/start.tcl @@ -744,6 +744,7 @@ tools/optable.tcl tools/preport.tcl tools/pinfo.tcl tools/analysis.tcl +tools/finishgame.tcl tools/wbdetect.tcl tools/graphs.tcl tools/ptracker.tcl diff --git a/tcl/tools/finishgame.tcl b/tcl/tools/finishgame.tcl new file mode 100644 index 000000000..1e16d6ebe --- /dev/null +++ b/tcl/tools/finishgame.tcl @@ -0,0 +1,188 @@ +### +### finishgame.tcl: part of Scid. +### This file is part of Scid (Shane's Chess Information Database). +### Copyright (C) 2025 Uwe Klimmek +### uses code from Fulvio Benini https://github.com/benini/chess_accuracy and analysis.tcl +########################################################################################## +### finishGame Dialog: uses a chess engine to play a game + +namespace eval ::finishgame { + + set ::finishGame(annotate) 1 + set ::finishGame(annotateShort) 1 + set ::finishGame(enginewhite) "" + set ::finishGame(engineblack) "" + set ::finishGame(cmdwhite) movetime + set ::finishGame(cmdblack) movetime + set ::finishGame(cmdValuewhite) 2 + set ::finishGame(cmdValueblack) 2 + + ################################################################################ + # will ask engine(s) to play the game till the end + ################################################################################ + proc finishGameDialog { } { + if { $::autoplayMode } { return } + + # UCI engines + # On exit save values in options.dat + ::options.store ::finishGame(annotate) + ::options.store ::finishGamen(annotateShort) + ::options.store ::finishGame(enginewhite) + ::options.store ::finishGame(engineblack) + + set w .configFinishGame + win::createDialog $w + wm resizable $w 0 0 + ::setTitle $w "Scid: $::tr(FinishGame)" + + ttk::labelframe $w.wh_f -text "$::tr(White)" -padding 5 + grid $w.wh_f -column 0 -row 0 -columnspan 2 -sticky we -pady 8 + foreach psize $::boardSizes { + if {$psize >= 40} { break } + } + set engList [::enginecfg::names ] + if { $::finishGame(enginewhite) eq "" } { set ::finishGame(enginewhite) [lindex $engList 0] } + if { $::finishGame(engineblack) eq "" } { set ::finishGame(engineblack) [lindex $engList 0] } + ttk::label $w.wh_f.p -image wk$psize + grid $w.wh_f.p -column 0 -row 0 -rowspan 3 + ttk::combobox $w.wh_f.engine -width 26 -state readonly -values $engList -textvariable ::finishGame(enginewhite) + ttk::spinbox $w.wh_f.cv -width 3 -textvariable ::finishGame(cmdValuewhite) -from 1 -to 999 -justify right + ttk::radiobutton $w.wh_f.c1 -text $::tr(seconds) -variable ::finishGame(cmdwhite) -value "movetime" + ttk::radiobutton $w.wh_f.c2 -text $::tr(FixedDepth) -variable ::finishGame(cmdwhite) -value "depth" + grid $w.wh_f.engine -column 1 -row 1 -columnspan 3 -sticky w + grid $w.wh_f.cv -column 1 -row 2 -sticky w + grid $w.wh_f.c1 -column 2 -row 2 -sticky w -padx 6 + grid $w.wh_f.c2 -column 3 -row 2 -sticky w + + ttk::labelframe $w.bk_f -text "$::tr(Black)" -padding 5 + grid $w.bk_f -column 0 -row 1 -columnspan 2 -sticky we -pady 8 + ttk::label $w.bk_f.p -image bk$psize + grid $w.bk_f.p -column 0 -row 0 -rowspan 3 + ttk::combobox $w.bk_f.engine -width 26 -state readonly -values $engList -textvariable ::finishGame(engineblack) + ttk::spinbox $w.bk_f.cv -width 3 -textvariable ::finishGame(cmdValueblack) -from 1 -to 999 -justify right + ttk::radiobutton $w.bk_f.c1 -text $::tr(seconds) -variable ::finishGame(cmdblack) -value "movetime" + ttk::radiobutton $w.bk_f.c2 -text $::tr(FixedDepth) -variable ::finishGame(cmdblack) -value "depth" + grid $w.bk_f.engine -column 1 -row 1 -columnspan 3 -sticky w + grid $w.bk_f.cv -column 1 -row 2 -sticky w + grid $w.bk_f.c1 -column 2 -row 2 -sticky w -padx 6 + grid $w.bk_f.c2 -column 3 -row 2 -sticky w + + ttk::checkbutton $w.finishGame -text $::tr(Annotate) -variable ::finishGame(annotate) + grid $w.finishGame -column 0 -row 2 -sticky w -padx 5 -pady 8 + ttk::checkbutton $w.finishGameShort -text $::tr(ShortAnnotations) -variable ::finishGame(annotateShort) + grid $w.finishGameShort -column 1 -row 2 -sticky w -padx 5 -pady 8 + + ttk::frame $w.fbuttons + ttk::button $w.fbuttons.cancel -text $::tr(Cancel) -command { + if { $::autoplayMode } { + set ::autoplayMode 0 + } else { + destroy .configFinishGame + } + } + + ttk::button $w.fbuttons.ok -text "OK" -command { + set msg [::finishgame::initfgEngine white $::finishGame(enginewhite)] + if { $msg eq "ok" } { + set msg [::finishgame::initfgEngine black $::finishGame(engineblack)] + if { $msg eq "ok" } { + ::finishgame::runFinishGame + } + } + if { $msg ne "ok" } { + tk_messageBox -title Scid -icon info -type ok -message $msg + } + } + packbuttons right $w.fbuttons.cancel $w.fbuttons.ok + grid $w.fbuttons -row 3 -column 1 -columnspan 2 -sticky we + focus $w.fbuttons.ok + bind $w { .configFinishGame.cancel invoke } + bind $w { .configFinishGame.ok invoke } + bind $w { focus . } + grab $w + } + + # Open the engine and configure it + proc initfgEngine { color engine } { + set config [::enginecfg::get $engine] + lassign $config name cmd args wdir elo time url uci options + if { ! $uci } { return "Only UCI-Engines are supported!" } + ::engine::setLogCmd fgEngine$color {} + ::engine::connect fgEngine$color ::finishgame::eng_messages $cmd {} + lappend options "MultiPV 2" + ::engine::send fgEngine$color SetOptions $options + return "ok" + } + + proc ::finishgame::annotate { tomove } { + lassign $::finishGame(PV1) score score_type pv + if { $tomove eq "black" } {set score [expr 0.0 - $score] } + set tmp [sc_pos getComment] + if { $score_type eq "mate" } { + set score "M$score" + } else { + set score "\[%eval $score\]" + } + sc_pos setComment "$tmp $score" + } + + proc ::finishgame::runFinishGame { } { + set w .configFinishGame + grid forget $w.wh_f $w.bk_f + pack forget $w.fbuttons.ok + set ::autoplayMode 1 + set tomove [sc_pos side] + set value(white) $::finishGame(cmdValuewhite) + set value(black) $::finishGame(cmdValueblack) + if { $::finishGame(cmdwhite) eq "movetime" } { set value(white) [expr {$::finishGame(cmdValuewhite) * 1000 }] } + if { $::finishGame(cmdblack) eq "movetime" } { set value(black) [expr {$::finishGame(cmdValueblack) * 1000 }] } + + sc_var create + while {$::autoplayMode} { + ::engine::send fgEngine$tomove Go [list [sc_game UCI_currentPos] [list $::finishGame(cmd$tomove) $value($tomove)]] + vwait ::finishGame(moveDone) + if { [catch { sc_move addSan $::finishGame(bestmove) }] } { + set ::autoplayMode 0 + } else { + ::finishgame::annotate $tomove + } + sc_move forward + ::notify::PosChanged -pgn + set tomove [expr {$tomove eq "white" ? "black" : "white"}] + } + sc_var exit + + set ::autoplayMode 0 + set tmp [sc_pos getComment] + sc_pos setComment "$tmp\n\n$::tr(FinishGame) $::tr(White): $::finishGame(enginewhite) $::finishGame(cmdwhite) $::finishGame(cmdValuewhite)\n\n$::tr(Black): $::finishGame(engineblack) $::finishGame(cmdblack) $::finishGame(cmdValueblack)" + ::engine::close fgEnginewhite + ::engine::close fgEngineblack + ::notify::PosChanged -pgn + destroy .configFinishGame + } + + proc ::finishgame::eng_messages {msg} { + lassign $msg msgType msgData + switch $msgType { + "InfoPV" { + lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv + if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } + set ::finishGame(PV$multipv) [list $score $score_type $pv] + } + "InfoBestMove" { + lassign $msgData ::finishGame(bestmove) + set ::finishGame(moveDone) 1 + } + "InfoGo" { + lassign $msgData ::annotate(position) + } + "InfoDisconnected" { + lassign $msgData errorMsg + if {$errorMsg eq ""} { set errorMsg "The connection with the engine terminated unexpectedly." } + tk_messageBox -icon warning -type ok -parent . -message $errorMsg + set ::autoplayMode 0 + } + } + } + +} From a9c1d05485d836da974ff4c3778dc192e3017ef1 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 3 Feb 2025 16:53:40 +0100 Subject: [PATCH 18/57] implement long annotation --- tcl/tools/finishgame.tcl | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tcl/tools/finishgame.tcl b/tcl/tools/finishgame.tcl index 1e16d6ebe..aae268451 100644 --- a/tcl/tools/finishgame.tcl +++ b/tcl/tools/finishgame.tcl @@ -117,13 +117,21 @@ namespace eval ::finishgame { proc ::finishgame::annotate { tomove } { lassign $::finishGame(PV1) score score_type pv if { $tomove eq "black" } {set score [expr 0.0 - $score] } - set tmp [sc_pos getComment] - if { $score_type eq "mate" } { - set score "M$score" - } else { - set score "\[%eval $score\]" + if {! $::finishGame(annotateShort) } { + sc_var create + # Add the starting move + sc_move addSan $pv + sc_var exit + } + if {$::finishGame(annotate) } { + set tmp [sc_pos getComment] + if { $score_type eq "mate" } { + set score "M$score" + } else { + set score "\[%eval $score\]" + } + sc_pos setComment "$tmp $score" } - sc_pos setComment "$tmp $score" } proc ::finishgame::runFinishGame { } { @@ -184,5 +192,4 @@ namespace eval ::finishgame { } } } - } From 511c00b1c37b879c54ffe88853da2907ba44bda3 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 5 Feb 2025 17:13:29 +0100 Subject: [PATCH 19/57] check for 50-move rule and 3-fold repetition --- tcl/tools/finishgame.tcl | 48 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/tcl/tools/finishgame.tcl b/tcl/tools/finishgame.tcl index aae268451..e03c39558 100644 --- a/tcl/tools/finishgame.tcl +++ b/tcl/tools/finishgame.tcl @@ -16,6 +16,7 @@ namespace eval ::finishgame { set ::finishGame(cmdblack) movetime set ::finishGame(cmdValuewhite) 2 set ::finishGame(cmdValueblack) 2 + set ::finishGame(msg) "" ################################################################################ # will ask engine(s) to play the game till the end @@ -71,6 +72,7 @@ namespace eval ::finishgame { grid $w.finishGame -column 0 -row 2 -sticky w -padx 5 -pady 8 ttk::checkbutton $w.finishGameShort -text $::tr(ShortAnnotations) -variable ::finishGame(annotateShort) grid $w.finishGameShort -column 1 -row 2 -sticky w -padx 5 -pady 8 + ttk::label $w.line1 -textvariable ::finishGame(msg) -width 60 ttk::frame $w.fbuttons ttk::button $w.fbuttons.cancel -text $::tr(Cancel) -command { @@ -136,9 +138,15 @@ namespace eval ::finishgame { proc ::finishgame::runFinishGame { } { set w .configFinishGame - grid forget $w.wh_f $w.bk_f + grid forget $w.wh_f $w.bk_f $w.finishGame $w.finishGameShort pack forget $w.fbuttons.ok + grid $w.line1 -row 2 -column 0 -columnspan 2 -sticky we + set ::autoplayMode 1 + set repetition {} + set moves 0 + set material 0 + set pawns "" set tomove [sc_pos side] set value(white) $::finishGame(cmdValuewhite) set value(black) $::finishGame(cmdValueblack) @@ -153,6 +161,18 @@ namespace eval ::finishgame { set ::autoplayMode 0 } else { ::finishgame::annotate $tomove + lassign [checkRepetition $repetition] isRepetition repetition + lassign [checkfiftyMoveRule $moves $material $pawns] isFifty moves material pawns + if { $isRepetition || $isFifty } { + if { $isFifty } { + set text "50-moves rule" + } else { + set text "3-fold repetition" + } + set tmp [sc_pos getComment] + sc_pos setComment "$tmp $text" + set ::autoplayMode 0 + } } sc_move forward ::notify::PosChanged -pgn @@ -176,6 +196,7 @@ namespace eval ::finishgame { lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } set ::finishGame(PV$multipv) [list $score $score_type $pv] + set ::finishGame(msg) $::finishGame(PV$multipv) } "InfoBestMove" { lassign $msgData ::finishGame(bestmove) @@ -192,4 +213,29 @@ namespace eval ::finishgame { } } } + + ################################################################################ + # add current position for 3fold repetition detection and returns 1 if + # the position is a repetition + ################################################################################ + proc checkRepetition { journal } { + set elt [lrange [split [sc_pos fen]] 0 2] + set isRep 0 + # append the position only if different from the last element + if { $elt != [ lindex $journal end ] } { lappend journal $elt } + # 3fold repetion detected + if { [llength [lsearch -all $journal $elt] ] >=3 } { set isRep 1 } + return [list $isRep $journal] + } + + proc checkfiftyMoveRule { moves prevmaterial prevpawns } { + set isFiftyRule 0 + set elt [string range [sc_pos board] 0 63] + incr moves + set material [string length [string map {"." ""} $elt]] + set pawns [string map {"n" "." "b" "." "r" "." "q" "." "k" "." "N" "." "B" "." "R" "." "Q" "." "K" "." } $elt] + if { $pawns ne $prevpawns || $material ne $prevmaterial } { set moves 0 } + if { $moves >= 100 || $material == 2 } { set isFiftyRule 1 } + return [list $isFiftyRule $moves $material $pawns] + } } From 13a0c3e102bce414b7aa501da157db7edc99f3a9 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 9 Feb 2025 09:14:39 +0100 Subject: [PATCH 20/57] Frame added to edit engine parameter --- tcl/menus.tcl | 2 +- tcl/tools/finishgame.tcl | 56 +++++++++++++++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/tcl/menus.tcl b/tcl/menus.tcl index 9fa59e80c..e5ba83a58 100644 --- a/tcl/menus.tcl +++ b/tcl/menus.tcl @@ -250,7 +250,7 @@ $m add command -label ToolsStartEngine1 \ -command "::enginewin::start 1" -accelerator "F2" $m add command -label ToolsStartEngine2 \ -command "::enginewin::start 2" -accelerator "F3" -$m add command -label "Fishish Game" -command "::finishgame::finishGameDialog" +$m add command -label "Finish Game" -command "::finishgame::finishGameDialog" $m add command -label ToolsAnalysis -command "makeAnalysisWin 1" $m add separator $m add checkbutton -label ToolsFilterGraph \ diff --git a/tcl/tools/finishgame.tcl b/tcl/tools/finishgame.tcl index e03c39558..3db67f5f1 100644 --- a/tcl/tools/finishgame.tcl +++ b/tcl/tools/finishgame.tcl @@ -50,10 +50,21 @@ namespace eval ::finishgame { ttk::spinbox $w.wh_f.cv -width 3 -textvariable ::finishGame(cmdValuewhite) -from 1 -to 999 -justify right ttk::radiobutton $w.wh_f.c1 -text $::tr(seconds) -variable ::finishGame(cmdwhite) -value "movetime" ttk::radiobutton $w.wh_f.c2 -text $::tr(FixedDepth) -variable ::finishGame(cmdwhite) -value "depth" - grid $w.wh_f.engine -column 1 -row 1 -columnspan 3 -sticky w + ttk::button $w.wh_f.config -image tb_eng_config -style Toolbutton \ + -command { grid forget .configFinishGame.bconf + grid .configFinishGame.wconf -column 3 -row 0 -rowspan 4 -sticky w -padx 5 + .configFinishGame.wconf.text insert 0.0 "White $::finishGame(enginewhite)" + ::finishgame::initfgEngine white $::finishGame(enginewhite) } + ttk::frame $w.wconf + ::enginecfg::createConfigFrame fgEnginewhite $w.wconf "";#"White $::finishGame(enginewhite)" + $w.wconf.text configure -state normal -wrap word -width 60 -height 14 + ttk::button $w.wconf.ok -text "OK" -command "grid forget $w.wconf" + grid $w.wconf.ok -row 2 -column 1 + grid $w.wh_f.engine -column 1 -row 0 -columnspan 3 -sticky w grid $w.wh_f.cv -column 1 -row 2 -sticky w grid $w.wh_f.c1 -column 2 -row 2 -sticky w -padx 6 grid $w.wh_f.c2 -column 3 -row 2 -sticky w + grid $w.wh_f.config -column 4 -row 0 -sticky w ttk::labelframe $w.bk_f -text "$::tr(Black)" -padding 5 grid $w.bk_f -column 0 -row 1 -columnspan 2 -sticky we -pady 8 @@ -63,10 +74,21 @@ namespace eval ::finishgame { ttk::spinbox $w.bk_f.cv -width 3 -textvariable ::finishGame(cmdValueblack) -from 1 -to 999 -justify right ttk::radiobutton $w.bk_f.c1 -text $::tr(seconds) -variable ::finishGame(cmdblack) -value "movetime" ttk::radiobutton $w.bk_f.c2 -text $::tr(FixedDepth) -variable ::finishGame(cmdblack) -value "depth" + ttk::button $w.bk_f.config -image tb_eng_config -style Toolbutton \ + -command { grid forget .configFinishGame.wconf + grid .configFinishGame.bconf -column 3 -row 0 -rowspan 4 -sticky w -padx 5 + .configFinishGame.bconf.text insert 0.0 "Black $::finishGame(engineblack)" + ::finishgame::initfgEngine black $::finishGame(engineblack) } + ttk::frame $w.bconf + ::enginecfg::createConfigFrame fgEngineblack $w.bconf "" + $w.bconf.text configure -state normal -wrap word -width 60 -height 14 + ttk::button $w.bconf.ok -text "OK" -command "grid forget $w.bconf" + grid $w.bconf.ok -row 2 -column 1 grid $w.bk_f.engine -column 1 -row 1 -columnspan 3 -sticky w grid $w.bk_f.cv -column 1 -row 2 -sticky w grid $w.bk_f.c1 -column 2 -row 2 -sticky w -padx 6 grid $w.bk_f.c2 -column 3 -row 2 -sticky w + grid $w.bk_f.config -column 4 -row 1 -sticky w ttk::checkbutton $w.finishGame -text $::tr(Annotate) -variable ::finishGame(annotate) grid $w.finishGame -column 0 -row 2 -sticky w -padx 5 -pady 8 @@ -79,6 +101,10 @@ namespace eval ::finishgame { if { $::autoplayMode } { set ::autoplayMode 0 } else { + ::engine::close fgEnginewhite + ::engine::close fgEngineblack + catch { unset ::enginewin::engConfig_fgEnginewhite } + catch { unset ::enginewin::engConfig_fgEngineblack } destroy .configFinishGame } } @@ -106,13 +132,16 @@ namespace eval ::finishgame { # Open the engine and configure it proc initfgEngine { color engine } { + set id fgEngine$color + if { [info exists ::enginewin::engConfig_$id] } { return "ok" } set config [::enginecfg::get $engine] lassign $config name cmd args wdir elo time url uci options if { ! $uci } { return "Only UCI-Engines are supported!" } - ::engine::setLogCmd fgEngine$color {} - ::engine::connect fgEngine$color ::finishgame::eng_messages $cmd {} + set ::enginewin::engConfig_$id [list $name $cmd $args $wdir $elo $time $url $uci {}] + ::engine::setLogCmd $id {} + ::engine::connect $id [list ::finishgame::eng_messages $id] $cmd {} lappend options "MultiPV 2" - ::engine::send fgEngine$color SetOptions $options + ::engine::send $id SetOptions $options return "ok" } @@ -138,7 +167,7 @@ namespace eval ::finishgame { proc ::finishgame::runFinishGame { } { set w .configFinishGame - grid forget $w.wh_f $w.bk_f $w.finishGame $w.finishGameShort + grid forget $w.wh_f $w.bk_f $w.finishGame $w.finishGameShort $w.wconf $w.bconf pack forget $w.fbuttons.ok grid $w.line1 -row 2 -column 0 -columnspan 2 -sticky we @@ -185,13 +214,28 @@ namespace eval ::finishgame { sc_pos setComment "$tmp\n\n$::tr(FinishGame) $::tr(White): $::finishGame(enginewhite) $::finishGame(cmdwhite) $::finishGame(cmdValuewhite)\n\n$::tr(Black): $::finishGame(engineblack) $::finishGame(cmdblack) $::finishGame(cmdValueblack)" ::engine::close fgEnginewhite ::engine::close fgEngineblack + unset ::enginewin::engConfig_fgEnginewhite + unset ::enginewin::engConfig_fgEngineblack ::notify::PosChanged -pgn destroy .configFinishGame } - proc ::finishgame::eng_messages {msg} { + proc ::finishgame::eng_messages {id msg} { lassign $msg msgType msgData switch $msgType { + "InfoConfig" { + upvar ::enginewin::engConfig_$id engConfig_ + if { $::autoplayMode } { return } + set msgData [lindex $msgData 2] + set w .configFinishGame.wconf + if { $id eq "fgEngineblack" } { set w .configFinishGame.bconf } + if { ! [winfo exists $w.text.reset] } { + lset ::enginewin::engConfig_$id 8 $msgData + ::enginecfg::createOptionWidgets $id $w $msgData + } else { + ::enginecfg::updateOptionWidgets $id $w $msgData {} + } + } "InfoPV" { lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } From ff14757427863e256944f87b8a6e6d2ea2a9ed10 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 9 Feb 2025 16:50:10 +0100 Subject: [PATCH 21/57] new namespace engineNoWin make all function to need for editing engine options independent from dedicate window --- tcl/tools/annotate.tcl | 93 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 18 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 715b440ac..cf8586d09 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -10,6 +10,50 @@ #improve Tactical Exercise #"finish game" function #accuracy function +namespace eval ::engineNoWin {} +# Open the engine and configure it +proc ::engineNoWin::initEngine { id engine callback {addOpts "MultiPV 2"}} { + if { [info exists ::enginewin::engConfig_$id] } { return "ok" } + set config [::enginecfg::get $engine] + lassign $config name cmd args wdir elo time url uci options + if { ! $uci } { return "Only UCI-Engines are supported!" } + set ::enginewin::engConfig_$id [list $name $cmd $args $wdir $elo $time $url $uci {}] + ::engine::setLogCmd $id {} + ::engine::connect $id $callback $cmd {} + # tactical positions is selected, must be in multipv mode + # if {$::annotate(tacticalExercises)} { set addOpts "MultiPV 4" } + lappend options $addOpts + ::engine::send $id SetOptions $options + return "ok" +} + +proc ::engineNoWin::changeEngine {id w {button ""}} { + ::engine::close $id + $w.text configure -state normal + $w.text delete 1.0 end + foreach wchild [winfo children $w.text] { destroy $wchild } + catch { unset ::enginewin::engConfig_$id } + if { $button ne "" && [winfo ismapped $w] } { + event generate $button <> + } +} +proc ::engineNoWin::editEngine {id w engine} { + grid $w -row 0 -column 2 -rowspan 2 -sticky ne -padx 10 + set msg [::engineNoWin::initEngine $id $engine [list ::annotation::eng_messages $id $w]] + if { $msg ne "ok" } { tk_messageBox -title Scid -icon info -type ok -message $msg } +} + +proc ::engineNoWin::initEngineOptions {id w options} { + upvar ::enginewin::engConfig_$id engConfig_ + if { ! [winfo exists $w.text.reset] } { + lset ::enginewin::engConfig_$id 8 $options + ::enginecfg::createOptionWidgets $id $w $options + } else { + ::enginecfg::updateOptionWidgets $id $w $options {} + $w.text configure -state disabled + } +} + namespace eval ::annotation { # Typ may be "movetime": time per move or "depth": analyse till depth is reached @@ -87,7 +131,22 @@ namespace eval ::annotation { ttk::checkbutton $f.annotate.cbBook -text $::tr(UseBook) -variable ::annotate(useAnalysisBook) set engList [::enginecfg::names ] if { $::annotate(engine) eq "" } { set ::annotate(engine) [lindex $engList 0] } - ttk::combobox $f.annotate.engine -width 26 -state readonly -values $engList -textvariable ::annotate(engine) + ttk::frame $f.annotate.eng + ttk::combobox $f.annotate.eng.engine -width 20 -state readonly -values $engList -textvariable ::annotate(engine) + #create frame for edit engine parameter + bind $f.annotate.eng.engine <> { ::engineNoWin::changeEngine AnnoEngine .annotationDialog.f.engpara .annotationDialog.f.annotate.eng.conf} + ttk::button $f.annotate.eng.conf -image ::icon::filter_adv -style Toolbutton \ + -command { ::engineNoWin::editEngine AnnoEngine .annotationDialog.f.engpara $::annotate(engine) } + pack $f.annotate.eng.engine $f.annotate.eng.conf -side left -padx { 0 5 } + ttk::labelframe $f.engpara -text "Engine Parameter" + ttk::label $f.engpara.l -textvariable ::annotate(engine) + ttk::button $f.engpara.x -text "X" -style Toolbutton -command "grid forget $f.engpara" + ttk_text $f.engpara.text -wrap none -padx 4 + autoscrollBars both $f.engpara $f.engpara.text 1 + $f.engpara.text configure -state normal -wrap word -width 60 -height 18 + grid $f.engpara.l -row 0 -column 0 -sticky w + grid $f.engpara.x -row 0 -column 0 -sticky e + # choose a book for analysis # load book names set bookPath $::scidBooksDir @@ -117,7 +176,7 @@ namespace eval ::annotation { pack $f.annotate.blunderbox -side bottom -anchor w pack $f.annotate.blundersonly -side bottom -anchor w pack $f.annotate.allmoves -side bottom -anchor w - pack $f.annotate.engine -side bottom -anchor w + pack $f.annotate.eng -side bottom -anchor w pack $f.annotate.typ -side bottom -anchor w grid $f.annotate.typ.label -row 0 -column 0 -sticky w grid $f.annotate.typ.ldepth -row 1 -column 0 -sticky w @@ -181,6 +240,8 @@ namespace eval ::annotation { if { $::autoplayMode } { set ::autoplayMode 0 } else { + catch { unset ::enginewin::engConfig_AnnoEngine } + ::engine::close AnnoEngine destroy .annotationDialog } } @@ -189,7 +250,7 @@ namespace eval ::annotation { set ::annotate(movetime) [expr {int($::annotateTime * 1000.0)}] set ::annotate(blunderThreshold) $::annotateBlunderThreshold set ::annotate(time) $::annotateTime - set msg [::annotation::initAnnotationEngine] + set msg [::engineNoWin::initEngine AnnoEngine $::annotate(engine) [list ::annotation::eng_messages AnnoEngine .annotationDialog.f.engpara]] if { $msg eq "ok" } { ::annotation::runAnnotation } else { @@ -230,19 +291,6 @@ namespace eval ::annotation { } } - # Open the engine and configure it - proc initAnnotationEngine { } { - set config [::enginecfg::get $::annotate(engine)] - lassign $config name cmd args wdir elo time url uci options - if { ! $uci } { return "Only UCI-Engines are supported!" } - ::engine::setLogCmd AnnoEngine {} - ::engine::connect AnnoEngine ::annotation::eng_messages $cmd {} - # tactical positions is selected, must be in multipv mode - if {$::annotate(tacticalExercises)} { lappend options "MultiPV 4" } - ::engine::send AnnoEngine SetOptions $options - return "ok" - } - proc annotateGame { } { initGameAnnotation makeBookAnnotation @@ -262,7 +310,7 @@ namespace eval ::annotation { proc runAnnotation { } { set f .annotationDialog.f - grid forget $f.annotate $f.comment $f.av $f.batch + grid forget $f.annotate $f.comment $f.av $f.batch $f.engpara pack forget $f.buttons.ok if {!$::annotate(batchMode)} { grid forget $f.running.games $f.running.line2 } # show progressbar and game infos @@ -271,6 +319,9 @@ namespace eval ::annotation { $f.running.games configure -maximum [expr {$::annotate(batchEnd) - $gameNo + 1}] grid $f.running -row 2 -column 0 -columnspan 2 -sticky we + # tactical positions is selected, must be in multipv mode + if {$::annotate(tacticalExercises)} { ::engine::send AnnoEngine SetOptions "MultiPV 4" } + set ::autoplayMode 1 set gameNo [sc_game number] if { $gameNo == 0 } { return } @@ -284,6 +335,7 @@ namespace eval ::annotation { annotateGame } set ::autoplayMode 0 + unset ::enginewin::engConfig_AnnoEngine ::engine::close AnnoEngine ::notify::PosChanged -pgn destroy .annotationDialog @@ -594,9 +646,14 @@ namespace eval ::annotation { return 1 } - proc ::annotation::eng_messages {msg} { + proc ::annotation::eng_messages {id w msg} { lassign $msg msgType msgData switch $msgType { + "InfoConfig" { + if { $::autoplayMode } { return } + set msgData [lindex $msgData 2] + ::engineNoWin::initEngineOptions $id $w $msgData + } "InfoPV" { lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } From 713f8ce5fac0d07d439ef9fda7ad51d250e001bc Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 9 Feb 2025 18:11:40 +0100 Subject: [PATCH 22/57] move edit frame to namespace engineNoWin --- tcl/tools/annotate.tcl | 50 +++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index cf8586d09..bed3a9ce0 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -10,6 +10,8 @@ #improve Tactical Exercise #"finish game" function #accuracy function + +# engineNoWin will be used by annotate and finish game namespace eval ::engineNoWin {} # Open the engine and configure it proc ::engineNoWin::initEngine { id engine callback {addOpts "MultiPV 2"}} { @@ -20,8 +22,6 @@ proc ::engineNoWin::initEngine { id engine callback {addOpts "MultiPV 2"}} { set ::enginewin::engConfig_$id [list $name $cmd $args $wdir $elo $time $url $uci {}] ::engine::setLogCmd $id {} ::engine::connect $id $callback $cmd {} - # tactical positions is selected, must be in multipv mode - # if {$::annotate(tacticalExercises)} { set addOpts "MultiPV 4" } lappend options $addOpts ::engine::send $id SetOptions $options return "ok" @@ -37,12 +37,33 @@ proc ::engineNoWin::changeEngine {id w {button ""}} { event generate $button <> } } -proc ::engineNoWin::editEngine {id w engine} { +proc ::engineNoWin::editEngine {id w enginevar} { grid $w -row 0 -column 2 -rowspan 2 -sticky ne -padx 10 + set engine [set $enginevar] set msg [::engineNoWin::initEngine $id $engine [list ::annotation::eng_messages $id $w]] if { $msg ne "ok" } { tk_messageBox -title Scid -icon info -type ok -message $msg } } +#create frame for edit engine options +proc ::engineNoWin::createEngineOptionsFrame {f id var} { + ttk::frame $f.engine + set engList [::enginecfg::names ] + if { [set $var] eq "" } { set $var [lindex $engList 0] } + ttk::combobox $f.engine.eng -width 20 -state readonly -values $engList -textvariable $var + bind $f.engine.eng <> "::engineNoWin::changeEngine $id $f.engineOptions $f.engine.opts" + ttk::button $f.engine.opts -image ::icon::filter_adv -style Toolbutton \ + -command "::engineNoWin::editEngine $id $f.engineOptions $var" + pack $f.engine.eng $f.engine.opts -side left -padx { 0 5 } + ttk::labelframe $f.engineOptions -text "Engine Parameter" + ttk::label $f.engineOptions.l -textvariable ::annotate(engine) + ttk::button $f.engineOptions.x -text "X" -style Toolbutton -command "grid forget $f.engineOptions" + ttk_text $f.engineOptions.text -wrap none -padx 4 + autoscrollBars both $f.engineOptions $f.engineOptions.text 1 + $f.engineOptions.text configure -state normal -wrap word -width 60 -height 18 + grid $f.engineOptions.l -row 0 -column 0 -sticky w + grid $f.engineOptions.x -row 0 -column 1 -sticky e +} + proc ::engineNoWin::initEngineOptions {id w options} { upvar ::enginewin::engConfig_$id engConfig_ if { ! [winfo exists $w.text.reset] } { @@ -129,23 +150,8 @@ namespace eval ::annotation { ttk::spinbox $f.annotate.blunderbox.spBlunder -width 4 -textvariable ::annotateBlunderThreshold \ -from 0.1 -to 3.0 -increment 0.1 -justify right ttk::checkbutton $f.annotate.cbBook -text $::tr(UseBook) -variable ::annotate(useAnalysisBook) - set engList [::enginecfg::names ] - if { $::annotate(engine) eq "" } { set ::annotate(engine) [lindex $engList 0] } - ttk::frame $f.annotate.eng - ttk::combobox $f.annotate.eng.engine -width 20 -state readonly -values $engList -textvariable ::annotate(engine) - #create frame for edit engine parameter - bind $f.annotate.eng.engine <> { ::engineNoWin::changeEngine AnnoEngine .annotationDialog.f.engpara .annotationDialog.f.annotate.eng.conf} - ttk::button $f.annotate.eng.conf -image ::icon::filter_adv -style Toolbutton \ - -command { ::engineNoWin::editEngine AnnoEngine .annotationDialog.f.engpara $::annotate(engine) } - pack $f.annotate.eng.engine $f.annotate.eng.conf -side left -padx { 0 5 } - ttk::labelframe $f.engpara -text "Engine Parameter" - ttk::label $f.engpara.l -textvariable ::annotate(engine) - ttk::button $f.engpara.x -text "X" -style Toolbutton -command "grid forget $f.engpara" - ttk_text $f.engpara.text -wrap none -padx 4 - autoscrollBars both $f.engpara $f.engpara.text 1 - $f.engpara.text configure -state normal -wrap word -width 60 -height 18 - grid $f.engpara.l -row 0 -column 0 -sticky w - grid $f.engpara.x -row 0 -column 0 -sticky e + ::engineNoWin::createEngineOptionsFrame $f AnnoEngine ::annotate(engine) + # choose a book for analysis # load book names @@ -176,7 +182,7 @@ namespace eval ::annotation { pack $f.annotate.blunderbox -side bottom -anchor w pack $f.annotate.blundersonly -side bottom -anchor w pack $f.annotate.allmoves -side bottom -anchor w - pack $f.annotate.eng -side bottom -anchor w + pack $f.engine -in $f.annotate -side bottom -anchor w pack $f.annotate.typ -side bottom -anchor w grid $f.annotate.typ.label -row 0 -column 0 -sticky w grid $f.annotate.typ.ldepth -row 1 -column 0 -sticky w @@ -310,7 +316,7 @@ namespace eval ::annotation { proc runAnnotation { } { set f .annotationDialog.f - grid forget $f.annotate $f.comment $f.av $f.batch $f.engpara + grid forget $f.annotate $f.comment $f.av $f.batch $f.engineOptions pack forget $f.buttons.ok if {!$::annotate(batchMode)} { grid forget $f.running.games $f.running.line2 } # show progressbar and game infos From 830f341c58a797f905ff2d90f65ff74a7d9f56c2 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 9 Feb 2025 21:31:49 +0100 Subject: [PATCH 23/57] new namespace engineNoWin make all function to need for editing engine options independent from dedicate window --- tcl/tools/finishgame.tcl | 112 +++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 40 deletions(-) diff --git a/tcl/tools/finishgame.tcl b/tcl/tools/finishgame.tcl index 3db67f5f1..f8e172df5 100644 --- a/tcl/tools/finishgame.tcl +++ b/tcl/tools/finishgame.tcl @@ -6,6 +6,71 @@ ########################################################################################## ### finishGame Dialog: uses a chess engine to play a game +# engineNoWin will be used by annotate and finish game +#copied from annotate.tcl. to be removed later +namespace eval ::engineNoWin {} +# Open the engine and configure it +proc ::engineNoWin::initEngine { id engine callback {addOpts "MultiPV 2"}} { + if { [info exists ::enginewin::engConfig_$id] } { return "ok" } + set config [::enginecfg::get $engine] + lassign $config name cmd args wdir elo time url uci options + if { ! $uci } { return "Only UCI-Engines are supported!" } + set ::enginewin::engConfig_$id [list $name $cmd $args $wdir $elo $time $url $uci {}] + ::engine::setLogCmd $id {} + ::engine::connect $id $callback $cmd {} + lappend options $addOpts + ::engine::send $id SetOptions $options + return "ok" +} + +proc ::engineNoWin::changeEngine {id w {button ""}} { + ::engine::close $id + $w.text configure -state normal + $w.text delete 1.0 end + foreach wchild [winfo children $w.text] { destroy $wchild } + catch { unset ::enginewin::engConfig_$id } + if { $button ne "" && [winfo ismapped $w] } { + event generate $button <> + } +} +proc ::engineNoWin::editEngine {id w enginevar} { + grid $w -row 0 -column 4 -rowspan 2 -sticky ne -padx 10 + set engine [set $enginevar] + set msg [::engineNoWin::initEngine $id $engine [list ::finishgame::eng_messages $id $w]] + if { $msg ne "ok" } { tk_messageBox -title Scid -icon info -type ok -message $msg } +} + +#create frame for edit engine options +proc ::engineNoWin::createEngineOptionsFrame {f id var} { + ttk::frame $f.$id + set engList [::enginecfg::names ] + if { [set $var] eq "" } { set $var [lindex $engList 0] } + ttk::combobox $f.$id.eng -width 20 -state readonly -values $engList -textvariable $var + bind $f.$id.eng <> "::engineNoWin::changeEngine $id $f.opts$id $f.$id.opts" + ttk::button $f.$id.opts -image ::icon::filter_adv -style Toolbutton \ + -command "::engineNoWin::editEngine $id $f.opts$id $var" + pack $f.$id.eng $f.$id.opts -side left -padx { 0 5 } + ttk::labelframe $f.opts$id -text "Engine Parameter" + ttk::label $f.opts$id.l -textvariable $var + ttk::button $f.opts$id.x -text "X" -style Toolbutton -command "grid forget $f.opts$id" + ttk_text $f.opts$id.text -wrap none -padx 4 + autoscrollBars both $f.opts$id $f.opts$id.text 1 + $f.opts$id.text configure -state normal -wrap word -width 60 -height 18 + grid $f.opts$id.l -row 0 -column 0 -sticky w + grid $f.opts$id.x -row 0 -column 1 -sticky e +} + +proc ::engineNoWin::initEngineOptions {id w options} { + upvar ::enginewin::engConfig_$id engConfig_ + if { ! [winfo exists $w.text.reset] } { + lset ::enginewin::engConfig_$id 8 $options + ::enginecfg::createOptionWidgets $id $w $options + } else { + ::enginecfg::updateOptionWidgets $id $w $options {} + $w.text configure -state disabled + } +} + namespace eval ::finishgame { set ::finishGame(annotate) 1 @@ -41,54 +106,29 @@ namespace eval ::finishgame { foreach psize $::boardSizes { if {$psize >= 40} { break } } - set engList [::enginecfg::names ] - if { $::finishGame(enginewhite) eq "" } { set ::finishGame(enginewhite) [lindex $engList 0] } - if { $::finishGame(engineblack) eq "" } { set ::finishGame(engineblack) [lindex $engList 0] } ttk::label $w.wh_f.p -image wk$psize grid $w.wh_f.p -column 0 -row 0 -rowspan 3 - ttk::combobox $w.wh_f.engine -width 26 -state readonly -values $engList -textvariable ::finishGame(enginewhite) ttk::spinbox $w.wh_f.cv -width 3 -textvariable ::finishGame(cmdValuewhite) -from 1 -to 999 -justify right ttk::radiobutton $w.wh_f.c1 -text $::tr(seconds) -variable ::finishGame(cmdwhite) -value "movetime" ttk::radiobutton $w.wh_f.c2 -text $::tr(FixedDepth) -variable ::finishGame(cmdwhite) -value "depth" - ttk::button $w.wh_f.config -image tb_eng_config -style Toolbutton \ - -command { grid forget .configFinishGame.bconf - grid .configFinishGame.wconf -column 3 -row 0 -rowspan 4 -sticky w -padx 5 - .configFinishGame.wconf.text insert 0.0 "White $::finishGame(enginewhite)" - ::finishgame::initfgEngine white $::finishGame(enginewhite) } - ttk::frame $w.wconf - ::enginecfg::createConfigFrame fgEnginewhite $w.wconf "";#"White $::finishGame(enginewhite)" - $w.wconf.text configure -state normal -wrap word -width 60 -height 14 - ttk::button $w.wconf.ok -text "OK" -command "grid forget $w.wconf" - grid $w.wconf.ok -row 2 -column 1 - grid $w.wh_f.engine -column 1 -row 0 -columnspan 3 -sticky w + ::engineNoWin::createEngineOptionsFrame $w fgEnginewhite ::finishGame(enginewhite) + grid $w.fgEnginewhite -in $w.wh_f -column 1 -row 0 -columnspan 3 -sticky w grid $w.wh_f.cv -column 1 -row 2 -sticky w grid $w.wh_f.c1 -column 2 -row 2 -sticky w -padx 6 grid $w.wh_f.c2 -column 3 -row 2 -sticky w - grid $w.wh_f.config -column 4 -row 0 -sticky w ttk::labelframe $w.bk_f -text "$::tr(Black)" -padding 5 grid $w.bk_f -column 0 -row 1 -columnspan 2 -sticky we -pady 8 ttk::label $w.bk_f.p -image bk$psize grid $w.bk_f.p -column 0 -row 0 -rowspan 3 - ttk::combobox $w.bk_f.engine -width 26 -state readonly -values $engList -textvariable ::finishGame(engineblack) ttk::spinbox $w.bk_f.cv -width 3 -textvariable ::finishGame(cmdValueblack) -from 1 -to 999 -justify right ttk::radiobutton $w.bk_f.c1 -text $::tr(seconds) -variable ::finishGame(cmdblack) -value "movetime" ttk::radiobutton $w.bk_f.c2 -text $::tr(FixedDepth) -variable ::finishGame(cmdblack) -value "depth" - ttk::button $w.bk_f.config -image tb_eng_config -style Toolbutton \ - -command { grid forget .configFinishGame.wconf - grid .configFinishGame.bconf -column 3 -row 0 -rowspan 4 -sticky w -padx 5 - .configFinishGame.bconf.text insert 0.0 "Black $::finishGame(engineblack)" - ::finishgame::initfgEngine black $::finishGame(engineblack) } - ttk::frame $w.bconf - ::enginecfg::createConfigFrame fgEngineblack $w.bconf "" - $w.bconf.text configure -state normal -wrap word -width 60 -height 14 - ttk::button $w.bconf.ok -text "OK" -command "grid forget $w.bconf" - grid $w.bconf.ok -row 2 -column 1 - grid $w.bk_f.engine -column 1 -row 1 -columnspan 3 -sticky w + ::engineNoWin::createEngineOptionsFrame $w fgEngineblack ::finishGame(engineblack) + grid $w.fgEngineblack -in $w.bk_f -column 1 -row 0 -columnspan 3 -sticky w grid $w.bk_f.cv -column 1 -row 2 -sticky w grid $w.bk_f.c1 -column 2 -row 2 -sticky w -padx 6 grid $w.bk_f.c2 -column 3 -row 2 -sticky w - grid $w.bk_f.config -column 4 -row 1 -sticky w ttk::checkbutton $w.finishGame -text $::tr(Annotate) -variable ::finishGame(annotate) grid $w.finishGame -column 0 -row 2 -sticky w -padx 5 -pady 8 @@ -167,7 +207,7 @@ namespace eval ::finishgame { proc ::finishgame::runFinishGame { } { set w .configFinishGame - grid forget $w.wh_f $w.bk_f $w.finishGame $w.finishGameShort $w.wconf $w.bconf + grid forget $w.wh_f $w.bk_f $w.finishGame $w.finishGameShort $w.optsfgEnginewhite $w.optsfgEngineblack pack forget $w.fbuttons.ok grid $w.line1 -row 2 -column 0 -columnspan 2 -sticky we @@ -220,21 +260,13 @@ namespace eval ::finishgame { destroy .configFinishGame } - proc ::finishgame::eng_messages {id msg} { + proc ::finishgame::eng_messages {id w msg} { lassign $msg msgType msgData switch $msgType { "InfoConfig" { - upvar ::enginewin::engConfig_$id engConfig_ if { $::autoplayMode } { return } set msgData [lindex $msgData 2] - set w .configFinishGame.wconf - if { $id eq "fgEngineblack" } { set w .configFinishGame.bconf } - if { ! [winfo exists $w.text.reset] } { - lset ::enginewin::engConfig_$id 8 $msgData - ::enginecfg::createOptionWidgets $id $w $msgData - } else { - ::enginecfg::updateOptionWidgets $id $w $msgData {} - } + ::engineNoWin::initEngineOptions $id $w $msgData } "InfoPV" { lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv From 6cf8a16e629994f58327b8ec134e90731e6a9fe0 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 9 Feb 2025 21:49:52 +0100 Subject: [PATCH 24/57] place option widget side by side --- tcl/tools/finishgame.tcl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tcl/tools/finishgame.tcl b/tcl/tools/finishgame.tcl index f8e172df5..c8d540d55 100644 --- a/tcl/tools/finishgame.tcl +++ b/tcl/tools/finishgame.tcl @@ -33,22 +33,23 @@ proc ::engineNoWin::changeEngine {id w {button ""}} { event generate $button <> } } -proc ::engineNoWin::editEngine {id w enginevar} { - grid $w -row 0 -column 4 -rowspan 2 -sticky ne -padx 10 +proc ::engineNoWin::editEngine {id w enginevar col callback} { + if { [winfo ismapped $w] } { grid forget $w ; return } + grid $w -row 0 -column $col -rowspan 2 -sticky ne -padx 10 set engine [set $enginevar] - set msg [::engineNoWin::initEngine $id $engine [list ::finishgame::eng_messages $id $w]] + set msg [::engineNoWin::initEngine $id $engine [list $callback $id $w]] if { $msg ne "ok" } { tk_messageBox -title Scid -icon info -type ok -message $msg } } #create frame for edit engine options -proc ::engineNoWin::createEngineOptionsFrame {f id var} { +proc ::engineNoWin::createEngineOptionsFrame {f id var col} { ttk::frame $f.$id set engList [::enginecfg::names ] if { [set $var] eq "" } { set $var [lindex $engList 0] } ttk::combobox $f.$id.eng -width 20 -state readonly -values $engList -textvariable $var bind $f.$id.eng <> "::engineNoWin::changeEngine $id $f.opts$id $f.$id.opts" ttk::button $f.$id.opts -image ::icon::filter_adv -style Toolbutton \ - -command "::engineNoWin::editEngine $id $f.opts$id $var" + -command "::engineNoWin::editEngine $id $f.opts$id $var $col ::finishgame::eng_messages" pack $f.$id.eng $f.$id.opts -side left -padx { 0 5 } ttk::labelframe $f.opts$id -text "Engine Parameter" ttk::label $f.opts$id.l -textvariable $var @@ -111,7 +112,7 @@ namespace eval ::finishgame { ttk::spinbox $w.wh_f.cv -width 3 -textvariable ::finishGame(cmdValuewhite) -from 1 -to 999 -justify right ttk::radiobutton $w.wh_f.c1 -text $::tr(seconds) -variable ::finishGame(cmdwhite) -value "movetime" ttk::radiobutton $w.wh_f.c2 -text $::tr(FixedDepth) -variable ::finishGame(cmdwhite) -value "depth" - ::engineNoWin::createEngineOptionsFrame $w fgEnginewhite ::finishGame(enginewhite) + ::engineNoWin::createEngineOptionsFrame $w fgEnginewhite ::finishGame(enginewhite) 4 grid $w.fgEnginewhite -in $w.wh_f -column 1 -row 0 -columnspan 3 -sticky w grid $w.wh_f.cv -column 1 -row 2 -sticky w grid $w.wh_f.c1 -column 2 -row 2 -sticky w -padx 6 @@ -124,7 +125,7 @@ namespace eval ::finishgame { ttk::spinbox $w.bk_f.cv -width 3 -textvariable ::finishGame(cmdValueblack) -from 1 -to 999 -justify right ttk::radiobutton $w.bk_f.c1 -text $::tr(seconds) -variable ::finishGame(cmdblack) -value "movetime" ttk::radiobutton $w.bk_f.c2 -text $::tr(FixedDepth) -variable ::finishGame(cmdblack) -value "depth" - ::engineNoWin::createEngineOptionsFrame $w fgEngineblack ::finishGame(engineblack) + ::engineNoWin::createEngineOptionsFrame $w fgEngineblack ::finishGame(engineblack) 5 grid $w.fgEngineblack -in $w.bk_f -column 1 -row 0 -columnspan 3 -sticky w grid $w.bk_f.cv -column 1 -row 2 -sticky w grid $w.bk_f.c1 -column 2 -row 2 -sticky w -padx 6 From 66bd72fd249d2c10072d168a364fb670412af823 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 9 Feb 2025 22:32:09 +0100 Subject: [PATCH 25/57] code cleanup. --- tcl/tools/annotate.tcl | 85 ++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index bed3a9ce0..0b5ba2fe1 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -34,34 +34,36 @@ proc ::engineNoWin::changeEngine {id w {button ""}} { foreach wchild [winfo children $w.text] { destroy $wchild } catch { unset ::enginewin::engConfig_$id } if { $button ne "" && [winfo ismapped $w] } { + grid forget $w event generate $button <> } } -proc ::engineNoWin::editEngine {id w enginevar} { - grid $w -row 0 -column 2 -rowspan 2 -sticky ne -padx 10 +proc ::engineNoWin::editEngine {id w enginevar col callback} { + if { [winfo ismapped $w] } { grid forget $w ; return } + grid $w -row 0 -column $col -rowspan 2 -sticky ne -padx 10 set engine [set $enginevar] - set msg [::engineNoWin::initEngine $id $engine [list ::annotation::eng_messages $id $w]] + set msg [::engineNoWin::initEngine $id $engine [list $callback $id $w]] if { $msg ne "ok" } { tk_messageBox -title Scid -icon info -type ok -message $msg } } #create frame for edit engine options -proc ::engineNoWin::createEngineOptionsFrame {f id var} { - ttk::frame $f.engine +proc ::engineNoWin::createEngineOptionsFrame {f id var col callback} { + ttk::frame $f.$id set engList [::enginecfg::names ] if { [set $var] eq "" } { set $var [lindex $engList 0] } - ttk::combobox $f.engine.eng -width 20 -state readonly -values $engList -textvariable $var - bind $f.engine.eng <> "::engineNoWin::changeEngine $id $f.engineOptions $f.engine.opts" - ttk::button $f.engine.opts -image ::icon::filter_adv -style Toolbutton \ - -command "::engineNoWin::editEngine $id $f.engineOptions $var" - pack $f.engine.eng $f.engine.opts -side left -padx { 0 5 } - ttk::labelframe $f.engineOptions -text "Engine Parameter" - ttk::label $f.engineOptions.l -textvariable ::annotate(engine) - ttk::button $f.engineOptions.x -text "X" -style Toolbutton -command "grid forget $f.engineOptions" - ttk_text $f.engineOptions.text -wrap none -padx 4 - autoscrollBars both $f.engineOptions $f.engineOptions.text 1 - $f.engineOptions.text configure -state normal -wrap word -width 60 -height 18 - grid $f.engineOptions.l -row 0 -column 0 -sticky w - grid $f.engineOptions.x -row 0 -column 1 -sticky e + ttk::combobox $f.$id.eng -width 20 -state readonly -values $engList -textvariable $var + bind $f.$id.eng <> "::engineNoWin::changeEngine $id $f.opts$id $f.$id.opts" + ttk::button $f.$id.opts -image ::icon::filter_adv -style Toolbutton \ + -command "::engineNoWin::editEngine $id $f.opts$id $var $col $callback" + pack $f.$id.eng $f.$id.opts -side left -padx { 0 5 } + ttk::labelframe $f.opts$id -text "Engine Parameter" + ttk::label $f.opts$id.l -textvariable $var + ttk::button $f.opts$id.x -text "X" -style Toolbutton -command "grid forget $f.opts$id" + ttk_text $f.opts$id.text -wrap none -padx 4 + autoscrollBars both $f.opts$id $f.opts$id.text 1 + $f.opts$id.text configure -state normal -wrap word -width 60 -height 18 + grid $f.opts$id.l -row 0 -column 0 -sticky w + grid $f.opts$id.x -row 0 -column 1 -sticky e } proc ::engineNoWin::initEngineOptions {id w options} { @@ -137,21 +139,18 @@ namespace eval ::annotation { ttk::labelframe $f.annotate -text $::tr(GameReview) ttk::frame $f.annotate.typ - ttk::radiobutton $f.annotate.typ.label -text $::tr(AnnotateTime) -variable ::annotate(typ) -value "movetime" - ttk::radiobutton $f.annotate.typ.ldepth -text "Depth per move" -variable ::annotate(typ) -value "depth" - ttk::spinbox $f.annotate.typ.spDelay -width 5 -textvariable ::annotateTime -from 0.1 -to 999 \ - -validate key -justify right - ttk::spinbox $f.annotate.typ.depth -width 5 -textvariable ::annotate(depth) -from 2 -to 999 \ - -validate key -justify right - ttk::radiobutton $f.annotate.allmoves -text $::tr(AnnotateAllMoves) -variable ::annotate(annotateBlunders) -value allmoves - ttk::radiobutton $f.annotate.blundersonly -text $::tr(AnnotateBlundersOnly) -variable ::annotate(annotateBlunders) -value blundersonly + ttk::radiobutton $f.annotate.typ.label -text $::tr(AnnotateTime) -variable ::annotate(typ) -value "movetime" + ttk::radiobutton $f.annotate.typ.ldepth -text "Depth per move" -variable ::annotate(typ) -value "depth" + ttk::spinbox $f.annotate.typ.spDelay -width 5 -textvariable ::annotateTime -from 0.1 -to 999 -validate key -justify right + ttk::spinbox $f.annotate.typ.depth -width 5 -textvariable ::annotate(depth) -from 2 -to 999 -validate key -justify right + ttk::radiobutton $f.annotate.allmoves -text $::tr(AnnotateAllMoves) -variable ::annotate(annotateBlunders) -value allmoves + ttk::radiobutton $f.annotate.blundersonly -text $::tr(AnnotateBlundersOnly) -variable ::annotate(annotateBlunders) -value blundersonly ttk::frame $f.annotate.blunderbox ttk::label $f.annotate.blunderbox.label -text $::tr(BlundersThreshold:) ttk::spinbox $f.annotate.blunderbox.spBlunder -width 4 -textvariable ::annotateBlunderThreshold \ -from 0.1 -to 3.0 -increment 0.1 -justify right ttk::checkbutton $f.annotate.cbBook -text $::tr(UseBook) -variable ::annotate(useAnalysisBook) - ::engineNoWin::createEngineOptionsFrame $f AnnoEngine ::annotate(engine) - + ::engineNoWin::createEngineOptionsFrame $f annotateEngine ::annotate(engine) 3 ::annotation::eng_messages # choose a book for analysis # load book names @@ -175,15 +174,13 @@ namespace eval ::annotation { if { $::annotate(AnalysisBookName) eq "" } { set ::annotate(AnalysisBookName) [lindex $tmp $idx] } ttk::combobox $f.annotate.comboBooks -width 12 -values $tmp -textvariable ::annotate(AnalysisBookName) catch { $f.annotate.comboBooks current $idx } - pack $f.annotate.comboBooks -side bottom -anchor w -padx 20 - pack $f.annotate.cbBook -side bottom -anchor w pack $f.annotate.blunderbox.label -side left -padx { 20 0 } pack $f.annotate.blunderbox.spBlunder -side left -anchor w - pack $f.annotate.blunderbox -side bottom -anchor w - pack $f.annotate.blundersonly -side bottom -anchor w - pack $f.annotate.allmoves -side bottom -anchor w - pack $f.engine -in $f.annotate -side bottom -anchor w - pack $f.annotate.typ -side bottom -anchor w + pack $f.annotate.typ -side top -anchor w + pack $f.annotateEngine -in $f.annotate -side top -anchor w + pack $f.annotate.allmoves $f.annotate.blundersonly $f.annotate.blunderbox -side top -anchor w + pack $f.annotate.cbBook -side top -anchor w + pack $f.annotate.comboBooks -side top -anchor w -padx 20 grid $f.annotate.typ.label -row 0 -column 0 -sticky w grid $f.annotate.typ.ldepth -row 1 -column 0 -sticky w grid $f.annotate.typ.spDelay -row 0 -column 1 -sticky w @@ -246,8 +243,8 @@ namespace eval ::annotation { if { $::autoplayMode } { set ::autoplayMode 0 } else { - catch { unset ::enginewin::engConfig_AnnoEngine } - ::engine::close AnnoEngine + catch { unset ::enginewin::engConfig_annotateEngine } + ::engine::close annotateEngine destroy .annotationDialog } } @@ -256,7 +253,7 @@ namespace eval ::annotation { set ::annotate(movetime) [expr {int($::annotateTime * 1000.0)}] set ::annotate(blunderThreshold) $::annotateBlunderThreshold set ::annotate(time) $::annotateTime - set msg [::engineNoWin::initEngine AnnoEngine $::annotate(engine) [list ::annotation::eng_messages AnnoEngine .annotationDialog.f.engpara]] + set msg [::engineNoWin::initEngine annotateEngine $::annotate(engine) [list ::annotation::eng_messages annotateEngine .annotationDialog.f.engpara]] if { $msg eq "ok" } { ::annotation::runAnnotation } else { @@ -271,7 +268,7 @@ namespace eval ::annotation { # reset values for every game proc initGameAnnotation { } { #reset engine - ::engine::send AnnoEngine NewGame [list analysis post_pv post_wdl] + ::engine::send annotateEngine NewGame [list analysis post_pv post_wdl] # calc amount of moves to analyze for progressbar set firstmove [llength [sc_game moves]] sc_game push copyfast @@ -303,7 +300,7 @@ namespace eval ::annotation { # Annotate all remaining moves of the game while { 1 } { set ::annotate(PV1) [list "" "" ""] - ::engine::send AnnoEngine Go [list [sc_game UCI_currentPos] [list $::annotate(typ) $::annotate($::annotate(typ))]] + ::engine::send annotateEngine Go [list [sc_game UCI_currentPos] [list $::annotate(typ) $::annotate($::annotate(typ))]] vwait ::annotate(move_done) addAnnotation incr ::annotate(progress) @@ -316,7 +313,7 @@ namespace eval ::annotation { proc runAnnotation { } { set f .annotationDialog.f - grid forget $f.annotate $f.comment $f.av $f.batch $f.engineOptions + grid forget $f.annotate $f.comment $f.av $f.batch $f.optsannotateEngine pack forget $f.buttons.ok if {!$::annotate(batchMode)} { grid forget $f.running.games $f.running.line2 } # show progressbar and game infos @@ -326,7 +323,7 @@ namespace eval ::annotation { grid $f.running -row 2 -column 0 -columnspan 2 -sticky we # tactical positions is selected, must be in multipv mode - if {$::annotate(tacticalExercises)} { ::engine::send AnnoEngine SetOptions "MultiPV 4" } + if {$::annotate(tacticalExercises)} { ::engine::send annotateEngine SetOptions "MultiPV 4" } set ::autoplayMode 1 set gameNo [sc_game number] @@ -341,8 +338,8 @@ namespace eval ::annotation { annotateGame } set ::autoplayMode 0 - unset ::enginewin::engConfig_AnnoEngine - ::engine::close AnnoEngine + unset ::enginewin::engConfig_annotateEngine + ::engine::close annotateEngine ::notify::PosChanged -pgn destroy .annotationDialog } From 94ea51addf2574aaa844d7c80016ea4bbb77ad36 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 10 Feb 2025 16:55:04 +0100 Subject: [PATCH 26/57] check uci engine in initEngine rename editengine in showhideFrame --- tcl/tools/annotate.tcl | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 0b5ba2fe1..b4c9719d6 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -15,35 +15,36 @@ namespace eval ::engineNoWin {} # Open the engine and configure it proc ::engineNoWin::initEngine { id engine callback {addOpts "MultiPV 2"}} { - if { [info exists ::enginewin::engConfig_$id] } { return "ok" } + if { [info exists ::enginewin::engConfig_$id] } { return 1 } set config [::enginecfg::get $engine] lassign $config name cmd args wdir elo time url uci options - if { ! $uci } { return "Only UCI-Engines are supported!" } + if { ! $uci } { + tk_messageBox -title Scid -icon info -type ok -message "Only UCI-Engines are supported!" + return 0 + } set ::enginewin::engConfig_$id [list $name $cmd $args $wdir $elo $time $url $uci {}] ::engine::setLogCmd $id {} ::engine::connect $id $callback $cmd {} lappend options $addOpts ::engine::send $id SetOptions $options - return "ok" + return 1 } -proc ::engineNoWin::changeEngine {id w {button ""}} { +proc ::engineNoWin::changeEngine {id w enginevar callback} { ::engine::close $id $w.text configure -state normal $w.text delete 1.0 end foreach wchild [winfo children $w.text] { destroy $wchild } catch { unset ::enginewin::engConfig_$id } - if { $button ne "" && [winfo ismapped $w] } { - grid forget $w - event generate $button <> - } + set engine [set $enginevar] + ::engineNoWin::initEngine $id $engine [list $callback $id $w] } -proc ::engineNoWin::editEngine {id w enginevar col callback} { + +proc ::engineNoWin::showHideOptionsFrame {id w enginevar callback col} { if { [winfo ismapped $w] } { grid forget $w ; return } grid $w -row 0 -column $col -rowspan 2 -sticky ne -padx 10 set engine [set $enginevar] - set msg [::engineNoWin::initEngine $id $engine [list $callback $id $w]] - if { $msg ne "ok" } { tk_messageBox -title Scid -icon info -type ok -message $msg } + ::engineNoWin::initEngine $id $engine [list $callback $id $w] } #create frame for edit engine options @@ -52,9 +53,9 @@ proc ::engineNoWin::createEngineOptionsFrame {f id var col callback} { set engList [::enginecfg::names ] if { [set $var] eq "" } { set $var [lindex $engList 0] } ttk::combobox $f.$id.eng -width 20 -state readonly -values $engList -textvariable $var - bind $f.$id.eng <> "::engineNoWin::changeEngine $id $f.opts$id $f.$id.opts" + bind $f.$id.eng <> "::engineNoWin::changeEngine $id $f.opts$id $var $callback" ttk::button $f.$id.opts -image ::icon::filter_adv -style Toolbutton \ - -command "::engineNoWin::editEngine $id $f.opts$id $var $col $callback" + -command "::engineNoWin::showHideOptionsFrame $id $f.opts$id $var $callback $col" pack $f.$id.eng $f.$id.opts -side left -padx { 0 5 } ttk::labelframe $f.opts$id -text "Engine Parameter" ttk::label $f.opts$id.l -textvariable $var @@ -253,11 +254,9 @@ namespace eval ::annotation { set ::annotate(movetime) [expr {int($::annotateTime * 1000.0)}] set ::annotate(blunderThreshold) $::annotateBlunderThreshold set ::annotate(time) $::annotateTime - set msg [::engineNoWin::initEngine annotateEngine $::annotate(engine) [list ::annotation::eng_messages annotateEngine .annotationDialog.f.engpara]] - if { $msg eq "ok" } { + if { [::engineNoWin::initEngine annotateEngine $::annotate(engine) \ + [list ::annotation::eng_messages annotateEngine .annotationDialog.f.engpara]] } { ::annotation::runAnnotation - } else { - tk_messageBox -title Scid -icon info -type ok -message $msg } } pack $f.buttons.cancel $f.buttons.ok -side right -padx 5 -pady 5 From 308d2276ed14deb048065aa4a65471bb7487905d Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 10 Feb 2025 17:31:04 +0100 Subject: [PATCH 27/57] check uci engine in initEngine rename editengine in showhideFrame --- tcl/tools/finishgame.tcl | 46 +++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/tcl/tools/finishgame.tcl b/tcl/tools/finishgame.tcl index c8d540d55..bee636eb6 100644 --- a/tcl/tools/finishgame.tcl +++ b/tcl/tools/finishgame.tcl @@ -11,45 +11,47 @@ namespace eval ::engineNoWin {} # Open the engine and configure it proc ::engineNoWin::initEngine { id engine callback {addOpts "MultiPV 2"}} { - if { [info exists ::enginewin::engConfig_$id] } { return "ok" } + if { [info exists ::enginewin::engConfig_$id] } { return 1 } set config [::enginecfg::get $engine] lassign $config name cmd args wdir elo time url uci options - if { ! $uci } { return "Only UCI-Engines are supported!" } + if { ! $uci } { + tk_messageBox -title Scid -icon info -type ok -message "Only UCI-Engines are supported!" + return 0 + } set ::enginewin::engConfig_$id [list $name $cmd $args $wdir $elo $time $url $uci {}] ::engine::setLogCmd $id {} ::engine::connect $id $callback $cmd {} lappend options $addOpts ::engine::send $id SetOptions $options - return "ok" + return 1 } -proc ::engineNoWin::changeEngine {id w {button ""}} { +proc ::engineNoWin::changeEngine {id w enginevar callback} { ::engine::close $id $w.text configure -state normal $w.text delete 1.0 end foreach wchild [winfo children $w.text] { destroy $wchild } catch { unset ::enginewin::engConfig_$id } - if { $button ne "" && [winfo ismapped $w] } { - event generate $button <> - } + set engine [set $enginevar] + ::engineNoWin::initEngine $id $engine [list $callback $id $w] } -proc ::engineNoWin::editEngine {id w enginevar col callback} { + +proc ::engineNoWin::showHideOptionsFrame {id w enginevar callback col} { if { [winfo ismapped $w] } { grid forget $w ; return } grid $w -row 0 -column $col -rowspan 2 -sticky ne -padx 10 set engine [set $enginevar] - set msg [::engineNoWin::initEngine $id $engine [list $callback $id $w]] - if { $msg ne "ok" } { tk_messageBox -title Scid -icon info -type ok -message $msg } + ::engineNoWin::initEngine $id $engine [list $callback $id $w] } #create frame for edit engine options -proc ::engineNoWin::createEngineOptionsFrame {f id var col} { +proc ::engineNoWin::createEngineOptionsFrame {f id var col callback} { ttk::frame $f.$id set engList [::enginecfg::names ] if { [set $var] eq "" } { set $var [lindex $engList 0] } ttk::combobox $f.$id.eng -width 20 -state readonly -values $engList -textvariable $var - bind $f.$id.eng <> "::engineNoWin::changeEngine $id $f.opts$id $f.$id.opts" + bind $f.$id.eng <> "::engineNoWin::changeEngine $id $f.opts$id $var $callback" ttk::button $f.$id.opts -image ::icon::filter_adv -style Toolbutton \ - -command "::engineNoWin::editEngine $id $f.opts$id $var $col ::finishgame::eng_messages" + -command "::engineNoWin::showHideOptionsFrame $id $f.opts$id $var $callback $col" pack $f.$id.eng $f.$id.opts -side left -padx { 0 5 } ttk::labelframe $f.opts$id -text "Engine Parameter" ttk::label $f.opts$id.l -textvariable $var @@ -112,7 +114,7 @@ namespace eval ::finishgame { ttk::spinbox $w.wh_f.cv -width 3 -textvariable ::finishGame(cmdValuewhite) -from 1 -to 999 -justify right ttk::radiobutton $w.wh_f.c1 -text $::tr(seconds) -variable ::finishGame(cmdwhite) -value "movetime" ttk::radiobutton $w.wh_f.c2 -text $::tr(FixedDepth) -variable ::finishGame(cmdwhite) -value "depth" - ::engineNoWin::createEngineOptionsFrame $w fgEnginewhite ::finishGame(enginewhite) 4 + ::engineNoWin::createEngineOptionsFrame $w fgEnginewhite ::finishGame(enginewhite) 4 ::finishgame::eng_messages grid $w.fgEnginewhite -in $w.wh_f -column 1 -row 0 -columnspan 3 -sticky w grid $w.wh_f.cv -column 1 -row 2 -sticky w grid $w.wh_f.c1 -column 2 -row 2 -sticky w -padx 6 @@ -125,7 +127,7 @@ namespace eval ::finishgame { ttk::spinbox $w.bk_f.cv -width 3 -textvariable ::finishGame(cmdValueblack) -from 1 -to 999 -justify right ttk::radiobutton $w.bk_f.c1 -text $::tr(seconds) -variable ::finishGame(cmdblack) -value "movetime" ttk::radiobutton $w.bk_f.c2 -text $::tr(FixedDepth) -variable ::finishGame(cmdblack) -value "depth" - ::engineNoWin::createEngineOptionsFrame $w fgEngineblack ::finishGame(engineblack) 5 + ::engineNoWin::createEngineOptionsFrame $w fgEngineblack ::finishGame(engineblack) 5 ::finishgame::eng_messages grid $w.fgEngineblack -in $w.bk_f -column 1 -row 0 -columnspan 3 -sticky w grid $w.bk_f.cv -column 1 -row 2 -sticky w grid $w.bk_f.c1 -column 2 -row 2 -sticky w -padx 6 @@ -151,15 +153,11 @@ namespace eval ::finishgame { } ttk::button $w.fbuttons.ok -text "OK" -command { - set msg [::finishgame::initfgEngine white $::finishGame(enginewhite)] - if { $msg eq "ok" } { - set msg [::finishgame::initfgEngine black $::finishGame(engineblack)] - if { $msg eq "ok" } { - ::finishgame::runFinishGame - } - } - if { $msg ne "ok" } { - tk_messageBox -title Scid -icon info -type ok -message $msg + if { [::engineNoWin::initEngine fgEnginewhite $::finishGame(enginewhite) \ + [list ::finishgame::eng_messages fgEnginewhite .configFinishGame.optsfgEnginewhite]] && + [::engineNoWin::initEngine fgEngineblack $::finishGame(engineblack) \ + [list ::finishgame::eng_messages fgEngineblack .configFinishGame.optsfgEngineblack]] } { + ::finishgame::runFinishGame } } packbuttons right $w.fbuttons.cancel $w.fbuttons.ok From c62a932ba6dc7c0e617585a8c894762ebf5d5cf1 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sat, 15 Feb 2025 22:12:12 +0100 Subject: [PATCH 28/57] add chess960 --- tcl/tools/annotate.tcl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index b4c9719d6..3388871cf 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -267,7 +267,7 @@ namespace eval ::annotation { # reset values for every game proc initGameAnnotation { } { #reset engine - ::engine::send annotateEngine NewGame [list analysis post_pv post_wdl] + ::engine::send annotateEngine NewGame [list analysis post_pv post_wdl [sc_game variant]] # calc amount of moves to analyze for progressbar set firstmove [llength [sc_game moves]] sc_game push copyfast @@ -660,9 +660,6 @@ namespace eval ::annotation { lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } set ::annotate(PV$multipv) [list $score $score_type $pv] - if { $multipv == 1 } { - set pv [sc_pos coordToSAN $::annotate(position) $pv] - } } "InfoBestMove" { lassign $msgData ::annotate(bestmove) From 0af49ee22bab19bf6c989c32a6dcbfaf799c56a1 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sat, 15 Feb 2025 22:20:32 +0100 Subject: [PATCH 29/57] use new engine interface for serious game --- tcl/tools/sergame.tcl | 232 ++++++++++++++++++------------------------ 1 file changed, 101 insertions(+), 131 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 1e9851d72..3d0e1da8b 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -48,58 +48,15 @@ namespace eval sergame { ttk::labelframe $w.ftime -text $::tr(TimeMode) ttk::labelframe $w.fopening -text $::tr(Opening) - grid $w.fengines -row 0 -column 0 -pady { 0 10 } -sticky we -padx { 0 10 } + grid $w.fengines -row 0 -column 0 -pady { 0 10 } -sticky nswe -padx { 0 10 } grid $w.fopening -row 0 -column 1 -pady { 0 10 } -sticky nswe -padx { 10 0 } grid $w.ftime -row 1 -column 0 -pady { 10 0 } -sticky nswe -padx { 0 10 } grid $w.fconfig -row 1 -column 1 -pady { 10 0 } -sticky we -padx { 10 0 } grid $w.fbuttons -row 2 -column 1 -sticky we # builds the list of UCI engines - ttk::frame $w.fengines.fEnginesList - ttk::treeview $w.fengines.fEnginesList.lbEngines -columns {0} -show {} -selectmode browse \ - -yscrollcommand "$w.fengines.fEnginesList.ybar set" - $w.fengines.fEnginesList.lbEngines column 0 -width 100 - $w.fengines.fEnginesList.lbEngines configure -height 5 - ttk::scrollbar $w.fengines.fEnginesList.ybar -command "$w.fengines.fEnginesList.lbEngines yview" - pack $w.fengines.fEnginesList.ybar -side right -fill y - pack $w.fengines.fEnginesList.lbEngines -side left -fill x -expand 1 - pack $w.fengines.fEnginesList -expand yes -fill x -side top - - - set i 0 - set idx 0 - foreach e $::engines(list) { - if { [lindex $e 7] != 1} { incr idx ; continue } - set ::sergame::engineListBox($i) $idx - set name [lindex $e 0] - $w.fengines.fEnginesList.lbEngines insert {} end -id $idx -values [list $name] - incr i - incr idx - } - - # Engine configuration (limit strength for example) - ttk::button $w.fengines.bEngineConfig -text $::tr(ConfigureUCIengine) -command { - set sel [.configSerGameWin.fengines.fEnginesList.lbEngines selection] - set index $::sergame::engineListBox($sel) - set engineData [lindex $::engines(list) $index] - set name [lindex $engineData 0] - set cmd [ toAbsPath [lindex $engineData 1] ] - set args [lindex $engineData 2] - set dir [ toAbsPath [lindex $engineData 3] ] - set options [lindex $engineData 8] - ::uci::uciConfig 3 [ toAbsPath $cmd ] $args [ toAbsPath $dir ] $options - } - pack $w.fengines.bEngineConfig -side top -pady 5 -anchor e -padx 4 - - # if no engines defined, bail out - if {$i == 0} { - tk_messageBox -type ok -message "No UCI engine defined" -icon error - destroy $w - return - } - - $w.fengines.fEnginesList.lbEngines selection set $::sergame::chosenEngine - $w.fengines.fEnginesList.lbEngines see $::sergame::chosenEngine + ::engineNoWin::createEngineOptionsFrame $w serEngine ::sergame::engineName 5 ::sergame::eng_messages + pack $w.serEngine -in $w.fengines -side top -pady 5 -anchor w -padx 4 # load book names ttk::checkbutton $w.fconfig.cbUseBook -text $::tr(UseBook) -variable ::sergame::useBook @@ -230,8 +187,6 @@ namespace eval sergame { ttk::button $w.fbuttons.close -text $::tr(Play) -command { focus . - set ::sergame::chosenEngine [.configSerGameWin.fengines.fEnginesList.lbEngines selection] - set ::sergame::engineName [.configSerGameWin.fengines.fEnginesList.lbEngines set $::sergame::chosenEngine 0] set ::sergame::chosenOpening [.configSerGameWin.fopening.fOpeningList.lbOpening selection] if {$::sergame::useBook} { set ::sergame::bookToUse [.configSerGameWin.fconfig.combo get] @@ -247,8 +202,11 @@ namespace eval sergame { set ::uci::uciInfo(fixednodes3) [expr [.configSerGameWin.ftime.nodes.value get]*1000] set ::uci::uciInfo(movetime3) [expr [.configSerGameWin.ftime.movetime.value get]*1000] - destroy .configSerGameWin - ::sergame::play $::sergame::chosenEngine + set callback [list ::sergame::eng_messages serEngine nop] + if { [::engineNoWin::initEngine serEngine $::sergame::engineName $callback] } { + destroy .configSerGameWin + ::sergame::play serEngine + } } ttk::button $w.fbuttons.cancel -textvar ::tr(Cancel) -command "focus .; destroy $w" @@ -264,7 +222,7 @@ namespace eval sergame { ################################################################################ # ################################################################################ - proc play { engine {n 3} } { + proc play { engine } { global ::sergame::chosenOpening ::sergame::isOpening ::tacgame::openingList ::sergame::openingMovesList \ ::sergame::openingMovesHash ::sergame::openingMoves ::sergame::outOfOpening @@ -273,28 +231,18 @@ namespace eval sergame { } set ::sergame::lFen {} - - ::uci::startEngine $::sergame::engineListBox($engine) $n - set engineData [lindex $::engines(list) $::sergame::engineListBox($engine)] - foreach {option} [lindex $engineData 8] { - array set ::uciOptions$n $option - } - ::uci::sendUCIoptions $n - - set ::uci::uciInfo(prevscore$n) 0.0 - set ::uci::uciInfo(score$n) 0.0 - set ::uci::uciInfo(ponder$n) "" + set ::uci::uciInfo(prevscore3) 0.0 + set ::uci::uciInfo(score3) 0.0 + set ::uci::uciInfo(ponder3) "" if {$::sergame::startFromCurrent} { set isOpening 0 } # ponder - if {$::sergame::ponder} { - ::sergame::sendToEngine $n "setoption name Ponder value true" - } else { - ::sergame::sendToEngine $n "setoption name Ponder value false" - } + set ponder false + if {$::sergame::ponder} { set ponder true } + ::engine::send $engine SetOptions [list {Ponder true}] # if will follow a specific opening line if {$isOpening} { @@ -342,10 +290,37 @@ namespace eval sergame { ::setPlayMode "::sergame::callback" ::notify::GameChanged - clocks init $n + clocks init clocks start - ::sergame::engineGo $n + ::sergame::engineGo + } + + proc ::sergame::eng_messages {id w msg} { + lassign $msg msgType msgData + switch $msgType { + "InfoConfig" { + if { ! [winfo exists $w] } { return } + set msgData [lindex $msgData 2] + ::engineNoWin::initEngineOptions $id $w $msgData + } + "InfoPV" { + lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv + if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } + } + "InfoBestMove" { + lassign $msgData ::uci::uciInfo(bestmove3) ponder ::uci::uciInfo(ponder3) + } + "InfoGo" { + lassign $msgData ::annotate(position) + } + "InfoDisconnected" { + lassign $msgData errorMsg + if {$errorMsg eq ""} { set errorMsg "The connection with the engine terminated unexpectedly." } + tk_messageBox -icon warning -type ok -parent . -message $errorMsg + set ::autoplayMode 0 + } + } } proc callback {cmd args} { @@ -358,27 +333,27 @@ namespace eval sergame { return 0 } - proc abortGame { { n 3 } } { + proc abortGame { } { ::setPlayMode "" - after cancel ::sergame::engineGo $n + after cancel ::sergame::engineGo clocks stop set ::sergame::lFen {} - if { $::uci::uciInfo(pipe$n) != ""} { - ::uci::closeUCIengine $n - set ::uci::uciInfo(bestmove$n) "abort" - } + ::engine::send serEngine StopGo + ::engine::close serEngine + unset ::enginewin::engConfig_serEngine + set ::uci::uciInfo(bestmove3) "abort" ::notify::GameChanged } - proc clocks {cmd {n 3}} { + proc clocks {cmd} { if {$::sergame::timeMode != "timebonus"} { return } switch $cmd { init { ::gameclock::new "" 1 ::gameclock::new "" 2 - ::gameclock::setSec 1 [expr 0 - $::uci::uciInfo(wtime$n)/1000] - ::gameclock::setSec 2 [expr 0 - $::uci::uciInfo(btime$n)/1000] + ::gameclock::setSec 1 [expr 0 - $::uci::uciInfo(wtime3)/1000] + ::gameclock::setSec 2 [expr 0 - $::uci::uciInfo(btime3)/1000] } start { if { [sc_pos side] == "white" } { @@ -393,11 +368,11 @@ namespace eval sergame { } toggle { if {[::gameclock::stop 1]} { - ::gameclock::add 1 [expr $::uci::uciInfo(winc$n)/1000] + ::gameclock::add 1 [expr $::uci::uciInfo(winc3)/1000] ::gameclock::storeTimeComment 1 ::gameclock::start 2 } elseif {[::gameclock::stop 2]} { - ::gameclock::add 2 [expr $::uci::uciInfo(binc$n)/1000] + ::gameclock::add 2 [expr $::uci::uciInfo(binc3)/1000] ::gameclock::storeTimeComment 2 ::gameclock::start 1 } @@ -416,13 +391,6 @@ namespace eval sergame { ::notify::PosChanged -pgn } - ################################################################################ - # - ################################################################################ - proc sendToEngine {n text} { - ::sergame::logEngine $n "Scid : $text" - catch {puts $::uci::uciInfo(pipe$n) $text} - } ################################################################################ # returns true if last move is a mate and stops clocks ################################################################################ @@ -437,17 +405,17 @@ namespace eval sergame { ################################################################################ # ################################################################################ - proc engineGo { n } { + proc engineGo { } { global ::sergame::isOpening ::sergame::openingMovesList ::sergame::openingMovesHash ::sergame::openingMoves \ ::sergame::timeMode ::sergame::outOfOpening - after cancel ::sergame::engineGo $n + after cancel ::sergame::engineGo if { [::sergame::endOfGame] } { return } if { [sc_pos side] != $::sergame::engineColor } { set ::sergame::waitPlayerMove 1 - after 1000 ::sergame::engineGo $n + after 1000 ::sergame::engineGo return } @@ -459,7 +427,7 @@ namespace eval sergame { if {$::sergame::timeMode == "timebonus"} { set takebackClockW [::gameclock::getSec 1] set takebackClockB [::gameclock::getSec 2] - clocks toggle $n + clocks toggle } repetition } @@ -480,7 +448,7 @@ namespace eval sergame { -message "$::tr(NotFollowedLine) $openingMoves\n $::tr(DoYouWantContinue)" ] if {$answer == no} { takeBack $takebackClockW $takebackClockB - after 1000 ::sergame::engineGo $n + after 1000 ::sergame::engineGo return } else { set outOfOpening 1 @@ -512,10 +480,10 @@ namespace eval sergame { sc_move forward 1 } - clocks toggle $n + clocks toggle updateBoard -pgn -animate repetition - after 1000 ::sergame::engineGo $n + after 1000 ::sergame::engineGo return } } @@ -530,49 +498,47 @@ namespace eval sergame { sc_move addSan $move ::utils::sound::AnnounceNewMove $move # we made a book move so assume a score = 0 - set ::uci::uciInfo(prevscore$n) 0.0 - clocks toggle $n + set ::uci::uciInfo(prevscore3) 0.0 + clocks toggle updateBoard -pgn -animate repetition - after 1000 ::sergame::engineGo $n + after 1000 ::sergame::engineGo return } } # ------------------------------------------------------------- # check if the engine pondered on the right move - if { $::sergame::ponder && $::uci::uciInfo(ponder$n) == [sc_game info previousMoveUCI]} { - ::sergame::sendToEngine $n "ponderhit" + if { $::sergame::ponder && $::uci::uciInfo(ponder3) == [sc_game info previousMoveUCI]} { + ::engine::rawsend serEngine "ponderhit" } else { - if { $::sergame::ponder } { - ::sergame::sendToEngine $n "stop" + ::engine::send serEngine StopGo } - set ::analysis(waitForReadyOk$n) 1 - ::sergame::sendToEngine $n "isready" - vwait ::analysis(waitForReadyOk$n) - ::sergame::sendToEngine $n "position fen [sc_pos fen]" if {$timeMode == "timebonus"} { set wtime [expr [::gameclock::getSec 1] * 1000 ] set btime [expr [::gameclock::getSec 2] * 1000 ] - ::sergame::sendToEngine $n "go wtime $wtime btime $btime winc $::uci::uciInfo(winc$n) binc $::uci::uciInfo(binc$n)" + set parameter "wtime $wtime btime $btime winc $::uci::uciInfo(winc3) binc $::uci::uciInfo(binc3)" } elseif {$timeMode == "depth"} { - ::sergame::sendToEngine $n "go depth $::uci::uciInfo(fixeddepth$n)" + set parameter "depth $::uci::uciInfo(fixeddepth3)" } elseif {$timeMode == "movetime"} { - ::sergame::sendToEngine $n "go movetime $::uci::uciInfo(movetime$n)" + set parameter "movetime $::uci::uciInfo(movetime3)" } elseif {$timeMode == "nodes"} { - ::sergame::sendToEngine $n "go nodes $::uci::uciInfo(fixednodes$n)" + set parameter "nodes $::uci::uciInfo(fixednodes3)" } +# ::engine::send serEngine Position "fen [sc_pos fen]" + ::engine::send serEngine Go [list "position fen [sc_pos fen]" $parameter]; #[list $::annotate(typ) $::annotate($::annotate(typ))]] +# ::engine::send serEngine Go [list [sc_game UCI_currentPos] $parameter]; #[list $::annotate(typ) $::annotate($::annotate(typ))]] } - set ::uci::uciInfo(bestmove$n) "" - vwait ::uci::uciInfo(bestmove$n) + set ::uci::uciInfo(bestmove3) "" + vwait ::uci::uciInfo(bestmove3) # ------------------------------------------------------------- # if weak move detected, propose the user to tack back - if { $::sergame::coachIsWatching && $::uci::uciInfo(prevscore$n) != "" } { + if { $::sergame::coachIsWatching && $::uci::uciInfo(prevscore3) != "" } { set blunder 0 - set delta [expr $::uci::uciInfo(score$n) - $::uci::uciInfo(prevscore$n)] + set delta [expr $::uci::uciInfo(score3) - $::uci::uciInfo(prevscore3)] if {$delta > $::informant("?!") && $::sergame::engineColor == "white" || $delta < [expr 0.0 - $::informant("?!")] && $::sergame::engineColor == "black" } { set blunder 1 @@ -601,7 +567,7 @@ namespace eval sergame { set answer [tk_messageBox -icon question -parent .main -title "Scid" -type yesno -message $::tr($tBlunder) ] if {$answer == yes} { takeBack $takebackClockW $takebackClockB - after 1000 ::sergame::engineGo $n + after 1000 ::sergame::engineGo return } clocks start @@ -609,38 +575,42 @@ namespace eval sergame { } # ------------------------------------------------------------- - if { $::uci::uciInfo(bestmove$n) == "abort" } { + if { $::uci::uciInfo(bestmove3) == "abort" } { return } - ::uci::sc_move_add $::uci::uciInfo(bestmove$n) - ::utils::sound::AnnounceNewMove $::uci::uciInfo(bestmove$n) - set ::uci::uciInfo(prevscore$n) $::uci::uciInfo(score$n) + ::uci::sc_move_add $::uci::uciInfo(bestmove3) + ::utils::sound::AnnounceNewMove $::uci::uciInfo(bestmove3) + set ::uci::uciInfo(prevscore3) $::uci::uciInfo(score3) if { $::sergame::storeEval == 1 } { - storeEvalComment $::uci::uciInfo(score$n) + storeEvalComment $::uci::uciInfo(score3) } updateBoard -pgn -animate repetition - clocks toggle $n + clocks toggle - # ponder mode (the engine just played its move) - if {$::sergame::ponder && $::uci::uciInfo(ponder$n) != ""} { - ::sergame::sendToEngine $n "position fen [sc_pos fen] moves $::uci::uciInfo(ponder$n)" - set wtime [expr [::gameclock::getSec 1] * 1000 ] - set btime [expr [::gameclock::getSec 2] * 1000 ] + # ponder mode (the engine just played its move) ;&& $::uci::uciInfo(ponder3) != "" + if {$::sergame::ponder } { if {$timeMode == "timebonus"} { - ::sergame::sendToEngine $n "go ponder wtime $wtime btime $btime winc $::uci::uciInfo(winc$n) binc $::uci::uciInfo(binc$n)" + set wtime [expr [::gameclock::getSec 1] * 1000 ] + set btime [expr [::gameclock::getSec 2] * 1000 ] + set parameter "ponder wtime $wtime btime $btime winc $::uci::uciInfo(winc3) binc $::uci::uciInfo(binc3)" } elseif {$timeMode == "depth"} { - ::sergame::sendToEngine $n "go ponder depth $::uci::uciInfo(fixeddepth$n)" + set parameter "ponder depth $::uci::uciInfo(fixeddepth3)" } elseif {$timeMode == "movetime"} { - ::sergame::sendToEngine $n "go ponder movetime $::uci::uciInfo(movetime$n)" + set parameter "ponder movetime $::uci::uciInfo(movetime3)" } elseif {$timeMode == "nodes"} { - ::sergame::sendToEngine $n "go ponder nodes $::uci::uciInfo(fixednodes$n)" + set parameter "ponder nodes $::uci::uciInfo(fixednodes3)" } +# ::sergame::sendToEngine $n "position fen [sc_pos fen] moves $::uci::uciInfo(ponder$n)" +# ::engine::send serEngine Position "fen [sc_pos fen] moves $::uci::uciInfo(ponder$n)" +puts "[list "position fen [sc_pos fen] moves $::uci::uciInfo(ponder3)" $parameter]" + ::engine::send serEngine Go [list "position fen [sc_pos fen] moves $::uci::uciInfo(ponder3)" $parameter] +# ::engine::send serEngine Go [list [sc_game UCI_currentPos] ponder $parameter] } - after 1000 ::sergame::engineGo $n + after 1000 ::sergame::engineGo } ################################################################################ # add current position for 3fold repetition detection and returns 1 if @@ -664,7 +634,7 @@ namespace eval sergame { # ################################################################################ proc logEngine {n text} { - if {$::uci::uciInfo(log_stdout$n)} { + if {$::uci::uciInfo(log_stdout3)} { puts stdout "$n $text" } } From 702b669713ab7f4c2e38a0cf8d67114566ef1d02 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sat, 15 Feb 2025 22:21:34 +0100 Subject: [PATCH 30/57] store ponder move from engine --- tcl/enginecomm.tcl | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tcl/enginecomm.tcl b/tcl/enginecomm.tcl index 094498583..5e8366518 100644 --- a/tcl/enginecomm.tcl +++ b/tcl/enginecomm.tcl @@ -761,13 +761,9 @@ proc ::uci::parseline {id line} { } if {[string match "bestmove *" $line]} { - lassign [split $line] -> ::engconn(InfoBestMove_$id) ponder ponder_move - #TODO: - # lassign [lsearch -inline -index 0 $::engconn(options_$id) "Ponder"] -> do_ponder - # if {$do_ponder eq "true" && $ponder eq "ponder"} - # set ::engconn(waitReply_$id) "Go?" - # ::engine::rawsend $id position ... - # ::engine::rawsend $id go ponder ... + # assign ponder move as well + # starting ponder should not be done here because other parameter like time or depth not available here + set ::engconn(InfoBestMove_$id) [lrange [split $line] 1 3] return 1 } From d852884ab998377af4d8a6c1a6feda385be7ae3e Mon Sep 17 00:00:00 2001 From: Uwe Date: Sat, 15 Feb 2025 23:13:39 +0100 Subject: [PATCH 31/57] activate blunder check --- tcl/tools/sergame.tcl | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 3d0e1da8b..23dada892 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -306,7 +306,9 @@ namespace eval sergame { } "InfoPV" { lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv - if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } + if { $multipv == 1 } { + set ::uci::uciInfo(score3) [expr $score / 100.0] + } } "InfoBestMove" { lassign $msgData ::uci::uciInfo(bestmove3) ponder ::uci::uciInfo(ponder3) @@ -383,6 +385,7 @@ namespace eval sergame { proc takeBack {takebackClockW takebackClockB} { sc_move back 1 + sc_game truncate if {$takebackClockW != ""} { ::gameclock::setSec 1 [expr 0 - $takebackClockW] ::gameclock::setSec 2 [expr 0 - $takebackClockB] @@ -537,32 +540,13 @@ namespace eval sergame { # ------------------------------------------------------------- # if weak move detected, propose the user to tack back if { $::sergame::coachIsWatching && $::uci::uciInfo(prevscore3) != "" } { - set blunder 0 - set delta [expr $::uci::uciInfo(score3) - $::uci::uciInfo(prevscore3)] - if {$delta > $::informant("?!") && $::sergame::engineColor == "white" || - $delta < [expr 0.0 - $::informant("?!")] && $::sergame::engineColor == "black" } { - set blunder 1 - } - - if {$delta > $::informant("?") && $::sergame::engineColor == "white" || - $delta < [expr 0.0 - $::informant("?")] && $::sergame::engineColor == "black" } { - set blunder 2 - } - - if {$delta > $::informant("??") && $::sergame::engineColor == "white" || - $delta < [expr 0.0 - $::informant("??")] && $::sergame::engineColor == "black" } { - set blunder 3 - } - - if {$blunder == 1} { - set tBlunder "DubiousMovePlayedTakeBack" - } elseif {$blunder == 2} { - set tBlunder "WeakMovePlayedTakeBack" - } elseif {$blunder == 3} { - set tBlunder "BadMovePlayedTakeBack" - } + set tBlunder "" + set delta [expr abs($::uci::uciInfo(score3) - $::uci::uciInfo(prevscore3))] + if {$delta > $::informant("?!") } { set tBlunder "DubiousMovePlayedTakeBack" } + if {$delta > $::informant("?") } { set tBlunder "WeakMovePlayedTakeBack" } + if {$delta > $::informant("??") } { set tBlunder "BadMovePlayedTakeBack" } - if {$blunder != 0} { + if {$tBlunder ne ""} { clocks stop set answer [tk_messageBox -icon question -parent .main -title "Scid" -type yesno -message $::tr($tBlunder) ] if {$answer == yes} { @@ -605,7 +589,6 @@ namespace eval sergame { } # ::sergame::sendToEngine $n "position fen [sc_pos fen] moves $::uci::uciInfo(ponder$n)" # ::engine::send serEngine Position "fen [sc_pos fen] moves $::uci::uciInfo(ponder$n)" -puts "[list "position fen [sc_pos fen] moves $::uci::uciInfo(ponder3)" $parameter]" ::engine::send serEngine Go [list "position fen [sc_pos fen] moves $::uci::uciInfo(ponder3)" $parameter] # ::engine::send serEngine Go [list [sc_game UCI_currentPos] ponder $parameter] } From 8ccc5445623aedb396e0578fb6a9b3780d3a1327 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sat, 15 Feb 2025 23:33:50 +0100 Subject: [PATCH 32/57] replace ::uci::sc_move_add --- tcl/tools/sergame.tcl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 23dada892..3235d1547 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -529,9 +529,7 @@ namespace eval sergame { } elseif {$timeMode == "nodes"} { set parameter "nodes $::uci::uciInfo(fixednodes3)" } -# ::engine::send serEngine Position "fen [sc_pos fen]" ::engine::send serEngine Go [list "position fen [sc_pos fen]" $parameter]; #[list $::annotate(typ) $::annotate($::annotate(typ))]] -# ::engine::send serEngine Go [list [sc_game UCI_currentPos] $parameter]; #[list $::annotate(typ) $::annotate($::annotate(typ))]] } set ::uci::uciInfo(bestmove3) "" @@ -563,7 +561,7 @@ namespace eval sergame { return } - ::uci::sc_move_add $::uci::uciInfo(bestmove3) + sc_move addSan $::uci::uciInfo(bestmove3) ::utils::sound::AnnounceNewMove $::uci::uciInfo(bestmove3) set ::uci::uciInfo(prevscore3) $::uci::uciInfo(score3) if { $::sergame::storeEval == 1 } { @@ -587,10 +585,7 @@ namespace eval sergame { } elseif {$timeMode == "nodes"} { set parameter "ponder nodes $::uci::uciInfo(fixednodes3)" } -# ::sergame::sendToEngine $n "position fen [sc_pos fen] moves $::uci::uciInfo(ponder$n)" -# ::engine::send serEngine Position "fen [sc_pos fen] moves $::uci::uciInfo(ponder$n)" ::engine::send serEngine Go [list "position fen [sc_pos fen] moves $::uci::uciInfo(ponder3)" $parameter] -# ::engine::send serEngine Go [list [sc_game UCI_currentPos] ponder $parameter] } after 1000 ::sergame::engineGo From 0dc7440873bbd9440a3095adfae1f184df18caa9 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 16 Feb 2025 11:10:35 +0100 Subject: [PATCH 33/57] replace ::uci to ::sergame --- tcl/options.tcl | 8 ++-- tcl/tools/sergame.tcl | 88 +++++++++++++++++++++---------------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/tcl/options.tcl b/tcl/options.tcl index 102563427..b3c9cd8c3 100644 --- a/tcl/options.tcl +++ b/tcl/options.tcl @@ -340,10 +340,10 @@ set ::sergame::depth 3 set ::sergame::movetime 0 set ::sergame::nodes 10000 set ::sergame::ponder 0 -set ::uci::uciInfo(wtime3) [expr 5 * 60 * 1000 ] -set ::uci::uciInfo(winc3) [expr 10 * 1000 ] -set ::uci::uciInfo(btime3) [expr 5 * 60 * 1000 ] -set ::uci::uciInfo(binc3) [expr 10 * 1000 ] +set ::sergame::uciInfo(wtime3) [expr 5 * 60 * 1000 ] +set ::sergame::uciInfo(winc3) [expr 10 * 1000 ] +set ::sergame::uciInfo(btime3) [expr 5 * 60 * 1000 ] +set ::sergame::uciInfo(binc3) [expr 10 * 1000 ] # Defaults for initial directories: set initialDir(base) "." diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 3235d1547..2a442492f 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -9,7 +9,7 @@ namespace eval sergame { # DEBUG - set ::uci::uciInfo(log_stdout3) 0 + set ::sergame::uciInfo(log_stdout3) 0 # if true, follow a specific opening set openingMovesList {} @@ -112,10 +112,10 @@ namespace eval sergame { ttk::label $w.ftime.timebonus.blacklseconds -text $::tr(TimeSec) grid $w.ftime.timebonus.blacklseconds -row $row -column 5 - $w.ftime.timebonus.whitespminutes set [expr $::uci::uciInfo(wtime3) / (60 * 1000)] - $w.ftime.timebonus.whitespseconds set [expr $::uci::uciInfo(winc3) / 1000] - $w.ftime.timebonus.blackspminutes set [expr $::uci::uciInfo(btime3) / (60 * 1000)] - $w.ftime.timebonus.blackspseconds set [expr $::uci::uciInfo(binc3) / 1000 ] + $w.ftime.timebonus.whitespminutes set [expr $::sergame::uciInfo(wtime3) / (60 * 1000)] + $w.ftime.timebonus.whitespseconds set [expr $::sergame::uciInfo(winc3) / 1000] + $w.ftime.timebonus.blackspminutes set [expr $::sergame::uciInfo(btime3) / (60 * 1000)] + $w.ftime.timebonus.blackspseconds set [expr $::sergame::uciInfo(binc3) / 1000 ] # Fixed depth ttk::frame $w.ftime.depth @@ -194,13 +194,13 @@ namespace eval sergame { set ::sergame::useBook 0 } } - set ::uci::uciInfo(wtime3) [expr [.configSerGameWin.ftime.timebonus.whitespminutes get]*1000*60] - set ::uci::uciInfo(btime3) [expr [.configSerGameWin.ftime.timebonus.blackspminutes get]*1000*60] - set ::uci::uciInfo(winc3) [expr [.configSerGameWin.ftime.timebonus.whitespseconds get]*1000] - set ::uci::uciInfo(binc3) [expr [.configSerGameWin.ftime.timebonus.blackspseconds get]*1000] - set ::uci::uciInfo(fixeddepth3) [.configSerGameWin.ftime.depth.value get] - set ::uci::uciInfo(fixednodes3) [expr [.configSerGameWin.ftime.nodes.value get]*1000] - set ::uci::uciInfo(movetime3) [expr [.configSerGameWin.ftime.movetime.value get]*1000] + set ::sergame::uciInfo(wtime3) [expr [.configSerGameWin.ftime.timebonus.whitespminutes get]*1000*60] + set ::sergame::uciInfo(btime3) [expr [.configSerGameWin.ftime.timebonus.blackspminutes get]*1000*60] + set ::sergame::uciInfo(winc3) [expr [.configSerGameWin.ftime.timebonus.whitespseconds get]*1000] + set ::sergame::uciInfo(binc3) [expr [.configSerGameWin.ftime.timebonus.blackspseconds get]*1000] + set ::sergame::uciInfo(fixeddepth3) [.configSerGameWin.ftime.depth.value get] + set ::sergame::uciInfo(fixednodes3) [expr [.configSerGameWin.ftime.nodes.value get]*1000] + set ::sergame::uciInfo(movetime3) [expr [.configSerGameWin.ftime.movetime.value get]*1000] set callback [list ::sergame::eng_messages serEngine nop] if { [::engineNoWin::initEngine serEngine $::sergame::engineName $callback] } { @@ -231,9 +231,9 @@ namespace eval sergame { } set ::sergame::lFen {} - set ::uci::uciInfo(prevscore3) 0.0 - set ::uci::uciInfo(score3) 0.0 - set ::uci::uciInfo(ponder3) "" + set ::sergame::uciInfo(prevscore3) 0.0 + set ::sergame::uciInfo(score3) 0.0 + set ::sergame::uciInfo(ponder3) "" if {$::sergame::startFromCurrent} { set isOpening 0 @@ -307,11 +307,11 @@ namespace eval sergame { "InfoPV" { lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv if { $multipv == 1 } { - set ::uci::uciInfo(score3) [expr $score / 100.0] + set ::sergame::uciInfo(score3) [expr $score / 100.0] } } "InfoBestMove" { - lassign $msgData ::uci::uciInfo(bestmove3) ponder ::uci::uciInfo(ponder3) + lassign $msgData ::sergame::uciInfo(bestmove3) ponder ::sergame::uciInfo(ponder3) } "InfoGo" { lassign $msgData ::annotate(position) @@ -343,7 +343,7 @@ namespace eval sergame { ::engine::send serEngine StopGo ::engine::close serEngine unset ::enginewin::engConfig_serEngine - set ::uci::uciInfo(bestmove3) "abort" + set ::sergame::uciInfo(bestmove3) "abort" ::notify::GameChanged } @@ -354,8 +354,8 @@ namespace eval sergame { init { ::gameclock::new "" 1 ::gameclock::new "" 2 - ::gameclock::setSec 1 [expr 0 - $::uci::uciInfo(wtime3)/1000] - ::gameclock::setSec 2 [expr 0 - $::uci::uciInfo(btime3)/1000] + ::gameclock::setSec 1 [expr 0 - $::sergame::uciInfo(wtime3)/1000] + ::gameclock::setSec 2 [expr 0 - $::sergame::uciInfo(btime3)/1000] } start { if { [sc_pos side] == "white" } { @@ -370,11 +370,11 @@ namespace eval sergame { } toggle { if {[::gameclock::stop 1]} { - ::gameclock::add 1 [expr $::uci::uciInfo(winc3)/1000] + ::gameclock::add 1 [expr $::sergame::uciInfo(winc3)/1000] ::gameclock::storeTimeComment 1 ::gameclock::start 2 } elseif {[::gameclock::stop 2]} { - ::gameclock::add 2 [expr $::uci::uciInfo(binc3)/1000] + ::gameclock::add 2 [expr $::sergame::uciInfo(binc3)/1000] ::gameclock::storeTimeComment 2 ::gameclock::start 1 } @@ -501,7 +501,7 @@ namespace eval sergame { sc_move addSan $move ::utils::sound::AnnounceNewMove $move # we made a book move so assume a score = 0 - set ::uci::uciInfo(prevscore3) 0.0 + set ::sergame::uciInfo(prevscore3) 0.0 clocks toggle updateBoard -pgn -animate repetition @@ -512,7 +512,7 @@ namespace eval sergame { # ------------------------------------------------------------- # check if the engine pondered on the right move - if { $::sergame::ponder && $::uci::uciInfo(ponder3) == [sc_game info previousMoveUCI]} { + if { $::sergame::ponder && $::sergame::uciInfo(ponder3) == [sc_game info previousMoveUCI]} { ::engine::rawsend serEngine "ponderhit" } else { if { $::sergame::ponder } { @@ -521,25 +521,25 @@ namespace eval sergame { if {$timeMode == "timebonus"} { set wtime [expr [::gameclock::getSec 1] * 1000 ] set btime [expr [::gameclock::getSec 2] * 1000 ] - set parameter "wtime $wtime btime $btime winc $::uci::uciInfo(winc3) binc $::uci::uciInfo(binc3)" + set parameter "wtime $wtime btime $btime winc $::sergame::uciInfo(winc3) binc $::sergame::uciInfo(binc3)" } elseif {$timeMode == "depth"} { - set parameter "depth $::uci::uciInfo(fixeddepth3)" + set parameter "depth $::sergame::uciInfo(fixeddepth3)" } elseif {$timeMode == "movetime"} { - set parameter "movetime $::uci::uciInfo(movetime3)" + set parameter "movetime $::sergame::uciInfo(movetime3)" } elseif {$timeMode == "nodes"} { - set parameter "nodes $::uci::uciInfo(fixednodes3)" + set parameter "nodes $::sergame::uciInfo(fixednodes3)" } ::engine::send serEngine Go [list "position fen [sc_pos fen]" $parameter]; #[list $::annotate(typ) $::annotate($::annotate(typ))]] } - set ::uci::uciInfo(bestmove3) "" - vwait ::uci::uciInfo(bestmove3) + set ::sergame::uciInfo(bestmove3) "" + vwait ::sergame::uciInfo(bestmove3) # ------------------------------------------------------------- # if weak move detected, propose the user to tack back - if { $::sergame::coachIsWatching && $::uci::uciInfo(prevscore3) != "" } { + if { $::sergame::coachIsWatching && $::sergame::uciInfo(prevscore3) != "" } { set tBlunder "" - set delta [expr abs($::uci::uciInfo(score3) - $::uci::uciInfo(prevscore3))] + set delta [expr abs($::sergame::uciInfo(score3) - $::sergame::uciInfo(prevscore3))] if {$delta > $::informant("?!") } { set tBlunder "DubiousMovePlayedTakeBack" } if {$delta > $::informant("?") } { set tBlunder "WeakMovePlayedTakeBack" } if {$delta > $::informant("??") } { set tBlunder "BadMovePlayedTakeBack" } @@ -557,35 +557,35 @@ namespace eval sergame { } # ------------------------------------------------------------- - if { $::uci::uciInfo(bestmove3) == "abort" } { + if { $::sergame::uciInfo(bestmove3) == "abort" } { return } - sc_move addSan $::uci::uciInfo(bestmove3) - ::utils::sound::AnnounceNewMove $::uci::uciInfo(bestmove3) - set ::uci::uciInfo(prevscore3) $::uci::uciInfo(score3) + sc_move addSan $::sergame::uciInfo(bestmove3) + ::utils::sound::AnnounceNewMove $::sergame::uciInfo(bestmove3) + set ::sergame::uciInfo(prevscore3) $::sergame::uciInfo(score3) if { $::sergame::storeEval == 1 } { - storeEvalComment $::uci::uciInfo(score3) + storeEvalComment $::sergame::uciInfo(score3) } updateBoard -pgn -animate repetition clocks toggle - # ponder mode (the engine just played its move) ;&& $::uci::uciInfo(ponder3) != "" + # ponder mode (the engine just played its move) ;&& $::sergame::uciInfo(ponder3) != "" if {$::sergame::ponder } { if {$timeMode == "timebonus"} { set wtime [expr [::gameclock::getSec 1] * 1000 ] set btime [expr [::gameclock::getSec 2] * 1000 ] - set parameter "ponder wtime $wtime btime $btime winc $::uci::uciInfo(winc3) binc $::uci::uciInfo(binc3)" + set parameter "ponder wtime $wtime btime $btime winc $::sergame::uciInfo(winc3) binc $::sergame::uciInfo(binc3)" } elseif {$timeMode == "depth"} { - set parameter "ponder depth $::uci::uciInfo(fixeddepth3)" + set parameter "ponder depth $::sergame::uciInfo(fixeddepth3)" } elseif {$timeMode == "movetime"} { - set parameter "ponder movetime $::uci::uciInfo(movetime3)" + set parameter "ponder movetime $::sergame::uciInfo(movetime3)" } elseif {$timeMode == "nodes"} { - set parameter "ponder nodes $::uci::uciInfo(fixednodes3)" + set parameter "ponder nodes $::sergame::uciInfo(fixednodes3)" } - ::engine::send serEngine Go [list "position fen [sc_pos fen] moves $::uci::uciInfo(ponder3)" $parameter] + ::engine::send serEngine Go [list "position fen [sc_pos fen] moves $::sergame::uciInfo(ponder3)" $parameter] } after 1000 ::sergame::engineGo @@ -612,7 +612,7 @@ namespace eval sergame { # ################################################################################ proc logEngine {n text} { - if {$::uci::uciInfo(log_stdout3)} { + if {$::sergame::uciInfo(log_stdout3)} { puts stdout "$n $text" } } From 32a0e7e9bf5fadc08ab6badf5f9a5a8a2374b6d4 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 16 Feb 2025 11:45:25 +0100 Subject: [PATCH 34/57] adjust score for comment and coach --- tcl/tools/sergame.tcl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 2a442492f..9a3d14fe2 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -539,7 +539,7 @@ namespace eval sergame { # if weak move detected, propose the user to tack back if { $::sergame::coachIsWatching && $::sergame::uciInfo(prevscore3) != "" } { set tBlunder "" - set delta [expr abs($::sergame::uciInfo(score3) - $::sergame::uciInfo(prevscore3))] + set delta [expr $::sergame::uciInfo(score3) - $::sergame::uciInfo(prevscore3)] if {$delta > $::informant("?!") } { set tBlunder "DubiousMovePlayedTakeBack" } if {$delta > $::informant("?") } { set tBlunder "WeakMovePlayedTakeBack" } if {$delta > $::informant("??") } { set tBlunder "BadMovePlayedTakeBack" } @@ -565,7 +565,9 @@ namespace eval sergame { ::utils::sound::AnnounceNewMove $::sergame::uciInfo(bestmove3) set ::sergame::uciInfo(prevscore3) $::sergame::uciInfo(score3) if { $::sergame::storeEval == 1 } { - storeEvalComment $::sergame::uciInfo(score3) + set score $::sergame::uciInfo(score3) + if { $::sergame::engineColor eq "black" } { set score [expr 0.0 - $score] } + storeEvalComment $score } updateBoard -pgn -animate repetition From 26bc1602fe14049141056f07f3a1b18ca632df9e Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 16 Feb 2025 11:56:47 +0100 Subject: [PATCH 35/57] initialize PV2 for non multipv engines --- tcl/tools/annotate.tcl | 1 + 1 file changed, 1 insertion(+) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 3388871cf..9376af31f 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -299,6 +299,7 @@ namespace eval ::annotation { # Annotate all remaining moves of the game while { 1 } { set ::annotate(PV1) [list "" "" ""] + set ::annotate(PV2) [list "" "" ""] ::engine::send annotateEngine Go [list [sc_game UCI_currentPos] [list $::annotate(typ) $::annotate($::annotate(typ))]] vwait ::annotate(move_done) addAnnotation From fca154058a0bb20826a7cfd494e087f5e0dddbf6 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 16 Feb 2025 15:34:29 +0100 Subject: [PATCH 36/57] rename uciInfo to data --- tcl/options.tcl | 12 ++-- tcl/tools/sergame.tcl | 127 +++++++++++++++++++++--------------------- 2 files changed, 70 insertions(+), 69 deletions(-) diff --git a/tcl/options.tcl b/tcl/options.tcl index b3c9cd8c3..d40c57d11 100644 --- a/tcl/options.tcl +++ b/tcl/options.tcl @@ -337,13 +337,13 @@ set ::sergame::startFromCurrent 0 set ::sergame::coachIsWatching 0 set ::sergame::timeMode "timebonus" set ::sergame::depth 3 -set ::sergame::movetime 0 +set ::sergame::movetime 1000 set ::sergame::nodes 10000 set ::sergame::ponder 0 -set ::sergame::uciInfo(wtime3) [expr 5 * 60 * 1000 ] -set ::sergame::uciInfo(winc3) [expr 10 * 1000 ] -set ::sergame::uciInfo(btime3) [expr 5 * 60 * 1000 ] -set ::sergame::uciInfo(binc3) [expr 10 * 1000 ] +set ::sergame::data(wtime) [expr 5 * 60 * 1000 ] +set ::sergame::data(winc) [expr 10 * 1000 ] +set ::sergame::data(btime) [expr 5 * 60 * 1000 ] +set ::sergame::data(binc) [expr 10 * 1000 ] # Defaults for initial directories: set initialDir(base) "." @@ -658,7 +658,7 @@ proc options.write {} { ::sergame::chosenOpening ::sergame::chosenEngine ::sergame::useBook ::sergame::bookToUse \ ::sergame::startFromCurrent ::sergame::coachIsWatching ::sergame::timeMode \ ::sergame::depth ::sergame::movetime ::sergame::nodes ::sergame::ponder ::sergame::isOpening \ - ::uci::uciInfo(wtime3) ::uci::uciInfo(winc3) ::uci::uciInfo(btime3) ::uci::uciInfo(binc3) \ + ::sergame::data(wtime) ::sergame::data(winc) ::sergame::data(btime) ::sergame::data(binc) \ boardfile_lite boardfile_dark \ FilterMaxMoves FilterMinMoves FilterStepMoves FilterMaxElo FilterMinElo FilterStepElo \ FilterMaxYear FilterMinYear FilterStepYear FilterGuessELO lookTheme ThemePackageFile autoResizeBoard \ diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 9a3d14fe2..91f6be1e0 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -9,14 +9,13 @@ namespace eval sergame { # DEBUG - set ::sergame::uciInfo(log_stdout3) 0 + set ::sergame::data(log_stdout) 0 # if true, follow a specific opening set openingMovesList {} set openingMovesHash {} set openingMoves "" set outOfOpening 0 - array set engineListBox {} set engineName "" set bookSlot 2 set storeEval 0 @@ -55,8 +54,8 @@ namespace eval sergame { grid $w.fbuttons -row 2 -column 1 -sticky we # builds the list of UCI engines - ::engineNoWin::createEngineOptionsFrame $w serEngine ::sergame::engineName 5 ::sergame::eng_messages - pack $w.serEngine -in $w.fengines -side top -pady 5 -anchor w -padx 4 + ::engineNoWin::createEngineOptionsFrame $w seriousEngine ::sergame::engineName 5 ::sergame::eng_messages + pack $w.seriousEngine -in $w.fengines -side top -pady 5 -anchor w -padx 4 # load book names ttk::checkbutton $w.fconfig.cbUseBook -text $::tr(UseBook) -variable ::sergame::useBook @@ -112,10 +111,10 @@ namespace eval sergame { ttk::label $w.ftime.timebonus.blacklseconds -text $::tr(TimeSec) grid $w.ftime.timebonus.blacklseconds -row $row -column 5 - $w.ftime.timebonus.whitespminutes set [expr $::sergame::uciInfo(wtime3) / (60 * 1000)] - $w.ftime.timebonus.whitespseconds set [expr $::sergame::uciInfo(winc3) / 1000] - $w.ftime.timebonus.blackspminutes set [expr $::sergame::uciInfo(btime3) / (60 * 1000)] - $w.ftime.timebonus.blackspseconds set [expr $::sergame::uciInfo(binc3) / 1000 ] + $w.ftime.timebonus.whitespminutes set [expr $::sergame::data(wtime) / (60 * 1000)] + $w.ftime.timebonus.whitespseconds set [expr $::sergame::data(winc) / 1000] + $w.ftime.timebonus.blackspminutes set [expr $::sergame::data(btime) / (60 * 1000)] + $w.ftime.timebonus.blackspseconds set [expr $::sergame::data(binc) / 1000 ] # Fixed depth ttk::frame $w.ftime.depth @@ -194,18 +193,18 @@ namespace eval sergame { set ::sergame::useBook 0 } } - set ::sergame::uciInfo(wtime3) [expr [.configSerGameWin.ftime.timebonus.whitespminutes get]*1000*60] - set ::sergame::uciInfo(btime3) [expr [.configSerGameWin.ftime.timebonus.blackspminutes get]*1000*60] - set ::sergame::uciInfo(winc3) [expr [.configSerGameWin.ftime.timebonus.whitespseconds get]*1000] - set ::sergame::uciInfo(binc3) [expr [.configSerGameWin.ftime.timebonus.blackspseconds get]*1000] - set ::sergame::uciInfo(fixeddepth3) [.configSerGameWin.ftime.depth.value get] - set ::sergame::uciInfo(fixednodes3) [expr [.configSerGameWin.ftime.nodes.value get]*1000] - set ::sergame::uciInfo(movetime3) [expr [.configSerGameWin.ftime.movetime.value get]*1000] + set ::sergame::data(wtime) [expr [.configSerGameWin.ftime.timebonus.whitespminutes get]*1000*60] + set ::sergame::data(btime) [expr [.configSerGameWin.ftime.timebonus.blackspminutes get]*1000*60] + set ::sergame::data(winc) [expr [.configSerGameWin.ftime.timebonus.whitespseconds get]*1000] + set ::sergame::data(binc) [expr [.configSerGameWin.ftime.timebonus.blackspseconds get]*1000] + set ::sergame::data(fixeddepth) [.configSerGameWin.ftime.depth.value get] + set ::sergame::data(fixednodes) [expr [.configSerGameWin.ftime.nodes.value get]*1000] + set ::sergame::data(movetime) [expr [.configSerGameWin.ftime.movetime.value get]*1000] - set callback [list ::sergame::eng_messages serEngine nop] - if { [::engineNoWin::initEngine serEngine $::sergame::engineName $callback] } { + set callback [list ::sergame::eng_messages seriousEngine nop] + if { [::engineNoWin::initEngine seriousEngine $::sergame::engineName $callback] } { destroy .configSerGameWin - ::sergame::play serEngine + ::sergame::play seriousEngine } } ttk::button $w.fbuttons.cancel -textvar ::tr(Cancel) -command "focus .; destroy $w" @@ -231,9 +230,9 @@ namespace eval sergame { } set ::sergame::lFen {} - set ::sergame::uciInfo(prevscore3) 0.0 - set ::sergame::uciInfo(score3) 0.0 - set ::sergame::uciInfo(ponder3) "" + set ::sergame::data(prevscore) 0.0 + set ::sergame::data(score) 0.0 + set ::sergame::data(ponder) "" if {$::sergame::startFromCurrent} { set isOpening 0 @@ -307,11 +306,11 @@ namespace eval sergame { "InfoPV" { lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv if { $multipv == 1 } { - set ::sergame::uciInfo(score3) [expr $score / 100.0] + set ::sergame::data(score) [expr $score / 100.0] } } "InfoBestMove" { - lassign $msgData ::sergame::uciInfo(bestmove3) ponder ::sergame::uciInfo(ponder3) + lassign $msgData ::sergame::data(bestmove) ponder ::sergame::data(ponder) } "InfoGo" { lassign $msgData ::annotate(position) @@ -340,10 +339,10 @@ namespace eval sergame { after cancel ::sergame::engineGo clocks stop set ::sergame::lFen {} - ::engine::send serEngine StopGo - ::engine::close serEngine - unset ::enginewin::engConfig_serEngine - set ::sergame::uciInfo(bestmove3) "abort" + ::engine::send seriousEngine StopGo + ::engine::close seriousEngine + unset ::enginewin::engConfig_seriousEngine + set ::sergame::data(bestmove) "abort" ::notify::GameChanged } @@ -354,8 +353,8 @@ namespace eval sergame { init { ::gameclock::new "" 1 ::gameclock::new "" 2 - ::gameclock::setSec 1 [expr 0 - $::sergame::uciInfo(wtime3)/1000] - ::gameclock::setSec 2 [expr 0 - $::sergame::uciInfo(btime3)/1000] + ::gameclock::setSec 1 [expr 0 - $::sergame::data(wtime)/1000] + ::gameclock::setSec 2 [expr 0 - $::sergame::data(btime)/1000] } start { if { [sc_pos side] == "white" } { @@ -370,11 +369,11 @@ namespace eval sergame { } toggle { if {[::gameclock::stop 1]} { - ::gameclock::add 1 [expr $::sergame::uciInfo(winc3)/1000] + ::gameclock::add 1 [expr $::sergame::data(winc)/1000] ::gameclock::storeTimeComment 1 ::gameclock::start 2 } elseif {[::gameclock::stop 2]} { - ::gameclock::add 2 [expr $::sergame::uciInfo(binc3)/1000] + ::gameclock::add 2 [expr $::sergame::data(binc)/1000] ::gameclock::storeTimeComment 2 ::gameclock::start 1 } @@ -432,7 +431,7 @@ namespace eval sergame { set takebackClockB [::gameclock::getSec 2] clocks toggle } - repetition + if { [repetition] } { [return } } # make a move corresponding to a specific opening, (it is engine's turn) @@ -485,8 +484,9 @@ namespace eval sergame { clocks toggle updateBoard -pgn -animate - repetition - after 1000 ::sergame::engineGo + if { ! [repetition] } { + after 1000 ::sergame::engineGo + } return } } @@ -501,45 +501,46 @@ namespace eval sergame { sc_move addSan $move ::utils::sound::AnnounceNewMove $move # we made a book move so assume a score = 0 - set ::sergame::uciInfo(prevscore3) 0.0 + set ::sergame::data(prevscore) 0.0 clocks toggle updateBoard -pgn -animate - repetition - after 1000 ::sergame::engineGo + if { ! [repetition] } { + after 1000 ::sergame::engineGo + } return } } # ------------------------------------------------------------- # check if the engine pondered on the right move - if { $::sergame::ponder && $::sergame::uciInfo(ponder3) == [sc_game info previousMoveUCI]} { - ::engine::rawsend serEngine "ponderhit" + if { $::sergame::ponder && $::sergame::data(ponder) == [sc_game info previousMoveUCI]} { + ::engine::rawsend seriousEngine "ponderhit" } else { if { $::sergame::ponder } { - ::engine::send serEngine StopGo + ::engine::send seriousEngine StopGo } if {$timeMode == "timebonus"} { set wtime [expr [::gameclock::getSec 1] * 1000 ] set btime [expr [::gameclock::getSec 2] * 1000 ] - set parameter "wtime $wtime btime $btime winc $::sergame::uciInfo(winc3) binc $::sergame::uciInfo(binc3)" + set parameter "wtime $wtime btime $btime winc $::sergame::data(winc) binc $::sergame::data(binc)" } elseif {$timeMode == "depth"} { - set parameter "depth $::sergame::uciInfo(fixeddepth3)" + set parameter "depth $::sergame::data(fixeddepth)" } elseif {$timeMode == "movetime"} { - set parameter "movetime $::sergame::uciInfo(movetime3)" + set parameter "movetime $::sergame::data(movetime)" } elseif {$timeMode == "nodes"} { - set parameter "nodes $::sergame::uciInfo(fixednodes3)" + set parameter "nodes $::sergame::data(fixednodes)" } - ::engine::send serEngine Go [list "position fen [sc_pos fen]" $parameter]; #[list $::annotate(typ) $::annotate($::annotate(typ))]] + ::engine::send seriousEngine Go [list "position fen [sc_pos fen]" $parameter]; #[list $::annotate(typ) $::annotate($::annotate(typ))]] } - set ::sergame::uciInfo(bestmove3) "" - vwait ::sergame::uciInfo(bestmove3) + set ::sergame::data(bestmove) "" + vwait ::sergame::data(bestmove) # ------------------------------------------------------------- # if weak move detected, propose the user to tack back - if { $::sergame::coachIsWatching && $::sergame::uciInfo(prevscore3) != "" } { + if { $::sergame::coachIsWatching && $::sergame::data(prevscore) != "" } { set tBlunder "" - set delta [expr $::sergame::uciInfo(score3) - $::sergame::uciInfo(prevscore3)] + set delta [expr $::sergame::data(score) - $::sergame::data(prevscore)] if {$delta > $::informant("?!") } { set tBlunder "DubiousMovePlayedTakeBack" } if {$delta > $::informant("?") } { set tBlunder "WeakMovePlayedTakeBack" } if {$delta > $::informant("??") } { set tBlunder "BadMovePlayedTakeBack" } @@ -557,37 +558,37 @@ namespace eval sergame { } # ------------------------------------------------------------- - if { $::sergame::uciInfo(bestmove3) == "abort" } { + if { $::sergame::data(bestmove) == "abort" } { return } - sc_move addSan $::sergame::uciInfo(bestmove3) - ::utils::sound::AnnounceNewMove $::sergame::uciInfo(bestmove3) - set ::sergame::uciInfo(prevscore3) $::sergame::uciInfo(score3) + sc_move addSan $::sergame::data(bestmove) + ::utils::sound::AnnounceNewMove $::sergame::data(bestmove) + set ::sergame::data(prevscore) $::sergame::data(score) if { $::sergame::storeEval == 1 } { - set score $::sergame::uciInfo(score3) + set score $::sergame::data(score) if { $::sergame::engineColor eq "black" } { set score [expr 0.0 - $score] } storeEvalComment $score } updateBoard -pgn -animate - repetition + if { [repetition] } { return } clocks toggle - # ponder mode (the engine just played its move) ;&& $::sergame::uciInfo(ponder3) != "" + # ponder mode (the engine just played its move) ;&& $::sergame::data(ponder) != "" if {$::sergame::ponder } { if {$timeMode == "timebonus"} { set wtime [expr [::gameclock::getSec 1] * 1000 ] set btime [expr [::gameclock::getSec 2] * 1000 ] - set parameter "ponder wtime $wtime btime $btime winc $::sergame::uciInfo(winc3) binc $::sergame::uciInfo(binc3)" + set parameter "ponder wtime $wtime btime $btime winc $::sergame::data(winc) binc $::sergame::data(binc)" } elseif {$timeMode == "depth"} { - set parameter "ponder depth $::sergame::uciInfo(fixeddepth3)" + set parameter "ponder depth $::sergame::data(fixeddepth)" } elseif {$timeMode == "movetime"} { - set parameter "ponder movetime $::sergame::uciInfo(movetime3)" + set parameter "ponder movetime $::sergame::data(movetime)" } elseif {$timeMode == "nodes"} { - set parameter "ponder nodes $::sergame::uciInfo(fixednodes3)" + set parameter "ponder nodes $::sergame::data(fixednodes)" } - ::engine::send serEngine Go [list "position fen [sc_pos fen] moves $::sergame::uciInfo(ponder3)" $parameter] + ::engine::send seriousEngine Go [list "position fen [sc_pos fen] moves $::sergame::data(ponder)" $parameter] } after 1000 ::sergame::engineGo @@ -605,7 +606,7 @@ namespace eval sergame { if { [llength [lsearch -all $::sergame::lFen $elt] ] >=3 } { tk_messageBox -type ok -message $::tr(Draw) -parent .main -icon info - puts $::sergame::lFen + ::sergame::abortGame return 1 } return 0 @@ -614,7 +615,7 @@ namespace eval sergame { # ################################################################################ proc logEngine {n text} { - if {$::sergame::uciInfo(log_stdout3)} { + if {$::sergame::data(log_stdout)} { puts stdout "$n $text" } } From 018221752b8ff51cc6890854e518a6dd9cc10107 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 16 Feb 2025 16:26:14 +0100 Subject: [PATCH 37/57] use same check for 3-fold-repetition in finshgame and sergame --- tcl/tools/finishgame.tcl | 46 ++++++++++++++++++++-------------------- tcl/tools/sergame.tcl | 9 ++------ 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/tcl/tools/finishgame.tcl b/tcl/tools/finishgame.tcl index bee636eb6..0b8ea1ee5 100644 --- a/tcl/tools/finishgame.tcl +++ b/tcl/tools/finishgame.tcl @@ -288,29 +288,29 @@ namespace eval ::finishgame { } } } +} - ################################################################################ - # add current position for 3fold repetition detection and returns 1 if - # the position is a repetition - ################################################################################ - proc checkRepetition { journal } { - set elt [lrange [split [sc_pos fen]] 0 2] - set isRep 0 - # append the position only if different from the last element - if { $elt != [ lindex $journal end ] } { lappend journal $elt } - # 3fold repetion detected - if { [llength [lsearch -all $journal $elt] ] >=3 } { set isRep 1 } - return [list $isRep $journal] - } +################################################################################ +# add current position for 3fold repetition detection and returns 1 if +# the position is a repetition +################################################################################ +proc checkRepetition { journal } { + set elt [lrange [split [sc_pos fen]] 0 2] + set isRep 0 + # append the position only if different from the last element + if { $elt != [ lindex $journal end ] } { lappend journal $elt } + # 3fold repetion detected + if { [llength [lsearch -all $journal $elt] ] >=3 } { set isRep 1 } + return [list $isRep $journal] +} - proc checkfiftyMoveRule { moves prevmaterial prevpawns } { - set isFiftyRule 0 - set elt [string range [sc_pos board] 0 63] - incr moves - set material [string length [string map {"." ""} $elt]] - set pawns [string map {"n" "." "b" "." "r" "." "q" "." "k" "." "N" "." "B" "." "R" "." "Q" "." "K" "." } $elt] - if { $pawns ne $prevpawns || $material ne $prevmaterial } { set moves 0 } - if { $moves >= 100 || $material == 2 } { set isFiftyRule 1 } - return [list $isFiftyRule $moves $material $pawns] - } +proc checkfiftyMoveRule { moves prevmaterial prevpawns } { + set isFiftyRule 0 + set elt [string range [sc_pos board] 0 63] + incr moves + set material [string length [string map {"." ""} $elt]] + set pawns [string map {"n" "." "b" "." "r" "." "q" "." "k" "." "N" "." "B" "." "R" "." "Q" "." "K" "." } $elt] + if { $pawns ne $prevpawns || $material ne $prevmaterial } { set moves 0 } + if { $moves >= 100 || $material == 2 } { set isFiftyRule 1 } + return [list $isFiftyRule $moves $material $pawns] } diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 91f6be1e0..1afcbd677 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -598,13 +598,8 @@ namespace eval sergame { # the position is a repetition ################################################################################ proc repetition {} { - set elt [lrange [split [sc_pos fen]] 0 2] - # append the position only if different from the last element - if { $elt != [ lindex $::sergame::lFen end ] } { - lappend ::sergame::lFen $elt - } - - if { [llength [lsearch -all $::sergame::lFen $elt] ] >=3 } { + lassign [checkRepetition $::sergame::lFen] isRepetition ::sergame::lFen + if { $isRepetition } { tk_messageBox -type ok -message $::tr(Draw) -parent .main -icon info ::sergame::abortGame return 1 From 3d55cc67adecc9f1e552a000f421f98e0788d7ef Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 16 Feb 2025 18:29:03 +0100 Subject: [PATCH 38/57] ::engineNoWin:: removed from finishgame.tcl exists in annotate.tcl --- tcl/tools/finishgame.tcl | 68 ---------------------------------------- 1 file changed, 68 deletions(-) diff --git a/tcl/tools/finishgame.tcl b/tcl/tools/finishgame.tcl index 0b8ea1ee5..ec6865f7b 100644 --- a/tcl/tools/finishgame.tcl +++ b/tcl/tools/finishgame.tcl @@ -6,74 +6,6 @@ ########################################################################################## ### finishGame Dialog: uses a chess engine to play a game -# engineNoWin will be used by annotate and finish game -#copied from annotate.tcl. to be removed later -namespace eval ::engineNoWin {} -# Open the engine and configure it -proc ::engineNoWin::initEngine { id engine callback {addOpts "MultiPV 2"}} { - if { [info exists ::enginewin::engConfig_$id] } { return 1 } - set config [::enginecfg::get $engine] - lassign $config name cmd args wdir elo time url uci options - if { ! $uci } { - tk_messageBox -title Scid -icon info -type ok -message "Only UCI-Engines are supported!" - return 0 - } - set ::enginewin::engConfig_$id [list $name $cmd $args $wdir $elo $time $url $uci {}] - ::engine::setLogCmd $id {} - ::engine::connect $id $callback $cmd {} - lappend options $addOpts - ::engine::send $id SetOptions $options - return 1 -} - -proc ::engineNoWin::changeEngine {id w enginevar callback} { - ::engine::close $id - $w.text configure -state normal - $w.text delete 1.0 end - foreach wchild [winfo children $w.text] { destroy $wchild } - catch { unset ::enginewin::engConfig_$id } - set engine [set $enginevar] - ::engineNoWin::initEngine $id $engine [list $callback $id $w] -} - -proc ::engineNoWin::showHideOptionsFrame {id w enginevar callback col} { - if { [winfo ismapped $w] } { grid forget $w ; return } - grid $w -row 0 -column $col -rowspan 2 -sticky ne -padx 10 - set engine [set $enginevar] - ::engineNoWin::initEngine $id $engine [list $callback $id $w] -} - -#create frame for edit engine options -proc ::engineNoWin::createEngineOptionsFrame {f id var col callback} { - ttk::frame $f.$id - set engList [::enginecfg::names ] - if { [set $var] eq "" } { set $var [lindex $engList 0] } - ttk::combobox $f.$id.eng -width 20 -state readonly -values $engList -textvariable $var - bind $f.$id.eng <> "::engineNoWin::changeEngine $id $f.opts$id $var $callback" - ttk::button $f.$id.opts -image ::icon::filter_adv -style Toolbutton \ - -command "::engineNoWin::showHideOptionsFrame $id $f.opts$id $var $callback $col" - pack $f.$id.eng $f.$id.opts -side left -padx { 0 5 } - ttk::labelframe $f.opts$id -text "Engine Parameter" - ttk::label $f.opts$id.l -textvariable $var - ttk::button $f.opts$id.x -text "X" -style Toolbutton -command "grid forget $f.opts$id" - ttk_text $f.opts$id.text -wrap none -padx 4 - autoscrollBars both $f.opts$id $f.opts$id.text 1 - $f.opts$id.text configure -state normal -wrap word -width 60 -height 18 - grid $f.opts$id.l -row 0 -column 0 -sticky w - grid $f.opts$id.x -row 0 -column 1 -sticky e -} - -proc ::engineNoWin::initEngineOptions {id w options} { - upvar ::enginewin::engConfig_$id engConfig_ - if { ! [winfo exists $w.text.reset] } { - lset ::enginewin::engConfig_$id 8 $options - ::enginecfg::createOptionWidgets $id $w $options - } else { - ::enginecfg::updateOptionWidgets $id $w $options {} - $w.text configure -state disabled - } -} - namespace eval ::finishgame { set ::finishGame(annotate) 1 From 0b0f59229b95b6612ceaf688df3d7f19b2d1c3cb Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 16 Feb 2025 18:47:54 +0100 Subject: [PATCH 39/57] stop game if engine terminated --- tcl/tools/sergame.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 1afcbd677..b2dc71a04 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -319,7 +319,7 @@ namespace eval sergame { lassign $msgData errorMsg if {$errorMsg eq ""} { set errorMsg "The connection with the engine terminated unexpectedly." } tk_messageBox -icon warning -type ok -parent . -message $errorMsg - set ::autoplayMode 0 + ::sergame::abortGame } } } From bfcd55195e03d438f997b7ee7477a5ca80ed9d32 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 6 Apr 2025 16:47:39 +0200 Subject: [PATCH 40/57] button to save changes of engine configuration in OptionFrame --- tcl/tools/annotate.tcl | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 9376af31f..7b1dbfef1 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -22,7 +22,7 @@ proc ::engineNoWin::initEngine { id engine callback {addOpts "MultiPV 2"}} { tk_messageBox -title Scid -icon info -type ok -message "Only UCI-Engines are supported!" return 0 } - set ::enginewin::engConfig_$id [list $name $cmd $args $wdir $elo $time $url $uci {}] + set ::enginewin::engConfig_$id [list $name $cmd $args $wdir $elo $time $url $uci {} {}] ::engine::setLogCmd $id {} ::engine::connect $id $callback $cmd {} lappend options $addOpts @@ -42,7 +42,7 @@ proc ::engineNoWin::changeEngine {id w enginevar callback} { proc ::engineNoWin::showHideOptionsFrame {id w enginevar callback col} { if { [winfo ismapped $w] } { grid forget $w ; return } - grid $w -row 0 -column $col -rowspan 2 -sticky ne -padx 10 + grid $w -row 0 -column $col -rowspan 5 -sticky ne -padx 10 set engine [set $enginevar] ::engineNoWin::initEngine $id $engine [list $callback $id $w] } @@ -63,8 +63,11 @@ proc ::engineNoWin::createEngineOptionsFrame {f id var col callback} { ttk_text $f.opts$id.text -wrap none -padx 4 autoscrollBars both $f.opts$id $f.opts$id.text 1 $f.opts$id.text configure -state normal -wrap word -width 60 -height 18 + ttk::button $f.opts$id.save -text "Save Setup" -command "::engineNoWin::saveEngineSetup $id" grid $f.opts$id.l -row 0 -column 0 -sticky w grid $f.opts$id.x -row 0 -column 1 -sticky e + grid $f.opts$id.save -row 2 -column 0 -columnspan 2 -sticky e -pady { 5 0 } + bind $f.$id "catch { unset ::enginewin::engConfig_$id }; ::engine::close $id" } proc ::engineNoWin::initEngineOptions {id w options} { @@ -73,11 +76,20 @@ proc ::engineNoWin::initEngineOptions {id w options} { lset ::enginewin::engConfig_$id 8 $options ::enginecfg::createOptionWidgets $id $w $options } else { + # changed options stored in #9, but do not save + lset ::enginewin::engConfig_$id 9 $options ::enginecfg::updateOptionWidgets $id $w $options {} $w.text configure -state disabled } } +proc ::engineNoWin::saveEngineSetup { id } { + upvar ::enginewin::engConfig_$id engConfig_ + # copy #9 to #8 to save the options + lset ::enginewin::engConfig_$id 8 [lindex [set ::enginewin::engConfig_$id] 9] + ::enginecfg::save [set ::enginewin::engConfig_$id] +} + namespace eval ::annotation { # Typ may be "movetime": time per move or "depth": analyse till depth is reached From aedbeedc21eb7a35de97be52d6edda41bdf25870 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 6 Apr 2025 17:15:20 +0200 Subject: [PATCH 41/57] use ::engineNoWin::disconnected in finishgame --- tcl/tools/finishgame.tcl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tcl/tools/finishgame.tcl b/tcl/tools/finishgame.tcl index ec6865f7b..b392b3050 100644 --- a/tcl/tools/finishgame.tcl +++ b/tcl/tools/finishgame.tcl @@ -185,8 +185,8 @@ namespace eval ::finishgame { sc_pos setComment "$tmp\n\n$::tr(FinishGame) $::tr(White): $::finishGame(enginewhite) $::finishGame(cmdwhite) $::finishGame(cmdValuewhite)\n\n$::tr(Black): $::finishGame(engineblack) $::finishGame(cmdblack) $::finishGame(cmdValueblack)" ::engine::close fgEnginewhite ::engine::close fgEngineblack - unset ::enginewin::engConfig_fgEnginewhite - unset ::enginewin::engConfig_fgEngineblack + catch { unset ::enginewin::engConfig_fgEnginewhite } + catch { unset ::enginewin::engConfig_fgEngineblack } ::notify::PosChanged -pgn destroy .configFinishGame } @@ -213,9 +213,7 @@ namespace eval ::finishgame { lassign $msgData ::annotate(position) } "InfoDisconnected" { - lassign $msgData errorMsg - if {$errorMsg eq ""} { set errorMsg "The connection with the engine terminated unexpectedly." } - tk_messageBox -icon warning -type ok -parent . -message $errorMsg + ::engineNoWin::disconnected $id $msgData set ::autoplayMode 0 } } From 6e39c8ab027bc7a4148f86b0414310423e71fb33 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 6 Apr 2025 17:16:55 +0200 Subject: [PATCH 42/57] new proc ::engineNoWin::disconnected --- tcl/tools/annotate.tcl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 7b1dbfef1..1815480d6 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -90,6 +90,14 @@ proc ::engineNoWin::saveEngineSetup { id } { ::enginecfg::save [set ::enginewin::engConfig_$id] } +proc ::engineNoWin::disconnected { id data } { + upvar ::enginewin::engConfig_$id engConfig_ + lassign $data errorMsg + lassign [set ::enginewin::engConfig_$id] engine + if {$errorMsg eq ""} { set errorMsg "The connection with the engine $id $engine terminated unexpectedly." } + tk_messageBox -icon warning -type ok -parent . -message $errorMsg +} + namespace eval ::annotation { # Typ may be "movetime": time per move or "depth": analyse till depth is reached @@ -682,9 +690,7 @@ namespace eval ::annotation { lassign $msgData ::annotate(position) } "InfoDisconnected" { - lassign $msgData errorMsg - if {$errorMsg eq ""} { set errorMsg "The connection with the engine terminated unexpectedly." } - tk_messageBox -icon warning -type ok -parent . -message $errorMsg + ::engineNoWin::disconnected $id $msgData set ::autoplayMode 0 } } From 1c4953ab8d9b1a5fddaddadbf231bf434046e734 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 6 Apr 2025 18:45:16 +0200 Subject: [PATCH 43/57] remove default value from initEngine --- tcl/tools/annotate.tcl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 1815480d6..66a23425b 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -14,7 +14,7 @@ # engineNoWin will be used by annotate and finish game namespace eval ::engineNoWin {} # Open the engine and configure it -proc ::engineNoWin::initEngine { id engine callback {addOpts "MultiPV 2"}} { +proc ::engineNoWin::initEngine { id engine callback } { if { [info exists ::enginewin::engConfig_$id] } { return 1 } set config [::enginecfg::get $engine] lassign $config name cmd args wdir elo time url uci options @@ -25,7 +25,6 @@ proc ::engineNoWin::initEngine { id engine callback {addOpts "MultiPV 2"}} { set ::enginewin::engConfig_$id [list $name $cmd $args $wdir $elo $time $url $uci {} {}] ::engine::setLogCmd $id {} ::engine::connect $id $callback $cmd {} - lappend options $addOpts ::engine::send $id SetOptions $options return 1 } @@ -332,6 +331,8 @@ namespace eval ::annotation { } proc runAnnotation { } { + # make sure, we have 2 best lines + ::engine::send annotateEngine SetOptions [list {MultiPV 2}] set f .annotationDialog.f grid forget $f.annotate $f.comment $f.av $f.batch $f.optsannotateEngine pack forget $f.buttons.ok @@ -343,7 +344,7 @@ namespace eval ::annotation { grid $f.running -row 2 -column 0 -columnspan 2 -sticky we # tactical positions is selected, must be in multipv mode - if {$::annotate(tacticalExercises)} { ::engine::send annotateEngine SetOptions "MultiPV 4" } + if {$::annotate(tacticalExercises)} { ::engine::send annotateEngine SetOptions [list {MultiPV 4}] } set ::autoplayMode 1 set gameNo [sc_game number] From f16a3bdfa4864e7698d6fe015bdb891fce996096 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 6 May 2025 18:06:38 +0200 Subject: [PATCH 44/57] move annotate vars to namespace annotation and rename to annotateData --- tcl/tools/annotate.tcl | 304 +++++++++++++++++++++-------------------- 1 file changed, 156 insertions(+), 148 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 66a23425b..806c5f06b 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -100,42 +100,43 @@ proc ::engineNoWin::disconnected { id data } { namespace eval ::annotation { # Typ may be "movetime": time per move or "depth": analyse till depth is reached - set ::annotate(typ) "movetime" - set ::annotate(movetime) 1000 - set ::annotate(time) 1 - set ::annotate(depth) 20 - set ::annotate(engine) "" - set ::annotate(progress) 0 - set ::annotate(blunderThreshold) 0.5 - set ::annotate(annotateMoves) all - set ::annotate(annotateBlunders) blundersonly - set ::annotate(scoreAllMoves) 1 - set ::annotate(annotateMode) 0 - set ::annotate(useAnalysisBook) 0 - set ::annotate(AnalysisBookName) "" - set ::annotate(BookSlot) 1 - set ::annotate(tacticalExercises) 0 - set ::annotate(addAnnotatorTag) 1 - set ::annotate(OpeningErrors) 0 - set ::annotate(OpeningMoves) 0 - set ::annotate(annotateShort) 1 - set ::annotate(addScoreToShortAnnotations) 1 - set ::annotate(batchMode) 0 - set ::annotate(batchEnd) 0 - set ::annotate(msg1) "" - set ::annotate(msg2) "" - set ::annotate(msg3) "" - set ::annotate(prevscore1) 0 - set ::annotate(prevscore2) 0 - set ::annotate(prevmoves1) "" - set ::annotate(prevmoves2) "" - set ::annotate(score) 0 - set ::annotate(moves) "" - set ::annotate(scoremate) 0 - set ::annotate(prevscoremate) 0 - set ::annotate(anzVariation) 1 + set annotateData(typ) "movetime" + set annotateData(movetime) 1000 + set annotateData(time) 1 + set annotateData(depth) 20 + set annotateData(engine) "" + set annotateData(progress) 0 + set annotateData(blunderThreshold) 0.5 + set annotateData(annotateMoves) all + set annotateData(annotateBlunders) blundersonly + set annotateData(scoreAllMoves) 1 + set annotateData(annotateMode) 0 + set annotateData(useAnalysisBook) 0 + set annotateData(AnalysisBookName) "" + set annotateData(BookSlot) 1 + set annotateData(tacticalExercises) 0 + set annotateData(addAnnotatorTag) 1 + set annotateData(OpeningErrors) 0 + set annotateData(OpeningMoves) 0 + set annotateData(annotateShort) 1 + set annotateData(addScoreToShortAnnotations) 1 + set annotateData(batchMode) 0 + set annotateData(batchEnd) 0 + set annotateData(msg1) "" + set annotateData(msg2) "" + set annotateData(msg3) "" + set annotateData(prevscore1) 0 + set annotateData(prevscore2) 0 + set annotateData(prevmoves1) "" + set annotateData(prevmoves2) "" + set annotateData(score) 0 + set annotateData(moves) "" + set annotateData(scoremate) 0 + set annotateData(prevscoremate) 0 + set annotateData(anzVariation) 1 proc doAnnotate {} { + global ::annotation::annotateData set w .annotationDialog # Do not do anything if the window exists if { [winfo exists $w] } { @@ -145,8 +146,8 @@ namespace eval ::annotation { } #Workaround for error in trace var for arrays - set ::annotateBlunderThreshold $::annotate(blunderThreshold) - set ::annotateTime $::annotate(time) + set ::annotateBlunderThreshold $annotateData(blunderThreshold) + set ::annotateTime $annotateData(time) trace variable ::annotateBlunderThreshold w {::utils::validate::Regexp {^[0-9]*\.?[0-9]*$}} trace variable ::annotateTime w {::utils::validate::Regexp {^[0-9]*\.?[0-9]*$}} @@ -159,18 +160,18 @@ namespace eval ::annotation { ttk::labelframe $f.annotate -text $::tr(GameReview) ttk::frame $f.annotate.typ - ttk::radiobutton $f.annotate.typ.label -text $::tr(AnnotateTime) -variable ::annotate(typ) -value "movetime" - ttk::radiobutton $f.annotate.typ.ldepth -text "Depth per move" -variable ::annotate(typ) -value "depth" + ttk::radiobutton $f.annotate.typ.label -text $::tr(AnnotateTime) -variable ::annotation::annotateData(typ) -value "movetime" + ttk::radiobutton $f.annotate.typ.ldepth -text "Depth per move" -variable ::annotation::annotateData(typ) -value "depth" ttk::spinbox $f.annotate.typ.spDelay -width 5 -textvariable ::annotateTime -from 0.1 -to 999 -validate key -justify right - ttk::spinbox $f.annotate.typ.depth -width 5 -textvariable ::annotate(depth) -from 2 -to 999 -validate key -justify right - ttk::radiobutton $f.annotate.allmoves -text $::tr(AnnotateAllMoves) -variable ::annotate(annotateBlunders) -value allmoves - ttk::radiobutton $f.annotate.blundersonly -text $::tr(AnnotateBlundersOnly) -variable ::annotate(annotateBlunders) -value blundersonly + ttk::spinbox $f.annotate.typ.depth -width 5 -textvariable ::annotation::annotateData(depth) -from 2 -to 999 -validate key -justify right + ttk::radiobutton $f.annotate.allmoves -text $::tr(AnnotateAllMoves) -variable ::annotation::annotateData(annotateBlunders) -value allmoves + ttk::radiobutton $f.annotate.blundersonly -text $::tr(AnnotateBlundersOnly) -variable ::annotation::annotateData(annotateBlunders) -value blundersonly ttk::frame $f.annotate.blunderbox ttk::label $f.annotate.blunderbox.label -text $::tr(BlundersThreshold:) ttk::spinbox $f.annotate.blunderbox.spBlunder -width 4 -textvariable ::annotateBlunderThreshold \ -from 0.1 -to 3.0 -increment 0.1 -justify right - ttk::checkbutton $f.annotate.cbBook -text $::tr(UseBook) -variable ::annotate(useAnalysisBook) - ::engineNoWin::createEngineOptionsFrame $f annotateEngine ::annotate(engine) 3 ::annotation::eng_messages + ttk::checkbutton $f.annotate.cbBook -text $::tr(UseBook) -variable ::annotation::annotateData(useAnalysisBook) + ::engineNoWin::createEngineOptionsFrame $f annotateEngine ::annotation::annotateData(engine) 3 ::annotation::eng_messages # choose a book for analysis # load book names @@ -178,7 +179,7 @@ namespace eval ::annotation { set bookList [ lsort -dictionary [ glob -nocomplain -directory $bookPath *.bin ] ] # No book found if { [llength $bookList] == 0 } { - set ::annotate(useAnalysisBook) 0 + set annotateData(useAnalysisBook) 0 $f.annotate.cbBook configure -state disabled } set tmp {} @@ -191,8 +192,8 @@ namespace eval ::annotation { } incr i } - if { $::annotate(AnalysisBookName) eq "" } { set ::annotate(AnalysisBookName) [lindex $tmp $idx] } - ttk::combobox $f.annotate.comboBooks -width 12 -values $tmp -textvariable ::annotate(AnalysisBookName) + if { $annotateData(AnalysisBookName) eq "" } { set annotateData(AnalysisBookName) [lindex $tmp $idx] } + ttk::combobox $f.annotate.comboBooks -width 12 -values $tmp -textvariable ::annotation::annotateData(AnalysisBookName) catch { $f.annotate.comboBooks current $idx } pack $f.annotate.blunderbox.label -side left -padx { 20 0 } pack $f.annotate.blunderbox.spBlunder -side left -anchor w @@ -209,30 +210,30 @@ namespace eval ::annotation { bind $w { .configAnnotation.f.buttons.ok invoke } ttk::labelframe $f.av -text $::tr(AnnotateWhich) - ttk::radiobutton $f.av.all -text $::tr(AnnotateAll) -variable ::annotate(annotateMoves) -value all - ttk::radiobutton $f.av.white -text $::tr(AnnotateWhite) -variable ::annotate(annotateMoves) -value white - ttk::radiobutton $f.av.black -text $::tr(AnnotateBlack) -variable ::annotate(annotateMoves) -value black - ttk::checkbutton $f.av.vars -text "Store two variations" -variable ::annotate(anzVariation) -onvalue 2 -offvalue 1 + ttk::radiobutton $f.av.all -text $::tr(AnnotateAll) -variable ::annotation::annotateData(annotateMoves) -value all + ttk::radiobutton $f.av.white -text $::tr(AnnotateWhite) -variable ::annotation::annotateData(annotateMoves) -value white + ttk::radiobutton $f.av.black -text $::tr(AnnotateBlack) -variable ::annotation::annotateData(annotateMoves) -value black + ttk::checkbutton $f.av.vars -text "Store two variations" -variable ::annotation::annotateData(anzVariation) -onvalue 2 -offvalue 1 pack $f.av.all $f.av.white $f.av.black $f.av.vars -side top -fill x -anchor w ttk::labelframe $f.comment -text $::tr(Comments) # Checkmark to enable all-move-scoring - ttk::checkbutton $f.comment.scoreAll -text $::tr(ScoreAllMoves) -variable ::annotate(scoreAllMoves) - ttk::checkbutton $f.comment.cbShortAnnotation -text $::tr(ShortAnnotations) -variable ::annotate(annotateShort) - ttk::checkbutton $f.comment.cbAddScore -text $::tr(AddScoreToShortAnnotations) -variable ::annotate(addScoreToShortAnnotations) - ttk::checkbutton $f.comment.cbAddAnnotatorTag -text $::tr(addAnnotatorTag) -variable ::annotate(addAnnotatorTag) - ttk::checkbutton $f.comment.cbMarkTactics -text $::tr(MarkTacticalExercises) -variable ::annotate(tacticalExercises) + ttk::checkbutton $f.comment.scoreAll -text $::tr(ScoreAllMoves) -variable ::annotation::annotateData(scoreAllMoves) + ttk::checkbutton $f.comment.cbShortAnnotation -text $::tr(ShortAnnotations) -variable ::annotation::annotateData(annotateShort) + ttk::checkbutton $f.comment.cbAddScore -text $::tr(AddScoreToShortAnnotations) -variable ::annotation::annotateData(addScoreToShortAnnotations) + ttk::checkbutton $f.comment.cbAddAnnotatorTag -text $::tr(addAnnotatorTag) -variable ::annotation::annotateData(addAnnotatorTag) + ttk::checkbutton $f.comment.cbMarkTactics -text $::tr(MarkTacticalExercises) -variable ::annotation::annotateData(tacticalExercises) pack $f.comment.scoreAll $f.comment.cbShortAnnotation $f.comment.cbAddScore \ $f.comment.cbAddAnnotatorTag $f.comment.cbMarkTactics -fill x -anchor w # batch annotation of consecutive games, and optional opening errors finder ttk::labelframe $f.batch -text "Batch Annotation" ttk::frame $f.buttons ttk::frame $f.running - ttk::label $f.running.line1 -textvariable ::annotate(msg1) -width 60 - ttk::label $f.running.line2 -textvariable ::annotate(msg2) -width 10 - ttk::label $f.running.line3 -textvariable ::annotate(msg3) -width 10 - ttk::progressbar $f.running.progress -variable ::annotate(progress) -orient horizontal -length 600 - ttk::progressbar $f.running.games -variable ::annotate(games) -orient horizontal -length 600 + ttk::label $f.running.line1 -textvariable ::annotation::annotateData(msg1) -width 60 + ttk::label $f.running.line2 -textvariable ::annotation::annotateData(msg2) -width 10 + ttk::label $f.running.line3 -textvariable ::annotation::annotateData(msg3) -width 10 + ttk::progressbar $f.running.progress -variable ::annotation::annotateData(progress) -orient horizontal -length 600 + ttk::progressbar $f.running.games -variable ::annotation::annotateData(games) -orient horizontal -length 600 grid $f.running.line1 -row 0 -column 1 -sticky w -pady { 0 10 } grid $f.running.line2 -row 1 -column 0 -sticky w grid $f.running.line3 -row 2 -column 0 -sticky w @@ -244,13 +245,13 @@ namespace eval ::annotation { grid $f.batch -row 1 -column 1 -pady { 10 0 } -sticky nswe -padx { 10 0 } grid $f.buttons -row 3 -column 1 -sticky we - set ::annotate(batchEnd) [sc_base numGames $::curr_db] - if {$::annotate(batchEnd) <1} { set ::annotate(batchEnd) 1 } - ttk::checkbutton $f.batch.cbBatch -text $::tr(AnnotateSeveralGames) -variable ::annotate(batchMode) - ttk::spinbox $f.batch.spBatchEnd -width 8 -textvariable ::annotate(batchEnd) \ - -from 1 -to $::annotate(batchEnd) -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } - ttk::checkbutton $f.batch.cbBatchOpening -text $::tr(FindOpeningErrors) -variable ::annotate(OpeningErrors) - ttk::spinbox $f.batch.spBatchOpening -width 2 -textvariable ::annotate(OpeningMoves) \ + set annotateData(batchEnd) [sc_base numGames $::curr_db] + if {$annotateData(batchEnd) <1} { set annotateData(batchEnd) 1 } + ttk::checkbutton $f.batch.cbBatch -text $::tr(AnnotateSeveralGames) -variable ::annotation::annotateData(batchMode) + ttk::spinbox $f.batch.spBatchEnd -width 8 -textvariable ::annotation::annotateData(batchEnd) \ + -from 1 -to $annotateData(batchEnd) -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } + ttk::checkbutton $f.batch.cbBatchOpening -text $::tr(FindOpeningErrors) -variable ::annotation::annotateData(OpeningErrors) + ttk::spinbox $f.batch.spBatchOpening -width 2 -textvariable ::annotation::annotateData(OpeningMoves) \ -from 10 -to 20 -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } ttk::label $f.batch.lBatchOpening -text $::tr(moves) pack $f.batch.cbBatch -side top -anchor w -pady { 0 0 } @@ -270,10 +271,10 @@ namespace eval ::annotation { } ttk::button $f.buttons.ok -text "Annotate" -command { if {$::annotateTime < 0.1} { set ::annotateTime 0.1 } - set ::annotate(movetime) [expr {int($::annotateTime * 1000.0)}] - set ::annotate(blunderThreshold) $::annotateBlunderThreshold - set ::annotate(time) $::annotateTime - if { [::engineNoWin::initEngine annotateEngine $::annotate(engine) \ + set annotateData(movetime) [expr {int($::annotateTime * 1000.0)}] + set annotateData(blunderThreshold) $::annotateBlunderThreshold + set annotateData(time) $::annotateTime + if { [::engineNoWin::initEngine annotateEngine $::annotation::annotateData(engine) \ [list ::annotation::eng_messages annotateEngine .annotationDialog.f.engpara]] } { ::annotation::runAnnotation } @@ -285,6 +286,7 @@ namespace eval ::annotation { # reset values for every game proc initGameAnnotation { } { + global ::annotation::annotateData #reset engine ::engine::send annotateEngine NewGame [list analysis post_pv post_wdl [sc_game variant]] # calc amount of moves to analyze for progressbar @@ -295,35 +297,36 @@ namespace eval ::annotation { sc_game pop .annotationDialog.f.running.progress configure -maximum $anz #reset values - set ::annotate(prevscore1) 0 - set ::annotate(prevscore2) 0 - set ::annotate(score) 0 - set ::annotate(scoremate) 0 - set ::annotate(prevscoremate) 0 - set ::annotate(prevmoves1) "" - set ::annotate(prevmoves2) "" - set ::annotate(moves) "" - set ::annotate(progress) 1 - set ::annotate(msg1) "$::tr(game) [sc_game number]: [sc_game info white] - [sc_game info black]" - set ::annotate(msg2) "$::tr(game) $::annotate(games)" - set ::annotate(msg3) "$::tr(move) 1" - if { $::annotate(addAnnotatorTag) } { - appendAnnotator "$::annotate(engine) $::annotate(typ) $::annotate($::annotate(typ))" + set annotateData(prevscore1) 0 + set annotateData(prevscore2) 0 + set annotateData(score) 0 + set annotateData(scoremate) 0 + set annotateData(prevscoremate) 0 + set annotateData(prevmoves1) "" + set annotateData(prevmoves2) "" + set annotateData(moves) "" + set annotateData(progress) 1 + set annotateData(msg1) "$::tr(game) [sc_game number]: [sc_game info white] - [sc_game info black]" + set annotateData(msg2) "$::tr(game) $annotateData(games)" + set annotateData(msg3) "$::tr(move) 1" + if { $annotateData(addAnnotatorTag) } { + appendAnnotator "$annotateData(engine) $annotateData(typ) $annotateData($annotateData(typ))" } } proc annotateGame { } { + global ::annotation::annotateData initGameAnnotation makeBookAnnotation # Annotate all remaining moves of the game while { 1 } { - set ::annotate(PV1) [list "" "" ""] - set ::annotate(PV2) [list "" "" ""] - ::engine::send annotateEngine Go [list [sc_game UCI_currentPos] [list $::annotate(typ) $::annotate($::annotate(typ))]] - vwait ::annotate(move_done) + set annotateData(PV1) [list "" "" ""] + set annotateData(PV2) [list "" "" ""] + ::engine::send annotateEngine Go [list [sc_game UCI_currentPos] [list $annotateData(typ) $annotateData($annotateData(typ))]] + vwait ::annotation::annotateData(move_done) addAnnotation - incr ::annotate(progress) - set ::annotate(msg3) "$::tr(move) $::annotate(progress)" + incr annotateData(progress) + set annotateData(msg3) "$::tr(move) $annotateData(progress)" if {[sc_pos isAt end] || ! $::autoplayMode } break sc_move forward ::notify::PosChanged -pgn @@ -331,30 +334,31 @@ namespace eval ::annotation { } proc runAnnotation { } { + global ::annotation::annotateData # make sure, we have 2 best lines ::engine::send annotateEngine SetOptions [list {MultiPV 2}] set f .annotationDialog.f grid forget $f.annotate $f.comment $f.av $f.batch $f.optsannotateEngine pack forget $f.buttons.ok - if {!$::annotate(batchMode)} { grid forget $f.running.games $f.running.line2 } + if {!$annotateData(batchMode)} { grid forget $f.running.games $f.running.line2 } # show progressbar and game infos - set ::annotate(games) 1 + set annotateData(games) 1 set gameNo [sc_game number] - $f.running.games configure -maximum [expr {$::annotate(batchEnd) - $gameNo + 1}] + $f.running.games configure -maximum [expr {$annotateData(batchEnd) - $gameNo + 1}] grid $f.running -row 2 -column 0 -columnspan 2 -sticky we # tactical positions is selected, must be in multipv mode - if {$::annotate(tacticalExercises)} { ::engine::send annotateEngine SetOptions [list {MultiPV 4}] } + if {$annotateData(tacticalExercises)} { ::engine::send annotateEngine SetOptions [list {MultiPV 4}] } set ::autoplayMode 1 set gameNo [sc_game number] if { $gameNo == 0 } { return } annotateGame - while {$::annotate(batchMode)} { + while {$annotateData(batchMode)} { sc_game save $gameNo incr gameNo - incr ::annotate(games) - if { ! $::autoplayMode || $gameNo > $::annotate(batchEnd) } { break } + incr annotateData(games) + if { ! $::autoplayMode || $gameNo > $annotateData(batchEnd) } { break } sc_game load $gameNo annotateGame } @@ -370,19 +374,20 @@ namespace eval ::annotation { # when going out of it ################################################################################ proc makeBookAnnotation { } { - if {$::annotate(useAnalysisBook)} { + global ::annotation::annotateData + if {$annotateData(useAnalysisBook)} { set prevbookmoves "" - set bn [ file join $::scidBooksDir $::annotate(AnalysisBookName) ] - sc_book load $bn $::annotate(BookSlot) + set bn [ file join $::scidBooksDir $annotateData(AnalysisBookName) ] + sc_book load $bn $annotateData(BookSlot) - lassign [sc_book moves $::annotate(BookSlot)] bookmoves + lassign [sc_book moves $annotateData(BookSlot)] bookmoves while {[string length $bookmoves] != 0 && ![sc_pos isAt vend]} { # we are in book, so move immediately forward ::move::Forward set prevbookmoves $bookmoves - lassign [sc_book moves $::annotate(BookSlot)] bookmoves + lassign [sc_book moves $annotateData(BookSlot)] bookmoves } - sc_book close $::annotate(BookSlot) + sc_book close $annotateData(BookSlot) if { [ string match -nocase "*[sc_game info previousMoveNT]*" $prevbookmoves ] != 1 } { if {$prevbookmoves != ""} { @@ -395,7 +400,7 @@ namespace eval ::annotation { } else { sc_pos setComment "[sc_pos getComment] $::tr(MoveOutOfBook)" } - if { $::annotate(OpeningErrors) && ([sc_pos moveNumber] < $::annotate(OpeningMoves) ) } { + if { $annotateData(OpeningErrors) && ([sc_pos moveNumber] < $annotateData(OpeningMoves) ) } { appendAnnotator "opBlunder [sc_pos moveNumber] ([sc_pos side])" } } @@ -434,6 +439,7 @@ namespace eval ::annotation { } proc addAnnotation { } { + global ::annotation::annotateData # Let's try to assess the situation: # We are here, now that the engine has analyzed the position reached by # our last move. Currently it is the opponent to move: @@ -441,9 +447,9 @@ namespace eval ::annotation { set gamemove [sc_game info previousMoveUCI] # And this is his best line: - lassign $::annotate(PV1) score score_type ::annotate(moves) - if { $gamemove eq "" || $score eq "" } { set ::annotate(prevscore1) $score; return } - set moves $::annotate(moves) + lassign $annotateData(PV1) score score_type annotateData(moves) + if { $gamemove eq "" || $score eq "" } { set annotateData(prevscore1) $score; return } + set moves $annotateData(moves) set bestMoveIsMate 0 if { $score_type eq "mate" } { # We do not want to insert a best-line variation into the game @@ -452,12 +458,12 @@ namespace eval ::annotation { # Sooner or later the game will deviate anyway; a variation at that point will # do nicely and is probably more accurate as well. set bestMoveIsMate 1 - set ::annotate(scoremate) $score + set annotateData(scoremate) $score set score [expr { $score < 0 ? -127 : 127 }] - set ::annotate(score) $score + set annotateData(score) $score } else { - set ::annotate(score) $score - set ::annotate(scoremate) 0 + set annotateData(score) $score + set annotateData(scoremate) 0 } # We will add a closing line at the end of variation or game @@ -467,7 +473,7 @@ namespace eval ::annotation { } # This is the score we could have had if we had played our best move - set prevscore $::annotate(prevscore1) + set prevscore $annotateData(prevscore1) # Note that the engine's judgement is in relative terms, a negative score # being favorable to opponent, a positive score favorable to player @@ -485,7 +491,7 @@ namespace eval ::annotation { # Set an "isBlunder" filter. # Let's mark moves with a decay greater than the threshold. set isBlunder 0 - if { $deltamove > $::annotate(blunderThreshold) } { + if { $deltamove > $annotateData(blunderThreshold) } { set isBlunder 2 } elseif { $deltamove > 0 } { set isBlunder 1 @@ -493,11 +499,11 @@ namespace eval ::annotation { set absdeltamove [expr { abs($deltamove) } ] # to parse scores if the engine's name contains - or + chars (see sc_game_scores) - set engine_name [string map {"-" " " "+" " "} $::annotate(engine)] + set engine_name [string map {"-" " " "+" " "} $annotateData(engine)] # Prepare score strings for the opponent - if { $::annotate(scoremate) != 0 } { - set text [format "M%d" [expr abs($::annotate(scoremate))]] + if { $annotateData(scoremate) != 0 } { + set text [format "M%d" [expr abs($annotateData(scoremate))]] } else { set wscore [format "%+.2f" $score] if { $tomove eq "black" } {set wscore [expr 0.0 - $wscore] } @@ -507,10 +513,10 @@ namespace eval ::annotation { # See if we have the threshold filter activated. # If so, take only bad moves and missed mates until the position is lost anyway # Or that we must annotate all moves - if { ( $::annotate(annotateBlunders) == "blundersonly" + if { ( $annotateData(annotateBlunders) == "blundersonly" && ($isBlunder > 1 || ($isBlunder > 0 && [expr abs($score)] >= 327.0)) && ! $gameIsLost) - || ($::annotate(annotateBlunders) == "allmoves") } { + || ($annotateData(annotateBlunders) == "allmoves") } { if { $isBlunder > 0 } { # Add move score nag, and possibly an exercise if { $absdeltamove > $::informant("??") } { @@ -525,9 +531,9 @@ namespace eval ::annotation { } # Add score comment and engine name if needed - if { ! $::annotate(annotateShort) } { + if { ! $annotateData(annotateShort) } { sc_pos setComment "[sc_pos getComment] $engine_name: $text" - } elseif { $::annotate(addScoreToShortAnnotations) || $::annotate(scoreAllMoves) } { + } elseif { $annotateData(addScoreToShortAnnotations) || $annotateData(scoreAllMoves) } { sc_pos setComment "[sc_pos getComment] $text" } @@ -535,37 +541,37 @@ namespace eval ::annotation { sc_pos addNag [scoreToNag $score] # Add the variation sc_move back - if { $::annotate(annotateBlunders) == "blundersonly" } { + if { $annotateData(annotateBlunders) == "blundersonly" } { # Add a diagram tag, but avoid doubles if { [string first "D" "[sc_pos getNags]"] == -1 } { sc_pos addNag "D" } } - if { $::annotate(prevmoves1) != "" && ( $::annotate(annotateMoves) == "all" || - $::annotate(annotateMoves) == "white" && $tomove == "black" || - $::annotate(annotateMoves) == "black" && $tomove == "white" )} { + if { $annotateData(prevmoves1) != "" && ( $annotateData(annotateMoves) == "all" || + $annotateData(annotateMoves) == "white" && $tomove == "black" || + $annotateData(annotateMoves) == "black" && $tomove == "white" )} { set n 1 - while { $n <= $::annotate(anzVariation) && $::annotate(prevmoves$n) ne "" } { + while { $n <= $annotateData(anzVariation) && $annotateData(prevmoves$n) ne "" } { sc_var create # Add the starting move - sc_move addSan [lrange $::annotate(prevmoves$n) 0 0] + sc_move addSan [lrange $annotateData(prevmoves$n) 0 0] # Add its score - if { ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations) } { + if { ! $annotateData(annotateShort) || $annotateData(addScoreToShortAnnotations) } { # And for the (missed?) chance - if { $::annotate(prevscoremate) != 0 } { - set prevtext [format "M%d" [expr abs($::annotate(prevscoremate))]] + if { $annotateData(prevscoremate) != 0 } { + set prevtext [format "M%d" [expr abs($annotateData(prevscoremate))]] } else { - set wprevscore [format "%+.2f" $::annotate(prevscore$n)] + set wprevscore [format "%+.2f" $annotateData(prevscore$n)] if { $tomove eq "white" } {set wprevscore [expr 0.0 - $wprevscore] } set prevtext "\[%eval $wprevscore\]" } sc_pos setComment "$prevtext" } # Add remaining moves - sc_move addSan [lrange $::annotate(prevmoves$n) 1 end] + sc_move addSan [lrange $annotateData(prevmoves$n) 1 end] # Add position NAG, unless the line ends in mate - if { $n == 1 && $::annotate(prevscoremate) == 0 } { + if { $n == 1 && $annotateData(prevscoremate) == 0 } { sc_pos addNag [scoreToNag $prevscore] } sc_var exit @@ -577,7 +583,7 @@ namespace eval ::annotation { if { $isBlunder == 0 && $absdeltamove > $::informant("!?") } { sc_pos addNag "!?" } - if { $::annotate(scoreAllMoves) } { + if { $annotateData(scoreAllMoves) } { # Add a score mark anyway sc_pos setComment "[sc_pos getComment] $text" } @@ -587,21 +593,21 @@ namespace eval ::annotation { sc_move back sc_var create sc_move addSan $gamemove - if { ($::annotate(scoremate) == 0) && ( ! $::annotate(annotateShort) || $::annotate(addScoreToShortAnnotations)) } { + if { ($annotateData(scoremate) == 0) && ( ! $annotateData(annotateShort) || $annotateData(addScoreToShortAnnotations)) } { sc_pos setComment "$text" } sc_move addSan $moves - if { $::annotate(scoremate) == 0 } { + if { $annotateData(scoremate) == 0 } { sc_pos addNag [scoreToNag $score] } sc_var exit # Now up to the end of the game ::move::Forward } - set ::annotate(prevscore1) $::annotate(score) - set ::annotate(prevmoves1) $::annotate(moves) - lassign $::annotate(PV2) ::annotate(prevscore2) score_type ::annotate(prevmoves2) - set ::annotate(prevscoremate) $::annotate(scoremate) + set annotateData(prevscore1) $annotateData(score) + set annotateData(prevmoves1) $annotateData(moves) + lassign $annotateData(PV2) annotateData(prevscore2) score_type annotateData(prevmoves2) + set annotateData(prevscoremate) $annotateData(scoremate) updateBoard -pgn } @@ -610,8 +616,9 @@ namespace eval ::annotation { # check at which depth the tactical shot is found ################################################################################ proc markExercise { prevscore score nag} { + global ::annotation::annotateData sc_pos addNag $nag - if { ! $::annotate(tacticalExercises)} { return 0 } + if { ! $annotateData(tacticalExercises)} { return 0 } set deltamove [expr {$score + $prevscore}] # filter tactics so only those with high gains are kept @@ -623,7 +630,7 @@ namespace eval ::annotation { } # The best move is much better than others. - set sc2 [lindex $::annotate(PV2) 0] + set sc2 [lindex $annotateData(PV2) 0] if { [expr abs( $score - $sc2 )] < 1.5 } { return 0 } # The best move does not lose position. @@ -631,10 +638,10 @@ namespace eval ::annotation { if {([sc_pos side] == "white") && ($score > $::informant("+/-")) } { return 0} # Move is not obvious: check that it is not the first move guessed at low depths - set pv [ lindex [ lindex $::annotate(PV1) 2 ] 0 ] + set pv [ lindex [ lindex $annotateData(PV1) 2 ] 0 ] # bm0 must SAN, pv is UCI: convert set bm0 [string range [lindex $pv 0] 0 4] - set bm0 [sc_pos coordToSAN $::annotate(position) $bm0] + set bm0 [sc_pos coordToSAN $annotateData(position) $bm0] set bm0 [string range $bm0 [expr [string first "." $bm0] + 1] end] foreach depth {1 2 3} { @@ -671,6 +678,7 @@ namespace eval ::annotation { } proc ::annotation::eng_messages {id w msg} { + global ::annotation::annotateData lassign $msg msgType msgData switch $msgType { "InfoConfig" { @@ -681,14 +689,14 @@ namespace eval ::annotation { "InfoPV" { lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } - set ::annotate(PV$multipv) [list $score $score_type $pv] + set annotateData(PV$multipv) [list $score $score_type $pv] } "InfoBestMove" { - lassign $msgData ::annotate(bestmove) - set ::annotate(move_done) 1 + lassign $msgData annotateData(bestmove) + set annotateData(move_done) 1 } "InfoGo" { - lassign $msgData ::annotate(position) + lassign $msgData annotateData(position) } "InfoDisconnected" { ::engineNoWin::disconnected $id $msgData From 5f4d4567305560c9c1f0d64727f599c11a55cc35 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 11 May 2025 10:03:56 +0200 Subject: [PATCH 45/57] improvement for init engine only send option if available fill options dialog when only default options are present --- tcl/tools/annotate.tcl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 806c5f06b..5aa5ccef5 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -18,14 +18,14 @@ proc ::engineNoWin::initEngine { id engine callback } { if { [info exists ::enginewin::engConfig_$id] } { return 1 } set config [::enginecfg::get $engine] lassign $config name cmd args wdir elo time url uci options - if { ! $uci } { + if { $uci ne "" && ! $uci } { tk_messageBox -title Scid -icon info -type ok -message "Only UCI-Engines are supported!" return 0 } set ::enginewin::engConfig_$id [list $name $cmd $args $wdir $elo $time $url $uci {} {}] ::engine::setLogCmd $id {} - ::engine::connect $id $callback $cmd {} - ::engine::send $id SetOptions $options + ::engine::connect $id $callback $cmd $args + if { $options ne "" } { ::engine::send $id SetOptions $options } return 1 } @@ -74,6 +74,7 @@ proc ::engineNoWin::initEngineOptions {id w options} { if { ! [winfo exists $w.text.reset] } { lset ::enginewin::engConfig_$id 8 $options ::enginecfg::createOptionWidgets $id $w $options + ::engine::replyInfoConfig $id } else { # changed options stored in #9, but do not save lset ::enginewin::engConfig_$id 9 $options From f23e8fa249e0a9f7f19b5aebf33415cb4140f488 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 11 May 2025 19:47:46 +0200 Subject: [PATCH 46/57] engConfig 9 not needed, removed --- tcl/tools/annotate.tcl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 5aa5ccef5..d520f1a92 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -76,8 +76,7 @@ proc ::engineNoWin::initEngineOptions {id w options} { ::enginecfg::createOptionWidgets $id $w $options ::engine::replyInfoConfig $id } else { - # changed options stored in #9, but do not save - lset ::enginewin::engConfig_$id 9 $options + lset ::enginewin::engConfig_$id 8 $options ::enginecfg::updateOptionWidgets $id $w $options {} $w.text configure -state disabled } @@ -85,8 +84,6 @@ proc ::engineNoWin::initEngineOptions {id w options} { proc ::engineNoWin::saveEngineSetup { id } { upvar ::enginewin::engConfig_$id engConfig_ - # copy #9 to #8 to save the options - lset ::enginewin::engConfig_$id 8 [lindex [set ::enginewin::engConfig_$id] 9] ::enginecfg::save [set ::enginewin::engConfig_$id] } From b1f3f9ed4681aed607fe7f9c2b341c1ce7e838ff Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 14 May 2025 17:34:34 +0200 Subject: [PATCH 47/57] add parameter engine typ (uci,winboard,all) in createEngineOptionsFrame --- tcl/tools/annotate.tcl | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index d520f1a92..10bc27d77 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -6,23 +6,15 @@ ########################################################################################## ### Annotate Dialog: uses a chess engine to analyze and annotate a chess game. -#TODO -#improve Tactical Exercise -#"finish game" function -#accuracy function - # engineNoWin will be used by annotate and finish game namespace eval ::engineNoWin {} # Open the engine and configure it proc ::engineNoWin::initEngine { id engine callback } { if { [info exists ::enginewin::engConfig_$id] } { return 1 } +# tk_messageBox -title Scid -icon info -type ok -message "Only UCI-Engines are supported!" set config [::enginecfg::get $engine] lassign $config name cmd args wdir elo time url uci options - if { $uci ne "" && ! $uci } { - tk_messageBox -title Scid -icon info -type ok -message "Only UCI-Engines are supported!" - return 0 - } - set ::enginewin::engConfig_$id [list $name $cmd $args $wdir $elo $time $url $uci {} {}] + set ::enginewin::engConfig_$id $config ::engine::setLogCmd $id {} ::engine::connect $id $callback $cmd $args if { $options ne "" } { ::engine::send $id SetOptions $options } @@ -46,10 +38,22 @@ proc ::engineNoWin::showHideOptionsFrame {id w enginevar callback col} { ::engineNoWin::initEngine $id $engine [list $callback $id $w] } -#create frame for edit engine options -proc ::engineNoWin::createEngineOptionsFrame {f id var col callback} { +#create frame for select and edit engine options +#engType: all, uci or winboard +proc ::engineNoWin::createEngineOptionsFrame {f id var col callback {engTyp "uci"}} { ttk::frame $f.$id - set engList [::enginecfg::names ] + set allEngList [::enginecfg::names ] + if { $engTyp ne "all"} { + set engList {} + foreach name $allEngList { + set typ [lindex [::enginecfg::get $name] 7] + if { $engTyp == "uci" && $typ || $engTyp == "winboard" && ! $typ } { + lappend engList $name + } + } + } else { + set engList $allEngList + } if { [set $var] eq "" } { set $var [lindex $engList 0] } ttk::combobox $f.$id.eng -width 20 -state readonly -values $engList -textvariable $var bind $f.$id.eng <> "::engineNoWin::changeEngine $id $f.opts$id $var $callback" From 92b5cc40d214f3ed078cf6c2264e2ff9d4e87d25 Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 16 May 2025 23:57:47 +0200 Subject: [PATCH 48/57] save options for annotation --- tcl/options.tcl | 43 ++++++++++++++++++++++++------------------ tcl/start.tcl | 2 +- tcl/tools/annotate.tcl | 22 --------------------- 3 files changed, 26 insertions(+), 41 deletions(-) diff --git a/tcl/options.tcl b/tcl/options.tcl index d40c57d11..50b526694 100644 --- a/tcl/options.tcl +++ b/tcl/options.tcl @@ -125,19 +125,27 @@ proc InitDefaultFonts {} { } proc InitDefaultAnnotate {} { - set ::isBatchOpening 0 - set ::isBatchOpeningMoves 12 - set ::isBatch 0 - set ::markTacticalExercises 0 - set ::isAnnotateVar 0 - set ::isShortAnnotation 0 - set ::addScoreToShortAnnotations 0 - set ::addAnnotatorTag 0 - set ::annotateMoves all - set ::annotateBlunders blundersonly - set ::scoreAllMoves 0 - # Blunder Threshold - set ::blunderThreshold 1.0 + set ::annotation::annotateData(typ) "movetime" + set ::annotation::annotateData(movetime) 1000 + set ::annotation::annotateData(time) 1 + set ::annotation::annotateData(depth) 20 + set ::annotation::annotateData(engine) "" + set ::annotation::annotateData(blunderThreshold) 0.5 + set ::annotation::annotateData(annotateMoves) all + set ::annotation::annotateData(annotateBlunders) blundersonly + set ::annotation::annotateData(scoreAllMoves) 1 + set ::annotation::annotateData(useAnalysisBook) 0 + set ::annotation::annotateData(AnalysisBookName) "" + set ::annotation::annotateData(BookSlot) 1 + set ::annotation::annotateData(tacticalExercises) 0 + set ::annotation::annotateData(addAnnotatorTag) 1 + set ::annotation::annotateData(OpeningErrors) 0 + set ::annotation::annotateData(OpeningMoves) 0 + set ::annotation::annotateData(annotateShort) 1 + set ::annotation::annotateData(addScoreToShortAnnotations) 1 + set ::annotation::annotateData(batchMode) 0 + set ::annotation::annotateData(batchEnd) 0 + set ::annotation::annotateData(anzVariation) 1 } InitDefaultFonts @@ -661,13 +669,12 @@ proc options.write {} { ::sergame::data(wtime) ::sergame::data(winc) ::sergame::data(btime) ::sergame::data(binc) \ boardfile_lite boardfile_dark \ FilterMaxMoves FilterMinMoves FilterStepMoves FilterMaxElo FilterMinElo FilterStepElo \ - FilterMaxYear FilterMinYear FilterStepYear FilterGuessELO lookTheme ThemePackageFile autoResizeBoard \ - isBatchOpening isBatchOpeningMoves isBatch \ - markTacticalExercises scoreAllMoves \ - isAnnotateVar isShortAnnotation addScoreToShortAnnotations annotateBlunders\ - addAnnotatorTag annotateMoves } { + FilterMaxYear FilterMinYear FilterStepYear FilterGuessELO lookTheme ThemePackageFile autoResizeBoard } { puts $optionF "set $i [list [set $i]]" } + foreach i [lsort [array names ::annotation::annotateData]] { + puts $optionF "set ::annotation::annotateData($i) [list $::annotation::annotateData($i)]" + } puts $optionF "" foreach i [lsort [array names winWidth]] { diff --git a/tcl/start.tcl b/tcl/start.tcl index 35db1c04a..37d1d5f63 100644 --- a/tcl/start.tcl +++ b/tcl/start.tcl @@ -242,7 +242,7 @@ foreach ns { ::tools::graphs ::tools::graphs::filter ::tools::graphs::absfilter ::tools::graphs::rating ::tools::graphs::score ::tb ::optable - ::board ::move + ::board ::move ::annotation ::tacgame ::sergame ::opening ::tactics ::calvar ::uci ::fics ::reviewgame ::novag ::config ::docking ::pinfo diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 10bc27d77..2ac9f9b71 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -102,28 +102,7 @@ proc ::engineNoWin::disconnected { id data } { namespace eval ::annotation { # Typ may be "movetime": time per move or "depth": analyse till depth is reached - set annotateData(typ) "movetime" - set annotateData(movetime) 1000 - set annotateData(time) 1 - set annotateData(depth) 20 - set annotateData(engine) "" set annotateData(progress) 0 - set annotateData(blunderThreshold) 0.5 - set annotateData(annotateMoves) all - set annotateData(annotateBlunders) blundersonly - set annotateData(scoreAllMoves) 1 - set annotateData(annotateMode) 0 - set annotateData(useAnalysisBook) 0 - set annotateData(AnalysisBookName) "" - set annotateData(BookSlot) 1 - set annotateData(tacticalExercises) 0 - set annotateData(addAnnotatorTag) 1 - set annotateData(OpeningErrors) 0 - set annotateData(OpeningMoves) 0 - set annotateData(annotateShort) 1 - set annotateData(addScoreToShortAnnotations) 1 - set annotateData(batchMode) 0 - set annotateData(batchEnd) 0 set annotateData(msg1) "" set annotateData(msg2) "" set annotateData(msg3) "" @@ -135,7 +114,6 @@ namespace eval ::annotation { set annotateData(moves) "" set annotateData(scoremate) 0 set annotateData(prevscoremate) 0 - set annotateData(anzVariation) 1 proc doAnnotate {} { global ::annotation::annotateData From 3bb0f140942e473494e29a8748f6e0077c403d63 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sat, 17 May 2025 17:21:05 +0200 Subject: [PATCH 49/57] Use grafic for close button --- tcl/tools/annotate.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 2ac9f9b71..f4abf9ac9 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -62,7 +62,7 @@ proc ::engineNoWin::createEngineOptionsFrame {f id var col callback {engTyp "uci pack $f.$id.eng $f.$id.opts -side left -padx { 0 5 } ttk::labelframe $f.opts$id -text "Engine Parameter" ttk::label $f.opts$id.l -textvariable $var - ttk::button $f.opts$id.x -text "X" -style Toolbutton -command "grid forget $f.opts$id" + ttk::button $f.opts$id.x -image tb_close -style Toolbutton -command "grid forget $f.opts$id" ttk_text $f.opts$id.text -wrap none -padx 4 autoscrollBars both $f.opts$id $f.opts$id.text 1 $f.opts$id.text configure -state normal -wrap word -width 60 -height 18 From 45e1fb56bee0350de5131e9a57243fb963daf70d Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 18 May 2025 12:53:54 +0200 Subject: [PATCH 50/57] shift namespace enginenowin in separate file --- tcl/start.tcl | 1 + tcl/tools/annotate.tcl | 93 ----------------------------------- tcl/tools/enginenowin.tcl | 100 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 93 deletions(-) create mode 100644 tcl/tools/enginenowin.tcl diff --git a/tcl/start.tcl b/tcl/start.tcl index 37d1d5f63..f1da85395 100644 --- a/tcl/start.tcl +++ b/tcl/start.tcl @@ -744,6 +744,7 @@ tools/optable.tcl tools/preport.tcl tools/pinfo.tcl tools/analysis.tcl +tools/enginenowin.tcl tools/annotate.tcl tools/finishgame.tcl tools/wbdetect.tcl diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index f4abf9ac9..cf97f021e 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -6,99 +6,6 @@ ########################################################################################## ### Annotate Dialog: uses a chess engine to analyze and annotate a chess game. -# engineNoWin will be used by annotate and finish game -namespace eval ::engineNoWin {} -# Open the engine and configure it -proc ::engineNoWin::initEngine { id engine callback } { - if { [info exists ::enginewin::engConfig_$id] } { return 1 } -# tk_messageBox -title Scid -icon info -type ok -message "Only UCI-Engines are supported!" - set config [::enginecfg::get $engine] - lassign $config name cmd args wdir elo time url uci options - set ::enginewin::engConfig_$id $config - ::engine::setLogCmd $id {} - ::engine::connect $id $callback $cmd $args - if { $options ne "" } { ::engine::send $id SetOptions $options } - return 1 -} - -proc ::engineNoWin::changeEngine {id w enginevar callback} { - ::engine::close $id - $w.text configure -state normal - $w.text delete 1.0 end - foreach wchild [winfo children $w.text] { destroy $wchild } - catch { unset ::enginewin::engConfig_$id } - set engine [set $enginevar] - ::engineNoWin::initEngine $id $engine [list $callback $id $w] -} - -proc ::engineNoWin::showHideOptionsFrame {id w enginevar callback col} { - if { [winfo ismapped $w] } { grid forget $w ; return } - grid $w -row 0 -column $col -rowspan 5 -sticky ne -padx 10 - set engine [set $enginevar] - ::engineNoWin::initEngine $id $engine [list $callback $id $w] -} - -#create frame for select and edit engine options -#engType: all, uci or winboard -proc ::engineNoWin::createEngineOptionsFrame {f id var col callback {engTyp "uci"}} { - ttk::frame $f.$id - set allEngList [::enginecfg::names ] - if { $engTyp ne "all"} { - set engList {} - foreach name $allEngList { - set typ [lindex [::enginecfg::get $name] 7] - if { $engTyp == "uci" && $typ || $engTyp == "winboard" && ! $typ } { - lappend engList $name - } - } - } else { - set engList $allEngList - } - if { [set $var] eq "" } { set $var [lindex $engList 0] } - ttk::combobox $f.$id.eng -width 20 -state readonly -values $engList -textvariable $var - bind $f.$id.eng <> "::engineNoWin::changeEngine $id $f.opts$id $var $callback" - ttk::button $f.$id.opts -image ::icon::filter_adv -style Toolbutton \ - -command "::engineNoWin::showHideOptionsFrame $id $f.opts$id $var $callback $col" - pack $f.$id.eng $f.$id.opts -side left -padx { 0 5 } - ttk::labelframe $f.opts$id -text "Engine Parameter" - ttk::label $f.opts$id.l -textvariable $var - ttk::button $f.opts$id.x -image tb_close -style Toolbutton -command "grid forget $f.opts$id" - ttk_text $f.opts$id.text -wrap none -padx 4 - autoscrollBars both $f.opts$id $f.opts$id.text 1 - $f.opts$id.text configure -state normal -wrap word -width 60 -height 18 - ttk::button $f.opts$id.save -text "Save Setup" -command "::engineNoWin::saveEngineSetup $id" - grid $f.opts$id.l -row 0 -column 0 -sticky w - grid $f.opts$id.x -row 0 -column 1 -sticky e - grid $f.opts$id.save -row 2 -column 0 -columnspan 2 -sticky e -pady { 5 0 } - bind $f.$id "catch { unset ::enginewin::engConfig_$id }; ::engine::close $id" -} - -proc ::engineNoWin::initEngineOptions {id w options} { - upvar ::enginewin::engConfig_$id engConfig_ - if { ! [winfo exists $w.text.reset] } { - lset ::enginewin::engConfig_$id 8 $options - ::enginecfg::createOptionWidgets $id $w $options - ::engine::replyInfoConfig $id - } else { - lset ::enginewin::engConfig_$id 8 $options - ::enginecfg::updateOptionWidgets $id $w $options {} - $w.text configure -state disabled - } -} - -proc ::engineNoWin::saveEngineSetup { id } { - upvar ::enginewin::engConfig_$id engConfig_ - ::enginecfg::save [set ::enginewin::engConfig_$id] -} - -proc ::engineNoWin::disconnected { id data } { - upvar ::enginewin::engConfig_$id engConfig_ - lassign $data errorMsg - lassign [set ::enginewin::engConfig_$id] engine - if {$errorMsg eq ""} { set errorMsg "The connection with the engine $id $engine terminated unexpectedly." } - tk_messageBox -icon warning -type ok -parent . -message $errorMsg -} - namespace eval ::annotation { # Typ may be "movetime": time per move or "depth": analyse till depth is reached diff --git a/tcl/tools/enginenowin.tcl b/tcl/tools/enginenowin.tcl new file mode 100644 index 000000000..14a6cfa79 --- /dev/null +++ b/tcl/tools/enginenowin.tcl @@ -0,0 +1,100 @@ +### +### enginenowin.tcl: part of Scid. +### This file is part of Scid (Shane's Chess Information Database). +### Copyright (C) 2025 Uwe Klimmek +### uses code from Fulvio Benini https://github.com/benini/chess_accuracy and analysis.tcl +########################################################################################## +### procs for using engines without a engine window + +# engineNoWin will be used by annotate and finish game +namespace eval ::engineNoWin {} +# Open the engine and configure it +proc ::engineNoWin::initEngine { id engine callback } { + if { [info exists ::enginewin::engConfig_$id] } { return 1 } +# tk_messageBox -title Scid -icon info -type ok -message "Only UCI-Engines are supported!" + set config [::enginecfg::get $engine] + lassign $config name cmd args wdir elo time url uci options + set ::enginewin::engConfig_$id $config + ::engine::setLogCmd $id {} + ::engine::connect $id $callback $cmd $args + if { $options ne "" } { ::engine::send $id SetOptions $options } + return 1 +} + +proc ::engineNoWin::changeEngine {id w enginevar callback} { + ::engine::close $id + $w.text configure -state normal + $w.text delete 1.0 end + foreach wchild [winfo children $w.text] { destroy $wchild } + catch { unset ::enginewin::engConfig_$id } + set engine [set $enginevar] + ::engineNoWin::initEngine $id $engine [list $callback $id $w] +} + +proc ::engineNoWin::showHideOptionsFrame {id w enginevar callback col} { + if { [winfo ismapped $w] } { grid forget $w ; return } + grid $w -row 0 -column $col -rowspan 5 -sticky ne -padx 10 + set engine [set $enginevar] + ::engineNoWin::initEngine $id $engine [list $callback $id $w] +} + +#create frame for select and edit engine options +#engType: all, uci or winboard +proc ::engineNoWin::createEngineOptionsFrame {f id var col callback {engTyp "uci"}} { + ttk::frame $f.$id + set allEngList [::enginecfg::names ] + if { $engTyp ne "all"} { + set engList {} + foreach name $allEngList { + set typ [lindex [::enginecfg::get $name] 7] + if { $engTyp == "uci" && $typ || $engTyp == "winboard" && ! $typ } { + lappend engList $name + } + } + } else { + set engList $allEngList + } + if { [set $var] eq "" } { set $var [lindex $engList 0] } + ttk::combobox $f.$id.eng -width 20 -state readonly -values $engList -textvariable $var + bind $f.$id.eng <> "::engineNoWin::changeEngine $id $f.opts$id $var $callback" + ttk::button $f.$id.opts -image ::icon::filter_adv -style Toolbutton \ + -command "::engineNoWin::showHideOptionsFrame $id $f.opts$id $var $callback $col" + pack $f.$id.eng $f.$id.opts -side left -padx { 0 5 } + ttk::labelframe $f.opts$id -text "Engine Parameter" + ttk::label $f.opts$id.l -textvariable $var + ttk::button $f.opts$id.x -image tb_close -style Toolbutton -command "grid forget $f.opts$id" + ttk_text $f.opts$id.text -wrap none -padx 4 + autoscrollBars both $f.opts$id $f.opts$id.text 1 + $f.opts$id.text configure -state normal -wrap word -width 60 -height 18 + ttk::button $f.opts$id.save -text "Save Setup" -command "::engineNoWin::saveEngineSetup $id" + grid $f.opts$id.l -row 0 -column 0 -sticky w + grid $f.opts$id.x -row 0 -column 1 -sticky e + grid $f.opts$id.save -row 2 -column 0 -columnspan 2 -sticky e -pady { 5 0 } + bind $f.$id "catch { unset ::enginewin::engConfig_$id }; ::engine::close $id" +} + +proc ::engineNoWin::initEngineOptions {id w options} { + upvar ::enginewin::engConfig_$id engConfig_ + if { ! [winfo exists $w.text.reset] } { + lset ::enginewin::engConfig_$id 8 $options + ::enginecfg::createOptionWidgets $id $w $options + ::engine::replyInfoConfig $id + } else { + lset ::enginewin::engConfig_$id 8 $options + ::enginecfg::updateOptionWidgets $id $w $options {} + $w.text configure -state disabled + } +} + +proc ::engineNoWin::saveEngineSetup { id } { + upvar ::enginewin::engConfig_$id engConfig_ + ::enginecfg::save [set ::enginewin::engConfig_$id] +} + +proc ::engineNoWin::disconnected { id data } { + upvar ::enginewin::engConfig_$id engConfig_ + lassign $data errorMsg + lassign [set ::enginewin::engConfig_$id] engine + if {$errorMsg eq ""} { set errorMsg "The connection with the engine $id $engine terminated unexpectedly." } + tk_messageBox -icon warning -type ok -parent . -message $errorMsg +} From 3563bcf059118261cebd139c186ed4668539a619 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 18 May 2025 14:06:45 +0200 Subject: [PATCH 51/57] new proc getBookList create a list with all opening books --- tcl/tools/annotate.tcl | 16 ++---------- tcl/windows/book.tcl | 58 ++++++++++++++++++------------------------ 2 files changed, 27 insertions(+), 47 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index cf97f021e..815a8a465 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -60,25 +60,13 @@ namespace eval ::annotation { ttk::checkbutton $f.annotate.cbBook -text $::tr(UseBook) -variable ::annotation::annotateData(useAnalysisBook) ::engineNoWin::createEngineOptionsFrame $f annotateEngine ::annotation::annotateData(engine) 3 ::annotation::eng_messages - # choose a book for analysis # load book names - set bookPath $::scidBooksDir - set bookList [ lsort -dictionary [ glob -nocomplain -directory $bookPath *.bin ] ] + lassign [getBookList $annotateData(AnalysisBookName)] idx tmp # No book found - if { [llength $bookList] == 0 } { + if { $idx < 0 } { set annotateData(useAnalysisBook) 0 $f.annotate.cbBook configure -state disabled } - set tmp {} - set idx 0 - set i 0 - foreach file $bookList { - lappend tmp [ file tail $file ] - if {$::book::lastBook == [ file tail $file ] } { - set idx $i - } - incr i - } if { $annotateData(AnalysisBookName) eq "" } { set annotateData(AnalysisBookName) [lindex $tmp $idx] } ttk::combobox $f.annotate.comboBooks -width 12 -values $tmp -textvariable ::annotation::annotateData(AnalysisBookName) catch { $f.annotate.comboBooks current $idx } diff --git a/tcl/windows/book.tcl b/tcl/windows/book.tcl index e411f0bb8..42fda9246 100644 --- a/tcl/windows/book.tcl +++ b/tcl/windows/book.tcl @@ -5,6 +5,27 @@ ###################################################################### ### Book window +# return index of actBook and a list of all books, return -1 if no books available +proc getBookList { actBook } { + set bookPath $::scidBooksDir + set bookList [ lsort -dictionary [ glob -nocomplain -directory $bookPath *.bin ] ] + # No book found + if { [llength $bookList] == 0 } { + return [list -1 {}] + } + set tmp {} + set idx 0 + set i 0 + foreach file $bookList { + lappend tmp [ file tail $file ] + if {$actBook == [ file tail $file ] } { + set idx $i + } + incr i + } + return [list $idx $tmp] +} + namespace eval book { set isOpen 0 set isReadonly 0 @@ -105,29 +126,15 @@ namespace eval book { if { $name == "" && $lastBook != "" } { set name $lastBook } - set bookPath $::scidBooksDir - set bookList [ lsort -dictionary [ glob -nocomplain -directory $bookPath *.bin ] ] - + lassign [getBookList $name] idx tmp # No book found - if { [llength $bookList] == 0 } { + if { $idx < 0 } { tk_messageBox -title "Scid" -type ok -icon error -message "No books found. Check books directory" set ::book::isOpen 0 set ::book::currentBook "" ::win::closeWindow $w return } - - set i 0 - set idx 0 - set tmp {} - foreach file $bookList { - set f [ file tail $file ] - lappend tmp $f - if {$name == $f} { - set idx $i - } - incr i - } ttk::combobox $w.f.combo -width 12 -values $tmp catch { $w.f.combo current $idx } @@ -270,11 +277,8 @@ namespace eval book { ttk::frame $w.f applyThemeColor_background $w # load book names - set bookPath $::scidBooksDir - set bookList [ lsort -dictionary [ glob -nocomplain -directory $bookPath *.bin ] ] - - # No book found - if { [llength $bookList] == 0 } { + lassign [getBookList $name] idx tmp + if { $idx < 0 } { tk_messageBox -title "Scid" -type ok -icon error -message "No books found. Check books directory" set ::book::isOpen 0 set ::book::currentBook "" @@ -282,18 +286,6 @@ namespace eval book { return } - set i 0 - set idx 0 - set tmp {} - foreach file $bookList { - set f [ file tail $file ] - lappend tmp $f - if {$name == $f} { - set idx $i - } - incr i - } - ttk::combobox $w.fcombo.combo -width 12 -values $tmp catch { $w.fcombo.combo current $idx } pack $w.fcombo.combo -expand yes -fill x From a8b195b43caa723ac50614a7fbd6e4f254a48c27 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 25 May 2025 14:29:22 +0200 Subject: [PATCH 52/57] split var annotatedata in options and internal _data save only annotate::options --- tcl/options.tcl | 45 ++++--- tcl/tools/annotate.tcl | 260 ++++++++++++++++++++--------------------- 2 files changed, 146 insertions(+), 159 deletions(-) diff --git a/tcl/options.tcl b/tcl/options.tcl index 50b526694..504253c2e 100644 --- a/tcl/options.tcl +++ b/tcl/options.tcl @@ -125,27 +125,26 @@ proc InitDefaultFonts {} { } proc InitDefaultAnnotate {} { - set ::annotation::annotateData(typ) "movetime" - set ::annotation::annotateData(movetime) 1000 - set ::annotation::annotateData(time) 1 - set ::annotation::annotateData(depth) 20 - set ::annotation::annotateData(engine) "" - set ::annotation::annotateData(blunderThreshold) 0.5 - set ::annotation::annotateData(annotateMoves) all - set ::annotation::annotateData(annotateBlunders) blundersonly - set ::annotation::annotateData(scoreAllMoves) 1 - set ::annotation::annotateData(useAnalysisBook) 0 - set ::annotation::annotateData(AnalysisBookName) "" - set ::annotation::annotateData(BookSlot) 1 - set ::annotation::annotateData(tacticalExercises) 0 - set ::annotation::annotateData(addAnnotatorTag) 1 - set ::annotation::annotateData(OpeningErrors) 0 - set ::annotation::annotateData(OpeningMoves) 0 - set ::annotation::annotateData(annotateShort) 1 - set ::annotation::annotateData(addScoreToShortAnnotations) 1 - set ::annotation::annotateData(batchMode) 0 - set ::annotation::annotateData(batchEnd) 0 - set ::annotation::annotateData(anzVariation) 1 + set ::annotation::options(typ) "movetime" + set ::annotation::options(movetime) 1000 + set ::annotation::options(time) 1 + set ::annotation::options(depth) 20 + set ::annotation::options(engine) "" + set ::annotation::options(blunderThreshold) 0.5 + set ::annotation::options(annotateMoves) all + set ::annotation::options(annotateBlunders) blundersonly + set ::annotation::options(scoreAllMoves) 1 + set ::annotation::options(useAnalysisBook) 0 + set ::annotation::options(AnalysisBookName) "" + set ::annotation::options(tacticalExercises) 0 + set ::annotation::options(addAnnotatorTag) 1 + set ::annotation::options(OpeningErrors) 0 + set ::annotation::options(OpeningMoves) 8 + set ::annotation::options(annotateShort) 1 + set ::annotation::options(addScoreToShortAnnotations) 1 + set ::annotation::options(batchMode) 0 + set ::annotation::options(batchEnd) 0 + set ::annotation::options(anzVariation) 1 } InitDefaultFonts @@ -672,8 +671,8 @@ proc options.write {} { FilterMaxYear FilterMinYear FilterStepYear FilterGuessELO lookTheme ThemePackageFile autoResizeBoard } { puts $optionF "set $i [list [set $i]]" } - foreach i [lsort [array names ::annotation::annotateData]] { - puts $optionF "set ::annotation::annotateData($i) [list $::annotation::annotateData($i)]" + foreach i [lsort [array names ::annotation::options]] { + puts $optionF "set ::annotation::options($i) [list $::annotation::options($i)]" } puts $optionF "" diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 815a8a465..ac8ad15b3 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -8,22 +8,10 @@ namespace eval ::annotation { - # Typ may be "movetime": time per move or "depth": analyse till depth is reached - set annotateData(progress) 0 - set annotateData(msg1) "" - set annotateData(msg2) "" - set annotateData(msg3) "" - set annotateData(prevscore1) 0 - set annotateData(prevscore2) 0 - set annotateData(prevmoves1) "" - set annotateData(prevmoves2) "" - set annotateData(score) 0 - set annotateData(moves) "" - set annotateData(scoremate) 0 - set annotateData(prevscoremate) 0 + set _Data(BookSlot) 1 proc doAnnotate {} { - global ::annotation::annotateData + global ::annotation::options ::annotation::_Data set w .annotationDialog # Do not do anything if the window exists if { [winfo exists $w] } { @@ -33,8 +21,8 @@ namespace eval ::annotation { } #Workaround for error in trace var for arrays - set ::annotateBlunderThreshold $annotateData(blunderThreshold) - set ::annotateTime $annotateData(time) + set ::annotateBlunderThreshold $options(blunderThreshold) + set ::annotateTime $options(time) trace variable ::annotateBlunderThreshold w {::utils::validate::Regexp {^[0-9]*\.?[0-9]*$}} trace variable ::annotateTime w {::utils::validate::Regexp {^[0-9]*\.?[0-9]*$}} @@ -47,28 +35,28 @@ namespace eval ::annotation { ttk::labelframe $f.annotate -text $::tr(GameReview) ttk::frame $f.annotate.typ - ttk::radiobutton $f.annotate.typ.label -text $::tr(AnnotateTime) -variable ::annotation::annotateData(typ) -value "movetime" - ttk::radiobutton $f.annotate.typ.ldepth -text "Depth per move" -variable ::annotation::annotateData(typ) -value "depth" + ttk::radiobutton $f.annotate.typ.label -text $::tr(AnnotateTime) -variable ::annotation::options(typ) -value "movetime" + ttk::radiobutton $f.annotate.typ.ldepth -text "Depth per move" -variable ::annotation::options(typ) -value "depth" ttk::spinbox $f.annotate.typ.spDelay -width 5 -textvariable ::annotateTime -from 0.1 -to 999 -validate key -justify right - ttk::spinbox $f.annotate.typ.depth -width 5 -textvariable ::annotation::annotateData(depth) -from 2 -to 999 -validate key -justify right - ttk::radiobutton $f.annotate.allmoves -text $::tr(AnnotateAllMoves) -variable ::annotation::annotateData(annotateBlunders) -value allmoves - ttk::radiobutton $f.annotate.blundersonly -text $::tr(AnnotateBlundersOnly) -variable ::annotation::annotateData(annotateBlunders) -value blundersonly + ttk::spinbox $f.annotate.typ.depth -width 5 -textvariable ::annotation::options(depth) -from 2 -to 999 -validate key -justify right + ttk::radiobutton $f.annotate.allmoves -text $::tr(AnnotateAllMoves) -variable ::annotation::options(annotateBlunders) -value allmoves + ttk::radiobutton $f.annotate.blundersonly -text $::tr(AnnotateBlundersOnly) -variable ::annotation::options(annotateBlunders) -value blundersonly ttk::frame $f.annotate.blunderbox ttk::label $f.annotate.blunderbox.label -text $::tr(BlundersThreshold:) ttk::spinbox $f.annotate.blunderbox.spBlunder -width 4 -textvariable ::annotateBlunderThreshold \ -from 0.1 -to 3.0 -increment 0.1 -justify right - ttk::checkbutton $f.annotate.cbBook -text $::tr(UseBook) -variable ::annotation::annotateData(useAnalysisBook) - ::engineNoWin::createEngineOptionsFrame $f annotateEngine ::annotation::annotateData(engine) 3 ::annotation::eng_messages + ttk::checkbutton $f.annotate.cbBook -text $::tr(UseBook) -variable ::annotation::options(useAnalysisBook) + ::engineNoWin::createEngineOptionsFrame $f annotateEngine ::annotation::options(engine) 3 ::annotation::eng_messages # load book names - lassign [getBookList $annotateData(AnalysisBookName)] idx tmp + lassign [getBookList $options(AnalysisBookName)] idx tmp # No book found if { $idx < 0 } { - set annotateData(useAnalysisBook) 0 + set options(useAnalysisBook) 0 $f.annotate.cbBook configure -state disabled } - if { $annotateData(AnalysisBookName) eq "" } { set annotateData(AnalysisBookName) [lindex $tmp $idx] } - ttk::combobox $f.annotate.comboBooks -width 12 -values $tmp -textvariable ::annotation::annotateData(AnalysisBookName) + if { $options(AnalysisBookName) eq "" } { set options(AnalysisBookName) [lindex $tmp $idx] } + ttk::combobox $f.annotate.comboBooks -width 12 -values $tmp -textvariable ::annotation::options(AnalysisBookName) catch { $f.annotate.comboBooks current $idx } pack $f.annotate.blunderbox.label -side left -padx { 20 0 } pack $f.annotate.blunderbox.spBlunder -side left -anchor w @@ -85,30 +73,30 @@ namespace eval ::annotation { bind $w { .configAnnotation.f.buttons.ok invoke } ttk::labelframe $f.av -text $::tr(AnnotateWhich) - ttk::radiobutton $f.av.all -text $::tr(AnnotateAll) -variable ::annotation::annotateData(annotateMoves) -value all - ttk::radiobutton $f.av.white -text $::tr(AnnotateWhite) -variable ::annotation::annotateData(annotateMoves) -value white - ttk::radiobutton $f.av.black -text $::tr(AnnotateBlack) -variable ::annotation::annotateData(annotateMoves) -value black - ttk::checkbutton $f.av.vars -text "Store two variations" -variable ::annotation::annotateData(anzVariation) -onvalue 2 -offvalue 1 + ttk::radiobutton $f.av.all -text $::tr(AnnotateAll) -variable ::annotation::options(annotateMoves) -value all + ttk::radiobutton $f.av.white -text $::tr(AnnotateWhite) -variable ::annotation::options(annotateMoves) -value white + ttk::radiobutton $f.av.black -text $::tr(AnnotateBlack) -variable ::annotation::options(annotateMoves) -value black + ttk::checkbutton $f.av.vars -text "Store two variations" -variable ::annotation::options(anzVariation) -onvalue 2 -offvalue 1 pack $f.av.all $f.av.white $f.av.black $f.av.vars -side top -fill x -anchor w ttk::labelframe $f.comment -text $::tr(Comments) # Checkmark to enable all-move-scoring - ttk::checkbutton $f.comment.scoreAll -text $::tr(ScoreAllMoves) -variable ::annotation::annotateData(scoreAllMoves) - ttk::checkbutton $f.comment.cbShortAnnotation -text $::tr(ShortAnnotations) -variable ::annotation::annotateData(annotateShort) - ttk::checkbutton $f.comment.cbAddScore -text $::tr(AddScoreToShortAnnotations) -variable ::annotation::annotateData(addScoreToShortAnnotations) - ttk::checkbutton $f.comment.cbAddAnnotatorTag -text $::tr(addAnnotatorTag) -variable ::annotation::annotateData(addAnnotatorTag) - ttk::checkbutton $f.comment.cbMarkTactics -text $::tr(MarkTacticalExercises) -variable ::annotation::annotateData(tacticalExercises) + ttk::checkbutton $f.comment.scoreAll -text $::tr(ScoreAllMoves) -variable ::annotation::options(scoreAllMoves) + ttk::checkbutton $f.comment.cbShortAnnotation -text $::tr(ShortAnnotations) -variable ::annotation::options(annotateShort) + ttk::checkbutton $f.comment.cbAddScore -text $::tr(AddScoreToShortAnnotations) -variable ::annotation::options(addScoreToShortAnnotations) + ttk::checkbutton $f.comment.cbAddAnnotatorTag -text $::tr(addAnnotatorTag) -variable ::annotation::options(addAnnotatorTag) + ttk::checkbutton $f.comment.cbMarkTactics -text $::tr(MarkTacticalExercises) -variable ::annotation::options(tacticalExercises) pack $f.comment.scoreAll $f.comment.cbShortAnnotation $f.comment.cbAddScore \ $f.comment.cbAddAnnotatorTag $f.comment.cbMarkTactics -fill x -anchor w # batch annotation of consecutive games, and optional opening errors finder ttk::labelframe $f.batch -text "Batch Annotation" ttk::frame $f.buttons ttk::frame $f.running - ttk::label $f.running.line1 -textvariable ::annotation::annotateData(msg1) -width 60 - ttk::label $f.running.line2 -textvariable ::annotation::annotateData(msg2) -width 10 - ttk::label $f.running.line3 -textvariable ::annotation::annotateData(msg3) -width 10 - ttk::progressbar $f.running.progress -variable ::annotation::annotateData(progress) -orient horizontal -length 600 - ttk::progressbar $f.running.games -variable ::annotation::annotateData(games) -orient horizontal -length 600 + ttk::label $f.running.line1 -textvariable ::annotation::_Data(msg1) -width 60 + ttk::label $f.running.line2 -textvariable ::annotation::_Data(msg2) -width 10 + ttk::label $f.running.line3 -textvariable ::annotation::_Data(msg3) -width 10 + ttk::progressbar $f.running.progress -variable ::annotation::_Data(progress) -orient horizontal -length 600 + ttk::progressbar $f.running.games -variable ::annotation::options(games) -orient horizontal -length 600 grid $f.running.line1 -row 0 -column 1 -sticky w -pady { 0 10 } grid $f.running.line2 -row 1 -column 0 -sticky w grid $f.running.line3 -row 2 -column 0 -sticky w @@ -120,13 +108,13 @@ namespace eval ::annotation { grid $f.batch -row 1 -column 1 -pady { 10 0 } -sticky nswe -padx { 10 0 } grid $f.buttons -row 3 -column 1 -sticky we - set annotateData(batchEnd) [sc_base numGames $::curr_db] - if {$annotateData(batchEnd) <1} { set annotateData(batchEnd) 1 } - ttk::checkbutton $f.batch.cbBatch -text $::tr(AnnotateSeveralGames) -variable ::annotation::annotateData(batchMode) - ttk::spinbox $f.batch.spBatchEnd -width 8 -textvariable ::annotation::annotateData(batchEnd) \ - -from 1 -to $annotateData(batchEnd) -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } - ttk::checkbutton $f.batch.cbBatchOpening -text $::tr(FindOpeningErrors) -variable ::annotation::annotateData(OpeningErrors) - ttk::spinbox $f.batch.spBatchOpening -width 2 -textvariable ::annotation::annotateData(OpeningMoves) \ + set options(batchEnd) [sc_base numGames $::curr_db] + if {$options(batchEnd) <1} { set options(batchEnd) 1 } + ttk::checkbutton $f.batch.cbBatch -text $::tr(AnnotateSeveralGames) -variable ::annotation::options(batchMode) + ttk::spinbox $f.batch.spBatchEnd -width 8 -textvariable ::annotation::options(batchEnd) \ + -from 1 -to $options(batchEnd) -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } + ttk::checkbutton $f.batch.cbBatchOpening -text $::tr(FindOpeningErrors) -variable ::annotation::options(OpeningErrors) + ttk::spinbox $f.batch.spBatchOpening -width 2 -textvariable ::annotation::options(OpeningMoves) \ -from 10 -to 20 -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } ttk::label $f.batch.lBatchOpening -text $::tr(moves) pack $f.batch.cbBatch -side top -anchor w -pady { 0 0 } @@ -146,10 +134,10 @@ namespace eval ::annotation { } ttk::button $f.buttons.ok -text "Annotate" -command { if {$::annotateTime < 0.1} { set ::annotateTime 0.1 } - set annotateData(movetime) [expr {int($::annotateTime * 1000.0)}] - set annotateData(blunderThreshold) $::annotateBlunderThreshold - set annotateData(time) $::annotateTime - if { [::engineNoWin::initEngine annotateEngine $::annotation::annotateData(engine) \ + set ::annotation::options(movetime) [expr {int($::annotateTime * 1000.0)}] + set ::annotation::options(blunderThreshold) $::annotateBlunderThreshold + set ::annotation::options(time) $::annotateTime + if { [::engineNoWin::initEngine annotateEngine $::annotation::options(engine) \ [list ::annotation::eng_messages annotateEngine .annotationDialog.f.engpara]] } { ::annotation::runAnnotation } @@ -161,7 +149,7 @@ namespace eval ::annotation { # reset values for every game proc initGameAnnotation { } { - global ::annotation::annotateData + global ::annotation::options ::annotation::_Data #reset engine ::engine::send annotateEngine NewGame [list analysis post_pv post_wdl [sc_game variant]] # calc amount of moves to analyze for progressbar @@ -172,36 +160,36 @@ namespace eval ::annotation { sc_game pop .annotationDialog.f.running.progress configure -maximum $anz #reset values - set annotateData(prevscore1) 0 - set annotateData(prevscore2) 0 - set annotateData(score) 0 - set annotateData(scoremate) 0 - set annotateData(prevscoremate) 0 - set annotateData(prevmoves1) "" - set annotateData(prevmoves2) "" - set annotateData(moves) "" - set annotateData(progress) 1 - set annotateData(msg1) "$::tr(game) [sc_game number]: [sc_game info white] - [sc_game info black]" - set annotateData(msg2) "$::tr(game) $annotateData(games)" - set annotateData(msg3) "$::tr(move) 1" - if { $annotateData(addAnnotatorTag) } { - appendAnnotator "$annotateData(engine) $annotateData(typ) $annotateData($annotateData(typ))" + set _Data(prevscore1) 0 + set _Data(prevscore2) 0 + set _Data(score) 0 + set _Data(scoremate) 0 + set _Data(prevscoremate) 0 + set _Data(prevmoves1) "" + set _Data(prevmoves2) "" + set _Data(moves) "" + set _Data(progress) 1 + set _Data(msg1) "$::tr(game) [sc_game number]: [sc_game info white] - [sc_game info black]" + set _Data(msg2) "$::tr(game) $options(games)" + set _Data(msg3) "$::tr(move) 1" + if { $options(addAnnotatorTag) } { + appendAnnotator "$options(engine) $options(typ) $options($options(typ))" } } proc annotateGame { } { - global ::annotation::annotateData + global ::annotation::options ::annotation::_Data initGameAnnotation makeBookAnnotation # Annotate all remaining moves of the game while { 1 } { - set annotateData(PV1) [list "" "" ""] - set annotateData(PV2) [list "" "" ""] - ::engine::send annotateEngine Go [list [sc_game UCI_currentPos] [list $annotateData(typ) $annotateData($annotateData(typ))]] - vwait ::annotation::annotateData(move_done) + set _Data(PV1) [list "" "" ""] + set _Data(PV2) [list "" "" ""] + ::engine::send annotateEngine Go [list [sc_game UCI_currentPos] [list $options(typ) $options($options(typ))]] + vwait ::annotation::_Data(move_done) addAnnotation - incr annotateData(progress) - set annotateData(msg3) "$::tr(move) $annotateData(progress)" + incr _Data(progress) + set _Data(msg3) "$::tr(move) $_Data(progress)" if {[sc_pos isAt end] || ! $::autoplayMode } break sc_move forward ::notify::PosChanged -pgn @@ -209,31 +197,31 @@ namespace eval ::annotation { } proc runAnnotation { } { - global ::annotation::annotateData + global ::annotation::options # make sure, we have 2 best lines ::engine::send annotateEngine SetOptions [list {MultiPV 2}] set f .annotationDialog.f grid forget $f.annotate $f.comment $f.av $f.batch $f.optsannotateEngine pack forget $f.buttons.ok - if {!$annotateData(batchMode)} { grid forget $f.running.games $f.running.line2 } + if {!$options(batchMode)} { grid forget $f.running.games $f.running.line2 } # show progressbar and game infos - set annotateData(games) 1 + set options(games) 1 set gameNo [sc_game number] - $f.running.games configure -maximum [expr {$annotateData(batchEnd) - $gameNo + 1}] + $f.running.games configure -maximum [expr {$options(batchEnd) - $gameNo + 1}] grid $f.running -row 2 -column 0 -columnspan 2 -sticky we # tactical positions is selected, must be in multipv mode - if {$annotateData(tacticalExercises)} { ::engine::send annotateEngine SetOptions [list {MultiPV 4}] } + if {$options(tacticalExercises)} { ::engine::send annotateEngine SetOptions [list {MultiPV 4}] } set ::autoplayMode 1 set gameNo [sc_game number] if { $gameNo == 0 } { return } annotateGame - while {$annotateData(batchMode)} { + while {$options(batchMode)} { sc_game save $gameNo incr gameNo - incr annotateData(games) - if { ! $::autoplayMode || $gameNo > $annotateData(batchEnd) } { break } + incr options(games) + if { ! $::autoplayMode || $gameNo > $options(batchEnd) } { break } sc_game load $gameNo annotateGame } @@ -249,20 +237,20 @@ namespace eval ::annotation { # when going out of it ################################################################################ proc makeBookAnnotation { } { - global ::annotation::annotateData - if {$annotateData(useAnalysisBook)} { + global ::annotation::options ::annotation::_Data + if {$options(useAnalysisBook)} { set prevbookmoves "" - set bn [ file join $::scidBooksDir $annotateData(AnalysisBookName) ] - sc_book load $bn $annotateData(BookSlot) + set bn [ file join $::scidBooksDir $options(AnalysisBookName) ] + sc_book load $bn $_Data(BookSlot) - lassign [sc_book moves $annotateData(BookSlot)] bookmoves + lassign [sc_book moves $_Data(BookSlot)] bookmoves while {[string length $bookmoves] != 0 && ![sc_pos isAt vend]} { # we are in book, so move immediately forward ::move::Forward set prevbookmoves $bookmoves - lassign [sc_book moves $annotateData(BookSlot)] bookmoves + lassign [sc_book moves $_Data(BookSlot)] bookmoves } - sc_book close $annotateData(BookSlot) + sc_book close $_Data(BookSlot) if { [ string match -nocase "*[sc_game info previousMoveNT]*" $prevbookmoves ] != 1 } { if {$prevbookmoves != ""} { @@ -275,7 +263,7 @@ namespace eval ::annotation { } else { sc_pos setComment "[sc_pos getComment] $::tr(MoveOutOfBook)" } - if { $annotateData(OpeningErrors) && ([sc_pos moveNumber] < $annotateData(OpeningMoves) ) } { + if { $options(OpeningErrors) && ([sc_pos moveNumber] < $options(OpeningMoves) ) } { appendAnnotator "opBlunder [sc_pos moveNumber] ([sc_pos side])" } } @@ -314,7 +302,7 @@ namespace eval ::annotation { } proc addAnnotation { } { - global ::annotation::annotateData + global ::annotation::options ::annotation::_Data # Let's try to assess the situation: # We are here, now that the engine has analyzed the position reached by # our last move. Currently it is the opponent to move: @@ -322,9 +310,9 @@ namespace eval ::annotation { set gamemove [sc_game info previousMoveUCI] # And this is his best line: - lassign $annotateData(PV1) score score_type annotateData(moves) - if { $gamemove eq "" || $score eq "" } { set annotateData(prevscore1) $score; return } - set moves $annotateData(moves) + lassign $_Data(PV1) score score_type _Data(moves) + if { $gamemove eq "" || $score eq "" } { set _Data(prevscore1) $score; return } + set moves $_Data(moves) set bestMoveIsMate 0 if { $score_type eq "mate" } { # We do not want to insert a best-line variation into the game @@ -333,12 +321,12 @@ namespace eval ::annotation { # Sooner or later the game will deviate anyway; a variation at that point will # do nicely and is probably more accurate as well. set bestMoveIsMate 1 - set annotateData(scoremate) $score + set _Data(scoremate) $score set score [expr { $score < 0 ? -127 : 127 }] - set annotateData(score) $score + set _Data(score) $score } else { - set annotateData(score) $score - set annotateData(scoremate) 0 + set _Data(score) $score + set _Data(scoremate) 0 } # We will add a closing line at the end of variation or game @@ -348,7 +336,7 @@ namespace eval ::annotation { } # This is the score we could have had if we had played our best move - set prevscore $annotateData(prevscore1) + set prevscore $_Data(prevscore1) # Note that the engine's judgement is in relative terms, a negative score # being favorable to opponent, a positive score favorable to player @@ -366,7 +354,7 @@ namespace eval ::annotation { # Set an "isBlunder" filter. # Let's mark moves with a decay greater than the threshold. set isBlunder 0 - if { $deltamove > $annotateData(blunderThreshold) } { + if { $deltamove > $options(blunderThreshold) } { set isBlunder 2 } elseif { $deltamove > 0 } { set isBlunder 1 @@ -374,11 +362,11 @@ namespace eval ::annotation { set absdeltamove [expr { abs($deltamove) } ] # to parse scores if the engine's name contains - or + chars (see sc_game_scores) - set engine_name [string map {"-" " " "+" " "} $annotateData(engine)] + set engine_name [string map {"-" " " "+" " "} $options(engine)] # Prepare score strings for the opponent - if { $annotateData(scoremate) != 0 } { - set text [format "M%d" [expr abs($annotateData(scoremate))]] + if { $_Data(scoremate) != 0 } { + set text [format "M%d" [expr abs($_Data(scoremate))]] } else { set wscore [format "%+.2f" $score] if { $tomove eq "black" } {set wscore [expr 0.0 - $wscore] } @@ -388,10 +376,10 @@ namespace eval ::annotation { # See if we have the threshold filter activated. # If so, take only bad moves and missed mates until the position is lost anyway # Or that we must annotate all moves - if { ( $annotateData(annotateBlunders) == "blundersonly" + if { ( $options(annotateBlunders) == "blundersonly" && ($isBlunder > 1 || ($isBlunder > 0 && [expr abs($score)] >= 327.0)) && ! $gameIsLost) - || ($annotateData(annotateBlunders) == "allmoves") } { + || ($options(annotateBlunders) == "allmoves") } { if { $isBlunder > 0 } { # Add move score nag, and possibly an exercise if { $absdeltamove > $::informant("??") } { @@ -406,9 +394,9 @@ namespace eval ::annotation { } # Add score comment and engine name if needed - if { ! $annotateData(annotateShort) } { + if { ! $options(annotateShort) } { sc_pos setComment "[sc_pos getComment] $engine_name: $text" - } elseif { $annotateData(addScoreToShortAnnotations) || $annotateData(scoreAllMoves) } { + } elseif { $options(addScoreToShortAnnotations) || $options(scoreAllMoves) } { sc_pos setComment "[sc_pos getComment] $text" } @@ -416,37 +404,37 @@ namespace eval ::annotation { sc_pos addNag [scoreToNag $score] # Add the variation sc_move back - if { $annotateData(annotateBlunders) == "blundersonly" } { + if { $options(annotateBlunders) == "blundersonly" } { # Add a diagram tag, but avoid doubles if { [string first "D" "[sc_pos getNags]"] == -1 } { sc_pos addNag "D" } } - if { $annotateData(prevmoves1) != "" && ( $annotateData(annotateMoves) == "all" || - $annotateData(annotateMoves) == "white" && $tomove == "black" || - $annotateData(annotateMoves) == "black" && $tomove == "white" )} { + if { $_Data(prevmoves1) != "" && ( $options(annotateMoves) == "all" || + $options(annotateMoves) == "white" && $tomove == "black" || + $options(annotateMoves) == "black" && $tomove == "white" )} { set n 1 - while { $n <= $annotateData(anzVariation) && $annotateData(prevmoves$n) ne "" } { + while { $n <= $options(anzVariation) && $_Data(prevmoves$n) ne "" } { sc_var create # Add the starting move - sc_move addSan [lrange $annotateData(prevmoves$n) 0 0] + sc_move addSan [lrange $_Data(prevmoves$n) 0 0] # Add its score - if { ! $annotateData(annotateShort) || $annotateData(addScoreToShortAnnotations) } { + if { ! $options(annotateShort) || $options(addScoreToShortAnnotations) } { # And for the (missed?) chance - if { $annotateData(prevscoremate) != 0 } { - set prevtext [format "M%d" [expr abs($annotateData(prevscoremate))]] + if { $_Data(prevscoremate) != 0 } { + set prevtext [format "M%d" [expr abs($_Data(prevscoremate))]] } else { - set wprevscore [format "%+.2f" $annotateData(prevscore$n)] + set wprevscore [format "%+.2f" $_Data(prevscore$n)] if { $tomove eq "white" } {set wprevscore [expr 0.0 - $wprevscore] } set prevtext "\[%eval $wprevscore\]" } sc_pos setComment "$prevtext" } # Add remaining moves - sc_move addSan [lrange $annotateData(prevmoves$n) 1 end] + sc_move addSan [lrange $_Data(prevmoves$n) 1 end] # Add position NAG, unless the line ends in mate - if { $n == 1 && $annotateData(prevscoremate) == 0 } { + if { $n == 1 && $_Data(prevscoremate) == 0 } { sc_pos addNag [scoreToNag $prevscore] } sc_var exit @@ -458,7 +446,7 @@ namespace eval ::annotation { if { $isBlunder == 0 && $absdeltamove > $::informant("!?") } { sc_pos addNag "!?" } - if { $annotateData(scoreAllMoves) } { + if { $options(scoreAllMoves) } { # Add a score mark anyway sc_pos setComment "[sc_pos getComment] $text" } @@ -468,21 +456,21 @@ namespace eval ::annotation { sc_move back sc_var create sc_move addSan $gamemove - if { ($annotateData(scoremate) == 0) && ( ! $annotateData(annotateShort) || $annotateData(addScoreToShortAnnotations)) } { + if { ($_Data(scoremate) == 0) && ( ! $options(annotateShort) || $options(addScoreToShortAnnotations)) } { sc_pos setComment "$text" } sc_move addSan $moves - if { $annotateData(scoremate) == 0 } { + if { $_Data(scoremate) == 0 } { sc_pos addNag [scoreToNag $score] } sc_var exit # Now up to the end of the game ::move::Forward } - set annotateData(prevscore1) $annotateData(score) - set annotateData(prevmoves1) $annotateData(moves) - lassign $annotateData(PV2) annotateData(prevscore2) score_type annotateData(prevmoves2) - set annotateData(prevscoremate) $annotateData(scoremate) + set _Data(prevscore1) $_Data(score) + set _Data(prevmoves1) $_Data(moves) + lassign $_Data(PV2) _Data(prevscore2) score_type _Data(prevmoves2) + set _Data(prevscoremate) $_Data(scoremate) updateBoard -pgn } @@ -491,9 +479,9 @@ namespace eval ::annotation { # check at which depth the tactical shot is found ################################################################################ proc markExercise { prevscore score nag} { - global ::annotation::annotateData + global ::annotation::options ::annotation::_Data sc_pos addNag $nag - if { ! $annotateData(tacticalExercises)} { return 0 } + if { ! $options(tacticalExercises)} { return 0 } set deltamove [expr {$score + $prevscore}] # filter tactics so only those with high gains are kept @@ -505,7 +493,7 @@ namespace eval ::annotation { } # The best move is much better than others. - set sc2 [lindex $annotateData(PV2) 0] + set sc2 [lindex $_Data(PV2) 0] if { [expr abs( $score - $sc2 )] < 1.5 } { return 0 } # The best move does not lose position. @@ -513,10 +501,10 @@ namespace eval ::annotation { if {([sc_pos side] == "white") && ($score > $::informant("+/-")) } { return 0} # Move is not obvious: check that it is not the first move guessed at low depths - set pv [ lindex [ lindex $annotateData(PV1) 2 ] 0 ] + set pv [ lindex [ lindex $_Data(PV1) 2 ] 0 ] # bm0 must SAN, pv is UCI: convert set bm0 [string range [lindex $pv 0] 0 4] - set bm0 [sc_pos coordToSAN $annotateData(position) $bm0] + set bm0 [sc_pos coordToSAN $_Data(position) $bm0] set bm0 [string range $bm0 [expr [string first "." $bm0] + 1] end] foreach depth {1 2 3} { @@ -553,7 +541,7 @@ namespace eval ::annotation { } proc ::annotation::eng_messages {id w msg} { - global ::annotation::annotateData + global ::annotation::_Data lassign $msg msgType msgData switch $msgType { "InfoConfig" { @@ -564,14 +552,14 @@ namespace eval ::annotation { "InfoPV" { lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv if { $score_type ne "mate" } { set score [expr {$score / 100.0}] } - set annotateData(PV$multipv) [list $score $score_type $pv] + set _Data(PV$multipv) [list $score $score_type $pv] } "InfoBestMove" { - lassign $msgData annotateData(bestmove) - set annotateData(move_done) 1 + lassign $msgData _Data(bestmove) + set _Data(move_done) 1 } "InfoGo" { - lassign $msgData annotateData(position) + lassign $msgData _Data(position) } "InfoDisconnected" { ::engineNoWin::disconnected $id $msgData From 35b5e0a9e7176c370584b34759cd5ab5d5bc2f5b Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 4 Jun 2025 16:56:32 +0200 Subject: [PATCH 53/57] new proc ::engineNoWin::closeEngine --- tcl/tools/annotate.tcl | 6 ++---- tcl/tools/enginenowin.tcl | 6 +++++- tcl/tools/finishgame.tcl | 12 ++++-------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index ac8ad15b3..a5871c9ec 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -127,8 +127,7 @@ namespace eval ::annotation { if { $::autoplayMode } { set ::autoplayMode 0 } else { - catch { unset ::enginewin::engConfig_annotateEngine } - ::engine::close annotateEngine + ::engineNoWin::closeEngine annotateEngine destroy .annotationDialog } } @@ -226,8 +225,7 @@ namespace eval ::annotation { annotateGame } set ::autoplayMode 0 - unset ::enginewin::engConfig_annotateEngine - ::engine::close annotateEngine + ::engineNoWin::closeEngine annotateEngine ::notify::PosChanged -pgn destroy .annotationDialog } diff --git a/tcl/tools/enginenowin.tcl b/tcl/tools/enginenowin.tcl index 14a6cfa79..3befcf26f 100644 --- a/tcl/tools/enginenowin.tcl +++ b/tcl/tools/enginenowin.tcl @@ -11,7 +11,6 @@ namespace eval ::engineNoWin {} # Open the engine and configure it proc ::engineNoWin::initEngine { id engine callback } { if { [info exists ::enginewin::engConfig_$id] } { return 1 } -# tk_messageBox -title Scid -icon info -type ok -message "Only UCI-Engines are supported!" set config [::enginecfg::get $engine] lassign $config name cmd args wdir elo time url uci options set ::enginewin::engConfig_$id $config @@ -21,6 +20,11 @@ proc ::engineNoWin::initEngine { id engine callback } { return 1 } +proc ::engineNoWin::closeEngine { id } { + ::engine::close $id + unset -nocomplain ::enginewin::engConfig_$id +} + proc ::engineNoWin::changeEngine {id w enginevar callback} { ::engine::close $id $w.text configure -state normal diff --git a/tcl/tools/finishgame.tcl b/tcl/tools/finishgame.tcl index b392b3050..b3d541d7e 100644 --- a/tcl/tools/finishgame.tcl +++ b/tcl/tools/finishgame.tcl @@ -76,10 +76,8 @@ namespace eval ::finishgame { if { $::autoplayMode } { set ::autoplayMode 0 } else { - ::engine::close fgEnginewhite - ::engine::close fgEngineblack - catch { unset ::enginewin::engConfig_fgEnginewhite } - catch { unset ::enginewin::engConfig_fgEngineblack } + ::engineNoWin::closeEngine fgEnginewhite + ::engineNoWin::closeEngine fgEngineblack destroy .configFinishGame } } @@ -183,10 +181,8 @@ namespace eval ::finishgame { set ::autoplayMode 0 set tmp [sc_pos getComment] sc_pos setComment "$tmp\n\n$::tr(FinishGame) $::tr(White): $::finishGame(enginewhite) $::finishGame(cmdwhite) $::finishGame(cmdValuewhite)\n\n$::tr(Black): $::finishGame(engineblack) $::finishGame(cmdblack) $::finishGame(cmdValueblack)" - ::engine::close fgEnginewhite - ::engine::close fgEngineblack - catch { unset ::enginewin::engConfig_fgEnginewhite } - catch { unset ::enginewin::engConfig_fgEngineblack } + ::engineNoWin::closeEngine fgEnginewhite + ::engineNoWin::closeEngine fgEngineblack ::notify::PosChanged -pgn destroy .configFinishGame } From d981eb8b49ba911c1af594e882417be0973f36c7 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 4 Jun 2025 17:25:07 +0200 Subject: [PATCH 54/57] use ::engineNoWin::closeEngine --- tcl/tools/enginenowin.tcl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tcl/tools/enginenowin.tcl b/tcl/tools/enginenowin.tcl index 3befcf26f..24d52afb5 100644 --- a/tcl/tools/enginenowin.tcl +++ b/tcl/tools/enginenowin.tcl @@ -26,11 +26,10 @@ proc ::engineNoWin::closeEngine { id } { } proc ::engineNoWin::changeEngine {id w enginevar callback} { - ::engine::close $id + ::engineNoWin::closeEngine $id $w.text configure -state normal $w.text delete 1.0 end foreach wchild [winfo children $w.text] { destroy $wchild } - catch { unset ::enginewin::engConfig_$id } set engine [set $enginevar] ::engineNoWin::initEngine $id $engine [list $callback $id $w] } @@ -74,7 +73,7 @@ proc ::engineNoWin::createEngineOptionsFrame {f id var col callback {engTyp "uci grid $f.opts$id.l -row 0 -column 0 -sticky w grid $f.opts$id.x -row 0 -column 1 -sticky e grid $f.opts$id.save -row 2 -column 0 -columnspan 2 -sticky e -pady { 5 0 } - bind $f.$id "catch { unset ::enginewin::engConfig_$id }; ::engine::close $id" + bind $f.$id "::engineNoWin::closeEngine $id" } proc ::engineNoWin::initEngineOptions {id w options} { From 07dfea3c921d9ecf62829135bc898b96e123a777 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sat, 6 Sep 2025 22:30:19 +0200 Subject: [PATCH 55/57] Move infolists from global to local vars --- tcl/tools/annotate.tcl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index a5871c9ec..10b0ad0b0 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -565,15 +565,14 @@ namespace eval ::annotation { } } } - # Informant index strings - array set ana_informantList { 0 "+=" 1 "+/-" 2 "+-" 3 "+--" } - # Nags. Note the slight inconsistency for the "crushing" symbol (see game.cpp) - array set ana_nagList { 0 "=" 1 "+=" 2 "+/-" 3 "+-" 4 "+--" 5 "=" 6 "=+" 7 "-/+" 8 "-+" 9 "--+" } ################################################################################ # ################################################################################ proc scoreToNag {score} { - global ana_informantList ana_nagList + # Informant index strings + array set ana_informantList { 0 "+=" 1 "+/-" 2 "+-" 3 "+--" } + # Nags. Note the slight inconsistency for the "crushing" symbol (see game.cpp) + array set ana_nagList { 0 "=" 1 "+=" 2 "+/-" 3 "+-" 4 "+--" 5 "=" 6 "=+" 7 "-/+" 8 "-+" 9 "--+" } # Find the score in the informant map set tmp [expr { abs( $score ) }] for { set i 0 } { $i < 4 } { incr i } { From 09f3d0bbe152cd049349cc5d0a9b41f8dbacee0b Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 10 Sep 2025 21:40:31 +0200 Subject: [PATCH 56/57] annotate not saved new game --- tcl/tools/annotate.tcl | 1 - 1 file changed, 1 deletion(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 10b0ad0b0..80d8fa637 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -214,7 +214,6 @@ namespace eval ::annotation { set ::autoplayMode 1 set gameNo [sc_game number] - if { $gameNo == 0 } { return } annotateGame while {$options(batchMode)} { sc_game save $gameNo From 56d4413a74217b5a0122d31bcb7293e67680b404 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 7 Apr 2026 18:04:33 +0200 Subject: [PATCH 57/57] correct validation for 2 vars --- tcl/tools/annotate.tcl | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 80d8fa637..f667ddb5e 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -20,12 +20,6 @@ namespace eval ::annotation { return } - #Workaround for error in trace var for arrays - set ::annotateBlunderThreshold $options(blunderThreshold) - set ::annotateTime $options(time) - trace variable ::annotateBlunderThreshold w {::utils::validate::Regexp {^[0-9]*\.?[0-9]*$}} - trace variable ::annotateTime w {::utils::validate::Regexp {^[0-9]*\.?[0-9]*$}} - win::createDialog $w ::setTitle $w "Scid: $::tr(Annotate)" catch {grab $w} @@ -37,14 +31,15 @@ namespace eval ::annotation { ttk::frame $f.annotate.typ ttk::radiobutton $f.annotate.typ.label -text $::tr(AnnotateTime) -variable ::annotation::options(typ) -value "movetime" ttk::radiobutton $f.annotate.typ.ldepth -text "Depth per move" -variable ::annotation::options(typ) -value "depth" - ttk::spinbox $f.annotate.typ.spDelay -width 5 -textvariable ::annotateTime -from 0.1 -to 999 -validate key -justify right + ttk::spinbox $f.annotate.typ.spDelay -width 5 -textvariable ::annotation::options(time) -from 0.1 -to 999 \ + -validate key -justify right -validatecommand { regexp {^[0-9]*\.?[0-9]*$} %P } ttk::spinbox $f.annotate.typ.depth -width 5 -textvariable ::annotation::options(depth) -from 2 -to 999 -validate key -justify right ttk::radiobutton $f.annotate.allmoves -text $::tr(AnnotateAllMoves) -variable ::annotation::options(annotateBlunders) -value allmoves ttk::radiobutton $f.annotate.blundersonly -text $::tr(AnnotateBlundersOnly) -variable ::annotation::options(annotateBlunders) -value blundersonly ttk::frame $f.annotate.blunderbox ttk::label $f.annotate.blunderbox.label -text $::tr(BlundersThreshold:) - ttk::spinbox $f.annotate.blunderbox.spBlunder -width 4 -textvariable ::annotateBlunderThreshold \ - -from 0.1 -to 3.0 -increment 0.1 -justify right + ttk::spinbox $f.annotate.blunderbox.spBlunder -width 4 -validate key -textvariable ::annotation::options(blunderThreshold) \ + -from 0.1 -to 3.0 -increment 0.1 -justify right -validatecommand { regexp {^[0-9]*\.?[0-9]*$} %P } ttk::checkbutton $f.annotate.cbBook -text $::tr(UseBook) -variable ::annotation::options(useAnalysisBook) ::engineNoWin::createEngineOptionsFrame $f annotateEngine ::annotation::options(engine) 3 ::annotation::eng_messages @@ -132,10 +127,8 @@ namespace eval ::annotation { } } ttk::button $f.buttons.ok -text "Annotate" -command { - if {$::annotateTime < 0.1} { set ::annotateTime 0.1 } - set ::annotation::options(movetime) [expr {int($::annotateTime * 1000.0)}] - set ::annotation::options(blunderThreshold) $::annotateBlunderThreshold - set ::annotation::options(time) $::annotateTime + if {$::annotation::options(time) < 0.1} { set ::annotation::options(time) 0.1 } + set ::annotation::options(movetime) [expr {int($::annotation::options(time) * 1000.0)}] if { [::engineNoWin::initEngine annotateEngine $::annotation::options(engine) \ [list ::annotation::eng_messages annotateEngine .annotationDialog.f.engpara]] } { ::annotation::runAnnotation