import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import java.util.*;
import java.awt.event.*;
import java.io.*;
/** A GUI for the game of Boggle.
*
* BoggleGUI defines and implements methods to do these things:
*
* 1. Draw the boggle board
* 2. Manage the computer and human players' word lists
* 3. Update the scoreboard
*
* BoggleGUI requires class BogglePlayer, to define these instance methods:
*
* public void buildLexicon(Set wordList)
* public void setBoard(String[] letterArray)
* public Vector getAllValidWords(int minimumWordLength)
* public boolean isInLexicon(String wordToCheck)
* public Vector isOnBoard(String wordToCheck)
* public String[] getCustomBoard()
*
*
* @author Michael Green
* @author Paul Kube
* @author Eric Chown
* @author Laura Toma
*
* Copyright (C) 2002 Michael Green
* Copyright (C) 2002 Paul Kube
* Copyright (C) 2003 Eric Chown
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. See
* http://www.gnu.org/copyleft/gpl.html
*/
public class BoggleGUI extends JFrame {
public static String WORDLISTFILENAME = "dict.txt"; // default
public static int DICEROWS = 4; // default 4
public static int DICECOLS = 4; // default 4
public static int MINIMUMWORDLENGTH = 4; // default
private BogglePlayer computerPlayer;
private BoggleBoard theBoard;
private ScoreArea humanArea, computerArea;
private WordEntryField wordEntryField;
public BoggleGUI() {
super("Welcome to CS 210!");
// create a BogglePlayer computer player to use
computerPlayer = new BogglePlayer(MINIMUMWORDLENGTH, DICEROWS, DICECOLS);
// Read word list from file and initialize computerPlayer's Lexicon
initLexicon();
// Intialize graphics panels
initPanels();
// Establish menu bar options and listeners for them
setUpMenuBar();
// WindowClosing listener: for JDK 1.2 compatibility
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}});
// Trust contained components to set their sizes; top level frame packs
pack();
}
/**
* Get ready for a new game, given a letter list specification of
* the Boggle board.
* @param letterList specification of the board
* @return void
*/
public void newGame(String[] letterList) {
// Tell theBoard about the board layout
theBoard.setBoard(letterList);
// Tell computerPlayer about the board layout
computerPlayer.setBoard(letterList);
// Prepare score areas
humanArea.setReady();
computerArea.setReady();
// Clear board highlighting
theBoard.unHighlightAllDice();
// Allow edits to text area and set focus
wordEntryField.setReady();
// redraw the whole thing so it looks as nice as can be
repaint();
}
/**
* Consider a word entered by the human player.
* @param wordToCheck word entered by human
* @return Nothing, but highlight the letters when on board
*/
public void checkAndAddWordHuman(String wordToCheck) {
// clear any board highlighting
theBoard.unHighlightAllDice();
// if empty word, the computer gets to take its turn
if(wordToCheck.length() == 0) {
wordEntryField.setUnready();
computerPlay();
return;
}
// check that word is:
// (1) at least minimum length, (2) on the board, (3) in the lexicon,
// (4) not already entered by the player
if(wordToCheck.length() < MINIMUMWORDLENGTH) {
chideUser(wordToCheck, "Less Than " + MINIMUMWORDLENGTH + " Letters");
return;
}
Vector letterLocations;
// letterLocations = theBoard.getLocationsForWord(wordToCheck);
// String wordOnBoard = theBoard.getWordFromLocations(letterLocations);
// if(!wordToCheck.equalsIgnoreCase(wordOnBoard)) { // double check
// we just trust the computer player to check this
letterLocations = computerPlayer.isOnBoard(wordToCheck);
if(letterLocations == null) {
chideUser(wordToCheck, "Not On Board");
return;
}
if(!computerPlayer.isInLexicon(wordToCheck)) {
chideUser(wordToCheck, "Not In Lexicon");
return;
}
if(humanArea.containsWord(wordToCheck)) {
chideUser(wordToCheck, "Duplicate Word");
return;
}
// OK, this word passed our rigorous suite of tests. Add it
humanArea.addWord(wordToCheck);
// Highlight locations on board
theBoard.highlightDice(letterLocations);
wordEntryField.clear(); //clear the wordEntryField text
}
/**
* Print a message that the human player did something wrong.
* @param attemptedWord The word the human entered
* complaint Description of what went wrong
* @return void
*/
public void chideUser(String attemptedWord, String complaint) {
JOptionPane.showMessageDialog(this,
attemptedWord + ": " + complaint + "!!!",
complaint,
JOptionPane.ERROR_MESSAGE);
wordEntryField.clear(); //clear the wordEntryField text
}
/**
* Let the computer player take its turn.
* @param none
* @return nothing, but highlights words computer got and computes its score
*/
public void computerPlay() {
computerArea.setName("Thinking!");
computerArea.paintImmediately(computerArea.getVisibleRect());
Vector allWords = computerPlayer.getAllValidWords(MINIMUMWORDLENGTH);
if (allWords == null) {
System.out.println("getAllValidWords returns null; cannot play");
System.exit(1);
}
Iterator allWordsIterator = allWords.iterator();
computerArea.setName("Computer");
while(allWordsIterator.hasNext()) {
String newWord = (String) allWordsIterator.next();
Vector wordPath = computerPlayer.isOnBoard(newWord);
// Add word to appropriate score area & highlight appropriate dice
computerArea.addWord(newWord);
theBoard.highlightDice(wordPath);
// pause for a tenth of a second
//try {Thread.sleep(100); } catch (Exception e) {}
}
theBoard.unHighlightAllDice(); // leave board unhighlighted when done
//computerPlayer.debug(); //Show debug info
}
/**
* Read word list from file with name WORDLISTFILENAME, and pass a Set
* containing those words to the computer player to intialize its lexicon.
* @param none
* @return void
*/
private void initLexicon() {
boolean EOF = false;
HashSet wordsFromFile = new HashSet();
System.err.print("Reading word list from file " + WORDLISTFILENAME + "...");
try {
// Open File
BufferedReader input = new BufferedReader( new FileReader(WORDLISTFILENAME));
String nextWord;
while((nextWord = input.readLine()) != null)
// there is a contract that these are all lowercase but
// we'll just assume it here for speed
wordsFromFile.add(nextWord);
//Close the file
input.close();
} catch (FileNotFoundException fnfex ) {
JOptionPane.showMessageDialog(null,
"Unable to find word list file " + WORDLISTFILENAME, "Error",
JOptionPane.ERROR_MESSAGE);
}
catch( IOException e) {
JOptionPane.showMessageDialog(null,
"Error Opening File", "Error",
JOptionPane.ERROR_MESSAGE);
}
System.err.println(" Read " + wordsFromFile.size() + " words.");
System.err.print("Building player's lexicon... ");
computerPlayer.buildLexicon(wordsFromFile);
System.err.println("done.");
}
/**
* Initialize graphic display.
* @param none
* @return void
*/
private void initPanels() {
Container contentPane = getContentPane();
humanArea = new ScoreArea("Me");
computerArea = new ScoreArea("Computer");
theBoard = new BoggleBoard(DICEROWS,DICECOLS,computerPlayer);
wordEntryField = new WordEntryField();
contentPane.add(wordEntryField,BorderLayout.SOUTH);
contentPane.add(humanArea, BorderLayout.WEST);
contentPane.add(theBoard, BorderLayout.CENTER);
contentPane.add(computerArea, BorderLayout.EAST);
}
/**
* Initialize menus for controlling game.
* @param none
* @return void
*/
private void setUpMenuBar() {
//Set Up Menu Bar
JMenuBar menu = new JMenuBar();
// Game Menu
JMenu gameMenu = new JMenu("Game");
gameMenu.setMnemonic('G');
menu.add(gameMenu);
JMenuItem newRandom = new JMenuItem("New Random");
gameMenu.add(newRandom);
newRandom.setMnemonic('N');
newRandom.addActionListener(new ActionListener() {
public void actionPerformed( ActionEvent e) {
newGame(theBoard.getRandomBoard());
}});
JMenuItem customGame = new JMenuItem("New Custom");
gameMenu.add(customGame);
customGame.setMnemonic('C');
customGame.addActionListener(new ActionListener() {
public void actionPerformed( ActionEvent e) {
newGame(computerPlayer.getCustomBoard());
}});
JMenuItem quitGame = new JMenuItem("Quit");
gameMenu.add(quitGame);
quitGame.setMnemonic('Q');
quitGame.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e) {
System.exit(0);
}});
// Help menu
JMenu helpMenu = new JMenu("Help");
menu.add(helpMenu);
helpMenu.setMnemonic('H');
JMenuItem aboutGame = new JMenuItem("About...");
helpMenu.add(aboutGame);
aboutGame.setMnemonic('A');
aboutGame.addActionListener(new ActionListener() {
public void actionPerformed( ActionEvent e) {
JOptionPane.showMessageDialog(BoggleGUI.this,
"It's ABOUT time you got to work",
"About Game",
JOptionPane.PLAIN_MESSAGE);
}});
setJMenuBar(menu);
}
/**
* A class defining the visual appearance of a Boggle board, and
* defining some related methods.
*/
static class BoggleBoard extends JPanel {
public static final Color BACKGROUNDCOLOR = new Color(255, 219, 13);
private DiePanel theDice[][];
private Vector diceBag; // used for drawing a random game
private int rows, cols;
private BogglePlayer theComputer;
BoggleBoard(int rows, int cols, BogglePlayer theComputer ) {
this.rows = rows; this.cols = cols;
this.theComputer = theComputer;
// create some dice to use when getting a random game
initDiceBag();
// create a JPanel with rowsXcols GridLayout to hold the DiePanels
JPanel innerPanel = new JPanel();
innerPanel.setLayout(new GridLayout(rows,cols,1,1));
innerPanel.setBackground(BACKGROUNDCOLOR);
// Create Individual DiePanels, and add them
theDice = new DiePanel[rows][cols];
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
theDice[row][col] = new DiePanel();
innerPanel.add(theDice[row][col]);
}
}
innerPanel.setBorder(BorderFactory.createMatteBorder(10,10,10,10,
BACKGROUNDCOLOR));
this.add(innerPanel);
}
/**
* fill the diceBag with the 16 "official" Boggle dice for the 4X4 game
* @param none
* @return void
*/
private void initDiceBag() {
diceBag = new Vector(16);
diceBag.add(new Die("A", "O", "B", "B", "O", "J"));
diceBag.add(new Die("W", "H", "G", "E", "E", "N"));
diceBag.add(new Die("N", "R", "N", "Z", "H", "L"));
diceBag.add(new Die("N", "A", "E", "A", "G", "E"));
diceBag.add(new Die("D", "I", "Y", "S", "T", "T"));
diceBag.add(new Die("I", "E", "S", "T", "S", "O"));
diceBag.add(new Die("A", "O", "T", "T", "W", "O"));
diceBag.add(new Die("H", "Qu", "U", "M", "N", "I"));
diceBag.add(new Die("R", "Y", "T", "L", "T", "E"));
diceBag.add(new Die("P", "O", "H", "C", "S", "A"));
diceBag.add(new Die("L", "R", "E", "V", "Y", "D"));
diceBag.add(new Die("E", "X", "L", "D", "I", "R"));
diceBag.add(new Die("I", "E", "N", "S", "U", "E"));
diceBag.add(new Die("S", "F", "F", "K", "A", "P"));
diceBag.add(new Die("I", "O", "T", "M", "U", "C"));
diceBag.add(new Die("E", "H", "W", "V", "T", "R"));
}
/**
* Return an array of Strings showing the sequence of faces on
* a randomly generated board.
* @param none
* @return array of strings representing the letters on the board
*/
public String[] getRandomBoard() {
// Get random faces from randomy selected dice to fill the letterList
int diceBagSize = diceBag.size();
int boardSize = rows * cols;
String[] letterList = new String[(rows * cols)];
// we try to handle any size board with a fixed diceBag
for (int i = 0; i wordList;
private final Font ScoreFont = new Font("SansSerif", Font.PLAIN, 18);
private final Font WordFont = new Font("Geneva", Font.PLAIN, 9);
private final Font LabelFont = new Font("Helvitica", Font.PLAIN, 9);
private final Color WordColor = new Color(3, 128, 77);
private final Color LabelColor = new Color(3, 115, 64);
private JPanel topPanel, wordPanel, namePanel, scorePanel;
private JTextArea wordArea;
private JLabel nameText, scoreText;
public ScoreArea(String player) {
playerName = new String(player);
playerScore = 0;
wordList = new HashSet();
// Set-Up Top of Score Area
namePanel = new JPanel();
nameText = new JLabel(player);
nameText.setFont(ScoreFont);
namePanel.setLayout( new BorderLayout() );
namePanel.add(nameText, BorderLayout.CENTER);
scorePanel = new JPanel();
scoreText = new JLabel(" 0");
scoreText.setFont(ScoreFont);
scorePanel.setLayout( new BorderLayout() );
scorePanel.add(scoreText, BorderLayout.CENTER);
topPanel = new JPanel();
topPanel.setLayout( new BorderLayout());
topPanel.add(namePanel, BorderLayout.WEST);
topPanel.add(scorePanel, BorderLayout.EAST);
// Create bordering for top panel
Border raisedBevel, loweredBevel, compound;
raisedBevel = BorderFactory.createRaisedBevelBorder();
loweredBevel = BorderFactory.createLoweredBevelBorder();
compound = BorderFactory.createCompoundBorder(raisedBevel,
loweredBevel);
topPanel.setBorder(compound);
// Set-Up area to display word list
wordPanel = new JPanel();
Border etched = BorderFactory.createEtchedBorder();
TitledBorder etchedTitle =
BorderFactory.createTitledBorder(etched, "Word List");
etchedTitle.setTitleJustification(TitledBorder.RIGHT);
wordPanel.setBorder(etchedTitle);
wordArea = new JTextArea(WORDAREALINES,
// 2/3 of max word len is a good # of columns
BoggleGUI.DICEROWS*BoggleGUI.DICECOLS*2/3);
wordArea.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
// if double-click, highlight the selection on the board
if(e.getClickCount() == 2) {
String word = wordArea.getSelectedText().trim();
theBoard.highlightDice(theBoard.getLocationsForWord(word));
}
}});
wordArea.setEditable(false); // for now
wordPanel.add(new JScrollPane(wordArea,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));
setLayout(new BorderLayout());
add(topPanel, BorderLayout.NORTH);
add(wordPanel, BorderLayout.CENTER);
}
public void setReady() {
resetScore(); // zero out score
wordList.clear(); // remove words from HashSet
// remove words from TextArea
wordArea.setEditable(true);
wordArea.selectAll();
wordArea.cut();
wordArea.setEditable(false);
paintImmediately(getVisibleRect());
}
public void setName(String newName) {
playerName = newName;
nameText.setText(playerName);
repaint();
}
public boolean containsWord(String word) {
return wordList.contains(word);
}
public void addWord(String word) {
if (containsWord(word)) return;
wordList.add(word);
wordArea.append(" " +word+"\n");
addPoints(pointsForWord(word));
wordArea.paintImmediately(wordArea.getVisibleRect());
}
/**
* Define how many points a player gets for a given word.
*/
public int pointsForWord(String word) {
return word.length() - BoggleGUI.MINIMUMWORDLENGTH + 1;
}
public void addPoints(int points) {
playerScore += points;
scoreText.setText(playerScore+"");
scoreText.paintImmediately(scoreText.getVisibleRect());
}
public void resetScore() {
playerScore = 0;
scoreText.setText(playerScore+"");
scoreText.paintImmediately(scoreText.getVisibleRect());
}
} // class ScoreArea
class WordEntryField extends JPanel {
private JTextField textField;
public WordEntryField() {
//Set up for human player's Text Entry Field
textField = new JTextField(30);
//Add listener to text entry field
textField.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e) {
checkAndAddWordHuman(textField.getText());
}});
this.add(new JLabel("Enter word: "));
this.add(textField);
setUnready();
}
public void clear() {
textField.setText("");
}
public void setUnready() {
clear();
textField.setEditable(false);
paintImmediately(getVisibleRect());
// attempt to give up focus to top-level frame
BoggleGUI.this.requestFocus();
}
public void setReady() {
textField.setEditable(true);
textField.requestFocus();
}
} // class WordEntryField
/**
* The entry point for the BoggleGUI application.
* Usage:
* java BoggleGUI [ wordfile [ rows [ columns [ minwordlength ]]]]
*/
public static void main(String args[] ) {
if(args.length > 0) WORDLISTFILENAME = args[0];
if(args.length > 1) DICEROWS = Integer.parseInt(args[1]);
if(args.length > 2) DICECOLS = Integer.parseInt(args[2]);
if(args.length > 3) MINIMUMWORDLENGTH = Integer.parseInt(args[3]);
System.err.println("Starting " + DICEROWS + "x" + DICECOLS + " game," +
" words from " + WORDLISTFILENAME +
", min word length " + MINIMUMWORDLENGTH + ".");
(new BoggleGUI()).setVisible(true);
}
} //class BoggleGUI