CompSci.rocks
FRQcsapa

SumOrSameGame: 2025 FRQ 4

A step-by-step solution to the 2025 AP CSA FRQ 4 (SumOrSameGame), covering filling a 2D array with random values and searching it for a matching pair to clear in Java.

A number-matching puzzle built on a 2D grid drives this AP Computer Science A free-response question — first filling the grid with random digits, then searching it for a second value that either matches or completes a pair summing to 10.

What This FRQ Tests

  • AP CSA units: Unit 8 (2D Array) and Unit 4 (Iteration)
  • Core skill: filling every cell of a 2D array with an independently random value in a fixed range
  • Secondary skill: searching a 2D array in a specific, rule-constrained order and stopping at the first valid match
  • Official category: "2D Array" — always FRQ 4 on the AP CSA exam

The Setup

  • SumOrSameGame holds:
    • private int[][] puzzle — a grid where each cell holds a value from 1 to 9, or 0 once cleared
  • You're asked to write:
    • The constructor SumOrSameGame(int numRows, int numCols) — builds puzzle and fills every cell with a random value from 1 to 9, inclusive
    • boolean clearPair(int row, int col) — finds another cell that can pair with (row, col) and clears both

Part (a): Writing the Constructor

The Rule, Broken Down

  1. Allocate puzzle with the given number of rows and columns.
  2. Every cell, independently, gets a random value from 1 to 9, each equally likely.

Step-by-Step Approach

  1. Create the array: puzzle = new int[numRows][numCols].
  2. Loop over every row, and inside that, every column.
  3. For each cell, generate a random whole number from 1 to 9 and store it.

The Code

public SumOrSameGame(int numRows, int numCols)
{
    puzzle = new int[numRows][numCols];

    for (int r = 0; r < numRows; r++)
    {
        for (int c = 0; c < numCols; c++)
        {
            puzzle[r][c] = (int) (Math.random() * 9) + 1;
        }
    }
}

Why Each Piece Matters

  • Math.random() * 9 — scales Math.random()'s [0.0, 1.0) range into [0.0, 9.0).
  • (int) (...) truncates that into a whole number from 0 to 8 — nine equally likely possibilities.
  • + 1 shifts that range up to 1 through 9, matching the required range exactly.
  • Nested loops, not one looppuzzle is two-dimensional, so every row needs its own pass across every column.

Common Mistakes to Avoid

  • Off-by-one on the random range, e.g. (int) (Math.random() * 9) without the + 1, which produces 0 through 8 instead of 1 through 9.
  • Reusing one random value for an entire row or column instead of generating a fresh one for every single cell — the rule requires each cell's value to be independently random.
  • Swapping numRows and numCols when allocating the array, which silently transposes the grid's dimensions.

Part (b): Writing clearPair(int row, int col)

The Rule, Broken Down

  1. Look for another cell — anywhere in row row or any row after it — whose value either equals the value at (row, col), or sums with it to exactly 10.
  2. If more than one such cell exists, any one of them can be used.
  3. If a match is found, clear both cells (set them to 0) and return true.
  4. If no match exists anywhere in the allowed search area, leave puzzle unchanged and return false.

Step-by-Step Approach

  1. Save the value at (row, col) before anything else changes.
  2. Loop through every row from row to the last row of puzzle.
  3. Within each of those rows, loop through every column.
  4. Skip the starting cell itself — it can't pair with itself.
  5. At every other cell in the search area, check whether its value equals the saved value, or sums with it to 10.
  6. The moment a match is found, clear both cells and return true immediately.
  7. If both loops finish with no match found, return false.

The Code

public boolean clearPair(int row, int col)
{
    int value = puzzle[row][col];

    for (int r = row; r < puzzle.length; r++)
    {
        for (int c = 0; c < puzzle[r].length; c++)
        {
            if (!(r == row && c == col) && (puzzle[r][c] == value || puzzle[r][c] + value == 10))
            {
                puzzle[row][col] = 0;
                puzzle[r][c] = 0;
                return true;
            }
        }
    }

    return false;
}

Why Each Piece Matters

  • The outer loop starts at r = row, not 0 — the rule only allows pairing with cells in row or a later row, never an earlier one.
  • The inner loop always runs c from 0, even when r == row — the rule places no column restriction at all, only a row one, so every column in the starting row is fair game (except the starting cell itself).
  • !(r == row && c == col) — explicitly excludes the starting cell from being considered its own match, since without this check a cell could "pair" with itself.
  • Already-cleared cells (value 0) never accidentally match0 can never equal a value from 1 to 9, and 0 plus that value can never reach 10 either, so no special check is needed to skip them.
  • Returning immediately on the first match found — since "any one of those identified array elements can be used," the first one encountered in this row-major, starting-row-first search order is always an acceptable answer.

Tracing the Example

Using one of the question's grids — 8 1 0 5 / 0 4 3 6 / 3 4 5 8 — and the call clearPair(1, 1) (value 4):

Cell checked Value Same row/later? Match?
(1, 0) 0 yes no (0 ≠ 4, 0 + 4 ≠ 10)
(1, 1) 4 (the starting cell — skipped)
(1, 2) 3 yes no (3 ≠ 4, 3 + 4 ≠ 10)
(1, 3) 6 yes yes — 6 + 4 = 10

The search stops at (1, 3), clearing both (1, 1) and (1, 3) and returning true — matching the question's expected result exactly, including its note that row 2's 4 "could also have been matched," since our row-major search simply happens to reach row 1's 6 first.

Common Mistakes to Avoid

  • Starting the outer loop at r = 0 instead of r = row. This would allow pairing with a cell in an earlier row, which the rule explicitly forbids.
  • Restricting the inner loop's starting column when r == row (e.g., starting at c = col + 1) — the rule places no column constraint at all, only a row one, so a valid partner can be anywhere in the starting row, including before col.
  • Forgetting to exclude the starting cell itself, which would let a cell "pair" with itself and immediately return true without ever really finding a second cell.
  • Continuing to search after a match is found instead of returning immediately — this doesn't break correctness (any valid match is acceptable), but it's unnecessary extra work once one has already been found.

Key Takeaways

  • "Equal chance of being assigned" for a bounded random range always follows the same pattern: scale Math.random() by the range's size, truncate, then shift by the range's starting value.
  • A search rule with a constraint on only one dimension (here, row) should only restrict the loop bound for that dimension — don't invent a matching restriction for the other dimension that the rule never asked for.
  • "Find any one match and act on it" only needs a single pass with an early return — there's no need to collect every possible match before picking one, when the rule says any of them will do.

Related FRQs