Project P048

2048

Slide the tiles of a 4 x 4 board - or 3 x 3 to 6 x 6 - up, down, left or right: two equal tiles that meet join into one with their sum, each tile once a move, and every move that changes the board brings a new 2 or 4. Reach the goal tile and keep going until no move is left; the same seed brings the same tiles.

3 modules · 2 recorded sessionstext-menu UI in the terminalupdated 2026-10-10

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

Slide every tile on the board up, down, left or right. Two equal tiles that meet join into one tile holding their sum, and the sum is added to your score. Every move that changes the board brings a new tile. Try to reach the goal tile - 2048, unless a new game sets another - and keep going until no move is left.

  • main.eml - the menu, the moves and their messages, and a new game with its size, goal and seed
  • board.eml - the board: sliding a row, turning the board for the other directions, the new tiles, and whether a move is left
  • rng.eml - random numbers written in EML, the generator of P008

How each part works:

  • A row slides left in two steps. First the tiles close up. Then two equal tiles side by side join, taken from the left, and a tile joins at most once a move: 2 2 2 2 becomes 4 4, not 8.
  • The other directions reuse the left slide on a turned board, and turn the result back. Right reverses each row; up transposes the board, as in the corpus case matrix-transpose-manual; down transposes it and reverses each row. So the tiles join from the side they move towards.
  • A move that changes nothing is not a move: no new tile comes, and the move count stays the same. Otherwise a new tile appears on an empty cell, every empty cell equally likely: a 2 nine times in ten, a 4 otherwise. The random numbers come from EML code, so a seed brings the same tiles on every machine.
  • A move is left while a cell is empty or two equal tiles touch, side by side or one above the other. When none is left the game is over, and its score is shown.

What is checked: moves as w, a, s and d, or as up, left, down and right; n for a new game, q to quit. A new game takes a size from 3 to 6, a goal that is a power of two from 8 to 65536, and a seed from 0 to 999999999. An empty answer cancels.

Sessions: sessions/basic.in makes eight moves on the default board (seed 2026). The second move lines up 2 2 2 2 along the top, and the third turns it into 4 4 for 8 points. A down move joins 2 2 near the top of a column whose bottom tiles do not join. Then a new 3 x 3 game with goal 32 (seed 318) runs to the end. A left move joins three rows at once, 4 + 8 + 32 points, and makes the goal on move 21. The board is full after 26 moves, with no two equal tiles touching, and the score is 152.

sessions/bad-input.in gives: - the moves x and an empty one; - moves written as up and left; - up when nothing can move up; - a new game that first gets sizes 2, 7 and x, goals 4, 12 (not a power of two) and 100000, and seed abc. It then runs a 3 x 3 game with goal 8 (seed 261) to the end: the goal is made on move 5, and the game is over after 18 moves with a score of 72; - a move after that; - a new game cancelled at each of its three questions.

Built on the verified corpus case matrix-transpose-manual (a list of lists transposed by hand, column by column).

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
== 2048 ==
Slide the tiles: two equal tiles that meet join into one. Every move brings a new tile.
A board of 4 x 4, goal 2048, seed 2026.
     .     .     .     .
     .     .     .     2
     2     .     .     .
     .     .     .     .
Score 0, moves 0, largest tile 2.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> x
Type w, a, s or d to move, n for a new game or q to quit.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> 
Type w, a, s or d to move, n for a new game or q to quit.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> up
     2     .     .     2
     .     .     2     .
     .     .     .     .
     .     .     .     .
Score 0, moves 1, largest tile 2.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> w
     2     2     2     2
     .     .     .     .
     .     .     .     .
     .     .     .     .
Score 0, moves 2, largest tile 2.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> left
     4     4     .     .
     .     2     .     .
     .     .     .     .
     .     .     .     .
Score 8, moves 3, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> w
Nothing moves up.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> n
size (3 to 6)> 2
Type a number from 3 to 6.
size (3 to 6)> 7
Type a number from 3 to 6.
size (3 to 6)> x
Type a number from 3 to 6.
size (3 to 6)> 3
goal (8 to 65536)> 4
Type a number from 8 to 65536.
goal (8 to 65536)> 12
The goal is a tile, so a power of two: 8, 16, 32 and so on.
goal (8 to 65536)> 100000
Type a number from 8 to 65536.
goal (8 to 65536)> 8
seed (0 to 999999999)> abc
Type a number from 0 to 999999999.
seed (0 to 999999999)> 261
A board of 3 x 3, goal 8, seed 261.
     .     .     .
     .     .     2
     .     2     .
Score 0, moves 0, largest tile 2.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     .     .     2
     .     .     .
     .     2     2
Score 0, moves 1, largest tile 2.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     .     .     .
     4     .     .
     .     2     4
Score 4, moves 2, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     .     .     2
     .     .     .
     4     2     4
Score 4, moves 3, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     4     .     .
     .     .     2
     4     2     4
Score 4, moves 4, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     .     .     .
     .     4     2
     8     2     4
Score 12, moves 5, largest tile 8.
You made 8 in 5 moves - keep going if you like.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> a
     2     .     .
     4     2     .
     8     2     4
Score 12, moves 6, largest tile 8.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     .     .
     4     4     .
     8     4     4
Score 16, moves 7, largest tile 8.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     2     .
     4     .     .
     8     8     4
Score 24, moves 8, largest tile 8.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     2     .
     4     2     .
     8     8     4
Score 24, moves 9, largest tile 8.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     .     2
     4     4     .
     8     8     4
Score 28, moves 10, largest tile 8.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     .     4
     4     4     2
     8     8     4
Score 28, moves 11, largest tile 8.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> a
     2     4     .
     8     2     2
    16     4     .
Score 52, moves 12, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     4     .
     8     2     2
    16     4     2
Score 52, moves 13, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     4     2
     8     2     .
    16     4     4
Score 56, moves 14, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     4     2
     8     2     2
    16     4     4
Score 56, moves 15, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     4     2
     8     2     4
    16     4     4
Score 60, moves 16, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     4     2
     8     2     2
    16     4     8
Score 68, moves 17, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     4     2
     8     2     4
    16     4     8
Score 72, moves 18, largest tile 16.
No move is left. Game over: score 72, largest tile 16, 18 moves.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> a
No move is left - start a new game.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> n
size (3 to 6)> 
Cancelled.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> n
size (3 to 6)> 3
goal (8 to 65536)> 
Cancelled.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> n
size (3 to 6)> 3
goal (8 to 65536)> 16
seed (0 to 999999999)> 
Cancelled.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> q
Bye.
What was typed (46 lines)
x

up
w
left
w
n
2
7
x
3
4
12
100000
8
abc
261
s
s
s
s
s
a
s
s
s
s
s
a
s
s
s
s
s
s
a
n

n
3

n
3
16

q

basic

interpreter: byte-equal
== 2048 ==
Slide the tiles: two equal tiles that meet join into one. Every move brings a new tile.
A board of 4 x 4, goal 2048, seed 2026.
     .     .     .     .
     .     .     .     2
     2     .     .     .
     .     .     .     .
Score 0, moves 0, largest tile 2.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> w
     2     .     .     2
     .     .     2     .
     .     .     .     .
     .     .     .     .
Score 0, moves 1, largest tile 2.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> w
     2     2     2     2
     .     .     .     .
     .     .     .     .
     .     .     .     .
Score 0, moves 2, largest tile 2.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> a
     4     4     .     .
     .     2     .     .
     .     .     .     .
     .     .     .     .
Score 8, moves 3, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     .     .     .     .
     .     2     .     .
     .     4     .     .
     4     2     .     .
Score 8, moves 4, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> d
     2     .     .     .
     .     .     .     2
     .     .     .     4
     .     .     4     2
Score 8, moves 5, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     .     .     .     2
     .     .     .     2
     .     .     .     4
     2     .     4     2
Score 8, moves 6, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> a
     2     2     .     .
     2     .     .     .
     4     .     .     .
     2     4     2     .
Score 8, moves 7, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     .     .     2     .
     4     .     .     .
     4     2     .     .
     2     4     2     .
Score 12, moves 8, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> n
size (3 to 6)> 3
goal (8 to 65536)> 32
seed (0 to 999999999)> 318
A board of 3 x 3, goal 32, seed 318.
     .     .     .
     .     4     2
     .     .     .
Score 0, moves 0, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     .     .     .
     .     .     .
     2     4     2
Score 0, moves 1, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> w
     2     4     2
     .     .     .
     .     2     .
Score 0, moves 2, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     .     2     .
     .     4     .
     2     2     2
Score 0, moves 3, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> a
     2     .     .
     4     .     .
     4     2     2
Score 4, moves 4, largest tile 4.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     .     .
     2     .     .
     8     2     2
Score 12, moves 5, largest tile 8.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     .     .
     4     .     .
     8     2     2
Score 16, moves 6, largest tile 8.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> a
     2     2     .
     4     .     .
     8     4     .
Score 20, moves 7, largest tile 8.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     2     .
     4     2     .
     8     4     .
Score 20, moves 8, largest tile 8.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     .     2
     4     4     .
     8     4     .
Score 24, moves 9, largest tile 8.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     .     2
     4     .     .
     8     8     2
Score 32, moves 10, largest tile 8.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     .     .
     4     .     2
     8     8     4
Score 36, moves 11, largest tile 8.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> a
     2     2     .
     4     2     .
    16     4     .
Score 52, moves 12, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     .     .
     4     4     .
    16     4     2
Score 56, moves 13, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     .     4
     4     .     .
    16     8     2
Score 64, moves 14, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     2     .
     4     .     4
    16     8     2
Score 64, moves 15, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     2     .
     4     2     4
    16     8     2
Score 64, moves 16, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     .     2
     4     4     4
    16     8     2
Score 68, moves 17, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> a
     4     2     .
     8     4     .
    16     8     2
Score 80, moves 18, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> d
     2     4     2
     .     8     4
    16     8     2
Score 80, moves 19, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     .     2     2
     2     4     4
    16    16     2
Score 96, moves 20, largest tile 16.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> a
     4     .     .
     2     8     2
    32     2     .
Score 140, moves 21, largest tile 32.
You made 32 in 21 moves - keep going if you like.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     4     4     .
     2     8     .
    32     2     2
Score 140, moves 22, largest tile 32.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> a
     8     2     .
     2     8     .
    32     4     .
Score 152, moves 23, largest tile 32.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> d
     2     8     2
     .     2     8
     .    32     4
Score 152, moves 24, largest tile 32.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     4     8     2
     .     2     8
     2    32     4
Score 152, moves 25, largest tile 32.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> s
     2     8     2
     4     2     8
     2    32     4
Score 152, moves 26, largest tile 32.
No move is left. Game over: score 152, largest tile 32, 26 moves.

w) up  a) left  s) down  d) right  n) new game  q) quit
move> q
Bye.
What was typed (39 lines)
w
w
a
s
d
s
a
s
n
3
32
318
s
w
s
a
s
s
a
s
s
s
s
a
s
s
s
s
s
a
d
s
a
s
a
d
s
s
q

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
# P048 2048: slide the tiles; two equal tiles that meet join into one with
# their sum, and every move brings a new tile. Reach the goal tile - 2048
# unless a new game sets another - and keep going until no move is left.
import board
import rng

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 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 show(game):
    for line in board.drawn(game[0]):
        line ^0
    ("Score " + str(game[2]) + ", moves " + str(game[3]) + ", largest tile " + str(board.largest(game[0])) + ".") ^0

def start(size, goal, seed):
    ("A board of " + str(size) + " x " + str(size) + ", goal " + str(goal) + ", seed " + str(seed) + ".") ^0
    # [board, the generator, score, moves, goal, goal reached, over]
    [board.empty_board(size), rng.Rng(seed), 0, 0, goal, False, False] => game
    board.add_tile(game[0], game[1])
    board.add_tile(game[0], game[1])
    show(game)
    return game

def play(game, direction):
    if game[6]:
        "No move is left - start a new game." ^0
        return game
    board.moved(game[0], direction) => res
    if res[0] == game[0]:
        ("Nothing moves " + direction + ".") ^0
        return game
    res[0] => game[0]
    game[2] + res[1] => game[2]
    game[3] + 1 => game[3]
    board.add_tile(game[0], game[1])
    show(game)
    if not game[5] and board.largest(game[0]) >= game[4]:
        True => game[5]
        ("You made " + str(game[4]) + " in " + str(game[3]) + " moves - keep going if you like.") ^0
    if not board.can_move(game[0]):
        True => game[6]
        ("No move is left. Game over: score " + str(game[2]) + ", largest tile " + str(board.largest(game[0])) + ", " + str(game[3]) + " moves.") ^0
    return game

def ask_number(prompt, low, high):
    while True:
        trim(input(prompt + " (" + str(low) + " to " + str(high) + ")> ")) => answer
        if answer == "":
            return -1
        number(answer) => n
        if n >= low and n <= high:
            return n
        ("Type a number from " + str(low) + " to " + str(high) + ".") ^0

def power_of_two(n):
    while n > 1 and n % 2 == 0:
        int(n / 2) => n
    return n == 1

def new_game(game):
    ask_number("size", 3, 6) => size
    if size == -1:
        "Cancelled." ^0
        return game
    -1 => goal
    while goal == -1:
        ask_number("goal", 8, 65536) => goal
        if goal == -1:
            "Cancelled." ^0
            return game
        if not power_of_two(goal):
            "The goal is a tile, so a power of two: 8, 16, 32 and so on." ^0
            -1 => goal
    ask_number("seed", 0, 999999999) => seed
    if seed == -1:
        "Cancelled." ^0
        return game
    return start(size, goal, seed)

"== 2048 ==" ^0
"Slide the tiles: two equal tiles that meet join into one. Every move brings a new tile." ^0
start(4, 2048, 2026) => game
True => running
while running:
    "" ^0
    "w) up  a) left  s) down  d) right  n) new game  q) quit" ^0
    trim(input("move> ")) => choice
    if choice == "w" or choice == "up":
        play(game, "up") => game
    elif choice == "a" or choice == "left":
        play(game, "left") => game
    elif choice == "s" or choice == "down":
        play(game, "down") => game
    elif choice == "d" or choice == "right":
        play(game, "right") => game
    elif choice == "n":
        new_game(game) => game
    elif choice == "q":
        False => running
    else:
        "Type w, a, s or d to move, n for a new game or q to quit." ^0
"Bye." ^0
Python projection (main.py)
import board
import rng

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 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 show(game):
    for line in board.drawn(game[0]):
        print(line)
    print("Score " + str(game[2]) + ", moves " + str(game[3]) + ", largest tile " + str(board.largest(game[0])) + ".")

def start(size, goal, seed):
    print("A board of " + str(size) + " x " + str(size) + ", goal " + str(goal) + ", seed " + str(seed) + ".")
    game = [board.empty_board(size), rng.Rng(seed), 0, 0, goal, False, False]
    board.add_tile(game[0], game[1])
    board.add_tile(game[0], game[1])
    show(game)
    return game

def play(game, direction):
    if game[6]:
        print("No move is left - start a new game.")
        return game
    res = board.moved(game[0], direction)
    if res[0] == game[0]:
        print("Nothing moves " + direction + ".")
        return game
    game[0] = res[0]
    game[2] = game[2] + res[1]
    game[3] = game[3] + 1
    board.add_tile(game[0], game[1])
    show(game)
    if not game[5] and board.largest(game[0]) >= game[4]:
        game[5] = True
        print("You made " + str(game[4]) + " in " + str(game[3]) + " moves - keep going if you like.")
    if not board.can_move(game[0]):
        game[6] = True
        print("No move is left. Game over: score " + str(game[2]) + ", largest tile " + str(board.largest(game[0])) + ", " + str(game[3]) + " moves.")
    return game

def ask_number(prompt, low, high):
    while True:
        answer = trim(input(prompt + " (" + str(low) + " to " + str(high) + ")> "))
        if answer == "":
            return -1
        n = number(answer)
        if n >= low and n <= high:
            return n
        print("Type a number from " + str(low) + " to " + str(high) + ".")

def power_of_two(n):
    while n > 1 and n % 2 == 0:
        n = int(n / 2)
    return n == 1

def new_game(game):
    size = ask_number("size", 3, 6)
    if size == -1:
        print("Cancelled.")
        return game
    goal = -1
    while goal == -1:
        goal = ask_number("goal", 8, 65536)
        if goal == -1:
            print("Cancelled.")
            return game
        if not power_of_two(goal):
            print("The goal is a tile, so a power of two: 8, 16, 32 and so on.")
            goal = -1
    seed = ask_number("seed", 0, 999999999)
    if seed == -1:
        print("Cancelled.")
        return game
    return start(size, goal, seed)

print("== 2048 ==")
print("Slide the tiles: two equal tiles that meet join into one. Every move brings a new tile.")
game = start(4, 2048, 2026)
running = True
while running:
    print("")
    print("w) up  a) left  s) down  d) right  n) new game  q) quit")
    choice = trim(input("move> "))
    if choice == "w" or choice == "up":
        game = play(game, "up")
    elif choice == "a" or choice == "left":
        game = play(game, "left")
    elif choice == "s" or choice == "down":
        game = play(game, "down")
    elif choice == "d" or choice == "right":
        game = play(game, "right")
    elif choice == "n":
        game = new_game(game)
    elif choice == "q":
        running = False
    else:
        print("Type w, a, s or d to move, n for a new game or q to quit.")
print("Bye.")

board.eml

eml
# P048 2048 - the board: a list of rows of numbers, 0 for an empty cell.

def empty_board(size):
    [] => b
    for r in [1:size]:
        b + [[0] * size] => b
    return b

def slid_left(row):
    # One row pushed to the left. The tiles close up, then two equal tiles
    # side by side join into one, taken from the left, and a tile joins at
    # most once a move: 2 2 2 2 gives 4 4, not 8. Returns [the row, the
    # points gained - the value of every joined tile].
    [] => tiles
    for x in row:
        if x != 0:
            tiles + [x] => tiles
    [] => out
    0 => gained
    0 => i
    while i < len(tiles):
        if i + 1 < len(tiles) and tiles[i] == tiles[i + 1]:
            out + [tiles[i] * 2] => out
            gained + tiles[i] * 2 => gained
            i + 2 => i
        else:
            out + [tiles[i]] => out
            i + 1 => i
    while len(out) < len(row):
        out + [0] => out
    return [out, gained]

def reversed_rows(b):
    [] => out
    for row in b:
        [] => r
        for x in row:
            [x] + r => r
        out + [r] => out
    return out

def transposed(b):
    # The corpus case matrix-transpose-manual: row j of the result is column
    # j of the board.
    [] => out
    for j in [0:len(b[0]) - 1]:
        [] => new_row
        for i in [0:len(b) - 1]:
            new_row + [b[i][j]] => new_row
        out + [new_row] => out
    return out

def moved(b, direction):
    # Every move is a slide to the left on a turned board, turned back: right
    # reverses each row, up transposes the board, down transposes it and
    # reverses each row. Returns [the new board, the points gained].
    b => t
    if direction == "right":
        reversed_rows(b) => t
    elif direction == "up":
        transposed(b) => t
    elif direction == "down":
        reversed_rows(transposed(b)) => t
    [] => slid
    0 => gained
    for row in t:
        slid_left(row) => res
        slid + [res[0]] => slid
        gained + res[1] => gained
    slid => out
    if direction == "right":
        reversed_rows(slid) => out
    elif direction == "up":
        transposed(slid) => out
    elif direction == "down":
        transposed(reversed_rows(slid)) => out
    return [out, gained]

def empty_cells(b):
    [] => out
    for r in [0:len(b) - 1]:
        for c in [0:len(b) - 1]:
            if b[r][c] == 0:
                out + [[r, c]] => out
    return out

def add_tile(b, rng):
    # A new tile on an empty cell, every empty cell equally likely: a 2 nine
    # times in ten, otherwise a 4. Returns [row, column, value], or [] when
    # no cell is empty.
    empty_cells(b) => cells
    if len(cells) == 0:
        return []
    cells[rng.below(len(cells))] => cell
    2 => value
    if rng.below(10) == 0:
        4 => value
    value => b[cell[0]][cell[1]]
    return [cell[0], cell[1], value]

def can_move(b):
    # Some move changes the board while a cell is empty or two equal tiles
    # touch, side by side or one above the other. (A board in play always
    # holds a tile; on an empty one nothing would move.)
    len(b) => n
    for r in [0:n - 1]:
        for c in [0:n - 1]:
            if b[r][c] == 0:
                return True
            if c + 1 < n and b[r][c] == b[r][c + 1]:
                return True
            if r + 1 < n and b[r][c] == b[r + 1][c]:
                return True
    return False

def largest(b):
    0 => top
    for row in b:
        for x in row:
            if x > top:
                x => top
    return top

def drawn(b):
    # Each cell five characters wide, a dot for an empty one.
    [] => lines
    for row in b:
        "" => line
        for x in row:
            "." => s
            if x != 0:
                str(x) => s
            while len(s) < 5:
                " " + s => s
            line + " " + s => line
        lines + [line] => lines
    return lines
Python projection (board.py)
def empty_board(size):
    b = []
    for r in range(1, size+1):
        b = b + [[0] * size]
    return b

def slid_left(row):
    tiles = []
    for x in row:
        if x != 0:
            tiles = tiles + [x]
    out = []
    gained = 0
    i = 0
    while i < len(tiles):
        if i + 1 < len(tiles) and tiles[i] == tiles[i + 1]:
            out = out + [tiles[i] * 2]
            gained = gained + tiles[i] * 2
            i = i + 2
        else:
            out = out + [tiles[i]]
            i = i + 1
    while len(out) < len(row):
        out = out + [0]
    return [out, gained]

def reversed_rows(b):
    out = []
    for row in b:
        r = []
        for x in row:
            r = [x] + r
        out = out + [r]
    return out

def transposed(b):
    out = []
    for j in range(0, len(b[0])):
        new_row = []
        for i in range(0, len(b)):
            new_row = new_row + [b[i][j]]
        out = out + [new_row]
    return out

def moved(b, direction):
    t = b
    if direction == "right":
        t = reversed_rows(b)
    elif direction == "up":
        t = transposed(b)
    elif direction == "down":
        t = reversed_rows(transposed(b))
    slid = []
    gained = 0
    for row in t:
        res = slid_left(row)
        slid = slid + [res[0]]
        gained = gained + res[1]
    out = slid
    if direction == "right":
        out = reversed_rows(slid)
    elif direction == "up":
        out = transposed(slid)
    elif direction == "down":
        out = transposed(reversed_rows(slid))
    return [out, gained]

def empty_cells(b):
    out = []
    for r in range(0, len(b)):
        for c in range(0, len(b)):
            if b[r][c] == 0:
                out = out + [[r, c]]
    return out

def add_tile(b, rng):
    cells = empty_cells(b)
    if len(cells) == 0:
        return []
    cell = cells[rng.below(len(cells))]
    value = 2
    if rng.below(10) == 0:
        value = 4
    b[cell[0]][cell[1]] = value
    return [cell[0], cell[1], value]

def can_move(b):
    n = len(b)
    for r in range(0, n):
        for c in range(0, n):
            if b[r][c] == 0:
                return True
            if c + 1 < n and b[r][c] == b[r][c + 1]:
                return True
            if r + 1 < n and b[r][c] == b[r + 1][c]:
                return True
    return False

def largest(b):
    top = 0
    for row in b:
        for x in row:
            if x > top:
                top = x
    return top

def drawn(b):
    lines = []
    for row in b:
        line = ""
        for x in row:
            s = "."
            if x != 0:
                s = str(x)
            while len(s) < 5:
                s = " " + s
            line = line + " " + s
        lines = lines + [line]
    return lines

rng.eml

eml
# P048 2048 - 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 tiles 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
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

Built on these corpus cases