
Task

Play a game of tic-tac-toe.

Ensure that legal moves are played and that a winning position is notified.


Tic-tac-toe   is also known as:

   naughts and crosses 
   tic tac toe 
   tick tack toe 
   three in a row 
   tres en rayo       and
   Xs  and  Os


See also
   MathWorld™, Tic-Tac-Toe game.
   Wikipedia tic-tac-toe.


 
# Play TicTacToe
 
$define E " " # empty square
$define X "X" # X piece
$define O "O" # O piece
 
# -- define a board 
record Board(a, b, c, d, e, f, g, h, i)
 
procedure display_board (board, player)
  write ("\n===============")
  write (board.a || " | " || board.b || " | " || board.c)
  write ("---------")
  write (board.d || " | " || board.e || " | " || board.f)
  write ("---------")
  write (board.g || " | " || board.h || " | " || board.i)
end
 
# return a set of valid moves (empty positions) in given board
procedure empty_fields (board)
  fields := set()
  every i := !fieldnames(board) do 
    if (board[i] == E) then insert (fields, i)
  return fields
end
 
procedure game_start ()
  return Board (E, E, E, E, E, E, E, E, E)
end
 
procedure game_is_drawn (board)
  return *empty_fields(board) = 0
end
 
procedure game_won_by (board, player)
  return (board.a == board.b == board.c == player) |
         (board.d == board.e == board.f == player) |
         (board.g == board.h == board.i == player) |
         (board.a == board.d == board.g == player) | 
         (board.b == board.e == board.h == player) |
         (board.c == board.f == board.i == player) |
         (board.a == board.e == board.i == player) |
         (board.g == board.e == board.c == player)
end
 
procedure game_over (board)
  return game_is_drawn (board) | game_won_by (board, O) | game_won_by (board, X)
end
 
# -- players make their move on the board
#    assume there is at least one empty square
 
procedure human_move (board, player)
  choice := "z"
  options := empty_fields (board)
  # keep prompting until player selects a valid square
  until member (options, choice) do {
    writes ("Choose one of: ")
    every writes (!options || " ")
    writes ("\n> ") 
    choice := read ()
  }
  board[choice] := player
end
 
# pick and make a move at random from empty positions
procedure random_move (board, player)
  board[?empty_fields(board)] := player
end
 
# -- manage the game play
procedure play_game ()
  # hold procedures for players' move in variables
  player_O := random_move
  player_X := human_move
 
  # randomly determine if human or computer moves first
  if (?2 = 0) 
    then {
      write ("Human plays first as O")
      player_O := human_move
      player_X := random_move
    }
    else write ("Computer plays first, human is X")
 
  # set up the game to start
  board := game_start ()
  player := O
  display_board (board, player)
  # loop until the game is over, getting each player to move in turn
  until game_over (board) do { 
    write (player || " to play next")
    # based on player, prompt for the next move
    if (player == O)
      then (player_O (board, player))
      else (player_X (board, player))
    # change player to move
    player := if (player == O) then X else O
    display_board (board, player)
  }
  # finish by writing out result
  if game_won_by (board, O) 
    then write ("O won") 
    else if game_won_by (board, X) 
      then write ("X won")
      else write ("draw") # neither player won, so must be a draw
end
 
# -- get things started
procedure main ()
  play_game ()
end
 