/* @(#)GuessingGameTester.java 1.0 26 September 2004 */ /* Useful imports */ import edu.neu.ccs.*; import edu.neu.ccs.gui.*; import edu.neu.ccs.codec.*; import edu.neu.ccs.console.*; import edu.neu.ccs.filter.*; import edu.neu.ccs.jpf.*; import edu.neu.ccs.parser.*; import edu.neu.ccs.pedagogy.*; import edu.neu.ccs.quick.*; import edu.neu.ccs.util.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.awt.font.*; import java.awt.image.*; import javax.swing.*; import javax.swing.border.*; import java.io.*; import java.util.*; import java.math.*; import java.beans.*; import java.lang.reflect.*; import java.net.URL; import java.util.regex.*; // import java.text.*; public class GuessingGameTester extends JPF { public static void main(String[] args) { // To optionally adjust the look and feel, // remove the comments from one of the two statements below. // LookAndFeelTools.showSelectLookAndFeelDialog(); LookAndFeelTools.adjustAllDefaultFontSizes(10); new GuessingGameTester(); } public void GuessingGame() { new GuessingGameInit(); } public void TestRandomRange() { printRange("Random Range", new RandomRange()); } public void TestUserRange() { printRange("User Range", new UserRange()); } private void printRange(String title, Range range) { console.out.println(title); console.out.println("Minimum = " + range.getMinimum()); console.out.println("Maximum = " + range.getMaximum()); console.out.println(); } } /** *
Class Range provides an integer range specified directly by its endpoints.
* *Class Range is also the base class for building specialized range classes.
* *A range should be immutable once construction is complete.
*/ class Range { private int minimum = 0; private int maximum = 0; /** *The public constructor that requires the range endpoints.
* *The endpoints do not need to be ordered.
*/ public Range(int a, int b) { setRange(a, b); } /** Returns the range minimum. */ public int getMinimum() { return minimum; } /** Returns the range maximum. */ public int getMaximum() { return maximum; } /** The protected default constructor available only to derived classes. */ protected Range() { } /** * The protected setRange method available only to this class and to * derived classes. */ protected void setRange(int a, int b) { minimum = Math.min(a, b); maximum = Math.max(a, b); } } /** *The class RandomRange provides a random range whose endpoints are between * 1 and Integer.MAX_VALUE = 2147483647.
*/ class RandomRange extends Range { public RandomRange() { setRange(MathUtilities.randomInt(1, Integer.MAX_VALUE), MathUtilities.randomInt(1, Integer.MAX_VALUE)); } } /** *The class UserRange provides a range specified by the user at construction.
*/ class UserRange extends Range implements JPTConstants { private int a; private int b; private int width = TextFieldView.getSampleWidth("0000000000000000"); /** The input text field for end point A. */ private final TextFieldView endpointA = new TextFieldView("", width); /** The input text field for end point B. */ private final TextFieldView endpointB = new TextFieldView("", width); /** The input panel. */ private TablePanel inputPanel = new TablePanel( new Object[][] { { "End Point A", new Halo(endpointA) }, { "End Point B", new Halo(endpointB) } }, 10, 10, WEST); /** * The action to extract the endpoints from the user data * and set the range. */ private SimpleAction inputAction = new SimpleAction("OK") { public void perform() { a = endpointA.demandInt(); b = endpointB.demandInt(); setRange(a, b); } }; /** The dialog to obtain the input. */ private GeneralDialog inputDialog = new GeneralDialog (inputPanel, "User Defined Range", new Object[][] {{ inputAction }}); /** The constructor. */ public UserRange() { // The range will be set when the dialog closes // Arrange that the input action must happen no matter how dialog closes inputDialog.setWindowClosingAction(inputAction, DialogAction.AUTO_CLOSE); inputDialog.setVisible(true); } } /** *The class RangeFactory permits the definition of a Range to be * deferred to run time choices of the user.
* *Since this class is abstract, it must be instantiated by one or * more derived classes.
*/ abstract class RangeFactory { /** Returns a range encapsulated or constructed by this factory. */ public abstract Range makeRange(); } /** *The class ConstantRangeFactory is designed to return the same * range object with each call of its method makeRange.
*/ class ConstantRangeFactory extends RangeFactory { private Range range; public ConstantRangeFactory(int a, int b) { range = new Range(a, b); } public Range makeRange() { return range; } } /** *The class RandomRangeFactory is designed to return a new random * range object with each call of its method makeRange.
*/ class RandomRangeFactory extends RangeFactory { public Range makeRange() { return new RandomRange(); } } /** *The class UserRangeFactory is designed to return a new user defined * range object with each call of its method makeRange.
*/ class UserRangeFactory extends RangeFactory { public Range makeRange() { return new UserRange(); } } /** *The class GuessingGameInit displays a GUI in which the user may * select the range of numbers and thereby initiate a GuessingGame.
* *This GUI remains open so that the user may start multiple games * and even have several games active simultaneously.
*/ class GuessingGameInit implements JPTConstants { /** Highest power of 10 to use in the standard ranges. */ private int limit = 6; /** The active radio button panel that is the heart of the GUI. */ private StringObjectRadioPanel rangePanel; /** * The action to initiate a new GuessingGame whenever one of the * radio buttons is clicked. */ private SimpleAction makeGameAction = new SimpleAction("Make Guessing Game") { public void perform() { makeGame(); } }; /** * The constructor that opens the GUI to permit a user to select * the initial range for a guessing game. */ public GuessingGameInit() { // Make radio button panel makeRangePanel(); // Open GUI frame. JPTFrame.createQuickJPTFrame("Make Game", rangePanel, NORTH); } /** *This method makes the pairs consisting of the strings that will * be displayed in the radio button panel and the objects that will * be associated with the strings.
* *Each string will be associated with a RangeFactory object. If * that option is selected in the radio button panel then the factory * will be used to make a new range that is then used to initialize a * new instance of a GuessingGame.
* *So, in short, a string in the radio button panel associates with * a range factory object that constructs a range object that is used * to construct a new guessing game.
*/ private Object[][] makePairs() { Object[][] pairs = new Object[limit + 3][2]; // create the ranges of the form 1 to "a power of 10" int endpoint = 10; for (int i = 0; i < limit; i++) { pairs[i][0] = "1 to " + endpoint; pairs[i][1] = new ConstantRangeFactory(1, endpoint); endpoint *= 10; } // create the range 1 to 2147483647 endpoint = Integer.MAX_VALUE; pairs[limit][0] = "1 to " + endpoint; pairs[limit][1] = new ConstantRangeFactory(1, endpoint); // create a random range pairs[limit + 1][0] = "Random Range"; pairs[limit + 1][1] = new RandomRangeFactory(); // create a user defined range pairs[limit + 2][0] = "User Defined Range"; pairs[limit + 2][1] = new UserRangeFactory(); return pairs; } /** Make the radio button panel using the pairs and the make game action. */ private void makeRangePanel() { rangePanel = new StringObjectRadioPanel(makePairs(), makeGameAction); } /** * The method that instantiates the make game action by obtaining the * range factory associated with the selected option and using that * factory to make a range that is used to build a new GuessingGame. */ private void makeGame() { RangeFactory factory = (RangeFactory) rangePanel.getSelectedObject(); new GuessingGame(factory.makeRange()); } } /** *The class GuessingGame displays a GUI for the guessing game and * manages the game progress as the user makes guesses.
*/ class GuessingGame implements JPTConstants { private Range current; private int value = 0; private int guess = 0; private int guessCount = 0; private int width = TextFieldView.getSampleWidth("00000000000000000000000000000000"); private JLabel minimumView = new JLabel(""); private JLabel maximumView = new JLabel(""); private TextFieldView guessView = new TextFieldView("", width); private JLabel countView = new JLabel(""); private JLabel commentView = new JLabel(""); /** The action to process a user guess. */ private SimpleAction makeGuessAction = new SimpleAction("Make Guess") { public void perform() { processGuess(); } }; /** The action to reveal the hidden answer to the user. */ private SimpleAction revealAnswerAction = new SimpleAction("Reveal Answer") { public void perform() { revealAnswer(); } }; /** * The guess panel to show the current minimum, maximum, and number of * previous guesses plus the text field for input of the next guess. */ private TablePanel guessPanel = new TablePanel( new Object[][] { { "Minimum", minimumView }, { "Maximum", maximumView }, { "Count", countView }, { "Guess", new Halo(guessView) } }, 10, 10, WEST); /** * The view panel that combines the guess panel, the button to trigger * a guess, the comment that follows the guess, and the button to show * the hidden answer if the user becomes frustrated. */ private TablePanel viewPanel = new TablePanel( new Object[] { guessPanel, makeGuessAction, commentView, revealAnswerAction }, VERTICAL, 10, 10, CENTER); /** * The contructor that builds the guessing game GUI initialized with * a range. */ public GuessingGame(Range initial) { // Initialize state current = initial; value = MathUtilities.randomInt(current.getMinimum(), current.getMaximum()); // Add action listener to the guess view guessView.addActionListener(makeGuessAction); // Initialize view resetView(); // Open GUI frame JPTFrame.createQuickJPTFrame("Guessing Game", viewPanel); } /** Get a new guess within the range of the current minimum and maximum. */ private int getGuess() { RangeFilter filter = new RangeFilter.Long (current.getMinimum(), current.getMaximum()); return guessView.demandInt(filter); } /** Process the user guess. */ private void processGuess() { guess = getGuess(); guessCount++; resetView(); } /** Reset the view depending on current state. */ private void resetView() { countView.setText("" + guessCount); // Handle the first view separately if (guessCount == 0) { firstView(); return; } // The "Too Low" view if (guess < value) { current = new Range(guess + 1, current.getMaximum()); guessView.setViewState(""); commentView.setText("Too Low"); } else // The "Too High" view if (guess > value) { current = new Range(current.getMinimum(), guess - 1); guessView.setViewState(""); commentView.setText("Too High"); } else // The "Absolutely Correct" view { current = new Range(value, value); commentView.setText("Absolutely Correct"); makeGuessAction.setEnabled(false); } // reset the minimum and maximum after changes to the current range resetMinMax(); } /** Handle the first view. */ private void firstView() { resetMinMax(); commentView.setText("Make Your First Guess"); } /** Reset the minimum and maximum after changes to the current range. */ private void resetMinMax() { minimumView.setText("" + current.getMinimum()); maximumView.setText("" + current.getMaximum()); } /** Open a dialog to reveal the hidden number being guessed. */ private void revealAnswer() { GeneralDialog.showOKDialog(new JLabel("The hidden answer is " + value), "Answer"); } }