Sprint View

2023 FRQ 4

13 min read

FRQ Question

This question involves pieces of candy in a box. The Candy class represents a single piece of candy.

public class Candy { /** Returns a String representing the flavor of this piece of candy / public String getFlavor() { / implementation not shown */ }

// There may be instance variables, constructors, and methods that are not shown. }

The BoxOfCandy class represents a candy box where the candy is arranged in a rectangular grid. The instance variable of the class, box, is a rectangular two-dimensional array of Candy objects. A location in the candy box may contain a piece of candy or may be empty. A piece of candy is represented by a Candy object. An empty location is represented by null. You will write two methods of the BoxOfCandy class.

public class BoxOfCandy { /** box contains at least one row and is initialized in the constructor. */ private Candy[][] box;

/**
 * Moves one piece of candy in column col, if necessary and possible, so that the box
 * element in row 0 of column col contains a piece of candy, as described in part (a).
 * Returns false if there is no piece of candy in column col and returns true otherwise.
 * Precondition: col is a valid column index in box.
 */
public boolean moveCandyToFirstRow(int col)
{ /* to be implemented in part (a) */ }

/**
 * Removes from box and returns a piece of candy with flavor specified by the parameter, or
 * returns null if no such piece is found, as described in part (b)
 */
public Candy removeNextByFlavor(String flavor)
{ /* to be implemented in part (b) */ }

// There may be instance variables, constructors, and methods that are not shown. }

FRQ Question Part A

Write the moveCandyToFirstRow method, which attempts to ensure that the box element at row 0 and column col contains a piece of candy, using the following steps.

• If the element at row 0 and column col already contains a piece of candy, then box is unchanged and the method returns true.

• If the element at row 0 and column col does not contain a piece of candy, then the method searches the remaining rows of column col for a piece of candy.

If a piece of candy can be found in column col, it is moved to row 0, its previous location is set to null, and the method returns true; otherwise, the method returns false. In the following example, the grid represents the contents of box. An empty square in the grid is null in box. A non-empty square in the grid represents a box element that contains a Candy object. The string in the square of the grid indicates the flavor of the piece of candy

The method call moveCandyToFirstRow(0) returns false because the box element at row 0 and column 0 does not contain a piece of candy and there are no pieces of candy in column 0 that can be moved to row 0. The contents of box are unchanged. The method call moveCandyToFirstRow(1) returns true because the box element at row 0 and column 1 already contains a piece of candy. The contents of box are unchanged.

The method call moveCandyToFirstRow(2) moves one of the two pieces of candy in column 2 to row 0 of column 2, sets the previous location of the piece of candy that was moved to null, and returns true. The new contents of box could be either of the following.

  0 1 2
0   “lime” “cherry”
1   “orange”  
2      
3   “lemon” “grape”

or

  0 1 2
0   “lime” “grape”
1   “orange”  
2     “cherry”
3   “lemon”  

Complete the moveCandyToFirstRow method.

/**

  • Moves one piece of candy in column col, if necessary and possible, so that the box
  • element in row 0 of column col contains a piece of candy, as described in part (a).
  • Returns false if there is no piece of candy in column col and returns true otherwise.
  • Precondition: col is a valid column index in box. */ public boolean moveCandyToFirstRow(int col)

// CODE_RUNNER: <challenge text>

Code Runner Challenge

FRQ #4 (a) - BoxOfCandy

View IPYNB Source
// CODE_RUNNER: FRQ #4 (a) - BoxOfCandy

public class Main {
    
    // Inner Candy class
    static class Candy {
        private String flavor;
        
        public Candy(String flavor) {
            this.flavor = flavor;
        }
        
        public String getFlavor() {
            return flavor;
        }
    }
    
    // Inner BoxOfCandy class
    static class BoxOfCandy {
        private Candy[][] box;
        
        public BoxOfCandy(Candy[][] box) {
            this.box = box;
        }
        
        public boolean moveCandyToFirstRow(int col) {
            if (box[0][col] != null) {
                return true;
            }
            for (int row = 1; row < box.length; row++) {
                if (box[row][col] != null) {
                    box[0][col] = box[row][col];
                    box[row][col] = null;
                    return true;
                }
            }
            return false;
        }
        
        // Helper method to print the box
        public void printBox() {
            System.out.println("Current box contents:");
            for (int row = 0; row < box.length; row++) {
                System.out.print("Row " + row + ": ");
                for (int col = 0; col < box[row].length; col++) {
                    if (box[row][col] == null) {
                        System.out.print("[empty]\t\t");
                    } else {
                        System.out.print("[" + box[row][col].getFlavor() + "]\t");
                    }
                }
                System.out.println();
            }
            System.out.println();
        }
    }
    
    // Main method with test cases
    public static void main(String[] args) {
        // Create the initial box setup from the FRQ question
        Candy[][] candyBox = new Candy[4][3];
        candyBox[0][1] = new Candy("lime");
        candyBox[1][1] = new Candy("orange");
        candyBox[2][2] = new Candy("cherry");
        candyBox[3][1] = new Candy("lemon");
        candyBox[3][2] = new Candy("grape");
        
        BoxOfCandy box = new BoxOfCandy(candyBox);
        
        System.out.println("===== INITIAL BOX SETUP =====");
        box.printBox();
        
        // Test case 1: moveCandyToFirstRow(0)
        System.out.println("===== TEST CASE 1: moveCandyToFirstRow(0) =====");
        System.out.println("Expected: returns false (no candy in column 0)");
        boolean result1 = box.moveCandyToFirstRow(0);
        System.out.println("Result: " + result1);
        box.printBox();
        
        // Test case 2: moveCandyToFirstRow(1)
        System.out.println("===== TEST CASE 2: moveCandyToFirstRow(1) =====");
        System.out.println("Expected: returns true (already has candy at row 0)");
        boolean result2 = box.moveCandyToFirstRow(1);
        System.out.println("Result: " + result2);
        box.printBox();
        
        // Test case 3: moveCandyToFirstRow(2)
        System.out.println("===== TEST CASE 3: moveCandyToFirstRow(2) =====");
        System.out.println("Expected: returns true (moves candy from row 2 or 3 to row 0)");
        boolean result3 = box.moveCandyToFirstRow(2);
        System.out.println("Result: " + result3);
        box.printBox();
    }
}

// Call the main method to execute
Main.main(null);
Lines: 1 Characters: 0
Output
Click "Run" in code control panel to see output ...

Scoring Guidelines Part A

1. Accesses all necessary elements of column col of box (no bounds errors) — 1 point

Decision Rules:

Responses can still earn the point even if they:

  • return early, as long as the loop bounds are appropriate

Responses will not earn the point if they:

  • fail to access an element of box in the loop
  • access the elements of box incorrectly

2. Compares candy box element at row 0 and column col to null — 1 point

Decision Rules:

Responses can still earn the point even if they:

  • make the comparison inside the loop or at some incorrect point in the code

Responses will not earn the point if they:

  • fail to use != or equivalent

3. Identifies and moves appropriate Candy object to first row if necessary (algorithm) — 1 point

Decision Rules:

Responses can still earn the point even if they:

  • return early, as long as the identify-and-move are inside a loop and would work if the loop got that far

Responses will not earn the point if they:

  • fail to replace the moved Candy object with null
  • move or swap objects when the first row is already occupied

4. Returns true when non-empty square is identified and false if non-empty square is not identified in the context of a loop (algorithm) — 1 point

Decision Rules:

Responses can still earn the point even if they:

  • fail to replace the moved Candy object with null
  • incorrectly identify a non-empty square

Responses will not earn the point if they:

  • return early

Total for part (a): 4 points

// CODE_RUNNER: <challenge text>

FRQ Question Part B

(b) Write the removeNextByFlavor method, which attempts to remove and return one piece of candy from the box. The piece of candy to be removed is the first piece of candy with a flavor equal to the parameter flavor that is encountered while traversing the candy box in the following order: the last row of the box is traversed from left to right, then the next-to-last row of the box is traversed from left to right, etc., until either a piece of candy with the desired flavor is found or until the entire candy box has been searched.

If the removeNextByFlavor method finds a Candy object with the desired flavor, the corresponding box element is assigned null, all other box elements are unchanged, and the removed Candy object is returned. Otherwise, box is unchanged and the method returns null.

The following examples show three consecutive calls to the removeNextByFlavor method. The traversal of the candy box always begins in the last row and first column of the box.

The following grid shows the contents of box before any of the removeNextByFlavor method calls.

  0 1 2 3 4
0 “lime” “lime”   “lemon”  
1 “orange”     “lime” “lime”
2 “cherry”   “lemon”   “orange”

The method call removeNextByFlavor("cherry") removes and returns the Candy object located in row 2 and column 0. The following grid shows the updated contents of box.

  0 1 2 3 4
0 “lime” “lime”   “lemon”  
1 “orange”     “lime” “lime”
2     “lemon”   “orange”

The method call removeNextByFlavor("lime") removes and returns the Candy object located in row 1 and column 3. The following grid shows the updated contents of box.

  0 1 2 3 4
0 “lime” “lime”   “lemon”  
1 “orange”       “lime”
2     “lemon”   “orange”

The method call removeNextByFlavor("grape") returns null because no grape-flavored candy is found. The contents of box are unchanged.

Complete the removeNextByFlavor method.

/**

  • Removes from box and returns a piece of candy with flavor specified by the parameter, or
  • returns null if no such piece is found, as described in part (b) */ public Candy removeNextByFlavor(String flavor)

// CODE_RUNNER: <challenge text>

Code Runner Challenge

FRQ #4 (b) - BoxOfCandyTest

View IPYNB Source
// CODE_RUNNER: FRQ #4 (b) - BoxOfCandyTest

public class Main {
    
    // Inner Candy class
    static class Candy {
        private String flavor;
        
        public Candy(String flavor) {
            this.flavor = flavor;
        }
        
        public String getFlavor() {
            return flavor;
        }
    }
    
    // Inner BoxOfCandy class
    static class BoxOfCandy {
        private Candy[][] box;
        
        public BoxOfCandy(Candy[][] box) {
            this.box = box;
        }
        
        public Candy removeNextByFlavor(String flavor) {
            for (int row = box.length - 1; row >= 0; row--) {
                for (int col = 0; col < box[0].length; col++) {
                    if (box[row][col] != null && box[row][col].getFlavor().equals(flavor)) {
                        Candy taken = box[row][col];
                        box[row][col] = null;
                        System.out.println("Removed " + flavor + " candy from row " + row + ", column " + col);
                        return taken;
                    }
                }
            }
            return null;
        }
        
        // Helper method to print the box
        public void printBox() {
            System.out.println("Current box contents:");
            for (int row = 0; row < box.length; row++) {
                System.out.print("Row " + row + ": ");
                for (int col = 0; col < box[row].length; col++) {
                    if (box[row][col] == null) {
                        System.out.print("[empty]\t\t");
                    } else {
                        System.out.print("[" + box[row][col].getFlavor() + "]\t");
                    }
                }
                System.out.println();
            }
            System.out.println();
        }
    }
    
    // Main method with test cases for Part B
    public static void main(String[] args) {
        System.out.println("========================================");
        System.out.println("PART B: removeNextByFlavor Tests");
        System.out.println("========================================\n");
        
        Candy[][] candyBox = new Candy[3][5];
        candyBox[0][0] = new Candy("lime");
        candyBox[0][1] = new Candy("lime");
        candyBox[0][3] = new Candy("lemon");
        candyBox[1][0] = new Candy("orange");
        candyBox[1][3] = new Candy("lime");
        candyBox[1][4] = new Candy("lime");
        candyBox[2][0] = new Candy("cherry");
        candyBox[2][2] = new Candy("lemon");
        candyBox[2][4] = new Candy("orange");
        
        BoxOfCandy box = new BoxOfCandy(candyBox);
        
        System.out.println("===== INITIAL BOX SETUP =====");
        box.printBox();
        
        // Test case 1: removeNextByFlavor("cherry")
        System.out.println("===== TEST CASE 1: removeNextByFlavor(\"cherry\") =====");
        System.out.println("Expected: removes cherry from row 2, column 0");
        Candy removed1 = box.removeNextByFlavor("cherry");
        System.out.println("Returned: " + (removed1 != null ? removed1.getFlavor() : "null"));
        box.printBox();
        
        // Test case 2: removeNextByFlavor("lime")
        System.out.println("===== TEST CASE 2: removeNextByFlavor(\"lime\") =====");
        System.out.println("Expected: removes lime from row 1, column 3 (first lime found from bottom-left)");
        Candy removed2 = box.removeNextByFlavor("lime");
        System.out.println("Returned: " + (removed2 != null ? removed2.getFlavor() : "null"));
        box.printBox();
        
        // Test case 3: removeNextByFlavor("grape")
        System.out.println("===== TEST CASE 3: removeNextByFlavor(\"grape\") =====");
        System.out.println("Expected: returns null (no grape found), box unchanged");
        Candy removed3 = box.removeNextByFlavor("grape");
        System.out.println("Returned: " + (removed3 != null ? removed3.getFlavor() : "null"));
        box.printBox();
    }
}

// Call the main method to execute
Main.main(null);
Lines: 1 Characters: 0
Output
Click "Run" in code control panel to see output ...

Scoring Guidelines Part B

5. Traverses box in specified order (bottom to top, left to right) (no bounds errors) — 1 point

Decision Rules:

Responses will not earn the point if they:

  • fail to access an element of box in the loop
  • access the elements of box incorrectly

6. Guards against a method call on a null element of the candy box (in the context of an if statement) — 1 point

Decision Rules:

Responses will not earn the point if they:

  • fail to use != or equivalent

7. Calls getFlavor on a Candy object — 1 point

Decision Rules:

Responses can still earn the point even if they:

  • access the element of the candy box incorrectly
  • call getFlavor on the incorrect Candy object

Responses will not earn the point if they:

  • call getFlavor on an object of a different type or on the Candy class
  • attempt to create a Candy object
  • include parameters

8. Compares a Candy object’s flavor with flavor parameter — 1 point

Decision Rules:

Responses will not earn the point if they:

  • fail to use equals

9. Replaces first matching Candy object with null and returns replaced object (algorithm) — 1 point

Decision Rules:

Responses can still earn the point even if they:

  • access elements of the candy box incorrectly
  • call getFlavor incorrectly or on a null Candy object
  • compare a Candy object’s flavor to the parameter using ==

Responses will not earn the point if they:

  • fail to replace the identified object within the 2D array with null before returning
  • fail to return null when no matching Candy object is found
  • set multiple elements to null

Total for part (b): 5 points

Course Timeline