FRQ
› csapa
NumberCube: 2009 FRQ 1
A step-by-step solution to the 2009 AP CSA FRQ 1 (NumberCube), covering building an array from repeated method calls and finding the longest run of repeated values in Java.
Rolling a six-sided number cube over and over again, then hunting through the results for streaks of repeats, is the scenario behind this AP Computer Science A free-response question — one method builds up an array from repeated calls to a helper, and the other scans that array to find its longest run of matching values.
What This FRQ Tests
- AP CSA units: Unit 4 (Iteration) and Unit 6 (Array), with Unit 3 (Boolean Expressions and if Statements) showing up in the run-comparison logic
- Core skill: filling an array one element at a time from repeated calls to another object's method
- Secondary skill: scanning an array while keeping track of two running values at once — the current run in progress, and the best run seen so far
- Official category: "Methods and Control Structures" — always FRQ 1 on the AP CSA exam
The Setup
- The given
NumberCubeclass provides one method you call but never modify:int toss()— returns a single value between 1 and 6, inclusive, each time it's called
- You're asked to write two static methods (they aren't tied to a
NumberCubeinstance's own state, so they take whatever objects they need as parameters):int[] getCubeTosses(NumberCube cube, int numTosses)— returns an array ofnumTossesresults, in the order they were rolledint getLongestRun(int[] values)— returns the starting index of the longest run of two or more consecutive, equal values; returns-1if the array has no run at all
- A "run" means the same value appears in two or more consecutive array positions. If more than one run ties for longest, returning the starting index of either one is acceptable.
Part (a): Writing getCubeTosses
Step-by-Step Approach
- Create a new
intarray sized exactlynumTosses. - Loop from
0up to (but not including)numTosses. - On each iteration, call
cube.toss()once and store the result at the current index. - After the loop finishes, return the completed array.
The Code
public static int[] getCubeTosses(NumberCube cube, int numTosses)
{
int[] tosses = new int[numTosses];
for (int i = 0; i < numTosses; i++)
{
tosses[i] = cube.toss();
}
return tosses;
}
Why Each Piece Matters
new int[numTosses]— the array has to be created at its final size up front; Java arrays can't grow after they're created.- Calling
cube.toss()inside the loop, not before it — each call totoss()produces a fresh, independent result. Calling it once and reusing the value would just repeat the same numbernumTossestimes. tosses[i] = cube.toss()— the toss result is stored at the same index as the loop counter, so the array ends up in the exact order the tosses happened.
Common Mistakes to Avoid
- Sizing the array wrong, such as
new int[numTosses - 1]ornew int[numTosses + 1]— either one throws anArrayIndexOutOfBoundsExceptionor leaves an unused slot. - Calling
cube.toss()outside the loop and reusing the result — this collects the same valuenumTossestimes instead ofnumTossesindependent tosses. - Off-by-one on the loop condition —
i <= numTossesruns one iteration too many and throws an exception whenireachesnumTosses.
Part (b): Writing getLongestRun
The Rule, Broken Down
- Compare each value in the array to the one right before it.
- As long as consecutive values keep matching, the current run keeps growing.
- The moment a value doesn't match the one before it, the current run ends and a new one starts at that position.
- Whenever the current run's length beats the best run found so far, that run becomes the new best — and its starting index is what gets remembered.
- If no run of length 2 or more is ever found, the answer is
-1.
Step-by-Step Approach
- Track four things while scanning: the starting index of the current run, the length of the current run, the starting index of the best run found so far, and the length of the best run found so far.
- Start both run lengths at
1(a single value, by itself, is a "run" of length one — not yet a real run) and the best starting index at-1, since no qualifying run has been found yet. - Loop from index
1to the end of the array, comparing each value to the one before it. - If they match, extend the current run by one. If they don't, reset the current run to start fresh at this index.
- After updating the current run's length, check whether it's now strictly longer than the best run recorded so far — if so, save its length and starting index as the new best.
- After the loop ends, return the best starting index found (which is still
-1if nothing ever beat a length of1).
The Code
public static int getLongestRun(int[] values)
{
int bestStart = -1;
int bestLength = 1;
int currentStart = 0;
int currentLength = 1;
for (int i = 1; i < values.length; i++)
{
if (values[i] == values[i - 1])
{
currentLength++;
}
else
{
currentStart = i;
currentLength = 1;
}
if (currentLength > bestLength)
{
bestLength = currentLength;
bestStart = currentStart;
}
}
return bestStart;
}
Why Each Piece Matters
bestLengthstarts at1, not0. A run has to be at least length 2 to count at all, so starting the bar at1means a merely "tied" first match (length 2) is genuinely an improvement, triggering the very first update tobestStart.bestStartstarts at-1. If the array never contains any repeated adjacent values,bestStartis never overwritten, so the method correctly falls through to returning-1.- The
elsebranch resetscurrentStartto the current indexi, noti - 1. The new run — if one starts here — begins at the position that didn't match, which isi. - The comparison
currentLength > bestLengthuses strict>. This means when a later run ties an earlier run's length (as happens in this exact problem, at index 14), the earlier run's starting index is kept rather than being replaced — matching the rule that the method may return either tied index.
Tracing the Example
Using the array from the question:
| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Value | 1 | 5 | 5 | 4 | 3 | 1 | 2 | 2 | 2 | 2 | 6 | 1 | 3 | 3 | 5 | 5 | 5 | 5 |
Walking through the key moments:
- At
i = 2,values[2] == values[1](both5), so the current run (start1, length2) becomes the new best:bestStart = 1. - At
i = 8andi = 9, the run of2s (starting at index6) grows to length3, then length4— each time beating the previous best, sobestStartbecomes6andbestLengthbecomes4. - At
i = 15,16, and17, the run of5s (starting at index14) grows to length2,3, then4— but4only ties the existing best of4, so the strict>check never fires, andbestStartstays6. - Final result:
6— one of the two accepted answers (the question also allows14, since both runs tie at length 4).
Common Mistakes to Avoid
- Starting
bestLengthat0instead of1. This isn't fatal by itself, but it makes single, non-repeated values look like "runs of length 1 that need beating," which invites subtle bugs elsewhere in the comparison logic. - Using
>=instead of>when updating the best run. This would make the method always jump to the last tied run instead of correctly being allowed to keep the first one — technically still a legal answer either way here, but it's easy to introduce an actual bug this way if the reset logic isn't handled carefully alongside it. - Resetting
currentLengthwithout resettingcurrentStart. If only the length gets reset on a mismatch, the run's starting index silently drifts to the wrong position, so the returned index no longer points at the actual start of the longest run. - Forgetting the array could be entirely run-free. Because
bestStartstarts at-1and only ever gets overwritten by an actual, qualifying run, this case is already handled correctly — but it's worth deliberately checking with an array like[1, 2, 3, 4]to confirm the method still returns-1.
Key Takeaways
- Building an array from repeated calls to an external method is a simple, standard loop: call the method fresh on every iteration, and store each result at the matching index.
- "Find the longest streak in a sequence" is a two-variable tracking pattern: one pair of variables for the current run in progress, another pair for the best run confirmed so far.
- A strict
>(rather than>=) comparison when updating a "best so far" value is what lets ties resolve to the first occurrence — worth checking deliberately any time a problem says multiple answers are acceptable.