<!-- canonical: https://efficientnewlanguage.org/eml-p/projects/P080-rock-paper-scissors/ | updated: 2026-10-14 -->

# P080 Rock paper scissors

Rock paper scissors against a computer that learns how often you play each move and what you play after each, guessing your next move and playing what beats it; fixed strategies played for hundreds of rounds; and what it has learned about you.

EML-P project `projects/rock-paper-scissors` in the EML language repo: 2 module(s), entry `main.eml`, terminal UI. There, `eml project run projects/rock-paper-scissors` runs it and `eml project verify projects/rock-paper-scissors` replays every session under CPython (two hash seeds) and in the interpreter; the site build replays every session in the interpreter again and publishes a session only if its screen matches.

Built on verified corpus cases: rock-paper-scissors-simulator (https://efficientnewlanguage.org/cases/035-rock-paper-scissors-simulator/).

## Sessions

### bad-input - interpreter: byte-equal to the golden

Input:

```text
0
x
1
rk
lizard

2
mirror
cycle
9
301
abc

2

5
```

Screen:

```text
== Rock paper scissors ==
Against a computer that learns how often you play each move, and what you play after each.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 0
Pick a number from 1 to 5.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> x
Pick a number from 1 to 5.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 1
your move (rock, paper, scissors, or r, p, s)> rk
Type rock, paper or scissors (or r, p, s); an empty answer stops.
your move (rock, paper, scissors, or r, p, s)> lizard
Type rock, paper or scissors (or r, p, s); an empty answer stops.
your move (rock, paper, scissors, or r, p, s)> 
Stopped after 0 rounds. You 0, computer 0, draws 0.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 2
strategy (cycle, rock, copy, beat-last or random)> mirror
Type cycle, rock, copy, beat-last or random.
strategy (cycle, rock, copy, beat-last or random)> cycle
rounds (10 to 300)> 9
Type a whole number from 10 to 300.
rounds (10 to 300)> 301
Type a whole number from 10 to 300.
rounds (10 to 300)> abc
Type a whole number from 10 to 300.
rounds (10 to 300)> 
Cancelled.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 2
strategy (cycle, rock, copy, beat-last or random)> 
Cancelled.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 5
Bye.
```

### basic - interpreter: byte-equal to the golden

Input:

```text
1
r
p
s
r
p
s
rock
paper
scissors

3
2
cycle
300
2
rock
30
2
random
300
2
beat-last
300
2
copy
300
4
3
5
```

Screen:

```text
== Rock paper scissors ==
Against a computer that learns how often you play each move, and what you play after each.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 1
your move (rock, paper, scissors, or r, p, s)> r
You rock, computer paper - it guessed rock at random, with nothing to go on. The computer wins.
You 0, computer 1, draws 0.
your move (rock, paper, scissors, or r, p, s)> p
You paper, computer paper - it guessed rock from what you play most. A draw.
You 0, computer 1, draws 1.
your move (rock, paper, scissors, or r, p, s)> s
You scissors, computer scissors - it guessed paper from what you play most. A draw.
You 0, computer 1, draws 2.
your move (rock, paper, scissors, or r, p, s)> r
You rock, computer rock - it guessed scissors from what you play most. A draw.
You 0, computer 1, draws 3.
your move (rock, paper, scissors, or r, p, s)> p
You paper, computer scissors - it guessed paper from what you played after rock. The computer wins.
You 0, computer 2, draws 3.
your move (rock, paper, scissors, or r, p, s)> s
You scissors, computer rock - it guessed scissors from what you played after paper. The computer wins.
You 0, computer 3, draws 3.
your move (rock, paper, scissors, or r, p, s)> rock
You rock, computer paper - it guessed rock from what you played after scissors. The computer wins.
You 0, computer 4, draws 3.
your move (rock, paper, scissors, or r, p, s)> paper
You paper, computer scissors - it guessed paper from what you played after rock. The computer wins.
You 0, computer 5, draws 3.
your move (rock, paper, scissors, or r, p, s)> scissors
You scissors, computer rock - it guessed scissors from what you played after paper. The computer wins.
You 0, computer 6, draws 3.
your move (rock, paper, scissors, or r, p, s)> 
Stopped after 9 rounds. You 0, computer 6, draws 3.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 3
After 9 rounds:
                    rock  paper  scissors
  you played           3      3         3
  after rock           0      3         0
  after paper          0      0         3
  after scissors       2      0         0
Next it guesses rock from what you played after scissors, so it will play paper.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 2
strategy (cycle, rock, copy, beat-last or random)> cycle
rounds (10 to 300)> 300
300 rounds of cycle against a fresh computer: strategy 0, computer 297, draws 3.
The computer won 99.0% of the rounds - against moves chosen at random it could expect a third.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 2
strategy (cycle, rock, copy, beat-last or random)> rock
rounds (10 to 300)> 30
30 rounds of rock against a fresh computer: strategy 0, computer 30, draws 0.
The computer won 100.0% of the rounds - against moves chosen at random it could expect a third.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 2
strategy (cycle, rock, copy, beat-last or random)> random
rounds (10 to 300)> 300
300 rounds of random against a fresh computer: strategy 112, computer 92, draws 96.
The computer won 30.7% of the rounds - against moves chosen at random it could expect a third.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 2
strategy (cycle, rock, copy, beat-last or random)> beat-last
rounds (10 to 300)> 300
300 rounds of beat-last against a fresh computer: strategy 135, computer 124, draws 41.
The computer won 41.3% of the rounds - against moves chosen at random it could expect a third.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 2
strategy (cycle, rock, copy, beat-last or random)> copy
rounds (10 to 300)> 300
300 rounds of copy against a fresh computer: strategy 31, computer 129, draws 140.
The computer won 43.0% of the rounds - against moves chosen at random it could expect a third.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 4
It has forgotten everything.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 3
Nothing yet: no rounds played.

1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit
choice> 5
Bye.
```

## Modules

### main.eml

```eml
# P080 rock paper scissors against a computer that learns: play it
# yourself, or let a fixed strategy play it for many rounds, and see what
# it has learned about you.
import rps

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 number(s):
    if len(s) == 0 or len(s) > 3:
        return -1
    0 => n
    for c in s:
        if not (c >= "0" and c <= "9"):
            return -1
        n * 10 + int(c) => n
    return n

def lpad(s, n):
    while len(s) < n:
        " " + s => s
    return s

def pad(s, n):
    while len(s) < n:
        s + " " => s
    return s

["rock", "paper", "scissors"] => NAMES

def move_of(s):
    if s == "rock" or s == "r":
        return 0
    if s == "paper" or s == "p":
        return 1
    if s == "scissors" or s == "s":
        return 2
    return -1

def percent(a, b):
    # a / b as a percentage to one decimal, rounded half up.
    2000 * a + b => n
    2 * b => d
    0 => q
    while n >= d:
        n - d => n
        q + 1 => q
    0 => whole
    q => r
    while r >= 10:
        r - 10 => r
        whole + 1 => whole
    return str(whole) + "." + str(r) + "%"

def score(mem):
    return "You " + str(mem[5]) + ", computer " + str(mem[6]) + ", draws " + str(mem[7]) + "."

def why(how, last):
    if how == "after":
        return "from what you played after " + NAMES[last]
    if how == "often":
        return "from what you play most"
    return "at random, with nothing to go on"

def play(mem):
    0 => k
    while True:
        trim(input("your move (rock, paper, scissors, or r, p, s)> ")) => s
        if s == "":
            ("Stopped after " + str(k) + " rounds. " + score(mem)) ^0
            return mem
        move_of(s) => you
        if you == -1:
            "Type rock, paper or scissors (or r, p, s); an empty answer stops." ^0
        else:
            mem[2] => last
            rps.play(mem, you) => r
            r[0] => mem
            k + 1 => k
            ["A draw.", "You win.", "The computer wins."] => said
            ("You " + NAMES[you] + ", computer " + NAMES[r[1]] + " - it guessed " + NAMES[r[2]] + " " + why(r[3], last) + ". " + said[r[4]]) ^0
            score(mem) ^0

def auto(mem):
    while True:
        trim(input("strategy (cycle, rock, copy, beat-last or random)> ")) => name
        if name == "":
            "Cancelled." ^0
            return mem
        if name == "cycle" or name == "rock" or name == "copy" or name == "beat-last" or name == "random":
            while True:
                trim(input("rounds (10 to 300)> ")) => a
                if a == "":
                    "Cancelled." ^0
                    return mem
                number(a) => n
                if n >= 10 and n <= 300:
                    rps.new_memory(2026) => m
                    -1 => last_cpu
                    99 => x
                    for k in [0:n - 1]:
                        rps.strategy_move(name, k, last_cpu, x) => sm
                        sm[1] => x
                        rps.play(m, sm[0]) => r
                        r[0] => m
                        r[1] => last_cpu
                    (str(n) + " rounds of " + name + " against a fresh computer: strategy " + str(m[5]) + ", computer " + str(m[6]) + ", draws " + str(m[7]) + ".") ^0
                    ("The computer won " + percent(m[6], n) + " of the rounds - against moves chosen at random it could expect a third.") ^0
                    return mem
                "Type a whole number from 10 to 300." ^0
        "Type cycle, rock, copy, beat-last or random." ^0

def learned(mem):
    if mem[4] == 0:
        "Nothing yet: no rounds played." ^0
        return
    ("After " + str(mem[4]) + " rounds:") ^0
    ("  " + pad("", 16) + lpad("rock", 6) + lpad("paper", 7) + lpad("scissors", 10)) ^0
    ("  " + pad("you played", 16) + lpad(str(mem[0][0]), 6) + lpad(str(mem[0][1]), 7) + lpad(str(mem[0][2]), 10)) ^0
    for m in [0:2]:
        mem[1][m] => row
        ("  " + pad("after " + NAMES[m], 16) + lpad(str(row[0]), 6) + lpad(str(row[1]), 7) + lpad(str(row[2]), 10)) ^0
    rps.guess(mem) => g
    ("Next it guesses " + NAMES[g[0]] + " " + why(g[1], mem[2]) + ", so it will play " + NAMES[rps.beats(g[0])] + ".") ^0

rps.new_memory(2026) => memory

"== Rock paper scissors ==" ^0
"Against a computer that learns how often you play each move, and what you play after each." ^0
True => running
while running:
    "" ^0
    "1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit" ^0
    trim(input("choice> ")) => choice
    if choice == "1":
        play(memory) => memory
    elif choice == "2":
        auto(memory) => memory
    elif choice == "3":
        learned(memory)
    elif choice == "4":
        rps.new_memory(2026) => memory
        "It has forgotten everything." ^0
    elif choice == "5":
        False => running
    else:
        "Pick a number from 1 to 5." ^0
"Bye." ^0
```

Python projection of main.eml:

```python
import rps

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 number(s):
    if len(s) == 0 or len(s) > 3:
        return -1
    n = 0
    for c in s:
        if not (c >= "0" and c <= "9"):
            return -1
        n = n * 10 + int(c)
    return n

def lpad(s, n):
    while len(s) < n:
        s = " " + s
    return s

def pad(s, n):
    while len(s) < n:
        s = s + " "
    return s

NAMES = ["rock", "paper", "scissors"]

def move_of(s):
    if s == "rock" or s == "r":
        return 0
    if s == "paper" or s == "p":
        return 1
    if s == "scissors" or s == "s":
        return 2
    return -1

def percent(a, b):
    n = 2000 * a + b
    d = 2 * b
    q = 0
    while n >= d:
        n = n - d
        q = q + 1
    whole = 0
    r = q
    while r >= 10:
        r = r - 10
        whole = whole + 1
    return str(whole) + "." + str(r) + "%"

def score(mem):
    return "You " + str(mem[5]) + ", computer " + str(mem[6]) + ", draws " + str(mem[7]) + "."

def why(how, last):
    if how == "after":
        return "from what you played after " + NAMES[last]
    if how == "often":
        return "from what you play most"
    return "at random, with nothing to go on"

def play(mem):
    k = 0
    while True:
        s = trim(input("your move (rock, paper, scissors, or r, p, s)> "))
        if s == "":
            print("Stopped after " + str(k) + " rounds. " + score(mem))
            return mem
        you = move_of(s)
        if you == -1:
            print("Type rock, paper or scissors (or r, p, s); an empty answer stops.")
        else:
            last = mem[2]
            r = rps.play(mem, you)
            mem = r[0]
            k = k + 1
            said = ["A draw.", "You win.", "The computer wins."]
            print("You " + NAMES[you] + ", computer " + NAMES[r[1]] + " - it guessed " + NAMES[r[2]] + " " + why(r[3], last) + ". " + said[r[4]])
            print(score(mem))

def auto(mem):
    while True:
        name = trim(input("strategy (cycle, rock, copy, beat-last or random)> "))
        if name == "":
            print("Cancelled.")
            return mem
        if name == "cycle" or name == "rock" or name == "copy" or name == "beat-last" or name == "random":
            while True:
                a = trim(input("rounds (10 to 300)> "))
                if a == "":
                    print("Cancelled.")
                    return mem
                n = number(a)
                if n >= 10 and n <= 300:
                    m = rps.new_memory(2026)
                    last_cpu = -1
                    x = 99
                    for k in range(0, n):
                        sm = rps.strategy_move(name, k, last_cpu, x)
                        x = sm[1]
                        r = rps.play(m, sm[0])
                        m = r[0]
                        last_cpu = r[1]
                    print(str(n) + " rounds of " + name + " against a fresh computer: strategy " + str(m[5]) + ", computer " + str(m[6]) + ", draws " + str(m[7]) + ".")
                    print("The computer won " + percent(m[6], n) + " of the rounds - against moves chosen at random it could expect a third.")
                    return mem
                print("Type a whole number from 10 to 300.")
        print("Type cycle, rock, copy, beat-last or random.")

def learned(mem):
    if mem[4] == 0:
        print("Nothing yet: no rounds played.")
        return
    print("After " + str(mem[4]) + " rounds:")
    print("  " + pad("", 16) + lpad("rock", 6) + lpad("paper", 7) + lpad("scissors", 10))
    print("  " + pad("you played", 16) + lpad(str(mem[0][0]), 6) + lpad(str(mem[0][1]), 7) + lpad(str(mem[0][2]), 10))
    for m in range(0, 3):
        row = mem[1][m]
        print("  " + pad("after " + NAMES[m], 16) + lpad(str(row[0]), 6) + lpad(str(row[1]), 7) + lpad(str(row[2]), 10))
    g = rps.guess(mem)
    print("Next it guesses " + NAMES[g[0]] + " " + why(g[1], mem[2]) + ", so it will play " + NAMES[rps.beats(g[0])] + ".")

memory = rps.new_memory(2026)
print("== Rock paper scissors ==")
print("Against a computer that learns how often you play each move, and what you play after each.")
running = True
while running:
    print("")
    print("1) play  2) let a strategy play  3) what it has learned  4) make it forget  5) quit")
    choice = trim(input("choice> "))
    if choice == "1":
        memory = play(memory)
    elif choice == "2":
        memory = auto(memory)
    elif choice == "3":
        learned(memory)
    elif choice == "4":
        memory = rps.new_memory(2026)
        print("It has forgotten everything.")
    elif choice == "5":
        running = False
    else:
        print("Pick a number from 1 to 5.")
print("Bye.")
```

### rps.eml

```eml
# P080 rock paper scissors - a computer that learns how you play. It counts
# how often you play each move, and what you play after each move; it
# guesses your next move from what you played last (or, with nothing to go
# on, from how often you play each), and plays what beats it. Moves are 0
# rock, 1 paper, 2 scissors; (m + 1) % 3 beats m.

def beats(m):
    return (m + 1) % 3

def result(you, cpu):
    # 0 a draw, 1 you win, 2 the computer wins.
    return (you - cpu + 3) % 3

def draw(x):
    # One step of a linear congruential generator (Numerical Recipes'
    # constants, mod 2^32): [next state, its top 16 bits]; x / 65536 is a
    # division by a power of two below 2^53, so it is exact.
    (1664525 * x + 1013904223) % 4294967296 => x
    return [x, int(x / 65536)]

def new_memory(seed):
    # [how often each move, after each move how often each next move, your
    # last move (-1 none), the random state, rounds, your wins, its wins,
    # draws, right guesses].
    return [[0, 0, 0], [[0, 0, 0], [0, 0, 0], [0, 0, 0]], -1, seed, 0, 0, 0, 0, 0]

def most(counts, x):
    # The move with the highest count; a tie is broken at random, so the
    # guess cannot be steered by the order of the moves. [move, state].
    0 => top
    for c in counts:
        if c > top:
            c => top
    [] => best
    for m in [0:2]:
        if counts[m] == top:
            best + [m] => best
    if len(best) == 1:
        return [best[0], x]
    draw(x) => d
    return [best[d[1] % len(best)], d[0]]

def guess(mem):
    # The guess and why: [move, "after" | "often" | "random", state].
    mem[3] => x
    if mem[2] >= 0:
        mem[1][mem[2]] => after
        if after[0] + after[1] + after[2] > 0:
            most(after, x) => g
            return [g[0], "after", g[1]]
    if mem[0][0] + mem[0][1] + mem[0][2] > 0:
        most(mem[0], x) => g
        return [g[0], "often", g[1]]
    draw(x) => d
    return [d[1] % 3, "random", d[0]]

def play(mem, you):
    # One round: the computer commits to its move before seeing yours,
    # then learns from yours. Returns [memory, its move, its guess, why,
    # result].
    guess(mem) => g
    beats(g[0]) => cpu
    result(you, cpu) => r
    [mem[0][0], mem[0][1], mem[0][2]] => often
    often[you] + 1 => often[you]
    [[mem[1][0][0], mem[1][0][1], mem[1][0][2]], [mem[1][1][0], mem[1][1][1], mem[1][1][2]], [mem[1][2][0], mem[1][2][1], mem[1][2][2]]] => after
    if mem[2] >= 0:
        after[mem[2]][you] + 1 => after[mem[2]][you]
    [often, after, you, g[2], mem[4] + 1, mem[5], mem[6], mem[7], mem[8]] => out
    if r == 1:
        out[5] + 1 => out[5]
    elif r == 2:
        out[6] + 1 => out[6]
    else:
        out[7] + 1 => out[7]
    if g[0] == you:
        out[8] + 1 => out[8]
    return [out, cpu, g[0], g[1], r]

def strategy_move(name, k, last_cpu, x):
    # What a fixed strategy plays in round k (from 0): [move, state].
    if name == "cycle":
        return [k % 3, x]
    if name == "rock":
        return [0, x]
    if name == "copy":
        if last_cpu == -1:
            return [0, x]
        return [last_cpu, x]
    if name == "beat-last":
        if last_cpu == -1:
            return [0, x]
        return [beats(last_cpu), x]
    draw(x) => d
    return [d[1] % 3, d[0]]
```

Python projection of rps.eml:

```python
def beats(m):
    return (m + 1) % 3

def result(you, cpu):
    return (you - cpu + 3) % 3

def draw(x):
    x = (1664525 * x + 1013904223) % 4294967296
    return [x, int(x / 65536)]

def new_memory(seed):
    return [[0, 0, 0], [[0, 0, 0], [0, 0, 0], [0, 0, 0]], -1, seed, 0, 0, 0, 0, 0]

def most(counts, x):
    top = 0
    for c in counts:
        if c > top:
            top = c
    best = []
    for m in range(0, 3):
        if counts[m] == top:
            best = best + [m]
    if len(best) == 1:
        return [best[0], x]
    d = draw(x)
    return [best[d[1] % len(best)], d[0]]

def guess(mem):
    x = mem[3]
    if mem[2] >= 0:
        after = mem[1][mem[2]]
        if after[0] + after[1] + after[2] > 0:
            g = most(after, x)
            return [g[0], "after", g[1]]
    if mem[0][0] + mem[0][1] + mem[0][2] > 0:
        g = most(mem[0], x)
        return [g[0], "often", g[1]]
    d = draw(x)
    return [d[1] % 3, "random", d[0]]

def play(mem, you):
    g = guess(mem)
    cpu = beats(g[0])
    r = result(you, cpu)
    often = [mem[0][0], mem[0][1], mem[0][2]]
    often[you] = often[you] + 1
    after = [[mem[1][0][0], mem[1][0][1], mem[1][0][2]], [mem[1][1][0], mem[1][1][1], mem[1][1][2]], [mem[1][2][0], mem[1][2][1], mem[1][2][2]]]
    if mem[2] >= 0:
        after[mem[2]][you] = after[mem[2]][you] + 1
    out = [often, after, you, g[2], mem[4] + 1, mem[5], mem[6], mem[7], mem[8]]
    if r == 1:
        out[5] = out[5] + 1
    elif r == 2:
        out[6] = out[6] + 1
    else:
        out[7] = out[7] + 1
    if g[0] == you:
        out[8] = out[8] + 1
    return [out, cpu, g[0], g[1], r]

def strategy_move(name, k, last_cpu, x):
    if name == "cycle":
        return [k % 3, x]
    if name == "rock":
        return [0, x]
    if name == "copy":
        if last_cpu == -1:
            return [0, x]
        return [last_cpu, x]
    if name == "beat-last":
        if last_cpu == -1:
            return [0, x]
        return [beats(last_cpu), x]
    d = draw(x)
    return [d[1] % 3, d[0]]
```

## README

# P080 - Rock paper scissors

Rock paper scissors against a computer that learns how you play. You can
play it yourself, or let a fixed strategy play it for up to 300 rounds,
and see what it has learned about you.

- `main.eml` - the menu, playing rounds, the strategies, and what it has
  learned
- `rps.eml` - the rules, the computer's memory and guess, one round, and
  the fixed strategies

How each part works:

- The rules, as in the corpus case `rock-paper-scissors-simulator`, here
  with moves as 0 rock, 1 paper, 2 scissors: (m + 1) % 3 beats m, and
  (you - computer + 3) % 3 says who won.
- The computer counts how often you play each move, and what you play
  after each move. To guess your next move it looks at what you have
  played after your last one; with nothing there, at what you play most;
  with nothing at all, it picks at random. Then it plays what beats the
  guess. It chooses before it sees your move. A tie between equally
  common moves is broken at random, from a linear congruential
  generator's top 16 bits, so the order of the moves cannot steer it.
- What it can and cannot learn: a cycle (rock, paper, scissors, ...) or
  always the same move, it learns within a few rounds and then wins
  almost every time. Against random moves it can do no better than a
  third. It only remembers your own last move, so a strategy built on
  ITS last move - playing what beats it, or copying it - is something it
  cannot see. Over 300 rounds it wins only 41% and 43% of those.

What is checked: menu choices 1 to 5; moves as rock, paper, scissors or
r, p, s, an empty answer stopping; a strategy of cycle, rock, copy,
beat-last or random; 10 to 300 rounds.

Sessions: `sessions/basic.in` gives:
- nine rounds of rock, paper, scissors over and over: the computer wins
  the first by luck, draws three while it learns, then wins every round;
- what it learned from them;
- 300 rounds each of cycle (99.0% to the computer), random (30.7%),
  beat-last (41.3%) and copy (43.0%), and 30 of rock (100%);
- forgetting, then nothing learned.

`sessions/bad-input.in` gives:
- menu choices 0 and x;
- the moves rk and lizard, then stopping after no rounds;
- the strategy mirror, then 9, 301 and abc rounds, and two cancels.

Built on the verified corpus case `rock-paper-scissors-simulator` (who
beats whom, and keeping score).
