4-B: 2048

Learning Targets

  • I can create and initialize 2D arrays of primitives.

  • I can traverse 2D arrays using nested loops in both row-major and column-major order.

  • I can implement algorithms that operate on entire rows or columns.

  • I can manipulate array contents by shifting, merging, or transforming values.

From Pictures to Grids

In the PicLab project, you worked with 2D arrays of Pixel objects to manipulate images. Each pixel had a location (row, column) and properties like color. Now we're going to work with 2D arrays of primitives—specifically integers—to represent game boards, data tables, and other grid-based structures.

The concepts are similar:

  • PicLab: A 2D array where each cell holds a Pixel object

  • Grid games: A 2D array where each cell holds an integer representing game state

The difference? Instead of calling methods on Pixel objects, we'll be performing calculations and comparisons directly on integer values.

Understanding 2D Arrays

A 2D array is like a table with rows and columns. Think of a classroom seating chart, a chess board, or a spreadsheet—any time you have data organized in a grid, a 2D array is a natural fit.

Declaration and Initialization

Here's how you create a 2D array:

// Create a 4x4 grid of integers, all initialized to 0
int[][] grid = new int[4][4];

// You can also initialize with values
int[][] scores = {
    {90, 85, 88, 92},
    {78, 91, 84, 87},
    {95, 89, 93, 90}
};

Important terminology:

  • grid.length gives you the number of rows (3 in the scores example)

  • grid[0].length gives you the number of columns (4 in the scores example)

  • grid[row][col] accesses the element at that position

Visualizing 2D Arrays

Let's say you have this array:

To access the value 30, you'd use numbers[2][3].

Think of it this way: The first index is "which row?" and the second index is "which seat in that row?"

Traversing 2D Arrays

The most fundamental skill with 2D arrays is visiting every cell. You do this with nested loops.

Row-Major Traversal

This is the most common pattern—go through each row, and for each row, visit every column:

Pattern: Outer loop controls rows, inner loop controls columns.

Column-Major Traversal

Sometimes you need to process by columns instead. For example, finding the highest score in each subject:

Pattern: Outer loop controls columns, inner loop controls rows.

This is crucial: Notice how the indices swap. When traversing by column, scores[row][col] still uses row first, but your outer loop is the column counter.

Common 2D Array Patterns

Finding Values

Let's say you want to find if a specific value exists in the grid:

Or find the location of the maximum value:

Collecting Matching Cells

Sometimes you need to find all cells that meet certain criteria. For example, finding all empty cells in a game board:

Pattern: Use an ArrayList to accumulate results, storing coordinates as int[] arrays.

Operating on Rows vs Columns

Many grid-based algorithms need to process entire rows or columns as units. This is where things get interesting.

Processing a Single Row

Let's say you want to shift all non-zero values in a row to the left:

Example: [0, 3, 0, 5, 0, 2] becomes [3, 5, 2, 0, 0, 0]

Processing a Single Column

The same operation on a column requires careful index management:

Notice the difference: When working with rows, col changes but row is fixed. When working with columns, row changes but col is fixed.

Processing All Rows or Columns

Once you can process a single row or column, you can loop through all of them:

Advanced Pattern: Merging Adjacent Values

A common requirement in grid games is merging adjacent equal values. Here's how you might merge adjacent matching numbers in a row:

Example: [2, 2, 4, 4, 0, 0] becomes [4, 8, 0, 0, 0, 0]

This pattern of compress → process → compress is very common in grid manipulation algorithms.

Putting It All Together

When working with 2D arrays in games or simulations, you typically:

  1. Initialize the grid with starting values

  2. Implement row/column operations as helper methods

  3. Apply operations based on user input or game rules

  4. Check win/loss conditions by traversing the grid

  5. Update display by traversing and rendering

The key skills are:

  • Traversing correctly (row-major vs column-major)

  • Keeping indices straight (row first, then column)

  • Operating on rows vs columns (which index is fixed?)

  • Using helper methods to break complex operations into steps

2048 Project

You're now ready to build a fully functional 2048 game! In this project, you'll:

  • Manage a 4x4 grid of integers representing tiles

  • Implement sliding and merging logic for all four directions (up, down, left, right)

  • Add random tiles after each move

  • Check for win conditions (reaching 2048) and game over (no valid moves)

  • Handle complex array manipulation with multiple passes

The game combines everything you've learned about 2D arrays:

  • Creating and initializing grids

  • Traversing by rows and columns

  • Operating on entire rows or columns

  • Checking conditions across the board

  • Modifying arrays in place

You'll get a GitHub Classroom link to set up the project. The repository includes a complete game GUI, and your job is to implement the core game logic. The README provides detailed guidance on each method you need to write, along with the algorithms and patterns to use.

This is one of the most satisfying projects in the course—when your move methods work correctly and you see the tiles sliding and merging on screen, you'll have built a real, playable game using fundamental CS concepts!

Good luck! 🎮

Last updated

Was this helpful?