Updated 2017-02-16 05:17:25 by DDG

Move validator for Chess edit

  • snit wrapper to the ChessBoard implementation on Chess in Tcl but with castling, enpassant and mate, stalemate notifications
  • both from-to move syntax as well as pgn short moves are implemented
  • graphical chessboard will follow soon

ToDo

  • catch errors as return types
  • possibility to return Chess Berlin ttf codes
  • add Pgn move list with numbers
  • get complete move list of made moves

Links

Provide:
 #  Created By    : Dr. D. Groth
 #  Created       : Fri Feb 10 05:15:30 2017
 #  Last Modified : <170216.0558>
 #
 #  Description        : An implementation to send moves to a chessboard and to validate the moves
 #
 #  Notes: Implemented all chess rules including castling, enpassant, check, mate, stalemate
 #         implementation is based on Wikipage: http://wiki.tcl.tk/4070 Chess in Tcl
 #         Initial version was by Richard Suchenwirth in 2002
 #
 #  History: 2017-02-16 Wiki-Release Version 0.1
 #  License: MIT
 #        
 ##############################################################################

 package require snit
 package provide ChessValidator 0.1

 snit::type ChessValidator {
    option -fen -default "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
    variable cv1
    variable cv2
    delegate method * to cv1 except [list validMoves move fen2Setup validMove]
    constructor {args} {
        $self configurelist $args
        # the real board is cv1
        set cv1 [ChessBoard %AUTO%]
        # cv2 is a playground for check and mate testing
        # I became crazy by infinite loops if using cv1-class directly directly
        set cv2 [ChessBoard %AUTO%]
        $cv1 reset
        $cv2 reset
    }
    method fen2Setup {fen} {
        $cv1 fen2Setup $fen
        $cv2 fen2Setup $fen
    }
    method move {move} {
        if {![regexp -nocase {^([a-h][1-8])-([a-h][1-8])} $move]} {
            set move [$self pgn2move $move]
        }
        $cv1 move $move
        $cv2 move $move
    }
    method pgn2move {pgnmove} {
        set side [$cv2 getSide]

        if {[regexp {^([RNBQK])x?([a-h][1-8])} $pgnmove -> piece square]} {
            ;# standard move 
        } elseif {[regexp {^([RN])([a-h])x?([a-h][1-8])} $pgnmove -> piece fromline square]} {
            # two possibility move type1 for rooks and knights
            if {$side eq "black"} {
                set piece [string tolower $piece]
            }
            foreach piecesquare [$cv1 getPieces $piece] {
                #puts $piecesquare
                if {[$cv1 validMove "$piecesquare-$square"]} {
                    if {[string range [string toupper $piecesquare] 0 0] eq [string toupper $fromline]} {
                        return "$piecesquare-$square"
                    }
                }
            }
        } elseif {[regexp {^([RN])([1-8])x?([a-h][1-8])} $pgnmove -> piece fromline square]} {
            # two possibility move type2 for rooks and knights
            if {$side eq "black"} {
                set piece [string tolower $piece]
            }
            foreach piecesquare [$cv1 getPieces $piece] {
                if {[$cv1 validMove "$piecesquare-$square"]} {
                    if {[string range [string toupper $piecesquare] 1 1] eq [string toupper $fromline]} {
                        return "$piecesquare-$square"
                    }
                }
            }
        } elseif {[regexp {^([a-h][2-7])} $pgnmove -> square]} {
            # move of pawn
            set piece P
        } elseif {[regexp {^([a-h])x([a-h][2-7])} $pgnmove -> from square]} {
            # pawn has taken something
            # find out from where it came
            set piece P
            set line [string range $square 1 1]
            if {$side eq "black"} {
                incr line
                return "$from$line-$square"
            } else {
                incr line -1
                return "$from$line-$square"

            }
        } elseif {[regexp {^([a-h])([18])([QRNB])} $pgnmove -> col line promo]} {
            # move of pawn with promotion
            set square $col$line
            set piece P
            if {$side eq "black"} {
                incr line
                return "$col$line-$square$promo"
            } else {
                incr line -1
                return "$col$line-$square$promo"

            }

        } elseif {[regexp {^([a-h])x([a-h])([18])([QRBN])} $pgnmove -> colfrom colto line promo]} {
            # take of pawn with promotion
            set piece P
            set square $colto$line
            if {$side eq "black"} {
                incr line
                return "$colfrom$line-$square$promo"
            } else {
                incr line -1
                return "$colfrom$line-$square$promo"

            }
        } elseif {$pgnmove eq "O-O" || $pgnmove eq "0-0"} {
            # castling using O-syntax
            if {$side eq "black"} {
                return "e8-g8"
            } else {
                return "e1-g1"
            }
        } elseif {$pgnmove eq "O-O-O" || $pgnmove eq "0-0-0"} {
            if {$side eq "black"} {
                return "e8-c8"
            } else {
                return "e1-c1"
            }
        } 
        if {$side eq "black"} {
            set piece [string tolower $piece]
        }
        foreach piecesquare [$cv1 getPieces $piece] {
            if {[$cv1 validMove "$piecesquare-$square"]} {
                return "$piecesquare-$square"
            }
        }
        
    }
    method validMove {move} {
        # check for validity of move
        foreach {from to} [split [string toupper $move] -] break
        set moves [$self validMoves $from]
        if {[lsearch $moves [string toupper $move]] >= 0} {
            return true
        } else {
            return false
        }
    }
    method check {} {
        # check for check
        return [$self isCheck [$cv2 getSide]]
    }
    method mate {} {
        # check for mate by looping over all squares
        # and hunting for valid moves
        if {[$self isCheck [$cv2 getSide]]} {
            foreach row {8 7 6 5 4 3 2 1} {
                foreach col {A B C D E F G H} {
                    if {[llength [$self validMoves $col$row]] > 0} {
                        return false
                    }
                }
            }
            return true
        }
        return false
    }
    method stalemate {} {
        # check for stalemate
        # looping over all squares and hunting for valid moves
        if {![$self isCheck [$cv2 getSide]]} {
            foreach row {8 7 6 5 4 3 2 1} {
                foreach col {A B C D E F G H} {
                    if {[llength [$self validMoves $col$row]] > 0} {
                                     return false
                    }
                }
            }
            return true
        }
        return false
    }

    method validMoves {square} {
        set color [$cv2 getSide]
        set fen [$cv2 getFen]
        set moves [list]
        foreach move [$cv2 validMoves $square] {
            #puts "checking move $move"
            # trying oout a move on cv2
            if {[$cv2 validMove $move]} {
                $cv2 move $move
                if {$color eq "white"} {
                    if {[$cv2 isCheck white]} {
                        $cv2 fen2Setup $fen
                        continue
                    } else {
                        lappend moves [regsub -nocase -- {.[PNBRQ]$} $move ""]
                    }
                } elseif {$color eq "black"} {
                    if {[$cv2 isCheck black]} {
                        $cv2 fen2Setup $fen
                        continue
                    } else {
                        lappend moves [regsub -nocase -- {.[PNBRQ]$} $move ""]
                    }
                }
                # going back to previous move
                $cv2 fen2Setup $fen
            }
        }
        return $moves
    }
 }
 # the real board used to move pieces
 snit::type ChessBoard {
    option -fen -default "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
    variable board 
    variable eppossible false
    variable squares [list]
    constructor {args} {
        $self configurelist $args
        array set board [list]
        foreach row {8 7 6 5 4 3 2 1} {
            foreach col {A B C D E F G H} {
                lappend squares $col$row
            }
        }
        $self reset
    }
    method getSide {} {
        return $board(toMove)
    }
    method fen2Setup {fen} {
        # setup a new board using FEN syntax
        set fen [regsub -all { +} $fen " "]
        set pos [lindex [split $fen " "] 0]
        set pos [string map {1 . 2 .. 3 ... 4 .... 5 ..... 6 ...... 7 ....... 8 ........ / " \n "} $pos]
        set pos [regsub -all {([A-Za-z\.])} " $pos" "\\1 "]
        set castling [lindex [split $fen " "] 2] 
        $self reset $pos $castling
        set turn [lindex [split $fen " "] 1]
        set board(toMove) [regsub "b" [regsub "w" $turn "white"] "black"]
        set board(history) {} ;# start a new history...
        set board(castling) $castling
        set board(castlinghistory) [list $castling]
        set ep [lindex [split $fen " "] 3]
        set board(enpassant) $ep
        return $pos
                 
    }
    method getFen {} {
        # return the actual fen position
        set res ""
        foreach row {8 7 6 5 4 3 2 1} {
            foreach col {A B C D E F G H} {
                append res $board($col$row)
            }
            append res /
        }
        set res [string map {........ 8 ....... 7 ...... 6 ..... 5 .... 4 ... 3 .. 2 . 1} $res]
        return "$res [string range $board(toMove) 0 0] $board(castling) $board(enpassant) 0 1"
    }
    method reset {{setup ""} {castling ""}} {
        # clear the board and start from scratch
        if {$setup eq "" && $castling eq ""} {
            set castling "KQkq"
            set enpassant "-"
            set board(enpassant) "-"

        }
        if {$setup ne "" && $castling eq ""} {
            error "nocastling given: default KQkq for all castlings"
        } 
        if {$setup eq ""} {
            set setup \
                  "r n b q k b n r
                   p p p p p p p p
                   . . . . . . . .
                   . . . . . . . .
                   . . . . . . . .
                   . . . . . . . .
                   P P P P P P P P
                   R N B Q K B N R"
        }
        foreach line [split [string trim $setup] \n] y {8 7 6 5 4 3 2 1} {
            foreach word $line x {A B C D E F G H} {
                set board($x$y) $word
            }
        }
            
        set board(toMove) white
        set board(history) {} ;# start a new history...
        set board(castling) $castling
        set board(castlinghistory) [list $board(castling)] ;# start a new history...

    }
    method format {} {
        # render current board into a well-readable string
        # useful for terminal display
        foreach row {8 7 6 5 4 3 2 1} {
            foreach column {A B C D E F G H} {
                append res " " $board($column$row)
            }
            append res \n
        }
        set res
    }
    method move {move} {
        # actually does a move
        set promoMan ""
        foreach {from to} [split [string toupper $move] -] break
        set fromMan $board($from)
        if {$fromMan == "."} {error "no man to move at $from"} 
        set flag false
        if {$fromMan eq "P" && [regexp -nocase {[a-h][7]} $from] || $fromMan eq "p" && [regexp -nocase {[a-h][2]} $from]} {
            # promoting-pawn
            if {[regexp -nocase {([a-h][18])([QRBN])} $to -> newto promoMan]} {
                set to $newto 
                set move [string range $move 0 4]
                set flag true
            } 
        } 
        set toMan $board($to)
        if ![$self validMove $move] {error "invalid move for a [$self manName $fromMan]"}
        # check enpassant done
        if {$fromMan eq "P" && [regexp -nocase {[a-h]5-[a-h]6} $move]} {
            if {[string range $move 0 0] != [string range $move 3 3]} {
                #set epsquare [string map {5 6} $to]
                set epsquare $to
                set pawnsquare [string map {6 5} $epsquare]
                if {$board(enpassant) eq [string tolower $epsquare]} {
                    append move -$board($pawnsquare)

                    set board($pawnsquare) .

                } 
            }
        } elseif {$fromMan eq "p" && [regexp -nocase {[a-h]4-[a-h]3} $move]} {
            if {[string range $move 0 0] != [string range $move 3 3]} {
                set epsquare $to
                set pawnsquare [string map {3 4} $epsquare]
                #set epsquare [string map {4 3} $to]
                if {$board(enpassant) eq [string tolower $epsquare]} {
                    append move -$board($pawnsquare)
                    set board($pawnsquare) .
                } 
            }
        }
        if {[string toupper $fromMan] == "P" && [regexp -nocase {[a-h][18]} $to]} {
            set board($to) [expr {$fromMan == "P"? [string toupper $promoMan]: [string tolower $promoMan]}]
        }

        # check enpassant will be available in next move
        if {$fromMan eq "P" && [regexp -nocase {[a-h]2-[a-h]4} $move]} {
            set board(enpassant) [string map {4 3} [string tolower $to]]
        } elseif {$fromMan eq "p" && [regexp -nocase {[a-h]7-[a-h]5} $move]} {
            set board(enpassant) [string map {5 6} [string tolower $to]]
        } else {
            set board(enpassant) "-"
        }

        # check castling
        # update rook position
        if {$fromMan eq "K" && [regexp -nocase {e1-g1} $move]} {
            set board(H1) .
            set board(F1) R
        } elseif {$fromMan eq "K" && [regexp -nocase {e1-c1} $move]} {
            set board(A1) .
            set board(D1) R
        } elseif {$fromMan eq "k" && [regexp -nocase {e8-g8} $move]} {
            set board(H8) .
            set board(F8) r
        } elseif {$fromMan eq "k" && [regexp -nocase {e8-c8} $move]} {
            set board(A8) .
            set board(D8) r
        } 
        # update castling flag
        if {$from eq "E1"} {
            set board(castling) [regsub {[KQ]} $board(castling) ""]

        } elseif {$from eq "H1"} {
            set board(castling) [regsub {K} $board(castling) ""]

        } elseif {$from eq "A1"} {
            set board(castling) [regsub {Q} $board(castling) ""]

        } elseif {$from eq "E8"} {
            set board(castling) [regsub {[kq]} $board(castling) ""]

        } elseif {$from eq "H8"} {
            set board(castling) [regsub {k} $board(castling) ""]

        } elseif {$from eq "A8"} {
            set board(castling) [regsub {q} $board(castling) ""]

        } 
        set board($from) .

        if {$flag} {
            append move $promoMan
            if {$board(toMove) eq "white"} {
                set board($to) [string toupper $promoMan]
            } else {
                set board($to) [string tolower $promoMan]
            }
        } else {
            set board($to) $fromMan
        }
        if {$board(castling) eq ""} {
            set board(castling) -
        }
        if {$toMan != "."} {append move -$toMan} ;# taken one
        lappend board(history) $move
        lappend board(castlinghistory) $board(castling)
        set board(toMove) [expr {$board(toMove) == "white"? "black": "white"}]
        set toMan ;# report possible victim
    }
    method toggleSide {} {
        if {$board(toMove) eq "white"} {
            set board(toMove) black
        } else {
            set board(toMove) white
        }
    }
    method isKingAttacked {} {
        $self toggleSide
        foreach row {8 7 6 5 4 3 2 1} {
            foreach column {A B C D E F G H} {
                if {[lsearch -regexp [$self validMoves $column$row] {.+[kK]$}] >= 0} {
                    $self toggleSide
                    return true
                }
            }
        }
        $self toggleSide
        return false
    }
    method sgn x {expr {$x>0? 1: $x<0? -1: 0}} 
    method validMove {move} {
        foreach {from to} [split [string toupper $move] -] break
        if {$to==""} {return 0}
        set fromMan $board($from)
        if {[$self color $fromMan] != $board(toMove)} {return 0}
        set toMan   $board($to)
        if {[$self sameSide $fromMan $toMan]} {return 0}
        foreach {x0 y0} [$self coords $from] {x1 y1} [$self coords $to] break
        set dx  [expr {$x1-$x0}]
        set adx [expr {abs($dx)}]
        set dy  [expr {$y1-$y0}]
        set ady [expr {abs($dy)}]
        if {[string tolower $fromMan] != "n" && (!$adx || !$ady || $adx==$ady)} {
            for {set x $x0; set y $y0} {($x!=$x1 || $y!=$y1)} \
                  {incr x [$self sgn $dx]; incr y [$self sgn $dy]} {
                if {($x!=$x0 || $y!=$y0) && $board([$self square $x $y])!="."} {
                    return 0
                } ;# planned path is blocked
            }
        }
        # castling check
        
        if {$fromMan eq "K" && $to eq "G1" && $board(F1) eq "." && $board(G1) eq "."} {
            if {![$self isAttacked black F1] && ![$self isAttacked black G1] && [regexp {K} $board(castling)]} {
                return true
            }
        } elseif {$fromMan eq "K" && $to eq "C1" && $board(C1) eq "." && $board(D1) eq "." && $board(B1) eq "."} {
            if {![$self isAttacked black D1] && ![$self isAttacked black C1] && [regexp {Q} $board(castling)]} {
                return true
            }
        } elseif {$fromMan eq "k" && $to eq "G8" && $board(F8) eq "." && $board(G8) eq "."} {
            if {![$self isAttacked white F8] && ![$self isAttacked white G8] && [regexp {k} $board(castling)]} {
                return true
            }
        }  elseif { $fromMan eq "k" && $to eq "C8"&& $board(C8) eq "." && $board(D8) eq "." && $board(B8) eq "."} {
            if {![$self isAttacked white D8] && ![$self isAttacked white C8] && [regexp {q} $board(castling)]} {
                return true
            }
        }  
        # check enpassant
        if {$fromMan eq "P" && [regexp -nocase {[a-h]5-[a-h]6} $move]} {
            if {[string range $move 0 0] != [string range $move 3 3]} {
                set epsquare $to
                if {$board(enpassant) eq [string tolower $epsquare]} {
                    set eppossible true
                    return true
                } 
            }
        } elseif {$fromMan eq "p" && [regexp -nocase {[a-h]4-[a-h]3} $move]} {
            if {[string range $move 0 0] != [string range $move 3 3]} {
                set epsquare $to
                if {$board(enpassant) eq [string tolower $epsquare]} {
                    set eppossible true
                    return true
                } 
            }
        } else {
            set eppossible false
        }
        set ret true
        switch -- $fromMan {
            K - k {set ret [expr {$adx<2 && $ady<2}] }
            Q - q {set ret [expr {$adx==0 || $ady==0 || $adx==$ady}] }
            B - b {set ret [expr {$adx==$ady}] }
            N - n {set ret [expr {($adx==1 && $ady==2)||($adx==2 && $ady==1)}] }
            R - r {set ret [expr {$adx==0 || $ady==0}] }
            P {
                set ret [expr {(($y0==2 && $dy==2) || $dy==1)
                    && (($dx==0 && $toMan==".") ||
                        ($adx==1 && $ady==1 && [$self sameSide p $toMan]))}] 
            }
            p {
                set ret [expr {(($y0==7 && $dy==-2) || $dy==-1)
                    && (($dx==0 && $toMan==".") ||
                        ($adx==1 && $ady==1 && [$self sameSide P $toMan]))}]
            }
        }
        return $ret
    }
    method color man {expr {[string is upper $man]? "white" : "black"}}
    method validMoves {from} {
        set from [string toupper $from]
        set res {}
        if {$board($from) eq "."} {
            return $res
        }
        foreach to [array names board ??] {
            set move $from-$to
            if [$self validMove $move] {
                if {$board($to) != "."} {append move -$board($to)}
                if {$eppossible && $board(toMove) eq "white"} {append move -p}
                if {$eppossible && $board(toMove) eq "black"} {append move -P}
                lappend res $move
            }
        }
        
        return [lsort $res]
        
    }
    method coords {square} {
        # translate square name to numeric coords: C5 -> {3 5}
        foreach {c y} [split $square ""] break
        list [lsearch {- A B C D E F G H} $c] $y
    }
    method square {x y} {
        # translate numeric coords to sq uare name: {3 5} -> C5
        return [string map {1 A 2 B 3 C 4 D 5 E 6 F 7 G 8 H} $x]$y
    }
    method undo {} {
        if ![llength $board(history)] {error "Nothing to undo"}
        set move [lindex $board(history) end]
        foreach {from to hit} [split $move -]  break
        set board(history) [lrange $board(history) 0 end-1]
        #puts "before: $board(castlinghistory)"
        set board(castlinghistory) [lrange $board(castlinghistory) 0 end-1]
        #puts "after: $board(castlinghistory)"
        #puts $board(history)
        set board($from)   $board($to)
        if {$hit==""} {set hit .}
        set board($to) $hit
        set board(toMove) [expr {$board(toMove) == "white"? "black": "white"}]
    }
    method sameSide {a b} {
        return [regexp {[a-z][a-z]|[A-Z][A-Z]} $a$b ]
    }

    method history {} { return $board(history) }
 
    method manName {man} {
        set table {- k king q queen b bishop n knight r rook p pawn}
        set i [lsearch $table [string tolower $man]]
        lindex $table [incr i]
    }
    method values {} {
        # returns the current numeric value of white and black crews
        set white 0; set black 0
        foreach square [array names board ??] {
            set man $board($square)
            switch -regexp -- $man {
                [A-Z] {set white [expr {$white + [$self manValue $man]}]}
                [a-z] {set black [expr {$black + [$self manValue $man]}]}
            }
        }
        return [list $white $black]
    }
    method manValue {man} {
        array set a {k 0 q 9 b 3.2 n 3 r 5 p 1}
        set a([string tolower $man])
    }
    
    method pieceGetColor {field} {
        if {[string tolower $board($field)] eq $board($field)} {
            return black
        } else {
            return white
        }
    }
    method getPieces {{mtype X}} {
        set res [list]
        foreach square $squares {
            if {$board($square) ne "."} {
                if {$mtype eq "X"} {
                    lappend res $square
                } elseif {$mtype eq $board($square)} {
                    lappend res $square
                }
            }
        }
        return $res
    }
    method isCheck {color} {
        if {$color eq "white"} {
            set k [$self getPieces K]
            return [$self isAttacked black $k]

        } else {
            set k [$self getPieces k]
            return [$self isAttacked white $k]
        }
    }
    method isAttacked {color square} {
        set oldcol $board(toMove)
        set board(toMove) $color
        foreach piece [$self getPieces] {
            if {[$self pieceGetColor $piece] ne $color} {
                continue
            } elseif {[$self validMove ${piece}-${square}]} {
                #puts "Piece at: $piece attacks $square"
                set board(toMove) $oldcol
                return true
            }
                
        }
        set board(toMove) $oldcol
        return false
    }
 }

 # just a dry trial of some possibilties
 if {$argv0 eq [info script]} {
    ChessValidator cval 
    puts [cval fen2Setup "8/7p/5k2/5p2/p1p2P2/Pr1pPK2/1P1R3P/8 b - - bm Rxb2;"]
    puts [cval fen2Setup "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"]
    cval move e2-e4
    cval move e7-e5
    puts [cval getFen]
    puts [cval validMoves d2]
    cval move d2-d4
    puts [cval validMoves e5]
    cval move e5-d4
    cval move d1-d4
    cval move b8-c6
    cval move d4-e3
    cval move g8-f6
    cval move b1-c3
    puts nocastling?

    puts [cval validMoves e8]

    cval move f8-b4
    puts [cval validMoves e1]
        
        
    cval move c1-d2
    # check castling
    puts castling?

    puts [cval validMoves e8]
    cval move b4-c3
    cval move d2-c3
    cval move a7-a6
    cval move c3-b4
    puts nocastling?
    puts [cval validMoves e8]
    puts rookmoves
    puts [cval format]
    cval move a8-a7
    puts checkDone?
    cval move b4-c3
    puts castling?
    puts [cval validMoves e8]
    puts "find castling?"
    cval move e8-g8
    puts "castling done!"
    puts [cval validMoves e1]
    puts [cval format]
    cval move e1-c1
    puts [cval format]
    puts [cval validMoves f8]
    cval move f8-e8
    cval move e3-g3
    cval move a6-a5
    cval move e4-e5
    # check enpassant
    puts checkenpassant
    cval move d7-d5
    puts [cval format]
    puts [cval validMoves e5]
    puts [cval move e5-d6]
    puts checkenpassant-done
        
    puts [cval format]
    cval move b7-b5
    cval move g3-d3
    cval move b5-b4
    cval move a2-a4
    puts [cval format]
    puts [cval validMoves b4]
    cval move b4-a3
    puts [cval format]
    # check promotion
    cval move d6-c7
    cval move a3-a2
    puts [cval validMoves c7]
    cval move c7-d8Q
    puts [cval format]
    cval move a7-a8
    puts "moves for King?"
    puts [cval validMoves c1]
    cval move c3-a5
    cval move a2-a1R
    puts [cval format]
    puts [cval isKingAttacked]
    puts "moves for King?"
    puts [cval validMoves c1]
    puts [cval isAttacked black B1]
    puts [cval isAttacked black B8]
    puts [cval isAttacked white B8]
    puts "where is white King? [cval getPieces K]!"
    puts "where is black King? [cval getPieces k]!"
    puts "where are black nights? [cval getPieces n]!"
    puts "White isCheck?: [cval isCheck white]"
    puts "Black is Checked?: [cval isCheck black]"
    puts [cval validMoves c1]
    cval move c1-d2
    puts [cval fen2Setup "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"]
    cval move e2-e4
    cval move e7-e5
    cval move d1-h5
    cval move b8-c6
    cval move f1-c4
    puts "check on mate"
    puts [cval format]
    cval move g8-f6
    puts "King stalemated? [cval stalemate]"

    cval move h5-f7
    puts [cval format]
    puts "King attacked? [cval check]"
    puts "King mated? [cval mate]"
    cval fen2Setup "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
    cval move Nf3
    cval move d5
    cval move c4
    cval move dxc4
    cval move Qa4+
    cval move c6
    cval move Qxc4
    cval move Nf6
    cval move d4
    cval move Bg4
    cval move e3
    cval move e6
    cval move Nbd2
    puts [cval format]
    cval move Be7
    cval move Be2
    cval move O-O
    cval move b3
    cval move Nbd7
    cval move Bb2
    cval move Qc7
    cval move O-O-O
    cval move Rac8
    cval move Rhe1
    cval move a6
    cval move Nf1
    cval move h6
    cval move N1d2
    cval move g5
    cval move a4
    cval move Rfe8
    cval move a5
    cval move b5
    cval move axb6
    cval move a5
    cval move bxc7
    cval move a4
    puts [cval  format]
    cval move h3
    cval move Bxf3
    cval move Nxf3
    cval move Rb8
    #cval move Ra8
    cval move cxb8Q
    puts [cval format]
    cval move Rxb8
    cval move Rd3
    cval move Rf8
    cval move Red1
    puts [cval format]
    cval move a3
    cval move R3d2
    puts [cval format]
    cval move a2
    cval move Ba3
    cval move Bb4
    cval move Rc2
    puts [cval format]
    cval move Bc3
    cval move g4
    cval move a1R ;# mate
    puts [cval format]
    puts "is mate? [cval mate]"
    puts [cval fen2Setup "3k4/8/3P4/3K4/8/8/8/8 w - - 0 1"]
    puts [cval format]
    cval move Kc6
    cval move Kc8
    cval move d7+
    puts [cval format]
    puts "is check? [cval check]"
    puts "is mate? [cval mate]"
    puts "is stalemate? [cval stalemate]"
    cval move Kd8
    cval move Kd6
    puts [cval format]
    puts "is check? [cval check]"
    puts "is mate? [cval mate]"
    puts "is stalemate? [cval stalemate]"
    puts "Well done! We passed all tests!"
 }