Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
Skip to content
World desk5 min

Building 2048 in JavaScript: One Merge Function for Four Directions

Use one left-oriented line merge function, then transpose or reverse the board to support right, up, and down without duplicating merge rules.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Build 2048’s movement around one left-oriented line function, then reuse it for all four directions by transforming the board. This keeps the rules for compaction and merging in one place, makes the “a tile can merge only once per move” rule explicit, and gives you a small, testable game engine separate from the DOM.

Model the board and move rules

Classic 2048 uses a 4×4 board. Each cell contains a power-of-two tile or zero for an empty cell. A move slides tiles in one direction; equal neighboring tiles combine into their sum, and the resulting tile’s value is added to the score. A tile created by a merge cannot merge again during that same move.

As an Amazon Associate I earn from qualifying purchases.

The original implementation is available in Gabriele Cirulli’s 2048 source repository, which is MIT-licensed. Its move logic records whether a destination tile has already merged so it cannot absorb another tile in the same turn. The classic rules, including the 2048 win condition, are also described in Szubert and Jaśkowski’s 2014 paper on 2048.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Write one line merge function

Make the left move the canonical operation. First remove zeros so tiles are packed toward the left. Then scan from left to right: when two adjacent values match, replace them with their sum and skip both inputs. Finally, pad the line with zeros to restore its original length.

function slideAndMergeLine(line) {
  const values = line.filter(value => value !== 0);
  const result = [];
  let scoreGained = 0;

  for (let i = 0; i < values.length; i++) {
    if (values[i] === values[i + 1]) {
      const merged = values[i] * 2;
      result.push(merged);
      scoreGained += merged;
      i++; // The consumed pair cannot merge again this move.
    } else {
      result.push(values[i]);
    }
  }

  while (result.length < line.length) result.push(0);
  return { line: result, scoreGained };
}

This implementation assumes the standard game’s powers-of-two values and uses zero for empty cells. It returns both the transformed line and the score increase, leaving the board and UI untouched.

Check merge-once-per-move cases

  • [2, 2, 2, 2] becomes [4, 4, 0, 0], not [8, 0, 0, 0].
  • [2, 2, 4, 0] becomes [4, 4, 0, 0]. The newly formed 4 cannot merge with the adjacent 4 in the same move.
  • [0, 2, 0, 2] compacts to [2, 2, 0, 0] before merging, producing [4, 0, 0, 0].

Reuse the line function for four directions

Orient the board so the chosen direction is left, run the same line function on each row, then restore the original orientation. A compact implementation can use transpose and row reversal:

function transpose(board) {
  return board[0].map((_, column) => board.map(row => row[column]));
}

function reverseRows(board) {
  return board.map(row => [...row].reverse());
}

function orientForMove(board, direction) {
  switch (direction) {
    case "left": return board.map(row => [...row]);
    case "right": return reverseRows(board);
    case "up": return transpose(board);
    case "down": return reverseRows(transpose(board));
    default: throw new Error(`Unknown direction: ${direction}`);
  }
}

function restoreAfterMove(board, direction) {
  switch (direction) {
    case "left": return board;
    case "right": return reverseRows(board);
    case "up": return transpose(board);
    case "down": return transpose(reverseRows(board));
    default: throw new Error(`Unknown direction: ${direction}`);
  }
}

function move(board, direction) {
  const oriented = orientForMove(board, direction);
  let scoreGained = 0;

  const movedLines = oriented.map(row => {
    const result = slideAndMergeLine(row);
    scoreGained += result.scoreGained;
    return result.line;
  });

  const nextBoard = restoreAfterMove(movedLines, direction);
  const changed = nextBoard.some((row, r) =>
    row.some((value, c) => value !== board[r][c])
  );

  return { board: nextBoard, scoreGained, changed };
}

Here, “right” reverses each row before and after the left operation. “Up” transposes the board, making each original column a row. “Down” transposes and reverses those rows, then applies the inverse transforms. This is the same single-primitive pattern used in Zoltan Dul’s 2048-Game example repository.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The code expects a rectangular board and treats it as immutable: the input rows are not modified. For a fixed 4×4 game this is straightforward; if you support other board sizes, ensure every row has the same length.

Connect moves to score, spawning, and game state

Keep move calculation pure: it should return a new board, the score gained, and whether anything changed. The surrounding game controller can then decide whether to update the score, spawn a tile, and check for the end of the game.

  1. Call move(board, direction) and read its result.
  2. If changed is false, leave the board, score, and spawn state alone. A blocked move should not create a tile.
  3. If changed is true, add scoreGained to the score and spawn one tile in an empty cell.
  4. After the spawn, check whether the player has reached 2048 and whether any legal moves remain.

In the classic rules described by Szubert and Jaśkowski, a new tile is 2 with 90% probability or 4 with 10% probability. Keep the random-number source injectable into your spawn function so tests can select a known empty cell and tile value rather than depending on chance. These probabilities concern spawning, not merging.

The win condition is creating a 2048 tile. A board with no empty cells and no equal adjacent tiles has no legal move and is game over. For a general implementation, check equality only between horizontal and vertical neighbors; diagonal tiles cannot merge.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep game rules separate from rendering

The move function should not read buttons, update HTML, or inspect CSS classes. A small UI layer can translate a key press into a direction, call the game logic, then render the returned state. This separation makes merge behavior testable without a browser and avoids tying rules to a particular layout.

When you later add animation, render the transition from the prior board to the returned board, but do not let animation state determine whether tiles are allowed to merge. The board calculation remains the authority for game rules.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Test the shared primitive and transforms

Test the line operation independently, then test each direction against a board whose expected result is easy to inspect. Include cases where tiles move without merging, multiple pairs merge, a merge cannot chain, and a move changes nothing.

  • For the canonical left operation, verify the three edge cases above and confirm that score gained equals the sum of newly created tiles.
  • For right, up, and down, use mirrored or transposed versions of the same inputs and check that results match the corresponding transform of the left result.
  • Assert that a no-op move reports changed: false and does not cause the controller to spawn.
  • Use deterministic random values in spawn tests to check both a selected empty cell and the 2/4 choice.
  • Check that the original board is unchanged after every move if your engine is intended to be immutable.

Choose shared logic or explicit directional branches

Approach Rule-code duplication Testing and clarity Main risk
One line merge function plus transforms Low: merge behavior lives in one primitive. Test the primitive once, then verify the four orientation mappings. A mistaken inverse transform can put tiles in the wrong cells.
Separate logic for each direction Higher: movement and merging are repeated across branches. Each branch can read naturally at first, but all four must stay consistent. Direction-specific bugs or rule drift between branches.

The comparison is about code organization, not measured runtime performance. For this game, a canonical line operation is a useful default because merge rules change in one place; explicit branches can still be reasonable for a tiny prototype if you test all directions thoroughly.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.