Pig (dice game)
The dice game Pig for two players or against the computer: roll as often as you like, every roll adds to the turn, a 1 loses the whole turn, and holding banks it - first to the target wins. The computer holds at 20; the dice come from a random generator written in EML, so a seed always gives the same game, and a tally shows how often each face came up.
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
The dice game Pig, for two players or one against the computer. On a turn you roll a die as often as you like: every roll adds to the turn, but a 1 loses the whole turn, and holding banks it. The first to the target - 100 unless it is changed - wins; a turn that reaches the target is banked at once. The dice come from a random generator written in EML, so a seed always gives the same rolls, and a tally shows how often each face has come up.
main.eml- the menu, the names, the target and the seed, a turn at the keyboard and the gamepig.eml- the computer's strategy and its turnrng.eml- the random generator of P008 (number guessing), with a dietally.eml- how often each face came up, with a bar for each
How each part works:
- The computer holds at 20, or as soon as its turn would win. A roll at turn total t loses t one time in six and otherwise adds 4 on average ((2 + 3 + 4 + 5 + 6) / 5), so it is worth (20 - t) / 6 points on average: rolling pays while the turn is below 20. Worked out exactly over a whole turn, holding at 20 gives the most points per turn, 8.14 on average (so does 21: at 20 one more roll gains nothing on average). That is the best rule for points per turn; winning a game is a different question - near the end, the score still needed matters more - and the computer looks at that only to stop when its turn would win.
- The generator is a linear congruential one with the constants of the C standard's example rand(), its state below 2^31; a die is 1 plus the high bits of the state (the state divided by 65536) modulo 6. The constants meet the conditions for the full period, so in one period every state comes once and each face comes up exactly as often as its share of the 32,768 high-bit values: 5,462 for faces 1 and 2 and 5,461 for the others - a sixth, to within 0.012%.
- Python's random module is not used: a session is checked byte for byte in CPython and in the EML interpreter, and both must see the same rolls.
- The tally counts every roll of the run, the computer's too, as in the corpus case
dice-roll-tally: a count for each face, the face that came up most often (the lowest on a tie) and the share a fair die would give.
What is checked: names of 1 to 12 letters, different in any case, and not Computer when playing the computer; r, h or q on a turn, in either case, and no hold before the first roll; a target from 10 to 500; a seed of up to 9 digits. An empty answer cancels.
Sessions: sessions/basic.in sets the target to 40. Ana and Bo play: Bo holds once his turn reaches 12 and Ana at 15; Ana loses four turns to a 1 before her first hold, and Bo wins 43 to 32 with a single 4 that takes him past 40. Then Cy plays the computer, which holds once with 21 and loses three turns to a 1; Cy wins 41 to 21. The tally of the 57 rolls ends the session. sessions/bad-input.in gives menu choices 0 and x, the tally before any roll, targets 5, 501 and x, seeds x and 1234567890, a name with a digit, a name of 13 letters, the same name in capitals, a hold before rolling, x and roll as moves, R in capitals, q to stop the game, Computer as a name and two cancelled games; then it sets seed 7 twice, and both games start with 5, 3 and 3 - the same seed gives the same rolls.
Built on the verified corpus case dice-roll-tally (a tally of dice faces, with the most frequent face found by comparison).
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
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 0
Pick a number from 1 to 6.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> x
Pick a number from 1 to 6.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 5
No dice rolled yet.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 3
target (10 to 500)> 5
The target stays 100: type a number from 10 to 500.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 3
target (10 to 500)> 501
The target stays 100: type a number from 10 to 500.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 3
target (10 to 500)> x
The target stays 100: type a number from 10 to 500.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 3
target (10 to 500)>
Cancelled.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 4
seed> x
A seed is a whole number of up to 9 digits.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 4
seed> 1234567890
A seed is a whole number of up to 9 digits.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 4
seed>
Cancelled.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 1
player 1> Al1
A name is 1 to 12 letters.
player 1> Abcdefghijklm
A name is 1 to 12 letters.
player 1> Ann
player 2> ANN
Ann is already playing.
player 2> Ben
Type r to roll, h to hold, q to stop the game.
Ann 0, Ben 0 - Ann to play.
Ann (turn 0)> h
Roll at least once before holding.
Ann (turn 0)> x
Type r to roll, h to hold or q to stop the game.
Ann (turn 0)> roll
Type r to roll, h to hold or q to stop the game.
Ann (turn 0)> R
Ann rolls 5 - turn 5.
Ann (turn 5)> r
Ann rolls 1 - the turn is lost.
Ann 0, Ben 0 - Ben to play.
Ben (turn 0)> q
Game stopped.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 2
your name> computer
Computer is already playing.
your name>
Cancelled.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 1
player 1> Ann
player 2>
Cancelled.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 5
2 rolls so far:
1: 1 #
2: 0
3: 0
4: 0
5: 1 #
6: 0
Most often: 1 (1 time). A fair die would give each face 0.3 of them.
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 4
seed> 7
Seed set to 7: the same seed gives the same rolls.
== Pig (first to 100, seed 7) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 1
player 1> Ann
player 2> Ben
Type r to roll, h to hold, q to stop the game.
Ann 0, Ben 0 - Ann to play.
Ann (turn 0)> r
Ann rolls 5 - turn 5.
Ann (turn 5)> r
Ann rolls 3 - turn 8.
Ann (turn 8)> r
Ann rolls 3 - turn 11.
Ann (turn 11)> q
Game stopped.
== Pig (first to 100, seed 7) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 4
seed> 7
Seed set to 7: the same seed gives the same rolls.
== Pig (first to 100, seed 7) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 1
player 1> Ann
player 2> Ben
Type r to roll, h to hold, q to stop the game.
Ann 0, Ben 0 - Ann to play.
Ann (turn 0)> r
Ann rolls 5 - turn 5.
Ann (turn 5)> r
Ann rolls 3 - turn 8.
Ann (turn 8)> r
Ann rolls 3 - turn 11.
Ann (turn 11)> q
Game stopped.
== Pig (first to 100, seed 7) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 5
8 rolls so far:
1: 1 #
2: 0
3: 4 ####
4: 0
5: 3 ###
6: 0
Most often: 3 (4 times). A fair die would give each face 1.3 of them.
== Pig (first to 100, seed 7) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 6
Bye.
What was typed (56 lines)
0
x
5
3
5
3
501
3
x
3
4
x
4
1234567890
4
1
Al1
Abcdefghijklm
Ann
ANN
Ben
h
x
roll
R
r
q
2
computer
1
Ann
5
4
7
1
Ann
Ben
r
r
r
q
4
7
1
Ann
Ben
r
r
r
q
5
6
basic
interpreter: byte-equal
== Pig (first to 100, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 3
target (10 to 500)> 40
The first to 40 wins.
== Pig (first to 40, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 1
player 1> Ana
player 2> Bo
Type r to roll, h to hold, q to stop the game.
Ana 0, Bo 0 - Ana to play.
Ana (turn 0)> r
Ana rolls 5 - turn 5.
Ana (turn 5)> r
Ana rolls 1 - the turn is lost.
Ana 0, Bo 0 - Bo to play.
Bo (turn 0)> r
Bo rolls 2 - turn 2.
Bo (turn 2)> r
Bo rolls 3 - turn 5.
Bo (turn 5)> r
Bo rolls 3 - turn 8.
Bo (turn 8)> r
Bo rolls 6 - turn 14.
Bo (turn 14)> h
Bo holds: 14, score 14.
Ana 0, Bo 14 - Ana to play.
Ana (turn 0)> r
Ana rolls 3 - turn 3.
Ana (turn 3)> r
Ana rolls 2 - turn 5.
Ana (turn 5)> r
Ana rolls 4 - turn 9.
Ana (turn 9)> r
Ana rolls 1 - the turn is lost.
Ana 0, Bo 14 - Bo to play.
Bo (turn 0)> r
Bo rolls 1 - the turn is lost.
Ana 0, Bo 14 - Ana to play.
Ana (turn 0)> r
Ana rolls 3 - turn 3.
Ana (turn 3)> r
Ana rolls 1 - the turn is lost.
Ana 0, Bo 14 - Bo to play.
Bo (turn 0)> r
Bo rolls 5 - turn 5.
Bo (turn 5)> r
Bo rolls 4 - turn 9.
Bo (turn 9)> r
Bo rolls 3 - turn 12.
Bo (turn 12)> h
Bo holds: 12, score 26.
Ana 0, Bo 26 - Ana to play.
Ana (turn 0)> r
Ana rolls 3 - turn 3.
Ana (turn 3)> r
Ana rolls 1 - the turn is lost.
Ana 0, Bo 26 - Bo to play.
Bo (turn 0)> r
Bo rolls 3 - turn 3.
Bo (turn 3)> r
Bo rolls 3 - turn 6.
Bo (turn 6)> r
Bo rolls 4 - turn 10.
Bo (turn 10)> r
Bo rolls 1 - the turn is lost.
Ana 0, Bo 26 - Ana to play.
Ana (turn 0)> r
Ana rolls 4 - turn 4.
Ana (turn 4)> r
Ana rolls 4 - turn 8.
Ana (turn 8)> r
Ana rolls 2 - turn 10.
Ana (turn 10)> r
Ana rolls 2 - turn 12.
Ana (turn 12)> r
Ana rolls 4 - turn 16.
Ana (turn 16)> h
Ana holds: 16, score 16.
Ana 16, Bo 26 - Bo to play.
Bo (turn 0)> r
Bo rolls 2 - turn 2.
Bo (turn 2)> r
Bo rolls 6 - turn 8.
Bo (turn 8)> r
Bo rolls 5 - turn 13.
Bo (turn 13)> h
Bo holds: 13, score 39.
Ana 16, Bo 39 - Ana to play.
Ana (turn 0)> r
Ana rolls 5 - turn 5.
Ana (turn 5)> r
Ana rolls 6 - turn 11.
Ana (turn 11)> r
Ana rolls 5 - turn 16.
Ana (turn 16)> h
Ana holds: 16, score 32.
Ana 32, Bo 39 - Bo to play.
Bo (turn 0)> r
Bo rolls 4 - turn 4, score 43.
Bo wins, 43 to 32!
== Pig (first to 40, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 2
your name> Cy
Type r to roll, h to hold, q to stop the game.
The computer holds at 20, or as soon as its turn would win.
Cy 0, Computer 0 - Cy to play.
Cy (turn 0)> r
Cy rolls 6 - turn 6.
Cy (turn 6)> r
Cy rolls 5 - turn 11.
Cy (turn 11)> r
Cy rolls 1 - the turn is lost.
Cy 0, Computer 0 - Computer to play.
Computer rolls 3, 2, 4, 5, 2, 5 and holds: 21, score 21.
Cy 0, Computer 21 - Cy to play.
Cy (turn 0)> r
Cy rolls 6 - turn 6.
Cy (turn 6)> r
Cy rolls 1 - the turn is lost.
Cy 0, Computer 21 - Computer to play.
Computer rolls 1 - the turn is lost.
Cy 0, Computer 21 - Cy to play.
Cy (turn 0)> r
Cy rolls 5 - turn 5.
Cy (turn 5)> r
Cy rolls 6 - turn 11.
Cy (turn 11)> r
Cy rolls 6 - turn 17.
Cy (turn 17)> h
Cy holds: 17, score 17.
Cy 17, Computer 21 - Computer to play.
Computer rolls 2, 1 - the turn is lost.
Cy 17, Computer 21 - Cy to play.
Cy (turn 0)> r
Cy rolls 4 - turn 4.
Cy (turn 4)> r
Cy rolls 5 - turn 9.
Cy (turn 9)> r
Cy rolls 5 - turn 14.
Cy (turn 14)> h
Cy holds: 14, score 31.
Cy 31, Computer 21 - Computer to play.
Computer rolls 1 - the turn is lost.
Cy 31, Computer 21 - Cy to play.
Cy (turn 0)> r
Cy rolls 6 - turn 6.
Cy (turn 6)> r
Cy rolls 4 - turn 10, score 41.
Cy wins, 41 to 21!
== Pig (first to 40, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 5
57 rolls so far:
1: 11 ###########
2: 8 ########
3: 9 #########
4: 10 ##########
5: 11 ###########
6: 8 ########
Most often: 1 (11 times). A fair die would give each face 9.5 of them.
== Pig (first to 40, seed 2026) ==
1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit
choice> 6
Bye.
What was typed (63 lines)
3
40
1
Ana
Bo
r
r
r
r
r
r
h
r
r
r
r
r
r
r
r
r
r
h
r
r
r
r
r
r
r
r
r
r
r
h
r
r
r
h
r
r
r
h
r
2
Cy
r
r
r
r
r
r
r
r
h
r
r
r
h
r
r
5
6
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# P035 dice game Pig: two players, or one against the computer. On a turn
# you roll a die as often as you like - every roll adds to the turn, but a 1
# loses the whole turn - and holding banks it. The first to the target wins.
# The dice come from a random generator written in EML, so the same seed
# always gives the same rolls, and a tally shows how often each face came up.
import pig
import rng
import tally
12 => longest_name
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 lower(s):
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" => big
"abcdefghijklmnopqrstuvwxyz" => small
"" => out
for c in s:
0 => k
while k < 26 and big[k] != c:
k + 1 => k
if k < 26:
out + small[k] => out
else:
out + c => out
return out
def number(s):
# The value of 1 to 9 digits, otherwise -1.
if s == "" or len(s) > 9:
return -1
0 => n
for c in s:
if not (c in "0123456789"):
return -1
n * 10 + int(c) => n
return n
def ask_name(prompt, taken):
# A name of 1 to 12 letters, not taken (in any case); "" when the
# answer is empty.
while True:
trim(input(prompt)) => name
if name == "":
return ""
True => letters
for c in name:
if not (c in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"):
False => letters
if not letters or len(name) > longest_name:
("A name is 1 to " + str(longest_name) + " letters.") ^0
else:
"" => clash
for t in taken:
if lower(t) == lower(name):
t => clash
if clash == "":
return name
(clash + " is already playing.") ^0
def human_turn(r, name, score, target):
# One turn at the keyboard. Returns [how it ended, points, rolls], where
# it ends "lost", "held", "won" or "stopped".
0 => turn
[] => rolls
while True:
lower(trim(input(name + " (turn " + str(turn) + ")> "))) => answer
if answer == "r":
r.die() => d
rolls + [d] => rolls
if d == 1:
(name + " rolls 1 - the turn is lost.") ^0
return ["lost", 0, rolls]
turn + d => turn
if score + turn >= target:
(name + " rolls " + str(d) + " - turn " + str(turn) + ", score " + str(score + turn) + ".") ^0
return ["won", turn, rolls]
(name + " rolls " + str(d) + " - turn " + str(turn) + ".") ^0
elif answer == "h":
if turn == 0:
"Roll at least once before holding." ^0
else:
(name + " holds: " + str(turn) + ", score " + str(score + turn) + ".") ^0
return ["held", turn, rolls]
elif answer == "q":
return ["stopped", 0, rolls]
else:
"Type r to roll, h to hold or q to stop the game." ^0
def computer_turn(r, score, target):
# The computer's turn, shown on one line. Returns [how it ended, points,
# rolls] like human_turn.
pig.computer_turn(r, score, target) => rolls
if rolls[len(rolls) - 1] == 1:
("Computer rolls " + pig.listed(rolls) + " - the turn is lost.") ^0
return ["lost", 0, rolls]
0 => turn
for d in rolls:
turn + d => turn
if score + turn >= target:
("Computer rolls " + pig.listed(rolls) + " - turn " + str(turn) + ", score " + str(score + turn) + ".") ^0
return ["won", turn, rolls]
("Computer rolls " + pig.listed(rolls) + " and holds: " + str(turn) + ", score " + str(score + turn) + ".") ^0
return ["held", turn, rolls]
def play(r, names, computer, target, counts):
# One game; the first name starts. Returns the tally with this game's
# rolls added.
[0, 0] => scores
0 => p
"Type r to roll, h to hold, q to stop the game." ^0
if computer:
"The computer holds at 20, or as soon as its turn would win." ^0
while True:
(names[0] + " " + str(scores[0]) + ", " + names[1] + " " + str(scores[1]) + " - " + names[p] + " to play.") ^0
if computer and p == 1:
computer_turn(r, scores[1], target) => t
else:
human_turn(r, names[p], scores[p], target) => t
tally.counted(counts, t[2]) => counts
if t[0] == "stopped":
"Game stopped." ^0
return counts
scores[p] + t[1] => scores[p]
if t[0] == "won":
(names[p] + " wins, " + str(scores[p]) + " to " + str(scores[1 - p]) + "!") ^0
return counts
1 - p => p
def new_game(r, computer, target, counts):
if computer:
ask_name("your name> ", ["Computer"]) => a
if a == "":
"Cancelled." ^0
return counts
return play(r, [a, "Computer"], True, target, counts)
ask_name("player 1> ", []) => a
if a == "":
"Cancelled." ^0
return counts
ask_name("player 2> ", [a]) => b
if b == "":
"Cancelled." ^0
return counts
return play(r, [a, b], False, target, counts)
100 => target
2026 => seed
rng.Rng(seed) => r
[0, 0, 0, 0, 0, 0] => counts
True => running
while running:
"" ^0
("== Pig (first to " + str(target) + ", seed " + str(seed) + ") ==") ^0
"1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit" ^0
trim(input("choice> ")) => choice
if choice == "1":
new_game(r, False, target, counts) => counts
elif choice == "2":
new_game(r, True, target, counts) => counts
elif choice == "3":
trim(input("target (10 to 500)> ")) => answer
number(answer) => n
if answer == "":
"Cancelled." ^0
elif n >= 10 and n <= 500:
n => target
("The first to " + str(target) + " wins.") ^0
else:
("The target stays " + str(target) + ": type a number from 10 to 500.") ^0
elif choice == "4":
trim(input("seed> ")) => answer
number(answer) => s
if answer == "":
"Cancelled." ^0
elif s < 0:
"A seed is a whole number of up to 9 digits." ^0
else:
s => seed
rng.Rng(seed) => r
("Seed set to " + str(seed) + ": the same seed gives the same rolls.") ^0
elif choice == "5":
tally.show(counts)
elif choice == "6":
False => running
else:
"Pick a number from 1 to 6." ^0
"Bye." ^0
Python projection (main.py)
import pig
import rng
import tally
longest_name = 12
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 lower(s):
big = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
small = "abcdefghijklmnopqrstuvwxyz"
out = ""
for c in s:
k = 0
while k < 26 and big[k] != c:
k = k + 1
if k < 26:
out = out + small[k]
else:
out = out + c
return out
def number(s):
if s == "" or len(s) > 9:
return -1
n = 0
for c in s:
if not c in "0123456789":
return -1
n = n * 10 + int(c)
return n
def ask_name(prompt, taken):
while True:
name = trim(input(prompt))
if name == "":
return ""
letters = True
for c in name:
if not c in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz":
letters = False
if not letters or len(name) > longest_name:
print("A name is 1 to " + str(longest_name) + " letters.")
else:
clash = ""
for t in taken:
if lower(t) == lower(name):
clash = t
if clash == "":
return name
print(clash + " is already playing.")
def human_turn(r, name, score, target):
turn = 0
rolls = []
while True:
answer = lower(trim(input(name + " (turn " + str(turn) + ")> ")))
if answer == "r":
d = r.die()
rolls = rolls + [d]
if d == 1:
print(name + " rolls 1 - the turn is lost.")
return ["lost", 0, rolls]
turn = turn + d
if score + turn >= target:
print(name + " rolls " + str(d) + " - turn " + str(turn) + ", score " + str(score + turn) + ".")
return ["won", turn, rolls]
print(name + " rolls " + str(d) + " - turn " + str(turn) + ".")
elif answer == "h":
if turn == 0:
print("Roll at least once before holding.")
else:
print(name + " holds: " + str(turn) + ", score " + str(score + turn) + ".")
return ["held", turn, rolls]
elif answer == "q":
return ["stopped", 0, rolls]
else:
print("Type r to roll, h to hold or q to stop the game.")
def computer_turn(r, score, target):
rolls = pig.computer_turn(r, score, target)
if rolls[len(rolls) - 1] == 1:
print("Computer rolls " + pig.listed(rolls) + " - the turn is lost.")
return ["lost", 0, rolls]
turn = 0
for d in rolls:
turn = turn + d
if score + turn >= target:
print("Computer rolls " + pig.listed(rolls) + " - turn " + str(turn) + ", score " + str(score + turn) + ".")
return ["won", turn, rolls]
print("Computer rolls " + pig.listed(rolls) + " and holds: " + str(turn) + ", score " + str(score + turn) + ".")
return ["held", turn, rolls]
def play(r, names, computer, target, counts):
scores = [0, 0]
p = 0
print("Type r to roll, h to hold, q to stop the game.")
if computer:
print("The computer holds at 20, or as soon as its turn would win.")
while True:
print(names[0] + " " + str(scores[0]) + ", " + names[1] + " " + str(scores[1]) + " - " + names[p] + " to play.")
if computer and p == 1:
t = computer_turn(r, scores[1], target)
else:
t = human_turn(r, names[p], scores[p], target)
counts = tally.counted(counts, t[2])
if t[0] == "stopped":
print("Game stopped.")
return counts
scores[p] = scores[p] + t[1]
if t[0] == "won":
print(names[p] + " wins, " + str(scores[p]) + " to " + str(scores[1 - p]) + "!")
return counts
p = 1 - p
def new_game(r, computer, target, counts):
if computer:
a = ask_name("your name> ", ["Computer"])
if a == "":
print("Cancelled.")
return counts
return play(r, [a, "Computer"], True, target, counts)
a = ask_name("player 1> ", [])
if a == "":
print("Cancelled.")
return counts
b = ask_name("player 2> ", [a])
if b == "":
print("Cancelled.")
return counts
return play(r, [a, b], False, target, counts)
target = 100
seed = 2026
r = rng.Rng(seed)
counts = [0, 0, 0, 0, 0, 0]
running = True
while running:
print("")
print("== Pig (first to " + str(target) + ", seed " + str(seed) + ") ==")
print("1) two players 2) play the computer 3) target 4) seed 5) dice tally 6) quit")
choice = trim(input("choice> "))
if choice == "1":
counts = new_game(r, False, target, counts)
elif choice == "2":
counts = new_game(r, True, target, counts)
elif choice == "3":
answer = trim(input("target (10 to 500)> "))
n = number(answer)
if answer == "":
print("Cancelled.")
elif n >= 10 and n <= 500:
target = n
print("The first to " + str(target) + " wins.")
else:
print("The target stays " + str(target) + ": type a number from 10 to 500.")
elif choice == "4":
answer = trim(input("seed> "))
s = number(answer)
if answer == "":
print("Cancelled.")
elif s < 0:
print("A seed is a whole number of up to 9 digits.")
else:
seed = s
r = rng.Rng(seed)
print("Seed set to " + str(seed) + ": the same seed gives the same rolls.")
elif choice == "5":
tally.show(counts)
elif choice == "6":
running = False
else:
print("Pick a number from 1 to 6.")
print("Bye.")
pig.eml
eml# P035 dice game Pig - the computer's play. On a turn a player rolls as
# often as they like: every roll adds to the turn, a 1 loses the whole turn,
# and holding banks the turn. The first to the target wins; a turn that
# reaches the target is banked at once.
20 => hold_at
def should_hold(turn, score, target):
# The computer's fixed strategy: hold at 20, or as soon as the turn wins.
# A roll at turn total t loses t one time in six and otherwise adds 4 on
# average ((2 + 3 + 4 + 5 + 6) / 5), so it is worth (20 - t) / 6 points
# on average: rolling pays while the turn is below 20.
return turn >= hold_at or score + turn >= target
def computer_turn(r, score, target):
# The rolls of one turn, in order; the last one is 1 if the turn was lost.
[] => rolls
0 => turn
while True:
r.die() => d
rolls + [d] => rolls
if d == 1:
return rolls
turn + d => turn
if should_hold(turn, score, target):
return rolls
def listed(rolls):
# [3, 5, 1] as "3, 5, 1".
"" => out
for d in rolls:
if out != "":
out + ", " => out
out + str(d) => out
return out
Python projection (pig.py)
hold_at = 20
def should_hold(turn, score, target):
return turn >= hold_at or score + turn >= target
def computer_turn(r, score, target):
rolls = []
turn = 0
while True:
d = r.die()
rolls = rolls + [d]
if d == 1:
return rolls
turn = turn + d
if should_hold(turn, score, target):
return rolls
def listed(rolls):
out = ""
for d in rolls:
if out != "":
out = out + ", "
out = out + str(d)
return out
rng.eml
eml# P035 dice game Pig - random numbers written in EML: the linear
# congruential generator of P008 (number guessing), with the constants of
# the C standard's example rand(). Python's random module is not used, so a
# seed gives the same rolls on every machine, and the interpreter can check
# a whole session byte for byte.
class Rng:
def __init__(self, seed):
seed % 2147483648 => self.state
def step(self):
(1103515245 * self.state + 12345) % 2147483648 => self.state
return self.state
def below(self, n):
# A number from 0 to n - 1, taken from the high bits of the state: the
# low bits of this generator repeat with short periods. Dividing by
# 65536 is exact in a float for a state below 2^31.
return int(self.step() / 65536) % n
def die(self):
return 1 + self.below(6)
Python projection (rng.py)
class Rng:
def __init__(self, seed):
self.state = seed % 2147483648
def step(self):
self.state = (1103515245 * self.state + 12345) % 2147483648
return self.state
def below(self, n):
return int(self.step() / 65536) % n
def die(self):
return 1 + self.below(6)
tally.eml
eml# P035 dice game Pig - the dice tally: how often each face has come up in
# this run, as in the corpus case dice-roll-tally, with a bar for each face.
def quotient(a, b):
return int((a - a % b) / b)
def counted(counts, rolls):
# counts is [how many 1s, ..., how many 6s]; returns it with rolls added.
[] => out
for face in [1:6]:
counts[face - 1] => n
for d in rolls:
if d == face:
n + 1 => n
out + [n] => out
return out
def show(counts):
0 => total
for n in counts:
total + n => total
if total == 0:
"No dice rolled yet." ^0
return 0
if total == 1:
"1 roll so far:" ^0
else:
(str(total) + " rolls so far:") ^0
for face in [1:6]:
str(counts[face - 1]) => n
while len(n) < 3:
" " + n => n
(" " + str(face) + ":" + n + " " + "#" * counts[face - 1]) => line
if counts[face - 1] == 0:
(" " + str(face) + ":" + n) => line
line ^0
# the most frequent face, the lowest on a tie
1 => best
for face in [2:6]:
if counts[face - 1] > counts[best - 1]:
face => best
"times" => times
if counts[best - 1] == 1:
"time" => times
# a fair die gives each face a sixth of the rolls; in tenths, halves up
quotient(total * 20 + 6, 12) => tenths
("Most often: " + str(best) + " (" + str(counts[best - 1]) + " " + times + "). A fair die would give each face " + str(quotient(tenths, 10)) + "." + str(tenths % 10) + " of them.") ^0
return 0
Python projection (tally.py)
def quotient(a, b):
return int((a - a % b) / b)
def counted(counts, rolls):
out = []
for face in range(1, 7):
n = counts[face - 1]
for d in rolls:
if d == face:
n = n + 1
out = out + [n]
return out
def show(counts):
total = 0
for n in counts:
total = total + n
if total == 0:
print("No dice rolled yet.")
return 0
if total == 1:
print("1 roll so far:")
else:
print(str(total) + " rolls so far:")
for face in range(1, 7):
n = str(counts[face - 1])
while len(n) < 3:
n = " " + n
line = " " + str(face) + ":" + n + " " + "#" * counts[face - 1]
if counts[face - 1] == 0:
line = " " + str(face) + ":" + n
print(line)
best = 1
for face in range(2, 7):
if counts[face - 1] > counts[best - 1]:
best = face
times = "times"
if counts[best - 1] == 1:
times = "time"
tenths = quotient(total * 20 + 6, 12)
print("Most often: " + str(best) + " (" + str(counts[best - 1]) + " " + times + "). A fair die would give each face " + str(quotient(tenths, 10)) + "." + str(tenths % 10) + " of them.")
return 0