package kakuro.server.tablefactory;
import java.util.HashMap;
import kakuro.server.ISkeletonTable;
import kakuro.table.ITable;
import kakuro.table.Position;
import kakuro.table.TableException;
/** Altalános ITable implementáció. A cellákat map-ben tárolja, a felhasználó által beírt értékeket
* egy kétdimenziós int tömbben. */
public class GeneralTable implements ITable {
/** tábla mögött álló skeleton */
private ISkeletonTable _skeleton = null;
/** felhasználó által beírt értékek */
private int[][] _matrix = null;
/** cellák */
private HashMap<Position,Cell> _cellMap;
/** Alap konstruktor, megjegyzi a kapott paramétereket.
*
* @param skeleton váz
* @param matrix valódi értékek
* @param user felhasználó által beírt értékek (ez mentésből jöhet pl.)
* */
public GeneralTable(ISkeletonTable skeleton, int[][] matrix, int[][] user) {
_skeleton = skeleton;
_matrix = matrix;
_cellMap = new HashMap<Position,Cell>();
init(user);
}
/** Alap konstruktor, megjegyzi a kapott paramétereket.
*
* @param skeleton váz
* @param matrix valódi értékek
* */
public GeneralTable(ISkeletonTable skeleton, int[][] matrix) {
_skeleton = skeleton;
_matrix = matrix;
_cellMap = new HashMap<Position,Cell>();
init(null);
}
/** Kiszámolja az összeg cellák értékét, és berakja őket a fekete cellákba. Felépíti a
* cella map-et is. */
private void init(int[][] user) {
int cols = getColumnCount();
int rows = getRowCount();
int[][] vSums = new int[cols][rows];
int[][] hSums = new int[cols][rows];
for (int i=cols-1; i>=0; i--) {
int vSum = 0;
for (int j=rows-1; j>=0; j--) {
if (_matrix[i][j]>0) {
vSum += _matrix[i][j];
} else {
if (vSum>0) {
vSums[i][j] = vSum;
vSum = 0;
}
}
}
}
for (int j=rows-1; j>=0; j--) {
int hSum = 0;
for (int i=cols-1; i>=0; i--) {
if (_matrix[i][j]>0) {
hSum += _matrix[i][j];
} else {
if (hSum>0) {
hSums[i][j] = hSum;
hSum = 0;
}
}
}
}
for (int i=0; i<cols; i++) {
for (int j=0; j<rows; j++) {
Position p = new Position(i, j);
if (_matrix[i][j]>0) {
_cellMap.put(p, new WhiteCell(p, _matrix[i][j], (user==null?0:user[i][j])));
} else {
_cellMap.put(p, new BlackCell(p, hSums[i][j], vSums[i][j]));
}
}
}
}
/** Oszlopszám getter */
public int getColumnCount() {
return _skeleton.getColumnCount();
}
/** Nehézségi szint getter
*
* @return {@link ITable#DIFFICULTY_EASY},{@link ITable#DIFFICULTY_MEDIUM},{@link ITable#DIFFICULTY_HARD}
* */
public int getDifficulty() {
return _skeleton.getDifficulty();
}
/** Sorszám getter */
public int getRowCount() {
return _skeleton.getRowCount();
}
/** Belső függvény, ellenőrzi, hogy legális-e a kapott pozíció
*
* @param p pozíció
* @throws IndexOutOfBoundsException ha nem legális
* */
private void checkIndex(Position p) throws IndexOutOfBoundsException {
if (p.x > (getColumnCount()-1) || p.y > (getRowCount()-1))
throw new IndexOutOfBoundsException("Indexing out! got " + p
+ " out of [" +getColumnCount()+","+getRowCount()+"]");
}
/** Adott helyhez tartozó összeg lekérő
*
* @param p pozíció
* @param type összeg típus: {@link ITable#SUM_HORIZONTAL},{@link ITable#SUM_VERTICAL}
* */
public int getSum(Position p, int type) throws TableException,
IndexOutOfBoundsException {
checkIndex(p);
try {
BlackCell c = (BlackCell)_cellMap.get(p);
return (type==ITable.SUM_HORIZONTAL?c.horizontalSum:c.verticalSum);
} catch (Throwable t) {
throw new TableException("getSum", t);
}
}
/** Kapott pozíción található cella típusát adja vissza.
*
* @param p pozíció
* @return típus, lehetséges értékek: {@link ITable#WHITE},{@link ITable#BLACK_BOTH},{@link ITable#BLACK_HORIZONTAL},
* {@link ITable#BLACK_NONE},{@link ITable#BLACK_VERTICAL}
* @throws IndexOutOfBoundsException ha a pozíció kívül esik a táblán
* */
public int getType(Position p) throws IndexOutOfBoundsException {
checkIndex(p);
return _cellMap.get(p).getType();
}
/**
* Jó-e az adott helyen levő cella tartalma. True, ha jó. False, ha rossz,
* vagy ha üres!
*
* @param p pozíció
* @return korrekt-e a cella tartalma
*/
public boolean isCorrect(Position p) throws IndexOutOfBoundsException {
checkIndex(p);
if (_cellMap.get(p) instanceof BlackCell) return true;
WhiteCell wc = (WhiteCell)_cellMap.get(p);
return wc.isCorrect();
}
/** Leellenőrzi az állást, true ha kész. Nem azt nézi, hogy egyezik-e a beírt érték az előre
* megadottal, hanem azt, hogy minden ki van-e töltve és az összeg stimmelnek-e!
*
* @return kész-e a tábla
* */
public boolean isReady() throws TableException {
for (int j=0; j<_matrix[0].length; j++) {
for (int i=0; i<_matrix.length; i++) {
Position p = new Position(i, j);
boolean r = true;
int type = getType(p);
switch (type) {
case ITable.WHITE:
case ITable.BLACK_NONE:
break;
case ITable.BLACK_BOTH:
r = checkVertical(p) && checkHorizontal(p);
break;
case ITable.BLACK_HORIZONTAL:
r = checkHorizontal(p);
break;
case ITable.BLACK_VERTICAL:
r = checkVertical(p);
break;
}
if (!r) return false;
}
}
return true;
}
/** Leellenőrzi, hogy az adott helytől lefelé eső fehér cellák kiadják-e a kívánt összeget, és mind ki van-e
* töltve.
*
* @param p fekete, függőleges összeget tartalmazó cella helye
* @return jó-e a szó
* */
private boolean checkVertical(Position p) throws TableException {
int sum = getSum(p, ITable.SUM_VERTICAL);
for (int j=p.y+1; j<_matrix[0].length; j++) {
int type = getType(new Position(p.x, j));
if (type!=ITable.WHITE) break;
int value = readCell(new Position(p.x, j), ITable.VALUE_NORMAL);
if (value<1) return false;
sum -= value;
}
return sum==0;
}
/** Leellenőrzi, hogy az adott helytől jobbra eső fehér cellák kiadják-e a kívánt összeget, és mind ki van-e
* töltve.
*
* @param p fekete, vízszintes összeget tartalmazó cella helye
* @return jó-e a szó
* */
private boolean checkHorizontal(Position p) throws TableException {
int sum = getSum(p, ITable.SUM_HORIZONTAL);
for (int i=p.x+1; i<_matrix.length; i++) {
int type = getType(new Position(i, p.y));
if (type!=ITable.WHITE) break;
int value = readCell(new Position(i, p.y), ITable.VALUE_NORMAL);
if (value<1) return false;
sum -= value;
}
return sum==0;
}
/** nincs implementálva */
public void load(Object state) throws TableException {
}
/** Fehér cella értékét olvassa ki
*
* @param p pozíció
* @param type értékei: {@link ITable#VALUE_TRUE} a valódi értéket, {@link ITable#VALUE_NORMAL} a felhasználó
* által beírt értéket választja ki
* @return fehér cella értéke
* @throws IndexOutOfBoundsException ha a pozíció érvénytelen
* */
public int readCell(Position p, int type) throws TableException,
IndexOutOfBoundsException {
checkIndex(p);
try {
WhiteCell wc = (WhiteCell)_cellMap.get(p);
switch (type) {
case ITable.VALUE_NORMAL: return wc.normalValue;
case ITable.VALUE_TRUE: return wc.trueValue;
default: throw new TableException("invalid type "+type);
}
} catch (Throwable t) {
throw new TableException("readCell", t);
}
}
/** Nincs implementálva */
public Object save() throws TableException {
return null;
}
/**
* Adott érték letárolása. Visszatérési érték true ha
* helyes az érték, false ha rossz. Type: VALUE_PENCIL v VALUE_NORMAL
* VALUE_TRUE-ra TableException
*
* @param p pozíció
* @param value érték
* @param type típus, értékei: {@link ITable#VALUE_NORMAL},{@link ITable#VALUE_PENCIL},{@link ITable#VALUE_TRUE}
* @return helyes-e az érték
*/
public boolean writeCell(Position p, int value, int type)
throws TableException, IndexOutOfBoundsException {
checkIndex(p);
try {
WhiteCell wc = (WhiteCell)_cellMap.get(p);
switch (type) {
case ITable.VALUE_NORMAL: wc.normalValue = value; break;
case ITable.VALUE_TRUE: wc.trueValue = value; break;
default: throw new TableException("invalid type "+type);
}
} catch (Throwable t) {
throw new TableException("writeCell", t);
}
return false;
}
/** Belső absztakt Cella ősosztály */
abstract class Cell {
/** hely */
public Position position = null;
/** Megjegyzi a helyet, mást nem csinál */
public Cell(Position p) {
position = p;
}
/** Típus lekérése
*
* @return {@link ITable#WHITE},{@link ITable#BLACK_BOTH},{@link ITable#BLACK_HORIZONTAL},
* {@link ITable#BLACK_NONE},{@link ITable#BLACK_VERTICAL}
* */
public abstract int getType();
}
/** Fehér cella */
class WhiteCell extends Cell {
/** Valódi érték */
public int trueValue;
/** beírt érték */
public int normalValue;
/** Fehér cell konstruktor bejegyzi a kapott értékeket
*
* @param p hely
* @param v valódi
* @param user beírt
* */
public WhiteCell(Position p, int v, int user) {
super(p);
trueValue = v;
normalValue = user;
}
/** helyes-e */
public boolean isCorrect() {
return trueValue==normalValue;
}
/** Fixen fehér */
public int getType() {
return ITable.WHITE;
}
}
/** Fekete cella */
class BlackCell extends Cell {
/** vízszintes összeg */
public int horizontalSum;
/** függőleges összeg */
public int verticalSum;
/** Megjegyzi az értékeket, mást nem csinál */
public BlackCell(Position p, int h, int v) {
super(p);
horizontalSum = h;
verticalSum = v;
}
/** Típus lekérése
*
* @return {@link ITable#WHITE},{@link ITable#BLACK_BOTH},{@link ITable#BLACK_HORIZONTAL},
* {@link ITable#BLACK_NONE},{@link ITable#BLACK_VERTICAL}
* */
public int getType() {
if (horizontalSum+verticalSum==0) return ITable.BLACK_NONE;
if (horizontalSum>0 && verticalSum>0) return ITable.BLACK_BOTH;
if (horizontalSum>0 && verticalSum==0) return ITable.BLACK_HORIZONTAL;
return ITable.BLACK_VERTICAL;
}
}
/** Skeleton azonosító getter */
public int getSkeletonId() {
return _skeleton.getId();
}
}