Design a 2048 Game
AsanaSoftware Engineer · OnsiteLast updated
This question evaluates object-oriented design, state management, deterministic randomness handling, and algorithmic reasoning for implementing game rules on a configurable N x N grid.
Design the object model and core game logic for a 2048 game on an N x N grid. This is an object-oriented design problem: focus on the classes, their responsibilities, the relationships between them, and the algorithm for a move — not on the UI or rendering.
For reference, 2048 is a sliding-tile puzzle. The player moves all tiles in one of four directions; tiles slide as far as they can, and two adjacent tiles of equal value merge into one tile of their sum. After every move that changes the board, a new tile spawns in a random empty cell.
Constraints & Assumptions
- The grid is square,
N x N, withN = 4by default but configurable. - Tile values are powers of two; empty cells are represented by
0. - A new tile is a
2with probability $0.9$ and a4with probability $0.1$ (configurable). - The game starts with two random tiles already placed.
- The default win condition is reaching a
2048tile; the game may continue after winning. - The game logic must be deterministically testable — randomness has to be injectable so tests are reproducible.
- Assume a single local player; no networking or persistence is required (but the design should not preclude them).
The Problem
Produce a class model and the core logic that satisfies the following requirements:
- Initialization — start a board with two random tiles.
- Moves — support
UP,DOWN,LEFT, andRIGHT. - Slide & merge — implement the standard rules: tiles slide toward the move direction, and each tile may merge at most once per move.
- Scoring — increase the score by the value of each merged tile.
- Tile spawn — spawn a new tile only when a move actually changes the board.
- State detection — detect the win state and the game-over state.
- Separation of concerns — keep UI/presentation strictly out of the game logic.
Also discuss how you would support these extensions without rewriting the merge logic: undo, custom board sizes, deterministic tests, and configurable spawn probabilities.
Clarifying Questions to Ask
- Is the board always square, and is
Nfixed at 4 or configurable? - After reaching
2048, does the game end immediately or can the player keep playing for a higher tile? - What are the exact spawn probabilities for
2vs4, and must they be configurable? - Is undo a hard requirement, and if so, how many steps deep?
- Do we need persistence (save/resume) or networking, or is this a single local session?
- Should the score include only merge gains, or are there bonus/penalty rules?
Hints
Hint 1
Look for the repeated operation: Before writing four separate move handlers, ask whether `UP`/`DOWN`/`RIGHT` are genuinely different from `LEFT`, or whether they're the same operation viewed from a different angle. If you can reduce them to one case, what's the smallest unit of the board that operation acts on?
Hint 2
The merge rule has a trap: The "each tile merges at most once per move" rule is the easiest thing to get wrong. Pick a concrete line that exposes the ambiguity and decide its result *before* coding: what should `2 2 2` become when slid? Your answer here dictates exactly how your merge loop must advance.
Hint 3
Which pieces deserve to be pulled out?: Two parts of this system are the ones you'll most want to test in isolation and vary later: the rule that's easy to get wrong, and the part that's non-deterministic. Consider what the `Game` should depend on so that neither of those is hardwired into it.
Hint 4
Game over isn't "board full": Watch out: a completely full board can still have a legal move. Think about what actually has to be true for *every* direction to be blocked at once — that condition, not "no empty cells," is your game-over test.
Follow-up questions
- How would you implement **undo** with minimal memory? Compare snapshotting the whole board vs. recording a reversible move delta, including how merges and the spawned tile complicate a pure inverse.
- How would you detect game-over *efficiently* if `N` were large — can you maintain it incrementally instead of scanning the whole board after every move?
- How would you adapt this design to a multiplayer or server-authoritative version where the server must validate moves and prevent cheating?
- How would you generalize from "merge equal values into their sum" to a configurable rule (e.g., a different target tile, or Fibonacci-style merges) without touching the slide logic?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.