From 436dc7ab3ee052df470d108dcb3bfcd0627d2a8d Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 26 Jan 2025 17:06:08 +0100 Subject: [PATCH 01/78] 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 e66623ef..d350ef1e 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 51cae61a..813f4e9a 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 00000000..17768c96 --- /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/78] 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 d350ef1e..1d3fbc40 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 17768c96..d53b1610 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/78] 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 d53b1610..c5ea4f4d 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/78] 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 c5ea4f4d..5658b2ce 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/78] 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 5658b2ce..1195581b 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/78] 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 1195581b..ff01cde0 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/78] 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 ff01cde0..522074e6 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/78] 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 522074e6..0fc77ba2 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/78] 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 0fc77ba2..8942045c 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/78] 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 8942045c..c7791dfa 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/78] 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 c7791dfa..5e1d3d3e 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/78] 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 5e1d3d3e..03ffa7d4 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/78] 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 03ffa7d4..ce26b84a 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/78] 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 ce26b84a..b9bb1704 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/78] 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 b9bb1704..15e31aba 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/78] 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 15e31aba..715b440a 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/78] 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 e66623ef..9fa59e80 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 51cae61a..cae4365c 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 00000000..1e16d6eb --- /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/78] 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 1e16d6eb..aae26845 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/78] 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 aae26845..e03c3955 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/78] 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 9fa59e80..e5ba83a5 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 e03c3955..3db67f5f 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/78] 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 715b440a..cf8586d0 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/78] 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 cf8586d0..bed3a9ce 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/78] 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 3db67f5f..f8e172df 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/78] 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 f8e172df..c8d540d5 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/78] 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 bed3a9ce..0b5ba2fe 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/78] 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 0b5ba2fe..b4c9719d 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/78] 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 c8d540d5..bee636eb 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/78] 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 b4c9719d..3388871c 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/78] 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 1e9851d7..3d0e1da8 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/78] 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 09449858..5e836651 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/78] 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 3d0e1da8..23dada89 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/78] 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 23dada89..3235d154 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/78] 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 10256342..b3c9cd8c 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 3235d154..2a442492 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/78] 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 2a442492..9a3d14fe 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/78] 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 3388871c..9376af31 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/78] 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 b3c9cd8c..d40c57d1 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 9a3d14fe..91f6be1e 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/78] 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 bee636eb..0b8ea1ee 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 91f6be1e..1afcbd67 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/78] ::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 0b8ea1ee..ec6865f7 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/78] 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 1afcbd67..b2dc71a0 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 c3c408fc144a73ba5e7fbcbc2229ef683f5fd560 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sat, 1 Mar 2025 18:42:40 +0100 Subject: [PATCH 40/78] Add first functions to give advice in serious game from second engine will later add tactical advice --- tcl/tools/annotate.tcl | 2 +- tcl/tools/sergame.tcl | 77 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/tcl/tools/annotate.tcl b/tcl/tools/annotate.tcl index 9376af31..fcb89570 100644 --- a/tcl/tools/annotate.tcl +++ b/tcl/tools/annotate.tcl @@ -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 4 -sticky nswe -padx 10 set engine [set $enginevar] ::engineNoWin::initEngine $id $engine [list $callback $id $w] } diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index b2dc71a0..cfebbe58 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -17,9 +17,12 @@ namespace eval sergame { set openingMoves "" set outOfOpening 0 set engineName "" + set coachName "" set bookSlot 2 set storeEval 0 - + set coachTypeMove 1 + set coachTypeTactic 1 + set useCoachEngine 1 # list of fen positions played to detect 3 fold repetition set lFen {} @@ -56,6 +59,16 @@ namespace eval sergame { # builds the list of UCI engines ::engineNoWin::createEngineOptionsFrame $w seriousEngine ::sergame::engineName 5 ::sergame::eng_messages pack $w.seriousEngine -in $w.fengines -side top -pady 5 -anchor w -padx 4 + # coach engine + ::engineNoWin::createEngineOptionsFrame $w coachEngine ::sergame::coachName 6 ::sergame::eng_messages + ttk::label $w.fengines.lcoach -text "Coaching" + ttk::frame $w.fengines.cb + ttk::checkbutton $w.fengines.cb.noCoach -text "use $::tr(Coachengine)" -variable ::sergame::useCoachEngine + ttk::checkbutton $w.fengines.cb.coach -text "Move" -variable ::sergame::coachTypeMove + ttk::checkbutton $w.fengines.cb.fullCoach -text "Tactical advice" -variable ::sergame::coachTypeTactic + pack $w.fengines.cb.noCoach $w.fengines.cb.coach $w.fengines.cb.fullCoach -side left -padx 4 + pack $w.fengines.lcoach $w.fengines.cb -side top -anchor w -padx 4 + pack $w.coachEngine -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 @@ -155,9 +168,6 @@ namespace eval sergame { ttk::checkbutton $w.fconfig.cbPonder -text $::tr(Ponder) -variable ::sergame::ponder pack $w.fconfig.cbPonder -side top -anchor w - # Warn if the user makes weak/bad moves - ttk::checkbutton $w.fconfig.cbCoach -text $::tr(CoachIsWatching) -variable ::sergame::coachIsWatching - pack $w.fconfig.cbCoach -side top -anchor w #Should the evaluation of the position stored in the comment? ttk::checkbutton $w.fconfig.storeEval -text $::tr(AddScoreToShortAnnotations) -variable ::sergame::storeEval pack $w.fconfig.storeEval -side top -anchor w @@ -289,6 +299,13 @@ namespace eval sergame { ::setPlayMode "::sergame::callback" ::notify::GameChanged + if { $::sergame::coachTypeTactic || $::sergame::useCoachEngine } { + set ::sergame::useCoachEngine 1 + set callback [list ::sergame::coachEng_messages coachEngine nop] + if { ! [::engineNoWin::initEngine coachEngine $::sergame::coachName $callback] } { + set ::sergame::useCoachEngine 0 + } + } clocks init clocks start @@ -296,6 +313,34 @@ namespace eval sergame { } 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" { + if { ! $::sergame::useCoachEngine } { + # no coach engine then use score from playing engine + lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv + if { $multipv == 1 } { + set ::sergame::data(score) [expr $score / 100.0] + } + } + } + "InfoBestMove" { + lassign $msgData ::sergame::data(bestmove) ponder ::sergame::data(ponder) + } + "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 + ::sergame::abortGame + } + } + } + proc ::sergame::coachEng_messages {id w msg} { lassign $msg msgType msgData switch $msgType { "InfoConfig" { @@ -306,14 +351,12 @@ 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::data(bestCoachmove) $pv set ::sergame::data(score) [expr $score / 100.0] } } "InfoBestMove" { - lassign $msgData ::sergame::data(bestmove) ponder ::sergame::data(ponder) - } - "InfoGo" { - lassign $msgData ::annotate(position) + lassign $msgData ::sergame::data(bestCoachmove) } "InfoDisconnected" { lassign $msgData errorMsg @@ -343,6 +386,11 @@ namespace eval sergame { ::engine::close seriousEngine unset ::enginewin::engConfig_seriousEngine set ::sergame::data(bestmove) "abort" + if { $::sergame::useCoachEngine } { + ::engine::send coachEngine StopGo + ::engine::close coachEngine + unset ::enginewin::engConfig_coachEngine + } ::notify::GameChanged } @@ -530,17 +578,24 @@ namespace eval sergame { } elseif {$timeMode == "nodes"} { set parameter "nodes $::sergame::data(fixednodes)" } - ::engine::send seriousEngine 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] + if { $::sergame::useCoachEngine } { + ::engine::send coachEngine Go [list "position fen [sc_pos fen]" "infinite"] + } } set ::sergame::data(bestmove) "" vwait ::sergame::data(bestmove) + if { $::sergame::useCoachEngine } { + ::engine::send coachEngine StopGo + } # ------------------------------------------------------------- # if weak move detected, propose the user to tack back - if { $::sergame::coachIsWatching && $::sergame::data(prevscore) != "" } { + if { $::sergame::coachTypeMove && $::sergame::data(prevscore) != "" } { set tBlunder "" set delta [expr $::sergame::data(score) - $::sergame::data(prevscore)] + if { [sc_pos side] != $::sergame::engineColor } { set delta [expr 0.0 - $delta] } if {$delta > $::informant("?!") } { set tBlunder "DubiousMovePlayedTakeBack" } if {$delta > $::informant("?") } { set tBlunder "WeakMovePlayedTakeBack" } if {$delta > $::informant("??") } { set tBlunder "BadMovePlayedTakeBack" } @@ -575,7 +630,7 @@ namespace eval sergame { clocks toggle - # ponder mode (the engine just played its move) ;&& $::sergame::data(ponder) != "" + # ponder mode (the engine just played its move) ;&& $::sergame::data(ponder) != "" if {$::sergame::ponder } { if {$timeMode == "timebonus"} { set wtime [expr [::gameclock::getSec 1] * 1000 ] From bfcfd62fa1c5abc2b123f52039c6fc9d6d355318 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 2 Mar 2025 12:33:30 +0100 Subject: [PATCH 41/78] show engine blunder in infobar for tactical advice --- tcl/tools/sergame.tcl | 71 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index cfebbe58..df99b714 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -23,6 +23,11 @@ namespace eval sergame { set coachTypeMove 1 set coachTypeTactic 1 set useCoachEngine 1 + set tacticBlunder "" + set tacTime 10 + set actTacTime 0 + set threshold 0.6 + set isLimitedAnalysisTime 1 # list of fen positions played to detect 3 fold repetition set lFen {} @@ -67,7 +72,15 @@ namespace eval sergame { ttk::checkbutton $w.fengines.cb.coach -text "Move" -variable ::sergame::coachTypeMove ttk::checkbutton $w.fengines.cb.fullCoach -text "Tactical advice" -variable ::sergame::coachTypeTactic pack $w.fengines.cb.noCoach $w.fengines.cb.coach $w.fengines.cb.fullCoach -side left -padx 4 - pack $w.fengines.lcoach $w.fengines.cb -side top -anchor w -padx 4 + ttk::frame $w.fengines.th + ttk::label $w.fengines.th.l -text $::tr(moveblunderthreshold) + ttk::spinbox $w.fengines.th.val -width 3 -from 0.4 -to 5.0 -increment 0.1 -textvariable ::sergame::threshold -validate all -validatecommand { regexp {^[0-9]\.[0-9]$} %P } + pack $w.fengines.th.l $w.fengines.th.val -side left -anchor w -padx 4 + ttk::frame $w.fengines.ad + ttk::checkbutton $w.fengines.ad.l -text $::tr(limitanalysis) -variable ::sergame::isLimitedAnalysisTime + ttk::spinbox $w.fengines.ad.val -width 3 -from 1 -to 360 -increment 1 -textvariable ::sergame::tacTime -validate all -validatecommand { regexp {^[0-9]$} %P } + pack $w.fengines.ad.l $w.fengines.ad.val -side left -anchor w -padx 4 + pack $w.fengines.lcoach $w.fengines.cb $w.fengines.th $w.fengines.ad -side top -anchor w -padx 4 pack $w.coachEngine -in $w.fengines -side top -pady 5 -anchor w -padx 4 # load book names @@ -292,6 +305,13 @@ namespace eval sergame { sc_game tags set -$::sergame::playerColor "Player" sc_game tags set -$::sergame::engineColor "$::sergame::engineName" sc_game tags set -date [::utils::date::today] + if { $::sergame::coachTypeMove || $::sergame::coachTypeTactic } { + set co "Coached Game: " + if { $::sergame::coachTypeMove } { append co "Bad Move Warning; " } + if { $::sergame::coachTypeTactic } { append co "Engine Blunder Information; " } + append co "Blunder Threshold: $::sergame::threshold " + sc_pos setComment $co + } } set ::sergame::waitPlayerMove 0 @@ -466,8 +486,42 @@ namespace eval sergame { if { [sc_pos side] != $::sergame::engineColor } { set ::sergame::waitPlayerMove 1 after 1000 ::sergame::engineGo + if { $::sergame::useCoachEngine && $::sergame::coachTypeTactic && $::sergame::actTacTime > 0 && $::sergame::data(prevscore) != "" } { + incr ::sergame::actTacTime -1 + if { $::sergame::isLimitedAnalysisTime && ! $::sergame::actTacTime } { + ::engine::send coachEngine StopGo + } else { + set ::sergame::tacticBlunder "" + set delta [expr $::sergame::data(score) + $::sergame::data(prevscore)] + if { [sc_pos side] == $::sergame::engineColor } { set delta [expr 0.0 - $delta] } + if { $delta >= $::sergame::threshold } { + if {$delta > $::informant("?!") } { set ::sergame::tacticBlunder "?!" } + if {$delta > $::informant("?") } { set ::sergame::tacticBlunder "?" } + if {$delta > $::informant("??") } { set ::sergame::tacticBlunder "??" } + if { $::sergame::tacticBlunder ne "" } { + if { $::sergame::engineColor eq "white" } { + set from $::sergame::data(prevscore) + set to [expr 0.0 - $::sergame::data(score)] + } else { + set from [expr 0.0 - $::sergame::data(prevscore)] + set to $::sergame::data(score) + } + ::board::setInfoAlert .main.board "Engine blunders: $::sergame::tacticBlunder" "$from -> $to" red {{*}$::playMode stop} + } + } + } + } return } + if { $::sergame::useCoachEngine } { + ::board::updateEvalBar .main.board "" + ::engine::send coachEngine StopGo + if { $::sergame::tacticBlunder ne "" } { + sc_move back + sc_pos addNag $::sergame::tacticBlunder + sc_move forward + } + } set takebackClockW "" set takebackClockB "" @@ -595,11 +649,12 @@ namespace eval sergame { if { $::sergame::coachTypeMove && $::sergame::data(prevscore) != "" } { set tBlunder "" set delta [expr $::sergame::data(score) - $::sergame::data(prevscore)] - if { [sc_pos side] != $::sergame::engineColor } { set delta [expr 0.0 - $delta] } - if {$delta > $::informant("?!") } { set tBlunder "DubiousMovePlayedTakeBack" } - if {$delta > $::informant("?") } { set tBlunder "WeakMovePlayedTakeBack" } - if {$delta > $::informant("??") } { set tBlunder "BadMovePlayedTakeBack" } - + if { [sc_pos side] != $::sergame::engineColor } { set delta [expr 0.0 - $delta] } + if { $delta >= $::sergame::threshold } { + if {$delta > $::informant("?!") } { set tBlunder "DubiousMovePlayedTakeBack" } + if {$delta > $::informant("?") } { set tBlunder "WeakMovePlayedTakeBack" } + if {$delta > $::informant("??") } { set tBlunder "BadMovePlayedTakeBack" } + } if {$tBlunder ne ""} { clocks stop set answer [tk_messageBox -icon question -parent .main -title "Scid" -type yesno -message $::tr($tBlunder) ] @@ -646,6 +701,10 @@ namespace eval sergame { ::engine::send seriousEngine Go [list "position fen [sc_pos fen] moves $::sergame::data(ponder)" $parameter] } + if { $::sergame::useCoachEngine } { + set ::sergame::actTacTime $::sergame::tacTime + ::engine::send coachEngine Go [list "position fen [sc_pos fen]" "infinite"] + } after 1000 ::sergame::engineGo } ################################################################################ From 3666104e585946eefcd0a4d41b69fa6ac76a893c Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 2 Mar 2025 14:29:19 +0100 Subject: [PATCH 42/78] store timecontrol --- tcl/tools/sergame.tcl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index df99b714..ef38f09b 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -305,7 +305,12 @@ namespace eval sergame { sc_game tags set -$::sergame::playerColor "Player" sc_game tags set -$::sergame::engineColor "$::sergame::engineName" sc_game tags set -date [::utils::date::today] + if {$::sergame::timeMode eq "timebonus"} { + sc_game tags set -extra [list "TimeControlWhite \"[expr $::sergame::data(wtime)/60000]+[expr $::sergame::data(winc)/1000]\"" \ + "TimeControlBlack \"[expr $::sergame::data(btime)/60000]+[expr $::sergame::data(binc)/1000]\""] + } if { $::sergame::coachTypeMove || $::sergame::coachTypeTactic } { + sc_game tags set -event "Coached game" set co "Coached Game: " if { $::sergame::coachTypeMove } { append co "Bad Move Warning; " } if { $::sergame::coachTypeTactic } { append co "Engine Blunder Information; " } @@ -506,7 +511,7 @@ namespace eval sergame { set from [expr 0.0 - $::sergame::data(prevscore)] set to $::sergame::data(score) } - ::board::setInfoAlert .main.board "Engine blunders: $::sergame::tacticBlunder" "$from -> $to" red {{*}$::playMode stop} + ::board::setInfoAlert .main.board "Engine blunders: $::sergame::tacticBlunder $from -> $to Playing..." [tr Stop] red {{*}$::playMode stop} } } } From 8e39c8773e954ace2494c6ffd6160caf6c283472 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 2 Mar 2025 16:55:53 +0100 Subject: [PATCH 43/78] rearrange config serious game dialog --- tcl/tools/sergame.tcl | 75 +++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index ef38f09b..0df5bd93 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -20,11 +20,11 @@ namespace eval sergame { set coachName "" set bookSlot 2 set storeEval 0 - set coachTypeMove 1 - set coachTypeTactic 1 - set useCoachEngine 1 + set coachTypeMove 0 + set coachTypeTactic 0 + set useCoachEngine 0 set tacticBlunder "" - set tacTime 10 + set tacTime 5 set actTacTime 0 set threshold 0.6 set isLimitedAnalysisTime 1 @@ -49,39 +49,52 @@ namespace eval sergame { bind $w { helpWindow SeriousGame } setWinLocation $w - ttk::frame $w.fconfig -padding 10 + ttk::frame $w.fconfig + ttk::frame $w.fconfig2 ttk::frame $w.fbuttons ttk::labelframe $w.fengines -text $::tr(Engine) + ttk::labelframe $w.coach -text "Coaching" 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 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 + grid $w.coach -row 0 -column 1 -pady { 0 10 } -sticky nswe -padx { 0 10 } + grid $w.fopening -row 1 -column 0 -pady { 0 10 } -sticky nswe -padx { 0 10 } + grid $w.ftime -row 1 -column 1 -pady { 0 10 } -sticky nswe -padx { 0 10 } + grid $w.fconfig -row 2 -column 0 -pady { 0 10 } -sticky we -padx { 0 10 } + grid $w.fconfig2 -row 2 -column 1 -pady { 0 10 } -sticky we -padx { 0 10 } + grid $w.fbuttons -row 3 -column 1 -sticky se # builds the list of UCI engines ::engineNoWin::createEngineOptionsFrame $w seriousEngine ::sergame::engineName 5 ::sergame::eng_messages + # ponder + ttk::checkbutton $w.fengines.ponder -text $::tr(Ponder) -variable ::sergame::ponder pack $w.seriousEngine -in $w.fengines -side top -pady 5 -anchor w -padx 4 + pack $w.fengines.ponder -side top -anchor w + # coach engine + ttk::frame $w.coach.en + ttk::checkbutton $w.coach.en.coach -text "$::tr(Engine)" -variable ::sergame::useCoachEngine + ::utils::tooltip::Set $w.coach.en.coach "Use a separate (strong) engine for coaching if the playing engine is weak." ::engineNoWin::createEngineOptionsFrame $w coachEngine ::sergame::coachName 6 ::sergame::eng_messages - ttk::label $w.fengines.lcoach -text "Coaching" - ttk::frame $w.fengines.cb - ttk::checkbutton $w.fengines.cb.noCoach -text "use $::tr(Coachengine)" -variable ::sergame::useCoachEngine - ttk::checkbutton $w.fengines.cb.coach -text "Move" -variable ::sergame::coachTypeMove - ttk::checkbutton $w.fengines.cb.fullCoach -text "Tactical advice" -variable ::sergame::coachTypeTactic - pack $w.fengines.cb.noCoach $w.fengines.cb.coach $w.fengines.cb.fullCoach -side left -padx 4 - ttk::frame $w.fengines.th - ttk::label $w.fengines.th.l -text $::tr(moveblunderthreshold) - ttk::spinbox $w.fengines.th.val -width 3 -from 0.4 -to 5.0 -increment 0.1 -textvariable ::sergame::threshold -validate all -validatecommand { regexp {^[0-9]\.[0-9]$} %P } - pack $w.fengines.th.l $w.fengines.th.val -side left -anchor w -padx 4 - ttk::frame $w.fengines.ad - ttk::checkbutton $w.fengines.ad.l -text $::tr(limitanalysis) -variable ::sergame::isLimitedAnalysisTime - ttk::spinbox $w.fengines.ad.val -width 3 -from 1 -to 360 -increment 1 -textvariable ::sergame::tacTime -validate all -validatecommand { regexp {^[0-9]$} %P } - pack $w.fengines.ad.l $w.fengines.ad.val -side left -anchor w -padx 4 - pack $w.fengines.lcoach $w.fengines.cb $w.fengines.th $w.fengines.ad -side top -anchor w -padx 4 - pack $w.coachEngine -in $w.fengines -side top -pady 5 -anchor w -padx 4 + pack $w.coach.en.coach -in $w.coach.en -side left -pady 5 -anchor w -padx 4 + pack $w.coachEngine -in $w.coach.en -side left -pady 5 -anchor w -padx 4 + ttk::frame $w.coach.cb + ttk::checkbutton $w.coach.cb.coach -text "Bad move warning" -variable ::sergame::coachTypeMove + ::utils::tooltip::Set $w.coach.cb.coach "Coach warns if player made a bad move. Player can take back this move." + ttk::checkbutton $w.coach.cb.fullCoach -text "Mark engine blunder" -variable ::sergame::coachTypeTactic \ + -command { if { $::sergame::coachTypeTactic } { set ::sergame::useCoachEngine 1 } } + ::utils::tooltip::Set $w.coach.cb.fullCoach "Gives a hint (in InfoBar) that engines has blundered. Needs coaching engine." + pack $w.coach.cb.coach $w.coach.cb.fullCoach -side left -padx 4 + ttk::frame $w.coach.th + ttk::label $w.coach.th.l -text $::tr(moveblunderthreshold) + ttk::spinbox $w.coach.th.val -width 3 -from 0.4 -to 5.0 -increment 0.1 -textvariable ::sergame::threshold -validate all -validatecommand { regexp {^[0-9]\.[0-9]$} %P } + pack $w.coach.th.l $w.coach.th.val -side left -anchor w -padx 4 + ttk::frame $w.coach.ad + ttk::checkbutton $w.coach.ad.l -text $::tr(limitanalysis) -variable ::sergame::isLimitedAnalysisTime + ttk::spinbox $w.coach.ad.val -width 3 -from 1 -to 360 -increment 1 -textvariable ::sergame::tacTime -validate all -validatecommand { regexp {^[0-9]$} %P } + pack $w.coach.ad.l $w.coach.ad.val -side left -anchor w -padx 4 + pack $w.coach.cb $w.coach.th $w.coach.en $w.coach.ad -side top -anchor w -padx 4 # load book names ttk::checkbutton $w.fconfig.cbUseBook -text $::tr(UseBook) -variable ::sergame::useBook @@ -174,16 +187,10 @@ namespace eval sergame { pack $w.fconfig.combo -side top -anchor w -padx 20 -fill x # New game or use current position ? - ttk::checkbutton $w.fconfig.cbPosition -text $::tr(StartFromCurrentPosition) -variable ::sergame::startFromCurrent - pack $w.fconfig.cbPosition -side top -anchor w - - # ponder - ttk::checkbutton $w.fconfig.cbPonder -text $::tr(Ponder) -variable ::sergame::ponder - pack $w.fconfig.cbPonder -side top -anchor w - + ttk::checkbutton $w.fconfig2.cbPosition -text $::tr(StartFromCurrentPosition) -variable ::sergame::startFromCurrent #Should the evaluation of the position stored in the comment? - ttk::checkbutton $w.fconfig.storeEval -text $::tr(AddScoreToShortAnnotations) -variable ::sergame::storeEval - pack $w.fconfig.storeEval -side top -anchor w + ttk::checkbutton $w.fconfig2.storeEval -text $::tr(AddScoreToShortAnnotations) -variable ::sergame::storeEval + pack $w.fconfig2.cbPosition $w.fconfig2.storeEval -side top -anchor w # choose a specific opening ttk::checkbutton $w.fopening.cbOpening -text $::tr(SpecificOpening) -variable ::sergame::isOpening From 01b558f957e719c4d6b9121a16b4ac46f7dc5830 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 5 Mar 2025 17:26:18 +0100 Subject: [PATCH 44/78] handle mate evaluation and correct evaluation on blunders --- tcl/tools/sergame.tcl | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 0df5bd93..1af4017f 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -28,6 +28,7 @@ namespace eval sergame { set actTacTime 0 set threshold 0.6 set isLimitedAnalysisTime 1 + set useBook 0 # list of fen positions played to detect 3 fold repetition set lFen {} @@ -358,6 +359,13 @@ namespace eval sergame { lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv if { $multipv == 1 } { set ::sergame::data(score) [expr $score / 100.0] + if { $score_type eq "mate" } { + if { $score > 0 } { + set ::sergame::data(score) 128.0 + } else { + set ::sergame::data(score) -128.0 + } + } } } } @@ -383,8 +391,15 @@ 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::data(bestCoachmove) $pv + set ::sergame::data(bestCoachmove) [lrange $pv 0 0] set ::sergame::data(score) [expr $score / 100.0] + if { $score_type eq "mate" } { + if { $score > 0 } { + set ::sergame::data(score) 128.0 + } else { + set ::sergame::data(score) -128.0 + } + } } } "InfoBestMove" { @@ -496,9 +511,11 @@ namespace eval sergame { if { [::sergame::endOfGame] } { return } if { [sc_pos side] != $::sergame::engineColor } { + # wait until player has moved set ::sergame::waitPlayerMove 1 after 1000 ::sergame::engineGo if { $::sergame::useCoachEngine && $::sergame::coachTypeTactic && $::sergame::actTacTime > 0 && $::sergame::data(prevscore) != "" } { + #check for engine blunder with coach engine incr ::sergame::actTacTime -1 if { $::sergame::isLimitedAnalysisTime && ! $::sergame::actTacTime } { ::engine::send coachEngine StopGo @@ -518,7 +535,8 @@ namespace eval sergame { set from [expr 0.0 - $::sergame::data(prevscore)] set to $::sergame::data(score) } - ::board::setInfoAlert .main.board "Engine blunders: $::sergame::tacticBlunder $from -> $to Playing..." [tr Stop] red {{*}$::playMode stop} + ::board::setInfoAlert .main.board "Engine blunders: $::sergame::tacticBlunder $from -> $to" "Show move" red \ + {::board::setInfoAlert .main.board "Try move $::sergame::data(bestCoachmove) Playing..." [tr Stop] red {{*}$::playMode stop}} } } } @@ -526,11 +544,16 @@ namespace eval sergame { return } if { $::sergame::useCoachEngine } { - ::board::updateEvalBar .main.board "" ::engine::send coachEngine StopGo if { $::sergame::tacticBlunder ne "" } { + # engine blundered, add nag and correct eval comment sc_move back sc_pos addNag $::sergame::tacticBlunder + if { $::sergame::storeEval == 1 } { + set score $::sergame::data(score) + if { $::sergame::engineColor eq "white" } { set score [expr 0.0 - $score] } + storeEvalComment $score + } sc_move forward } } From 0728f2158694832e7d1668e4d8cf46d90b75aeff Mon Sep 17 00:00:00 2001 From: Uwe Date: Sat, 15 Mar 2025 18:18:52 +0100 Subject: [PATCH 45/78] correct handling of coach engine: make sure a move and evaluation is always available. check for mate --- tcl/tools/sergame.tcl | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 1af4017f..781d42f9 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -261,7 +261,7 @@ namespace eval sergame { } set ::sergame::lFen {} - set ::sergame::data(prevscore) 0.0 + set ::sergame::data(prevscore) "" set ::sergame::data(score) 0.0 set ::sergame::data(ponder) "" @@ -494,7 +494,8 @@ namespace eval sergame { proc endOfGame {} { set move_done [sc_game info previousMove] if { [string index [sc_game info previousMove] end ] == "#"} { - clocks stop + tk_messageBox -type ok -message "This is Mate!" -parent .main -icon info + ::sergame::abortGame return 1 } return 0 @@ -518,6 +519,7 @@ namespace eval sergame { #check for engine blunder with coach engine incr ::sergame::actTacTime -1 if { $::sergame::isLimitedAnalysisTime && ! $::sergame::actTacTime } { + while { $::sergame::data(bestCoachmove) eq "" } { vwait ::sergame::data(bestCoachmove) } ::engine::send coachEngine StopGo } else { set ::sergame::tacticBlunder "" @@ -638,7 +640,7 @@ namespace eval sergame { sc_move addSan $move ::utils::sound::AnnounceNewMove $move # we made a book move so assume a score = 0 - set ::sergame::data(prevscore) 0.0 + set ::sergame::data(prevscore) "" clocks toggle updateBoard -pgn -animate if { ! [repetition] } { @@ -668,19 +670,21 @@ namespace eval sergame { set parameter "nodes $::sergame::data(fixednodes)" } ::engine::send seriousEngine Go [list "position fen [sc_pos fen]" $parameter] - if { $::sergame::useCoachEngine } { - ::engine::send coachEngine Go [list "position fen [sc_pos fen]" "infinite"] - } + } + if { $::sergame::useCoachEngine } { + set ::sergame::data(bestCoachmove) "" + ::engine::send coachEngine Go [list "position fen [sc_pos fen]" "infinite"] } set ::sergame::data(bestmove) "" vwait ::sergame::data(bestmove) if { $::sergame::useCoachEngine } { + while { $::sergame::data(bestCoachmove) eq "" } { vwait ::sergame::data(bestCoachmove) } ::engine::send coachEngine StopGo } # ------------------------------------------------------------- - # if weak move detected, propose the user to tack back + # if weak move detected, propose the user to take back if { $::sergame::coachTypeMove && $::sergame::data(prevscore) != "" } { set tBlunder "" set delta [expr $::sergame::data(score) - $::sergame::data(prevscore)] @@ -692,7 +696,7 @@ namespace eval sergame { } if {$tBlunder ne ""} { clocks stop - set answer [tk_messageBox -icon question -parent .main -title "Scid" -type yesno -message $::tr($tBlunder) ] + set answer [tk_messageBox -icon question -parent .main -title "Scid" -type yesno -message "$::tr($tBlunder)\n$::sergame::data(prevscore) -> $::sergame::data(score)" ] if {$answer == yes} { takeBack $takebackClockW $takebackClockB after 1000 ::sergame::engineGo @@ -738,6 +742,7 @@ namespace eval sergame { if { $::sergame::useCoachEngine } { set ::sergame::actTacTime $::sergame::tacTime + set ::sergame::data(bestCoachmove) "" ::engine::send coachEngine Go [list "position fen [sc_pos fen]" "infinite"] } after 1000 ::sergame::engineGo From e613638986abf70ae3353c51ffae0dbb4b5cfc86 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sat, 15 Mar 2025 18:20:53 +0100 Subject: [PATCH 46/78] save parameter for next game --- tcl/tools/sergame.tcl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 781d42f9..78cf91fd 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -231,6 +231,9 @@ namespace eval sergame { 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 ::sergame::depth [.configSerGameWin.ftime.depth.value get] + set ::sergame::nodes [expr [.configSerGameWin.ftime.nodes.value get]*1000] + set ::sergame::movetime [expr [.configSerGameWin.ftime.movetime.value get]*1000] set callback [list ::sergame::eng_messages seriousEngine nop] if { [::engineNoWin::initEngine seriousEngine $::sergame::engineName $callback] } { @@ -519,6 +522,7 @@ namespace eval sergame { #check for engine blunder with coach engine incr ::sergame::actTacTime -1 if { $::sergame::isLimitedAnalysisTime && ! $::sergame::actTacTime } { + # make sure we have a move and evaluation from coach engine while { $::sergame::data(bestCoachmove) eq "" } { vwait ::sergame::data(bestCoachmove) } ::engine::send coachEngine StopGo } else { @@ -679,6 +683,7 @@ namespace eval sergame { set ::sergame::data(bestmove) "" vwait ::sergame::data(bestmove) if { $::sergame::useCoachEngine } { + # make sure we have a move and evaluation from coach engine while { $::sergame::data(bestCoachmove) eq "" } { vwait ::sergame::data(bestCoachmove) } ::engine::send coachEngine StopGo } From 756ab52a4ae00a8685160d2fc3fc1a1ba7e63609 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sat, 15 Mar 2025 18:43:57 +0100 Subject: [PATCH 47/78] correct start of first move wth white on engine ponder --- tcl/tools/sergame.tcl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 78cf91fd..0f856adc 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -655,8 +655,7 @@ namespace eval sergame { } # ------------------------------------------------------------- # check if the engine pondered on the right move - - if { $::sergame::ponder && $::sergame::data(ponder) == [sc_game info previousMoveUCI]} { + if { $::sergame::ponder && $::sergame::data(ponder) ne "" && $::sergame::data(ponder) == [sc_game info previousMoveUCI]} { ::engine::rawsend seriousEngine "ponderhit" } else { if { $::sergame::ponder } { From 4cba1ab63237199ae5da72c4dd8256c7ab8887c7 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 16 Mar 2025 10:26:29 +0100 Subject: [PATCH 48/78] Set game result after the game --- tcl/tools/sergame.tcl | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 0f856adc..27d212a6 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -210,10 +210,10 @@ namespace eval sergame { $w.fopening.fOpeningList.lbOpening see $::sergame::chosenOpening ttk::scrollbar $w.fopening.fOpeningList.ybar -command "$w.fopening.fOpeningList.lbOpening yview" + pack $w.fopening.cbOpening -fill x -side top pack $w.fopening.fOpeningList.ybar -side right -fill y pack $w.fopening.fOpeningList.lbOpening -side left -fill both -expand 1 pack $w.fopening.fOpeningList -fill both -side top - pack $w.fopening.cbOpening -fill x -side top ttk::button $w.fbuttons.close -text $::tr(Play) -command { focus . @@ -427,6 +427,21 @@ namespace eval sergame { return 0 } + proc setResult {} { + set w .askResult + ::win::createDialog $w + wm resizable $w 0 0 + wm title $w "Scid: [tr Result]" + ttk::button $w.win -text " 1-0 " -command { sc_game tags set -result 1; destroy .askResult } + ttk::button $w.loss -text " 0-1 " -command { sc_game tags set -result 0; destroy .askResult } + ttk::button $w.draw -text "1/2-1/2" -command { sc_game tags set -result =; destroy .askResult } + ttk::button $w.undef -text " * " -command { sc_game tags set -result *; destroy .askResult } + pack $w.win $w.draw $w.loss $w.undef -side left -padx 10 + tk::PlaceWindow $w + grab $w + tkwait window $w + } + proc abortGame { } { ::setPlayMode "" after cancel ::sergame::engineGo @@ -441,6 +456,7 @@ namespace eval sergame { ::engine::close coachEngine unset ::enginewin::engConfig_coachEngine } + if { [sc_game tag get Result] eq "*" } { setResult } ::notify::GameChanged } @@ -495,9 +511,11 @@ namespace eval sergame { # returns true if last move is a mate and stops clocks ################################################################################ proc endOfGame {} { - set move_done [sc_game info previousMove] if { [string index [sc_game info previousMove] end ] == "#"} { tk_messageBox -type ok -message "This is Mate!" -parent .main -icon info + set result 0 + if { [sc_pos side] == "black" } { set result 1 } + sc_game tags set -result $result ::sergame::abortGame return 1 } @@ -759,6 +777,7 @@ namespace eval sergame { lassign [checkRepetition $::sergame::lFen] isRepetition ::sergame::lFen if { $isRepetition } { tk_messageBox -type ok -message $::tr(Draw) -parent .main -icon info + sc_game tags set -result = ::sergame::abortGame return 1 } From 53542c97597e6589c927ac74f8aea8b88d437722 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 17 Mar 2025 17:00:18 +0100 Subject: [PATCH 49/78] Play opening from start when selected and do not force asking a result at end of game --- tcl/tools/sergame.tcl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 27d212a6..6c8dfbb6 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -299,6 +299,9 @@ namespace eval sergame { if {[catch {sc_move addSan $m}]} { } lappend openingMovesHash [sc_pos hash] } + #goto start pos and clear the moves + sc_move start + sc_game truncate } # Engine plays for the upper side @@ -456,7 +459,7 @@ namespace eval sergame { ::engine::close coachEngine unset ::enginewin::engConfig_coachEngine } - if { [sc_game tag get Result] eq "*" } { setResult } + # if { [sc_game tag get Result] eq "*" } { setResult } ::notify::GameChanged } From 8dbd53427a027a677a484ad61c3774ceef40d44e Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 17 Mar 2025 17:37:21 +0100 Subject: [PATCH 50/78] procs checkBlunder and startEngine added for code optimisation --- tcl/tools/sergame.tcl | 96 ++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 52 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 6c8dfbb6..d2d3e96b 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -3,7 +3,7 @@ ### Copyright (C) 2007 Pascal Georges ### ################################################################################ -# The number used for the engine playing a serious game is 3 +# Use new engine interface for serious game and combine a coach engine ################################################################################ namespace eval sergame { @@ -524,6 +524,34 @@ namespace eval sergame { } return 0 } + + # start playing engine: ponder must be "" or "ponder" + proc startEngine { ponder } { + global ::sergame::timeMode + 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::data(winc) binc $::sergame::data(binc)" + } elseif {$timeMode == "depth"} { + set parameter "$ponder depth $::sergame::data(fixeddepth)" + } elseif {$timeMode == "movetime"} { + set parameter "$ponder movetime $::sergame::data(movetime)" + } elseif {$timeMode == "nodes"} { + set parameter "$ponder nodes $::sergame::data(fixednodes)" + } + if { $ponder ne "" } { set ponder "moves $::sergame::data(ponder)" } + ::engine::send seriousEngine Go [list "position fen [sc_pos fen] $ponder" $parameter] + } + + proc checkBlunder { delta } { + set ret "" + if { $delta >= $::sergame::threshold } { + if {$delta > $::informant("?!") } { set ret [list "?!" "DubiousMovePlayedTakeBack"] } + if {$delta > $::informant("?") } { set ret [list "?" "WeakMovePlayedTakeBack"] } + if {$delta > $::informant("??") } { set ret [list "??" "BadMovePlayedTakeBack"] } + } + return $ret + } ################################################################################ # ################################################################################ @@ -547,24 +575,19 @@ namespace eval sergame { while { $::sergame::data(bestCoachmove) eq "" } { vwait ::sergame::data(bestCoachmove) } ::engine::send coachEngine StopGo } else { - set ::sergame::tacticBlunder "" set delta [expr $::sergame::data(score) + $::sergame::data(prevscore)] if { [sc_pos side] == $::sergame::engineColor } { set delta [expr 0.0 - $delta] } - if { $delta >= $::sergame::threshold } { - if {$delta > $::informant("?!") } { set ::sergame::tacticBlunder "?!" } - if {$delta > $::informant("?") } { set ::sergame::tacticBlunder "?" } - if {$delta > $::informant("??") } { set ::sergame::tacticBlunder "??" } - if { $::sergame::tacticBlunder ne "" } { - if { $::sergame::engineColor eq "white" } { - set from $::sergame::data(prevscore) - set to [expr 0.0 - $::sergame::data(score)] - } else { - set from [expr 0.0 - $::sergame::data(prevscore)] - set to $::sergame::data(score) - } - ::board::setInfoAlert .main.board "Engine blunders: $::sergame::tacticBlunder $from -> $to" "Show move" red \ - {::board::setInfoAlert .main.board "Try move $::sergame::data(bestCoachmove) Playing..." [tr Stop] red {{*}$::playMode stop}} + lassign [checkBlunder $delta] ::sergame::tacticBlunder + if { $::sergame::tacticBlunder ne "" } { + if { $::sergame::engineColor eq "white" } { + set from $::sergame::data(prevscore) + set to [expr 0.0 - $::sergame::data(score)] + } else { + set from [expr 0.0 - $::sergame::data(prevscore)] + set to $::sergame::data(score) } + ::board::setInfoAlert .main.board "Engine blunders: $::sergame::tacticBlunder $from -> $to" "Show move" red \ + {::board::setInfoAlert .main.board "Try move $::sergame::data(bestCoachmove) Playing..." [tr Stop] red {{*}$::playMode stop}} } } } @@ -679,21 +702,8 @@ namespace eval sergame { if { $::sergame::ponder && $::sergame::data(ponder) ne "" && $::sergame::data(ponder) == [sc_game info previousMoveUCI]} { ::engine::rawsend seriousEngine "ponderhit" } else { - if { $::sergame::ponder } { - ::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::data(winc) binc $::sergame::data(binc)" - } elseif {$timeMode == "depth"} { - set parameter "depth $::sergame::data(fixeddepth)" - } elseif {$timeMode == "movetime"} { - set parameter "movetime $::sergame::data(movetime)" - } elseif {$timeMode == "nodes"} { - set parameter "nodes $::sergame::data(fixednodes)" - } - ::engine::send seriousEngine Go [list "position fen [sc_pos fen]" $parameter] + if { $::sergame::ponder } { ::engine::send seriousEngine StopGo } + startEngine "" } if { $::sergame::useCoachEngine } { set ::sergame::data(bestCoachmove) "" @@ -711,14 +721,9 @@ namespace eval sergame { # ------------------------------------------------------------- # if weak move detected, propose the user to take back if { $::sergame::coachTypeMove && $::sergame::data(prevscore) != "" } { - set tBlunder "" set delta [expr $::sergame::data(score) - $::sergame::data(prevscore)] if { [sc_pos side] != $::sergame::engineColor } { set delta [expr 0.0 - $delta] } - if { $delta >= $::sergame::threshold } { - if {$delta > $::informant("?!") } { set tBlunder "DubiousMovePlayedTakeBack" } - if {$delta > $::informant("?") } { set tBlunder "WeakMovePlayedTakeBack" } - if {$delta > $::informant("??") } { set tBlunder "BadMovePlayedTakeBack" } - } + lassign [checkBlunder $delta] nop tBlunder if {$tBlunder ne ""} { clocks stop set answer [tk_messageBox -icon question -parent .main -title "Scid" -type yesno -message "$::tr($tBlunder)\n$::sergame::data(prevscore) -> $::sergame::data(score)" ] @@ -749,21 +754,8 @@ namespace eval sergame { clocks toggle - # 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::data(winc) binc $::sergame::data(binc)" - } elseif {$timeMode == "depth"} { - set parameter "ponder depth $::sergame::data(fixeddepth)" - } elseif {$timeMode == "movetime"} { - set parameter "ponder movetime $::sergame::data(movetime)" - } elseif {$timeMode == "nodes"} { - set parameter "ponder nodes $::sergame::data(fixednodes)" - } - ::engine::send seriousEngine Go [list "position fen [sc_pos fen] moves $::sergame::data(ponder)" $parameter] - } + # ponder mode (the engine just played its move) + if {$::sergame::ponder } { startEngine ponder } if { $::sergame::useCoachEngine } { set ::sergame::actTacTime $::sergame::tacTime From ad7ae8fe6fa0a0105e81b5982d32c57c6c4aea41 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 17 Mar 2025 18:00:29 +0100 Subject: [PATCH 51/78] add proc checkEngineBlunder --- tcl/tools/sergame.tcl | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index d2d3e96b..ab1fc6d0 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -552,6 +552,23 @@ namespace eval sergame { } return $ret } + proc checkEngineBlunder { } { + set delta [expr $::sergame::data(score) + $::sergame::data(prevscore)] + if { [sc_pos side] == $::sergame::engineColor } { set delta [expr 0.0 - $delta] } + lassign [checkBlunder $delta] ::sergame::tacticBlunder + if { $::sergame::tacticBlunder ne "" } { + if { $::sergame::engineColor eq "white" } { + set from $::sergame::data(prevscore) + set to [expr 0.0 - $::sergame::data(score)] + } else { + set from [expr 0.0 - $::sergame::data(prevscore)] + set to $::sergame::data(score) + } + ::board::setInfoAlert .main.board "Engine blunders: $::sergame::tacticBlunder $from -> $to" "Show move" red \ + {::board::setInfoAlert .main.board "Try move $::sergame::data(bestCoachmove) Playing..." [tr Stop] red {{*}$::playMode stop}} + } + } + ################################################################################ # ################################################################################ @@ -575,24 +592,12 @@ namespace eval sergame { while { $::sergame::data(bestCoachmove) eq "" } { vwait ::sergame::data(bestCoachmove) } ::engine::send coachEngine StopGo } else { - set delta [expr $::sergame::data(score) + $::sergame::data(prevscore)] - if { [sc_pos side] == $::sergame::engineColor } { set delta [expr 0.0 - $delta] } - lassign [checkBlunder $delta] ::sergame::tacticBlunder - if { $::sergame::tacticBlunder ne "" } { - if { $::sergame::engineColor eq "white" } { - set from $::sergame::data(prevscore) - set to [expr 0.0 - $::sergame::data(score)] - } else { - set from [expr 0.0 - $::sergame::data(prevscore)] - set to $::sergame::data(score) - } - ::board::setInfoAlert .main.board "Engine blunders: $::sergame::tacticBlunder $from -> $to" "Show move" red \ - {::board::setInfoAlert .main.board "Try move $::sergame::data(bestCoachmove) Playing..." [tr Stop] red {{*}$::playMode stop}} - } + checkEngineBlunder } } return } + if { $::sergame::useCoachEngine } { ::engine::send coachEngine StopGo if { $::sergame::tacticBlunder ne "" } { From f62e23b15aa2079a2146bc59c5a91f95d534e1e8 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 19 Mar 2025 20:40:57 +0100 Subject: [PATCH 52/78] rename engineGo to playLoop --- tcl/tools/sergame.tcl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index ab1fc6d0..4acee136 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -348,7 +348,7 @@ namespace eval sergame { clocks init clocks start - ::sergame::engineGo + ::sergame::playLoop } proc ::sergame::eng_messages {id w msg} { @@ -447,7 +447,7 @@ namespace eval sergame { proc abortGame { } { ::setPlayMode "" - after cancel ::sergame::engineGo + after cancel ::sergame::playLoop clocks stop set ::sergame::lFen {} ::engine::send seriousEngine StopGo @@ -572,18 +572,18 @@ namespace eval sergame { ################################################################################ # ################################################################################ - proc engineGo { } { + proc playLoop { } { global ::sergame::isOpening ::sergame::openingMovesList ::sergame::openingMovesHash ::sergame::openingMoves \ ::sergame::timeMode ::sergame::outOfOpening - after cancel ::sergame::engineGo + after cancel ::sergame::playLoop if { [::sergame::endOfGame] } { return } if { [sc_pos side] != $::sergame::engineColor } { # wait until player has moved set ::sergame::waitPlayerMove 1 - after 1000 ::sergame::engineGo + after 1000 ::sergame::playLoop if { $::sergame::useCoachEngine && $::sergame::coachTypeTactic && $::sergame::actTacTime > 0 && $::sergame::data(prevscore) != "" } { #check for engine blunder with coach engine incr ::sergame::actTacTime -1 @@ -642,7 +642,7 @@ namespace eval sergame { -message "$::tr(NotFollowedLine) $openingMoves\n $::tr(DoYouWantContinue)" ] if {$answer == no} { takeBack $takebackClockW $takebackClockB - after 1000 ::sergame::engineGo + after 1000 ::sergame::playLoop return } else { set outOfOpening 1 @@ -677,7 +677,7 @@ namespace eval sergame { clocks toggle updateBoard -pgn -animate if { ! [repetition] } { - after 1000 ::sergame::engineGo + after 1000 ::sergame::playLoop } return } @@ -697,7 +697,7 @@ namespace eval sergame { clocks toggle updateBoard -pgn -animate if { ! [repetition] } { - after 1000 ::sergame::engineGo + after 1000 ::sergame::playLoop } return } @@ -734,7 +734,7 @@ namespace eval sergame { set answer [tk_messageBox -icon question -parent .main -title "Scid" -type yesno -message "$::tr($tBlunder)\n$::sergame::data(prevscore) -> $::sergame::data(score)" ] if {$answer == yes} { takeBack $takebackClockW $takebackClockB - after 1000 ::sergame::engineGo + after 1000 ::sergame::playLoop return } clocks start @@ -767,7 +767,7 @@ namespace eval sergame { set ::sergame::data(bestCoachmove) "" ::engine::send coachEngine Go [list "position fen [sc_pos fen]" "infinite"] } - after 1000 ::sergame::engineGo + after 1000 ::sergame::playLoop } ################################################################################ # add current position for 3fold repetition detection and returns 1 if From bfcd55195e03d438f997b7ee7477a5ca80ed9d32 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 6 Apr 2025 16:47:39 +0200 Subject: [PATCH 53/78] 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 9376af31..7b1dbfef 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 54/78] 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 ec6865f7..b392b305 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 55/78] 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 7b1dbfef..1815480d 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 56/78] 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 1815480d..66a23425 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 57/78] 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 66a23425..806c5f06 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 58/78] 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 806c5f06..5aa5ccef 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 59/78] 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 5aa5ccef..d520f1a9 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 277e483e88a2aa71c4f8b252dde5def82968c925 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 14 May 2025 09:06:51 +0200 Subject: [PATCH 60/78] engine init moved from dialog to proc play --- tcl/tools/sergame.tcl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 4acee136..9fd0e6c7 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -235,11 +235,8 @@ namespace eval sergame { set ::sergame::nodes [expr [.configSerGameWin.ftime.nodes.value get]*1000] set ::sergame::movetime [expr [.configSerGameWin.ftime.movetime.value get]*1000] - set callback [list ::sergame::eng_messages seriousEngine nop] - if { [::engineNoWin::initEngine seriousEngine $::sergame::engineName $callback] } { - destroy .configSerGameWin - ::sergame::play seriousEngine - } + destroy .configSerGameWin + ::sergame::play seriousEngine } ttk::button $w.fbuttons.cancel -textvar ::tr(Cancel) -command "focus .; destroy $w" @@ -259,6 +256,11 @@ namespace eval sergame { global ::sergame::chosenOpening ::sergame::isOpening ::tacgame::openingList ::sergame::openingMovesList \ ::sergame::openingMovesHash ::sergame::openingMoves ::sergame::outOfOpening + set callback [list ::sergame::eng_messages $engine nop] + if { ! [::engineNoWin::initEngine $engine $::sergame::engineName $callback] } { + tk_messageBox -title Scid -icon info -type ok -message "The UCI-Engines could not be started." + return + } if {$::sergame::isOpening || !$::sergame::startFromCurrent} { if {[::game::Clear] eq "cancel"} { return } } From b1f3f9ed4681aed607fe7f9c2b341c1ce7e838ff Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 14 May 2025 17:34:34 +0200 Subject: [PATCH 61/78] 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 d520f1a9..10bc27d7 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 a7405559ce026b549ea03d5177d7c0eb77a6b126 Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 16 May 2025 18:52:33 +0200 Subject: [PATCH 62/78] save all option for sergame --- tcl/options.tcl | 11 +++++++++++ tcl/tools/sergame.tcl | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tcl/options.tcl b/tcl/options.tcl index d40c57d1..6ef9f12f 100644 --- a/tcl/options.tcl +++ b/tcl/options.tcl @@ -339,6 +339,15 @@ set ::sergame::timeMode "timebonus" set ::sergame::depth 3 set ::sergame::movetime 1000 set ::sergame::nodes 10000 +set ::sergame::engineName "" +set ::sergame::coachName "" +set ::sergame::storeEval 0 +set ::sergame::coachTypeMove 0 +set ::sergame::coachTypeTactic 0 +set ::sergame::useCoachEngine 0 +set ::sergame::tacticBlunder "" +set ::sergame::threshold 0.6 +set ::sergame::tacTime 5 set ::sergame::ponder 0 set ::sergame::data(wtime) [expr 5 * 60 * 1000 ] set ::sergame::data(winc) [expr 10 * 1000 ] @@ -657,6 +666,8 @@ proc options.write {} { ::tacgame::isLimitedAnalysisTime ::tacgame::analysisTime ::tacgame::openingType ::tacgame::chosenOpening \ ::sergame::chosenOpening ::sergame::chosenEngine ::sergame::useBook ::sergame::bookToUse \ ::sergame::startFromCurrent ::sergame::coachIsWatching ::sergame::timeMode \ + ::sergame::storeEval ::sergame::coachTypeMove ::sergame::coachTypeTactic ::sergame::engineName \ + ::sergame::tacticBlunder ::sergame::threshold ::sergame::tacTime ::sergame::useCoachEngine \ ::sergame::depth ::sergame::movetime ::sergame::nodes ::sergame::ponder ::sergame::isOpening \ ::sergame::data(wtime) ::sergame::data(winc) ::sergame::data(btime) ::sergame::data(binc) \ boardfile_lite boardfile_dark \ diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 9fd0e6c7..caae5c18 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -14,21 +14,10 @@ namespace eval sergame { # if true, follow a specific opening set openingMovesList {} set openingMovesHash {} - set openingMoves "" set outOfOpening 0 - set engineName "" - set coachName "" set bookSlot 2 - set storeEval 0 - set coachTypeMove 0 - set coachTypeTactic 0 - set useCoachEngine 0 - set tacticBlunder "" - set tacTime 5 set actTacTime 0 - set threshold 0.6 set isLimitedAnalysisTime 1 - set useBook 0 # list of fen positions played to detect 3 fold repetition set lFen {} From ac028180bc7344b8dfaccff7bb9baa4402ffce07 Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 16 May 2025 22:18:15 +0200 Subject: [PATCH 63/78] tacgame removed tactical game and coached game is integrated in sergame --- tcl/lang/deutsch.tcl | 2 +- tcl/lang/english.tcl | 2 +- tcl/menus.tcl | 1 - tcl/options.tcl | 23 -------------- tcl/start.tcl | 2 +- tcl/tools/sergame.tcl | 70 +++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 70 insertions(+), 30 deletions(-) diff --git a/tcl/lang/deutsch.tcl b/tcl/lang/deutsch.tcl index 74b0ee0b..c8f6fa36 100644 --- a/tcl/lang/deutsch.tcl +++ b/tcl/lang/deutsch.tcl @@ -186,7 +186,7 @@ menuText D ToolsTracker "Figurenverteilung" 7 \ {Figurenverteilungsfenster öffnen} menuText D ToolsTraining "Training" 0 {Trainingswerkzeuge (Taktik, Eröffnungen,...) } menuText D ToolsTacticalGame "Trainingspartie" 0 {Trainingspartie spielen} -menuText D ToolsSeriousGame "Ernste Partie" 0 {Ernste Partie spielen} +menuText D ToolsSeriousGame "Partie spielen" 0 {Eine Partie gegen eine Engine spielen} menuText D ToolsTrainOpenings "Eröffnungen" 0 {Eröffnungsrepertoire trainieren} menuText D ToolsTrainReviewGame "Partie nachspielen" 0 {Finden von Zügen wie in vorgegebener Partie} menuText D ToolsTrainTactics "Taktik" 0 {Taktische Stellungen lösen} diff --git a/tcl/lang/english.tcl b/tcl/lang/english.tcl index 07bf3e09..b04f3e03 100644 --- a/tcl/lang/english.tcl +++ b/tcl/lang/english.tcl @@ -194,7 +194,7 @@ menuText E ToolsOpenRecentBaseAsTree "Open recent base as tree" 0 {Open a rece menuText E ToolsTracker "Piece Tracker" 6 {Open the Piece Tracker window} menuText E ToolsTraining "Training" 0 {Training tools (tactics, openings,...) } menuText E ToolsTacticalGame "Tactical game" 0 {Play a game with tactics} -menuText E ToolsSeriousGame "Serious game" 0 {Play a serious game} +menuText E ToolsSeriousGame "Play against engine" 0 {Play a game against an engine} menuText E ToolsTrainOpenings "Openings" 0 {Train with a repertoire} menuText E ToolsTrainReviewGame "Review game" 0 {Guess moves played in a game} menuText E ToolsTrainTactics "Tactics" 0 {Solve tactics} diff --git a/tcl/menus.tcl b/tcl/menus.tcl index e4140daa..efcd2a9d 100644 --- a/tcl/menus.tcl +++ b/tcl/menus.tcl @@ -213,7 +213,6 @@ set m .menu.play menu $m -postcommand "updateMenuStates $m" .menu add cascade -label Play -menu $m $m add command -label ToolsSeriousGame -command ::sergame::config -$m add command -label ToolsTacticalGame -command ::tacgame::config $m add command -label ToolsTrainFics -command ::fics::config $m add separator menu $m.training diff --git a/tcl/options.tcl b/tcl/options.tcl index 6ef9f12f..a5398929 100644 --- a/tcl/options.tcl +++ b/tcl/options.tcl @@ -234,24 +234,6 @@ set windowsDock 1 set ::tactics::analysisTime 3 -set ::tacgame::threshold 0.9 -set ::tacgame::blunderwarning false -set ::tacgame::blunderwarningvalue 0.0 -set ::tacgame::levelMin 1200 -set ::tacgame::levelMax 2200 -set ::tacgame::levelFixed 1500 -set ::tacgame::randomLevel 0 -set ::tacgame::isLimitedAnalysisTime 1 -set ::tacgame::showblunder 1 -set ::tacgame::showblundervalue 1 -set ::tacgame::showblunderfound 1 -set ::tacgame::showmovevalue 1 -set ::tacgame::showevaluation 1 -set ::tacgame::isLimitedAnalysisTime 1 -set ::tacgame::analysisTime 10 -set ::tacgame::openingType new -set ::tacgame::chosenOpening 0 - # Analysis command: to start chess analysis engine. set analysisCommand "" if {$windowsOS} { @@ -659,11 +641,6 @@ proc options.write {} { engineCoach1 engineCoach2 scidBooksDir scidBasesDir ::book::lastBook \ ::utils::sound::soundFolder ::utils::sound::announceNew \ ::utils::sound::announceForward ::utils::sound::announceBack \ - ::tacgame::threshold ::tacgame::blunderwarning ::tacgame::blunderwarningvalue \ - ::tacgame::levelMin ::tacgame::levelMax ::tacgame::levelFixed ::tacgame::randomLevel \ - ::tacgame::isLimitedAnalysisTime ::tacgame::showblunder ::tacgame::showblundervalue \ - ::tacgame::showblunderfound ::tacgame::showmovevalue ::tacgame::showevaluation \ - ::tacgame::isLimitedAnalysisTime ::tacgame::analysisTime ::tacgame::openingType ::tacgame::chosenOpening \ ::sergame::chosenOpening ::sergame::chosenEngine ::sergame::useBook ::sergame::bookToUse \ ::sergame::startFromCurrent ::sergame::coachIsWatching ::sergame::timeMode \ ::sergame::storeEval ::sergame::coachTypeMove ::sergame::coachTypeTactic ::sergame::engineName \ diff --git a/tcl/start.tcl b/tcl/start.tcl index 35db1c04..ce3e4f13 100644 --- a/tcl/start.tcl +++ b/tcl/start.tcl @@ -219,6 +219,7 @@ InitTooltip # initialized here, so that default values can be set up and # altered when the user options file is loaded. # +#ToDo remove tacgame from list, but prevent error from options.dat foreach ns { ::icon ::splash @@ -758,7 +759,6 @@ move.tcl main.tcl tools/uci.tcl end.tcl -tools/tacgame.tcl tools/sergame.tcl tools/calvar.tcl tools/fics.tcl diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index caae5c18..22ce2131 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -190,7 +190,7 @@ namespace eval sergame { $w.fopening.fOpeningList.lbOpening column 0 -width 250 $w.fopening.fOpeningList.lbOpening configure -height 5 set idx 0 - foreach o $::tacgame::openingList { + foreach o $::sergame::openingList { $w.fopening.fOpeningList.lbOpening insert {} end -id $idx -values [list $o] incr idx } @@ -242,7 +242,7 @@ namespace eval sergame { # ################################################################################ proc play { engine } { - global ::sergame::chosenOpening ::sergame::isOpening ::tacgame::openingList ::sergame::openingMovesList \ + global ::sergame::chosenOpening ::sergame::isOpening ::sergame::openingList ::sergame::openingMovesList \ ::sergame::openingMovesHash ::sergame::openingMoves ::sergame::outOfOpening set callback [list ::sergame::eng_messages $engine nop] @@ -782,7 +782,71 @@ namespace eval sergame { puts stdout "$n $text" } } - + ################################################################################ + # + ################################################################################ + set openingList [ list \ + "$::tr(Reti): 1.Nf3" \ + "$::tr(English): 1.c4" \ + "$::tr(d4Nf6Miscellaneous): 1.d4 Nf6" \ + "$::tr(Trompowsky): 1.d4 Nf6 2.Bg5" \ + "$::tr(Budapest): 1.d4 Nf6 2.c4 e5" \ + "$::tr(OldIndian): 1.d4 Nf6 2.c4 d6" \ + "$::tr(BenkoGambit): 1.d4 Nf6 2.c4 c5 3.d5 b5" \ + "$::tr(ModernBenoni): 1.d4 Nf6 2.c4 c5 3.d5 e6" \ + "$::tr(DutchDefence): 1.d4 f5" \ + "1.e4" \ + "$::tr(Scandinavian): 1.e4 d5" \ + "$::tr(AlekhineDefence): 1.e4 Nf6" \ + "$::tr(Pirc): 1.e4 d6" \ + "$::tr(CaroKann): 1.e4 c6" \ + "$::tr(CaroKannAdvance): 1.e4 c6 2.d4 d5 3.e5" \ + "$::tr(Sicilian): 1.e4 c5" \ + "$::tr(SicilianAlapin): 1.e4 c5 2.c3" \ + "$::tr(SicilianClosed): 1.e4 c5 2.Nc3" \ + "$::tr(Sicilian): 1.e4 c5 2.Nf3 Nc6" \ + "$::tr(Sicilian): 1.e4 c5 2.Nf3 e6" \ + "$::tr(SicilianRauzer): 1.e4 c5 2.Nf3 d6 3.d4 cxd4 4.Nxd4 Nf6 5.Nc3 Nc6" \ + "$::tr(SicilianDragon): 1.e4 c5 2.Nf3 d6 3.d4 cxd4 4.Nxd4 Nf6 5.Nc3 g6 " \ + "$::tr(SicilianScheveningen): 1.e4 c5 2.Nf3 d6 3.d4 cxd4 4.Nxd4 Nf6 5.Nc3 e6" \ + "$::tr(SicilianNajdorf): 1.e4 c5 2.Nf3 d6 3.d4 cxd4 4.Nxd4 Nf6 5.Nc3 a6" \ + "$::tr(OpenGame): 1.e4 e5" \ + "$::tr(Vienna): 1.e4 e5 2.Nc3" \ + "$::tr(KingsGambit): 1.e4 e5 2.f4" \ + "$::tr(RussianGame): 1.e4 e5 2.Nf3 Nf6" \ + "$::tr(OpenGame): 1.e4 e5 2.Nf3 Nc6" \ + "$::tr(ItalianTwoKnights): 1.e4 e5 2.Nf3 Nc6 3.Bc4" \ + "$::tr(Spanish): 1.e4 e5 2.Nf3 Nc6 3.Bb5" \ + "$::tr(SpanishExchange): 1.e4 e5 2.Nf3 Nc6 3.Bb5 a6 4.Bxc6" \ + "$::tr(SpanishOpen): 1.e4 e5 2.Nf3 Nc6 3.Bb5 a6 4.Ba4 Nf6 5.O-O Nxe4" \ + "$::tr(SpanishClosed): 1.e4 e5 2.Nf3 Nc6 3.Bb5 a6 4.Ba4 Nf6 5.O-O Be7" \ + "$::tr(FrenchDefence): 1.e4 e6" \ + "$::tr(FrenchAdvance): 1.e4 e6 2.d4 d5 3.e5" \ + "$::tr(FrenchTarrasch): 1.e4 e6 2.d4 d5 3.Nd2" \ + "$::tr(FrenchWinawer): 1.e4 e6 2.d4 d5 3.Nc3 Bb4" \ + "$::tr(FrenchExchange): 1.e4 e6 2.d4 d5 3.exd5 exd5" \ + "$::tr(QueensPawn): 1.d4 d5" \ + "$::tr(Slav): 1.d4 d5 2.c4 c6" \ + "$::tr(QGA): 1.d4 d5 2.c4 dxc4" \ + "$::tr(QGD): 1.d4 d5 2.c4 e6" \ + "$::tr(QGDExchange): 1.d4 d5 2.c4 e6 3.cxd5 exd5" \ + "$::tr(SemiSlav): 1.d4 d5 2.c4 e6 3.Nc3 Nf6 4.Nf3 c6" \ + "$::tr(QGDwithBg5): 1.d4 d5 2.c4 e6 3.Nc3 Nf6 4.Bg5" \ + "$::tr(QGDOrthodox): 1.d4 d5 2.c4 e6 3.Nc3 Nf6 4.Bg5 Be7 5.e3 O-O 6.Nf3 Nbd7" \ + "$::tr(Grunfeld): 1.d4 Nf6 2.c4 g6 3.Nc3 d5" \ + "$::tr(GrunfeldExchange): 1.d4 Nf6 2.c4 g6 3.Nc3 d5 4.cxd5" \ + "$::tr(GrunfeldRussian): 1.d4 Nf6 2.c4 g6 3.Nc3 d5 4.Nf3 Bg7 5.Qb3" \ + "$::tr(Catalan): 1.d4 Nf6 2.c4 e6 3.g3 " \ + "$::tr(CatalanOpen): 1.d4 Nf6 2.c4 e6 3.g3 d5 4.Bg2 dxc4" \ + "$::tr(CatalanClosed): 1.d4 Nf6 2.c4 e6 3.g3 d5 4.Bg2 Be7" \ + "$::tr(QueensIndian): 1.d4 Nf6 2.c4 e6 3.Nf3 b6" \ + "$::tr(NimzoIndian): 1.d4 Nf6 2.c4 e6 3.Nc3 Bb4" \ + "$::tr(NimzoIndianClassical): 1.d4 Nf6 2.c4 e6 3.Nc3 Bb4 4.Qc2" \ + "$::tr(NimzoIndianRubinstein): 1.d4 Nf6 2.c4 e6 3.Nc3 Bb4 4.e3" \ + "$::tr(KingsIndian): 1.d4 Nf6 2.c4 g6" \ + "$::tr(KingsIndianSamisch): 1.d4 Nf6 2.c4 g6 4.e4 d6 5.f3" \ + "$::tr(KingsIndianMainLine): 1.d4 Nf6 2.c4 g6 4.e4 d6 5.Nf3" \ + ] } ### ### End of file: sergame.tcl From 92b5cc40d214f3ed078cf6c2264e2ff9d4e87d25 Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 16 May 2025 23:57:47 +0200 Subject: [PATCH 64/78] 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 d40c57d1..50b52669 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 35db1c04..37d1d5f6 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 10bc27d7..2ac9f9b7 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 65/78] 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 2ac9f9b7..f4abf9ac 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 66/78] 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 37d1d5f6..f1da8539 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 f4abf9ac..cf97f021 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 00000000..14a6cfa7 --- /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 67/78] 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 cf97f021..815a8a46 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 e411f0bb..42fda924 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 0644d084a727b2999ac858bd6b2782ff88b276a5 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 18 May 2025 14:14:56 +0200 Subject: [PATCH 68/78] use new proc getBookList --- tcl/tools/sergame.tcl | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 22ce2131..f21b2a34 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -86,27 +86,15 @@ namespace eval sergame { pack $w.coach.ad.l $w.coach.ad.val -side left -anchor w -padx 4 pack $w.coach.cb $w.coach.th $w.coach.en $w.coach.ad -side top -anchor w -padx 4 - # load book names ttk::checkbutton $w.fconfig.cbUseBook -text $::tr(UseBook) -variable ::sergame::useBook - set bookPath $::scidBooksDir - set bookList [ lsort -dictionary [ glob -nocomplain -directory $bookPath *.bin ] ] - if { [llength $bookList] == 0 } { + # load book names + lassign [getBookList $::sergame::bookToUse] idx tmp + if { $idx < 0 } { $w.fconfig.cbUseBook configure -state disabled set ::sergame::useBook 0 } - set i 0 - set idx 0 - set tmp {} - foreach file $bookList { - lappend tmp [ file tail $file ] - if { $::sergame::bookToUse == [ file tail $file ]} { - set idx $i - } - incr i - } - ttk::combobox $w.fconfig.combo -width 12 -values $tmp - catch { ch$w.fconfig.combo current $idx } + catch { $w.fconfig.combo current $idx } set row 0 From 379fda8e1caf5f35290682cf55fc610246360a65 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 18 May 2025 14:16:59 +0200 Subject: [PATCH 69/78] debug logging removed --- tcl/tools/sergame.tcl | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index f21b2a34..3f8b9a23 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -8,9 +8,6 @@ namespace eval sergame { - # DEBUG - set ::sergame::data(log_stdout) 0 - # if true, follow a specific opening set openingMovesList {} set openingMovesHash {} @@ -765,14 +762,6 @@ namespace eval sergame { ################################################################################ # ################################################################################ - proc logEngine {n text} { - if {$::sergame::data(log_stdout)} { - puts stdout "$n $text" - } - } - ################################################################################ - # - ################################################################################ set openingList [ list \ "$::tr(Reti): 1.Nf3" \ "$::tr(English): 1.c4" \ From bba48ccf5fd35ca30a6b283f4ed94282567fb774 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 25 May 2025 11:25:55 +0200 Subject: [PATCH 70/78] unused var chosenEngine removed --- tcl/options.tcl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tcl/options.tcl b/tcl/options.tcl index 83c22cbd..5d31e747 100644 --- a/tcl/options.tcl +++ b/tcl/options.tcl @@ -320,7 +320,6 @@ set ::novag::referee "OFF" # Defaults for serious game training set ::sergame::isOpening 0 set ::sergame::chosenOpening 0 -set ::sergame::chosenEngine 0 set ::sergame::useBook 1 set ::sergame::bookToUse "" set ::sergame::startFromCurrent 0 @@ -649,7 +648,7 @@ proc options.write {} { engineCoach1 engineCoach2 scidBooksDir scidBasesDir ::book::lastBook \ ::utils::sound::soundFolder ::utils::sound::announceNew \ ::utils::sound::announceForward ::utils::sound::announceBack \ - ::sergame::chosenOpening ::sergame::chosenEngine ::sergame::useBook ::sergame::bookToUse \ + ::sergame::chosenOpening ::sergame::useBook ::sergame::bookToUse \ ::sergame::startFromCurrent ::sergame::coachIsWatching ::sergame::timeMode \ ::sergame::storeEval ::sergame::coachTypeMove ::sergame::coachTypeTactic ::sergame::engineName \ ::sergame::tacticBlunder ::sergame::threshold ::sergame::tacTime ::sergame::useCoachEngine \ From a8b195b43caa723ac50614a7fbd6e4f254a48c27 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 25 May 2025 14:29:22 +0200 Subject: [PATCH 71/78] 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 50b52669..504253c2 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 815a8a46..ac8ad15b 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 dcd9cefbcb9e07cb66ff0853544acdf6e8809f51 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sun, 25 May 2025 16:48:21 +0200 Subject: [PATCH 72/78] use array var for options and internal _data for sergame data save only sergame::options --- tcl/options.tcl | 57 +++---- tcl/tools/sergame.tcl | 387 +++++++++++++++++++++--------------------- 2 files changed, 224 insertions(+), 220 deletions(-) diff --git a/tcl/options.tcl b/tcl/options.tcl index 5d31e747..dd51103a 100644 --- a/tcl/options.tcl +++ b/tcl/options.tcl @@ -318,30 +318,30 @@ set ::pinfo::dnburl "http://d-nb.info/gnd" set ::novag::referee "OFF" # Defaults for serious game training -set ::sergame::isOpening 0 -set ::sergame::chosenOpening 0 -set ::sergame::useBook 1 -set ::sergame::bookToUse "" -set ::sergame::startFromCurrent 0 -set ::sergame::coachIsWatching 0 -set ::sergame::timeMode "timebonus" -set ::sergame::depth 3 -set ::sergame::movetime 1000 -set ::sergame::nodes 10000 -set ::sergame::engineName "" -set ::sergame::coachName "" -set ::sergame::storeEval 0 -set ::sergame::coachTypeMove 0 -set ::sergame::coachTypeTactic 0 -set ::sergame::useCoachEngine 0 -set ::sergame::tacticBlunder "" -set ::sergame::threshold 0.6 -set ::sergame::tacTime 5 -set ::sergame::ponder 0 -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 ] +set ::sergame::options(isOpening) 0 +set ::sergame::options(chosenOpening) 0 +set ::sergame::options(useBook) 1 +set ::sergame::options(bookToUse) "" +set ::sergame::options(startFromCurrent) 0 +set ::sergame::options(coachIsWatching) 0 +set ::sergame::options(timeMode) "timebonus" +set ::sergame::options(depth) 3 +set ::sergame::options(movetime) 1000 +set ::sergame::options(nodes) 10000 +set ::sergame::options(engineName) "" +set ::sergame::options(coachName) "" +set ::sergame::options(storeEval) 0 +set ::sergame::options(coachTypeMove) 0 +set ::sergame::options(coachTypeTactic) 0 +set ::sergame::options(useCoachEngine) 0 +set ::sergame::options(threshold) 0.6 +set ::sergame::options(tacTime) 5 +set ::sergame::options(ponder) 0 +set ::sergame::options(isLimitedAnalysisTime) 1 +set ::sergame::options(wtime) [expr 5 * 60 * 1000 ] +set ::sergame::options(winc) [expr 10 * 1000 ] +set ::sergame::options(btime) [expr 5 * 60 * 1000 ] +set ::sergame::options(binc) [expr 10 * 1000 ] # Defaults for initial directories: set initialDir(base) "." @@ -648,17 +648,14 @@ proc options.write {} { engineCoach1 engineCoach2 scidBooksDir scidBasesDir ::book::lastBook \ ::utils::sound::soundFolder ::utils::sound::announceNew \ ::utils::sound::announceForward ::utils::sound::announceBack \ - ::sergame::chosenOpening ::sergame::useBook ::sergame::bookToUse \ - ::sergame::startFromCurrent ::sergame::coachIsWatching ::sergame::timeMode \ - ::sergame::storeEval ::sergame::coachTypeMove ::sergame::coachTypeTactic ::sergame::engineName \ - ::sergame::tacticBlunder ::sergame::threshold ::sergame::tacTime ::sergame::useCoachEngine \ - ::sergame::depth ::sergame::movetime ::sergame::nodes ::sergame::ponder ::sergame::isOpening \ - ::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 } { puts $optionF "set $i [list [set $i]]" } + foreach i [lsort [array names ::sergame::options]] { + puts $optionF "set ::sergame::options($i) [list $::sergame::options($i)]" + } foreach i [lsort [array names ::annotation::annotateData]] { puts $optionF "set ::annotation::annotateData($i) [list $::annotation::annotateData($i)]" } diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 3f8b9a23..05e7ea8a 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -9,20 +9,20 @@ namespace eval sergame { # if true, follow a specific opening - set openingMovesList {} - set openingMovesHash {} - set outOfOpening 0 - set bookSlot 2 - set actTacTime 0 - set isLimitedAnalysisTime 1 + set _Data(openingMovesList) {} + set _Data(openingMovesHash) {} + set _Data(outOfOpening) 0 + set _Data(bookSlot) 2 + set _Data(tacticBlunder) "" + set _Data(actTacTime) 0 # list of fen positions played to detect 3 fold repetition - set lFen {} + set _Data(lFen) {} ################################################################################ # ################################################################################ proc config {} { - global ::sergame::configWin ::sergame::chosenOpening + global ::sergame::_Data ::sergame::options ::sergame::configWin set w ".configSerGameWin" if {[winfo exists $w]} { @@ -53,42 +53,42 @@ namespace eval sergame { grid $w.fbuttons -row 3 -column 1 -sticky se # builds the list of UCI engines - ::engineNoWin::createEngineOptionsFrame $w seriousEngine ::sergame::engineName 5 ::sergame::eng_messages + ::engineNoWin::createEngineOptionsFrame $w seriousEngine ::sergame::options(engineName) 5 ::sergame::eng_messages # ponder - ttk::checkbutton $w.fengines.ponder -text $::tr(Ponder) -variable ::sergame::ponder + ttk::checkbutton $w.fengines.ponder -text $::tr(Ponder) -variable ::sergame::options(ponder) pack $w.seriousEngine -in $w.fengines -side top -pady 5 -anchor w -padx 4 pack $w.fengines.ponder -side top -anchor w # coach engine ttk::frame $w.coach.en - ttk::checkbutton $w.coach.en.coach -text "$::tr(Engine)" -variable ::sergame::useCoachEngine + ttk::checkbutton $w.coach.en.coach -text "$::tr(Engine)" -variable ::sergame::options(useCoachEngine) ::utils::tooltip::Set $w.coach.en.coach "Use a separate (strong) engine for coaching if the playing engine is weak." - ::engineNoWin::createEngineOptionsFrame $w coachEngine ::sergame::coachName 6 ::sergame::eng_messages + ::engineNoWin::createEngineOptionsFrame $w coachEngine ::sergame::options(coachName) 6 ::sergame::eng_messages pack $w.coach.en.coach -in $w.coach.en -side left -pady 5 -anchor w -padx 4 pack $w.coachEngine -in $w.coach.en -side left -pady 5 -anchor w -padx 4 ttk::frame $w.coach.cb - ttk::checkbutton $w.coach.cb.coach -text "Bad move warning" -variable ::sergame::coachTypeMove + ttk::checkbutton $w.coach.cb.coach -text "Bad move warning" -variable ::sergame::options(coachTypeMove) ::utils::tooltip::Set $w.coach.cb.coach "Coach warns if player made a bad move. Player can take back this move." - ttk::checkbutton $w.coach.cb.fullCoach -text "Mark engine blunder" -variable ::sergame::coachTypeTactic \ - -command { if { $::sergame::coachTypeTactic } { set ::sergame::useCoachEngine 1 } } + ttk::checkbutton $w.coach.cb.fullCoach -text "Mark engine blunder" -variable ::sergame::options(coachTypeTactic) \ + -command { if { $::sergame::options(coachTypeTactic) } { set ::sergame::options(useCoachEngine) 1 } } ::utils::tooltip::Set $w.coach.cb.fullCoach "Gives a hint (in InfoBar) that engines has blundered. Needs coaching engine." pack $w.coach.cb.coach $w.coach.cb.fullCoach -side left -padx 4 ttk::frame $w.coach.th ttk::label $w.coach.th.l -text $::tr(moveblunderthreshold) - ttk::spinbox $w.coach.th.val -width 3 -from 0.4 -to 5.0 -increment 0.1 -textvariable ::sergame::threshold -validate all -validatecommand { regexp {^[0-9]\.[0-9]$} %P } + ttk::spinbox $w.coach.th.val -width 3 -from 0.4 -to 5.0 -increment 0.1 -textvariable ::sergame::options(threshold) -validate all -validatecommand { regexp {^[0-9]\.[0-9]$} %P } pack $w.coach.th.l $w.coach.th.val -side left -anchor w -padx 4 ttk::frame $w.coach.ad - ttk::checkbutton $w.coach.ad.l -text $::tr(limitanalysis) -variable ::sergame::isLimitedAnalysisTime - ttk::spinbox $w.coach.ad.val -width 3 -from 1 -to 360 -increment 1 -textvariable ::sergame::tacTime -validate all -validatecommand { regexp {^[0-9]$} %P } + ttk::checkbutton $w.coach.ad.l -text $::tr(limitanalysis) -variable ::sergame::options(isLimitedAnalysisTime) + ttk::spinbox $w.coach.ad.val -width 3 -from 1 -to 360 -increment 1 -textvariable ::sergame::options(tacTime) -validate all -validatecommand { regexp {^[0-9]$} %P } pack $w.coach.ad.l $w.coach.ad.val -side left -anchor w -padx 4 pack $w.coach.cb $w.coach.th $w.coach.en $w.coach.ad -side top -anchor w -padx 4 - ttk::checkbutton $w.fconfig.cbUseBook -text $::tr(UseBook) -variable ::sergame::useBook + ttk::checkbutton $w.fconfig.cbUseBook -text $::tr(UseBook) -variable ::sergame::options(useBook) # load book names - lassign [getBookList $::sergame::bookToUse] idx tmp + lassign [getBookList $options(bookToUse)] idx tmp if { $idx < 0 } { $w.fconfig.cbUseBook configure -state disabled - set ::sergame::useBook 0 + set options(useBook) 0 } ttk::combobox $w.fconfig.combo -width 12 -values $tmp catch { $w.fconfig.combo current $idx } @@ -99,7 +99,7 @@ namespace eval sergame { ttk::frame $w.ftime.timebonus pack $w.ftime.timebonus -side top -fill x - ttk::radiobutton $w.ftime.timebonus.rb1 -text $::tr(TimeBonus) -value "timebonus" -variable ::sergame::timeMode + ttk::radiobutton $w.ftime.timebonus.rb1 -text $::tr(TimeBonus) -value "timebonus" -variable ::sergame::options(timeMode) grid $w.ftime.timebonus.rb1 -row $row -column 0 -sticky w -rowspan 2 ttk::label $w.ftime.timebonus.whitelabel -text $::tr(White) @@ -125,34 +125,34 @@ 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::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 ] + $w.ftime.timebonus.whitespminutes set [expr $options(wtime) / (60 * 1000)] + $w.ftime.timebonus.whitespseconds set [expr $options(winc) / 1000] + $w.ftime.timebonus.blackspminutes set [expr $options(btime) / (60 * 1000)] + $w.ftime.timebonus.blackspseconds set [expr $options(binc) / 1000 ] # Fixed depth ttk::frame $w.ftime.depth - ttk::radiobutton $w.ftime.depth.button -text $::tr(FixedDepth) -value "depth" -variable ::sergame::timeMode -width 16 + ttk::radiobutton $w.ftime.depth.button -text $::tr(FixedDepth) -value "depth" -variable ::sergame::options(timeMode) -width 16 ttk::spinbox $w.ftime.depth.value -background white -width 3 -from 1 -to 20 -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } - $w.ftime.depth.value set $::sergame::depth + $w.ftime.depth.value set $options(depth) pack $w.ftime.depth -side top -fill x pack $w.ftime.depth.button -side left pack $w.ftime.depth.value -side left ttk::frame $w.ftime.nodes - ttk::radiobutton $w.ftime.nodes.button -text "$::tr(Nodes) (x1000)" -value "nodes" -variable ::sergame::timeMode -width 16 + ttk::radiobutton $w.ftime.nodes.button -text "$::tr(Nodes) (x1000)" -value "nodes" -variable ::sergame::options(timeMode) -width 16 ttk::spinbox $w.ftime.nodes.value -background white -width 3 -from 5 -to 10000 -increment 5 -validate all -validatecommand { regexp {^[0-9]+$} %P } - $w.ftime.nodes.value set [ expr $::sergame::nodes /1000] + $w.ftime.nodes.value set [ expr $options(nodes) /1000] pack $w.ftime.nodes -side top -fill x pack $w.ftime.nodes.button -side left pack $w.ftime.nodes.value -side left ttk::frame $w.ftime.movetime - ttk::radiobutton $w.ftime.movetime.button -text $::tr(SecondsPerMove) -value "movetime" -variable ::sergame::timeMode -width 16 + ttk::radiobutton $w.ftime.movetime.button -text $::tr(SecondsPerMove) -value "movetime" -variable ::sergame::options(timeMode) -width 16 ttk::spinbox $w.ftime.movetime.value -background white -width 3 -from 1 -to 120 -increment 1 -validate all -validatecommand { regexp {^[0-9]+$} %P } - $w.ftime.movetime.value set [ expr $::sergame::movetime /1000] + $w.ftime.movetime.value set [ expr $options(movetime) /1000] pack $w.ftime.movetime -side top -fill x pack $w.ftime.movetime.button -side left @@ -162,13 +162,13 @@ namespace eval sergame { pack $w.fconfig.combo -side top -anchor w -padx 20 -fill x # New game or use current position ? - ttk::checkbutton $w.fconfig2.cbPosition -text $::tr(StartFromCurrentPosition) -variable ::sergame::startFromCurrent + ttk::checkbutton $w.fconfig2.cbPosition -text $::tr(StartFromCurrentPosition) -variable ::sergame::options(startFromCurrent) #Should the evaluation of the position stored in the comment? - ttk::checkbutton $w.fconfig2.storeEval -text $::tr(AddScoreToShortAnnotations) -variable ::sergame::storeEval + ttk::checkbutton $w.fconfig2.storeEval -text $::tr(AddScoreToShortAnnotations) -variable ::sergame::options(storeEval) pack $w.fconfig2.cbPosition $w.fconfig2.storeEval -side top -anchor w # choose a specific opening - ttk::checkbutton $w.fopening.cbOpening -text $::tr(SpecificOpening) -variable ::sergame::isOpening + ttk::checkbutton $w.fopening.cbOpening -text $::tr(SpecificOpening) -variable ::sergame::options(isOpening) ttk::frame $w.fopening.fOpeningList ttk::treeview $w.fopening.fOpeningList.lbOpening -columns {0} -show {} -selectmode browse \ -yscrollcommand "$w.fopening.fOpeningList.ybar set" @@ -180,8 +180,8 @@ namespace eval sergame { incr idx } - $w.fopening.fOpeningList.lbOpening selection set $::sergame::chosenOpening - $w.fopening.fOpeningList.lbOpening see $::sergame::chosenOpening + $w.fopening.fOpeningList.lbOpening selection set $options(chosenOpening) + $w.fopening.fOpeningList.lbOpening see $options(chosenOpening) ttk::scrollbar $w.fopening.fOpeningList.ybar -command "$w.fopening.fOpeningList.lbOpening yview" pack $w.fopening.cbOpening -fill x -side top @@ -191,23 +191,23 @@ namespace eval sergame { ttk::button $w.fbuttons.close -text $::tr(Play) -command { focus . - set ::sergame::chosenOpening [.configSerGameWin.fopening.fOpeningList.lbOpening selection] - if {$::sergame::useBook} { - set ::sergame::bookToUse [.configSerGameWin.fconfig.combo get] - if {$::sergame::bookToUse == "" } { - set ::sergame::useBook 0 + set ::sergame::options(chosenOpening) [.configSerGameWin.fopening.fOpeningList.lbOpening selection] + if {$::sergame::options(useBook)} { + set ::sergame::options(bookToUse) [.configSerGameWin.fconfig.combo get] + if {$::sergame::options(bookToUse) == "" } { + set ::sergame::options(useBook) 0 } } - 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 ::sergame::depth [.configSerGameWin.ftime.depth.value get] - set ::sergame::nodes [expr [.configSerGameWin.ftime.nodes.value get]*1000] - set ::sergame::movetime [expr [.configSerGameWin.ftime.movetime.value get]*1000] + set ::sergame::options(wtime) [expr [.configSerGameWin.ftime.timebonus.whitespminutes get]*1000*60] + set ::sergame::options(btime) [expr [.configSerGameWin.ftime.timebonus.blackspminutes get]*1000*60] + set ::sergame::options(winc) [expr [.configSerGameWin.ftime.timebonus.whitespseconds get]*1000] + set ::sergame::options(binc) [expr [.configSerGameWin.ftime.timebonus.blackspseconds get]*1000] + set ::sergame::options(fixeddepth) [.configSerGameWin.ftime.depth.value get] + set ::sergame::options(fixednodes) [expr [.configSerGameWin.ftime.nodes.value get]*1000] + set ::sergame::options(movetime) [expr [.configSerGameWin.ftime.movetime.value get]*1000] + set ::sergame::options(depth) [.configSerGameWin.ftime.depth.value get] + set ::sergame::options(nodes) [expr [.configSerGameWin.ftime.nodes.value get]*1000] + set ::sergame::options(movetime) [expr [.configSerGameWin.ftime.movetime.value get]*1000] destroy .configSerGameWin ::sergame::play seriousEngine @@ -227,53 +227,52 @@ namespace eval sergame { # ################################################################################ proc play { engine } { - global ::sergame::chosenOpening ::sergame::isOpening ::sergame::openingList ::sergame::openingMovesList \ - ::sergame::openingMovesHash ::sergame::openingMoves ::sergame::outOfOpening + global ::sergame::_Data ::sergame::options set callback [list ::sergame::eng_messages $engine nop] - if { ! [::engineNoWin::initEngine $engine $::sergame::engineName $callback] } { + if { ! [::engineNoWin::initEngine $engine $options(engineName) $callback] } { tk_messageBox -title Scid -icon info -type ok -message "The UCI-Engines could not be started." return } - if {$::sergame::isOpening || !$::sergame::startFromCurrent} { + if {$options(isOpening) || !$options(startFromCurrent)} { if {[::game::Clear] eq "cancel"} { return } } - set ::sergame::lFen {} - set ::sergame::data(prevscore) "" - set ::sergame::data(score) 0.0 - set ::sergame::data(ponder) "" + set _Data(lFen) {} + set _Data(prevscore) "" + set _Data(score) 0.0 + set _Data(ponder) "" - if {$::sergame::startFromCurrent} { - set isOpening 0 + if {$options(startFromCurrent)} { + set options(isOpening) 0 } # ponder set ponder false - if {$::sergame::ponder} { set ponder true } + if {$options(ponder)} { set ponder true } ::engine::send $engine SetOptions [list {Ponder true}] # if will follow a specific opening line - if {$isOpening} { - set fields [split [lindex $openingList $chosenOpening] ":"] - set openingName [lindex $fields 0] - set openingMoves [string trim [lindex $fields 1]] - set openingMovesList "" - set openingMovesHash "" - set outOfOpening 0 - foreach m [split $openingMoves] { + if {$options(isOpening)} { + set fields [split [lindex $::sergame::openingList $options(chosenOpening)] ":"] +# set openingName [lindex $fields 0] + set _Data(openingMoves) [string trim [lindex $fields 1]] + set _Data(openingMovesList) "" + set _Data(openingMovesHash) "" + set _Data(outOfOpening) 0 + foreach m [split $_Data(openingMoves)] { # in case of multiple adjacent spaces in opening line if {$m =={}} { continue } set p [string trim $m] - lappend openingMovesList [string trim [regsub {^[1-9]+\.} $p ""] ] + lappend _Data(openingMovesList) [string trim [regsub {^[1-9]+\.} $p ""] ] } - lappend openingMovesHash [sc_pos hash] - foreach m $openingMovesList { + lappend _Data(openingMovesHash) [sc_pos hash] + foreach m $_Data(openingMovesList) { if {[catch {sc_move addSan $m}]} { } - lappend openingMovesHash [sc_pos hash] + lappend _Data(openingMovesHash) [sc_pos hash] } #goto start pos and clear the moves sc_move start @@ -282,43 +281,43 @@ namespace eval sergame { # Engine plays for the upper side if {[::board::isFlipped .main.board]} { - set ::sergame::playerColor "black" - set ::sergame::engineColor "white" + set _Data(playerColor) "black" + set _Data(engineColor) "white" } else { - set ::sergame::playerColor "white" - set ::sergame::engineColor "black" + set _Data(playerColor) "white" + set _Data(engineColor) "black" } - if {!$::sergame::startFromCurrent} { + if {!$options(startFromCurrent)} { # create a new game if a DB is opened sc_game tags set -event "Serious game" - sc_game tags set -$::sergame::playerColor "Player" - sc_game tags set -$::sergame::engineColor "$::sergame::engineName" + sc_game tags set -$_Data(playerColor) "Player" + sc_game tags set -$_Data(engineColor) "$options(engineName)" sc_game tags set -date [::utils::date::today] - if {$::sergame::timeMode eq "timebonus"} { - sc_game tags set -extra [list "TimeControlWhite \"[expr $::sergame::data(wtime)/60000]+[expr $::sergame::data(winc)/1000]\"" \ - "TimeControlBlack \"[expr $::sergame::data(btime)/60000]+[expr $::sergame::data(binc)/1000]\""] + if {$options(timeMode) eq "timebonus"} { + sc_game tags set -extra [list "TimeControlWhite \"[expr $options(wtime)/60000]+[expr $options(winc)/1000]\"" \ + "TimeControlBlack \"[expr $options(btime)/60000]+[expr $options(binc)/1000]\""] } - if { $::sergame::coachTypeMove || $::sergame::coachTypeTactic } { + if { $options(coachTypeMove) || $options(coachTypeTactic) } { sc_game tags set -event "Coached game" set co "Coached Game: " - if { $::sergame::coachTypeMove } { append co "Bad Move Warning; " } - if { $::sergame::coachTypeTactic } { append co "Engine Blunder Information; " } - append co "Blunder Threshold: $::sergame::threshold " + if { $options(coachTypeMove) } { append co "Bad Move Warning; " } + if { $options(coachTypeTactic) } { append co "Engine Blunder Information; " } + append co "Blunder Threshold: $options(threshold)" sc_pos setComment $co } } - set ::sergame::waitPlayerMove 0 - set ::sergame::wentOutOfBook 0 + set _Data(waitPlayerMove) 0 + set _Data(wentOutOfBook) 0 ::setPlayMode "::sergame::callback" ::notify::GameChanged - if { $::sergame::coachTypeTactic || $::sergame::useCoachEngine } { - set ::sergame::useCoachEngine 1 + if { $options(coachTypeTactic) || $options(useCoachEngine) } { + set options(useCoachEngine) 1 set callback [list ::sergame::coachEng_messages coachEngine nop] - if { ! [::engineNoWin::initEngine coachEngine $::sergame::coachName $callback] } { - set ::sergame::useCoachEngine 0 + if { ! [::engineNoWin::initEngine coachEngine $::sergame::options(coachName) $callback] } { + set options(useCoachEngine) 0 } } clocks init @@ -328,6 +327,7 @@ namespace eval sergame { } proc ::sergame::eng_messages {id w msg} { + global ::sergame::_Data ::sergame::options lassign $msg msgType msgData switch $msgType { "InfoConfig" { @@ -336,23 +336,23 @@ namespace eval sergame { ::engineNoWin::initEngineOptions $id $w $msgData } "InfoPV" { - if { ! $::sergame::useCoachEngine } { + if { ! $options(useCoachEngine) } { # no coach engine then use score from playing engine lassign $msgData multipv depth seldepth nodes nps hashfull tbhits time score score_type score_wdl pv if { $multipv == 1 } { - set ::sergame::data(score) [expr $score / 100.0] + set _Data(score) [expr $score / 100.0] if { $score_type eq "mate" } { if { $score > 0 } { - set ::sergame::data(score) 128.0 + set _Data(score) 128.0 } else { - set ::sergame::data(score) -128.0 + set _Data(score) -128.0 } } } } } "InfoBestMove" { - lassign $msgData ::sergame::data(bestmove) ponder ::sergame::data(ponder) + lassign $msgData _Data(bestmove) ponder _Data(ponder) } "InfoDisconnected" { lassign $msgData errorMsg @@ -363,6 +363,7 @@ namespace eval sergame { } } proc ::sergame::coachEng_messages {id w msg} { + global ::sergame::_Data ::sergame::options lassign $msg msgType msgData switch $msgType { "InfoConfig" { @@ -373,19 +374,19 @@ 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::data(bestCoachmove) [lrange $pv 0 0] - set ::sergame::data(score) [expr $score / 100.0] + set _Data(bestCoachmove) [lrange $pv 0 0] + set _Data(score) [expr $score / 100.0] if { $score_type eq "mate" } { if { $score > 0 } { - set ::sergame::data(score) 128.0 + set _Data(score) 128.0 } else { - set ::sergame::data(score) -128.0 + set _Data(score) -128.0 } } } } "InfoBestMove" { - lassign $msgData ::sergame::data(bestCoachmove) + lassign $msgData _Data(bestCoachmove) } "InfoDisconnected" { lassign $msgData errorMsg @@ -397,9 +398,10 @@ namespace eval sergame { } proc callback {cmd args} { + global ::sergame::_Data switch $cmd { premove { # TODO: currently we just return true if it is the engine turn. - return [expr { ! $::sergame::waitPlayerMove }] + return [expr { ! $_Data(waitPlayerMove) }] } stop { ::sergame::abortGame } } @@ -422,15 +424,16 @@ namespace eval sergame { } proc abortGame { } { + global ::sergame::_Data ::sergame::options ::setPlayMode "" after cancel ::sergame::playLoop clocks stop - set ::sergame::lFen {} + set _Data(lFen) {} ::engine::send seriousEngine StopGo ::engine::close seriousEngine unset ::enginewin::engConfig_seriousEngine - set ::sergame::data(bestmove) "abort" - if { $::sergame::useCoachEngine } { + set _Data(bestmove) "abort" + if { $options(useCoachEngine) } { ::engine::send coachEngine StopGo ::engine::close coachEngine unset ::enginewin::engConfig_coachEngine @@ -440,14 +443,15 @@ namespace eval sergame { } proc clocks {cmd} { - if {$::sergame::timeMode != "timebonus"} { return } + global ::sergame::options + if {$options(timeMode) != "timebonus"} { return } switch $cmd { init { ::gameclock::new "" 1 ::gameclock::new "" 2 - ::gameclock::setSec 1 [expr 0 - $::sergame::data(wtime)/1000] - ::gameclock::setSec 2 [expr 0 - $::sergame::data(btime)/1000] + ::gameclock::setSec 1 [expr 0 - $options(wtime)/1000] + ::gameclock::setSec 2 [expr 0 - $options(btime)/1000] } start { if { [sc_pos side] == "white" } { @@ -462,11 +466,11 @@ namespace eval sergame { } toggle { if {[::gameclock::stop 1]} { - ::gameclock::add 1 [expr $::sergame::data(winc)/1000] + ::gameclock::add 1 [expr $options(winc)/1000] ::gameclock::storeTimeComment 1 ::gameclock::start 2 } elseif {[::gameclock::stop 2]} { - ::gameclock::add 2 [expr $::sergame::data(binc)/1000] + ::gameclock::add 2 [expr $options(binc)/1000] ::gameclock::storeTimeComment 2 ::gameclock::start 1 } @@ -490,6 +494,7 @@ namespace eval sergame { # returns true if last move is a mate and stops clocks ################################################################################ proc endOfGame {} { + global ::sergame::_Data if { [string index [sc_game info previousMove] end ] == "#"} { tk_messageBox -type ok -message "This is Mate!" -parent .main -icon info set result 0 @@ -503,25 +508,26 @@ namespace eval sergame { # start playing engine: ponder must be "" or "ponder" proc startEngine { ponder } { - global ::sergame::timeMode - if {$timeMode == "timebonus"} { + global ::sergame::_Data ::sergame::options + if {$options(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::data(winc) binc $::sergame::data(binc)" - } elseif {$timeMode == "depth"} { - set parameter "$ponder depth $::sergame::data(fixeddepth)" - } elseif {$timeMode == "movetime"} { - set parameter "$ponder movetime $::sergame::data(movetime)" - } elseif {$timeMode == "nodes"} { - set parameter "$ponder nodes $::sergame::data(fixednodes)" + set parameter "$ponder wtime $wtime btime $btime winc $options(winc) binc $options(binc)" + } elseif {$options(timeMode) == "depth"} { + set parameter "$ponder depth $options(fixeddepth)" + } elseif {$options(timeMode) == "movetime"} { + set parameter "$ponder movetime $options(movetime)" + } elseif {$options(timeMode) == "nodes"} { + set parameter "$ponder nodes $options(fixednodes)" } - if { $ponder ne "" } { set ponder "moves $::sergame::data(ponder)" } + if { $ponder ne "" } { set ponder "moves $options(ponder)" } ::engine::send seriousEngine Go [list "position fen [sc_pos fen] $ponder" $parameter] } proc checkBlunder { delta } { + global ::sergame::options set ret "" - if { $delta >= $::sergame::threshold } { + if { $delta >= $options(threshold) } { if {$delta > $::informant("?!") } { set ret [list "?!" "DubiousMovePlayedTakeBack"] } if {$delta > $::informant("?") } { set ret [list "?" "WeakMovePlayedTakeBack"] } if {$delta > $::informant("??") } { set ret [list "??" "BadMovePlayedTakeBack"] } @@ -529,19 +535,20 @@ namespace eval sergame { return $ret } proc checkEngineBlunder { } { - set delta [expr $::sergame::data(score) + $::sergame::data(prevscore)] - if { [sc_pos side] == $::sergame::engineColor } { set delta [expr 0.0 - $delta] } - lassign [checkBlunder $delta] ::sergame::tacticBlunder - if { $::sergame::tacticBlunder ne "" } { - if { $::sergame::engineColor eq "white" } { - set from $::sergame::data(prevscore) - set to [expr 0.0 - $::sergame::data(score)] + global ::sergame::_Data ::sergame::options + set delta [expr $_Data(score) + $_Data(prevscore)] + if { [sc_pos side] == $_Data(engineColor) } { set delta [expr 0.0 - $delta] } + lassign [checkBlunder $delta] _Data(tacticBlunder) + if { $_Data(tacticBlunder) ne "" } { + if { $_Data(engineColor) eq "white" } { + set from $_Data(prevscore) + set to [expr 0.0 - $_Data(score)] } else { - set from [expr 0.0 - $::sergame::data(prevscore)] - set to $::sergame::data(score) + set from [expr 0.0 - $_Data(prevscore)] + set to $_Data(score) } - ::board::setInfoAlert .main.board "Engine blunders: $::sergame::tacticBlunder $from -> $to" "Show move" red \ - {::board::setInfoAlert .main.board "Try move $::sergame::data(bestCoachmove) Playing..." [tr Stop] red {{*}$::playMode stop}} + ::board::setInfoAlert .main.board "Engine blunders: $_Data(tacticBlunder) $from -> $to" "Show move" red \ + {::board::setInfoAlert .main.board "Try move $::sergame::_Data(bestCoachmove) Playing..." [tr Stop] red {{*}$::playMode stop}} } } @@ -549,23 +556,22 @@ namespace eval sergame { # ################################################################################ proc playLoop { } { - global ::sergame::isOpening ::sergame::openingMovesList ::sergame::openingMovesHash ::sergame::openingMoves \ - ::sergame::timeMode ::sergame::outOfOpening + global ::sergame::_Data ::sergame::options after cancel ::sergame::playLoop if { [::sergame::endOfGame] } { return } - if { [sc_pos side] != $::sergame::engineColor } { + if { [sc_pos side] != $_Data(engineColor) } { # wait until player has moved - set ::sergame::waitPlayerMove 1 + set _Data(waitPlayerMove) 1 after 1000 ::sergame::playLoop - if { $::sergame::useCoachEngine && $::sergame::coachTypeTactic && $::sergame::actTacTime > 0 && $::sergame::data(prevscore) != "" } { + if { $options(useCoachEngine) && $options(coachTypeTactic) && $_Data(actTacTime) > 0 && $_Data(prevscore) != "" } { #check for engine blunder with coach engine - incr ::sergame::actTacTime -1 - if { $::sergame::isLimitedAnalysisTime && ! $::sergame::actTacTime } { + incr _Data(actTacTime) -1 + if { $options(isLimitedAnalysisTime) && ! $_Data(actTacTime) } { # make sure we have a move and evaluation from coach engine - while { $::sergame::data(bestCoachmove) eq "" } { vwait ::sergame::data(bestCoachmove) } + while { $_Data(bestCoachmove) eq "" } { vwait ::sergame::_Data(bestCoachmove) } ::engine::send coachEngine StopGo } else { checkEngineBlunder @@ -574,15 +580,15 @@ namespace eval sergame { return } - if { $::sergame::useCoachEngine } { + if { $options(useCoachEngine) } { ::engine::send coachEngine StopGo - if { $::sergame::tacticBlunder ne "" } { + if { $_Data(tacticBlunder) ne "" } { # engine blundered, add nag and correct eval comment sc_move back - sc_pos addNag $::sergame::tacticBlunder - if { $::sergame::storeEval == 1 } { - set score $::sergame::data(score) - if { $::sergame::engineColor eq "white" } { set score [expr 0.0 - $score] } + sc_pos addNag $_Data(tacticBlunder) + if { $options(storeEval) == 1 } { + set score $_Data(score) + if { $_Data(engineColor) eq "white" } { set score [expr 0.0 - $score] } storeEvalComment $score } sc_move forward @@ -591,10 +597,10 @@ namespace eval sergame { set takebackClockW "" set takebackClockB "" - if {$::sergame::waitPlayerMove} { + if {$_Data(waitPlayerMove)} { # The player moved - set ::sergame::waitPlayerMove 0 - if {$::sergame::timeMode == "timebonus"} { + set _Data(waitPlayerMove) 0 + if {$options(timeMode) == "timebonus"} { set takebackClockW [::gameclock::getSec 1] set takebackClockB [::gameclock::getSec 2] clocks toggle @@ -603,25 +609,25 @@ namespace eval sergame { } # make a move corresponding to a specific opening, (it is engine's turn) - if {$isOpening && !$outOfOpening} { + if {$options(isOpening) && !$_Data(outOfOpening)} { set index 0 # Warn if the user went out of the opening line chosen - if { !$outOfOpening } { + if { !$_Data(outOfOpening) } { set ply [ expr [sc_pos moveNumber] * 2 - 1] if { [sc_pos side] == "white" } { set ply [expr $ply - 1] } - if { [lsearch $openingMovesHash [sc_pos hash]] == -1 && [llength $openingMovesList] >= $ply} { + if { [lsearch $_Data(openingMovesHash) [sc_pos hash]] == -1 && [llength $_Data(openingMovesList)] >= $ply} { clocks stop set answer [tk_messageBox -icon question -parent .main -title $::tr(OutOfOpening) -type yesno \ - -message "$::tr(NotFollowedLine) $openingMoves\n $::tr(DoYouWantContinue)" ] + -message "$::tr(NotFollowedLine) $_Data(openingMoves)\n $::tr(DoYouWantContinue)" ] if {$answer == no} { takeBack $takebackClockW $takebackClockB after 1000 ::sergame::playLoop return } else { - set outOfOpening 1 + set _Data(outOfOpening) 1 } clocks start } @@ -629,12 +635,12 @@ namespace eval sergame { set hpos [sc_pos hash] # Find a corresponding position in the opening line - set length [llength $openingMovesHash] + set length [llength $_Data(openingMovesHash)] for {set i 0} { $i < [expr $length-1] } { incr i } { - set h [lindex $openingMovesHash $i] + set h [lindex $_Data(openingMovesHash) $i] if {$h == $hpos} { - set index [lsearch $openingMovesHash $h] - set move [lindex $openingMovesList $index] + set index [lsearch $_Data(openingMovesHash) $h] + set move [lindex $_Data(openingMovesList) $index] # play the move set action "replace" if {![sc_pos isAt vend]} { set action [confirmReplaceMove] } @@ -661,15 +667,15 @@ namespace eval sergame { } # ------------------------------------------------------------- # use a book - if {$::sergame::useBook && ! $::sergame::wentOutOfBook} { - set move [ ::book::getMove $::sergame::bookToUse [sc_pos fen] $::sergame::bookSlot] + if {$options(useBook) && ! $_Data(wentOutOfBook)} { + set move [ ::book::getMove $options(bookToUse) [sc_pos fen] $_Data(bookSlot)] if {$move == ""} { - set ::sergame::wentOutOfBook 1 + set _Data(wentOutOfBook) 1 } else { sc_move addSan $move ::utils::sound::AnnounceNewMove $move # we made a book move so assume a score = 0 - set ::sergame::data(prevscore) "" + set _Data(prevscore) "" clocks toggle updateBoard -pgn -animate if { ! [repetition] } { @@ -680,34 +686,34 @@ namespace eval sergame { } # ------------------------------------------------------------- # check if the engine pondered on the right move - if { $::sergame::ponder && $::sergame::data(ponder) ne "" && $::sergame::data(ponder) == [sc_game info previousMoveUCI]} { + if { $options(ponder) && $_Data(ponder) ne "" && $_Data(ponder) == [sc_game info previousMoveUCI]} { ::engine::rawsend seriousEngine "ponderhit" } else { - if { $::sergame::ponder } { ::engine::send seriousEngine StopGo } + if { $options(ponder) } { ::engine::send seriousEngine StopGo } startEngine "" } - if { $::sergame::useCoachEngine } { - set ::sergame::data(bestCoachmove) "" + if { $options(useCoachEngine) } { + set _Data(bestCoachmove) "" ::engine::send coachEngine Go [list "position fen [sc_pos fen]" "infinite"] } - set ::sergame::data(bestmove) "" - vwait ::sergame::data(bestmove) - if { $::sergame::useCoachEngine } { + set _Data(bestmove) "" + vwait ::sergame::_Data(bestmove) + if { $options(useCoachEngine) } { # make sure we have a move and evaluation from coach engine - while { $::sergame::data(bestCoachmove) eq "" } { vwait ::sergame::data(bestCoachmove) } + while { $_Data(bestCoachmove) eq "" } { vwait ::sergame::_Data(bestCoachmove) } ::engine::send coachEngine StopGo } # ------------------------------------------------------------- # if weak move detected, propose the user to take back - if { $::sergame::coachTypeMove && $::sergame::data(prevscore) != "" } { - set delta [expr $::sergame::data(score) - $::sergame::data(prevscore)] - if { [sc_pos side] != $::sergame::engineColor } { set delta [expr 0.0 - $delta] } + if { $options(coachTypeMove) && $_Data(prevscore) != "" } { + set delta [expr $_Data(score) - $_Data(prevscore)] + if { [sc_pos side] != $_Data(engineColor) } { set delta [expr 0.0 - $delta] } lassign [checkBlunder $delta] nop tBlunder if {$tBlunder ne ""} { clocks stop - set answer [tk_messageBox -icon question -parent .main -title "Scid" -type yesno -message "$::tr($tBlunder)\n$::sergame::data(prevscore) -> $::sergame::data(score)" ] + set answer [tk_messageBox -icon question -parent .main -title "Scid" -type yesno -message "$::tr($tBlunder)\n$_Data(prevscore) -> $_Data(score)" ] if {$answer == yes} { takeBack $takebackClockW $takebackClockB after 1000 ::sergame::playLoop @@ -718,16 +724,16 @@ namespace eval sergame { } # ------------------------------------------------------------- - if { $::sergame::data(bestmove) == "abort" } { + if { $_Data(bestmove) == "abort" } { return } - 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::data(score) - if { $::sergame::engineColor eq "black" } { set score [expr 0.0 - $score] } + sc_move addSan $_Data(bestmove) + ::utils::sound::AnnounceNewMove $_Data(bestmove) + set _Data(prevscore) $_Data(score) + if { $options(storeEval) == 1 } { + set score $_Data(score) + if { $_Data(engineColor) eq "black" } { set score [expr 0.0 - $score] } storeEvalComment $score } updateBoard -pgn -animate @@ -736,11 +742,11 @@ namespace eval sergame { clocks toggle # ponder mode (the engine just played its move) - if {$::sergame::ponder } { startEngine ponder } + if {$options(ponder) } { startEngine ponder } - if { $::sergame::useCoachEngine } { - set ::sergame::actTacTime $::sergame::tacTime - set ::sergame::data(bestCoachmove) "" + if { $options(useCoachEngine) } { + set _Data(actTacTime) $options(tacTime) + set _Data(bestCoachmove) "" ::engine::send coachEngine Go [list "position fen [sc_pos fen]" "infinite"] } after 1000 ::sergame::playLoop @@ -750,7 +756,8 @@ namespace eval sergame { # the position is a repetition ################################################################################ proc repetition {} { - lassign [checkRepetition $::sergame::lFen] isRepetition ::sergame::lFen + global ::sergame::_Data + lassign [checkRepetition $_Data(lFen)] isRepetition _Data(lFen) if { $isRepetition } { tk_messageBox -type ok -message $::tr(Draw) -parent .main -icon info sc_game tags set -result = From 35b5e0a9e7176c370584b34759cd5ab5d5bc2f5b Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 4 Jun 2025 16:56:32 +0200 Subject: [PATCH 73/78] 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 ac8ad15b..a5871c9e 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 14a6cfa7..3befcf26 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 b392b305..b3d541d7 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 ba8bbb83a4f92b6c24f241316a0747f5156f5c2e Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 4 Jun 2025 17:12:38 +0200 Subject: [PATCH 74/78] use proc ::engineNoWin::closeEngine --- tcl/tools/sergame.tcl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 05e7ea8a..484b6e57 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -430,13 +430,11 @@ namespace eval sergame { clocks stop set _Data(lFen) {} ::engine::send seriousEngine StopGo - ::engine::close seriousEngine - unset ::enginewin::engConfig_seriousEngine + ::engineNoWin::closeEngine seriousEngine set _Data(bestmove) "abort" if { $options(useCoachEngine) } { ::engine::send coachEngine StopGo - ::engine::close coachEngine - unset ::enginewin::engConfig_coachEngine + ::engineNoWin::closeEngine coachEngine } # if { [sc_game tag get Result] eq "*" } { setResult } ::notify::GameChanged From 64fb102017b765409f87564e24252bf0d66321f4 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 2 Sep 2025 12:35:32 +0200 Subject: [PATCH 75/78] Show correct value on player blunder --- tcl/tools/sergame.tcl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 484b6e57..3b684242 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -478,8 +478,11 @@ namespace eval sergame { } proc takeBack {takebackClockW takebackClockB} { + global ::sergame::_Data sc_move back 1 sc_game truncate + set _Data(prevscore) "" + set _Data(score) 0.0 if {$takebackClockW != ""} { ::gameclock::setSec 1 [expr 0 - $takebackClockW] ::gameclock::setSec 2 [expr 0 - $takebackClockB] @@ -711,7 +714,13 @@ namespace eval sergame { lassign [checkBlunder $delta] nop tBlunder if {$tBlunder ne ""} { clocks stop - set answer [tk_messageBox -icon question -parent .main -title "Scid" -type yesno -message "$::tr($tBlunder)\n$_Data(prevscore) -> $_Data(score)" ] + set prevScore $_Data(prevscore) + set actScore $_Data(score) + if { $_Data(playerColor) eq "white" } { + set prevScore [expr 0.0 - $prevScore] + set actScore [expr 0.0 - $actScore] + } + set answer [tk_messageBox -icon question -parent .main -title "Scid" -type yesno -message "$::tr($tBlunder)\n$prevScore -> $actScore" ] if {$answer == yes} { takeBack $takebackClockW $takebackClockB after 1000 ::sergame::playLoop From d3004844522d077c7d2dc4ee4c8da1584d291c06 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 2 Sep 2025 17:10:33 +0200 Subject: [PATCH 76/78] Player can select color to play --- tcl/tools/sergame.tcl | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 3b684242..03ac660f 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -39,7 +39,7 @@ namespace eval sergame { ttk::frame $w.fconfig ttk::frame $w.fconfig2 ttk::frame $w.fbuttons - ttk::labelframe $w.fengines -text $::tr(Engine) + ttk::labelframe $w.fengines -text "$::tr(Player) - $::tr(Engine)" ttk::labelframe $w.coach -text "Coaching" ttk::labelframe $w.ftime -text $::tr(TimeMode) ttk::labelframe $w.fopening -text $::tr(Opening) @@ -58,6 +58,14 @@ namespace eval sergame { ttk::checkbutton $w.fengines.ponder -text $::tr(Ponder) -variable ::sergame::options(ponder) pack $w.seriousEngine -in $w.fengines -side top -pady 5 -anchor w -padx 4 pack $w.fengines.ponder -side top -anchor w + # Engine plays for the upper side + set _Data(playerColor) [expr {[::board::isFlipped .main.board] ? "black" : "white"}] + ttk::frame $w.fengines.player + ttk::label $w.fengines.player.l -text "$::tr(Player) $::tr(GlistColor)" + ttk::radiobutton $w.fengines.player.w -text $::tr(white) -value "white" -variable ::sergame::_Data(playerColor) + ttk::radiobutton $w.fengines.player.b -text $::tr(black) -value "black" -variable ::sergame::_Data(playerColor) + pack $w.fengines.player.l $w.fengines.player.w $w.fengines.player.b -side left + pack $w.fengines.player -side top -anchor w # coach engine ttk::frame $w.coach.en @@ -278,14 +286,10 @@ namespace eval sergame { sc_move start sc_game truncate } - - # Engine plays for the upper side - if {[::board::isFlipped .main.board]} { - set _Data(playerColor) "black" - set _Data(engineColor) "white" - } else { - set _Data(playerColor) "white" - set _Data(engineColor) "black" + set _Data(engineColor) [expr {$_Data(playerColor) eq "white" ? "black" : "white"}] + if { (![::board::isFlipped .main.board] && $_Data(playerColor) eq "black") || \ + ([::board::isFlipped .main.board] && $_Data(playerColor) eq "white") } { + board::flip .main.board } if {!$options(startFromCurrent)} { From 51c7da27e73e4ff50da2935d4caecf24edcb97bd Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 19 Sep 2025 22:35:58 +0200 Subject: [PATCH 77/78] do not destroy changed but not saved engine config --- tcl/tools/sergame.tcl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index 03ac660f..eb8c1dda 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -217,6 +217,8 @@ namespace eval sergame { set ::sergame::options(nodes) [expr [.configSerGameWin.ftime.nodes.value get]*1000] set ::sergame::options(movetime) [expr [.configSerGameWin.ftime.movetime.value get]*1000] + #do not destroy changed but not saved engine config + bind .configSerGameWin.seriousEngine "" destroy .configSerGameWin ::sergame::play seriousEngine } From 6dbbe17a47a21c6b38b07f8d87e54fad963ed648 Mon Sep 17 00:00:00 2001 From: Uwe Date: Sat, 27 Sep 2025 14:53:52 +0200 Subject: [PATCH 78/78] when player takes back a move, store it as variation --- tcl/tools/sergame.tcl | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tcl/tools/sergame.tcl b/tcl/tools/sergame.tcl index eb8c1dda..4455cef5 100644 --- a/tcl/tools/sergame.tcl +++ b/tcl/tools/sergame.tcl @@ -217,7 +217,6 @@ namespace eval sergame { set ::sergame::options(nodes) [expr [.configSerGameWin.ftime.nodes.value get]*1000] set ::sergame::options(movetime) [expr [.configSerGameWin.ftime.movetime.value get]*1000] - #do not destroy changed but not saved engine config bind .configSerGameWin.seriousEngine "" destroy .configSerGameWin ::sergame::play seriousEngine @@ -252,6 +251,7 @@ namespace eval sergame { set _Data(prevscore) "" set _Data(score) 0.0 set _Data(ponder) "" + set _Data(takeback) 0 if {$options(startFromCurrent)} { set options(isOpening) 0 @@ -485,8 +485,9 @@ namespace eval sergame { proc takeBack {takebackClockW takebackClockB} { global ::sergame::_Data + sc_pos setComment "Player takes back this move" sc_move back 1 - sc_game truncate + set _Data(takeback) 1 set _Data(prevscore) "" set _Data(score) 0.0 if {$takebackClockW != ""} { @@ -587,6 +588,15 @@ namespace eval sergame { return } + if { $_Data(takeback) } { + # player has taken back his move and played an new move, make new move mainline and old move to var + if {[info exists ::guessedAddMove]} { + sc_game undo; addMoveEx [lindex $::guessedAddMove 1] mainline + unset ::guessedAddMove + } + set _Data(takeback) 0 + } + if { $options(useCoachEngine) } { ::engine::send coachEngine StopGo if { $_Data(tacticBlunder) ne "" } {