Tic-tac-toe

From Rosetta Code
Jump to: navigation, search
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.

Contents

[edit] 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;


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


[edit] 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.

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
 

[edit] bash

Computer is X. Computer randomly goes first. Computer plays a good game, but not a perfect game. It will win when it can and draw when it can not.

It performs a depth-first scan of all following moves. It ignores dumb actions like not winning when either player can.

For each possible move it records if it will win, lose, or something else (like win and draw depending on opponent's move).

If there is a choice of best moves, it picks one at random.

I have not used simple bash code to:

  1. keep it under 100 lines;
  2. to demonstrate usefulness of bash integers;
  3. show-off ANSI ESC sequences;
  4. implement recursion in bash;
  5. demonstrate conditional and alternate execution using && and || with { ...; };
  6. show that you don't always need to use $ to refer to integer variables;
  7. encourage use of [[ ]] instead of [ ] for boolean expressions;
  8. provide examples of pattern matching; and
  9. encourage use of bash for more interesting tasks.
 
#!/bin/bash
declare -a B=( e e e e e e e e e ) # Board
 
function show(){ # show B - underline first 2 rows; highlight position; number empty positoins
local -i p POS=${1:-9}; local UL BOLD="\e[1m" GREEN="\e[32m" DIM="\e[2m" OFF="\e[m" ULC="\e[4m"
for p in 0 1 2 3 4 5 6 7 8; do
[[ p%3 -eq 0 ]] && printf " " # indent boards
UL=""; [[ p/3 -lt 2 ]] && UL=$ULC # underline first 2 rows
[[ p -eq POS ]] && printf "$BOLD$GREEN" # bold and colour for this position
[[ ${B[p]} = e ]] && printf "$UL$DIM%d$OFF" $p || printf "$UL%s$OFF" ${B[p]} # num or UL
{ [[ p%3 -lt 2 ]] && printf "$UL | $OFF"; } || printf "\n" # underline vertical bars or NL
done
};
 
function win(){ # win 'X' 3 return true if X wins after move in position 3
local ME=$1; local -i p=$2
[[ ${B[p/3*3]} = $ME && ${B[p/3*3+1]} = $ME && ${B[p/3*3+2]} = $ME ]] && return 0 # row
[[ ${B[p]} = $ME && ${B[(p+3)%9]} = $ME && ${B[(p+6)%9]} = $ME ]] && return 0 # col
[[ ${B[4]} != $ME ]] && return 1 # don't test diags
[[ p%4 -eq 0 && ${B[0]} = $ME && ${B[8]} = $ME ]] && return 0 # TL - BR diag
[[ p%4 -eq 2 || p -eq 4 ]] && [[ ${B[2]} = $ME && ${B[6]} = $ME ]] # TR - BL diag
};
 
function bestMove(){ # return best move or 9 if none possible
local ME=$1 OP=$2; local -i o s p
local -ia S=( -9 -9 -9 -9 -9 -9 -9 -9 -9 ) # score board
local -a SB # save board
[[ ${B[*]//[!e]} = "" ]] && return 9 # game over
SB=( ${B[*]} ) # save Board
for p in 0 1 2 3 4 5 6 7 8; do # for each board position
[[ ${B[p]} != e ]] && continue # skip occupied positions
B[p]=$ME # occupy position
win $ME $p && { S[p]=2; B=( ${SB[*]} ); return $p; } # ME wins so this is best move
bestMove $OP $ME; o=$? # what will opponent do
[[ o -le 8 ]] && { B[o]=$OP; win $OP $o; s=$?; } # opponent can make a legal move
S[p]=${s:-1} # save result of opponent move
B=( ${SB[*]} ) # restore board after each trial run
done
local -i best=-1; local -ia MOV=()
for p in 0 1 2 3 4 5 6 7 8; do # find all best moves
[[ S[p] -lt 0 ]] && continue # dont bother with occupied positions
[[ S[p] -eq S[best] ]] && { MOV+=(p); best=p; } # add this move to current list
[[ S[p] -gt S[best] ]] && { MOV=(p); best=p; } # a better move so scrap list and start again
done
return ${MOV[ RANDOM%${#MOV[*]} ]} # pick one at random
};
 
function getMove(){ # getMove from opponent
[[ $ME = X ]] && { bestMove $ME $OP; return $?; } # pick X move automatically
read -p "O move: " -n 1; printf "\n"; return $REPLY # get opponents move
};
 
function turn(){ # turn starts or continues a game. It is ME's turn
local -i p; local ME=$1 OP=$2
getMove; p=$?; [[ p -gt 8 ]] && { printf "Draw!\n"; show; return 1; } # no move so a draw
B[p]=$ME; printf "%s moves %d\n" $ME $p # mark board
win $ME $p && { printf "%s wins!\n" $ME; show $p; [[ $ME = X ]] && return 2; return 0; }
[[ ${B[*]//[!e]} = "" ]] && { printf "Draw!\n"; show; return 1; } # no move so a draw
show $p; turn $OP $ME # opponent moves
};
 
printf "Bic Bash Bow\n"
show; [[ RANDOM%2 -eq 0 ]] && { turn O X; exit $?; } || turn X O
 
 

Output: (nice ANSI formatting is not shown)

Bic Bash Bow
  0 | 1 | 2
  3 | 4 | 5
  6 | 7 | 8
X moves 1
  0 | X | 2
  3 | 4 | 5
  6 | 7 | 8
O move: 5
O moves 5
  0 | X | 2
  3 | 4 | O
  6 | 7 | 8
X moves 2
  0 | X | X
  3 | 4 | O
  6 | 7 | 8
O move: 0
O moves 0
  O | X | X
  3 | 4 | O
  6 | 7 | 8
X moves 4
  O | X | X
  3 | X | O
  6 | 7 | 8
O move: 6
O moves 6
  O | X | X
  3 | X | O
  O | 7 | 8
X moves 7
X wins!
  O | X | X
  3 | X | O
  O | X | 8

[edit] C

Opening alternates between human and computer. Computer never loses.

#include <stdio.h>
#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()
{
const 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");
}
 
#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;
}
 
const 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("%s", game(first = !first));
return 0;
}

[edit] C++

 
#include <windows.h>
#include <iostream>
#include <string>
 
//--------------------------------------------------------------------------------------------------
using namespace std;
 
//--------------------------------------------------------------------------------------------------
enum players { Computer, Human, Draw, None };
const int iWin[8][3] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }, { 0, 3, 6 }, { 1, 4, 7 }, { 2, 5, 8 }, { 0, 4, 8 }, { 2, 4, 6 } };
 
//--------------------------------------------------------------------------------------------------
class ttt
{
public:
ttt() { _p = rand() % 2; reset(); }
 
void play()
{
int res = Draw;
while( true )
{
drawGrid();
while( true )
{
if( _p ) getHumanMove();
else getComputerMove();
 
drawGrid();
 
res = checkVictory();
if( res != None ) break;
 
++_p %= 2;
}
 
if( res == Human ) cout << "CONGRATULATIONS HUMAN --- You won!";
else if( res == Computer ) cout << "NOT SO MUCH A SURPRISE --- I won!";
else cout << "It's a draw!";
 
cout << endl << endl;
 
string r;
cout << "Play again( Y / N )? "; cin >> r;
if( r != "Y" && r != "y" ) return;
 
++_p %= 2;
reset();
 
}
}
 
private:
void reset()
{
for( int x = 0; x < 9; x++ )
_field[x] = None;
}
 
void drawGrid()
{
system( "cls" );
 
COORD c = { 0, 2 };
SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), c );
 
cout << " 1 | 2 | 3 " << endl;
cout << "---+---+---" << endl;
cout << " 4 | 5 | 6 " << endl;
cout << "---+---+---" << endl;
cout << " 7 | 8 | 9 " << endl << endl << endl;
 
int f = 0;
for( int y = 0; y < 5; y += 2 )
for( int x = 1; x < 11; x += 4 )
{
if( _field[f] != None )
{
COORD c = { x, 2 + y };
SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), c );
string o = _field[f] == Computer ? "X" : "O";
cout << o;
}
f++;
}
 
c.Y = 9;
SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), c );
}
 
int checkVictory()
{
for( int i = 0; i < 8; i++ )
{
if( _field[iWin[i][0]] != None &&
_field[iWin[i][0]] == _field[iWin[i][1]] && _field[iWin[i][1]] == _field[iWin[i][2]] )
{
return _field[iWin[i][0]];
}
}
 
int i = 0;
for( int f = 0; f < 9; f++ )
{
if( _field[f] != None )
i++;
}
if( i == 9 ) return Draw;
 
return None;
}
 
void getHumanMove()
{
int m;
cout << "Enter your move ( 1 - 9 ) ";
while( true )
{
m = 0;
do
{ cin >> m; }
while( m < 1 && m > 9 );
 
if( _field[m - 1] != None )
cout << "Invalid move. Try again!" << endl;
else break;
}
 
_field[m - 1] = Human;
}
 
void getComputerMove()
{
int move = 0;
 
do{ move = rand() % 9; }
while( _field[move] != None );
 
for( int i = 0; i < 8; i++ )
{
int try1 = iWin[i][0], try2 = iWin[i][1], try3 = iWin[i][2];
 
if( _field[try1] != None && _field[try1] == _field[try2] && _field[try3] == None )
{
move = try3;
if( _field[try1] == Computer ) break;
}
 
if( _field[try1] != None && _field[try1] == _field[try3] && _field[try2] == None )
{
move = try2;
if( _field[try1] == Computer ) break;
}
 
if( _field[try2] != None && _field[try2] == _field[try3] && _field[try1] == None )
{
move = try1;
if( _field[try2] == Computer ) break;
}
}
_field[move] = Computer;
 
}
 
 
int _p;
int _field[9];
};
//--------------------------------------------------------------------------------------------------
int main( int argc, char* argv[] )
{
srand( GetTickCount() );
 
ttt tic;
tic.play();
 
return 0;
}
//--------------------------------------------------------------------------------------------------
 

Output: Computer plays 'X' and human plays 'O'

 1 | 2 | X
---+---+---
 X | 5 | 6
---+---+---
 7 | O | 9

Enter your move ( 1 - 9 )

[edit] C#

This implementation is purposely wordy because Tic-Tac-Toe is often a starting level program.
It tries to show a number of C# code features while still keeping each function small and understandable.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace RosettaTicTacToe
{
class Program
{
 
/*================================================================
*Pieces (players and board)
*================================================================*/

static string[][] Players = new string[][] {
new string[] { "COMPUTER", "X" }, // computer player
new string[] { "HUMAN", "O" } // human player
};
 
const int Unplayed = -1;
const int Computer = 0;
const int Human = 1;
 
// GameBoard holds index into Players[] (0 or 1) or Unplayed (-1) if location not yet taken
static int[] GameBoard = new int[9];
 
static int[] corners = new int[] { 0, 2, 6, 8 };
 
static int[][] wins = new int[][] {
new int[] { 0, 1, 2 }, new int[] { 3, 4, 5 }, new int[] { 6, 7, 8 },
new int[] { 0, 3, 6 }, new int[] { 1, 4, 7 }, new int[] { 2, 5, 8 },
new int[] { 0, 4, 8 }, new int[] { 2, 4, 6 } };
 
 
/*================================================================
*Main Game Loop (this is what runs/controls the game)
*================================================================*/

static void Main(string[] args)
{
while (true)
{
Console.Clear();
Console.WriteLine("Welcome to Rosetta Code Tic-Tac-Toe for C#.");
initializeGameBoard();
displayGameBoard();
int currentPlayer = rnd.Next(0, 2); // current player represented by Players[] index of 0 or 1
Console.WriteLine("The first move goes to {0} who is playing {1}s.\n", playerName(currentPlayer), playerToken(currentPlayer));
while (true)
{
int thisMove = getMoveFor(currentPlayer);
if (thisMove == Unplayed)
{
Console.WriteLine("{0}, you've quit the game ... am I that good?", playerName(currentPlayer));
break;
}
playMove(thisMove, currentPlayer);
displayGameBoard();
if (isGameWon())
{
Console.WriteLine("{0} has won the game!", playerName(currentPlayer));
break;
}
else if (isGameTied())
{
Console.WriteLine("Cat game ... we have a tie.");
break;
}
currentPlayer = getNextPlayer(currentPlayer);
}
if (!playAgain())
return;
}
}
 
/*================================================================
*Move Logic
*================================================================*/

static int getMoveFor(int player)
{
if (player == Human)
return getManualMove(player);
else
{
//int selectedMove = getManualMove(player);
//int selectedMove = getRandomMove(player);
int selectedMove = getSemiRandomMove(player);
//int selectedMove = getBestMove(player);
Console.WriteLine("{0} selects position {1}.", playerName(player), selectedMove + 1);
return selectedMove;
}
}
 
static int getManualMove(int player)
{
while (true)
{
Console.Write("{0}, enter you move (number): ", playerName(player));
ConsoleKeyInfo keyInfo = Console.ReadKey();
Console.WriteLine(); // keep the display pretty
if (keyInfo.Key == ConsoleKey.Escape)
return Unplayed;
if (keyInfo.Key >= ConsoleKey.D1 && keyInfo.Key <= ConsoleKey.D9)
{
int move = keyInfo.KeyChar - '1'; // convert to between 0..8, a GameBoard index position.
if (GameBoard[move] == Unplayed)
return move;
else
Console.WriteLine("Spot {0} is already taken, please select again.", move + 1);
}
else
Console.WriteLine("Illegal move, please select again.\n");
}
}
 
static int getRandomMove(int player)
{
int movesLeft = GameBoard.Count(position => position == Unplayed);
int x = rnd.Next(0, movesLeft);
for (int i = 0; i < GameBoard.Length; i++) // walk board ...
{
if (GameBoard[i] == Unplayed && x < 0) // until we reach the unplayed move.
return i;
x--;
}
return Unplayed;
}
 
// plays random if no winning move or needed block.
static int getSemiRandomMove(int player)
{
int posToPlay;
if (checkForWinningMove(player, out posToPlay))
return posToPlay;
if (checkForBlockingMove(player, out posToPlay))
return posToPlay;
return getRandomMove(player);
}
 
// purposely not implemented (this is the thinking part).
static int getBestMove(int player)
{
return -1;
}
 
static bool checkForWinningMove(int player, out int posToPlay)
{
posToPlay = Unplayed;
foreach (var line in wins)
if (twoOfThreeMatchPlayer(player, line, out posToPlay))
return true;
return false;
}
 
static bool checkForBlockingMove(int player, out int posToPlay)
{
posToPlay = Unplayed;
foreach (var line in wins)
if (twoOfThreeMatchPlayer(getNextPlayer(player), line, out posToPlay))
return true;
return false;
}
 
static bool twoOfThreeMatchPlayer(int player, int[] line, out int posToPlay)
{
int cnt = 0;
posToPlay = int.MinValue;
foreach (int pos in line)
{
if (GameBoard[pos] == player)
cnt++;
else if (GameBoard[pos] == Unplayed)
posToPlay = pos;
}
return cnt == 2 && posToPlay >= 0;
}
 
static void playMove(int boardPosition, int player)
{
GameBoard[boardPosition] = player;
}
 
static bool isGameWon()
{
return wins.Any(line => takenBySamePlayer(line[0], line[1], line[2]));
}
 
static bool takenBySamePlayer(int a, int b, int c)
{
return GameBoard[a] != Unplayed && GameBoard[a] == GameBoard[b] && GameBoard[a] == GameBoard[c];
}
 
static bool isGameTied()
{
return !GameBoard.Any(spot => spot == Unplayed);
}
 
/*================================================================
*Misc Methods
*================================================================*/

static Random rnd = new Random();
 
static void initializeGameBoard()
{
for (int i = 0; i < GameBoard.Length; i++)
GameBoard[i] = Unplayed;
}
 
static string playerName(int player)
{
return Players[player][0];
}
 
static string playerToken(int player)
{
return Players[player][1];
}
 
static int getNextPlayer(int player)
{
return (player + 1) % 2;
}
 
static void displayGameBoard()
{
Console.WriteLine(" {0} | {1} | {2}", pieceAt(0), pieceAt(1), pieceAt(2));
Console.WriteLine("---|---|---");
Console.WriteLine(" {0} | {1} | {2}", pieceAt(3), pieceAt(4), pieceAt(5));
Console.WriteLine("---|---|---");
Console.WriteLine(" {0} | {1} | {2}", pieceAt(6), pieceAt(7), pieceAt(8));
Console.WriteLine();
}
 
static string pieceAt(int boardPosition)
{
if (GameBoard[boardPosition] == Unplayed)
return (boardPosition + 1).ToString(); // display 1..9 on board rather than 0..8
return playerToken(GameBoard[boardPosition]);
}
 
private static bool playAgain()
{
Console.WriteLine("\nDo you want to play again?");
return Console.ReadKey(false).Key == ConsoleKey.Y;
}
}
 
}

Output example:

Welcome to Rosetta Code Tic-Tac-Toe for C#.
 1 | 2 | 3
---|---|---
 4 | 5 | 6
---|---|---
 7 | 8 | 9

The first move goes to HUMAN who is playing Os.

HUMAN, enter you move (number): 5
 1 | 2 | 3
---|---|---
 4 | O | 6
---|---|---
 7 | 8 | 9

COMPUTER selects position 7.
 1 | 2 | 3
---|---|---
 4 | O | 6
---|---|---
 X | 8 | 9

HUMAN, enter you move (number): 0
Illegal move, please select again.

HUMAN, enter you move (number): 1
 O | 2 | 3
---|---|---
 4 | O | 6
---|---|---
 X | 8 | 9

COMPUTER selects position 9.
 O | 2 | 3
---|---|---
 4 | O | 6
---|---|---
 X | 8 | X

HUMAN, enter you move (number):

[edit] D

import std.stdio, std.string, std.algorithm, std.conv, std.random,
std.ascii, std.array, std.range, std.math;
 
struct GameBoard {
dchar[9] board = "123456789";
enum : dchar { human = 'X', computer = 'O' }
enum Game { going, humanWins, computerWins, draw }
 
const pure nothrow invariant() {
int nHuman = 0, nComputer = 0;
foreach (immutable i, immutable c; board)
if (c.isDigit)
assert(i == c - '1'); // In correct position?
else {
assert(c == human || c == computer);
(c == human ? nHuman : nComputer)++;
}
assert(abs(nHuman - nComputer) <= 1);
}
 
string toString() const pure {
return format("%(%-(%s|%)\n-+-+-\n%)", board[].chunks(3));
}
 
bool isAvailable(in int i) const pure nothrow {
return i >= 0 && i < 9 && board[i].isDigit;
}
 
int[] availablePositions() const pure nothrow {
return 9.iota.filter!(i => isAvailable(i)).array;
}
 
Game winner() const pure nothrow {
static immutable 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 (immutable win; wins) {
immutable bw0 = board[win[0]];
if (bw0.isDigit)
continue; // Nobody wins on this one.
 
if (bw0 == board[win[1]] && bw0 == board[win[2]])
return bw0 == GameBoard.human ?
Game.humanWins :
Game.computerWins;
}
 
return availablePositions.empty ? Game.draw: Game.going;
}
 
bool isFinished() const pure nothrow {
return winner != Game.going;
}
 
int computerMove() const // Random move.
out(res) {
assert(res >= 0 && res < 9 && isAvailable(res));
} body {
// return availablePositions.choice;
return availablePositions[uniform(0, $)];
}
}
 
 
GameBoard playGame() {
GameBoard board;
bool playsHuman = true;
 
while (!board.isFinished) {
board.writeln;
 
int move;
if (playsHuman) {
do {
writef("Your move (available moves: %s)? ",
board.availablePositions.map!q{ a + 1 });
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("\n%s chose %d", playsHuman ? "You" : "I", move + 1);
board.board[move] = playsHuman ? GameBoard.human :
GameBoard.computer;
playsHuman = !playsHuman; // Switch player.
}
 
return board;
}
 
 
void main() {
"Tic-tac-toe game player.\n".writeln;
immutable outcome = playGame.winner;
 
final switch (outcome) {
case GameBoard.Game.going:
"Game stopped.".writeln;
break;
case GameBoard.Game.humanWins:
"\nYou win!".writeln;
break;
case GameBoard.Game.computerWins:
"\nI win.".writeln;
break;
case GameBoard.Game.draw:
"\nDraw".writeln;
break;
}
}
Output:
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 2
X|O|3
-+-+-
4|5|6
-+-+-
7|8|9
Your move (available moves: [3, 4, 5, 6, 7, 8, 9])? 5

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

I chose 3
X|O|O
-+-+-
4|X|6
-+-+-
7|8|9
Your move (available moves: [4, 6, 7, 8, 9])? 9

You chose 9

You win!

[edit] Erlang

The program will randomly chose if the computer ("X") or the user ("O") starts. The computer look ahead is only one level. Perhaps the computer might lose?

 
-module(tic_tac_toe).
 
-export( [task/0] ).
 
task() -> io:fwrite( "Result: ~p.~n", [turn(player(random:uniform()), board())] ).
 
 
 
board() -> [{X, erlang:integer_to_list(X)} || X <- lists:seq(1, 9)].
 
board_tuples( Selections, Board ) -> [lists:keyfind(X, 1, Board) || X <- Selections].
 
computer_move( Player, Board ) ->
[N | _T] = lists:flatten( [X(Player, Board) || X <- [fun computer_move_win/2, fun computer_move_block/2, fun computer_move_middle/2, fun computer_move_random/2]] ),
N.
 
computer_move_block( Player, Board ) -> computer_move_two_same_player( player(false, Player), Board ).
 
computer_move_middle( _Player, Board ) ->
{5, Y} = lists:keyfind( 5, 1, Board ),
computer_move_middle( is_empty(Y) ).
 
computer_move_middle( true ) -> [5];
computer_move_middle( false ) -> [].
 
computer_move_random( _Player, Board ) ->
Ns = [X || {X, Y} <- Board, is_empty(Y)],
[lists:nth( random:uniform(erlang:length(Ns)), Ns )].
 
computer_move_two_same_player( Player, Board ) ->
Selections = [X || X <- three_in_row_all(), is_two_same_player(Player, X, Board)],
computer_move_two_same_player( Player, Board, Selections ).
 
computer_move_two_same_player( _Player, _Board, [] ) -> [];
computer_move_two_same_player( _Player, Board, [Selection | _T] ) -> [X || {X, Y} <- board_tuples(Selection, Board), is_empty(Y)].
 
computer_move_win( Player, Board ) -> computer_move_two_same_player( Player, Board ).
 
is_empty( Square ) -> Square =< "9". % Do not use < "10".
 
is_finished( Board ) -> is_full( Board ) orelse is_three_in_row( Board ).
 
is_full( Board ) -> [] =:= [X || {X, Y} <- Board, is_empty(Y)].
 
is_three_in_row( Board ) ->
Fun = fun(Selections) -> is_three_in_row_same_player( board_tuples(Selections, Board) ) end,
lists:any( Fun, three_in_row_all() ).
 
is_three_in_row_same_player( Selected ) -> three_in_row_player( Selected ) =/= no_player.
 
is_two_same_player( Player, Selections, Board ) -> is_two_same_player( Player, [{X, Y} || {X, Y} <- board_tuples(Selections, Board), not is_empty(Y)] ).
 
is_two_same_player( Player, [{_X, Player}, {_Y, Player}] ) -> true;
is_two_same_player( _Player, _Selected ) -> false.
 
player( Random ) when Random > 0.5 -> "O";
player( _Random ) -> "X".
 
player( true, _Player ) -> finished;
player( false, "X" ) -> "O";
player( false, "O" ) -> "X".
 
result( Board ) -> result( is_full(Board), Board ).
 
result( true, _Board ) -> draw;
result( false, Board ) ->
[Winners] = [Selections || Selections <- three_in_row_all(), three_in_row_player(board_tuples(Selections, Board)) =/= no_player],
"Winner is " ++ three_in_row_player( board_tuples(Winners, Board) ).
 
three_in_row_all() -> three_in_row_horisontal() ++ three_in_row_vertical() ++ three_in_row_diagonal().
three_in_row_diagonal() -> [[1,5,9], [3,5,7]].
three_in_row_horisontal() -> [[1,2,3], [4,5,6], [7,8,9]].
three_in_row_vertical() -> [[1,4,7], [2,5,8], [3,6,9]].
 
three_in_row_player( [{_X, Player}, {_Y, Player}, {_Z, Player}] ) -> three_in_row_player( not is_empty(Player), Player );
three_in_row_player( _Selected ) -> no_player.
 
three_in_row_player( true, Player ) -> Player;
three_in_row_player( false, _Player ) -> no_player.
 
turn( finished, Board ) -> result( Board );
turn( "X"=Player, Board ) ->
N = computer_move( Player, Board ),
io:fwrite( "Computer, ~p, selected ~p~n", [Player, N] ),
New_board = [{N, Player} | lists:keydelete(N, 1, Board)],
turn( player(is_finished(New_board), Player), New_board );
turn( "O"=Player, Board ) ->
[turn_board_write_horisontal(X, Board) || X <- three_in_row_horisontal()],
Ns = [X || {X, Y} <- Board, is_empty(Y)],
Prompt = lists:flatten( io_lib:format("Player, ~p, select one of ~p: ", [Player, Ns]) ),
N = turn_next_move( Prompt, Ns ),
New_board = [{N, Player} | lists:keydelete(N, 1, Board)],
turn( player(is_finished(New_board), Player), New_board ).
 
turn_board_write_horisontal( Selections, Board ) ->
Tuples = [lists:keyfind(X, 1, Board) || X <- Selections],
[io:fwrite( "~p ", [Y]) || {_X, Y} <- Tuples],
io:fwrite( "~n" ).
 
turn_next_move( Prompt, Ns ) ->
{ok,[N]} = io:fread( Prompt, "~d" ),
turn_next_move_ok( lists:member(N, Ns), Prompt, Ns, N ).
 
turn_next_move_ok( true, _Prompt, _Ns, N ) -> N;
turn_next_move_ok( false, Prompt, Ns, _N ) -> turn_next_move( Prompt, Ns ).
 
Output:
96> tic_tac_toe:task().
"1" "2" "3" 
"4" "5" "6" 
"7" "8" "9" 
Player, "O", select one of [1,2,3,4,5,6,7,8,9]: 5
Computer, "X", selected 2
"1" "X" "3" 
"4" "O" "6" 
"7" "8" "9" 
Player, "O", select one of [1,3,4,6,7,8,9]: 1
Computer, "X", selected 9
"O" "X" "3" 
"4" "O" "6" 
"7" "8" "X" 
Player, "O", select one of [3,4,6,7,8]: 3
Computer, "X", selected 7
"O" "X" "O" 
"4" "O" "6" 
"X" "8" "X" 
Player, "O", select one of [4,6,8]: 8
Computer, "X", selected 6
"O" "X" "O" 
"4" "O" "X" 
"X" "O" "X" 
Player, "O", select one of [4]: 4
Result: draw.

[edit] F#

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

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

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.

[edit] 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.

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},
}

[edit] Groovy

Simplified version of Tic Tack Toe for player vs player (No AI computer controlled option)

 
 
class Main {
 
def input = new Scanner(System.in)
 
static main(args) {
Main main = new Main();
main.play();
}
 
public void play() {
 
TicTackToe game = new TicTackToe();
game.init()
def gameOver = false
def player = game.player1
 
println "Players take turns marking a square. Only squares \n"+
"not already marked can be picked. Once a player has \n"+
"marked three squares in a row, column or diagonal, they win! If all squares \n"+
"are marked and no three squares are the same, a tied game is declared.\n"+
"Player 1 is O and Player 2 is X \n"+
"Have Fun! \n\n"
println "${game.drawBoard()}"
 
while (!gameOver && game.plays < 9) {
 
player = game.currentPlayer == 1 ? game.player1 :game.player2
def validPick = false;
while (!validPick) {
 
def square
println "Next $player, enter move by selecting square's number :"
try {
square = input.nextLine();
} catch (Exception ex) { }
 
if (square.length() == 1 && Character.isDigit(square.toCharArray()[0])) { validPick = game.placeMarker(square) }
if (!validPick) { println "Select another Square" }
 
}
 
(game.checkWinner(player))? gameOver = true : game.switchPlayers()
println(game.drawBoard());
 
}
println "Game Over, " + ((gameOver == true)? "$player Wins" : "Draw")
}
 
}
 
public class TicTackToe {
 
def board = new Object[3][3]
def final player1 = "player 1"
def final player2 = "player 2"
def final marker1 = 'X'
def final marker2 = 'O'
 
int currentPlayer
int plays
 
public TicTacToe(){
 
}
 
 
def init() {
int counter = 0;
(0..2).each { row ->
(0..2).each { col ->
board[row][col] = (++counter).toString();
}
}
plays = 0
currentPlayer =1
}
 
def switchPlayers() {
currentPlayer = (currentPlayer == 1) ? 2:1
plays++
}
 
def placeMarker(play) {
def result = false
(0..2).each { row ->
(0..2).each { col ->
if (board[row][col].toString()==play.toString()){
board[row][col] = (currentPlayer == 1) ? marker2 : marker1;
result = true;
}
}
}
return result;
}
 
def checkWinner(player) {
char current = (player == player1)? marker2: marker1
//Checking
return checkRows(current) || checkColumns(current) ||checkDiagonals(current);
}
 
def checkRows(char current){
(0..2).any{ line ->
board[line].every { it == current}
}
}
 
 
def checkColumns(char current){
(0..2).any{i ->
(0..2).every{j ->
board[j][i]==current }
}
}
 
def checkDiagonals(char current){
def rightDiag = [board[0][0],board[1][1],board[2][2]]
def leftDiag = [board[0][2],board[1][1],board[2][0]]
return rightDiag.every{it == current} || leftDiag.every{it == current}
}
 
 
def drawBoard() {
StringBuilder builder = new StringBuilder("Game board: \n");
(0..2).each { row->
(0..2).each {col ->
builder.append("[" + board[row][col] + "]");
}
builder.append("\n");
}
builder.append("\n");
return builder.toString();
}
}
 

[edit] Haskell

Computer player has three strategies: 1. Try to block the opponent first, 2. Try to guess a good position for the next move, 3. Place a piece randomly. There are lots of comments throughout the code.

 
module Main where
 
import System.Random
import Data.List (intercalate, find, minimumBy)
import System.Environment (getArgs)
import Data.Char (digitToInt)
import Data.Maybe (listToMaybe, mapMaybe)
import Control.Monad (guard)
import Data.Ord (comparing)
 
-- check if there is a horizontal, vertical or diagonal line of
-- X or O
tictactoe :: String -> Bool
tictactoe a = tictactoeFor 'X' a /= tictactoeFor 'O' a
 
-- check if there is a horizontal, vertical or diagonal line
-- for the given player "n"
tictactoeFor :: Char -> String -> Bool
tictactoeFor n [a,b,c,d,e,f,g,h,i] =
[n,n,n] `elem` [[a,b,c],[d,e,f],[g,h,i],[a,d,g],
[b,e,h],[c,f,i],[a,e,i],[c,e,g]]
 
-- empty game board
start :: String
start = " "
 
-- check if there is an X or an O at the given position
isPossible :: Int -> String -> Bool
isPossible n game = (game !! n) `notElem` "XO"
 
-- try to place an X or an O at a given position.
-- "Right" + modified board means success, "Left" + unmodified board
-- means failure
place :: Int -> Char -> String -> Either String String
place i c game =
if isPossible i game
then Right $ take i game ++ [c] ++ drop (i + 1) game
else Left game
 
-- COMPUTER AI
-- get the number of movements, starting from a given non-empty board
-- and a position for the next movement, until the specified player
-- wins or no movement is possible
-- the positions are chosen sequentially, so there's not much
-- intelligence here anyway
developGame :: Bool -> Int -> Int -> Char -> String -> (Int, Char, String)
developGame iterateMore moves i player game
| i > 8 =
-- if i arrives to the last position, iterate again from 0
-- but do it only once
if iterateMore
then developGame False moves 0 player game
-- draw game (after one iteration, still no winning moves)
else (moves, player, game)
-- draw game (game board full) or a win for the player
| moves == 9 || tictactoeFor player game = (moves, player, game)
-- make a move, if possible, and continue playing
| otherwise = case place i otherPlayer game of
-- position i is not empty. try with the next position
Left _ -> developGame iterateMore moves (i + 1)
otherPlayer game
-- position i was empty, so it was a valid move.
-- change the player and make a new move, starting at pos 0
Right newGame -> developGame iterateMore (moves + 1) 0
otherPlayer newGame
where
otherPlayer = changePlayer player
 
-- COMPUTER AI
-- starting from a given non-empty board, try to guess which position
-- could lead the player to the fastest victory.
bestMoveFor :: Char -> String -> Int
bestMoveFor player game = bestMove
where
-- drive the game to its end for each starting position
continuations = [ (x, developGame True 0 x player game) |
x <- [0..8] ]
-- compare the number of moves of the game and take the
-- shortest one
move (_, (m, _, _)) = m
(bestMove, _) = minimumBy (comparing move) continuations
 
-- canBlock checks if the opponent has two pieces in a row and the
-- other cell in the row is empty, and places the player's piece there,
-- blocking the opponent
canBlock :: Char -> String -> Maybe Int
canBlock p [a,b,c,d,e,f,g,h,i] =
listToMaybe $ mapMaybe blockable [[a,b,c],[d,e,f],[g,h,i],[a,d,g],
[b,e,h],[c,f,i],[a,e,i],[c,e,g]]
where
blockable xs = do
guard $ length (filter (== otherPlayer) xs) == 2
x <- find (`elem` "123456789") xs
return $ digitToInt x
otherPlayer = changePlayer p
 
-- format a game board for on-screen printing
showGame :: String -> String
showGame [a,b,c,d,e,f,g,h,i] =
topBottom ++
"| | 1 | 2 | 3 |\n" ++
topBottom ++
row "0" [[a],[b],[c]] ++
row "3" [[d],[e],[f]] ++
row "6" [[g],[h],[i]]
where
topBottom = "+----+---+---+---+\n"
row n x = "| " ++ n ++ "+ | " ++
intercalate " | " x ++ " |\n" ++ topBottom
 
-- ask the user to press a numeric key and convert it to an int
enterNumber :: IO Int
enterNumber = do
c <- getChar
if c `elem` "123456789"
then do
putStrLn ""
return $ digitToInt c
else do
putStrLn "\nPlease enter a digit!"
enterNumber
 
-- a human player's turn: get the number of pieces put on the board,
-- the next piece to be put (X or O) and a game board, and return
-- a new game state, checking if the piece can be placed on the board.
-- if it can't, make the user try again.
turn :: (Int, Char, String) -> IO (Int, Char, String)
turn (count, player, game) = do
putStr $ "Please tell me where you want to put an " ++
[player] ++ ": "
pos <- enterNumber
case place (pos - 1) player game of
Left oldGame -> do
putStrLn "That place is already taken!\n"
turn (count, player, oldGame)
Right newGame ->
return (count + 1, changePlayer player, newGame)
 
-- alternate between X and O players
changePlayer :: Char -> Char
changePlayer 'O' = 'X'
changePlayer 'X' = 'O'
 
-- COMPUTER AI
-- make an automatic turn, placing an X or an O game board.
-- the first movement is always random.
-- first, the computer looks for two pieces of his opponent in a row
-- and tries to block.
-- otherwise, it tries to guess the best position for the next movement.
-- as a least ressource, it places a piece randomly.
autoTurn :: Bool -> (Int, Char, String) -> IO (Int, Char, String)
autoTurn forceRandom (count, player, game) = do
-- try a random position 'cause everything else failed
-- count == 0 overrides the value of forceRandom
i <- if count == 0 || forceRandom
then randomRIO (0,8)
else return $
case canBlock player game of
-- opponent can't be blocked. try to guess
-- the best movement
Nothing -> bestMoveFor player game
-- opponent can be blocked, so just do it!
Just blockPos -> blockPos
-- if trying to place a piece at a calculated position doesn't work,
-- just try again with a random value
case place i player game of
Left oldGame -> autoTurn True (count, player, oldGame)
Right newGame -> do
putStrLn $ "It's player " ++ [player] ++ "'s turn."
return (count + 1, changePlayer player, newGame)
 
-- play a game until someone wins or the board becomes full.
-- depending on the value of the variable "auto", ask the user(s) to
-- put some pieces on the board or do it automatically
play :: Int -> (Int, Char, String) -> IO ()
play auto cpg@(_, player, game) = do
newcpg@(newCount, newPlayer, newGame) <- case auto of
-- if both players are human, always ask them
0 -> turn cpg
-- if both players are computer, always play auto
1 -> autoTurn False cpg
-- X is computer, O is human
2 -> if player == 'X' then autoTurn False cpg else turn cpg
-- X is human, O is computer
3 -> if player == 'O' then autoTurn False cpg else turn cpg
putStrLn $ "\n" ++ showGame newGame
if tictactoe newGame
then putStrLn $ "Player " ++ [changePlayer newPlayer] ++ " wins!\n"
else
if newCount == 9
then putStrLn "Draw!\n"
else play auto newcpg
 
-- main program: greet the user, ask for a game type, ask for the
-- player that'll start the game, and play the game beginning with an
-- empty board
main :: IO ()
main = do
a <- getArgs
if null a
then usage
else do
let option = head a
if option `elem` ["0","1","2","3"]
then do
putStrLn $ "\n" ++ showGame start
let m = read option :: Int
play m (0, 'X', start)
else usage
 
usage :: IO ()
usage = do
putStrLn "TIC-TAC-TOE GAME\n================\n"
putStrLn "How do you want to play?"
putStrLn "Run the program with one of the following options."
putStrLn "0 : both players are human"
putStrLn "1 : both players are computer"
putStrLn "2 : player X is computer and player O is human"
putStrLn "3 : player X is human and player O is computer"
putStrLn "Player X always begins."
 

Output: Player X is computer, O is human.

+----+---+---+---+
|    | 1 | 2 | 3 |
+----+---+---+---+
| 0+ |   |   |   |
+----+---+---+---+
| 3+ |   |   |   |
+----+---+---+---+
| 6+ |   |   |   |
+----+---+---+---+

It's player X's turn.

+----+---+---+---+
|    | 1 | 2 | 3 |
+----+---+---+---+
| 0+ |   |   |   |
+----+---+---+---+
| 3+ |   | X |   |
+----+---+---+---+
| 6+ |   |   |   |
+----+---+---+---+

Please tell me where you want to put an O: 1

+----+---+---+---+
|    | 1 | 2 | 3 |
+----+---+---+---+
| 0+ | O |   |   |
+----+---+---+---+
| 3+ |   | X |   |
+----+---+---+---+
| 6+ |   |   |   |
+----+---+---+---+

It's player X's turn.

+----+---+---+---+
|    | 1 | 2 | 3 |
+----+---+---+---+
| 0+ | O |   | X |
+----+---+---+---+
| 3+ |   | X |   |
+----+---+---+---+
| 6+ |   |   |   |
+----+---+---+---+

Please tell me where you want to put an O: 7

+----+---+---+---+
|    | 1 | 2 | 3 |
+----+---+---+---+
| 0+ | O |   | X |
+----+---+---+---+
| 3+ |   | X |   |
+----+---+---+---+
| 6+ | O |   |   |
+----+---+---+---+

It's player X's turn.

+----+---+---+---+
|    | 1 | 2 | 3 |
+----+---+---+---+
| 0+ | O |   | X |
+----+---+---+---+
| 3+ | X | X |   |
+----+---+---+---+
| 6+ | O |   |   |
+----+---+---+---+

Please tell me where you want to put an O: 6

+----+---+---+---+
|    | 1 | 2 | 3 |
+----+---+---+---+
| 0+ | O |   | X |
+----+---+---+---+
| 3+ | X | X | O |
+----+---+---+---+
| 6+ | O |   |   |
+----+---+---+---+

It's player X's turn.

+----+---+---+---+
|    | 1 | 2 | 3 |
+----+---+---+---+
| 0+ | O |   | X |
+----+---+---+---+
| 3+ | X | X | O |
+----+---+---+---+
| 6+ | O | X |   |
+----+---+---+---+

Please tell me where you want to put an O: 2

+----+---+---+---+
|    | 1 | 2 | 3 |
+----+---+---+---+
| 0+ | O | O | X |
+----+---+---+---+
| 3+ | X | X | O |
+----+---+---+---+
| 6+ | O | X |   |
+----+---+---+---+

It's player X's turn.

+----+---+---+---+
|    | 1 | 2 | 3 |
+----+---+---+---+
| 0+ | O | O | X |
+----+---+---+---+
| 3+ | X | X | O |
+----+---+---+---+
| 6+ | O | X | X |
+----+---+---+---+

Draw!

[edit] 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.

 
# 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
 

[edit] J

To subsequent j poster: replacing this entry is fine by me.

 
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&(=/\)
 

[edit] Java

This version works in the terminal itself, and uses the numpad for data entry. The computer is unbeatable, but some lines can be removed to avoid that. There's also an override that thrown in, just for fun.

 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Hashtable;
 
public class TicTacToe
{
public static void main(String[] args)
{
TicTacToe now=new TicTacToe();
now.startMatch();
}
 
private int[][] marks;
private int[][] wins;
private int[] weights;
private char[][] grid;
private final int knotcount=3;
private final int crosscount=4;
private final int totalcount=5;
private final int playerid=0;
private final int compid=1;
private final int truceid=2;
private final int playingid=3;
private String movesPlayer;
private byte override;
private char[][] overridegrid={{'o','o','o'},{'o','o','o'},{'o','o','o'}};
private char[][] numpad={{'7','8','9'},{'4','5','6'},{'1','2','3'}};
private Hashtable<Integer,Integer> crossbank;
private Hashtable<Integer,Integer> knotbank;
 
public void startMatch()
{
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
System.out.print("Start?(y/n):");
char choice='y';
try
{
choice=br.readLine().charAt(0);
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
if(choice=='n'||choice=='N')
{
return;
}
 
System.out.println("Use a standard numpad as an entry grid, as so:\n ");
display(numpad);
System.out.println("Begin");
int playerscore=0;
int compscore=0;
do
{
int result=startGame();
if(result==playerid)
playerscore++;
else if(result==compid)
compscore++;
System.out.println("Score: Player-"+playerscore+" AI-"+compscore);
System.out.print("Another?(y/n):");
try
{
choice=br.readLine().charAt(0);
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
 
}while(choice!='n'||choice=='N');
 
System.out.println("Game over.");
}
private void put(int cell,int player)
{
int i=-1,j=-1;;
switch(cell)
{
case 1:i=2;j=0;break;
case 2:i=2;j=1;break;
case 3:i=2;j=2;break;
case 4:i=1;j=0;break;
case 5:i=1;j=1;break;
case 6:i=1;j=2;break;
case 7:i=0;j=0;break;
case 8:i=0;j=1;break;
case 9:i=0;j=2;break;
default:display(overridegrid);return;
}
char mark='x';
if(player==0)
mark='o';
grid[i][j]=mark;
display(grid);
}
private int startGame()
{
init();
display(grid);
int status=playingid;
while(status==playingid)
{
put(playerMove(),0);
if(override==1)
{
System.out.println("O wins.");
return playerid;
}
status=checkForWin();
if(status!=playingid)
break;
try{Thread.sleep(1000);}catch(Exception e){System.out.print(e.getMessage());}
put(compMove(),1);
status=checkForWin();
}
return status;
}
private void init()
{
movesPlayer="";
override=0;
marks=new int[8][6];
wins=new int[][] //new int[8][3];
{
{7,8,9},
{4,5,6},
{1,2,3},
{7,4,1},
{8,5,2},
{9,6,3},
{7,5,3},
{9,5,1}
};
weights=new int[]{3,2,3,2,4,2,3,2,3};
grid=new char[][]{{' ',' ',' '},{' ',' ',' '},{' ',' ',' '}};
crossbank=new Hashtable<Integer,Integer>();
knotbank=new Hashtable<Integer,Integer>();
}
private void mark(int m,int player)
{
for(int i=0;i<wins.length;i++)
for(int j=0;j<wins[i].length;j++)
if(wins[i][j]==m)
{
marks[i][j]=1;
if(player==playerid)
marks[i][knotcount]++;
else
marks[i][crosscount]++;
marks[i][totalcount]++;
}
}
private void fixWeights()
{
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(marks[i][j]==1)
if(weights[wins[i][j]-1]!=Integer.MIN_VALUE)
weights[wins[i][j]-1]=Integer.MIN_VALUE;
 
for(int i=0;i<8;i++)
{
if(marks[i][totalcount]!=2)
continue;
if(marks[i][crosscount]==2)
{
int p=i,q=-1;
if(marks[i][0]==0)
q=0;
else if(marks[i][1]==0)
q=1;
else if(marks[i][2]==0)
q=2;
 
if(weights[wins[p][q]-1]!=Integer.MIN_VALUE)
{
weights[wins[p][q]-1]=6;
}
}
if(marks[i][knotcount]==2)
{
int p=i,q=-1;
if(marks[i][0]==0)
q=0;
else if(marks[i][1]==0)
q=1;
else if(marks[i][2]==0)
q=2;
 
if(weights[wins[p][q]-1]!=Integer.MIN_VALUE)
{
weights[wins[p][q]-1]=5;
}
}
}
}
private int compMove()
{
int cell=move();
System.out.println("Computer plays: "+cell);
//weights[cell-1]=Integer.MIN_VALUE;
return cell;
}
private int move()
{
int max=Integer.MIN_VALUE;
int cell=0;
for(int i=0;i<weights.length;i++)
if(weights[i]>max)
{
max=weights[i];
cell=i+1;
}
 
//This section ensures the computer never loses
//Remove it for a fair match
//Dirty kluge
if(movesPlayer.equals("76")||movesPlayer.equals("67"))
cell=9;
else if(movesPlayer.equals("92")||movesPlayer.equals("29"))
cell=3;
else if (movesPlayer.equals("18")||movesPlayer.equals("81"))
cell=7;
else if(movesPlayer.equals("73")||movesPlayer.equals("37"))
cell=4*((int)(Math.random()*2)+1);
else if(movesPlayer.equals("19")||movesPlayer.equals("91"))
cell=4+2*(int)(Math.pow(-1, (int)(Math.random()*2)));
 
mark(cell,1);
fixWeights();
crossbank.put(cell, 0);
return cell;
}
private int playerMove()
{
System.out.print("What's your move?: ");
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
int cell=0;
int okay=0;
while(okay==0)
{
try
{
cell=Integer.parseInt(br.readLine());
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
if(cell==7494)
{
override=1;
return -1;
}
if((cell<1||cell>9)||weights[cell-1]==Integer.MIN_VALUE)
System.out.print("Invalid move. Try again:");
else
okay=1;
}
playerMoved(cell);
System.out.println();
return cell;
}
private void playerMoved(int cell)
{
movesPlayer+=cell;
mark(cell,0);
fixWeights();
knotbank.put(cell, 0);
}
private int checkForWin()
{
int crossflag=0,knotflag=0;
for(int i=0;i<wins.length;i++)
{
if(crossbank.containsKey(wins[i][0]))
if(crossbank.containsKey(wins[i][1]))
if(crossbank.containsKey(wins[i][2]))
{
crossflag=1;
break;
}
if(knotbank.containsKey(wins[i][0]))
if(knotbank.containsKey(wins[i][1]))
if(knotbank.containsKey(wins[i][2]))
{
knotflag=1;
break;
}
}
if(knotflag==1)
{
display(grid);
System.out.println("O wins.");
return playerid;
}
else if(crossflag==1)
{
display(grid);
System.out.println("X wins.");
return compid;
}
 
for(int i=0;i<weights.length;i++)
if(weights[i]!=Integer.MIN_VALUE)
return playingid;
System.out.println("Truce");
 
return truceid;
}
private void display(char[][] grid)
{
for(int i=0;i<3;i++)
{
System.out.println("\n-------");
System.out.print("|");
for(int j=0;j<3;j++)
System.out.print(grid[i][j]+"|");
}
System.out.println("\n-------");
}
}
 
Start?(y/n):y
Use a standard numpad as an entry grid, as so:
-------
|7|8|9|
-------
|4|5|6|
-------
|1|2|3|
-------
Begin

-------
| | | |
-------
| | | |
-------
| | | |
-------
What's your move?: 4

-------
| | | |
-------
|o| | |
-------
| | | |
-------

(...)

Computer plays: 7
-------
|x| |o|
-------
|o|x|o|
-------
|x|o|x|
-------
X wins.

Score: Player-0 AI-1
Another?(y/n):n
Game over.


This version uses javax.swing.

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.logging.Logger;
/**
 
* TicTacToe Application
* @author Steve Robinson
* @version 1.0
*/

class TicTacToeFrame extends JFrame
{
JButton [][] buttons= new JButton[3][3];
JTextField statusBar;
GamePanel panel;
Integer turn;
GameListener listener=new GameListener();
Integer count;
public TicTacToeFrame()
{
setLayout(new BorderLayout());
panel=new GamePanel();
add(panel,BorderLayout.CENTER);
statusBar=new JTextField("Player1's Turn");
statusBar.setEditable(false);
add(statusBar,BorderLayout.SOUTH);
setTitle("Tic Tac Toe!");
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(400,400,300,300);
}
class GamePanel extends JPanel
{
public GamePanel()
{
setLayout(new GridLayout(3,3));
turn =1;
count=0;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++) {
buttons[i][j]=new JButton();
buttons[i][j].putClientProperty("INDEX", new Integer[]{i,j});
buttons[i][j].putClientProperty("OWNER", null);
buttons[i][j].addActionListener(listener);
add(buttons[i][j]);
}
}
}
class GameListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
count++;
JButton b=(JButton)e.getSource();
Integer[]index=(Integer[]) b.getClientProperty("INDEX");
//System.out.println(turn); //turn // //System.out.println("["+index[0]+"]"+"["+index[1]+"]"); //
b.putClientProperty("OWNER", turn);
Icon ico=new ImageIcon(turn.toString()+".gif");
b.setIcon(ico);
b.setEnabled(false);
boolean result=checkVictoryCondition(index);
if(result)
{
JOptionPane.showMessageDialog(null, "Player "+turn.toString()+" Wins");
initComponents();
}
else
{
if(turn==1)
{
turn=2;
statusBar.setText("Player2's Turn");
}
else
{
turn=1;
statusBar.setText("Player1's Turn");
}
}
if(count==9)
{
JOptionPane.showMessageDialog(null, "Match is a draw!");
initComponents();
}
}
Integer getOwner(JButton b)
{
return (Integer)b.getClientProperty("OWNER");
}
//PrintButtonMap for Diagnostics
void printbuttonMap(Integer [][]bMap)
{
for(int i=0;i for(int j=0;j System.out.print(bMap[i][j]+" ");
System.out.println("");
}
}
boolean checkVictoryCondition(Integer [] index)
{
/*Integer[][]buttonMap=new Integer[][] {
 
{ getOwner(buttons[0][0]),getOwner(buttons[0][1]),getOwner(buttons[0][2])},
 
{ getOwner(buttons[1][0]),getOwner(buttons[1][1]),getOwner(buttons[1][2])},
 
{ getOwner(buttons[2][0]),getOwner(buttons[2][1]),getOwner(buttons[2][2])}
};
printbuttonMap(buttonMap); */

Integer a=index[0];
Integer b=index[1];
int i;
//check row
for(i=0;i<3;i++) {
if(getOwner(buttons[a][i])!=getOwner(buttons[a][b]))
break;
}
if(i==3)
return true;
//check column
for(i=0;i<3;i++) {
if(getOwner(buttons[i][b])!=getOwner(buttons[a][b]))
break;
}
if(i==3)
return true;
//check diagonal
if((a==2&&b==2)||(a==0&&b==0)||(a==1&&b==1)||(a==0&&b==2)||(a==2&&b==0))
{
//left diagonal
for(i=0;i if(getOwner(buttons[i][i])!=getOwner(buttons[a][b]))
break;
if(i==3)
return true;
//right diagonal
if((getOwner(buttons[0][2])==getOwner(buttons[a][b]))&&(getOwner(buttons[1][1])==getOwner(buttons[a][b]))&&(getOwner(buttons[2][0])==getOwner(buttons[a][b])))
return true;
}
return false;
}
}
void initComponents()
{
for(int i=0;i<3;i++)
for(int j=0;j<3;j++) {
buttons[i][j].putClientProperty("INDEX", new Integer[]{i,j});
buttons[i][j].putClientProperty("OWNER",null);
buttons[i][j].setIcon(null);
buttons[i][j].setEnabled(true);
turn=1;
count=0;
statusBar.setText("Player1's Turn");
}
}
}
class TicTacToe {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable(){
public void run()
{
TicTacToeFrame frame=new TicTacToeFrame();
}
});
}
}
 

Graphical Java Example

 
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
 
 
//Make sure the name of the class is the same as the .java file name.
//If you change the class name you should change the class object name in runGUI method
public class ticTacToeCallum implements ActionListener {
 
static JFrame frame;
static JPanel contentPane;
static JLabel lblEnterFirstPlayerName, lblEnterSecondPlayerName, lblFirstPlayerScore, lblSecondPlayerScore;
static JButton btnButton1, btnButton2, btnButton3, btnButton4, btnButton5, btnButton6, btnButton7, btnButton8, btnButton9, btnClearBoard, btnClearAll, btnCloseGame;
static JTextField txtEnterFirstPlayerName, txtEnterSecondPlayerName;
static Icon imgicon = new ImageIcon("saveIcon.JPG");
 
Font buttonFont = new Font("Arial", Font.PLAIN, 20);
 
 
//to adjust the frame size change the values in pixels
static int width = 600;
static int length = 400;
static int firstPlayerScore = 0;
static int secondPlayerScore = 0;
static int playerTurn = 1;
static int roundComplete = 0;
static int button1 = 1, button2 = 1, button3 = 1, button4 = 1, button5 = 1, button6 = 1, button7 = 1, button8 = 1, button9 = 1; // 1 is true, 0 is false
 
 
public ticTacToeCallum(){
 
frame = new JFrame("Tic Tac Toe ^_^");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
contentPane = new JPanel();
contentPane.setLayout(new GridLayout(6, 3, 10, 10));
contentPane.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
 
btnButton1 = new JButton("");
btnButton1.setFont(buttonFont);
btnButton1.setAlignmentX(JButton.CENTER_ALIGNMENT);
btnButton1.setIcon(imgicon);
btnButton1.setActionCommand("CLICK1");
btnButton1.addActionListener(this);
contentPane.add(btnButton1);
 
btnButton2 = new JButton("");
btnButton2.setFont(buttonFont);
btnButton2.setAlignmentX(JButton.CENTER_ALIGNMENT);
btnButton2.setIcon(imgicon);
btnButton2.setActionCommand("CLICK2");
btnButton2.addActionListener(this);
contentPane.add(btnButton2);
 
btnButton3 = new JButton("");
btnButton3.setFont(buttonFont);
btnButton3.setAlignmentX(JButton.CENTER_ALIGNMENT);
btnButton3.setIcon(imgicon);
btnButton3.setActionCommand("CLICK3");
btnButton3.addActionListener(this);
contentPane.add(btnButton3);
 
btnButton4 = new JButton("");
btnButton4.setFont(buttonFont);
btnButton4.setAlignmentX(JButton.CENTER_ALIGNMENT);
btnButton4.setIcon(imgicon);
btnButton4.setActionCommand("CLICK4");
btnButton4.addActionListener(this);
contentPane.add(btnButton4);
 
btnButton5 = new JButton("");
btnButton5.setFont(buttonFont);
btnButton5.setAlignmentX(JButton.CENTER_ALIGNMENT);
btnButton5.setIcon(imgicon);
btnButton5.setActionCommand("CLICK5");
btnButton5.addActionListener(this);
contentPane.add(btnButton5);
 
btnButton6 = new JButton("");
btnButton6.setFont(buttonFont);
btnButton6.setAlignmentX(JButton.CENTER_ALIGNMENT);
btnButton6.setIcon(imgicon);
btnButton6.setActionCommand("CLICK6");
btnButton6.addActionListener(this);
contentPane.add(btnButton6);
 
btnButton7 = new JButton("");
btnButton7.setFont(buttonFont);
btnButton7.setAlignmentX(JButton.CENTER_ALIGNMENT);
btnButton7.setIcon(imgicon);
btnButton7.setActionCommand("CLICK7");
btnButton7.addActionListener(this);
contentPane.add(btnButton7);
 
btnButton8 = new JButton("");
btnButton8.setFont(buttonFont);
btnButton8.setAlignmentX(JButton.CENTER_ALIGNMENT);
btnButton8.setIcon(imgicon);
btnButton8.setActionCommand("CLICK8");
btnButton8.addActionListener(this);
contentPane.add(btnButton8);
 
btnButton9 = new JButton("");
btnButton9.setFont(buttonFont);
btnButton9.setAlignmentX(JButton.CENTER_ALIGNMENT);
btnButton9.setIcon(imgicon);
btnButton9.setActionCommand("CLICK9");
btnButton9.addActionListener(this);
contentPane.add(btnButton9);
 
lblEnterFirstPlayerName = new JLabel("Enter First Player's Name");
contentPane.add(lblEnterFirstPlayerName);
 
txtEnterFirstPlayerName = new JTextField("");
contentPane.add(txtEnterFirstPlayerName);
 
lblFirstPlayerScore = new JLabel("Score: " + firstPlayerScore);
contentPane.add(lblFirstPlayerScore);
 
lblEnterSecondPlayerName = new JLabel("Enter Second Player's Name");
contentPane.add(lblEnterSecondPlayerName);
 
txtEnterSecondPlayerName = new JTextField("");
contentPane.add(txtEnterSecondPlayerName);
 
lblSecondPlayerScore = new JLabel("Score: " + secondPlayerScore);
contentPane.add(lblSecondPlayerScore);
 
btnClearBoard = new JButton("Clear Board");
btnClearBoard.setAlignmentX(JButton.CENTER_ALIGNMENT);
btnClearBoard.setIcon(imgicon);
btnClearBoard.setActionCommand("CLICKClearBoard");
btnClearBoard.addActionListener(this);
contentPane.add(btnClearBoard);
 
btnClearAll = new JButton("Clear All");
btnClearAll.setAlignmentX(JButton.CENTER_ALIGNMENT);
btnClearAll.setIcon(imgicon);
btnClearAll.setActionCommand("CLICKClearAll");
btnClearAll.addActionListener(this);
contentPane.add(btnClearAll);
 
btnCloseGame = new JButton("Close Game");
btnCloseGame.setAlignmentX(JButton.CENTER_ALIGNMENT);
btnCloseGame.setIcon(imgicon);
btnCloseGame.setActionCommand("CLICKCloseGame");
btnCloseGame.addActionListener(this);
contentPane.add(btnCloseGame);
 
frame.setContentPane(contentPane);
frame.pack();
frame.setSize(width,length);
frame.setVisible(true);
 
}
 
public void actionPerformed(ActionEvent event) {
String eventName = event.getActionCommand();
if (eventName.equals("CLICK1")) {
if (button1 == 1){
if (playerTurn == 1){
btnButton1.setForeground(Color.RED);
btnButton1.setText("X");
playerTurn = 2;
button1 = 0;
} else if (playerTurn == 2) {
btnButton1.setForeground(Color.GREEN);
btnButton1.setText("O");
playerTurn = 1;
button1 = 0;
}
}
} else if (eventName.equals ("CLICK2")) {
if (button2 == 1){
if (playerTurn == 1){
btnButton2.setForeground(Color.RED);
btnButton2.setText("X");
playerTurn = 2;
button2 = 0;
} else if (playerTurn == 2) {
btnButton2.setForeground(Color.GREEN);
btnButton2.setText("O");
playerTurn = 1;
button2 = 0;
}
}
} else if (eventName.equals ("CLICK3")) {
if (button3 == 1){
if (playerTurn == 1){
btnButton3.setForeground(Color.RED);
btnButton3.setText("X");
playerTurn = 2;
button3 = 0;
} else if (playerTurn == 2) {
btnButton3.setForeground(Color.GREEN);
btnButton3.setText("O");
playerTurn = 1;
button3 = 0;
}
}
} else if (eventName.equals ("CLICK4")) {
if (button4 == 1){
if (playerTurn == 1){
btnButton4.setForeground(Color.RED);
btnButton4.setText("X");
playerTurn = 2;
button4 = 0;
} else if (playerTurn == 2) {
btnButton4.setForeground(Color.GREEN);
btnButton4.setText("O");
playerTurn = 1;
button4 = 0;
}
}
} else if (eventName.equals ("CLICK5")) {
if (button5 == 1){
if (playerTurn == 1){
btnButton5.setForeground(Color.RED);
btnButton5.setText("X");
playerTurn = 2;
button5 = 0;
} else if (playerTurn == 2) {
btnButton5.setForeground(Color.GREEN);
btnButton5.setText("O");
playerTurn = 1;
button5 = 0;
}
}
} else if (eventName.equals ("CLICK6")) {
if (button6 == 1){
if (playerTurn == 1){
btnButton6.setForeground(Color.RED);
btnButton6.setText("X");
playerTurn = 2;
button6 = 0;
} else if (playerTurn == 2) {
btnButton6.setForeground(Color.GREEN);
btnButton6.setText("O");
playerTurn = 1;
button6 = 0;
}
}
} else if (eventName.equals ("CLICK7")) {
if (button7 == 1){
if (playerTurn == 1){
btnButton7.setForeground(Color.RED);
btnButton7.setText("X");
playerTurn = 2;
button7 = 0;
} else if (playerTurn == 2) {
btnButton7.setForeground(Color.GREEN);
btnButton7.setText("O");
playerTurn = 1;
button7 = 0;
}
}
} else if (eventName.equals ("CLICK8")) {
if (button8 == 1){
if (playerTurn == 1){
btnButton8.setForeground(Color.RED);
btnButton8.setText("X");
playerTurn = 2;
button8 = 0;
} else if (playerTurn == 2) {
btnButton8.setForeground(Color.GREEN);
btnButton8.setText("O");
playerTurn = 1;
button8 = 0;
}
}
} else if (eventName.equals ("CLICK9")) {
if (button9 == 1){
if (playerTurn == 1){
btnButton9.setForeground(Color.RED);
btnButton9.setText("X");
playerTurn = 2;
button9 = 0;
} else if (playerTurn == 2) {
btnButton9.setForeground(Color.GREEN);
btnButton9.setText("O");
playerTurn = 1;
button9 = 0;
}
}
} else if (eventName.equals ("CLICKClearBoard")) {
 
btnButton1.setText("");
btnButton2.setText("");
btnButton3.setText("");
btnButton4.setText("");
btnButton5.setText("");
btnButton6.setText("");
btnButton7.setText("");
btnButton8.setText("");
btnButton9.setText("");
 
button1 = 1;
button2 = 1;
button3 = 1;
button4 = 1;
button5 = 1;
button6 = 1;
button7 = 1;
button8 = 1;
button9 = 1;
 
playerTurn = 1;
 
roundComplete = 0;
 
} else if (eventName.equals ("CLICKClearAll")) {
 
btnButton1.setText("");
btnButton2.setText("");
btnButton3.setText("");
btnButton4.setText("");
btnButton5.setText("");
btnButton6.setText("");
btnButton7.setText("");
btnButton8.setText("");
btnButton9.setText("");
 
firstPlayerScore = 0;
lblFirstPlayerScore.setText("Score: " + firstPlayerScore);
secondPlayerScore = 0;
lblSecondPlayerScore.setText("Score: " + secondPlayerScore);
 
txtEnterFirstPlayerName.setText("");
txtEnterSecondPlayerName.setText("");
 
button1 = 1;
button2 = 1;
button3 = 1;
button4 = 1;
button5 = 1;
button6 = 1;
button7 = 1;
button8 = 1;
button9 = 1;
 
playerTurn = 1;
 
roundComplete = 0;
 
} else if (eventName.equals ("CLICKCloseGame")) {
System.exit(0);
}
score();
}
 
 
public static void score(){
if (roundComplete == 0){
if (btnButton1.getText().equals(btnButton2.getText()) && btnButton1.getText().equals(btnButton3.getText())){
if (btnButton1.getText().equals("X")){
firstPlayerScore += 1;
lblFirstPlayerScore.setText("Score: " + firstPlayerScore);
roundComplete = 1;
} else if (btnButton1.getText().equals("O")){
secondPlayerScore += 1;
lblSecondPlayerScore.setText("Score: " + secondPlayerScore);
roundComplete = 1;
}
}
if (btnButton1.getText().equals(btnButton4.getText()) && btnButton1.getText().equals(btnButton7.getText())){
if (btnButton1.getText().equals("X")){
firstPlayerScore += 1;
lblFirstPlayerScore.setText("Score: " + firstPlayerScore);
roundComplete = 1;
} else if (btnButton1.getText().equals("O")){
secondPlayerScore += 1;
lblSecondPlayerScore.setText("Score: " + secondPlayerScore);
roundComplete = 1;
}
}
if (btnButton1.getText().equals(btnButton5.getText()) && btnButton1.getText().equals(btnButton9.getText())){
if (btnButton1.getText().equals("X")){
firstPlayerScore += 1;
lblFirstPlayerScore.setText("Score: " + firstPlayerScore);
roundComplete = 1;
} else if (btnButton1.getText().equals("O")){
secondPlayerScore += 1;
lblSecondPlayerScore.setText("Score: " + secondPlayerScore);
roundComplete = 1;
}
}
if (btnButton7.getText().equals(btnButton8.getText()) && btnButton7.getText().equals(btnButton9.getText())){
if (btnButton7.getText().equals("X")){
firstPlayerScore += 1;
lblFirstPlayerScore.setText("Score: " + firstPlayerScore);
roundComplete = 1;
} else if (btnButton7.getText().equals("O")){
secondPlayerScore += 1;
lblSecondPlayerScore.setText("Score: " + secondPlayerScore);
roundComplete = 1;
}
}
if (btnButton7.getText().equals(btnButton5.getText()) && btnButton7.getText().equals(btnButton3.getText())){
if (btnButton7.getText().equals("X")){
firstPlayerScore += 1;
lblFirstPlayerScore.setText("Score: " + firstPlayerScore);
roundComplete = 1;
} else if (btnButton7.getText().equals("O")){
secondPlayerScore += 1;
lblSecondPlayerScore.setText("Score: " + secondPlayerScore);
roundComplete = 1;
}
}
if (btnButton3.getText().equals(btnButton6.getText()) && btnButton3.getText().equals(btnButton9.getText())){
if (btnButton3.getText().equals("X")){
firstPlayerScore += 1;
lblFirstPlayerScore.setText("Score: " + firstPlayerScore);
roundComplete = 1;
} else if (btnButton3.getText().equals("O")){
secondPlayerScore += 1;
lblSecondPlayerScore.setText("Score: " + secondPlayerScore);
roundComplete = 1;
}
}
if (btnButton4.getText().equals(btnButton5.getText()) && btnButton4.getText().equals(btnButton6.getText())){
if (btnButton4.getText().equals("X")){
firstPlayerScore += 1;
lblFirstPlayerScore.setText("Score: " + firstPlayerScore);
roundComplete = 1;
} else if (btnButton4.getText().equals("O")){
secondPlayerScore += 1;
lblSecondPlayerScore.setText("Score: " + secondPlayerScore);
roundComplete = 1;
}
}
if (btnButton2.getText().equals(btnButton5.getText()) && btnButton2.getText().equals(btnButton8.getText())){
if (btnButton2.getText().equals("X")){
firstPlayerScore += 1;
lblFirstPlayerScore.setText("Score: " + firstPlayerScore);
roundComplete = 1;
} else if (btnButton2.getText().equals("O")){
secondPlayerScore += 1;
lblSecondPlayerScore.setText("Score: " + secondPlayerScore);
roundComplete = 1;
}
}
}
if (roundComplete == 1){
button1 = 0;
button2 = 0;
button3 = 0;
button4 = 0;
button5 = 0;
button6 = 0;
button7 = 0;
button8 = 0;
button9 = 0;
}
}
 
/**
* Create and show the GUI.
*/

private static void runGUI() {
ticTacToeCallum greeting = new ticTacToeCallum();
}
 
 
 
//Do not change this method
public static void main(String[] args) {
/* Methods that create and show a GUI should be run from an event-dispatching thread */
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
runGUI();
}
});
}
}
 


[edit] Lasso

This example is incomplete. Computer doesn't play - it merely manages the board. Please ensure that it meets all task requirements and remove this message.

This example uses an HTML form for the UI, buttons representing the game state, and Lasso's built inn session handler to keep track of who's turn it is, what the game matrix state is, and the winner history.

As image uploads has been disabled, a live version can be viewed at: http://jono.guthrie.net.nz/rosetta/Tic-tac-toe.lasso

[
session_start('user')
session_addvar('user', 'matrix')
session_addvar('user', 'winrecord')
session_addvar('user', 'turn')
var(matrix)->isNotA(::array) ? var(matrix = array('-','-','-','-','-','-','-','-','-'))
var(winrecord)->isNotA(::array) ? var(winrecord = array)
var(turn)->isNotA(::string) ? var(turn = 'x')
 
if(web_request->params->asStaticArray >> 'reset') => {
$matrix = array('-','-','-','-','-','-','-','-','-')
$turn = 'x'
}
 
with i in web_request->params->asStaticArray do => {
if(#i->name->beginswith('p')) => {
local(num = #i->name->asCopy)
#num->removeLeading('p')
#num = integer(#num)
#num > 0 && $matrix->get(#num) == '-' ? $matrix->get(#num) = #i->value
$turn == 'o' ? $turn = 'x' | $turn = 'o'
}
}
 
local(
istie = false,
winner = 'noone',
clear = false
)
 
// determine if we have a winner
if($matrix->find('-')->size < 9) => {
local(winners = array('123','456','789','147','258','369','159','357'))
loop(8) => {
local(xscore = 0,oscore = 0,use = #winners->get(loop_count))
with v in #use->values do => {
$matrix->findposition('x') >> integer(#v) ? #xscore++
$matrix->findposition('o') >> integer(#v) ? #oscore++
}
if(#xscore == 3) => {
#winner = 'x'
$winrecord->insert('x')
#clear = true
loop_abort
}
if(#oscore == 3) => {
#winner = 'o'
$winrecord->insert('o')
#clear = true
loop_abort
}
 
}
 
}
// determine if tie
if(not $matrix->find('-')->size && #winner == 'noone') => {
#istie = true
#winner = 'tie'
$winrecord->insert('tie')
#clear = true
}
]
<form action="?" method="post">
<table>
<tr>
[loop(3) => {^]<td><button name="p[loop_count]" value="[$turn]"[
$matrix->get(loop_count) != '-' || #winner != 'noone' ? ' disabled="disabled"'
]>[$matrix->get(loop_count) != '-' ? $matrix->get(loop_count) | ' ']</button></td>[^}]
</tr>
<tr>
[loop(-from=4,-to=6) => {^]<td><button name="p[loop_count]" value="[$turn]"[
$matrix->get(loop_count) != '-' || #winner != 'noone' ? ' disabled="disabled"'
]>[$matrix->get(loop_count) != '-' ? $matrix->get(loop_count) | ' ']</button></td>[^}]
</tr>
<tr>
[loop(-from=7,-to=9) => {^]<td><button name="p[loop_count]" value="[$turn]"[
$matrix->get(loop_count) != '-' || #winner != 'noone' ? ' disabled="disabled"'
]>[$matrix->get(loop_count) != '-' ? $matrix->get(loop_count) | ' ']</button></td>[^}]
</tr>
</table>
</form>
[if(#istie && #winner == 'tie')]
<p><b>It's a tie!</b></p>
[else(#winner != 'noone')]
<p>[#winner->uppercase&] won! Congratulations.</p>
[else]<math>Insert formula here</math>
<p>It is now [$turn]'s turn!</p>
[/if]
<p><a href="?reset">Reset</a></p>
[if($winrecord->size)]<p>Win record: [$winrecord->join(', ')]</p>[/if]
[if(#clear == true) => {
$matrix = array('-','-','-','-','-','-','-','-','-')
$turn = 'x'
}]

[edit] Mathematica

DynamicModule[{board = ConstantArray[0, {3, 3}], text = "Playing...", 
first, rows =
Join[#, Transpose@#, {Diagonal@#, Diagonal@Reverse@#}] &},
Column@{Graphics[{Thickness[.02],
Table[With[{i = i, j = j},
Button[{White, Rectangle[{i, j} - 1, {i, j}], Black,
Dynamic[Switch[board[[i, j]], 0, Black, 1,
Circle[{i, j} - .5, .3], -1,
Line[{{{i, j} - .2, {i, j} - .8}, {{i - .2,
j - .8}, {i - .8, j - .2}}}]]]},
Which[text != "Playing...", board = ConstantArray[0, {3, 3}];
text = "Playing...", board[[i, j]] == 0,
If[board == ConstantArray[0, {3, 3}],
first = {i,
j} /. {{2, 2} -> 1, {1 | 3, 1 | 3} -> 2, _ -> 3}];
board[[i, j]] = 1;
FinishDynamic[];
Which[MemberQ[rows[board], {1, 1, 1}], text = "You win.",
FreeQ[board, 0], text = "Draw.", True,
board[[Sequence @@
SortBy[Select[Tuples[{Range@3, Range@3}],
board[[Sequence @@ #]] ==
0 &], -Total[
Sort /@
rows[ReplacePart[
board, # -> -1]] /. {{-1, -1, -1} ->
512, {-1, 1, 1} -> 64, {-1, -1, 0} ->
8, {0, 1, 1} -> -1, {_, _, _} -> 0}] -
Switch[#, {2, 2}, 1, {1 | 3, 1 | 3},
If[first == 2, -1, 0], _,
If[first == 2, 0, -1]] &][[1]]]] = -1;
Which[MemberQ[rows[board], {-1, -1, -1}],
text = "You lost.", FreeQ[board, 0],
text = "Draw."]]]]], {i, 1, 3}, {j, 1, 3}], Thickness[.01],
Line[{{{1, 0}, {1, 3}}, {{2, 0}, {2, 3}}, {{0, 1}, {3, 1}}, {{0,
2}, {3, 2}}}]}], Dynamic@text}]

[edit] mIRC Scripting Language

alias ttt {
if ($2 isin %ttt) || (!%ttt) {
var %ttt~ = $remove($iif(%ttt,%ttt,1 2 3 4 5 6 7 8 9),$2,X,O)
var %ttt~~ = $replace($iif(%ttt,%ttt,1 2 3 4 5 6 7 8 9),$2,X)
set %ttt $replace(%ttt~~,$iif(($regex(%ttt~~,/(?:O . . (?:(?:. O .|O) . . (\d)|(?:. (\d) .|(\d)) . . O)|(\d) . . (?:. O .|O) . . O|. . (?:O . (?:O . (\d)|(\d) . O)|(\d) . O . O) . .)/)) || ($regex(%ttt~~,/^(?:. . . )*(?:O (?:O (\d)|(\d) O)|(\d) O O)(?: . . .)*$/)),$regml(1),$iif(($regex(%ttt~~,/(?:X . . (?:(?:. X .|X) . . (\d)|(?:. (\d) .|(\d)) . . X)|(\d) . . (?:. X .|X) . . X|. . (?:X . (?:X . (\d)|(\d) . X)|(\d) . X . X) . .)/)) || ($regex(%ttt~~,/^(?:. . . )*(?:X (?:X (\d)|(\d) X)|(\d) X X)(?: . . .)*$/)),$regml(1),$iif($remove(%ttt~,2,4,6,8,$chr(32)),$iif((5 isin $remove(%ttt~,2,4,6,8)) && ($rand(0,$numtok($v2,32)) == 0),5,$gettok($remove(%ttt~,2,4,6,8),$rand(1,$numtok($remove(%ttt~,2,4,6,8),32)),32)),$gettok(%ttt~,$rand(1,$numtok(%ttt~,32)),32)))),O)
tokenize 32 %ttt
if ($regex(%ttt,/(?:X . . (?:X|. X .) . . X|. . X . X . X . .)/)) || ($regex(%ttt,/^(?:. . . )*X X X(?: . . .)*$/)) {
echo -ag $me Wins
tokenize 32 %ttt~~
unset %ttt
}
elseif ($regex(%ttt,/(?:O . . (?:O|. O .) . . O|. . O . O . O . .)/)) || ($regex(%ttt,/^(?:. . . )*O O O(?: . . .)*$/)) {
echo -ag $me Loses
unset %ttt
}
elseif (!$regex(%ttt,/\d/)) {
echo -ag Draw
unset %ttt
}
echo -ag � $+ $iif($1 isnum,$chr(32),$1) $+ $chr(124) $+ $iif($2 isnum,$chr(32),$2) $+ $chr(124) $+ $iif($3 isnum, ,$3)
echo -ag � $+ $iif($4 isnum,$chr(32),$4) $+ $chr(124) $+ $iif($5 isnum,$chr(32),$5) $+ $chr(124) $+ $iif($6 isnum, ,$6)
echo -ag � $+ $iif($7 isnum,$chr(32),$7) $+ $chr(124) $+ $iif($8 isnum,$chr(32),$8) $+ $chr(124) $+ $iif($9 isnum, ,$9)
}
else {
echo -ag Place Taken
tokenize 32 %ttt
echo -ag � $+ $iif($1 isnum,$chr(32),$1) $+ $chr(124) $+ $iif($2 isnum,$chr(32),$2) $+ $chr(124) $+ $iif($3 isnum, ,$3)
echo -ag � $+ $iif($4 isnum,$chr(32),$4) $+ $chr(124) $+ $iif($5 isnum,$chr(32),$5) $+ $chr(124) $+ $iif($6 isnum, ,$6)
echo -ag � $+ $iif($7 isnum,$chr(32),$7) $+ $chr(124) $+ $iif($8 isnum,$chr(32),$8) $+ $chr(124) $+ $iif($9 isnum, ,$9)
}
}

[edit] Perl

A basic negamax search (with caching) is done to find the best move. If there are several equally good moves, one of them is selected randomly.

The computer player is not perfect, and so a human player can sometimes win.

This is not perl's fault, but mine; it ought to always be a tie, or a win for the computer. Anyone who can identify the mistake, is welcome to fix it.

use warnings;
use strict;
 
my $initial = join ",", qw(abc def ghi);
my %reverse = qw(X O O X);
 
# In list context, returns best move,
# In scalar context, returns the score of best move.
my %cache;
sub best_move {
my ($b, $me) = @_;
if( exists $cache{$b,$me,wantarray} ) {
return $cache{$b,$me,wantarray};
} elsif( my $s = score( $b, $me ) ) {
return $cache{$b,$me,wantarray} = (wantarray ? undef : $s);
}
my $him = $reverse{$me};
my ($best, @best) = (-999);
for my $m (moves($b)) {
(my $with_m = $b) =~ s/$m/$me/ or die;
# The || operator supplies scalar context to best_move(...)
my $s = -(score($with_m, $him) || best_move($with_m, $him));
if( $s > $best ) {
($best, @best) = ($s, $m);
} elsif( $s == $best ) {
push @best, $m;
}
}
$cache{$b,$me,wantarray} = wantarray ? $best[rand @best] : $best;
}
 
my $winner = q[([XOxo])(?:\1\1|...\1...\1|..\1..\1|....\1....\1)];
sub score {
my ($b, $me) = @_;
$b =~ m/$winner/o or return 0;
return $1 eq $me ? +1 : -1;
}
 
sub moves {
my ($b) = @_;
$b =~ /([^xoXO,\n])/g;
}
 
sub print_board {
my ($b) = @_;
$b =~ s/\B/|/g;
$b =~ s/,/\n-+-+-\n/g;
print $b, "\n";
}
 
sub prompt {
my ($b, $color) = @_;
my @moves = moves($b);
unless( @moves ) {
return;
}
while( 1 ) {
print "Place your $color on one of [@moves]: ";
defined(my $m = <>) or return;
chomp($m);
return $m if grep $m eq $_, @moves;
}
}
 
my @players = (
{ whose => "your", name => "You",
verb => "You place", get_move => \&prompt },
{ whose => "the computer's", name => "Computer",
verb => "The computer places", get_move => \&best_move },
);
my $whose_turn = int rand 2;
 
my $color = "X";
my $b = $initial;
 
while( 1 ) {
my $p = $players[$whose_turn];
print_board($b);
print "It is $p->{whose} turn.\n";
# The parens around $m supply list context to the right side
# or the = operator, which causes sub best_move to return the
# best move, rather than the score of the best move.
my ( $m ) = $p->{get_move}->($b, $color);
if( $m ) {
print "$p->{verb} an $color at $m\n";
$b =~ s/$m/$color/;
my $s = score($b, $color) or next;
print_board($b);
print "$p->{name} ", $s > 0 ? "won!\n" : "lost!\n";
} else {
print "$p->{name} cannot move.\n";
}
print "Game over.\nNew Game...\n";
($b, $color, $whose_turn) = ($initial, "X", int rand 2);
redo;
} continue {
$color = $reverse{$color};
$whose_turn = !$whose_turn;
}
 
Output:
a|b|c
-+-+-
d|e|f
-+-+-
g|h|i
It is your turn.
Place your X on one of [a b c d e f g h i]: e
You place an X at e
a|b|c
-+-+-
d|X|f
-+-+-
g|h|i
It is the computer's turn.
The computer places an O at c
a|b|O
-+-+-
d|X|f
-+-+-
g|h|i
It is your turn.
Place your X on one of [a b d f g h i]: a
You place an X at a
X|b|O
-+-+-
d|X|f
-+-+-
g|h|i
It is the computer's turn.
The computer places an O at f
X|b|O
-+-+-
d|X|O
-+-+-
g|h|i
It is your turn.
Place your X on one of [b d g h i]: i
You place an X at i
X|b|O
-+-+-
d|X|O
-+-+-
g|h|X
You won!
Game over.
New Game...
a|b|c
-+-+-
d|e|f
-+-+-
g|h|i
It is your turn.
Place your X on one of [a b c d e f g h i]:

[edit] 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.

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

[edit] PicoLisp

This solution doesn't bother about the game logic, but simply uses the alpha-beta-pruning 'game' function in the "simul" library.

(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!") ) )

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

[edit] 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.

:- 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).
 
 

Module min-max.pl defines minimax algorithm.

:- 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).
 
 

[edit] Python

The computer enforces the rules but plays a random game.

 
'''
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')
 

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]

[edit] 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.

 
'''
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')

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


[edit] Racket

The program provides standard interface for implementation of any zero–sum game with perfect information such as tick-tack-toe, Nim, the 21 game etc. It is possible to create interactive players (as objects) with different playing strategy (AI-driven, user-driven, random etc.) and let them play with each other through message-sending technique.

The optimal strategy is implemented via lazy minimax algorythm with α-β-pruning and arbitrary depth of the recursion.

The program consists of separate modules:

+ minimax.rkt    -- Written in Lazy Racket, implements the general minimax algorythm as 
|                   given in Wikipedia.
|                   Knows nothing about games.
V
+ game.rkt       -- Written in Lazy Racket, defines general classes for the game and players.
|                   Knows nothing about tick-tack-toe or any other particular game.
V
+ tick-tack.rkt  -- Written in Racket, implements the tick-tack-toe game.


The minimax.rkt module:

 
#lang lazy
(provide minimax)
 
(define (minimax tree)
(! (let minimax ([node tree] [α -inf.0] [β +inf.0] [max-player #f])
(cond
[(number? node) node]
[(empty? node) 0.0]
[max-player
(let next ([x node] [α α])
(if (or (empty? x) (<= β α))
α
(next (cdr x)
(max α (minimax (car x) α β (not max-player))))))]
[else
(let next ([x node] [β β])
(if (or (empty? x) (<= β α))
β
(next (cdr x)
(min β (minimax (car x) α β (not max-player))))))]))))
 

The game.rkt module:

 
#lang lazy
(require racket/class
"minimax.rkt"
(only-in racket/list shuffle argmax))
 
(provide game%
interactive-player
define-partners)
 
;;--------------------------------------------------------------------
;; Class representing the logics and optimal strategy
;; for a zero-sum game with perfect information.
(define game%
(class object%
(super-new)
 
 ;; virtual methods which set up the game rules
(init-field my-win?  ; State -> Bool
my-loss?  ; State -> Bool
draw-game?  ; State -> Bool
my-move  ; State Move -> State
opponent-move  ; State Move -> State
possible-moves  ; State -> (list Move)
show-state)  ; State -> Any
 
 ;; optimal-move :: State -> Move
 ;; Choses the optimal move.
 ;; If several equipollent moves exist -- choses one randomly.
(define/public ((optimal-move look-ahead) S)
(! (argmax (λ (m) (! (minimax (game-tree S m look-ahead))))
(shuffle (possible-moves S)))))
 
 ;; game-tree :: State -> (Move -> (Treeof Real))
(define (game-tree S m look-ahead)
(let new-ply ([moves (cycle opponent-move my-move)]
[i 1]
[s (my-move S m)])
(cond
[(my-win? s) (/ 1 i)] ; more close wins and looses
[(my-loss? s) (/ -1 i)] ; have bigger weights
[(draw-game? s) 0]
[(>= i look-ahead) (/ 1 i)]
[else (map (λ (x) (new-ply (cdr moves) (+ 1 i) ((car moves) s x)))
(possible-moves s))])))
 
 ;; make-move :: State (State -> Move) -> (Move State Symbol)
(define/public (make-move S move)
(cond
[(my-loss? S) (values '() S 'loss)]
[(draw-game? S) (values '() S 'draw)]
[else (let* ([m* (! (move S))]
[S* (my-move S m*)])
(cond
[(my-win? S*) (values m* S* 'win)]
[(draw-game? S*) (values m* S* 'draw)]
[else (values m* S* 'next)]))]))))
 
;;--------------------------------------------------------------------
;; Mixin representing an interactive game player.
;; The parameter `game` defines a game which is played.
(define (interactive-player game)
(class game
(super-new)
 
(inherit-field show-state)
(inherit make-move optimal-move)
 
(init-field name
[look-ahead 4]
[opponent 'undefined]
[move-method (optimal-move look-ahead)])
 
(define/public (your-turn S)
(define-values (m S* status) (make-move S move-method))
(! (printf "\n~a makes move ~a\n" name m))
(! (show-state S*))
(! (case status
['stop (displayln "The game was interrupted.")]
['win (printf "~a wins!" name)]
['loss (printf "~a wins!" name)]
['draw (printf "Draw!")]
[else (send opponent your-turn S*)])))))
 
 
;;--------------------------------------------------------------------
;; a simple macro for initialization of game partners
(define-syntax-rule
(define-partners game (A #:win A-wins #:move A-move)
(B #:win B-wins #:move B-move))
(begin
(define A (class game
(super-new
[my-win? A-wins]
[my-loss? B-wins]
[my-move A-move]
[opponent-move B-move])))
(define B (class game
(super-new
[my-win? B-wins]
[my-loss? A-wins]
[my-move B-move]
[opponent-move A-move])))))
 
;;--------------------------------------------------------------------
;; the main procedure which initiates the game
(define (start-game p1 p2 initial-state)
(set-field! opponent p1 p2)
(set-field! opponent p2 p1)
(send p1 your-turn initial-state))
 

The tick-tack.rkt module:

#lang racket
 
(require "game.rkt"
racket/set
lazy/force)
 
;;--------------------------------------------------------------------
;; Tick-tack-toe game implementation
 
;; the structure representing a board
(struct board (x o))
 
;; sets of X's and O's
(define xs board-x)
(define os board-o)
 
(define empty-board (board (set) (set)))
 
(define all-cells
(set '(1 1) '(1 2) '(1 3)
'(2 1) '(2 2) '(2 3)
'(3 1) '(3 2) '(3 3)))
 
(define (free-cells b)
(set-subtract all-cells (xs b) (os b)))
 
(define winning-positions
(list (set '(1 1) '(2 2) '(3 3))
(set '(1 3) '(2 2) '(3 1))
(set '(1 1) '(1 2) '(1 3))
(set '(2 1) '(2 2) '(2 3))
(set '(3 1) '(3 2) '(3 3))
(set '(1 1) '(2 1) '(3 1))
(set '(1 2) '(2 2) '(3 2))
(set '(1 3) '(2 3) '(3 3))))
 
;; a predicate for winning state on the board
(define ((wins? s) b)
(ormap (curryr subset? (s b)) winning-positions))
 
;; player moves
(define (x-move b m) (board (set-add (xs b) m) (os b)))
(define (o-move b m) (board (xs b) (set-add (os b) m)))
 
;; textual representation of the board
(define (show-board b)
(for ([i '(3 2 1)])
(printf "~a " i)
(for ([j '(1 2 3)])
(display (cond
[(set-member? (os b) (list j i)) "|o"]
[(set-member? (xs b) (list j i)) "|x"]
[else "| "])))
(display "|\n"))
(display " 1 2 3 "))
 
;;--------------------------------------------------------------------
;; The definition of the game
;; general properties
(define tic-tac%
(class game%
(super-new
[draw-game? (compose set-empty? free-cells)]
[possible-moves (compose set->list free-cells)]
[show-state show-board])))
 
;; players
(define-partners tic-tac%
(x% #:win (wins? xs) #:move x-move)
(o% #:win (wins? os) #:move o-move))
 
;; Computer players
(define player-A (new (interactive-player x%) [name "A"] [look-ahead 6]))
 
(define player-B (new (interactive-player o%) [name "B"] [look-ahead 6]))
 
; The interactive user
(define User
(new (interactive-player x%)
[name "User"]
[move-method
(λ (b) (let make-move ([m (read)])
(match m
['q (exit)]
[(list (or 1 2 3) (or 1 2 3)) m]
[else (make-move (read))])))]))
 
;; The dummy player plays randomly
(define Dummy
(new (interactive-player o%) [name "Dummy"] [look-ahead 0]))
 
 

Sample games:

Computer plays with the computer:

> (!(start-game player-A player-B empty-board))

A makes move (3 1)
3 | | | |
2 | | | |
1 | | |x|
   1 2 3    
B makes move (2 2)
3 | | | |
2 | |o| |
1 | | |x|
   1 2 3    
A makes move (1 1)
3 | | | |
2 | |o| |
1 |x| |x|
   1 2 3    
B makes move (2 1)
3 | | | |
2 | |o| |
1 |x|o|x|
   1 2 3    
A makes move (2 3)
3 | |x| |
2 | |o| |
1 |x|o|x|
   1 2 3    
B makes move (3 2)
3 | |x| |
2 | |o|o|
1 |x|o|x|
   1 2 3    
A makes move (1 2)
3 | |x| |
2 |x|o|o|
1 |x|o|x|
   1 2 3    
B makes move (1 3)
3 |o|x| |
2 |x|o|o|
1 |x|o|x|
   1 2 3    
A makes move (3 3)
3 |o|x|x|
2 |x|o|o|
1 |x|o|x|
   1 2 3    Draw!

Computer plays with the dummy:

> (!(start-game player-A Dummy empty-board))

A makes move (3 1)
3 | | | |
2 | | | |
1 | | |x|
   1 2 3    
Dummy makes move (2 3)
3 | |o| |
2 | | | |
1 | | |x|
   1 2 3    
A makes move (1 1)
3 | |o| |
2 | | | |
1 |x| |x|
   1 2 3    
Dummy makes move (3 3)
3 | |o|o|
2 | | | |
1 |x| |x|
   1 2 3    
A makes move (2 1)
3 | |o|o|
2 | | | |
1 |x|x|x|
   1 2 3    A wins!

User plays with the dummy:

> (!(start-game Dummy User empty-board))

Dummy makes move (2 3)
3 | |o| |
2 | | | |
1 | | | |
   1 2 3    (1 2)

User makes move (1 2)
3 | |o| |
2 |x| | |
1 | | | |
   1 2 3    
Dummy makes move (3 2)
3 | |o| |
2 |x| |o|
1 | | | |
   1 2 3    (1 3)

User makes move (1 3)
3 |x|o| |
2 |x| |o|
1 | | | |
   1 2 3    
Dummy makes move (3 3)
3 |x|o|o|
2 |x| |o|
1 | | | |
   1 2 3    (1 1)

User makes move (1 1)
3 |x|o|o|
2 |x| |o|
1 |x| | |
   1 2 3    User wins!

As an example of another zero-sum game consider the classical Nim game:

 
#lang racket
 
(require "game.rkt"
lazy/force)
 
;;--------------------------------------------------------------------
;; The definition of the game
 
(define initial-state '(3 5 7))
 
(define (move s m) (map - s m))
 
(define (win? s) (= 1 (apply + s)))
 
(define (show-state s) (displayln (map (λ (n) (make-list n '●)) s)))
 
(define (possible-moves S)
(append-map
(λ (heap n)
(map (λ (x) (map (curry * x) heap))
(range 1 (+ 1 (min 3 n)))))
'((1 0 0) (0 1 0) (0 0 1)) S))
 
(define Nim% (class game%
(super-new
[draw-game? (const #f)]
[possible-moves possible-moves]
[show-state show-state])))
 
(define-partners Nim%
(first% #:win win? #:move move)
(second% #:win win? #:move move))
 
;; players
(define player-A
(new (interactive-player first%) [name "A"] [look-ahead 4]))
 
(define player-B
(new (interactive-player second%) [name "B"] [look-ahead 4]))
 

Computer plays with the computer:

> (!(start-game player-A player-B initial-state))

A makes move (0 0 2)
((● ● ●) (● ● ● ● ●) (● ● ● ● ●))

B makes move (1 0 0)
((● ●) (● ● ● ● ●) (● ● ● ● ●))

A makes move (2 0 0)
(() (● ● ● ● ●) (● ● ● ● ●))

B makes move (0 2 0)
(() (● ● ●) (● ● ● ● ●))

A makes move (0 3 0)
(() () (● ● ● ● ●))

B makes move (0 0 1)
(() () (● ● ● ●))

A makes move (0 0 3)
(() () (●))
A wins!

With use of memoization it is easy to train automatic players so that they would never loose and play very fast.

[edit] REXX

Options:

  • allow user to specify any   NxN   size tic-tac-toe grid
  • allow user to specify who plays first (default is the user)
  • allow user to specify what markers (symbols) to be used (can use hex pairs)
  • a (single) numbered grid is used instead of coördinates for easier marker placement
  • allows user to quit (exit) the game at any time
  • allows the human player to win (if human goes first and makes a certain move)
/*REXX program plays (with a human) the tic-tac-toe game on an NxN grid.*/
oops =$ '***error!*** '; cell# ='cell number' /*a couple of literals*/
$=copies('─',9) /*eyecatcher literal for messages*/
sing='│─┼'; jam='║'; bar='═'; junc='╬'; dbl=jam || bar || junc
sw=80-1 /*LINESIZE() bif would be better.*/
parse arg N hm cm .,@.; if N=='' then N=3; oN=N /*specifying some args?*/
N=abs(N); NN=N*N; middle=NN%2+N%2 /*if N < 0, computer goes first.*/
if N<2 then do; say oops 'tic-tac-toe grid is too small: ' N; exit; end
pad=copies(left('',sw%NN),1+(N<5)) /*display padding: 6x6 in 80 cols*/
if hm=='' then hm='X'; if cm=='' then cm='O' /*markers: Human, Computer*/
hm=aChar(hm,'human'); cm=aChar(cm,'computer') /*process the markers.*/
if hm==cm then cm='X' /*Human wants the "O"? Sheesh! */
if oN<0 then call Hmove middle /*comp moves 1st? Choose middling*/
else call showGrid /*···also checks for wins & draws*/
do forever /*'til the cows come home, by gum*/
call CBLF /*do carbon-based lifeform's move*/
call Hal /*figure Hal the computer's move.*/
end /*forever····showGrid does wins & draws*/
/*──────────────────────────────────ACHAR subroutine────────────────────*/
aChar: parse arg x; L=length(x) /*process markers.*/
if L==1 then return x /*1 char, as is. */
if L==2 & datatype(x,'X') then return x2c(x) /*2 chars, hex. */
if L==3 & datatype(x,'W') then return d2c(x) /*3 chars, decimal*/
say oops 'illegal character or character code for' arg(2) "marker: " x
exit /*stick a fork in it, we're done.*/
/*──────────────────────────────────CBLF subroutine─────────────────────*/
CBLF: prompt='Please enter a' cell# "to place your next marker ["hm'] (or Quit):'
do forever; say $ prompt; parse pull x 1 ux 1 ox; upper ux
if datatype(ox,'W') then ox=ox/1 /*maybe normalize cell#: +0007. */
select
when abbrev('QUIT',ux,1) then call tell 'quitting.'
when x='' then iterate /*Nada? Try again.*/
when words(x)\==1 then say oops "too many" cell# 'specified:' x
when \datatype(x,'N') then say oops cell# "isn't numeric: " x
when \datatype(x,'W') then say oops cell# "isn't an integer: " x
when x=0 then say oops cell# "can't be zero: " x
when x<0 then say oops cell# "can't be negative: " x
when x>NN then say oops cell# "can't exceed " NN
when @.ox\=='' then say oops cell# "is already occupied: " x
otherwise leave /*do forever*/
end /*select*/
end /*forever*/
@.ox=hm /*place a marker for the human.*/
call showGrid /*and show the tic-tac-toe grid.*/
return
/*──────────────────────────────────Hal subroutine──────────────────────*/
Hal: select /*Hal will try various moves. */
when win(cm,N-1) then call Hmove ,ec /* winning move? */
when win(hm,N-1) then call Hmove ,ec /* blocking move? */
when @.middle=='' then call Hmove middle /* center move. */
when @.N.N=='' then call Hmove ,N N /*bR corner move. */
when @.N.1=='' then call Hmove ,N 1 /*bL corner move. */
when @.1.N=='' then call Hmove ,1 N /*tR corner move. */
when @.1.1=='' then call Hmove ,1 1 /*tL corner move. */
otherwise call Hmove ,ac /*some blank cell.*/
end /*select*/
return
/*──────────────────────────────────HMOVE subroutine────────────────────*/
Hmove: parse arg Hplace,dr dc; if Hplace=='' then Hplace = (dr-1)*N + dc
@.Hplace=cm /*put marker for Hal the computer*/
say; say $ 'computer places a marker ['cm"] at cell number " Hplace
call showGrid
return
/*──────────────────────────────────SHOWGRID subroutine─────────────────*/
showGrid: _=0; open=0; cW=5; cH=3 /*cell width, cell height.*/
do r=1 for N; do c=1 for N; _=_+1; @.r.c=@._; open=open|@._==''; end; end
say; z=0 /* [↑] create grid coörds.*/
do j=1 for N /* [↓] show grids&markers.*/
do t=1 for cH; _=; __= /*mk is a marker in a cell*/
do k=1 for N; if t==2 then z=z+1; mk=; c#=
if t==2 then do; mk=@.z; c#=z; end /*c# is cell#*/
_= _||jam||center(mk,cW); __= __||jam||center(c#,cW)
end /*k*/
say pad substr(_,2) pad translate(substr(__,2), sing,dbl)
end /*t*/
if j==N then leave; _=
do b=1 for N; _=_||junc||copies(bar,cW); end /*b*/
say pad substr(_,2) pad translate(substr(_,2),sing,dbl)
end /*j*/
say
if win(hm) then call tell 'You ('hm") won!"
if win(cm) then call tell 'The computer ('cm") won."
if \open then call tell 'This tic-tac-toe is a draw.'
return
/*──────────────────────────────────TELL subroutine─────────────────────*/
tell: do 4; say; end; say center(' 'arg(1)" ",sw,'─'); do 5; say; end
exit /*stick a fork in it, we're done.*/
/*──────────────────────────────────WIN subroutine──────────────────────*/
win: parse arg wm,w; if w=='' then w=N /*see if there are W # of markers*/
ac=; do r=1 for N; _=0; ec= /*see if any rows are a winner.*/
do c=1 for N; _=_+ (@.r.c==wm); if @.r.c=='' then ec=r c; end
if ec\=='' then @c=ec; if _==N | (_>=w & ec\=='') then return 1
end /*r*/ /*if w=N-1, checking for near win*/
do c=1 for N; _=0; ec= /*see if any cols are a winner.*/
do r=1 for N; _=_+ (@.r.c==wm); if @.r.c=='' then ec=r c; end
if ec\=='' then @c=ec; if _==N | (_>=w & ec\=='') then return 1
end /*r*/ /*EC is a r,c version of cell #*/
_=0; ec= /*see if winning descending diag.*/
do d=1 for N; _=_+ (@.d.d==wm); if @.d.d=='' then ec=d d; end
if _==N | (_>=w & ec\=='') then return 1
_=0; ec=; r=0 /*see if winning ascending diag.*/
do c=N for N by -1; r=r+1; _=_+ (@.r.c==wm); if @.r.c=='' then ec=r c
end /*r*/
if _==N | (_>=w & ec\=='') then return 1
return 0

output when using the input of: -3
(a negative 3 indicates a grid of 3x3 and that the computer should play first.)

Note:   the user input is shown along with the program output.

───────── computer places a marker at cell number  5

               ║     ║                                      │     │
               ║     ║                                   1  │  2  │  3
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
               ║  O  ║                                   4  │  5  │  6
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
               ║     ║                                   7  │  8  │  9
               ║     ║                                      │     │

───────── Please enter a cell number to place your next marker   (or Quit):
2

               ║     ║                                      │     │
               ║  X  ║                                   1  │  2  │  3
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
               ║  O  ║                                   4  │  5  │  6
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
               ║     ║                                   7  │  8  │  9
               ║     ║                                      │     │


───────── computer places a marker at cell number  1

               ║     ║                                      │     │
            O  ║  X  ║                                   1  │  2  │  3
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
               ║  O  ║                                   4  │  5  │  6
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
               ║     ║                                   7  │  8  │  9
               ║     ║                                      │     │

───────── Please enter a cell number to place your next marker   (or Quit):
9

               ║     ║                                      │     │
            O  ║  X  ║                                   1  │  2  │  3
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
               ║  O  ║                                   4  │  5  │  6
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
               ║     ║  X                                7  │  8  │  9
               ║     ║                                      │     │

───────── computer places a marker at cell number  7

               ║     ║                                      │     │
            O  ║  X  ║                                   1  │  2  │  3
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
               ║  O  ║                                   4  │  5  │  6
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
            O  ║     ║  X                                7  │  8  │  9
               ║     ║                                      │     │

───────── Please enter a cell number to place your next marker   (or Quit):
3

               ║     ║                                      │     │
            O  ║  X  ║  X                                1  │  2  │  3
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
               ║  O  ║                                   4  │  5  │  6
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
            O  ║     ║  X                                7  │  8  │  9
               ║     ║                                      │     │

───────── computer places a marker at cell number  4

               ║     ║                                      │     │
            O  ║  X  ║  X                                1  │  2  │  3
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
            O  ║  O  ║                                   4  │  5  │  6
               ║     ║                                      │     │
          ═════╬═════╬═════                            ─────┼─────┼─────
               ║     ║                                      │     │
            O  ║     ║  X                                7  │  8  │  9
               ║     ║                                      │     │





─────────────────────────── The computer  (O)  won. ───────────────────────────

output when using the input of: 5 db
(which indicates a grid of 5x5 and that the marker for the human is a hex db [█].)

         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║            1  │  2  │  3  │  4  │  5
         ║     ║     ║     ║               │     │     │     │
    ═════╬═════╬═════╬═════╬═════     ─────┼─────┼─────┼─────┼─────
         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║            6  │  7  │  8  │  9  │ 10
         ║     ║     ║     ║               │     │     │     │
    ═════╬═════╬═════╬═════╬═════     ─────┼─────┼─────┼─────┼─────
         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║           11  │ 12  │ 13  │ 14  │ 15
         ║     ║     ║     ║               │     │     │     │
    ═════╬═════╬═════╬═════╬═════     ─────┼─────┼─────┼─────┼─────
         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║           16  │ 17  │ 18  │ 19  │ 20
         ║     ║     ║     ║               │     │     │     │
    ═════╬═════╬═════╬═════╬═════     ─────┼─────┼─────┼─────┼─────
         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║           21  │ 22  │ 23  │ 24  │ 25
         ║     ║     ║     ║               │     │     │     │

───────── Please enter a cell number to place your next marker [█]   (or Quit):
5

         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║  █         1  │  2  │  3  │  4  │  5
         ║     ║     ║     ║               │     │     │     │
    ═════╬═════╬═════╬═════╬═════     ─────┼─────┼─────┼─────┼─────
         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║            6  │  7  │  8  │  9  │ 10
         ║     ║     ║     ║               │     │     │     │
    ═════╬═════╬═════╬═════╬═════     ─────┼─────┼─────┼─────┼─────
         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║           11  │ 12  │ 13  │ 14  │ 15
         ║     ║     ║     ║               │     │     │     │
    ═════╬═════╬═════╬═════╬═════     ─────┼─────┼─────┼─────┼─────
         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║           16  │ 17  │ 18  │ 19  │ 20
         ║     ║     ║     ║               │     │     │     │
    ═════╬═════╬═════╬═════╬═════     ─────┼─────┼─────┼─────┼─────
         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║           21  │ 22  │ 23  │ 24  │ 25
         ║     ║     ║     ║               │     │     │     │


───────── computer places a marker [O] at cell number  14

         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║  █         1  │  2  │  3  │  4  │  5
         ║     ║     ║     ║               │     │     │     │
    ═════╬═════╬═════╬═════╬═════     ─────┼─────┼─────┼─────┼─────
         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║            6  │  7  │  8  │  9  │ 10
         ║     ║     ║     ║               │     │     │     │
    ═════╬═════╬═════╬═════╬═════     ─────┼─────┼─────┼─────┼─────
         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║  O  ║           11  │ 12  │ 13  │ 14  │ 15
         ║     ║     ║     ║               │     │     │     │
    ═════╬═════╬═════╬═════╬═════     ─────┼─────┼─────┼─────┼─────
         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║           16  │ 17  │ 18  │ 19  │ 20
         ║     ║     ║     ║               │     │     │     │
    ═════╬═════╬═════╬═════╬═════     ─────┼─────┼─────┼─────┼─────
         ║     ║     ║     ║               │     │     │     │
         ║     ║     ║     ║           21  │ 22  │ 23  │ 24  │ 25
         ║     ║     ║     ║               │     │     │     │

───────── Please enter a cell number to place your next marker [█]   (or Quit):
q




────────────────────────────────── quitting. ──────────────────────────────────

[edit] Ruby

module TicTacToe
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]]
 
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"
end
attr_reader :free_positions, :board, :turn
 
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
ROWS.each do |row|
if row.all? {|idx| @board[idx] == player.marker}
puts "#{player} wins!"
print_board
return
end
end
 
# no winner, is board full?
if @free_positions.empty?
puts "It's a draw."
print_board
return
end
 
nextTurn!
end
end
 
def nextTurn
1 - @turn
end
 
def nextTurn!
@turn = nextTurn
end
 
def opponent
@players[nextTurn]
end
 
def print_board
display =lambda{|row| row.map {|i| @board[i] ? @board[i] : i}.join("|")}
puts display[[1,2,3]], "-+-+-", display[[4,5,6]], "-+-+-", display[[7,8,9]]
end
end
 
class Player
def initialize(game)
@game = game
@marker = nil
end
attr_accessor :marker
end
 
class HumanPlayer < Player
def select
@game.print_board
loop do
print "Select your #{marker} position: "
selection = gets.to_i
return selection if @game.free_positions.include?(selection)
puts "Position #{selection} is not available. Try again."
end
end
 
def to_s
"Human"
end
end
 
class ComputerPlayer < Player
def group_row(row)
markers = row.group_by {|idx| @game.board[idx]}
markers.default = []
markers
end
 
def select
opponent_marker = @game.opponent.marker
 
# look for winning rows
for row in ROWS
markers = group_row(row)
next if markers[nil].length != 1
if markers[self.marker].length == 2
return markers[nil].first
elsif markers[opponent_marker].length == 2
idx = markers[nil].first
end
end
 
# look for opponent's winning rows to block
return idx if idx
 
# need some logic here to get the computer to pick a smarter position
 
# simply pick a position in order of preference
([5] + [1,3,7,9].shuffle + [2,4,6,8].shuffle).find do |pos|
@game.free_positions.include?(pos)
end
end
 
def to_s
"Computer#{@game.turn}"
end
end
end
 
include TicTacToe
 
Game.new(ComputerPlayer, ComputerPlayer).play
puts
Game.new(HumanPlayer,ComputerPlayer).play

sample output

Computer1 goes first.
Computer1 selects X position 5
Computer0 selects O position 3
Computer1 selects X position 7
Computer0 selects O position 1
Computer1 selects X position 2
Computer0 selects O position 8
Computer1 selects X position 9
Computer0 selects O position 6
Computer1 selects X position 4
It's a draw.
O|X|O
-+-+-
X|X|O
-+-+-
X|O|X

Human goes first.
1|2|3
-+-+-
4|5|6
-+-+-
7|8|9
Select your X position: 1
Human selects X position 1
Computer1 selects O position 5
X|2|3
-+-+-
4|O|6
-+-+-
7|8|9
Select your X position: 9
Human selects X position 9
Computer1 selects O position 3
X|2|O
-+-+-
4|O|6
-+-+-
7|8|X
Select your X position: 7
Human selects X position 7
Computer1 selects O position 4
X|2|O
-+-+-
O|O|6
-+-+-
X|8|X
Select your X position: 8
Human selects X position 8
Human wins!
X|2|O
-+-+-
O|O|6
-+-+-
X|X|X

[edit] Run BASIC

' ---------------------------
' TIC TAC TOE
' ---------------------------
winBox$ = "123 456 789 159 147 258 369 357"
boxPos$ = "123 231 456 564 789 897 159 591 357 753 132 465 798 174 285 396 159 471 582 693 147 258 369 195 375"
ai$ = "519628374"
ox$ = "OX"
[newGame]
for i = 1 to 9
box$(i) = ""
next i
goto [shoTic]
 
[loop]
for j = 1 to 2
tic$ = mid$(ox$,j,1)
for i = 1 to 25
b$ = word$(boxPos$,i," ")
b1 = val(mid$(b$,1,1))
b2 = val(mid$(b$,2,1))
b3 = val(mid$(b$,3,1))
if box$(b1) = tic$ AND box$(b2) = tic$ AND box$(b3) = "" then
box$(b3) = "O"
goto [shoTic]
end if
next i
next j
if box$(1) = "O" AND box$(5) = "X" and box$(9) = "X" then
if box$(3) = "" then
box$(3) = "O"
goto [shoTic]
end if
if box$(7) = "" then
box$(7) = "O"
goto [shoTic]
end if
end if
for i = 1 to 9
b1 = val(mid$(ai$,i,1))
if box$(b1) = "" then
box$(b1) = "O"
exit for
end if
next i
 
[shoTic]
cls
' ----------------------------------------
' show tic tac toe screen
' ----------------------------------------
html "<table border=1 width=300px height=225px><TR>"
for i = 1 to 9
html "<td align=center width=33%><h1>"
if box$(i) <> "" then
html box$(i)
else
button #box, " ";box$(i);" ", [doTic]
#box setkey(str$(i))
end if
if i mod 3 = 0 then html "</tr><tr>"
next i
html "</table>"
gosub [checkWin]
wait
 
[doTic]
box$(val(EventKey$)) = "X"
turn = 1
gosub [checkWin]
goto [loop]
 
' --- check for a winner ----------
[checkWin]
for i = 1 to 8
b$ = word$(winBox$,i," ")
b1 = val(mid$(b$,1,1))
b2 = val(mid$(b$,2,1))
b3 = val(mid$(b$,3,1))
if box$(b1) = "O" and box$(b2) = "O" and box$(b3) = "O" then
print "You Loose!"
goto [playAgain]
end if
if box$(b1) = "X" and box$(b2) = "X" and box$(b3) = "X" then
print "You Win!"
goto [playAgain]
end if
next i
 
moveCount = 0
for i = 1 to 9
if box$(i) <> "" then moveCount = moveCount + 1
next i
if moveCount = 9 then
print "Draw!"
goto [playAgain]
end if
RETURN
 
[playAgain]
input "Play again (y/n)";p$
if upper$(p$) = "Y" then goto [newGame]
end

[edit] Scala

Functional implementation.

Computer vs. human. Human starts. Computer plays 'O' and human plays 'X'. Computer moves are legal, but random.

package object tictactoe {
val Human = 'X'
val Computer = 'O'
val BaseBoard = ('1' to '9').toList
val WinnerLines = List((0,1,2), (3,4,5), (6,7,8), (0,3,6), (1,4,7), (2,5,8), (0,4,8), (2,4,6))
val randomGen = new util.Random(System.currentTimeMillis)
}
 
package tictactoe {
 
class Board(aBoard : List[Char] = BaseBoard) {
 
def availableMoves = aBoard.filter(c => c != Human && c != Computer)
 
def availableMovesIdxs = for ((c,i) <- aBoard.zipWithIndex if c != Human && c != Computer) yield i
 
def computerPlays = new Board(aBoard.updated(availableMovesIdxs(randomGen.nextInt(availableMovesIdxs.length)), Computer))
 
def humanPlays(move : Char) = new Board(aBoard.updated(aBoard.indexOf(move), Human))
 
def isDraw = aBoard.forall(c => c == Human || c == Computer)
 
def isWinner(winner : Char) =
WinnerLines.exists{case (i,j,k) => aBoard(i) == winner && aBoard(j) == winner && aBoard(k) == winner}
 
def isOver = isWinner(Computer) || isWinner(Human) || isDraw
 
def print {
aBoard.grouped(3).foreach(row => println(row(0) + " " + row(1) + " " + row(2)))
}
 
def printOverMessage {
if (isWinner(Human)) println("You win.")
else if (isWinner(Computer)) println("Computer wins.")
else if (isDraw) println("It's a draw.")
else println("Not over yet, or something went wrong.")
}
 
}
 
 
object TicTacToe extends Application {
 
def play(board : Board, turn : Char) {
 
// Reads a char from input until it is one of
// the available moves in the current board
def readValidMove() : Char = {
print("Choose a move: ")
val validMoves = board.availableMoves
val move = readChar
if (validMoves.contains(move)) {
move
} else {
println("Invalid move. Choose another one in " + validMoves)
readValidMove()
}
}
 
 
board.print
 
if (board.isOver) {
board.printOverMessage
return
}
 
if (turn == Human) { // Human plays
val nextBoard = board.humanPlays(readValidMove)
play(nextBoard, Computer)
} else { // Computer plays
println("Computer plays: ")
val nextBoard = board.computerPlays
play(nextBoard, Human)
}
}
 
play(new Board(),Human)
 
}
 
}

sample output (human is always first)

1 2 3
4 5 6
7 8 9
Choose a move: 1
X 2 3
4 5 6
7 8 9
Computer plays: 
X 2 O
4 5 6
7 8 9
Choose a move: 3
Invalid move. Choose another one in List(2, 4, 5, 6, 7, 8, 9)
Choose a move: 4
X 2 O
X 5 6
7 8 9
Computer plays: 
X 2 O
X 5 6
7 8 O
Choose a move: 7
X 2 O
X 5 6
X 8 O
You win.

[edit] Tcl

Translation of: Python
package require Tcl 8.6
 
# 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"
}
}
 
# 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"
}
}
 
# 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"
}
}
 
# Assemble the pieces
set ttt [TicTacToe new HumanPlayer RandomRoboPlayer]
$ttt game

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]

[edit] XPL0

TTTXPL0.GIF
\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
];
]
Personal tools
Namespaces

Variants
Actions
Community
Explore
Misc
Toolbox