Win/Lose Detection
Explore how to implement and integrate win and lose detection for the 2048 game by creating Python functions to check board states. Learn to identify if a player has won, lost, or the game should continue by analyzing tiles and possible moves. This lesson teaches managing game logic separately and using AI to assist in function creation and testing, culminating in a fully playable CLI game that ends correctly upon win or loss.
By the end of this lesson, you will:
Implement
check_game_state(board, target=2048) -> str.Detect a win: at least one
tile ≥ target.Detect a loss: no empty cells and no possible merges horizontally or vertically.
Keep game state logic separate from the main loop and move logic.
Integrate
check_game_stateintomain()so the game actually ends.Use AI to:
Clarify the rules
Implement helpers like
has_wonandhas_any_movesGenerate test boards for each state
We start from the codebase at the end of the last lesson (interactive moves are working, but there is no win/lose functionality yet).
Here’s the end goal of this lesson:
Step 0: Starter code
import random
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
def merge_row(row: list[int]) -> tuple[list[int], int]:
result = []
score = 0
i = 0
n = len(row)
while i < n:
if row[i] == 0:
break
if i + 1 < n and row[i] == row[i + 1] and row[i] != 0:
merged = 2 * row[i]
result.append(merged)
score += merged
i += 2
else:
result.append(row[i])
i += 1
while len(result) < n:
result.append(0)
return result, score
def transpose(board: list[list[int]]) -> list[list[int]]:
size = len(board)
return [[board[r][c] for r in range(size)] for c in range(size)]
def reverse_rows(board: list[list[int]]) -> list[list[int]]:
return [list(reversed(row)) for row in board]
def move_left(board: list[list[int]]) -> tuple[list[list[int]], int, bool]:
new_board = []
total_score = 0
for row in board:
c1 = compress_row(row)
m, gained = merge_row(c1)
c2 = compress_row(m)
new_board.append(c2)
total_score += gained
moved = new_board != board
return new_board, total_score, moved
def move_board(board: list[list[int]], direction: str) -> tuple[list[list[int]], int, bool]:
direction = direction.lower()
if direction == "left":
return move_left(board)
if direction == "right":
reversed_board = reverse_rows(board)
moved_board, score, moved = move_left(reversed_board)
return reverse_rows(moved_board), score, moved
if direction == "up":
transposed = transpose(board)
moved_board, score, moved = move_left(transposed)
return transpose(moved_board), score, moved
if direction == "down":
transposed = transpose(board)
reversed_board = reverse_rows(transposed)
moved_board, score, moved = move_left(reversed_board)
final_board = transpose(reverse_rows(moved_board))
return final_board, score, moved
return board, 0, False
# ===== Lesson 7 target function =====
def check_game_state(board: list[list[int]], target: int = 2048) -> str:
"""
Return 'ongoing', 'won', or 'lost' depending on the board state.
We'll implement this in this lesson.
"""
# TODO
raise NotImplementedError
def main():
print("=== Lesson 7: Game Over Logic (check_game_state) ===")
board = create_initial_board()
score = 0
while True:
print("\nScore:", score)
print(render_board(board))
command = input("Move (w/a/s/d, q to quit): ").strip().lower()
if command == "q":
print("Goodbye!")
break
key_to_direction = {
"w": "up",
"a": "left",
"s": "down",
"d": "right",
}
if command not in key_to_direction:
print("Invalid input. Use w/a/s/d or q.")
continue
direction = key_to_direction[command]
new_board, gained, moved = move_board(board, direction)
if not moved:
print("Board did not change. Try a different move.")
continue
board = new_board
score += gained
add_random_tile(board)
# We'll plug check_game_state here later.
if __name__ == "__main__":
main()