package pdp.scrabble.game.impl;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import org.jdom.Element;
import pdp.scrabble.Factory;
import pdp.scrabble.Main_old;
import pdp.scrabble.game.Bag;
import pdp.scrabble.game.Board;
import pdp.scrabble.game.BoardCase;
import pdp.scrabble.game.BoardCaseState;
import pdp.scrabble.game.Dictionary;
import pdp.scrabble.game.GameEnvironment;
import pdp.scrabble.game.Letter;
import pdp.scrabble.game.Player;
import pdp.scrabble.game.Rack;
import pdp.scrabble.game.exception.BoardWrongWordPlace;
import pdp.scrabble.utility.Debug;
import pdp.scrabble.utility.Display;
import static pdp.scrabble.game.BoardCaseState.FREE;
import static pdp.scrabble.game.BoardCaseState.NEW;
import static pdp.scrabble.game.BoardCaseState.OLD;
import static pdp.scrabble.game.BoardCaseIndex.FIRST;
import static pdp.scrabble.game.BoardCaseIndex.LAST;
import static pdp.scrabble.game.BoardCaseIndex.NONE;
import static pdp.scrabble.Game.SCRABBLE_PTS;
import static pdp.scrabble.Factory.FACTORY;
public class BoardImpl extends Observable implements Board {
/** Memory representation of the board. */
private BoardCase[][] map = null;
/** Dictionary reference. */
private Dictionary dictionary = null;
/** Center used state. */
private boolean centerUsed = false;
/** Last total points from formed words. */
private int points = 0;
/** Last length from a word. */
private int length = 0;
/** AutoSave flag. */
private boolean isAutoSave = false;
/** Last list of formed words. */
private List<String> words = null;
/** List of checked cases. */
private List<BoardCase> checkedCases = null;
/** List of new cases, addeed by the player recently. */
private List<BoardCase> newCases = null;
/** Valide state, use for AI board check (faster than throw exception). */
private boolean valide = false;
/** AI flag, to know if board called by an ai. */
private boolean isAI = false;
/** Number of dropped letters. */
private int droppedLetters = 0;
/** List of board observer*/
private ArrayList<Observer> listObs = new ArrayList<Observer>(1);
public BoardImpl() {
// This offset (3) is used to avoid board border error
// when checking letters (in order to skip critical test)
this.map = new BoardCase[VERT_DIM + 3][HORI_DIM + 3];
this.centerUsed = false;
this.points = 0;
this.length = 0;
this.words = new ArrayList<String>(1);
this.checkedCases = new ArrayList<BoardCase>(4);
this.newCases = new ArrayList<BoardCase>(7);
this.droppedLetters = 0;
this.loadConfigBoard(Main_old.RESSOURCES_PATH + "board.txt");
// Allocate secure cases (to avoid critical test,
// in order to check placement as fast as possible)
for (int v = 0; v < VERT_DIM + 3; v++) {
for (int h = 0; h < HORI_DIM + 3; h++) {
if (this.map[v][h] == null) {
this.map[v][h] = FACTORY.createBoardCase(0, 0, v, h);
}
}
}
}
private void loadConfigBoard(String cfgBoard) {
int letterMult = 1, wordMult = 1;
int mult, h = 0, v = 0;
try {
BufferedReader file = new BufferedReader(new FileReader(cfgBoard));
String line = file.readLine();
// Read data lines
while (line != null) {
for (h = 0; h < HORI_DIM; h++) {
mult = Integer.parseInt(String.valueOf(line.charAt(h + 1)));
// Normal case
if (mult == 0) {
letterMult = 1;
wordMult = 1;
} // Double letter case
else if (mult == 1) {
letterMult = 2;
wordMult = 1;
} // Triple letter case
else if (mult == 2) {
letterMult = 3;
wordMult = 1;
} // Double word case
else if (mult == 3) {
letterMult = 1;
wordMult = 2;
} // Triple word case
else if (mult == 4) {
letterMult = 1;
wordMult = 3;
}
// Assign case
this.setCase(v, h, FACTORY.createBoardCase(
letterMult, wordMult, v, h));
}
// Next line until last column
if (v < VERT_DIM - 1) {
line = file.readLine();
v++;
}
else {
break;
}
}
file.close();
}
catch (FileNotFoundException e) {
Display.error("Load board configuration", "File not found:",
" " + cfgBoard);
}
catch (IOException e) {
Display.error("Load board configuration", "Can't read datas:",
" " + cfgBoard);
}
}
public void setCase(int vertIndex, int horiIndex, BoardCase boardCase) {
this.map[vertIndex + 1][horiIndex + 1] = boardCase;
}
public BoardCase getCase(int vertIndex, int horiIndex) {
return this.map[vertIndex + 1][horiIndex + 1];
}
public void setCaseLetter(
int vertIndex, int horiIndex, Letter letter, boolean add) {
BoardCase boardCase = this.getCase(vertIndex, horiIndex);
boardCase.setLetter(letter);
boardCase.setState(NEW);
if (add) {
this.newCases.add(boardCase);
}
}
private void setCaseState(int vertIndex, int horiIndex, BoardCaseState bcs) {
this.getCase(vertIndex, horiIndex).setState(bcs);
}
public void resetCase(int vertIndex, int horiIndex) {
BoardCase boardCase = this.getCase(vertIndex, horiIndex);
boardCase.setLetter(null);
boardCase.setState(FREE);
boardCase.setAdjacentIgnorence(false);
boardCase.setChecked(false);
boardCase.setMultUsed(false);
boardCase.setIndex(NONE);
}
public void removeCaseNew(BoardCase newCase) {
int len = this.newCases.size();
for (int i = 0; i < len; i++) {
if (this.newCases.get(i) == newCase) {
this.newCases.remove(i);
break;
}
}
}
public int numberOfNewCases() {
return this.newCases.size();
}
public void validate(boolean confirm) throws BoardWrongWordPlace {
this.isAI = false;
try {
this.checkBoardRules();
this.resetCheckedStates();
}
catch (BoardWrongWordPlace bwwp) {
this.resetCheckedStates();
throw new BoardWrongWordPlace(bwwp.getError(), bwwp.getInfo());
}
// Validate all new case and set their flag to old
if (confirm) {
for (int v = 0; v < VERT_DIM; v++) {
for (int h = 0; h < HORI_DIM; h++) {
if (this.getCase(v, h).getState() == NEW) {
this.setCaseState(v, h, OLD);
this.droppedLetters++;
if (!this.listObs.isEmpty()) {
this.notifyObservers(this.getCase(v, h));
}
}
}
}
if (!this.isAutoSave()) {
this.newCases.clear();
}
}
}
public boolean validateAI() {
boolean state = false;
try {
this.isAI = true;
state = this.checkBoardRules();
}
catch (BoardWrongWordPlace ex) {
}
this.resetCheckedStates();
return state;
}
public void prepareTurn() {
this.words.clear();
this.setWordPoints(0);
}
/** Check if word is well placed before validate.
* @throws BoardWrongWordPlace exception thrown if word is on a wrong place.
*/
private boolean checkBoardRules() throws BoardWrongWordPlace {
// Number of new elements
this.valide = true;
int casesNum = this.newCases.size();
this.setWordLength(casesNum);
this.prepareTurn();
// Has new letter ?
if (casesNum > 0) {
// Ensure center case is used
this.checkCenterRules();
// Letters must be aligned (return alignement)
boolean isVert = this.checkAlignementRules(this.newCases, casesNum);
// Assign indexs
// (determin if letter is FIRST, MIDDLE, or LAST in the word)
this.assignIndex(this.newCases, casesNum, isVert);
// Check if word has unified letters, without blank space
// (check from first to last index)
this.checkWordUnifiedRules(this.newCases, casesNum, isVert);
// Word must hit another old one
this.checkWordHitRules(this.newCases, casesNum);
// Apply scrabble bonus
if (casesNum == Rack.MAX_RACK_LETTERS) {
this.points += SCRABBLE_PTS;
}
// AI ignore thrown exception, using boolean will be faster
if (this.isAI) {
if (!this.valide) {
return false;
}
}
}
this.resetCheckedStates();
return true;
}
/** Determin letters index (is it FIRST, MIDDLE, or LAST).
* @param newCases list of new cases.
* @param numberOfNewCases number of new cases.
* @param isVertical true if vertical, false else.
*/
private void assignIndex(
List<BoardCase> newCases, int newCasesNum, boolean isVertical) {
int min = newCases.get(0).getH(), minID = 0;
int max = min, maxID = 0;
if (isVertical) {
min = newCases.get(0).getV();
max = min;
}
// Check all new letters (get min and max index)
for (int i = 1; i < newCasesNum; i++) {
int result = newCases.get(i).getH();
if (isVertical) {
result = newCases.get(i).getV();
}
if (min > result) {
min = result;
minID = i;
}
if (max < result) {
max = result;
maxID = i;
}
}
// Update index
newCases.get(minID).setIndex(FIRST);
newCases.get(maxID).setIndex(LAST);
}
/** Check if the center case is used.
* @throws BoardWrongWordPlace center uses rules violation.
*/
private void checkCenterRules() throws BoardWrongWordPlace {
if (!this.centerUsed) {
if (this.getCase(VERT_DIM / 2, HORI_DIM / 2).getState() == FREE) {
this.centerUsed = false;
if (this.isAI) {
this.valide = false;
return;
}
else {
throw new BoardWrongWordPlace("Word must use center case !");
}
}
else {
this.centerUsed = true;
}
}
}
/** Check if all new cases are aligned on the same axis.
* @param newCases list of new cases.
* @param newCasesNum number of new cases on board.
* @return false if alignement is vertical, true if it is horizontal.
* @throws BoardWrongWordPlace alignement rules violation.
*/
private boolean checkAlignementRules(
List<BoardCase> newCases, int newCasesNum)
throws BoardWrongWordPlace {
int vertTest = newCases.get(0).getV();
int horiTest = newCases.get(0).getH();
int vertCount = 1, horiCount = 1;
// Check all cases
for (int i = 1; i < newCasesNum; i++) {
BoardCase boardCase = newCases.get(i);
if (boardCase.getV() == vertTest) {
vertCount++;
}
if (boardCase.getH() == horiTest) {
horiCount++;
}
}
// One or more are not aligned on the same axis
if (vertCount != newCasesNum && horiCount != newCasesNum) {
if (this.isAI) {
this.valide = false;
return false;
}
else {
throw new BoardWrongWordPlace("Letters must be aligned !");
}
}
// Test for just one letter
if (newCasesNum == 1) {
if (this.getCase(vertTest + 1, horiTest).getState() == OLD
|| this.getCase(vertTest - 1, horiTest).getState() == OLD) {
return true;
}
else {
return false;
}
}
// Get axis result (will be used on future check)
if (vertCount == newCasesNum) {
// All letters are vertically aligned,
// so the word is on the horizontal axis
return false;
}
else {
// All letters are horizontaly aligned,
// so the word is on the vertical axis
return true;
}
}
/** Check if the formed word has unified letters, without blank spaces.
* @param newCases list of new cases.
* @param numberOfNewCases number of new cases on board.
* @param isVert true if its for vertical axis check, false for horizontal.
* @throws BoardWrongWordPlace ununified letters rules violation.
*/
private void checkWordUnifiedRules(
List<BoardCase> newCases, int numberOfNewCases, boolean isVert)
throws BoardWrongWordPlace {
BoardCase boardCase = null;
int first = 0, last = 0;
int firstP = 0, lastP = 0;
int axis = 0;
// Get extremity index
for (int i = 0; i < numberOfNewCases; i++) {
boardCase = newCases.get(i);
if (boardCase.getIndex() == FIRST) {
first = i;
}
if (boardCase.getIndex() == LAST) {
last = i;
}
}
// Check unification
if (isVert) {
axis = newCases.get(first).getH();
firstP = newCases.get(first).getV();
lastP = newCases.get(last).getV();
for (int v = firstP; v <= lastP; v++) {
if (this.getCase(v, axis).getState() == FREE) {
if (this.isAI) {
this.valide = false;
return;
}
else {
throw new BoardWrongWordPlace(
"Word letters must be verticaly unified !");
}
}
}
}
else {
axis = newCases.get(first).getV();
firstP = newCases.get(first).getH();
lastP = newCases.get(last).getH();
for (int h = firstP; h <= lastP; h++) {
if (this.getCase(axis, h).getState() == FREE) {
if (this.isAI) {
this.valide = false;
return;
}
else {
throw new BoardWrongWordPlace(
"Word letters must be horizontaly unified !");
}
}
}
}
this.checkWordExistence(isVert, axis, firstP, lastP);
}
/** Check if new word hit an old one.
* @param newCases list of new cases.
* @param numberOfNewCases number of new cases on board.
* @throws BoardWrongWordPlace word hit rules violation.
*/
private void checkWordHitRules(List<BoardCase> newCases, int newCasesNum)
throws BoardWrongWordPlace {
BoardCase bCase = null;
int numberOfHit = 0;
// Check all cases
for (int i = 0; i < newCasesNum; i++) {
bCase = newCases.get(i);
if (this.caseJoinedTo(bCase.getV(), bCase.getH(), true, OLD)
|| this.caseJoinedTo(bCase.getV(), bCase.getH(), false, OLD)) {
numberOfHit++;
}
}
// No hit (only if it is not the first word placed)
if (numberOfHit == 0) {
if (this.getCase(VERT_DIM / 2, HORI_DIM / 2).getState() == OLD) {
if (this.isAI) {
this.valide = false;
return;
}
else {
throw new BoardWrongWordPlace(
"New word must be joined to an other one !");
}
}
}
}
/** Check word existence.
* @throws BoardWrongWordPlace word existence rules exception.
*/
private void checkWordExistence(
boolean isVertical, int axis, int first, int last)
throws BoardWrongWordPlace {
StringBuilder word = new StringBuilder("");
BoardCase boardCase = null;
List<Letter> jokers = new ArrayList<Letter>(1);
int firstP = first, lastP = last;
// Get complete word
if (isVertical) {
while (this.getCase(firstP - 1, axis).getState() != FREE) {
firstP--; // Top index
}
while (this.getCase(lastP + 1, axis).getState() != FREE) {
lastP++; // Down index
}
// Current word letter
for (int v = firstP; v <= lastP; v++) {
Letter letter = this.getCase(v, axis).getLetter();
word = word.append(letter.getName());
if (letter.getName() == Bag.JOKER) {
jokers.add(letter);
}
}
}
else {
while (this.getCase(axis, firstP - 1).getState() != FREE) {
firstP--; // Left index
}
while (this.getCase(axis, lastP + 1).getState() != FREE) {
lastP++; // Right index
}
// Current word letter
for (int h = firstP; h <= lastP; h++) {
Letter letter = this.getCase(axis, h).getLetter();
word = word.append(letter.getName());
if (letter.getName() == Bag.JOKER) {
jokers.add(letter);
}
}
}
// Apply joker char to use (if has and found)
int numOfJoker = jokers.size();
if (numOfJoker > 0) {
Character[] joker = this.tryJokers(word);
jokers.get(0).setJokerChar(joker[0]);
if (numOfJoker == 2) {
jokers.get(1).setJokerChar(joker[1]);
}
jokers.clear();
jokers = null;
joker = null;
}
// Check existence
if (!this.dictionary.contains(word)) {
if (this.isAI) {
this.valide = false;
return;
}
else {
throw new BoardWrongWordPlace(
"This word doesn't exist:", " " + word);
}
}
// If word exists, calculate points
int score = this.calculateWordPoints(isVertical, axis, firstP, lastP);
Debug.console(
"checkWordExistence", "word = ", word + " | score = " + score);
this.words.add(word.toString());
this.points += score;
// Check now adjacents words (for each word letters)
if (isVertical) {
for (int v = firstP; v <= lastP; v++) {
boardCase = this.getCase(v, axis);
// Letter has adjacent letters (right or left)
if (!boardCase.getAdjacentIgnorence() && !boardCase.isChecked()
&& boardCase.getState() == NEW) {
if (this.getCase(v, axis - 1).getState() == OLD
|| this.getCase(v, axis + 1).getState() == OLD) {
boardCase.setChecked(true);
this.checkedCases.add(boardCase);
this.checkWordExistence(!isVertical, v, axis, axis);
}
}
}
}
else {
for (int h = firstP; h <= lastP; h++) {
boardCase = this.getCase(axis, h);
// Letter has adjacent letters (up or down)
if (!boardCase.getAdjacentIgnorence() && !boardCase.isChecked()
&& boardCase.getState() == NEW) {
if (this.getCase(axis - 1, h).getState() == OLD
|| this.getCase(axis + 1, h).getState() == OLD) {
boardCase.setChecked(true);
this.checkedCases.add(boardCase);
this.checkWordExistence(!isVertical, h, axis, axis);
}
}
}
}
}
/** Try joker, returning characters used for the joker.
* @param word input word
* @return joker chars to use.
*/
private Character[] tryJokers(StringBuilder word) {
int len = word.length();
int j1 = -1, j2 = -1;
Character[] jokerChar = new Character[2];
jokerChar[0] = Bag.JOKER;
jokerChar[1] = Bag.JOKER;
// Search jokers indexs
for (int i = 0; i < len; i++) {
if (word.charAt(i) == Bag.JOKER) {
if (j1 == -1) {
j1 = i;
}
else if (j2 == -1) {
j2 = i;
}
}
}
// Replace joker using all possibilities
int chars = Bag.AVAILABLE_LETTERS.length;
for (int c1 = 0; c1 < chars; c1++) {
word.setCharAt(j1, Bag.AVAILABLE_LETTERS[c1]);
// Has 2 joker ?
if (j2 > -1) {
for (int c2 = 0; c2 < chars; c2++) {
word.setCharAt(j2, Bag.AVAILABLE_LETTERS[c2]);
if (this.dictionary.contains(word)) {
jokerChar[0] = Bag.AVAILABLE_LETTERS[c1];
jokerChar[1] = Bag.AVAILABLE_LETTERS[c2];
return jokerChar;
}
}
} // Has 1 joker ?
else {
if (this.dictionary.contains(word)) {
jokerChar[0] = Bag.AVAILABLE_LETTERS[c1];
return jokerChar;
}
}
}
// Nothing worked, reset joker(s)
if (j1 > -1) {
word.setCharAt(j1, Bag.JOKER);
}
if (j2 > -1) {
word.setCharAt(j2, Bag.JOKER);
}
return jokerChar;
}
/** Check if the new case is joined to another case.
* @param v vertical position.
* @param h horizontal position.
* @param isVertical false for vertical check, true for horizontal check.
* @param state letter state.
* @return true if case is joined, false else.
*/
private boolean caseJoinedTo(
int v, int h, boolean isVertical, BoardCaseState state) {
// Check only one axis
int addV = 0, addH = 0;
if (isVertical) {
addV = 1;
}
else {
addH = 1;
}
// Check hit
if (this.getCase(v - addV, h - addH).getState() == state
|| this.getCase(v + addV, h + addH).getState() == state) {
return true;
}
return false;
}
/** Reset checked state after board validation. */
private void resetCheckedStates() {
Iterator<BoardCase> itr = this.checkedCases.iterator();
while (itr.hasNext()) {
itr.next().setChecked(false);
}
this.checkedCases.clear();
}
public void resetCheckMultiplicator() {
for (int v = 0; v < VERT_DIM; v++) {
for (int h = 0; h < HORI_DIM; h++) {
this.getCase(v, h).setMultUsed(false);
}
}
}
/** Calculate points of a word.
* @param isVert false for vertical check, true for horizontal check.
* @param axis
* @param first
* @param last
*/
private int calculateWordPoints(
boolean isVert, int axis, int first, int last) {
BoardCase boardCase = null;
Letter letter = null;
int wordMult = 1, score = 0, letterValue = 0;
// Check from first letter to last
for (int index = first; index <= last; index++) {
if (isVert) {
boardCase = this.getCase(index, axis);
}
else {
boardCase = this.getCase(axis, index);
}
letter = boardCase.getLetter();
letterValue = letter.getValue();
// Apply bonus if not already used
if (!boardCase.getMultUsed()) {
letterValue *= boardCase.getLetterMult();
wordMult *= boardCase.getWordMult();
boardCase.setMultUsed(true);
}
score += letterValue;
}
// Apply word multiplicator found
score *= wordMult;
return score;
}
public void clear() {
for (int v = 0; v < VERT_DIM; v++) {
for (int h = 0; h < HORI_DIM; h++) {
this.resetCase(v, h);
}
}
this.checkedCases.clear();
this.newCases.clear();
this.droppedLetters = 0;
}
public void cancel(Player player) {
BoardCase boardCase = null;
int len = this.newCases.size();
for (int i = 0; i < len; i++) {
boardCase = this.newCases.get(i);
// Give letter back to the player
boardCase.getLetter().setJokerChar(Bag.NON_JOKER);
if (player != null) {
player.getRack().addLetter(boardCase.getLetter());
}
// Remove the letter from the board
this.resetCase(boardCase.getV(), boardCase.getH());
}
this.newCases.clear();
}
public void switchCasesLetter(int v1, int h1, int v2, int h2) {
BoardCase case1 = this.getCase(v1, h1);
BoardCase case2 = this.getCase(v2, h2);
Letter letter1 = case1.getLetter();
Letter letter2 = case2.getLetter();
BoardCaseState state1 = case1.getState();
BoardCaseState state2 = case2.getState();
if (state1 == FREE) {
int len = this.newCases.size();
for (int i = 0; i < len; i++) {
BoardCase boardCase = this.newCases.get(i);
if (boardCase == case2) {
this.newCases.set(i, case1);
break;
}
}
}
this.setCaseLetter(v1, h1, letter2, false);
this.setCaseState(v1, h1, state2);
this.setCaseLetter(v2, h2, letter1, false);
this.setCaseState(v2, h2, state1);
}
public void setDictionary(Dictionary dictionary) {
this.dictionary = dictionary;
}
public void setWordPoints(int points) {
this.points = points;
}
public int getWordPoints() {
return this.points;
}
public void setWordLength(int length) {
this.length = length;
}
public int getWordLength() {
return this.length;
}
public List<String> getFormedWords() {
return Collections.unmodifiableList(this.words);
}
public void saveTurn(Element root) {
Element element = new Element("Board");
root.addContent(element);
Iterator<BoardCase> it = this.newCases.iterator();
while (it.hasNext()) {
BoardCase boardCase = it.next();
boardCase.save(element);
}
this.newCases.clear();
}
public int getNumberOfDroppedLetters() {
return this.droppedLetters;
}
public void save(Element root) {
Element element = new Element("Board");
root.addContent(element);
for (int v = 0; v < VERT_DIM; v++) {
for (int h = 0; h < HORI_DIM; h++) {
this.getCase(v, h).save(element);
}
}
}
public void load(Element root) {
int h, v = 0;
if (root != null) {
Element element = root.getChild("Board");
@SuppressWarnings("unchecked")
Iterator<Element> Eboard = element.getChildren().iterator();
while (Eboard.hasNext()) {
Element EboardCase = Eboard.next();
h = Integer.parseInt(EboardCase.getAttributeValue("h"));
v = Integer.parseInt(EboardCase.getAttributeValue("v"));
this.getCase(v, h).load(EboardCase);
}
}
}
public void addObserver(Observer o) {
this.listObs.add(o);
}
public void deleteObserver(Observer o) {
this.listObs.remove(o);
}
public void deleteObservers() {
this.listObs = new ArrayList<Observer>(1);
}
public void notifyObservers(Object arg) {
for (Observer obs : this.listObs) {
obs.update(this, arg);
}
}
public void setData(boolean centerUsed, int points,
int length, List<String> words) {
this.centerUsed = centerUsed;
this.points = points;
this.length = length;
this.words.clear();
Iterator<String> itr = words.iterator();
while (itr.hasNext()) {
this.words.add(itr.next());
}
}
public boolean isAutoSave() {
return this.isAutoSave;
}
public void setAutoSave(boolean autoSave) {
this.isAutoSave = autoSave;
}
public Board clone() {
Board board = FACTORY.createBoard();
board.setDictionary(this.dictionary);
board.setData(this.centerUsed, this.points, this.length, this.words);
for (int v = 0; v < VERT_DIM; v++) {
for (int h = 0; h < HORI_DIM; h++) {
BoardCase boardCase = this.getCase(v, h).clone();
board.setCase(v, h, boardCase);
}
}
return board;
}
public Board transpose() {
Board trans = Factory.FACTORY.createBoard();
for (int v=0; v < VERT_DIM ; v++)
for (int h=0; h < HORI_DIM; h++)
trans.setCase(h, VERT_DIM-v, this.getCase(v, h).clone());
return trans;
}
public boolean isFree(int h, int v) {
return this.getCase(v, h).getState() == BoardCaseState.FREE;
}
public int countLetter(char c)
{
int amount = 0;
for (int i = 0; i < HORI_DIM; i++)
for (int j = 0; j < VERT_DIM; j++)
if (!isFree(j, i) && (this.getCase(i, j).getLetter().getName() == c))
amount++;
return amount;
}
@Override
public void changeEnvironment(GameEnvironment env) {
this.dictionary = env.getDictionary();
}
}