Search⌘ K
AI Features

Merging Tiles and Scoring

Explore the development of the merge_row function for 2048 to apply game-specific merge rules correctly. Learn to generate detailed specifications with AI help, implement and test the function in Python, and understand how merging combines with compressing rows to effect game moves.

By the end of this lesson, you will:

  • Generate detailed specs for merge_row(row: list[int]) -> tuple[list[int], int] with the help of AI.

  • Understand the exact 2048 merge rules (especially tricky cases like [2, 2, 2, 0]).

  • Generate code for the merge_row function, with the help of specs.

  • Test the AI-generated code.

  • See how compress_row and merge_row will combine to create a full left move.

Merged tiles in row when the left arrow key is pressed, score updated by 4
Merged tiles in row when the left arrow key is pressed, score updated by 4

If you look at the illustration closely, we can compress a row to the left, then merge any adjacent tiles that can be merged, and compress again to achieve the final row state.

Step 0: Starting point

We start from the end-of-lesson file from the previous lesson and highlight that merge_row is our target.

Python 3.10.4
import random
# ===== Working functions from previous lessons =====
def add_random_tile(board: list[list[int]]) -> None:
empty_positions = [
(r, c)
for r in range(4)
for c in range(4)
if board[r][c] == 0
]
if not empty_positions:
return
r, c = random.choice(empty_positions)
board[r][c] = 4 if random.random() < 0.1 else 2
def create_initial_board() -> list[list[int]]:
board = [[0] * 4 for _ in range(4)]
add_random_tile(board)
add_random_tile(board)
return board
def render_board(board: list[list[int]]) -> str:
size = len(board)
cell_width = 5
line = "+" + ("-" * cell_width + "+") * size
lines = [line]
for r in range(size):
row_text = "|"
for c in range(size):
val = board[r][c]
text = "" if val == 0 else str(val)
row_text += text.rjust(cell_width) + "|"
lines.append(row_text)
lines.append(line)
return "\n".join(lines)
def compress_row(row: list[int]) -> list[int]:
non_zero = [x for x in row if x != 0]
zeros_needed = len(row) - len(non_zero)
return non_zero + [0] * zeros_needed
# ===== Stubs for this lessons =====
def merge_row(row: list[int]) -> tuple[list[int], int]:
"""
Merge a *compressed* row (zeros at the right) from LEFT to RIGHT.
Rules:
- Only equal, adjacent tiles merge.
- After a merge, the new tile's value is added to the score.
- A tile can be part of at most ONE merge.
- The result row should still have length 4 and be "compressed" (no zeros in between).
"""
raise NotImplementedError
# ===== Stubs for later lessons =====
def move_board(board: list[list[int]], direction: str) -> tuple[list[list[int]], int, bool]:
raise NotImplementedError
def check_game_state(board: list[list[int]], target: int = 2048) -> str:
raise NotImplementedError
def main():
print("=== Compressing Actual Board Rows ===")
board = create_initial_board()
score = 0
print("Initial board:")
print(render_board(board))
# Compress each row on the actual board
for r in range(len(board)):
board[r] = compress_row(board[r])
print("\nBoard after compressing all rows:")
print(render_board(board))
# We'll add merge_row demos here later in the lesson.
if __name__ == "__main__":
main()

When you run the program, the board is initialized and rendered correctly. Assuming a left move is triggered, all tiles are ...