CPSC 225 | Intermediate Programming | Spring 2025 |
The purpose of this lab is to practice working with binary trees.
Labs are due at the start of lab on the date listed. Note: as the due date is an exam day, make sure you hand in your files before coming to lab, or arrive early enough so that you can hand in your files before the start of lab so that you do not lose time for the exam.
To be eligible for revise-and-resubmit, you must turn in something by the due date. Late work and extensions are generally not accepted/granted; see the posted late policy for more information.
You may work with a partner if you wish. Both partners must actively contribute to the solution! You only need one handin for the group — be sure to put both teammates' names in every file.
Otherwise you may get help but you may not use other resources (friends, neighbors, websites, ChatGPT, etc) to produce answers. See the posted policy on academic integrity for more information.
To hand in your work:
Make sure that your name (along with your partner's name if you are working with a partner) is at the beginning of each file (use the @author tag in Java files) and that all your Java files have been auto-formatted and saved.
Copy the entire lab8 project directory from your workspace ~/cs225/workspace to your handin directory in /classes/cs225/handin.
Check that everything got handed in correctly — navigate to your handin directory and make sure it contains a subdirectory src containing your Java files.
You may have played the game of 20 Questions — one person thinks of something and the other tries to guess what it is by asking only yes-no questions. It's called 20 Questions because traditionally the guesser only gets 20 guesses.
In this version, the user is the one thinking of the thing while the computer tries to guess it. There will not be any restriction on the number of questions — the computer will keep guessing until it runs out of questions.
The computer's knowledge of yes-no questions and things will be stored in a binary decision tree like the example shown. Each internal node will store a question; leaves will store the known things. (Note that this tree will not have dummy leaves — every node will store an element.) Use the convention that the left child of each node represents a "yes" answer to the question stored at that node and the right child represents a "no" answer. The computer's knowledge of the world will be built up as it plays the game.
You've been provided with a version of the TreeNode class used in class, a main program for the game 20 Questions (TwentyQuestions), a saved game tree (tree.txt), and solutions for the exercises in the lab in compiled form (20Q.jar). This allows you to run the program and use all the functionality as you work on each piece.
The provided TreeNode class has been defined as a generic so that it can be used for any type of element (instead of the int-only version from class). Work with like the Java Collections classes that are defined as generics — remember that the element type is part of the name of the type and the constructor.
Create a new Java project called lab8 in Eclipse.
Import the Java files (and just the .java files!) in /classes/cs225/lab8 into the project. Make sure the imported files end up in the src directory of the project. There will be compiler errors in TwentyQuestions — that's OK for the moment.
Import the other files (tree.txt and 20Q.jar) in /classes/cs225/lab8 into the project. Make sure they end up in the top level directory of the project, that is, under the project name itself and not in the src directory.
Right-click on the imported 20Q.jar file in the Package Explorer and choose Build Path → Add to Build Path. You should see a new item ("Referenced Libraries") appear and the compiler errors in TwentyQuestions should go away.
Make sure you've read about 20 Questions above (in the Preliminaries section), then run the program to see how things should work. (Make sure you understand the output in each case.) In particular, try the following (in order):
The " ** provided version" messages indicate that you are using the provided solutions; you will replacing these with your own code in the exercises.
All of your code should go into the provided TwentyQuestions.java file. You can do the three exercises in any order. Read the more detailed descriptions of each method given in each section before starting to write code for that method.
Hint: remember the patterns of movement for trees as discussed in class. When you are implementing something involving moving through a tree, first identify which pattern is applicable. That then gives you a template for your code.
Fill in the body of the play method to carry out one round (or game) of 20 Questions as described below.
Locate the TODO comment in the "play game" case in main and change TwentyQOps.play to play in order to use your code instead of the provided version. (The " ** provided version: play" message should go away.)
One round (or game) consists of the computer asking a series of questions until it guesses a thing, updating the tree if the guess is incorrect. That is, start at the root of the tree and as long as the current node is an internal node, ask the question stored at that node, get the user's response, and update the current node (go to the left child if the user responded yes and the right child if the user responded no). When a leaf is reached, the computer should guess the thing at the leaf and the user should be asked if the guess was correct. If the guess is correct, a victory message should be printed. If the guess is incorrect, the user should be prompted for the thing they were thinking of and a question which can be used to distinguish between the user's thing and the computer's guess. The new question and thing should then be added to the tree. Make sure that you know if the answer to the question should be yes or no for the user's thing so you can update the tree correctly.
Note: both questions and things entered by the user may contain spaces. Be sure to read an entire line of text when you read in one of these things.
Hint: Draw an example or two of a tree before and after adding the new information to understand what needs to be done to the tree. (This also helps you get the steps in the right order.)
A sample run of the program showing several rounds of the game is shown below. Bold underline indicates user input and italics indicates things printed out by the provided code; things not in bold, underline, or italics should be produced by your code in play. You do not need to reproduce the bold, underline, or italics — this is just to show what comes from where in the example.
(p)lay, print (t)ree, (f)ind thing, (s)ave, (l)oad, or (q)uit? l enter filename to load from: tree.txt (p)lay, print (t)ree, (f)ind thing, (s)ave, (l)oad, or (q)uit? t does it have fur? does it lay eggs? platypus cat is it a reptile? lizard duck (p)lay, print (t)ree, (f)ind thing, (s)ave, (l)oad, or (q)uit? p does it have fur? yes does it lay eggs? yes you're thinking of platypus! right? yes I guessed it! (p)lay, print (t)ree, (f)ind thing, (s)ave, (l)oad, or (q)uit? p does it have fur? no is it a reptile? yes you're thinking of lizard! right? no what were you thinking of? dragon a question which is 'yes' for dragon and 'no' for lizard: does it fly? (p)lay, print (t)ree, (f)ind thing, (s)ave, (l)oad, or (q)uit? t does it have fur? does it lay eggs? platypus cat is it a reptile? does it fly? dragon lizard duck
Fill in the body of the printQs method as specified in its comments. (Additional information is below.)
Locate the TODO comments in the "find game" case in main and change TwentyQOps.printQs(node) to printQs(node) in order to use your code instead of the provided version. (The " ** provided version: printQs" message should go away.)
printQs should print the series of questions and answers that lead to a particular thing in the tree. The method's comments show the format that the output should take.
Hint: Break this into three tasks — first just get the right questions printed in some order; don't worry about the printing the answers or the order of the questions. Then get the questions printed in the right order — if they are coming out in reverse order, think about what you know about reversing things... Finally, print the "yes" or "no" answer for each question. How can you figure out this information?
A sample run of the program showing the usage of printQs is shown below. Bold underline indicates user input and italics indicates things printed out by the provided code; things not in bold, underline, or italics should be produced by your code in printQs. You do not need to reproduce the bold, underline, or italics — this is just to show what comes from where in the example.
(p)lay, print (t)ree, (f)ind thing, (s)ave, (l)oad, or (q)uit? l enter filename to load from: tree.txt (p)lay, print (t)ree, (f)ind thing, (s)ave, (l)oad, or (q)uit? t does it have fur? does it lay eggs? platypus cat is it a reptile? lizard duck (p)lay, print (t)ree, (f)ind thing, (s)ave, (l)oad, or (q)uit? f enter the thing to look for: cat cat -- does it have fur? yes does it lay eggs? no (p)lay, print (t)ree, (f)ind thing, (s)ave, (l)oad, or (q)uit? f enter the thing to look for: lizard lizard -- does it have fur? no is it a reptile? yes (p)lay, print (t)ree, (f)ind thing, (s)ave, (l)oad, or (q)uit? f enter the thing to look for: emu emu is not a thing in this tree
Fill in the body of the find method as specified in its comments. (Additional information is below.)
Locate the TODO comments in the "find game" case in main and change TwentyQOps.find(root,thing) to find(root,thing) in order to use your code instead of the provided version. (The " ** provided version: find" message should go away.)
Note that only leaf nodes should be checked to see if they contain the thing being looked for — return null if no leaf nodes contain thing even if there is an internal node containing thing.
(This isn't required or graded, but is good to think about if you have extra time.)
Note: do not change any of the public method headers in the provided code, but you may add private helper methods if needed.
Fill in the body of the save method to save the tree in the specified file. Node elements should be written out according to a preorder traversal (i.e. the node's element should be written before any of its children's elements). Since it will be necessary to know which elements belong in leaves and which in internal nodes when reading the file back in, begin each line of output with either "[Q]" (for a question, indicating an internal node) or "[A]" (for an answer, indicating a leaf). For example:
[Q] does it have fur? [Q] does it lay eggs? [A] platypus [A] cat [Q] is it a reptile? [A] lizard [A] duck
Important: make sure you locate the TODO comment the "save tree" case in main and change TwentyQOps.save(root,filename) to save(root,filename) in order to use your code.
Hint: start by printing the output shown on the screen. Once you have that working, modify your solution to write to the specified file instead. But be careful! Only set up the hose for the stream once. This means that you'll need a private helper method — which can still be named save — with parameters for the subtree root and the stream to write to.
Fill in the body of the load method to load the tree from the specified file. As you read the file, you are once again traversing the tree in the same order you did when saving, except that you are also building the tree as you go. Start with a tree with a single node and no element, then as you read in each line of the file, store the read-in line as the element of the current node and, if the node is to be an internal node (i.e. the line read in starts with "[Q]"), add leaves for the left and right children in order to begin those subtrees.
Important: make sure you locate the TODO comment the "load tree" case in main and change TwentyQOps.load(filename) to load(filename) in order to use your code.
Hint: this is another case where you'll need a private helper method with parameters for the current node and the stream to read from.
Note: When you read a line from the file, you will need to separate the "[Q]" or "[A]" from the question or thing on the rest of the line; one way to do this is to read the line in two parts — first read a word (up to the first space), then read the rest of the line. (Note that you have to be careful about what happens to that first space — you may find the trim() method of String useful for getting rid of any extra whitespace at the beginning and end of a string.)
Modify the provided print method so that it also prints out a "yes" or "no" label identifying which branch is which:
does it have fur? yes: does it lay eggs? yes: platypus no: cat no: is it a reptile? yes: lizard no: duck
instead of
does it have fur? does it lay eggs? platypus cat is it a reptile? lizard duck
Make the program robust: it should not crash or do something cryptic, unexpected, or incorrect if the user does not enter an expected answer.