A little Tic Tac Toe game

TicTacToe for two players

package require Tk

frame .board -class TicTacToeBoard
pack  .board -fill both -expand 1 -padx 1m -pady 1m
 
foreach square { 1 2 3 4 5 6 7 8 9 } {
    button .board.sq$square -command [list tttButton $square]
}
grid .board.sq1 .board.sq2 .board.sq3 -sticky nsew -padx 1 -pady 1   
grid .board.sq4 .board.sq5 .board.sq6 -sticky nsew -padx 1 -pady 1 
grid .board.sq7 .board.sq8 .board.sq9 -sticky nsew -padx 1 -pady 1 
button .board.sq0 -text x
set w [expr {[winfo reqwidth .board.sq0] + 2}]
destroy .board.sq0
foreach n {0 1 2} {
    grid column .board $n -weight 1 -minsize $w
    grid row    .board $n -weight 1
}
 
switch $tcl_platform(platform) {
    unix {
        array set cursor {
            x {X_cursor red}
            o {circle green}
            illegal pirate
        }
    }
    default {
        array set cursor {
            x X_cursor
            o circle
            illegal pirate
        }
    }
}
 
proc newGame { } {
    global board turn
    set turn x
    .board configure -cursor $::cursor(x)
 
    if {[info exists board] } {
        unset board
    }
    foreach sq { 1 2 3 4 5 6 7 8 9 } {
        set w .board.sq$sq
        # Normally filled with spaces
        $w configure -text $sq -cursor {} -fg black -state normal
        bindtags $w [list $w Button . all]
    }
}
 
proc tttButton { sq } {
    global board turn
    set w .board.sq$sq
    if { [info exists board($sq) ] } {
        # Error
        return
    }
    set board($sq) $turn
    array set cols {x red o green}
    $w configure -text $turn -cursor $::cursor(illegal) \
            -fg $cols($turn) -state normal
    bindtags $w [list $w . all]
    if { $turn == "x" } {
        set turn o
    } else {
        set turn x
    }
    .board configure -cursor $::cursor($turn)
 
    update idletasks
    if {[checkwin]} {newGame}
}
 
proc checkwin {} {
    set x {}
    set o {}
    foreach sq {1 2 3 4 5 6 7 8 9} {
        if {[info exists ::board($sq)]} {
            append $::board($sq) $sq
        }
    }
 
    # winning patterns (glob-style matches)
    set winpats {
        123* *456* *789 1*4*7* 1*5*9 *2*5*8* *3*6*9 *3*5*7*
    }
 
    foreach pattern $winpats {
        if {[string match $pattern $x]} {
            tk_messageBox -message "X has won"
            return 1
        } elseif {[string match $pattern $o]} {
            tk_messageBox -message "O has won"
            return 2
        }
    }
 
    if {[string length $x$o] == 9} {
        tk_messageBox -message "A draw"
        return 3
    }
 
    return 0
}
 
button .new  -text "New Game" -command newGame
button .exit -text "Exit"     -command exit
pack .new .exit -fill x
 
newGame

See also: Tic Tac Toe