package com.github.donkirkby.vograbulary.anagrams;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import com.github.donkirkby.vograbulary.ultraghost.WordList;
/**
* Manage the state of a game, including the state of the board, current word,
* previously played words, and player scores.
*
* An AnagramsGameModel provides the data that is rendered on an AnagramsBoard
* object, and accepts user input via an AnagramsBoard object.
*
* @author Don Kirkby
*/
public class AnagramsGameModel implements Serializable {
private static final long serialVersionUID = 346139871345262525L;
// private static final int NUMBER_OF_LETTERS_IN_DECK = 50;
// private static final int NUMBER_OF_VOWELS_IN_DECK = 10;
// private static final String CONSONANTS = "BCDFHJKLMNPQRSTVWXYZ";
// private static final String VOWELS = "AEIOU";
private WordList wordList;
private LetterSet letterSet;
private HashMap<String, AnagramsPlayer> wordOwners;
private HashSet<String> playedWords;
private ArrayList<AnagramsPlayer> players;
/**
* Set the deck of letters to a given list. Resets the game state.
* @param deck
*/
public void setDeck(String deck) {
letterSet = new LetterSet(deck);
wordOwners = new HashMap<String, AnagramsPlayer>();
players = new ArrayList<AnagramsPlayer>();
playedWords = new HashSet<String>();
}
/**
* Reveal the next letter from the deck.
* @return the letter
*/
public char revealLetter() {
return letterSet.showNextLetter();
}
/**
* Get the list of currently unclaimed letters. Letters are added to this
* list by calling revealLetter() and removed by calling makeWord()
* or changeWord().
* @return
*/
public String getUnclaimedLetters() {
return letterSet.getVisibleLetters();
}
/**
* Add a player to the game.
* @param player The player to add.
*/
public void addPlayer(AnagramsPlayer player) {
players.add(player);
}
/**
* Get all the players that were added by addPlayer().
* @return a list of players.
*/
public List<AnagramsPlayer> getPlayers() {
return players;
}
/**
* Get all words claimed by a player.
* @param player The player who owns the words.
* @return A list of words.
*/
public List<String> getWords(AnagramsPlayer player) {
ArrayList<String> words = new ArrayList<String>();
for (Entry<String, AnagramsPlayer> entry : wordOwners.entrySet()) {
if (entry.getValue() == player) {
words.add(entry.getKey());
}
}
return words;
}
/**
* Make a word from unclaimed letters, and assign it to a player.
* @param word The word to make.
* @param player The player claiming the word.
* @throws InvalidWordException When word is not in the dictionary, or
* cannot be made from unclaimed letters.
*/
public void makeWord(String word, AnagramsPlayer player) throws InvalidWordException {
validateWord(word);
for (int i = 0; i < word.length(); i++) {
char letter = word.charAt(i);
if ( ! letterSet.reserveLetter(letter)) {
letterSet.releaseReservedLetters();
throw new InvalidWordException(String.format(
"The letter %c is not available to make %s.",
letter,
word));
}
}
letterSet.hideReservedLetters();
playedWords.add(word);
player.setScore(player.getScore() + word.length());
wordOwners.put(word, player);
}
private void validateWord(String word) throws InvalidWordException {
if (word.length() < 4) {
throw new InvalidWordException(
"Words must be at least 4 letters long.");
}
if (playedWords.contains(word)) {
throw new InvalidWordException(String.format(
"%s has already been played.",
word));
}
if ( ! wordList.contains(word)) {
throw new InvalidWordException(String.format(
"%s is not in the dictionary.",
word));
}
}
public WordList getWordList() {
return wordList;
}
public void setWordList(WordList mWordFinder) {
this.wordList = mWordFinder;
}
/**
* Make a new word from a claimed word, and assign it to a player.
* @param oldWord The existing word.
* @param newWord The word to make.
* @param player The player claiming the word.
* @throws InvalidWordException When oldWord has not been claimed, newWord
* is not in the dictionary or newWord is not makeable from all the
* letters in oldWord, plus zero or more unclaimed letters.
*/
public void changeWord(
String oldWord,
String newWord,
AnagramsPlayer player) throws InvalidWordException {
validateWord(newWord);
AnagramsPlayer oldPlayer = wordOwners.get(oldWord);
if (oldPlayer == null) {
throw new InvalidWordException(String.format(
"%s is not a claimed word.",
oldWord));
}
LetterSet oldSet = new LetterSet(oldWord);
oldSet.showAllRemaining();
try
{
for (int i = 0;
i < newWord.length(); i++) {
char letter = newWord.charAt(i);
if ( ! oldSet.reserveLetter(letter) &&
! letterSet.reserveLetter(letter)) {
throw new InvalidWordException(String.format(
"%s is not available to make %s.",
letter,
newWord));
}
}
oldSet.hideReservedLetters();
if (oldSet.getVisibleLetters().length() > 0) {
throw new InvalidWordException(String.format(
"Some letters of %s were not used in %s.",
oldWord,
newWord));
}
letterSet.hideReservedLetters();
playedWords.add(newWord);
wordOwners.put(newWord, player);
wordOwners.remove(oldWord);
}
finally {
letterSet.releaseReservedLetters();
}
player.setScore(player.getScore() + newWord.length());
oldPlayer.setScore(oldPlayer.getScore() - oldWord.length());
}
public boolean isDeckEmpty() {
return letterSet.getRemainingCount() <= 0;
}
}