Tic-tac-toe: Difference between revisions

From Rosetta Code
Content added Content deleted
mNo edit summary
m (Simplified code to turn off flashing cursor)
Line 2,294: Line 2,294:
</pre>
</pre>


=={{header|XPL0}}==
[[File:TTTXPL0.GIF|right]]
[[File:TTTXPL0.GIF|right]]
<lang XPL0>\The computer marks its moves with an "O" and the player uses an "X". The
<lang XPL0>\The computer marks its moves with an "O" and the player uses an "X". The
Line 2,315: Line 2,314:
int X, O; \bit arrays for player and computer
int X, O; \bit arrays for player and computer
\ bit 0 corresponds to playing square 1, etc.
\ bit 0 corresponds to playing square 1, etc.

func CallInt(Int, AX, BX, CX, DX, BP, DS, ES); \Call software interrupt
int Int, AX, BX, CX, DX, BP, DS, ES; \(unused arguments need not be passed)
int CpuReg;
[CpuReg:= GetReg;
CpuReg(0):= AX;
CpuReg(1):= BX;
CpuReg(2):= CX;
CpuReg(3):= DX;
CpuReg(6):= BP;
CpuReg(9):= DS;
CpuReg(11):= ES;
SoftInt(Int);
return CpuReg(0); \return AX register
]; \CallInt




Line 2,407: Line 2,391:


proc PlayGame; \Play one game
proc PlayGame; \Play one game
int I, V, V0;
[ChOut(0, $0C\FF\); \clear screen with a form feed
[ChOut(0, $0C\FF\); \clear screen with a form feed
HLine(X0-1, Y0+1); \draw grid (#)
HLine(X0-1, Y0+1); \draw grid (#)
Line 2,445: Line 2,428:




int CpuReg;
[SetVid(1); \set 40x25 text mode
[SetVid(1); \set 40x25 text mode
CallInt($10, $0100, 0, $2000); \turn off annoying flashing cursor
CpuReg:= GetReg; \turn off annoying flashing cursor
CpuReg(0):= $0100; \ with BIOS interrupt 10h, function 01h
CpuReg(2):= $2000; \set cursor type to disappear
SoftInt($10);
loop [PlayGame;
loop [PlayGame;
Key:= ChIn(1); \keep playing games until Esc key is hit
Key:= ChIn(1); \keep playing games until Esc key is hit

Revision as of 05:27, 10 May 2012

Task
Tic-tac-toe
You are encouraged to solve this task according to the task description, using any language you may know.

Play a game of tic-tac-toe. Ensure that legal moves are played and that a winning position is notified.

Ada

<lang Ada>with Ada.Text_IO, Ada.Numerics.Discrete_Random;

 -- can play human-human, human-computer, computer-human or computer-computer
 -- the computer isn't very clever: it just chooses a legal random move

procedure Tic_Tac_Toe is

  type The_Range is range 1 .. 3;
  type Board_Type is array (The_Range, The_Range) of Character;
  package Rand is new Ada.Numerics.Discrete_Random(The_Range);
  Gen: Rand.Generator; -- required for the random moves
  procedure Show_Board(Board: Board_Type) is
     use Ada.Text_IO;
  begin
     for Row in The_Range loop
        for Column in The_Range loop
           Put(Board(Row, Column));
        end loop;
        Put_Line("");
     end loop;
     Put_Line("");
  end Show_Board;
  function Find_Winner(Board: Board_Type) return Character is
     -- if 'x' or 'o' wins, it returns that, else it returns ' '
     function Three_Equal(A,B,C: Character) return Boolean is
     begin
        return (A=B) and (A=C);
     end Three_Equal;
  begin -- Find_Winner
     for I in The_Range loop
        if    Three_Equal(Board(I,1), Board(I,2), Board(I,3)) then
           return Board(I,1);
        elsif  Three_Equal(Board(1,I), Board(2,I), Board(3,I)) then
           return Board(1,I);
        end if;
     end loop;
     if Three_Equal(Board(1,1), Board(2,2), Board (3,3)) or
        Three_Equal(Board(3,1), Board(2,2), Board (1,3)) then
        return Board(2,2);
     end if;
     return ' ';
  end Find_Winner;
  procedure Do_Move(Board: in out Board_Type;
                    New_Char: Character; Computer_Move: Boolean) is
     Done: Boolean := False;
     C: Character;
     use Ada.Text_IO;
     procedure Do_C_Move(Board: in out Board_Type; New_Char: Character) is
        Found: Boolean := False;
        X,Y: The_Range;
     begin
        while not Found loop
           X := Rand.Random(Gen);
           Y := Rand.Random(Gen);
           if (Board(X,Y) /= 'x') and  (Board(X,Y) /= 'o') then
              Found := True;
              Board(X,Y) := New_Char;
           end if;
        end loop;
     end Do_C_Move;
  begin
     if Computer_Move then
        Do_C_Move(Board, New_Char);
     else -- read move;
        Put_Line("Choose your move, " & New_Char);
        while not Done loop
           Get(C);
           for Row in The_Range loop
              for Col in The_Range loop
                 if Board(Row, Col) = C then
                    Board(Row, Col) := New_Char;
                    Done := True;
                 end if;
              end loop;
           end loop;
        end loop;
     end if;
  end Do_Move;
  The_Board : Board_Type := (('1','2','3'), ('4','5','6'), ('7','8','9'));
  Cnt_Moves: Natural := 0;
  Players: array(0 .. 1) of Character := ('x', 'o'); -- 'x' begins
  C_Player: array(0 .. 1) of Boolean := (False, False);
  Reply: Character;

begin -- Tic_Tac_Toe

  -- firstly, ask whether the computer shall take over either player
  for I in Players'Range loop
     Ada.Text_IO.Put_Line("Shall " & Players(I) &
                            " be run by the computer? (y=yes)");
     Ada.Text_IO.Get(Reply);
     if Reply='y' or Reply='Y' then
        C_Player(I) := True;
        Ada.Text_IO.Put_Line("Yes!");
     else
        Ada.Text_IO.Put_Line("No!");
     end if;
  end loop;
  Rand.Reset(Gen); -- to initalize the random generator
  -- now run the game
  while (Find_Winner(The_Board) = ' ') and (Cnt_Moves < 9) loop
     Show_Board(The_Board);
     Do_Move(The_Board, Players(Cnt_Moves mod 2), C_Player(Cnt_Moves mod 2));
     Cnt_Moves := Cnt_Moves + 1;
  end loop;
  Ada.Text_IO.Put_Line("This is the end!");
  -- finally, output the outcome
  Show_Board (The_Board);
  if Find_Winner(The_Board) = ' ' then
     Ada.Text_IO.Put_Line("Draw");
  else
     Ada.Text_IO.Put_Line("The winner is: " & Find_Winner(The_Board));
  end if;

end Tic_Tac_Toe;</lang>


Output:

> ./tic_tac_toe 
Shall x be run by the computer? (y=yes)
y
Yes!
Shall o be run by the computer? (y=yes)
n
No!
123
456
789

1x3
456
789

Choose your move, o
5
1x3
4o6
789

1x3
xo6
789

Choose your move, o
6
1x3
xoo
789

1xx
xoo
789

Choose your move, o
1
oxx
xoo
789

oxx
xoo
78x

Choose your move, o
7
oxx
xoo
o8x

This is the end!
oxx
xoo
oxx

Draw

> ./tic_tac_toe 
Shall x be run by the computer? (y=yes)
n
No!
Shall o be run by the computer? (y=yes)
y
Yes!
123
456
789

Choose your move, x
6
123
45x
789

123
45x
o89

Choose your move, x
4
123
x5x
o89

123
x5x
o8o

Choose your move, x
8
123
x5x
oxo

o23
x5x
oxo

Choose your move, x
5
This is the end!
o23
xxx
oxo

The winner is: x


AutoHotkey

This program uses a Gui with 9 buttons. Clicking on one will place an X there, disable the button, and cause the program to go somewhere. It plays logically, trying to win, trying to block, or playing randomly in that order. <lang AutoHotkey>Gui, Add, Button, x12 y12 w30 h30 vB1 gButtonHandler, Gui, Add, Button, x52 y12 w30 h30 vB2 gButtonHandler, Gui, Add, Button, x92 y12 w30 h30 vB3 gButtonHandler, Gui, Add, Button, x12 y52 w30 h30 vB4 gButtonHandler, Gui, Add, Button, x52 y52 w30 h30 vB5 gButtonHandler, Gui, Add, Button, x92 y52 w30 h30 vB6 gButtonHandler, Gui, Add, Button, x12 y92 w30 h30 vB7 gButtonHandler, Gui, Add, Button, x52 y92 w30 h30 vB8 gButtonHandler, Gui, Add, Button, x92 y92 w30 h30 vB9 gButtonHandler,

Generated using SmartGUI Creator 4.0

Gui, Show, x127 y87 h150 w141, Tic-Tac-Toe Winning_Moves := "123,456,789,147,258,369,159,357" Return

ButtonHandler:

   ; Fired whenever the user clicks on an enabled button
   Go(A_GuiControl,"X")
   GoSub MyMove

Return

MyMove: ; Loops through winning moves. First attempts to win, then to block, then a random move

   Went=0
   Loop, parse, Winning_Moves,`,
   {
       Current_Set := A_LoopField
       X:=O:=0
       Loop, parse, Current_Set
       {
           GuiControlGet, Char,,Button%A_LoopField%
           If ( Char = "O" )
               O++
           If ( Char = "X" )
               X++
       }
       If ( O = 2 and X = 0 ) or ( X = 2 and O = 0 ){
           Finish_Line(Current_Set)
           Went = 1
           Break ; out of the Winning_Moves Loop to ensure the computer goes only once
       }
   }
   If (!Went)
       GoSub RandomMove

Return

Go(Control,chr){

   GuiControl,,%Control%, %chr%
   GuiControl,Disable,%Control%
   GoSub, CheckWin

}

CheckWin:

   Loop, parse, Winning_Moves,`,
   {
       Current_Set := A_LoopField
       X:=O:=0
       Loop, parse, Current_Set
       {
           GuiControlGet, Char,,Button%A_LoopField%
           If ( Char = "O" )
               O++
           If ( Char = "X" )
               X++
       }
       If ( O = 3 ){
           Msgbox O Wins!
           GoSub DisableAll
           Break
       }
       If ( X = 3 ){
           MsgBox X Wins!
           GoSub DisableAll
           Break
       }
   }

return

DisableAll:

   Loop, 9
       GuiControl, Disable, Button%A_Index%

return

Finish_Line(Set){ ; Finish_Line is called when a line exists with 2 of the same character. It goes in the remaining spot, thereby blocking or winning.

   Loop, parse, set
   {
       GuiControlGet, IsEnabled, Enabled, Button%A_LoopField%
       Control=Button%A_LoopField%
       If IsEnabled
           Go(Control,"O")
   }

}

RandomMove:

   Loop{
       Random, rnd, 1, 9
       GuiControlGet, IsEnabled, Enabled, Button%rnd%
       If IsEnabled
       {
           Control=Button%rnd%
           Go(Control,"O")
           Break
       }
   }

return

GuiClose: ExitApp </lang>

C

Opening alternates between human and computer. Computer never loses. <lang C>#include <stdio.h>

  1. include <stdlib.h>

int b[3][3]; /* board. 0: blank; -1: computer; 1: human */

int check_winner() { int i; for (i = 0; i < 3; i++) { if (b[i][0] && b[i][1] == b[i][0] && b[i][2] == b[i][0]) return b[i][0]; if (b[0][i] && b[1][i] == b[0][i] && b[2][i] == b[0][i]) return b[0][i]; } if (!b[1][1]) return 0;

if (b[1][1] == b[0][0] && b[2][2] == b[0][0]) return b[0][0]; if (b[1][1] == b[2][0] && b[0][2] == b[1][1]) return b[1][1];

return 0; }

void showboard() { char *t = "X O"; int i, j; for (i = 0; i < 3; i++, putchar('\n')) for (j = 0; j < 3; j++) printf("%c ", t[ b[i][j] + 1 ]); printf("-----\n"); }

  1. define for_ij for (i = 0; i < 3; i++) for (j = 0; j < 3; j++)

int best_i, best_j; int test_move(int val, int depth) { int i, j, score; int best = -1, changed = 0;

if ((score = check_winner())) return (score == val) ? 1 : -1;

for_ij { if (b[i][j]) continue;

changed = b[i][j] = val; score = -test_move(-val, depth + 1); b[i][j] = 0;

if (score <= best) continue; if (!depth) { best_i = i; best_j = j; } best = score; }

return changed ? best : 0; }

char* game(int user) { int i, j, k, move, win = 0; for_ij b[i][j] = 0;

printf("Board postions are numbered so:\n1 2 3\n4 5 6\n7 8 9\n"); printf("You have O, I have X.\n\n"); for (k = 0; k < 9; k++, user = !user) { while(user) { printf("your move: "); if (!scanf("%d", &move)) { scanf("%*s"); continue; } if (--move < 0 || move >= 9) continue; if (b[i = move / 3][j = move % 3]) continue;

b[i][j] = 1; break; } if (!user) { if (!k) { /* randomize if computer opens, less boring */ best_i = rand() % 3; best_j = rand() % 3; } else test_move(-1, 0);

b[best_i][best_j] = -1; printf("My move: %d\n", best_i * 3 + best_j + 1); }

showboard(); if ((win = check_winner())) return win == 1 ? "You win.\n\n": "I win.\n\n"; } return "A draw.\n\n"; }

int main() { int first = 0; while (1) printf(game(first = !first)); return 0; }</lang>

D

<lang d>import std.stdio, std.string, std.algorithm, std.conv,

      std.random, std.ascii, std.array, std.range;

struct GameBoard {

   char[9] board = "123456789".dup;
   enum char human = 'X',
             computer = 'O';
   enum GameState { going, humanWins, computerWins, draw }
   const pure nothrow invariant() {
       foreach (i, c; board)
           if (isDigit(c))
               assert(i == c - '1'); // in correct position?
           else
               assert(c == human || c == computer);
   }
   string toString() const {
       string row(in int start) {
           return format("%s|%s|%s\n", board[start + 0],
                                       board[start + 1],
                                       board[start + 2]);
       }
       enum string lineSep = "-+-+-\n";
       return row(0) ~ lineSep ~ row(3) ~ lineSep ~ row(6);
   }
   int[] availablePositions() const pure nothrow {
       //auto p = filter!((i){ return isDigit(board[i]); })(iota(9));
       //return array(p);
       int[] result;
       foreach (i; 0 .. 9)
           if (isDigit(board[i]))
               result ~= i;
       return result;
   }
   bool isAvailable(in int position) const pure nothrow {
       if (position < 0 || position >= 9)
           return false;
       return isDigit(board[position]);
   }
   bool finished() const pure nothrow {
       return winner() != GameState.going;
   }
   GameState winner() const pure nothrow {
       enum wins = [[0, 1, 2], [3, 4, 5], [6, 7, 8],
                    [0, 3, 6], [1, 4, 7], [2, 5, 8],
                    [0, 4, 8], [2, 4, 6]];
       foreach (win; wins) {
           win[0] = 1;
           immutable char desired = board[win[0]];
           if (isDigit(desired))
               continue; // nobody wins on this one
           // the same player needs to take all three positions
           if (board[win[1]] == desired && board[win[2]] == desired)
               return desired == GameBoard.human ?
                                 GameState.humanWins :
                                 GameState.computerWins;
       }
       if (availablePositions().length == 0)
           return GameState.draw;
       return GameState.going;
   }
   int computerMove() const // random move
   out(ret) {
       assert(ret >= 0 && ret < 9);
       assert(isAvailable(ret));
   } body {
       // return choice(availablePositions());
       return randomCover(availablePositions(),
                          Random(unpredictableSeed)).front;
   }

}


GameBoard playGame() {

   GameBoard board;
   bool playsHuman = true;
   while (!board.finished()) {
       writeln(board);
       int move;
       if (playsHuman) {
           do {
               writef("Your move (available moves %s)? ",
                      map!q{ a + 1 }(board.availablePositions()));
               readf("%d\n", &move);
               move--; // zero based indexing
               if (move < 0)
                   return board;
           } while (!board.isAvailable(move));
       } else
           move = board.computerMove();
       assert(board.isAvailable(move));
       writefln("%s chose %d", playsHuman ? "You" : "I", move + 1);
       board.board[move] = playsHuman ? GameBoard.human :
                                        GameBoard.computer;
       playsHuman = !playsHuman; // switch player
   }
   return board;

}


void main() {

   writeln("Tic-tac-toe game player.");
   immutable GameBoard.GameState outcome = playGame().winner();
   final switch (outcome) {
       case GameBoard.GameState.going:
           writeln("Game stopped.");
           break;
       case GameBoard.GameState.humanWins:
           writeln("\nYou win!");
           break;
       case GameBoard.GameState.computerWins:
           writeln("\nI win.");
           break;
       case GameBoard.GameState.draw:
           writeln("\nDraw");
           break;
   }

}</lang> Output example:

Tic-tac-toe game player.
1|2|3
-+-+-
4|5|6
-+-+-
7|8|9

Your move (available moves [1, 2, 3, 4, 5, 6, 7, 8, 9])? 1
You chose 1
X|2|3
-+-+-
4|5|6
-+-+-
7|8|9

I chose 4
X|2|3
-+-+-
O|5|6
-+-+-
7|8|9

Your move (available moves [2, 3, 5, 6, 7, 8, 9])? 5
You chose 5
X|2|3
-+-+-
O|X|6
-+-+-
7|8|9

I chose 9
X|2|3
-+-+-
O|X|6
-+-+-
7|8|O

Your move (available moves [2, 3, 6, 7, 8])? 3
You chose 3
X|2|X
-+-+-
O|X|6
-+-+-
7|8|O

I chose 6
X|2|X
-+-+-
O|X|O
-+-+-
7|8|O

Your move (available moves [2, 7, 8])? 7
You chose 7

You win!

F#

A purely-functional solution with a naive (but perfect) computer player implementation. The first move is played randomly by the computer.

<lang fsharp>type Brick =

| Empty
| Computer
| User

let brickToString = function

| Empty -> ' '
| Computer -> 'X'
| User -> 'O'

// y -> x -> Brick type Board = Map<int, Map<int, Brick> >

let emptyBoard =

 let emptyRow = Map.ofList [0,Empty; 1,Empty; 2,Empty]
 Map.ofList [0,emptyRow; 1,emptyRow; 2,emptyRow]

let get (b:Board) (x,y) = b.[y].[x]

let set player (b:Board) (x,y) : Board =

 let row = b.[y].Add(x, player)
 b.Add(y, row)

let winningPositions =

 [for x in [0..2] -> x,x] // first diagonal
 ::[for x in [0..2] -> 2-x,x] // second diagonal
 ::[for y in [0..2] do
    yield! [[for x in [0..2]->(y,x)]; // columns
            [for x in [0..2] -> (x,y)]]] // rows
 

let hasWon player board =

 List.exists
   (fun ps -> List.forall (fun pos -> get board pos = player) ps)
   winningPositions

let freeSpace board =

 [for x in 0..2 do
    for y in 0..2 do
      if get board (x,y) = Empty then yield x,y]

type Evaluation =

| Win
| Draw
| Lose

let rec evaluate board move =

 let b2 = set Computer board move
 if hasWon Computer b2 then Win
 else
   match freeSpace b2 with
   | [] -> Draw
   | userChoices -> 
      let b3s = List.map (set User b2) userChoices
      if List.exists (hasWon User) b3s then Lose
      elif List.exists (fun b3 -> bestOutcome b3 = Lose) b3s
      then Lose
      elif List.exists (fun b3 -> bestOutcome b3 = Draw) b3s
      then Draw
      else Win
       

and findBestChoice b =

 match freeSpace b with
 | [] -> ((-1,-1), Draw)
 | choices -> 
   match List.tryFind (fun c -> evaluate b c = Win) choices with
   | Some c -> (c, Win)
   | None -> match List.tryFind (fun c -> evaluate b c = Draw) choices with
             | Some c -> (c, Draw)
             | None -> (List.head choices, Lose)

and bestOutcome b = snd (findBestChoice b)

let bestChoice b = fst (findBestChoice b)

let computerPlay b = set Computer b (bestChoice b)

let printBoard b =

 printfn "   | A | B | C |"
 printfn "---+---+---+---+"
 for y in 0..2 do
  printfn " %d | %c | %c | %c |"
   (3-y)
   (get b (0,y) |> brickToString)
   (get b (1,y) |> brickToString)
   (get b (2,y) |> brickToString)
  printfn "---+---+---+---+"

let rec userPlay b =

 printfn "Which field do you play? (format: a1)"
 let input = System.Console.ReadLine()
 if input.Length <> 2
    || input.[0] < 'a' || input.[0] > 'c'
    || input.[1] < '1' || input.[1] > '3' then
    printfn "illegal input"
    userPlay b
 else
    let x = int(input.[0]) - int('a')
    let y = 2 - int(input.[1]) + int('1')
    if get b (x,y) <> Empty then
      printfn "Field is not free."
      userPlay b
    else
      set User b (x,y)

let rec gameLoop b player =

 if freeSpace b = [] then
   printfn "Game over. Draw."
 elif player = Computer then
   printfn "Computer plays..."
   let b2 = computerPlay b
   printBoard b2
   if hasWon Computer b2 then
     printfn "Game over. I have won."
   else
     gameLoop b2 User
 elif player = User then
   let b2 = userPlay b
   printBoard b2
   if hasWon User b2 then
     printfn "Game over. You have won."
   else
     gameLoop b2 Computer

// randomly choose an element of a list let choose =

 let rnd = new System.Random()
 fun (xs:_ list) -> xs.[rnd.Next(xs.Length)]

// play first brick randomly printfn "Computer starts." let b = set Computer emptyBoard (choose (freeSpace emptyBoard)) printBoard b gameLoop b User</lang>

Example game:

Computer starts.
   | A | B | C |
---+---+---+---+
 3 |   |   | X |
---+---+---+---+
 2 |   |   |   |
---+---+---+---+
 1 |   |   |   |
---+---+---+---+
Which field do you play? (format: a1)
a1
   | A | B | C |
---+---+---+---+
 3 |   |   | X |
---+---+---+---+
 2 |   |   |   |
---+---+---+---+
 1 | O |   |   |
---+---+---+---+
Computer plays...
   | A | B | C |
---+---+---+---+
 3 | X |   | X |
---+---+---+---+
 2 |   |   |   |
---+---+---+---+
 1 | O |   |   |
---+---+---+---+
Which field do you play? (format: a1)
b3
   | A | B | C |
---+---+---+---+
 3 | X | O | X |
---+---+---+---+
 2 |   |   |   |
---+---+---+---+
 1 | O |   |   |
---+---+---+---+
Computer plays...
   | A | B | C |
---+---+---+---+
 3 | X | O | X |
---+---+---+---+
 2 |   |   |   |
---+---+---+---+
 1 | O |   | X |
---+---+---+---+
Which field do you play? (format: a1)
c2
   | A | B | C |
---+---+---+---+
 3 | X | O | X |
---+---+---+---+
 2 |   |   | O |
---+---+---+---+
 1 | O |   | X |
---+---+---+---+
Computer plays...
   | A | B | C |
---+---+---+---+
 3 | X | O | X |
---+---+---+---+
 2 |   | X | O |
---+---+---+---+
 1 | O |   | X |
---+---+---+---+
Game over. I have won.

Go

Intermediate, like Python's "Better skilled player." Computer wins and blocks where it can, but otherwise plays randomly. Plays multiple games and keeps score. Player gets first move of first game. Afterwards, loser gets to go first, or after cat games, first move alternates. <lang go>package main

import (

   "bufio"
   "fmt"
   "math/rand"
   "os"
   "strings"

)

var b []byte

func printBoard() {

   fmt.Printf("%s\n%s\n%s\n", b[0:3], b[3:6], b[6:9])

}

var pScore, cScore int var pMark, cMark byte = 'X', 'O' var in = bufio.NewReader(os.Stdin)

func main() {

   b = make([]byte, 9)
   fmt.Println("Play by entering a digit.")
   for {
       // start of game
       for i := range b {
           b[i] = '1' + byte(i)
       }
       computerStart := cMark == 'X'
       if computerStart {
           fmt.Println("I go first, playing X's")
       } else {
           fmt.Println("You go first, playing X's")
       }
   TakeTurns:
       for {
           if !computerStart {
               if !playerTurn() {
                   return
               }
               if gameOver() {
                   break TakeTurns
               }
           }
           computerStart = false
           computerTurn()
           if gameOver() {
               break TakeTurns
           }
       }
       fmt.Println("Score: you", pScore, "me", cScore)
       fmt.Println("\nLet's play again.")
   }

}

func playerTurn() bool {

   var pm string
   var err error
   for i := 0; i < 3; i++ { // retry loop
       printBoard()
       fmt.Printf("%c's move? ", pMark)
       if pm, err = in.ReadString('\n'); err != nil {
           fmt.Println(err)
           return false
       }
       pm = strings.TrimSpace(pm)
       if pm >= "1" && pm <= "9" && b[pm[0]-'1'] == pm[0] {
           x := pm[0] - '1'
           b[x] = pMark
           return true
       }
   }
   fmt.Println("You're not playing right.")
   return false

}

var choices = make([]int, 9)

func computerTurn() {

   printBoard()
   var x int
   defer func() {
       fmt.Println("My move:", x+1)
       b[x] = cMark
   }()
   // look for two in a row
   block := -1
   for _, l := range lines {
       var mine, yours int
       x = -1
       for _, sq := range l {
           switch b[sq] {
           case cMark:
               mine++
           case pMark:
               yours++
           default:
               x = sq
           }
       }
       if mine == 2 && x >= 0 {
           return // strategy 1: make winning move
       }
       if yours == 2 && x >= 0 {
           block = x
       } 
   }
   if block >= 0 {
       x = block // strategy 2: make blocking move
       return
   }
   // default strategy: random move
   choices = choices[:0]
   for i, sq := range b { 
       if sq == '1'+byte(i) {
           choices = append(choices, i)
       }
   }
   x = choices[rand.Intn(len(choices))]

}

func gameOver() bool {

   // check for win
   for _, l := range lines {
       if b[l[0]] == b[l[1]] && b[l[1]] == b[l[2]] {
           printBoard()
           if b[l[0]] == cMark {
               fmt.Println("I win!")
               cScore++
               pMark, cMark = 'X', 'O'
           } else {
               fmt.Println("You win!")
               pScore++
               pMark, cMark = 'O', 'X'
           }
           return true 
       } 
   }
   // check for empty squares
   for i, sq := range b {
       if sq == '1'+byte(i) {
           return false
       }
   }
   fmt.Println("Cat game.")
   pMark, cMark = cMark, pMark
   return true

}

var lines = [][]int{

   {0, 1, 2}, // rows
   {3, 4, 5},
   {6, 7, 8},
   {0, 3, 6}, // columns
   {1, 4, 7},
   {2, 5, 8},
   {0, 4, 8}, // diagonals
   {2, 4, 6},

}</lang>

Icon and Unicon

The following works in both Icon and Unicon. The computer plays randomly against a human player, with legal moves enforced and wins/draws notified.

<lang Icon>

  1. Play TicTacToe

$define E " " # empty square $define X "X" # X piece $define O "O" # O piece

  1. -- 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

  1. 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

  1. -- players make their move on the board
  2. 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

  1. pick and make a move at random from empty positions

procedure random_move (board, player)

 board[?empty_fields(board)] := player

end

  1. -- 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

  1. -- get things started

procedure main ()

 play_game ()

end </lang>

J

To subsequent j poster: replacing this entry is fine by me. <lang J> Note 'ttt adjudicates or plays'

  use:  markers ttt characters
  characters represent the cell names.
  markers is a length 3 character vector of the
  characters to use for first and second player
  followed by the opponent's mark.
  'XOX' means X plays 1st, O is the other mark,
  and the first strategy plays 1st.
  'XOO' means X plays 1st, O is the other mark,
  and the second strategy moves first.
  The game  is set  up for  the computer as the
  first strategy (random), and human as second.
  A standard use:
     'XOX'ttt'abcdefghijkl'
  Example game reformatted w/ emacs artist-mode
  to fit your display:
     '#-#'ttt'wersdfxcv'
  w│e│r                     w│e│r        ....    -│e│r           .    -│e│#
  ─┼─┼─                 .   ─┼─┼─       ..       ─┼─┼─         ..     ─┼─┼─
  s│d│f                 .   s│#│f      ..        s│#│f        ..      -│#│f
  ─┼─┼─                ..   ─┼─┼─      .         ─┼─┼─      ...       ─┼─┼─
  x│c│v               ..    -│c│v      .         -│c│#     ..         -│c│#
  d                  ..     v         ..         r         .          VICTORY
  w│e│r             ..      w│e│r     ..         -│e│#     .
  ─┼─┼─           ...       ─┼─┼─     ..         ─┼─┼─    .
  s│#│f         ...         s│#│f     ..         s│#│f    .
  ─┼─┼─        ..           ─┼─┼─   ...          ─┼─┼─  ...
  x│c│v                     -│c│#                -│c│#
  -->cell for -?            -->cell for -?       -->cell for -?
  x                         w                    s

)

while=: conjunction def 'u ^: v ^:_' NB. j assumes while is a verb and needs to know while is a conjunction.

ttt=: outcome@:((display@:move) while undecided)@:display@:prepare

blindfolded_variant=: outcome@:display@:(move while undecided)@:display@:prepare

outcome=: {&(>;:'kitty VICTORY')@:won NB. (outcome does not pass along the state) move=: post locate undecided=: won nor full prepare=: , board@:] NB. form the state vector

Note 'locate'

 is a monadic verb.  y is the state vector.
 returns the character of the chosen cell.
 Generally:
 locate=: second_strategy`first_strategy@.(first = mark)
 Simplified:
 locate=: human_locate NB. human versus human

) locate=: human_locate`computer_locate@.(first = mark)

display=: show [: (1 1,:5 5)&(];.0)@:": [: <"0 fold

computer_locate=: [: show@{. board -. marks NB. strategy: first available computer_locate=: [: show@({~ ?@:#) board -. marks NB. strategy: random

human_locate=: monad define

 state=. y
 m=. mark state
 b=. board state
 cells=. b -. marks state
 show '-->cell for ' , m , '?'
 whilst. cell -.@:e. cells do. cell =. {. (1!:1]1) , m end.

)

post=: 2&A.@:(3&{.)@:[ prepare mark@:[`((i.~ board)~)`(board@:[)}

mark=: {. NB. mark of the current player from state marks=: 2&{. NB. extract both markers from state board=: _9&{. NB. extract board from state first=: 2&{ NB. extract first player from state

show=: [ smoutput

full=: 2 = #@:~. won=: test@:fold fold=: 3 3 $ board test=: [: any [: all [: infix_pairs_agree |:@:lines

lines=: , diagonal , diagonal@:|. , |: diagonal=: (<0 1)&|: all=: *./ any=: +./ nor=: 8 b. infix_pairs_agree=: 2&(=/\) </lang>

Perl 6

Works with: Rakudo version 2011.11

The computer plays a random game. Output is formatted in a similar way to that of the better python version. <lang Perl6> use v6;

my @board = 1..9; my @winning-positions = [0..2], [3..5], [6..8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [6,4,2];

sub get-winner() { for @winning-positions { return (@board[$_][0], $_) if [eq] @board[$_]; } }

sub free-indexes() { @board.keys.grep: { @board[$_] eq any(1..9) } }

sub ai-move() { given free-indexes.pick { @board[$_] = 'o'; say "I go at: { $_ + 1 }\n"; } }

sub print-board() { say @board.map({ "$^a | $^b | $^c" }).join("\n--+---+--\n"), "\n"; }

sub human-move() { my $pos = prompt "Choose one of { (free-indexes >>+>> 1).join(",") }: "; if $pos eq any(free-indexes >>+>> 1) { @board[$pos - 1] = 'x'; } else { say "Sorry, you want to put your 'x' where?"; human-move(); } }

for (&ai-move, &human-move) xx * { print-board; .(); last if get-winner() or not free-indexes; }

if get-winner() -> ($player, $across) { say "$player wins across [", ($across >>+>> 1).join(", "), "]."; } else { say "How boring, a draw!"; } </lang>

PicoLisp

This solution doesn't bother about the game logic, but simply uses the alpha-beta-pruning 'game' function in the "simul" library. <lang PicoLisp>(load "@lib/simul.l") # for 'game' function

(de display ()

  (for Y (3 2 1)
     (prinl "   +---+---+---+")
     (prin " " Y)
     (for X (1 2 3)
        (prin " | " (or (get *Board X Y) " ")) )
     (prinl " |") )
  (prinl "   +---+---+---+")
  (prinl "     a   b   c") )

(de find3 (P)

  (find
     '((X Y DX DY)
        (do 3
           (NIL (= P (get *Board X Y)))
           (inc 'X DX)
           (inc 'Y DY)
           T ) )
     (1 1 1 1 2 3 1 1)
     (1 2 3 1 1 1 1 3)
     (1 1 1 0 0 0 1 1)
     (0 0 0 1 1 1 1 -1) ) )

(de myMove ()

  (when
     (game NIL 8
        '((Flg)     # Moves
           (unless (find3 (or (not Flg) 0))
              (make
                 (for (X . L) *Board
                    (for (Y . P) L
                       (unless P
                          (link
                             (cons
                                (cons X Y (or Flg 0))
                                (list X Y) ) ) ) ) ) ) ) )
        '((Mov) # Move
           (set (nth *Board (car Mov) (cadr Mov)) (cddr Mov)) )
        '((Flg)     # Cost
           (if (find3 (or Flg 0)) -100 0) ) )
     (let Mov (caadr @)
        (set (nth *Board (car Mov) (cadr Mov)) 0) )
     (display) ) )

(de yourMove (X Y)

  (and
     (sym? X)
     (>= 3 (setq X (- (char X) 96)) 1)
     (num? Y)
     (>= 3 Y 1)
     (not (get *Board X Y))
     (set (nth *Board X Y) T)
     (display) ) )

(de main ()

  (setq *Board (make (do 3 (link (need 3)))))
  (display) )

(de go Args

  (cond
     ((not (yourMove (car Args) (cadr Args)))
        "Illegal move!" )
     ((find3 T) "Congratulation, you won!")
     ((not (myMove)) "No moves")
     ((find3 0) "Sorry, you lost!") ) )</lang>

Output:

: (main)
   +---+---+---+
 3 |   |   |   |
   +---+---+---+
 2 |   |   |   |
   +---+---+---+
 1 |   |   |   |
   +---+---+---+
     a   b   c

: (go a 1)
   +---+---+---+
 3 |   |   |   |
   +---+---+---+
 2 |   |   |   |
   +---+---+---+
 1 | T |   |   |
   +---+---+---+
     a   b   c
   +---+---+---+
 3 |   |   |   |
   +---+---+---+
 2 |   | 0 |   |
   +---+---+---+
 1 | T |   |   |
   +---+---+---+
     a   b   c

Prolog

Works with SWI-Prolog.
Uses a minimax algorithm with no Alpha-beta pruning, as the max depth of the recursion is 8. Computer never loses.
A GUI interface written in XPCE is given. <lang Prolog>:- use_module('min-max.pl').

-dynamic box/2.
- dynamic tic_tac_toe_window/1.

% Computer begins. tic-tac-toe(computer) :- V is random(9), TTT = [_,_,_,_,_,_ ,_,_,_], nth0(V, TTT, o), display_tic_tac_toe(TTT).

% Player begins tic-tac-toe(me) :- TTT = [_,_,_,_,_,_ ,_,_,_], display_tic_tac_toe(TTT).


display_tic_tac_toe(TTT) :- retractall(box(_,_)), retractall(tic_tac_toe_window(_)), new(D, window('Tic-tac-Toe')), send(D, size, size(170,170)), X = 10, Y = 10, display(D, X, Y, 0, TTT), assert(tic_tac_toe_window(D)), send(D, open).

display(_, _, _, _, []).

display(D, X, Y, N, [A,B,C|R]) :- display_line(D, X, Y, N, [A,B,C]), Y1 is Y+50, N3 is N+3, display(D, X, Y1, N3, R).


display_line(_, _, _, _, []). display_line(D, X, Y, N, [C|R]) :- ( nonvar(C)-> C1 = C; C1 = ' '), new(B, tic_tac_toe_box(C1)), assertz(box(N, B)), send(D, display, B, point(X, Y)), X1 is X + 50, N1 is N+1, display_line(D, X1, Y, N1, R).


% class tic_tac_toe_box % display an 'x' when the player clicks % display an 'o' when the computer plays

- pce_begin_class(tic_tac_toe_box, box, "Graphical window with text").

variable(mess, any, both, "text to display").

initialise(P, Lbl) :-> send(P, send_super, initialise), send(P, slot, mess, Lbl), WS = 50, HS = 50, send(P, size, size(WS,HS)), send(P, recogniser, handler_group(new(click_gesture(left, , single, message(@receiver, my_click))))).

% the box is clicked my_click(B) :-> send(B, set_val, x), send(@prolog, play).

% only works when the box is "free" set_val(B, Val) :-> get(B, slot, mess, ' '), send(B, slot, mess, Val), send(B, redraw), send(B, flush).


% redefined method to display custom graphical objects. '_redraw_area'(P, A:area) :-> send(P, send_super, '_redraw_area', A), %we display the text get(P, slot, mess, Lbl), new(Str1, string(Lbl)), get_object(P, area, area(X,Y,W,H)), send(P, draw_box, X, Y, W, H), send(P, draw_text, Str1, font(times, normal, 30), X, Y, W, H, center, center).

- pce_end_class.

play :- numlist(0, 8, L), maplist(init, L, TTT), finished(x, TTT, Val), ( Val = 2 -> send(@display, inform,'You win !'), tic_tac_toe_window(D), send(D, destroy) ; ( Val = 1 -> send(@display, inform,'Draw !'), tic_tac_toe_window(D), send(D, destroy)  ; next_move(TTT, TT1), maplist(display, L, TT1), finished(o, TT1, V), ( V = 2 -> send(@display, inform,'I win !'), tic_tac_toe_window(D), send(D, destroy) ; ( V = 1 -> send(@display, inform,'Draw !'), tic_tac_toe_window(D), send(D, destroy)  ; true)))).


% use minmax to compute the next move next_move(TTT, TT1) :- minimax(o, 0, 1024, TTT, _V1- TT1).


% we display the new board display(I, V) :- nonvar(V), box(I, V1), send(V1, set_val, V).

display(_I, _V).

% we create the board for minmax init(I, V) :- box(I, V1), get(V1, slot, mess, V), V \= ' '.

init(_I, _V).

% winning position for the player P ? winned(P, [A1, A2, A3, A4, A5, A6, A7, A8, A9]) :-

      (is_winning_line(P, [A1, A2, A3]);

is_winning_line(P, [A4, A5, A6]); is_winning_line(P, [A7, A8, A9]); is_winning_line(P, [A1, A4, A7]); is_winning_line(P, [A2 ,A5, A8]); is_winning_line(P, [A3, A6, A9]); is_winning_line(P, [A1, A5, A9]); is_winning_line(P, [A3, A5, A7])).


is_winning_line(P, [A, B, C]) :- nonvar(A), A = P, nonvar(B), B = P, nonvar(C), C = P.

% Winning position for the player eval(Player, Deep, TTT, V) :- winned(Player, TTT), ( Player = o -> V is 1000 - 50 * Deep; V is -1000+ 50 * Deep).

% Loosing position for the player eval(Player, Deep, TTT, V) :- select(Player, [o,x], [Player1]), winned(Player1, TTT), ( Player = x -> V is 1000 - 50 * Deep; V is -1000+ 50 * Deep).

% Draw position eval(_Player, _Deep, TTT, 0) :- include(var, TTT, []).


% we fetch the free positions of the board possible_move(TTT, LMove) :- new(C, chain), forall(between(0,8, I), ( nth0(I, TTT, X), ( var(X) -> send(C, append, I); true))), chain_list(C, LMove).

% we create the new position when the player P clicks % the box "N" assign_move(P, TTT, N, TT1) :- copy_term(TTT, TT1), nth0(N, TT1, P).

% We fetch all the possible boards obtained from board TTT % for the player P get_next(Player, Deep, TTT, Player1, Deep1, L):- possible_move(TTT, LMove), select(Player, [o,x], [Player1]), Deep1 is Deep + 1, maplist(assign_move(Player, TTT), LMove, L).


% The game is over ? % Player P wins finished(P, TTT, 2) :- winned(P, TTT).

% Draw finished(_P, TTT, 1) :- include(var, TTT, []).

% the game is not over finished(_P, _TTT, 0) .

% minmax must knows when the computer plays % (o for ordinateur in French) computer(o).

</lang> Module min-max.pl defines minimax algorithm. <lang prolog>:- module('min-max.pl', [minimax/5]).

% minimax(Player, Deep, MaxDeep, B, V-B) % @arg1 : current player at this level % @arg2 : current level of recursion % @arg3 : max level of recursion (in this version of the game no use : set to 1024 !) % @arg4 : current board % @arg5 : B is the evaluation of the board, the result is V-B to know the new board

% Here we get an evaluation minimax(Player, Deep, MaxDeep, B, V-B) :- ( eval(Player, Deep, B, V) -> true ; % in this version of the game this second division always fails ( Deep > MaxDeep -> V is random(1000) - 1000)).

% here we must compute all the possible moves to know the evaluation of the board minimax(Player, Deep, MaxDeep, B, V) :- get_next(Player, Deep, B, Player1, Deep1, L), maplist(minimax(Player1, Deep1, MaxDeep), L, LV), maplist(lie, L, LV, TLV), sort(TLV, SLVTmp), ( computer(Player) -> reverse(SLVTmp, SLV); SLV = SLVTmp), SLV = [V | _R].


lie(TTT, V-_, V-TTT).

</lang>

Python

The computer enforces the rules but plays a random game. <lang python>

   Tic-tac-toe game player.
   Input the index of where you wish to place your mark at your turn.

import random

board = list('123456789') wins = ((0,1,2), (3,4,5), (6,7,8),

       (0,3,6), (1,4,7), (2,5,8),
       (0,4,8), (2,4,6))

def printboard():

   print('\n'.join(' '.join(board[x:x+3]) for x in(0,3,6)))

def score():

   for w in wins:
       b = board[w[0]]
       if b in 'XO' and all (board[i] == b for i in w):
           return b, [i+1 for i in w]
   return None, None

def finished():

   return all (b in 'XO' for b in board)

def space():

   return [ b for b in board if b not in 'XO']

def my_turn(xo):

   options = space()
   choice = random.choice(options)
   board[int(choice)-1] = xo
   return choice

def your_turn(xo):

   options = space()
   while True:
       choice = input(" Put your %s in any of these positions: %s "
                      % (xo, .join(options))).strip()
       if choice in options:
           break
       print( "Whoops I don't understand the input" )
   board[int(choice)-1] = xo
   return choice

def me(xo='X'):

   printboard()
   print('I go at', my_turn(xo))
   return score()
   assert not s[0], "\n%s wins across %s" % s

def you(xo='O'):

   printboard()
   # Call my_turn(xo) below for it to play itself
   print('You went at', your_turn(xo))
   return score()
   assert not s[0], "\n%s wins across %s" % s


print(__doc__) while not finished():

   s = me('X')
   if s[0]:
       printboard()
       print("\n%s wins across %s" % s)
       break
   if not finished():
       s = you('O')
       if s[0]:
           printboard()
           print("\n%s wins across %s" % s)
           break

else:

   print('\nA draw')

</lang>

Sample Game

    Tic-tac-toe game player.
    Input the index of where you wish to place your mark at your turn.

1 2 3
4 5 6
7 8 9
I go at 9
1 2 3
4 5 6
7 8 X
 Put your O in any of these positions: 12345678 1
You went at 1
O 2 3
4 5 6
7 8 X
I go at 3
O 2 X
4 5 6
7 8 X
 Put your O in any of these positions: 245678 4
You went at 4
O 2 X
O 5 6
7 8 X
I go at 2
O X X
O 5 6
7 8 X
 Put your O in any of these positions: 5678 7
You went at 7
O X X
O 5 6
O 8 X

O wins across [1, 4, 7]

Better skilled player

In this version, The computer player will first complete a winning line of its own if it can, otherwise block a winning line of its opponent if they have two in a row, or then choose a random move.

<lang python>

   Tic-tac-toe game player.
   Input the index of where you wish to place your mark at your turn.

import random

board = list('123456789') wins = ((0,1,2), (3,4,5), (6,7,8),

       (0,3,6), (1,4,7), (2,5,8),
       (0,4,8), (2,4,6))

def printboard():

   print('\n-+-+-\n'.join('|'.join(board[x:x+3]) for x in(0,3,6)))

def score(board=board):

   for w in wins:
       b = board[w[0]]
       if b in 'XO' and all (board[i] == b for i in w):
           return b, [i+1 for i in w]
   return None

def finished():

   return all (b in 'XO' for b in board)

def space(board=board):

   return [ b for b in board if b not in 'XO']

def my_turn(xo, board):

   options = space()
   choice = random.choice(options)
   board[int(choice)-1] = xo
   return choice

def my_better_turn(xo, board):

   'Will return a next winning move or block your winning move if possible'
   ox = 'O' if xo =='X' else 'X'
   oneblock = None
   options  = [int(s)-1 for s in space(board)]
   for choice in options:
       brd = board[:]
       brd[choice] = xo
       if score(brd):
           break
       if oneblock is None:
           brd[choice] = ox
           if score(brd):
               oneblock = choice
   else:
       choice = oneblock if oneblock is not None else random.choice(options)
   board[choice] = xo
   return choice+1

def your_turn(xo, board):

   options = space()
   while True:
       choice = input("\nPut your %s in any of these positions: %s "
                      % (xo, .join(options))).strip()
       if choice in options:
           break
       print( "Whoops I don't understand the input" )
   board[int(choice)-1] = xo
   return choice

def me(xo='X'):

   printboard()
   print('\nI go at', my_better_turn(xo, board))
   return score()

def you(xo='O'):

   printboard()
   # Call my_turn(xo, board) below for it to play itself
   print('\nYou went at', your_turn(xo, board))
   return score()


print(__doc__) while not finished():

   s = me('X')
   if s:
       printboard()
       print("\n%s wins along %s" % s)
       break
   if not finished():
       s = you('O')
       if s:
           printboard()
           print("\n%s wins along %s" % s)
           break

else:

   print('\nA draw')</lang>

Sample output

    Tic-tac-toe game player.
    Input the index of where you wish to place your mark at your turn.

1|2|3
-+-+-
4|5|6
-+-+-
7|8|9

I go at 2
1|X|3
-+-+-
4|5|6
-+-+-
7|8|9

Put your O in any of these positions: 13456789 5

You went at 5
1|X|3
-+-+-
4|O|6
-+-+-
7|8|9

I go at 1
X|X|3
-+-+-
4|O|6
-+-+-
7|8|9

Put your O in any of these positions: 346789 3

You went at 3
X|X|O
-+-+-
4|O|6
-+-+-
7|8|9

I go at 7
X|X|O
-+-+-
4|O|6
-+-+-
X|8|9

Put your O in any of these positions: 4689 4

You went at 4
X|X|O
-+-+-
O|O|6
-+-+-
X|8|9

I go at 6
X|X|O
-+-+-
O|O|X
-+-+-
X|8|9

Put your O in any of these positions: 89 9

You went at 9
X|X|O
-+-+-
O|O|X
-+-+-
X|8|O

I go at 8

A draw

Ruby

<lang ruby>module TicTacToe

 class Game
   def initialize(player1Class, player2Class)
     @board = Array.new(10)
     @free_positions = (1..9).to_a
     @players = [player1Class.new(self), player2Class.new(self)]
     @turn = rand(2)
     puts "#{@players[@turn]} goes first."
     @players[@turn].marker = "X"
     @players[nextTurn].marker = "O"
     @winning_rows = [[1,2,3],[4,5,6],[7,8,9],[1,4,7],[2,5,8],[3,6,9],[1,5,9],[3,5,7]]
   end
   attr_reader :free_positions, :winning_rows, :board
   def play
     loop do
       player = @players[@turn]
       idx = player.select
       puts "#{player} selects #{player.marker} position #{idx}"
       @board[idx] = player.marker
       @free_positions.delete(idx)
       # check for a winner
       @winning_rows.each do |row|
         if row.all? {|idx| @board[idx] == player.marker}
           puts "#{player} wins!"
           print
           return
         end
       end
       # no winner, is board full?
       if @free_positions.empty?
         puts "It's a draw."
         print
         return
       end
       nextTurn!
     end
   end
   def nextTurn
     (@turn + 1) % 2
   end
   def nextTurn!
     @turn = nextTurn
   end
   def opponent
     @players[nextTurn]
   end
   def print
     puts [1,2,3].map {|i| @board[i].nil? ? i : @board[i]}.join("|")
     puts "-+-+-"
     puts [4,5,6].map {|i| @board[i].nil? ? i : @board[i]}.join("|")
     puts "-+-+-"
     puts [7,8,9].map {|i| @board[i].nil? ? i : @board[i]}.join("|")
   end
 end
 class Player
   def initialize(game)
     @game = game
     @marker = nil
   end
   attr_accessor :marker
 end
 class HumanPlayer < Player
   def initialize(game)
     super(game)
   end
   def select
     @game.print
     loop do
       print "Select your #{marker} position: "
       selection = $stdin.gets.to_i
       if not @game.free_positions.include?(selection)
         puts "Position #{selection} is not available. Try again."
         next
       end
       return selection
     end
   end
   def to_s
     "Human"
   end
 end
 class ComputerPlayer < Player
   def initialize(game)
     super(game)
   end
   def group_row(row, opponent)
     markers = {self.marker => [], opponent.marker => [], nil => []} .
               merge(row.group_by {|idx| @game.board[idx]})
     #p [row, markers].inspect
     markers
   end
   def select
     index = nil
     opponent = @game.opponent
     # look for winning rows
     @game.winning_rows.each do |row|
       markers = group_row(row, opponent)
       if markers[self.marker].length == 2 and markers[nil].length == 1
         return markers[nil].first
       end
     end
     # look for opponent's winning rows to block
     @game.winning_rows.each do |row|
       markers = group_row(row, opponent)
       if markers[opponent.marker].length == 2 and markers[nil].length == 1
         return markers[nil].first
       end
     end
     # need some logic here to get the computer to pick a smarter position
     # simply pick a position in order of preference
     [5].concat([1,3,7,9].shuffle).concat([2,4,6,8].shuffle).each do |pos|
       return pos if @game.free_positions.include?(pos)
     end
   end
   def to_s
     "Computer"
   end
 end

end

TicTacToe::Game.new(TicTacToe::ComputerPlayer, TicTacToe::ComputerPlayer).play TicTacToe::Game.new(TicTacToe::HumanPlayer, TicTacToe::ComputerPlayer).play</lang>

sample output

Computer goes first.
Computer selects X position 5
Computer selects O position 7
Computer selects X position 3
Computer selects O position 1
Computer selects X position 4
Computer selects O position 6
Computer selects X position 9
Computer selects O position 2
Computer selects X position 8
It's a draw.
O|O|X
-+-+-
X|X|O
-+-+-
O|X|X
Computer goes first.
Computer selects X position 5
1|2|3
-+-+-
4|X|6
-+-+-
7|8|9
Select your O position: 8
Human selects O position 8
Computer selects X position 1
X|2|3
-+-+-
4|X|6
-+-+-
7|O|9
Select your O position: 9
Human selects O position 9
Computer selects X position 7
X|2|3
-+-+-
4|X|6
-+-+-
X|O|O
Select your O position: 4
Human selects O position 4
Computer selects X position 3
Computer wins!
X|2|X
-+-+-
O|X|6
-+-+-
X|O|O

Tcl

Translation of: Python

<lang tcl>package require Tcl 8.6

  1. This code splits the players from the core game engine

oo::class create TicTacToe {

   variable board player letter who
   constructor {player1class player2class} {

set board {1 2 3 4 5 6 7 8 9} set player(0) [$player1class new [self] [set letter(0) "X"]] set player(1) [$player2class new [self] [set letter(1) "O"]] set who 0

   }
   method PrintBoard {} {

lassign $board a1 b1 c1 a2 b2 c2 a3 b3 c3 puts [format " %s | %s | %s" $a1 $b1 $c1] puts "---+---+---" puts [format " %s | %s | %s" $a2 $b2 $c2] puts "---+---+---" puts [format " %s | %s | %s" $a3 $b3 $c3]

   }
   method WinForSomeone {} {

foreach w { {0 1 2} {3 4 5} {6 7 8} {0 3 6} {1 4 7} {2 5 8} {0 4 8} {2 4 6} } { set b [lindex $board [lindex $w 0]] if {$b ni "X O"} continue foreach i $w {if {[lindex $board $i] ne $b} break} if {[lindex $board $i] eq $b} { foreach p $w {lappend w1 [expr {$p+1}]} return [list $b $w1] } } return ""

   }
   method status {} {

return $board

   }
   method IsDraw {} {

foreach b $board {if {[string is digit $b]} {return false}} return true

   }
   method legalMoves {} {

foreach b $board {if {[string is digit $b]} {lappend legal $b}} return $legal

   }
   method DoATurn {} {

set legal [my legalMoves] my PrintBoard while 1 { set move [$player($who) turn] if {$move in $legal} break puts "Illegal move!" } lset board [expr {$move - 1}] $letter($who) $player($who) describeMove $move set who [expr {1 - $who}] return [my WinForSomeone]

   }
   method game {} {
       puts "    Tic-tac-toe game player.
   Input the index of where you wish to place your mark at your turn.\n"

while {![my IsDraw]} { set winner [my DoATurn] if {$winner eq ""} continue lassign $winner winLetter winSites my PrintBoard puts "\n$winLetter wins across \[[join $winSites {, }]\]" return $winLetter } puts "\nA draw"

   }

}

  1. Stupid robotic player

oo::class create RandomRoboPlayer {

   variable g
   constructor {game letter} {

set g $game

   }
   method turn {} {

set legal [$g legalMoves] return [lindex $legal [expr {int(rand()*[llength $legal])}]]

   }
   method describeMove {move} {

puts "I go at $move"

   }

}

  1. Interactive human player delegate

oo::class create HumanPlayer {

   variable g char
   constructor {game letter} {

set g $game set char $letter

   }
   method turn {} {

set legal [$g legalMoves] puts ">>> Put your $char in any of these positions: [join $legal {}]" while 1 { puts -nonewline ">>> " flush stdout gets stdin number if {$number in $legal} break puts ">>> Whoops I don't understand the input!" } return $number

   }
   method describeMove {move} {

puts "You went at $move"

   }

}

  1. Assemble the pieces

set ttt [TicTacToe new HumanPlayer RandomRoboPlayer] $ttt game</lang> Sample game:

    Tic-tac-toe game player.
    Input the index of where you wish to place your mark at your turn.

 1 | 2 | 3
---+---+---
 4 | 5 | 6
---+---+---
 7 | 8 | 9
>>> Put your X in any of these positions: 123456789
>>> 1
You went at 1
 X | 2 | 3
---+---+---
 4 | 5 | 6
---+---+---
 7 | 8 | 9
I go at 5
 X | 2 | 3
---+---+---
 4 | O | 6
---+---+---
 7 | 8 | 9
>>> Put your X in any of these positions: 2346789
>>> 7
You went at 7
 X | 2 | 3
---+---+---
 4 | O | 6
---+---+---
 X | 8 | 9
I go at 9
 X | 2 | 3
---+---+---
 4 | O | 6
---+---+---
 X | 8 | O
>>> Put your X in any of these positions: 23468
>>> 4
You went at 4
 X | 2 | 3
---+---+---
 X | O | 6
---+---+---
 X | 8 | O

X wins across [1, 4, 7]

<lang XPL0>\The computer marks its moves with an "O" and the player uses an "X". The \ numeric keypad is used to make the player's move. \ \ 7 | 8 | 9 \ ---+---+--- \ 4 | 5 | 6 \ ---+---+--- \ 1 | 2 | 3 \ \The player always goes first, but the 0 key is used to skip a move. Thus \ it can be used to let the computer play first. Esc terminates program.

inc c:\cxpl\codes; \intrinsic routine declarations def X0=16, Y0=10; \coordinates of character in upper-left square int I0,

       PMove,          \player's move (^0..^9)
       Key;            \keystroke

int X, O; \bit arrays for player and computer

                       \ bit 0 corresponds to playing square 1, etc.


proc HLine(X, Y); \Draw a horizontal line int X, Y; int I; [Cursor(X, Y); for I:= 0 to 10 do ChOut(0, ^Ä); ]; \HLine


proc VLine(X, Y); \Draw a vertical line over the above horizontal line int X, Y; int I; [for I:= 0 to 4 do

       [Cursor(X, Y+I);
       ChOut(0, if I&1 then ^Å else ^³);
       ];

]; \VLine


func Won(p); \Return 'true' if player P has won int P; int T, I; [T:= [$007, $038, $1C0, $049, $092, $124, $111, $054]; for I:= 0 to 7 do \check if player matches a bit pattern for 3 in a row

   if (P & T(I)) = T(I) then return true;

return false; ]; \Won


func Cats; \Return 'true' if no more moves available (Cat's game) [if (X ! O) = $1FF then \all bit positions played

       [Cursor(17, 20);
       Text(0, "A draw!");
       return true;
       ];

return false; ]; \Cats


proc DoMove(P, M, Ch); \Make move in player's bit array and display it int P, \address of player's bit array

       M,              \index 0..8 where bit is placed
       Ch;

int I, X, Y; [P(0):= P(0) ! 1<<M; \make move

I:= M / 3; \display move X:= Rem(0) * 4; Y:= (2-I) * 2; Cursor(X+X0, Y+Y0); ChOut(0, Ch); ]; \DoMove


func Try(P); \Return the value of the best node for player P int P; \address of player's bit array int P1, I, I0, V, V0; [P1:= if P = addr X then addr O else addr X;

if Won(P1(0)) then return -1; if (X ! O) = $1FF then return 0;

V0:= -1; \assume the worst for I:= 0 to 8 do \for all of the squares...

   if ((O!X) & 1<<I) = 0 then          \if square is unused
       [P(0):= P(0) ! 1<<I;            \make tenative move
       V:= -(extend(Try(P1)));         \get value
       if V > V0 then                  \save best value
               [V0:= V;  I0:= I];
       P(0):= P(0) & ~(1<<I);          \undo tenative move
       ];

return V0 & $FF ! I0<<8; ]; \Try


proc PlayGame; \Play one game [ChOut(0, $0C\FF\); \clear screen with a form feed HLine(X0-1, Y0+1); \draw grid (#) HLine(X0-1, Y0+3); VLine(X0+2, Y0); VLine(X0+6, Y0);

X:= 0; O:= 0; \initialize player's bit arrays to empty loop [loop [PMove:= ChIn(1); \GET PLAYER'S MOVE (X)

               if PMove = $1B\Esc\ then
                       [SetVid(3);  exit];     \restore display and end program
               if PMove = ^0 then quit;
               if PMove>=^1 & PMove<=^9 &      \check for legal move
                  ((X!O) & 1<<(PMove-^1)) = 0 then quit;
               ChOut(0, 7\Bel\);               \beep the dude
               ];
       if PMove # ^0 then
               [DoMove(addr X, PMove-^1, ^X);
               if Won(X) then
                       [Cursor(17, 20);
                       Text(0, "X wins!");
                       quit;
                       ];
               ];
       if Cats then quit;
       I0:= Try(addr O) >>8;                   \GET COMPUTER'S MOVE (O)
       DoMove(addr O, I0, ^O);                 \do best move
       if Won(O) then
               [Cursor(17, 20);
               Text(0, "O wins!");
               quit;
               ];
       if Cats then quit;
       ];

]; \PlayGame


int CpuReg; [SetVid(1); \set 40x25 text mode CpuReg:= GetReg; \turn off annoying flashing cursor CpuReg(0):= $0100; \ with BIOS interrupt 10h, function 01h CpuReg(2):= $2000; \set cursor type to disappear SoftInt($10); loop [PlayGame;

       Key:= ChIn(1);                  \keep playing games until Esc key is hit
       if Key = $1B\Esc\ then
               [SetVid(3);  exit];     \clear screen & restore normal text mode
       ];

]</lang>