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!"
}