/*
* Created on 2004-7-27
*/
package boggle.controller;
import java.util.List;
import java.util.*;
import javax.swing.*;
import java.awt.event.*;
import boggle.view.*;
import boggle.model.*;
import boggle.controller.*;
import mvc143.*;
/**
* This interface provides the methods for overall control of a Boggle Game.
* For CSE143, the concrete Controller classes must be located in
* boggle.controller.BoggleController.java
*
* The constructor must take NO parameters.
* The constructor must create the Model and View.
*
* The class must have a main method which simply creates an instance of
* the Controller. After that, once the View is in execution, things
* should run on their own. There are no defined command-line arguments for main.
*
IMPORTANT NOTE #1! :
* Many of the methods are simply "pass-throughs" to the Model. Don't keep
* duplicate information in both modules. For example: what characters are
* on the board; how many rows and columns there are, etc. all are part of
* the Model, even though this Controller interface has methods which allow the
Controller
* to be queried for them. Before implementing any Controller methods, it
* is recommended that you be thoroughly familiar with what is in the other
* two modules to avoid duplication.
*
* The necessary resource files (if any), like dictionary and boards,
* are recommended to be located in the directory of the controller.
*
* IMPORTANT NOTE #2 :
* You must define these two public static fields to specify the author and a
brief description.
* as follows:
* public static final String author = " ... ";
* public static final String description = " ... ";
*
"authors" should identify the authors in some fashion, but pseudonyms or
nicknames are acceptable. "description" should give a brief description
of the module, especially if there are any unexpected or unique features
about it. The information in both fields may be visible to the general
public, indefinitely into the future, so do not place any information here
which you do not wish to be publicly available.
* @author xm
*
*/
public class BoggleController implements mvc143.IBoggleController{
private String proposal;
private BoggleModel currentModel;
private IBoggleView currentView;
private long gametime;
private long currenttime;
private javax.swing.Timer timer;
private boolean isRunning;
private ArrayList previousRows;
private ArrayList previousCols;
public static final String author = "Tom and Jing";
public static final String description = "Controller module aka God Module. Coordinates the whole program.";
public static void main(String[] args){
BoggleController bc = new BoggleController();
bc.currentModel = new BoggleModel("4x4board.txt", "JJonathanDictionary.txt", 4, 4);
}
public BoggleController(){
proposal = "";
isRunning = false;
previousRows = new ArrayList();
previousCols = new ArrayList();
gametime = 3*60*1000;
currentModel = new BoggleModel("36g 24u45un this is garbage used to pop up file chooser2b62.txt",
"36g 24u45un this is garbage used to pop up file chooser2b62.txt",
4, 4);
currentView = new BoggleView(this);
currentView.redraw();
}
/**
* This method will add the letter specified by row and col to the proposal
string.
* It will check if the position is valid or not before adding.
* @param row the row from which the letter comes.
* @param col the column from which the letter comes.
* @return true, if the letter is successfully added;
* false, otherwise .
*/
public boolean addToProposal(int row, int col){
if (isRunning){
try{
int lastRow = ( (Integer) previousRows.get(0) ).intValue();
int lastCol = ( (Integer) previousCols.get(0) ).intValue();
if( previousRows.size()==0 || Math.abs(lastRow - row) ==1 || Math.abs(lastCol - col) == 1){
char tempLetter = (char)getLetterInBoard(row, col);
if ( tempLetter == -1 ){
return false;
} else{
proposal = proposal + tempLetter;
List position = new ArrayList();
position.add(0,new Integer(col) );
position.add(0,new Integer(row) );
if (currentView != null){
currentView.redraw();
}
return true;
}
}else{
return false;
}
}
catch (Exception e){
return false;
}
}else{
return false;
}
}
/**
* Remove the last character in the proposed word.
* @return true, if succeessful,
* false, if not successful, or of the remove operation
*is not suported by the implemention.
*/
public boolean removeLetterFromProposal(){
return false;
/*if(proposal != null || proposal.length() >=1){
proposal = proposal.substring(0,proposal.length()-1);
previousRows.remove(0);
previousCols.remove(0);
return true;
}else{
return false;
}*/
}
/**
* When the user submits the proposed word,
* this method will check if the word is valid or not.
* The score will be updated then.
* @return -1 means the word was invalid. All other return values should
* indicate success, but no meaning is defined for them.
*/
public int submitProposal(){
int points = 0;
if ( isRunning && ( currentModel.getWordsModel() ).isValidWord(proposal) == 0 ){
currentModel.getWordsModel().addToHistory(proposal);
previousRows.clear();
previousCols.clear();
if (proposal.length() >= 8){
points = 11;
}else{
if (proposal.length() == 7){
points = 5;
}else{
if (proposal.length() == 6){
points = 3;
}else{
if (proposal.length() == 4){
points = 2;
}else{
points = 1;
}
}
}
}
//NO. OF LETTERS: 3 4 5 6 7 8or more
//POINTS: 1 1 2 3 5 11
currentModel.addScore(points);
if (currentView != null){
currentView.redraw();
}
newProposal();
return currentModel.getScore();
}else{
newProposal();
return -1;
}
}
/**
*The current word proposal will be cleared.
*/
public void newProposal(){
proposal = "";
previousRows.clear();
previousCols.clear();
if (currentView != null){
currentView.redraw();
}
}
/**
* Determine if the current word proposal is empty or not, i.e., whether
* any letters have been added to it.
* It may or may not be useful in your implementation, but keep it for someone's
need.
*@return true if the proposal is empty
* false otherwise
*/
public boolean isNewProposal(){
return !proposal.equals("");
}
/**
* Get the number of columns in the board.
* @return the cols of the boggle board
*/
public int getBoardCols(){
return currentModel.getCols();
}
/**
* get the number of rows in the board.
* @return the rows of the boggle board
*/
public int getBoardRows(){
return currentModel.getRows();
}
/**
* return the character at the position specified by row and col. (Implement
* this by asking the model for the information).
* @param row an integer specifying the row in the board
* @param col an integer specifying the col in the board
* @return the character, or
* -1, if the position is not valid
*/
public int getLetterInBoard(int row, int col){
if(isRunning){
return (int)currentModel.getLetter(row, col);
}else{
return -1;
}
}
/**
* Set the time duration of one game. If no time is ever set, then the
* default of three minutes (as specified by official Boggle rules)
* should be used.
* @param gametime the long type integer to specify the duration in milliseconds.
*/
public void setGameTime(long gametime){
this.gametime = gametime;
}
/**
* Get the total time duration of a game in milliseconds.
* @return a long type integer to specify the time duration of one game.
*/
public long getGameTime(){
return gametime;
}
/**
* Get the remaining left in the current game.
* @return the time remained for one game in milliseconds, if a game is in
* progress; return 0 if no game is in progress.
*/
public long getMillisRemaining(){
if(!isRunning){
return 0;
}else{
ActionListener[] al = timer.getActionListeners();
BoggleTimerListener btl = (BoggleTimerListener) al[0];
return btl.getTimeRemaining();
}
}
/**
* Start the timer, if the timer is stopped
* (create a new timer and start it, if there wasn't already a timer)
*/
public void startTimer(){
BoggleTimerListener boggleTL = new BoggleTimerListener(gametime);
timer = new javax.swing.Timer(1000, boggleTL);
timer.start();
isRunning = true;
if (currentView != null){
currentView.redraw();
}
}
/**
* Start a new game, with a new internally generated board.
* Starting a game may involve inititalizing or reinitializing
* various other parameters and features of the board.
*/
public void startGame(){
currentModel.createBoard(null);
(currentModel.getWordsModel() ).emptyHistory();
newProposal();
startTimer();
if (currentView != null){
currentView.redraw();
}
}
/**
* Start a new game, intializing the board from the file given.
* Starting a new game may also involve inititalizing or reinitializing
* various other parameters and features of the game.
* If the fileID is null or the file read fails, leave the board unchanged,
* supposing we have already have one.
*@param fileID the location of the board to use.
*/
public void startGame(String fileID){
currentModel.createBoard(fileID);
currentModel.resetScore();
( (IBoggleWordsModel) currentModel.getWordsModel()).emptyHistory();
newProposal();
startTimer();
if (currentView != null){
currentView.redraw();
}
}
/**
* End the current running game.
*
*/
public void endGame(){
isRunning = false;
if (currentView != null){
currentView.redraw();
}
}
/**
* This method will tell if the game is ended or not.
* Typically when the time is up, the game should be ended by controller
* automatically;
* everything will be stopped and waiting for start again
* @return return true, if the game is still running
* false, if a game is not in progress.
*/
public boolean isRunning(){
return isRunning;
}
/**
* Get the score for the current game.
* @return the current score
*/
public int getScore(){
return currentModel.getScore();
}
/**
* Return a message which this components wants to be visible to the user.
* It is strongly encouraged that this message contain the current status
* of the proposal (for example, the proposal itself; a message indicating
* rejection or acceptance if the proposal was recently submitted, etc.)
* For example, the View might choose to periodically display the message.
* @return a string containing the status message
*/
public String getStatus(){
String message = "Current proposal: " + proposal;
return message;
}
/**
* Get the words history from the Model.
* The idea of "history" is to let the model keep track of all the words
* the user has already got correct in the current game.
* @return a list of history information; if there is no history or a history
* is simly not implemented, should return empty List; should not return null.
*
*/
public List getWordsHistory(){
return (currentModel.getWordsModel()).getHistory();
}
/**
* Load a dictionary from a resource (file).
* @param fileID a string to specify the place to load the resource from.
* @return true if succeed
* false if not succeed or no file specified
*/
public boolean loadDictionary(String fileID){
return currentModel.getWordsModel().loadDictionary(fileID);
}
/**
* Add new words to dictionary without changing what
* was already there.
* Matching of words from
* the dictionary is supposed to be case-insensitive. Although this
* method is not responsible for matching, it may wish to convert all words
* to upper case before storing them.
* @param words an array of strings to store in the dictionary
*/
public void addToDictionary(String[] words){
currentModel.getWordsModel().addToDictionary(words);
}
/** Terminate the application. Among other things,
* call the terminate methods of the Model and the View so that they can
* perform any required clean-up.
*
*/
public void terminateApplication(){
currentModel.terminate();
if (currentView != null){
currentView.redraw();
}
}
class BoggleTimerListener implements ActionListener{
private long timeLeft;
public BoggleTimerListener(long time){
timeLeft = time;
}
public void actionPerformed(ActionEvent e){
timeLeft = timeLeft - 1000;
if (currentView != null){
currentView.redraw();
}
if ( timeLeft <= 0){
endGame();
}
}
public long getTimeRemaining(){
return timeLeft;
}
}
}