Chewbakka85

Tic Tac Toe TCL-Script

Jan 15th, 2021
645
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
TCL 8.91 KB | None | 0 0
  1. ##+##########################################################################
  2.  #
  3.  # tictactoe.tcl - plays tic tac toe
  4.  # by Keith Vetter  Sept 10, 2004
  5.  #
  6.  # Sept 11 : added 2 levels of computer opponent: random and smart--which
  7.  #            will win if possible, block if necessary or do a random move
  8.  
  9.  package require Tk
  10.  
  11.  array set S {title "Tic Tac Toe" who,1 "X" who,0 "" who,-1 "O" robot "0"}
  12.  array set C {bars red X blue O green win yellow}
  13.  namespace eval ::Robot {
  14.     variable skill Smart
  15.  }
  16.  
  17.  proc DoDisplay {} {
  18.     wm title . $::S(title)
  19.     frame .ctrl -relief ridge -bd 2 -padx 5 -pady 5
  20.     canvas .c -relief raised -bd 2 -height 500 -width 500 -highlightthickness 0
  21.     pack .c -side top -fill both -expand 1
  22.     pack .ctrl -side top -fill both
  23.  
  24.     bind all <Key-F2> {console show}
  25.     bind .c <Configure> {ReCenter %W %h %w}
  26.     DoCtrlFrame
  27.  }
  28.  proc DrawBoard {{redraw 0}} {
  29.     global S B GAME C
  30.  
  31.     if {$redraw} {                              ;# Must redraw everything
  32.         .c delete all
  33.         set w2 [expr {$B(w2) - 15}]             ;# Make a little margins
  34.         set h2 [expr {$B(h2) - 15}]
  35.         set hbar [expr {$h2 / 3.0}]
  36.         set vbar [expr {$w2 / 3.0}]
  37.  
  38.         set B(0) [list -$w2   -$h2   -$vbar -$hbar] ;# All 9 cells
  39.         set B(1) [list -$vbar -$h2    $vbar -$hbar]
  40.         set B(2) [list  $vbar -$h2    $w2   -$hbar]
  41.         set B(3) [list -$w2   -$hbar -$vbar  $hbar]
  42.         set B(4) [list -$vbar -$hbar  $vbar  $hbar]
  43.         set B(5) [list  $vbar -$hbar  $w2    $hbar]
  44.         set B(6) [list -$w2    $hbar -$vbar  $h2]
  45.         set B(7) [list -$vbar  $hbar  $vbar  $h2]
  46.         set B(8) [list  $vbar  $hbar  $w2    $h2]
  47.  
  48.         for {set i 0} {$i < 9} {incr i} {       ;# Rectangle for each cell
  49.             .c create rect $B($i) -tag b$i -fill {} -outline {}
  50.             .c bind b$i <Button-1> [list DoClick $i]
  51.             set B($i) [ShrinkBox $B($i) 25]
  52.         }
  53.         .c create line -$w2 $hbar $w2 $hbar -tag bar ;# Draw the cross bars
  54.         .c create line -$w2 -$hbar $w2 -$hbar -tag bar
  55.         .c create line $vbar -$h2 $vbar $h2 -tag bar
  56.         .c create line -$vbar -$h2 -$vbar $h2 -tag bar
  57.         .c itemconfig bar -width 20 -fill $::C(bars) -capstyle round
  58.     }
  59.     .new config -state [expr {$GAME(tcnt) == 0 ? "disabled" : "normal"}]
  60.  
  61.     for {set i 0} {$i < 9} {incr i} {
  62.         .c itemconfig b$i -fill {}              ;# Erase any win lines
  63.         DrawXO $GAME(board,$i) $i
  64.     }
  65.     foreach i $GAME(win) {                      ;# Do we have a winner???
  66.         .c itemconfig b$i -fill $C(win)
  67.     }
  68.  }
  69.  proc DoCtrlFrame {} {
  70.     button .new -text "New Game" -command NewGame -bd 4
  71.     .new configure -font "[font actual [.new cget -font]] -weight bold"
  72.     option add *Button.font [.new cget -font]
  73.     label .status -textvariable S(msg) -font {Times 36 bold} -bg white \
  74.         -bd 5 -relief ridge
  75.     button .about -text About -command \
  76.         [list tk_messageBox -message "$::S(title)\nby Keith Vetter, Sept 2004"]
  77.  
  78.     frame .r -bd 2 -relief ridge
  79.     pack .r -side bottom
  80.     label .r.lc -text "Computer" -font [.new cget -font]
  81.     label .r.lrobot -text "Plays: "
  82.     spinbox .r.robot -values {O None X} -textvariable S(robot) -wrap 1 \
  83.         -width 6 -justify center -command ::Robot::IsTurn
  84.     label .r.llevel -text "Level: "
  85.     spinbox .r.level -values {Smart Random} -textvariable ::Robot::skill \
  86.         -wrap 1 -width 8 -justify center
  87.     grid .r.lc - -row 0
  88.     grid .r.lrobot .r.robot -sticky we
  89.     grid .r.llevel .r.level -sticky we
  90.  
  91.  
  92.     pack .status -in .ctrl -side right -fill both -expand 1
  93.     pack .r -in .ctrl -side right -fill both -padx 5
  94.     pack .new .about -in .ctrl -side top -fill x -pady 2
  95.  }
  96.  proc ShrinkBox {xy d} {
  97.     foreach {x y x1 y1} $xy break
  98.     return [list [expr {$x+$d}] [expr {$y+$d}] [expr {$x1-$d}] [expr {$y1-$d}]]
  99.  }
  100.  ##+##########################################################################
  101.  #
  102.  # Recenter -- keeps 0,0 at the center of the canvas during resizing
  103.  #
  104.  proc ReCenter {W h w} {                   ;# Called by configure event
  105.     set ::B(h2) [expr {$h / 2}]
  106.     set ::B(w2) [expr {$w / 2}]
  107.     $W config -scrollregion [list -$::B(w2) -$::B(h2) $::B(w2) $::B(h2)]
  108.     DrawBoard 1
  109.  }
  110.  ##+##########################################################################
  111.  #
  112.  # DrawXO -- draws appropriate mark in a given cell
  113.  #
  114.  proc DrawXO {who where} {
  115.     global S B C
  116.  
  117.     if {$S(who,$who) eq "X"} {          
  118.         foreach {x0 y0 x1 y1} $B($where) break
  119.         .c create line $x0 $y0 $x1 $y1 -width 20 -fill $C(X) -capstyle round \
  120.             -tag xo$where
  121.         .c create line $x0 $y1 $x1 $y0 -width 20 -fill $C(X) -capstyle round \
  122.             -tag xo$where
  123.     } elseif {$S(who,$who) eq "O"} {
  124.         .c create oval $B($where) -width 20 -outline $C(O) -tag xo$where
  125.     } else {
  126.         .c delete xo$where
  127.     }
  128.  }
  129.  ##+##########################################################################
  130.  #
  131.  # InitGame -- resets all variables to start a new game
  132.  #
  133.  proc InitGame {} {
  134.     global GAME S
  135.  
  136.     set GAME(state) play
  137.     set GAME(turn) 1
  138.     set GAME(tcnt) 0
  139.     set GAME(win) {}
  140.     for {set i 0} {$i < 9} {incr i} {
  141.         set GAME(board,$i) 0
  142.     }
  143.     set S(msg) "X starts"
  144.  }
  145.  ##+##########################################################################
  146.  #
  147.  # NewGame -- starts a new game
  148.  #
  149.  proc NewGame {} {
  150.     InitGame
  151.     DrawBoard
  152.     if {$::S(who,$::GAME(turn)) == $::S(robot)} {
  153.         after idle ::Robot::Go
  154.     }
  155.  }
  156.  ##+##########################################################################
  157.  #
  158.  # DoClick -- handles button click in a cell
  159.  #
  160.  proc DoClick {where} {
  161.     global GAME S
  162.  
  163.     if {$GAME(state) ne "play"} return          ;# Game over
  164.     if {$GAME(board,$where) != 0} return        ;# Not empty
  165.     set GAME(board,$where) $GAME(turn)
  166.     set GAME(turn) [expr {- $GAME(turn)}]
  167.     incr GAME(tcnt)
  168.     set S(msg) "$S(who,$GAME(turn))'s turn"
  169.  
  170.     set n [WhoWon]                              ;# Do we have a winner???
  171.     if {$n != 0} {
  172.         set GAME(state) finished
  173.         set GAME(win) [lrange $n 1 end]
  174.         set S(msg) "$S(who,[lindex $n 0]) Wins!"
  175.     } elseif {$GAME(tcnt) == 9} {               ;# Is the game a draw???
  176.         set GAME(state) finished
  177.         set S(msg) "Draw"
  178.     }
  179.     DrawBoard
  180.     if {$S(who,$GAME(turn)) == $S(robot)} {
  181.         after idle ::Robot::Go
  182.     }
  183.  }
  184.  ##+##########################################################################
  185.  #
  186.  # WhoWon -- determines if anyone has won the game
  187.  #
  188.  proc WhoWon {} {
  189.     foreach {a b c} {0 1 2 3 4 5 6 7 8 0 3 6 1 4 7 2 5 8 0 4 8 2 4 6} {
  190.         set who $::GAME(board,$a)
  191.         if {$who == 0} continue
  192.         if {$who != $::GAME(board,$b) || $who != $::GAME(board,$c)} continue
  193.         return [list $who $a $b $c]
  194.     }
  195.     return 0
  196.  }
  197.  ##+##########################################################################
  198.  #
  199.  # ::Robot::Go -- gets and does robot's move
  200.  #
  201.  proc ::Robot::Go {} {
  202.     variable skill
  203.     if {$::GAME(state) ne "play"} return        ;# Game over
  204.     set move [::Robot::$skill]
  205.     if {$move == {}} return
  206.     ::DoClick $move
  207.  }
  208.  proc ::Robot::Random {} {                       ;# Picks a random move
  209.     set empty {}
  210.     for {set i 0} {$i < 9} {incr i} {
  211.         if {$::GAME(board,$i) == 0} {
  212.             lappend empty $i
  213.         }
  214.     }
  215.     return [lindex $empty [expr {int(rand() * [llength $empty])}]]
  216.  }
  217.  ##+##########################################################################
  218.  #
  219.  # ::Robot::Smart -- does winning move if possible, blocks if necessary
  220.  # or does a random move
  221.  #
  222.  proc ::Robot::Smart {} {
  223.     global GAME
  224.  
  225.     set blockers {}
  226.     foreach {aa bb cc} {0 1 2 3 4 5 6 7 8 0 3 6 1 4 7 2 5 8 0 4 8 2 4 6} {
  227.         set a $GAME(board,$aa)
  228.         set b $GAME(board,$bb)
  229.         set c $GAME(board,$cc)
  230.         if {$a * $b * $c != 0} continue         ;# No empty slots
  231.         if {$a + $b + $c == 2*$GAME(turn)} {    ;# Winning move
  232.             if {$a == 0} { return $aa}
  233.             if {$b == 0} { return $bb}
  234.             if {$c == 0} { return $cc}
  235.             error "no empty spot"               ;# Can't happen
  236.         }
  237.         if {$a + $b + $c == -2*$GAME(turn)} {   ;# Blocking move
  238.             if {$a == 0} { lappend blockers $aa}
  239.             if {$b == 0} { lappend blockers $bb}
  240.             if {$c == 0} { lappend blockers $cc}
  241.         }
  242.     }
  243.     if {$blockers != {}} {
  244.         return [lindex $blockers [expr {int(rand() * [llength $blockers])}]]
  245.     }
  246.     return [::Robot::Random]
  247.  }
  248.  ##+##########################################################################
  249.  #
  250.  # ::Robot::IsTurn -- called when who robot is changes and we may need to move
  251.  #
  252.  proc ::Robot::IsTurn {} {
  253.     if {$::S(who,$::GAME(turn)) == $::S(robot)} {
  254.         after idle ::Robot::Go
  255.     }
  256.  }    
  257.  
  258.  InitGame
  259.  DoDisplay
  260.  NewGame
Add Comment
Please, Sign In to add comment