PracHub
QuestionsPremiumCoachesLearningGuidesInterview Prep

Quick Overview

This question evaluates array and matrix manipulation, stateful simulation of game mechanics, and handling edge cases such as merge constraints, single-merge-per-move rules, and empty-cell management.

  • medium
  • Glean
  • Coding & Algorithms
  • Software Engineer

Implement 2048 Game Logic

Company: Glean

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

Implement the core movement logic for the **2048** game. You are given an `n x n` integer board where `0` represents an empty cell and every nonzero value represents a tile. Implement a method such as `move(direction)` that updates the board **in place** after a single move in one of four directions: `up`, `down`, `left`, or `right`. The rules for one move are: - All tiles slide as far as possible in the chosen direction (no gaps remain between a tile and the wall or the tiles ahead of it). - Two adjacent tiles with the **same** value merge into one tile of double the value. - A tile may participate in **at most one merge** per move. - After sliding and merging, the remaining cells are filled with `0`. - You do **not** need to implement the UI or scoring. If random new-tile spawning is needed, keep it **separate** from the deterministic movement logic so the move can be unit-tested. Worked examples for a `left` move on a single row (the leading edge is the left wall): - `[2, 0, 2, 0] -> [4, 0, 0, 0]` - `[2, 2, 2, 0] -> [4, 2, 0, 0]` - `[2, 2, 2, 2] -> [4, 4, 0, 0]` - `[4, 4, 2, 2] -> [8, 4, 0, 0]` - `[2, 0, 0, 2] -> [4, 0, 0, 0]` ```hint Decompose the problem Notice that a move in any direction is the **same one-dimensional operation applied to each row or column**. Solve "collapse one line toward index 0" once, then reuse it for all four directions by transforming the board so the chosen direction becomes "left". ``` ```hint The 1-D collapse For a single line, do it in two passes to avoid double-merging: (1) drop all zeros so nonzero values are packed to the front, then (2) scan left to right and merge `line[i] == line[i+1]` into `2*line[i]`, advancing past both. A "skip the next index after a merge" flag is what enforces *at most one merge per tile per move*. ``` ```hint Reusing one line function for four directions `left` is the line function directly. `right` is the line function applied to each **reversed** row (then reverse back). `up`/`down` apply it to **columns** — transpose the board, run the row logic, transpose back (reverse rows too for `down`). This keeps merge correctness in exactly one place. ``` ### Constraints & Assumptions - The board is square, `n x n`, with `n` typically small (e.g. `4`), but your solution should work for any `n >= 1`. - Tile values are positive powers of two; `0` denotes empty. - The board can have up to `n*n` tiles; aim for `O(n^2)` time and `O(n)` or `O(1)` extra space per move. - `move` mutates the board in place (or you may return the new board — state your choice). Spawning a new random tile after a move is out of scope for the deterministic logic. ### Clarifying Questions to Ask - Should `move` mutate the board in place, or return a new board and leave the input untouched? - Does the interviewer want `move` to report whether the board actually changed (useful for deciding whether to spawn a new tile)? - Are tile values guaranteed to be powers of two, or can the board contain arbitrary integers (which would only affect merge equality, not the algorithm)? - Is `n` always `4`, or must the solution generalize to arbitrary `n`? - For the merge order on `left`, do tiles merge starting from the leading (wall) edge? (Standard 2048 merges from the direction you push toward, so `[2,2,2]` → `[4,2]`, not `[2,4]`.) ### What a Strong Answer Covers - **One reusable core operation.** Implements the 1-D "collapse toward the wall" once and derives all four directions from it (via reverse / transpose), rather than writing four near-duplicate, bug-prone routines. - **Correct merge semantics.** Two passes (compact, then merge) so that `[2,2,2,2] -> [4,4,...]` and `[2,2,2] -> [4,2]`, and never `[2,2,2,2] -> [8,...]`; each tile merges at most once. - **In-place vs. returned state, and "did it change?"** A clear, stated contract; ideally a boolean signalling whether the move altered the board. - **Separation of concerns.** Deterministic movement is isolated from random tile spawning, making the logic unit-testable. - **Complexity.** `O(n^2)` time, modest extra space, and an awareness that the transpose/reverse approach trades a little copying for far simpler, correct code. - **Edge cases.** Empty board, a full board with no merges, a single row/column, `n = 1`, and a line that is all zeros. ### Follow-up Questions - Implement `isGameOver()`: return `true` when no legal move remains. The board is over when there are **no empty cells** and **no two horizontally- or vertically-adjacent equal tiles**. What is its time complexity, and can you do it without simulating all four moves? - Add a `canMove(direction)` / `hasMoved` signal so the caller knows when to spawn a new tile. How would you implement it cheaply inside `move`? - How would you unit-test the movement logic exhaustively for `n = 4`? What property-based invariants hold (e.g. tile-value sum is conserved except where merges occur, number of nonzero tiles never increases)? - Generalize: suppose tiles could merge in chains of three equal values, or the board were non-square. Which parts of your design stay fixed and which change?

Quick Answer: This question evaluates array and matrix manipulation, stateful simulation of game mechanics, and handling edge cases such as merge constraints, single-merge-per-move rules, and empty-cell management.

Implement the core movement logic for the **2048** game as a pure function. You are given an `n x n` integer `board` where `0` represents an empty cell and every nonzero value represents a tile, plus a `direction` string that is one of `"up"`, `"down"`, `"left"`, or `"right"`. Return the board **after a single move** in that direction. (Modeling the move as a function that returns the new board keeps the deterministic logic isolated from random tile-spawning and makes it directly unit-testable; an in-place `move(direction)` mutating method is an equivalent contract.) The rules for one move are: - All tiles slide as far as possible in the chosen direction (no gaps remain between a tile and the wall or the tiles ahead of it). - Two adjacent tiles with the **same** value merge into one tile of double the value. - A tile may participate in **at most one merge** per move (so `[2, 2, 2, 2]` moving left becomes `[4, 4, 0, 0]`, never `[8, 0, 0, 0]`). - After sliding and merging, the remaining cells are filled with `0`. Worked examples for a `left` move on a single row (the leading edge is the left wall): - `[2, 0, 2, 0] -> [4, 0, 0, 0]` - `[2, 2, 2, 0] -> [4, 2, 0, 0]` - `[2, 2, 2, 2] -> [4, 4, 0, 0]` - `[4, 4, 2, 2] -> [8, 4, 0, 0]` - `[2, 0, 0, 2] -> [4, 0, 0, 0]` **Key idea.** A move in any direction is the *same one-dimensional collapse* applied to each row or column. Solve "collapse one line toward index 0" once, then map the four directions onto it: `left` is the line function directly; `right` reverses each row first; `up`/`down` transpose the board to turn columns into rows (and `down` also reverses).

Constraints

  • The board is square, n x n, and the solution must work for any n >= 1 (not just n = 4).
  • Tile values are positive powers of two; 0 denotes empty. The algorithm only relies on value equality for merges, so arbitrary positive integers also work.
  • direction is exactly one of "up", "down", "left", "right".
  • Each tile participates in at most one merge per move.
  • Target O(n^2) time and O(n) or O(1) extra space per move.

Examples

Input: ([[2, 0, 2, 0], [2, 2, 2, 0], [2, 2, 2, 2], [4, 4, 2, 2]], 'left')

Expected Output: [[4, 0, 0, 0], [4, 2, 0, 0], [4, 4, 0, 0], [8, 4, 0, 0]]

Explanation: The four worked single-row examples as rows of one board moved left: [2,0,2,0]->[4,0,0,0]; [2,2,2,0]->[4,2,0,0]; [2,2,2,2]->[4,4,0,0] (two separate merges, not one chain); [4,4,2,2]->[8,4,0,0].

Input: ([[2, 0, 0, 2], [0, 0, 0, 0], [2, 2, 0, 0], [4, 2, 2, 4]], 'right')

Expected Output: [[0, 0, 0, 4], [0, 0, 0, 0], [0, 0, 0, 4], [0, 4, 4, 4]]

Explanation: Right slides toward the right wall. [2,0,0,2]->[..,4]; an all-zero row stays zero; [2,2,..]->[..,4]; [4,2,2,4]: the inner 2,2 merge to 4 while the outer 4s stay separate, giving [0,4,4,4].

Input: ([[2, 0, 0, 0], [2, 4, 0, 0], [4, 4, 2, 0], [0, 0, 2, 8]], 'up')

Expected Output: [[4, 8, 4, 8], [4, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

Explanation: Up collapses each column toward the top. col0 [2,2,4,0]->[4,4,0,0]; col1 [0,4,4,0]->[8,0,0,0]; col2 [0,0,2,2]->[4,0,0,0]; col3 [0,0,0,8]->[8,0,0,0]. Reassembled by rows.

Input: ([[2, 0, 0, 0], [2, 4, 0, 0], [4, 4, 2, 0], [0, 0, 2, 8]], 'down')

Expected Output: [[0, 0, 0, 0], [0, 0, 0, 0], [4, 0, 0, 0], [4, 8, 4, 8]]

Explanation: Same board moved down: each column collapses toward the bottom, so the merged tiles settle in the last rows — the mirror of the 'up' result.

Input: ([[5]], 'left')

Expected Output: [[5]]

Explanation: Edge case n = 1: a single cell cannot slide or merge, so the board is unchanged regardless of direction (value need not be a power of two).

Input: ([[0, 0, 0, 0]], 'left')

Expected Output: [[0, 0, 0, 0]]

Explanation: All-zero line: compacting yields an empty list, padded back to all zeros — nothing moves.

Input: ([[2, 4, 8, 16], [4, 8, 16, 32]], 'left')

Expected Output: [[2, 4, 8, 16], [4, 8, 16, 32]]

Explanation: No two adjacent tiles are equal and there are no gaps, so a left move leaves the board untouched (also shows a non-square 2x4 grid works).

Input: ([[2, 2, 2, 2, 2]], 'left')

Expected Output: [[4, 4, 2, 0, 0]]

Explanation: Odd-length line of five 2s: merge the first pair (4), the next pair (4), and the lone trailing 2 stays — [4,4,2,0,0]. Confirms 'at most one merge per tile' on an odd count.

Hints

  1. Every move is the same 1-D collapse applied to each row or column. Write 'collapse one line toward index 0' once, then transform the board so the chosen direction becomes 'left'.
  2. Collapse a single line in two passes to avoid double-merging: (1) drop all zeros so nonzero values pack to the front, then (2) scan front-to-back merging line[i] == line[i+1] into 2*line[i] and skipping the next index. The skip is what enforces 'at most one merge per tile'.
  3. left = the line function directly; right = reverse each row, collapse, reverse back; up/down = transpose so columns become rows (down also reverses), collapse, then transpose back. This keeps all merge correctness in exactly one place.
Last updated: Jun 26, 2026

Loading coding console...

PracHub

Master your tech interviews with 8,000+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Implement a Byte Pair Encoding (BPE) Tokenizer - Glean (medium)
  • Return Top Department Suggestions - Glean (medium)
  • Implement Rate-Limited Wikipedia Crawler - Glean (medium)
  • Find Earliest Train Route - Glean (medium)
  • Search Words in a Character Grid - Glean (hard)