Date: CS312 Credit by Exam, Sample 2 | Source: University of Texas at Austin, Introduction to Computer Programming
Tags: OOP, inheritance, extends, Critter class, encapsulation, private fields, instance variables, state machines, Scanner, data processing, file I/O, line-by-line parsing, .equals vs ==
Difficulty: Intermediate to Advanced. Prerequisites: basic Java classes, methods, constructors, if/else, loops, and familiarity with arrays.
This covers two of the highest-value question types on the CS312 exam: designing a class that extends an existing framework (the Critter pattern), and processing structured text data using Scanner. Both questions test your ability to manage state across multiple method calls, which is a step up from writing standalone methods. The Critter question specifically tests inheritance, encapsulation, and the separation of fight logic from movement logic. The data-processing question tests Scanner methods, line-by-line parsing, and String comparison.
The Critter pattern requires you to write a subclass that stores state in private instance variables and updates that state across separate method calls (fight and getMove are called at different times, not by you). Data processing with Scanner means reading lines, creating a second Scanner to parse each line, and converting between currency types or units while printing formatted output. Both questions reward careful state management and lose points fast for mixing up responsibilities between methods.
Inheritance (extends keyword)
A mechanism where a new class (subclass) inherits fields and methods from an existing class (superclass). In Java, you write public class JumpingBean extends Critter to create a subclass of Critter.
In simple terms, your class gets everything the parent class has, and you add or override what you need.
Encapsulation
The practice of making instance variables private and providing controlled access through methods. This prevents other classes from directly reading or modifying internal state.
Think of it as: the class owns its data and decides who gets to see or change it.
Instance variables
Variables declared at the class level (outside any method) that belong to each individual object. Each object gets its own copy. In the Critter pattern, these store state that persists between method calls.
In simple terms, these are the object's memory between one method call and the next.
Method overriding
When a subclass provides its own version of a method that the superclass already defines. In the Critter framework, you override fight() and getMove() to give your critter its own behaviour.
Think of it as: the parent class defines what methods exist, and you fill in your own logic.
Scanner (for data processing)
A Java class for reading input. Key methods: hasNextLine(), nextLine(), hasNext(), next(), hasNextInt(), nextInt(). A common pattern is creating a Scanner for each line of input to parse tokens within it.
In simple terms, Scanner is your tool for reading text piece by piece, whether from a file, a string, or the keyboard.
The .equals() method
The correct way to compare String contents in Java. Using == on Strings checks whether they are the same object in memory, which is almost never what you want. The exam deducts 3 points for using == instead of .equals() on Strings.
Think of it as: == asks "are these the same piece of paper?", .equals() asks "do these papers say the same thing?"
Enum values (Attack, Direction)
Predefined constant values used in the Critter framework. Attack has FORFEIT, SCRATCH, POUNCE, ROAR. Direction has NORTH, SOUTH, EAST, WEST, CENTER. You reference them as Attack.SCRATCH or Direction.NORTH.
In simple terms, these are the fixed set of options the framework gives you to choose from.
Your class extends Critter and overrides two methods: fight(String opp) and getMove()
The framework calls these methods at different times. You do not call them yourself
fight() is called when your critter encounters another critter. It returns an Attack value
getMove() is called every step to ask your critter which direction to move. It returns a Direction value
State must be stored in private instance variables so it persists between calls
The JumpingBean critter alternates between staying still (celebrating) and moving. Each time it wins a fight, it moves farther than the last time.
Instance variables needed:
stepsSoFar: tracks how many steps taken in the current leg
maxStepsThisLeg: the length of the current movement leg (increases by 2 each fight)
moving: whether the critter is currently in motion
dir: the current direction of movement (NORTH or WEST, chosen randomly)
All instance variables must be private (1 point on the exam)
A constructor is not required. Java's default values (0 for ints, false for booleans, null for objects) work here
If already moving, return Attack.FORFEIT (the critter is celebrating and does not want to fight)
If not moving, prepare for the next leg of movement:
Set moving to true
Increase maxStepsThisLeg by 2
Reset stepsSoFar to 0
Pick a random direction: Direction.NORTH if Math.random() < 0.5, otherwise Direction.WEST
Return Attack.SCRATCH
The direction is chosen once per fight, not once per step. Choosing a new direction every step costs 5 points
If not moving, return Direction.CENTER (stay in place)
If moving, increment stepsSoFar
If stepsSoFar equals maxStepsThisLeg, set moving to false (the leg is complete)
Return the stored direction
There must be no loop in getMove(). Each call returns exactly one move. Looping costs 7 points
The printMoney method reads lines of Harry Potter currency and converts to Galleons.
Outer loop: while(sc.hasNextLine()), read each line
Create a new Scanner from each line: Scanner line = new Scanner(sc.nextLine())
Print the person's name by reading tokens until an integer appears: while(!line.hasNextInt()) { print line.next() }
Parse currency pairs: read an int (amount) then a String (symbol: "K", "S", or "G")
Convert using the correct ratios:
1 Galleon = 17 Sickles
1 Sickle = 29 Knuts
So 1 Knut = 1 / 29.0 / 17.0 Galleons, and 1 Sickle = 1 / 17.0 Galleons
Compare symbols using .equals(), not ==. Using == costs 3 points
Print the total followed by "Galleon" or "Galleons" (no "s" if the total is exactly 1.0)
Students often put a loop inside getMove() to move multiple steps at once. The framework calls getMove() once per step. Your method must return a single Direction each time. Looping costs 7 points.
Students often have fight() call getMove() or vice versa. These methods must be independent. The framework calls them separately. Cross-calling costs 6 points.
Students often pick a new random direction every time getMove() is called. The direction should be chosen once in fight() and stored. Picking randomly every step costs 5 points.
Students often forget to set state in fight() so the critter knows it won. Without updating the instance variables, getMove() has no way to know the critter should start moving. This costs 5 points.
Students often use == to compare Strings in the Scanner problem. This compares references, not content. Use .equals(). The deduction is 3 points.
Students often forget that a Scanner created from a line (new Scanner(sc.nextLine())) only reads that one line. The outer Scanner reads lines; the inner Scanner reads tokens within each line.
Students often forget to include the space when printing the name tokens: line.next() + " " is needed, not just line.next(). Missing the space costs 1 point.
The Critter question is worth 16 points. The class header with the extends clause alone is 2 points. Making instance variables private is 1 point. These are free marks for knowing the pattern.
The data-processing question is worth 18 points. Creating a Scanner for each line is 2 points. Printing the name correctly is 3 points. Getting and checking the currency symbol is 4 points.
The exam penalises structural mistakes more heavily than small logic errors. A loop in getMove() costs 7 points. Cross-calling between fight() and getMove() costs 6 points. Using == instead of .equals() costs 3 points.
Default values in Java (0 for int, false for boolean, null for references) mean you do not always need a constructor. Knowing when default values are sufficient saves time on the exam.
The grading criteria explicitly accept multiple correct approaches. For example, tracking state with different variable names is fine, as long as the variables capture the needed information.
True or false: A Critter subclass should include a loop in getMove() to complete all steps of a movement leg in one call. (Answer: False. getMove() is called once per step. Return one Direction per call.)
Fill in the blank: To make a class inherit from Critter, the class header must include the keyword ______. (Answer: extends)
True or false: Instance variables in a Critter subclass should be declared public so the framework can access them. (Answer: False. They must be private. The framework uses the public methods fight() and getMove().)
Fill in the blank: To compare the String symbol to the literal "K", write symbol.______("K"). (Answer: equals)
True or false: If fight() is called and the critter is currently moving, it should return Attack.SCRATCH to fight. (Answer: False. It should return Attack.FORFEIT, because it is celebrating.)
Q: Write the class header for a Critter subclass called Hopper.
A: public class Hopper extends Critter {
Q: A Critter's fight() method is called while the critter is already moving. What should it return, and why?
A: It should return Attack.FORFEIT. The critter is currently celebrating (moving) and should not engage in a new fight.
Q: Why must the direction be chosen in fight() rather than in getMove()?
A: Because the critter should move in the same direction for the entire leg. If the direction were chosen in getMove(), each step could go a different way, which is incorrect behaviour. The exam deducts 5 points for picking a random direction every step.
Q: You are reading lines from a Scanner where each line has a name followed by numbers and letters. How do you separate the name from the data?
A: Create a new Scanner from the line (Scanner line = new Scanner(sc.nextLine())), then use a loop: while(!line.hasNextInt()) { System.out.print(line.next() + " "); }. This reads and prints name tokens until the first integer is found.
Q: Why do you need to create a new Scanner for each line?
A: Because the outer Scanner reads full lines with nextLine(), but you need to parse individual tokens (words and numbers) within each line. A second Scanner wrapping the line string gives you hasNext(), next(), hasNextInt(), and nextInt() for that line's tokens.
Q: How do you handle pluralisation of "Galleon" vs "Galleons" in the output?
A: Print "Galleon" always, then check: if (total != 1.0) { System.out.print("s"); }. This adds the "s" only when the total is not exactly 1.0.
The Critter pattern is a practical application of inheritance and encapsulation. It connects to every OOP concept in the course: polymorphism (the framework treats all Critters the same but each behaves differently), access modifiers (why private matters), and state management (instance variables persist between method calls). The data-processing pattern connects to array and ArrayList problems, because the same loop-and-accumulate pattern appears in all of them, just with different data structures.
Java inheritance, extends keyword, Critter class CS312, Critter fight method, Critter getMove method, Attack enum, Direction enum, encapsulation Java, private instance variables, state machine pattern, Scanner nextLine, Scanner hasNextInt, Scanner line-by-line, String .equals Java, == vs equals, data processing Java, currency conversion, method separation, framework callback pattern