Tic-tac-toe
Play tic-tac-toe against a friend or against the computer, which wins when it can and blocks when it must; wins and draws are found and a running score is kept - from a text menu.
Every screen below was recorded under CPython. When this page was built, the EML interpreter replayed each session from the same input and printed the same bytes.
About
Tic-tac-toe for two players, or for one player (X) against the computer (O). X always starts; the board shows the free squares by number; a game ends in a win or a draw, or when a player leaves it with q. The score is kept while the program runs.
main.eml- the menu, one game, and the checks on each square typedboard.eml- the board as nine cells, how it is drawn, the eight winning lines, and whether the board is fullai.eml- the computer's move, in order of preference: win now if it can; otherwise block the square where the other player would win; otherwise the centre, then the first free corner, then the first free side. There is no randomness, so the same moves always get the same answer.
What is checked: a square must be one of 1 to 9 and still free; anything else asks again without losing the turn.
Sessions: sessions/basic.in plays a two-player game won by X; a game against the computer in which it takes the centre and a corner and blocks one of two threats, and X wins with the other; a game the computer wins because winning comes before blocking; a two-player draw; and the score; sessions/bad-input.in types an unknown choice, squares 0, 10, a letter and nothing, a taken square, and leaves a game, which does not count.
Built on the verified corpus case mvp-tic-tac-toe (a board, moves that refuse a taken square, and finding a line of three).
Recorded sessions
What the screen shows while someone uses the program. Each typed line appears after its prompt, the way a terminal shows it.
bad-input
interpreter: byte-equal
== Tic-tac-toe ==
1) two players 2) against the computer 3) score 4) quit
choice> 9
Pick a number from 1 to 4.
== Tic-tac-toe ==
1) two players 2) against the computer 3) score 4) quit
choice> 3
X wins: 0, O wins: 0, draws: 0
== Tic-tac-toe ==
1) two players 2) against the computer 3) score 4) quit
choice> 1
1 | 2 | 3
---+---+---
4 | 5 | 6
---+---+---
7 | 8 | 9
X to play, square 1-9 (q to leave)> 0
Pick a free square from 1 to 9.
X to play, square 1-9 (q to leave)> 10
Pick a free square from 1 to 9.
X to play, square 1-9 (q to leave)> x
Pick a free square from 1 to 9.
X to play, square 1-9 (q to leave)>
Pick a free square from 1 to 9.
X to play, square 1-9 (q to leave)> 5
1 | 2 | 3
---+---+---
4 | X | 6
---+---+---
7 | 8 | 9
O to play, square 1-9 (q to leave)> 5
That square is taken.
O to play, square 1-9 (q to leave)> q
Game left.
== Tic-tac-toe ==
1) two players 2) against the computer 3) score 4) quit
choice> 3
X wins: 0, O wins: 0, draws: 0
== Tic-tac-toe ==
1) two players 2) against the computer 3) score 4) quit
choice> 4
Bye.
What was typed (12 lines)
9
3
1
0
10
x
5
5
q
3
4
basic
interpreter: byte-equal
== Tic-tac-toe ==
1) two players 2) against the computer 3) score 4) quit
choice> 1
1 | 2 | 3
---+---+---
4 | 5 | 6
---+---+---
7 | 8 | 9
X to play, square 1-9 (q to leave)> 1
X | 2 | 3
---+---+---
4 | 5 | 6
---+---+---
7 | 8 | 9
O to play, square 1-9 (q to leave)> 4
X | 2 | 3
---+---+---
O | 5 | 6
---+---+---
7 | 8 | 9
X to play, square 1-9 (q to leave)> 2
X | X | 3
---+---+---
O | 5 | 6
---+---+---
7 | 8 | 9
O to play, square 1-9 (q to leave)> 5
X | X | 3
---+---+---
O | O | 6
---+---+---
7 | 8 | 9
X to play, square 1-9 (q to leave)> 3
X | X | X
---+---+---
O | O | 6
---+---+---
7 | 8 | 9
X wins.
== Tic-tac-toe ==
1) two players 2) against the computer 3) score 4) quit
choice> 2
1 | 2 | 3
---+---+---
4 | 5 | 6
---+---+---
7 | 8 | 9
X to play, square 1-9 (q to leave)> 1
Computer plays 5.
X | 2 | 3
---+---+---
4 | O | 6
---+---+---
7 | 8 | 9
X to play, square 1-9 (q to leave)> 9
Computer plays 3.
X | 2 | O
---+---+---
4 | O | 6
---+---+---
7 | 8 | X
X to play, square 1-9 (q to leave)> 7
Computer plays 8.
X | 2 | O
---+---+---
4 | O | 6
---+---+---
X | O | X
X to play, square 1-9 (q to leave)> 4
X | 2 | O
---+---+---
X | O | 6
---+---+---
X | O | X
You win.
== Tic-tac-toe ==
1) two players 2) against the computer 3) score 4) quit
choice> 2
1 | 2 | 3
---+---+---
4 | 5 | 6
---+---+---
7 | 8 | 9
X to play, square 1-9 (q to leave)> 2
Computer plays 5.
1 | X | 3
---+---+---
4 | O | 6
---+---+---
7 | 8 | 9
X to play, square 1-9 (q to leave)> 8
Computer plays 1.
O | X | 3
---+---+---
4 | O | 6
---+---+---
7 | X | 9
X to play, square 1-9 (q to leave)> 3
Computer plays 9.
O | X | X
---+---+---
4 | O | 6
---+---+---
7 | X | O
The computer wins.
== Tic-tac-toe ==
1) two players 2) against the computer 3) score 4) quit
choice> 1
1 | 2 | 3
---+---+---
4 | 5 | 6
---+---+---
7 | 8 | 9
X to play, square 1-9 (q to leave)> 1
X | 2 | 3
---+---+---
4 | 5 | 6
---+---+---
7 | 8 | 9
O to play, square 1-9 (q to leave)> 5
X | 2 | 3
---+---+---
4 | O | 6
---+---+---
7 | 8 | 9
X to play, square 1-9 (q to leave)> 9
X | 2 | 3
---+---+---
4 | O | 6
---+---+---
7 | 8 | X
O to play, square 1-9 (q to leave)> 2
X | O | 3
---+---+---
4 | O | 6
---+---+---
7 | 8 | X
X to play, square 1-9 (q to leave)> 8
X | O | 3
---+---+---
4 | O | 6
---+---+---
7 | X | X
O to play, square 1-9 (q to leave)> 7
X | O | 3
---+---+---
4 | O | 6
---+---+---
O | X | X
X to play, square 1-9 (q to leave)> 3
X | O | X
---+---+---
4 | O | 6
---+---+---
O | X | X
O to play, square 1-9 (q to leave)> 6
X | O | X
---+---+---
4 | O | O
---+---+---
O | X | X
X to play, square 1-9 (q to leave)> 4
X | O | X
---+---+---
X | O | O
---+---+---
O | X | X
Draw.
== Tic-tac-toe ==
1) two players 2) against the computer 3) score 4) quit
choice> 3
X wins: 2, O wins: 1, draws: 1
== Tic-tac-toe ==
1) two players 2) against the computer 3) score 4) quit
choice> 4
Bye.
What was typed (27 lines)
1
1
4
2
5
3
2
1
9
7
4
2
2
8
3
1
1
5
9
2
8
7
3
6
4
3
4
Modules
The program as written, entry module first. Each module transpiles to its own Python file, which is what eml project run executes.
main.eml(entry)
eml# P009 tic-tac-toe: two players, or you (X) against the computer (O). X
# always starts. The score is kept while the program runs.
import board
import ai
def trim(s):
0 => i
len(s) => j
while i < j and s[i] == " ":
i + 1 => i
while j > i and s[j - 1] == " ":
j - 1 => j
return s[i:j]
def ask_square(b, who):
# The cell index of a free square for who, or -1 when q leaves the game.
while True:
trim(input(who + " to play, square 1-9 (q to leave)> ")) => typed
if typed == "q":
return -1
if len(typed) == 1 and typed in "123456789":
int(typed) - 1 => i
if b[i] == "":
return i
"That square is taken." ^0
else:
"Pick a free square from 1 to 9." ^0
def play(vs_computer):
# One game. Returns "X", "O", "draw", or "" when it was left.
board.empty() => b
"X" => who
while True:
if vs_computer and who == "O":
ai.move(b, "O", "X") => i
("Computer plays " + str(i + 1) + ".") ^0
else:
board.show(b)
ask_square(b, who) => i
if i < 0:
"Game left." ^0
return ""
board.placed(b, i, who) => b
board.winner(b) => w
if w != "":
board.show(b)
if not vs_computer:
(w + " wins.") ^0
elif w == "X":
"You win." ^0
else:
"The computer wins." ^0
return w
if board.full(b):
board.show(b)
"Draw." ^0
return "draw"
if who == "X":
"O" => who
else:
"X" => who
[0, 0, 0] => score
True => running
while running:
"" ^0
"== Tic-tac-toe ==" ^0
"1) two players 2) against the computer 3) score 4) quit" ^0
trim(input("choice> ")) => choice
if choice == "1" or choice == "2":
play(choice == "2") => result
if result == "X":
score[0] + 1 => score[0]
elif result == "O":
score[1] + 1 => score[1]
elif result == "draw":
score[2] + 1 => score[2]
elif choice == "3":
("X wins: " + str(score[0]) + ", O wins: " + str(score[1]) + ", draws: " + str(score[2])) ^0
elif choice == "4":
False => running
else:
"Pick a number from 1 to 4." ^0
"Bye." ^0
Python projection (main.py)
import board
import ai
def trim(s):
i = 0
j = len(s)
while i < j and s[i] == " ":
i = i + 1
while j > i and s[j - 1] == " ":
j = j - 1
return s[i:j]
def ask_square(b, who):
while True:
typed = trim(input(who + " to play, square 1-9 (q to leave)> "))
if typed == "q":
return -1
if len(typed) == 1 and typed in "123456789":
i = int(typed) - 1
if b[i] == "":
return i
print("That square is taken.")
else:
print("Pick a free square from 1 to 9.")
def play(vs_computer):
b = board.empty()
who = "X"
while True:
if vs_computer and who == "O":
i = ai.move(b, "O", "X")
print("Computer plays " + str(i + 1) + ".")
else:
board.show(b)
i = ask_square(b, who)
if i < 0:
print("Game left.")
return ""
b = board.placed(b, i, who)
w = board.winner(b)
if w != "":
board.show(b)
if not vs_computer:
print(w + " wins.")
elif w == "X":
print("You win.")
else:
print("The computer wins.")
return w
if board.full(b):
board.show(b)
print("Draw.")
return "draw"
if who == "X":
who = "O"
else:
who = "X"
score = [0, 0, 0]
running = True
while running:
print("")
print("== Tic-tac-toe ==")
print("1) two players 2) against the computer 3) score 4) quit")
choice = trim(input("choice> "))
if choice == "1" or choice == "2":
result = play(choice == "2")
if result == "X":
score[0] = score[0] + 1
elif result == "O":
score[1] = score[1] + 1
elif result == "draw":
score[2] = score[2] + 1
elif choice == "3":
print("X wins: " + str(score[0]) + ", O wins: " + str(score[1]) + ", draws: " + str(score[2]))
elif choice == "4":
running = False
else:
print("Pick a number from 1 to 4.")
print("Bye.")
board.eml
eml# P009 tic-tac-toe - the board. It is a list of nine cells, index 0 to 8 for
# squares 1 to 9 in reading order; a cell is "X", "O" or "" when empty.
# The eight lines that win: three rows, three columns, two diagonals.
[
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6],
] => lines
def empty():
return ["" for i in [0:8]]
def mark(cell, i):
# What a square shows: its mark, or its number while it is free.
if cell == "":
return str(i + 1)
return cell
def show(b):
"" ^0
for r in [0:2]:
(" " + mark(b[3 * r], 3 * r) + " | " + mark(b[3 * r + 1], 3 * r + 1) + " | " + mark(b[3 * r + 2], 3 * r + 2)) ^0
if r < 2:
"---+---+---" ^0
def winner(b):
# "X" or "O" if that player has a whole line, otherwise "".
for line in lines:
b[line[0]] => a
if a != "" and b[line[1]] == a and b[line[2]] == a:
return a
return ""
def full(b):
for cell in b:
if cell == "":
return False
return True
def placed(b, i, who):
# A new board with who's mark on cell i.
return b[0:i] + [who] + b[i + 1:9]
Python projection (board.py)
lines = [[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 empty():
return ["" for i in range(0, 9)]
def mark(cell, i):
if cell == "":
return str(i + 1)
return cell
def show(b):
print("")
for r in range(0, 3):
print(" " + mark(b[3 * r], 3 * r) + " | " + mark(b[3 * r + 1], 3 * r + 1) + " | " + mark(b[3 * r + 2], 3 * r + 2))
if r < 2:
print("---+---+---")
def winner(b):
for line in lines:
a = b[line[0]]
if a != "" and b[line[1]] == a and b[line[2]] == a:
return a
return ""
def full(b):
for cell in b:
if cell == "":
return False
return True
def placed(b, i, who):
return b[0:i] + [who] + b[i + 1:9]
ai.eml
eml# P009 tic-tac-toe - the computer's move, in order of preference: win now if
# it can; otherwise block the square where the other player would win;
# otherwise take the centre, then the first free corner, then the first free
# side. No randomness, so the same moves always get the same answer.
import board
def completing(b, who):
# The free cell that would give who a whole line, or -1. Lines are tried
# in order, so the first such cell is the answer.
for line in board.lines:
0 => mine
-1 => free
for i in line:
if b[i] == who:
mine + 1 => mine
elif b[i] == "":
i => free
if mine == 2 and free >= 0:
return free
return -1
def move(b, me, other):
completing(b, me) => i
if i >= 0:
return i
completing(b, other) => i
if i >= 0:
return i
for i in [4, 0, 2, 6, 8, 1, 3, 5, 7]:
if b[i] == "":
return i
return -1
Python projection (ai.py)
import board
def completing(b, who):
for line in board.lines:
mine = 0
free = -1
for i in line:
if b[i] == who:
mine = mine + 1
elif b[i] == "":
free = i
if mine == 2 and free >= 0:
return free
return -1
def move(b, me, other):
i = completing(b, me)
if i >= 0:
return i
i = completing(b, other)
if i >= 0:
return i
for i in [4, 0, 2, 6, 8, 1, 3, 5, 7]:
if b[i] == "":
return i
return -1