20 Questions How To
At this point, you should have a pretty good bird’s-eye view of 20 questions. The how to explains the steps you’re going to take to complete it and how it will be graded.
Step 0:
Snarf the code. Run GainMain; observe that it doesn’t do very much yet. Read through, in detail,IAnimalGameModel.java, which is the interface you’ll be implementing by completingAnimalGameModel.java. Also take a look at AnimalNode, which implements a single node in our Twenty Questions game tree.
Step 1: File I/O
As provided, the game doesn’t do very much. The first thing you’ll need to do is implement reading game trees from a file. When you run the game and select File -> Open File or File -> Open URL, theinitialize method of AnimalGameModel is called. The Java Scanner type (what’s passed as an argument) provides a nextLine method that will iteratively pull lines from whatever file you’ve opened.
To get file reading working, you’ll need to do (at least) these things:
- Add (to AnimalGameModel) the instance variable (or variables) necessary to store your game tree. We recommend two AnimalNodes: one called myRoot that always points to the root of your game tree, and one called myCurrent that points to the current question. These aren’t the only things you’ll need to store, but they are a start.
- Write a recursive helper method for reading in the game tree. Note that the tree was written out using a pre-order traversal, with interior nodes marked as discussed in class. One extra thing: lines that begin with “//” should be ignored completely.
- Have initialize call your recursive helper with the appropriate parameters, and set up your instance variables.
A few things to remember:
- Lines corresponding to interior nodes start with “#Q:”.
- Your helper method will return an AnimalNode. I suggest you write it by completing this code:
private AnimalNode readHelper(Scanner s) { String line = s.nextLine(); if (...line is a leaf...) { // Construct a leaf AnimalNode from line, and return it. } // Make a recursive call to read the left subtree. // Make a recursive call to read the right subtree. // Construct the resulting AnimalNode and return it. }
To write your game tree to a file, you’ll need to implement the write method ofAnimalGameModel. This method is called by the view whenever you choose File -> Save. Dealing with creating a new file is taken care of for you; all you need to do is write your tree to the providedFileWriter object. FileWriters have a write method that takes a String as input. To start a new line, write the string “\n”; this is Java’s syntax for the newline character. For example:
// Without a new line; produces // PotatoTomato writer.write("Potato"); writer.write("Tomato"); // With a new line; produces // Potato // Tomato writer.write("Potato\n"); writer.write("Tomato");
Write your tree out using a pre-order traversal (to match your read-in code).
You should test your write-out and read-in code by reading in the provided animals.txt, writing it out to a different file, and then comparing the two files.
To enable the buttons on the GUI, you should call myView.setEnabled(true) right after you’ve finished loading your game tree.
Step 2: Playing Twenty Questions
Here’s an example of a game tree: This is also the tree represented by the provided animal.txt. Here’s an example of somebody playing the game with this tree; in this case, the player is thinking of “chicken”.




Understanding how the model (code you write) and the view (code we provide) interact is vital to getting this right. This is documented in IAnimalGameModel.java, and goes like this:
- The player opens a file using the GUI. This calls initialize on the model, and is what you dealt with in part 1.
- The player starts a new game. They can do this using File -> New Game. Important note: loading a file should automatically start a game! This is done by calling the model’s newGame method, either from the view calls (player input) or automatically (as part of initialize). ThenewGame method will need to set up whatever instance variables the model needs to keep track of the state of the current game, and set them to initial values
- The model asks a question. This is done by sending a String to the view’s update method, like this:
StringBuilder sb = new StringBuffer(); sb.append("Is it a walrus?\n"); // Note the use of "\n" for a newline! myView.update(sb.toString());
- The player clicks YES or NO. The view calls the model’s processYesNo with the appropriate value. The model updates its internal game state (myCurrentNode, plus anything else you add) according to what the player said.
- Steps 3 and 4 repeat until the computer guesses correctly, or until the computer runs out of game tree without guessing correctly. If the computer won, it should send a victory message to the view using the showDialog method. If it lost, things are more complicated: see below.
- Your life will be easier if you get step 2 working before you do step 3.
Step 3: What if the computer loses the game? Here’s another sequence, in which I’m thinking of a lion. We’ll start on the second question, after I’ve already said “no” to feathers.




Now something more complicated has to happen: we need to add a node to our game tree. To add a node, we need two things: the the thing the player was thinking of (in this case, a lion), and a question that disambiguates that thing from the computer’s guess. Is this case, we’ll use “Does it have tusks?” as elephants do, and lions don’t.
The Yes answer to your question should always be the existing animal. In the example the question is ‘Does it have tusks?’ because the existing animal is Elephant and Lion is new.
You should follow this guideline, otherwise the UTAs will run into massive difficulty grading your assignment and you *will* lose points.
For the game to do the right thing in this case requires a fairly complex model-view interaction. First, consider how the model knows that it needs new information: it has landed at a leaf node in the tree, and still been told no. Now it needs to do several things:
- Display the path down the tree to the player. This is so that the player doesn’t use a question that contradicts something that’s already been seen higher up the tree. To display the path, construct a String storing it, and then call myView.update with that String. For example:
StringBuilder sb = new StringBuffer(); sb.append("You said no to feathers.\n"); // "\n" again... sb.append("You said yes to mammal."); myView.update(sb.toString());
Displaying the tree is going to require storing some instance variables; just knowing the root of your tree, and the current leaf, is not enough to reconstruct the path; you’ll need to store it.
- Request new information from the player: in this case, what the right question should have been, seen to the left.
Request new info from playerThis is done by calling the view’sgetNewInfoLeaf method.
- Now, the tricky part. The user types in their question (“Is it a lion?”), and clicks OK. When they do this, the view (which handled the mouse click) calls the model’saddNewQuestion method with the typed-inString as an argument. From the model’s point of view (that is, your point of view), this looks slightly strange: you call a view method from one place in your code (where you callgetNewInfoLeaf), and you get the answer back somewhere else (as an argument toaddNewQuestion). It looks like you’ve jumped from one method to another; that’s because the view has jumped you there! Handling this correctly means storing some extra state in your model just before you callgetNewInfoLeaf, so that you can use that state in addNewQuestion.
- You’re halfway there. At this point, you’ve queried the user for the correct answer to their question (“lion”). Now, you’ll need to add a differentiator: a question that tells lions and elephants apart. This process has the same flavor as the previous step. First, the model calls the view’s update to ask for a differentiator. Then the model calls getDifferentiator, which pops up a dialog box for the player to type into. The model then calls the view’saddNewKnowledge with whatever the player typed. As before, the model calls a view method one place, and gets the result back somewhere else. The figure below shows the flow back and forth between the model and view over the course of the game; it may help clear things up.
- Finally, once you’ve added a new node, start a new game!