Skip to the content.

CSA FRQ 2024 Question 2

CSA FRQ 2024 Question 2

Proof of Correct FRQ Solution (i.e Coderunner Working & verification against expected solution)

Step Expected Output Actual Output Match
2 (Step 2) info = 0-0-Red (Step 2) info = 0-0-Red Yes
4 (Step 4) info = 1-0-Red (Step 4) info = 1-0-Red Yes
6 (Step 6) info = 1-0-Blue (Step 6) info = 1-0-Blue Yes
7 (Step 7) info = 1-0-Blue (Step 7) info = 1-0-Blue Yes
9 (Step 9) info = 1-3-Blue (Step 9) info = 1-3-Blue Yes
12 (Step 12) info = 1-4-Red (Step 12) info = 1-4-Red Yes
16 (Step 16) info = 1-8-Red (Step 16) info = 1-8-Red Yes
18 (Step 18) match info = 0-0-Lions (Step 18) match info = 0-0-Lions Yes
19 (Step 19) game info = 1-8-Red (Step 19) game info = 1-8-Red Yes

FRQ Solution

class Scoreboard {
    private String team1;
    private String team2;
    private int score1;
    private int score2;
    private boolean team1Active;

    public Scoreboard(String team1Name, String team2Name) {
        team1 = team1Name;
        team2 = team2Name;
        score1 = 0;
        score2 = 0;
        team1Active = true;
    }

    public void recordPlay(int points) {
        if (points > 0) {
            if (team1Active) {
                score1 += points;
            } else {
                score2 += points;
            }
        } else {
            team1Active = !team1Active;
        }
    }

    public String getScore() {
        if (team1Active) {
            return score1 + "-" + score2 + "-" + team1;
        } else {
            return score1 + "-" + score2 + "-" + team2;
        }
    }
}

// DO NOT MODIFY BELOW THIS LINE
public class Main {
    public static void main(String[] args) {
        String info;

        Scoreboard game = new Scoreboard("Red", "Blue");

        info = game.getScore();
        System.out.println("(Step 2) info = " + info);

        game.recordPlay(1);

        info = game.getScore();
        System.out.println("(Step 4) info = " + info);

        game.recordPlay(0);

        info = game.getScore();
        System.out.println("(Step 6) info = " + info);

        info = game.getScore();
        System.out.println("(Step 7) info = " + info);

        game.recordPlay(3);

        info = game.getScore();
        System.out.println("(Step 9) info = " + info);

        game.recordPlay(1);
        game.recordPlay(0);

        info = game.getScore();
        System.out.println("(Step 12) info = " + info);

        game.recordPlay(0);
        game.recordPlay(4);
        game.recordPlay(0);

        info = game.getScore();
        System.out.println("(Step 16) info = " + info);

        Scoreboard match = new Scoreboard("Lions", "Tigers");

        info = match.getScore();
        System.out.println("(Step 18) match info = " + info);

        info = game.getScore();
        System.out.println("(Step 19) game info = " + info);
    }
}

Problem Overview

This problem involves designing a Scoreboard class to track the progress of a two-team game in which teams alternate turns. During a turn, a team may score points and remain active, or fail a play and cause the turn to switch to the other team. The class must correctly manage team names, scores, and the currently active team, while providing a formatted string representation of the game state through the getScore method.

Code Explanation & Design Choices

Code Explanation

The Scoreboard class keeps track of two teams, their scores, and which team is currently active. Team names are stored as strings, scores are stored as integers, and a boolean variable is used to represent whether team 1 is active.

private String team1;
private String team2;
private int score1;
private int score2;
private boolean team1Active;

The constructor initializes the team names, sets both scores to zero, and ensures the game always starts with team 1 as the active team.

public Scoreboard(String team1Name, String team2Name) {
    team1 = team1Name;
    team2 = team2Name;
    score1 = 0;
    score2 = 0;
    team1Active = true;
}

The recordPlay method updates the game state based on the number of points passed in. If the value is greater than zero, the active team’s score is increased and the team remains active. If the value is zero, the play fails and the active team switches.

public void recordPlay(int points) {
    if (points > 0) {
        if (team1Active) {
            score1 += points;
        } else {
            score2 += points;
        }
    } else {
        team1Active = !team1Active;
    }
}

The getScore method returns a formatted string showing both scores followed by the name of the currently active team. This method does not modify any data, ensuring that calling it multiple times does not change the game state.

public String getScore() {
    if (team1Active) {
        return score1 + "-" + score2 + "-" + team1;
    } else {
        return score1 + "-" + score2 + "-" + team2;
    }
}

Design Choices

A boolean variable was chosen to track the active team because the game only involves two teams, making this approach both simple and efficient. Toggling the active team using !team1Active allows the turn to switch in constant time without additional condition checks or extra variables.

Scores are stored as separate integer variables for each team, which makes updates straightforward and easy to read. This design avoids the need for arrays or collections, keeping the solution aligned with the problem’s simplicity and AP CSA expectations.

All methods run in constant time, O(1), since each operation involves only basic assignments and conditional checks. The solution also uses a constant amount of memory, O(1) space, because no data structures grow with input size.

The getScore method is intentionally read-only and does not modify any instance variables. This prevents unintended side effects and ensures consistent output even when the method is called multiple times in a row.

Summary:

  • Correctly tracks team names, scores, and the active team throughout the game
  • Ensures turns switch only when a play fails (points = 0)
  • Produces output in the exact format specified by the problem
  • Runs in constant time and constant space for all operations
  • Allows multiple Scoreboard objects to function independently

Pros (What I did well)

Strengths of My Solution

Correct Handling of Game Logic

The logic inside recordPlay accurately models the rules of the game. When the method is called with a positive number of points, the score of the currently active team is updated and the active team remains unchanged. When the method is called with a value of zero, no score is added and control switches to the other team. This clean separation ensures that scoring and turn-switching happen under the correct conditions and never interfere with one another.

The structure of the conditionals reflects the problem constraints well, since there are only two meaningful cases to handle: scoring or failing a play. This keeps the logic direct and easy to verify.

Reliable Output Through getScore

The getScore method correctly reports the current state of the game without modifying it. By assembling the score string using the stored scores and the currently active team’s name, the method consistently produces output in the required format. Repeated calls to getScore return identical results unless the game state has changed, which confirms that state updates are confined to recordPlay.

Proper Initialization and State Tracking

The constructor initializes all instance variables explicitly and correctly. Both team scores start at zero, and the active team is set to team 1 to satisfy the requirement that the game always begins with team 1’s turn. This prevents undefined behavior early in the game and guarantees a predictable starting state.

Throughout the game, state changes occur in a controlled way. Scores only change when points are scored, and the active team only changes when a play fails.

Object-Oriented Design and Independence

Each Scoreboard instance maintains its own data, which allows multiple games to exist simultaneously without affecting one another. This is demonstrated by creating two different objects and showing that updates to one do not alter the other. This reflects correct use of instance variables and reinforces a fundamental object-oriented principle.


Code Quality Observations

Readability and Structure

The code is easy to follow because each method has a single, clear purpose. The control flow is straightforward, and the logic does not rely on nested or overly complex conditionals. This makes the code easier to debug and reason about, especially in a timed assessment setting.

Meaningful Naming

Variable and method names clearly describe their roles in the program. Someone reading the code can understand how the game works simply by reading the names of the fields and methods, without needing additional explanation or comments.

Efficiency Without Overengineering

The solution avoids unnecessary data structures, helper methods, or abstractions. All operations run in constant time and constant space, which is appropriate for the problem and keeps the implementation efficient. This simplicity is a strength, not a limitation, given the scope of the task.


What I Handled Well

  • Ensured that scoring and turn-switching logic are handled in exactly one place (recordPlay), reducing the risk of inconsistent state changes
  • Maintained a clear distinction between methods that modify state and methods that only report state
  • Correctly handled multiple consecutive plays by the same team without forcing unnecessary turn switches
  • Verified that repeated calls to getScore do not change the game state
  • Confirmed that multiple Scoreboard objects function independently and correctly

Cons (what to improve)

One possible improvement would be making the design more flexible if the game rules were to change. For example, the current implementation is limited to exactly two teams, so supporting additional teams or different turn rules would require restructuring how the active team is tracked.

The recordPlay method also assumes that all inputs are valid. While this is acceptable given the problem constraints, adding input validation (such as guarding against negative point values) could make the solution more robust in a real-world setting.

Another area for growth is separating game logic from output formatting. Currently, getScore both retrieves game data and formats it as a string. While this is efficient for the problem, separating these concerns could improve extensibility if the scoreboard needed to support multiple output formats.

Finally, while the code is intentionally minimal, adding brief comments or documentation could help future readers understand the reasoning behind certain design choices, especially if the code were maintained or extended later.

CSA Concepts

Key CSA Concepts Demonstrated (2026 Framework)

  • Class Design and Encapsulation
    The solution demonstrates how to design a class that bundles related data and behavior together. Team names, scores, and the active team are stored as private instance variables, ensuring that the game state can only be modified through the class’s methods.

  • Object Construction and Initialization
    The constructor is used to establish a valid starting state for each Scoreboard object. All instance variables are explicitly initialized, which prevents undefined behavior and guarantees that every game begins with team 1 as the active team.

  • Instance Variables and Persistent State
    Instance variables are used to maintain game state across multiple method calls. Scores and turn information persist between calls to recordPlay and getScore, illustrating how objects remember information over time.

  • Control Flow and Conditional Logic
    The program uses conditional statements to determine whether to update a score or switch the active team. These decisions are driven by method parameters, showing how input values influence program behavior.

  • Method Design and Side Effects
    The solution distinguishes between methods that modify state (recordPlay) and methods that only return information (getScore). This separation reinforces good method design and helps prevent unintended side effects.

  • String Construction and Output Formatting
    The getScore method constructs a formatted string using concatenation to match an exact specification. This demonstrates precise string manipulation and attention to output requirements.

  • Multiple Object Instances
    Creating more than one Scoreboard object shows that each instance maintains its own independent state. This reinforces the concept that objects are separate entities, even when they are created from the same class.

  • Algorithmic Efficiency (Introductory Analysis)
    All operations in the class execute in constant time and use constant space. This aligns with AP CSA expectations for recognizing efficiency without requiring advanced data structures or algorithms.

Summary & Reflection

This project demonstrates how object-oriented design, clear method responsibilities, and simple control flow can be combined to model real-world rules accurately in code. Implementing the Scoreboard class reinforced the importance of managing state across method calls and separating logic that modifies data from logic that reports it. Overall, this problem helped solidify core AP CSA concepts while emphasizing clean, readable, and efficient program design.