package com.igormaznitsa.elife;
import com.igormaznitsa.prol.annotations.*;
import com.igormaznitsa.prol.data.*;
import com.igormaznitsa.prol.exceptions.ProlCriticalError;
import com.igormaznitsa.prol.io.DefaultProlStreamManagerImpl;
import com.igormaznitsa.prol.libraries.ProlAbstractLibrary;
import com.igormaznitsa.prol.logic.*;
import com.igormaznitsa.prol.parser.ProlConsult;
import com.igormaznitsa.prol.utils.Utils;
import java.util.*;
/**
* The class describes a world model of the application p.s. Of course I
* understand that in pure Java it will be working much more quickly but the
* game was written to check possibilities of intercommunication between the
* Prol engine an Java
*
* @author Igor Maznitsa (igor.maznitsa@igormaznitsa.com)
* @version 1.00
*/
@Consult(URL = "this://com/igormaznitsa/elife/elife.prl")
public final class WorldModel extends ProlAbstractLibrary {
public static final class Cell implements Cloneable {
protected int cellX;
protected int cellY;
protected int cellState;
protected int cellID;
public Cell(final int x, final int y, final int state, final int id) {
cellX = x;
cellY = y;
cellState = state;
cellID = id;
}
@Override
public Object clone() {
return new Cell(cellX, cellY, cellState, cellID);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Cell other = (Cell) obj;
if (this.cellID != other.cellID) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 5;
hash = 37 * hash + this.cellID;
return hash;
}
}
private final int columns;
private final int rows;
private final Map<Integer, Cell> cellTable = new HashMap<Integer, Cell>();
private final ProlContext prologEngine;
private final ProlConsult prologConsult;
public static final int EMPTY_CELL = 0;
private int insideIdCellCounter;
private Cell[] prevCellArray;
private static final Term TERM_EMPTY = new Term("empty");
private static final Term TERM_CELL = new Term("cell");
private final PreparedGoal preparedGoal;
public WorldModel(final int columns, final int rows) throws Exception {
super("E-Life");
this.columns = columns;
this.rows = rows;
insideIdCellCounter = 0;
prologEngine = new ProlContext("eLife", DefaultProlStreamManagerImpl.getInstance());
prologEngine.addLibrary(this);
prologConsult = new ProlConsult(prologEngine);
prologConsult.consult();
preparedGoal = new PreparedGoal("process_life({?},{?}).", prologEngine);
}
// +number,+number,?variable
@Predicate(Signature = "get_cell_state/3")
@Determined
public final void get_cell_state(final Goal goal, final TermStruct predicate) {
final int x = Utils.getNumberFromElement(predicate.getElement(0)).intValue();
final int y = Utils.getNumberFromElement(predicate.getElement(1)).intValue();
final Var var = (Var) predicate.getElement(2);
final Cell cell = getCellAt(x, y);
if (cell == null) {
if (!var.Equ(TERM_EMPTY)) {
throw new ProlCriticalError("Can't set variable");
}
}
else {
if (!var.Equ(TERM_CELL)) {
throw new ProlCriticalError("Can't set variable");
}
}
}
@Predicate(Signature = "get_cell_neighbours/3")
@Determined
public final void get_cell_neighbours(final Goal goal, final TermStruct predicate) {
final int x = Utils.getNumberFromElement(predicate.getElement(0)).intValue();
final int y = Utils.getNumberFromElement(predicate.getElement(1)).intValue();
final Var var = (Var) predicate.getElement(2);
final Cell[] p_cell = getAllNeighboursForCell(x, y, prevCellArray);
if (!var.Equ(new TermInteger(p_cell.length))) {
throw new ProlCriticalError("Can't set variable");
}
}
@Predicate(Signature = "delete_cell/2")
@Determined
public final void delete_cell(final Goal goal, final TermStruct predicate) {
final int x = Utils.getNumberFromElement(predicate.getElement(0)).intValue();
final int y = Utils.getNumberFromElement(predicate.getElement(1)).intValue();
final Cell cell = getCellAt(x, y);
if (cell != null) {
removeCell(cell);
}
}
@Predicate(Signature = "create_cell/2")
@Determined
public final void create_cell(final Goal goal, final TermStruct predicate) {
final int x = Utils.getNumberFromElement(predicate.getElement(0)).intValue();
final int y = Utils.getNumberFromElement(predicate.getElement(1)).intValue();
createCell(x, y, 0);
}
public synchronized final void processIteration() {
prevCellArray = getCellsArray();
try {
final Goal goal = preparedGoal.forIntegerParameters(columns, rows);
goal.solve();
}
catch (Throwable _ww) {
_ww = Utils.getRootThrowable(_ww);
_ww.printStackTrace();
System.exit(1);
}
}
public final int getColumnNumber() {
return columns;
}
public final int getRowNumber() {
return rows;
}
public synchronized final void clearAll() {
cellTable.clear();
insideIdCellCounter = 0;
}
public synchronized final Cell getCellAt(final int x, final int y) {
final int xx = Math.abs(x % columns);
final int yy = Math.abs(y % rows);
final Iterator<Cell> cellIterator = cellTable.values().iterator();
while (cellIterator.hasNext()) {
Cell p_cl = cellIterator.next();
if (p_cl.cellX == xx && p_cl.cellY == yy) {
return p_cl;
}
}
return null;
}
public synchronized final void setCellAt(final int x, final int y, final Cell cell) {
final int xx = Math.abs(x % columns);
final int yy = Math.abs(y % rows);
if (getCellAt(xx, yy) != null) {
throw new Error("You are trying to set a cell to a non empty field");
}
removeCell(cell);
addCell(xx, yy, cell);
}
public synchronized final void removeCell(final Cell cell) {
if (cell == null) {
return;
}
cellTable.remove(cell.hashCode());
}
public synchronized final Cell[] getAllNeighboursForCell(final Cell cell, final Cell[] cells) {
if (cells == null || cell == null) {
return null;
}
return getAllNeighboursForCell(cell.cellX, cell.cellY, cells);
}
public synchronized final Cell[] getAllNeighboursForCell(final int x, final int y, final Cell[] cells) {
if (cells == null) {
return null;
}
if (cells.length == 0) {
return new Cell[0];
}
final int xx = Math.abs(x % columns);
final int yy = Math.abs(y % rows);
final int cellArrayLength = cells.length;
final ArrayList<Cell> cellList = new ArrayList<Cell>(8);
int curX = xx;
int curY = yy;
int curXLeft = curX - 1;
if (curXLeft < 0) {
curXLeft = columns - 1;
}
int curXRight = curX + 1;
if (curXRight == columns) {
curXRight = 0;
}
int curYTop = curY - 1;
if (curYTop < 0) {
curYTop = rows - 1;
}
int curYBottom = curY + 1;
if (curYBottom == rows) {
curYBottom = 0;
}
for (int li = 0; li < cellArrayLength; li++) {
final Cell cell = cells[li];
final int cellX = cell.cellX;
final int cellY = cell.cellY;
if (cellX == xx && cellY == yy) {
continue;
}
if ((cellX == curXLeft || cellX == curXRight || cellX == curX) && (cellY == curYTop || cellY == curYBottom || cellY == curY)) {
cellList.add(cell);
}
}
return cellList.toArray(new Cell[cellList.size()]);
}
public synchronized final void addCell(final int x, final int y, final Cell cell) {
if (cell == null) {
return;
}
final int hashCodeForCell = cell.hashCode();
if (cellTable.containsKey(hashCodeForCell)) {
cellTable.remove(hashCodeForCell);
}
cellTable.put(hashCodeForCell, cell);
}
public synchronized final void createCell(final int x, final int y, final int state) {
final Cell newCell = new Cell(x, y, state, insideIdCellCounter++);
cellTable.put(newCell.hashCode(), newCell);
}
public synchronized Cell[] getCellsArray() {
final int cellTableSize = cellTable.values().size();
final Cell[] cells = new Cell[cellTableSize];
int index = 0;
final Iterator<Cell> cellIterator = cellTable.values().iterator();
while (cellIterator.hasNext()) {
Cell p_cell = (Cell) cellIterator.next().clone();
cells[index] = p_cell;
index++;
}
return cells;
}
}