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
SumOrSameGameholds:private int[][] puzzle— a grid where each cell holds a value from1to9, or0once cleared
- You're asked to write:
- The constructor
SumOrSameGame(int numRows, int numCols)— buildspuzzleand fills every cell with a random value from1to9, inclusive boolean clearPair(int row, int col)— finds another cell that can pair with(row, col)and clears both
- The constructor
Part (a): Writing the Constructor
The Rule, Broken Down
- Allocate
puzzlewith the given number of rows and columns. - Every cell, independently, gets a random value from
1to9, each equally likely.
Step-by-Step Approach
- Create the array:
puzzle = new int[numRows][numCols]. - Loop over every row, and inside that, every column.
- For each cell, generate a random whole number from
1to9and 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— scalesMath.random()'s[0.0, 1.0)range into[0.0, 9.0).(int) (...)truncates that into a whole number from0to8— nine equally likely possibilities.+ 1shifts that range up to1through9, matching the required range exactly.- Nested loops, not one loop —
puzzleis 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 produces0through8instead of1through9. - 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
numRowsandnumColswhen allocating the array, which silently transposes the grid's dimensions.
Part (b): Writing clearPair(int row, int col)
The Rule, Broken Down
- Look for another cell — anywhere in row
rowor any row after it — whose value either equals the value at(row, col), or sums with it to exactly10. - If more than one such cell exists, any one of them can be used.
- If a match is found, clear both cells (set them to
0) and returntrue. - If no match exists anywhere in the allowed search area, leave
puzzleunchanged and returnfalse.
Step-by-Step Approach
- Save the value at
(row, col)before anything else changes. - Loop through every row from
rowto the last row ofpuzzle. - Within each of those rows, loop through every column.
- Skip the starting cell itself — it can't pair with itself.
- At every other cell in the search area, check whether its value equals the saved value, or sums with it to
10. - The moment a match is found, clear both cells and return
trueimmediately. - 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, not0— the rule only allows pairing with cells inrowor a later row, never an earlier one. - The inner loop always runs
cfrom0, even whenr == 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 match —0can never equal a value from1to9, and0plus that value can never reach10either, 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 = 0instead ofr = 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 atc = 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 beforecol. - Forgetting to exclude the starting cell itself, which would let a cell "pair" with itself and immediately return
truewithout 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.