/*
* Copyright: Almende B.V. (2014), Rotterdam, The Netherlands
* License: The Apache Software License, Version 2.0
*/
package com.almende.eve.goldemo;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import com.almende.eve.agent.Agent;
import com.almende.eve.protocol.jsonrpc.annotation.Access;
import com.almende.eve.protocol.jsonrpc.annotation.AccessType;
import com.almende.eve.protocol.jsonrpc.annotation.Name;
import com.almende.eve.protocol.jsonrpc.annotation.Sender;
import com.almende.eve.protocol.jsonrpc.formats.Params;
import com.almende.util.TypeUtil;
import com.almende.util.URIUtil;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* The Class Cell.
*/
@Access(AccessType.PUBLIC)
public class Cell extends Agent {
private ArrayList<String> neighbors = null;
private static final TypeUtil<ArrayList<String>> NEIGHBORTYPE = new TypeUtil<ArrayList<String>>() {};
private static final TypeUtil<CycleState> CYCLESTATETYPE = new TypeUtil<CycleState>() {};
private static final TypeUtil<Boolean> BOOLEANSTATE = new TypeUtil<Boolean>() {};
private static final TypeUtil<Integer> INTEGERSTATE = new TypeUtil<Integer>() {};
/**
* Instantiates a new cell.
*
* @param config
* the config
*/
public Cell(final ObjectNode config) {
super(config);
}
/**
* Creates the.
*
* @param odd
* the odd
* @param even
* the even
* @param initState
* the init state
* @param totalSize
* the total size
*/
public void create(@Name("pathOdd") final String odd,
@Name("pathEven") final String even,
@Name("state") final Boolean initState,
@Name("totalSize") final int totalSize) {
getState().put("Stopped", false);
getState().put("val_0", new CycleState(0, initState));
getState().put("current_cycle", 1);
final String id = getId();
final int agentNo = Integer.parseInt(id.substring(id.indexOf('_') + 1));
calcNeighbours(odd, even, agentNo, totalSize);
try {
this.connect();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @param agentNo
* @param totalSize
*/
private void calcNeighbours(final String odd, final String even,
final int agentNo, final int totalSize) {
final int N = (int) Math.floor(Math.sqrt(totalSize));
final int M = N;
int cN = 0;
int cM = 0;
if (agentNo != 0) {
cM = agentNo % M;
cN = (int) Math.floor(agentNo / N);
}
neighbors = new ArrayList<String>(8);
for (int id = 0; id < 8; id++) {
final int neighborNo = getNeighborNo(id, cN, cM, N, M);
neighbors.add(addPath(odd, even, Goldemo.AGENTPREFIX + neighborNo,
neighborNo));
}
getState().put("neighbors", neighbors);
}
private String addPath(final String odd, final String even,
final String path, final int agentNo) {
return (agentNo % 2 == 0 ? even : odd) + path;
}
private int calcBack(final int cN, final int cM, final int M) {
return cM + cN * M;
}
private int getNeighborNo(final int id, final int cN, final int cM,
final int N, final int M) {
switch (id) {
case 0:
return calcBack(((N + cN - 1) % N), ((M + cM - 1) % M), M);
case 1:
return calcBack(((N + cN) % N), ((M + cM - 1) % M), M);
case 2:
return calcBack(((N + cN + 1) % N), ((M + cM - 1) % M), M);
case 3:
return calcBack(((N + cN - 1) % N), ((M + cM) % M), M);
case 4:
return calcBack(((N + cN + 1) % N), ((M + cM) % M), M);
case 5:
return calcBack(((N + cN - 1) % N), ((M + cM + 1) % M), M);
case 6:
return calcBack(((N + cN) % N), ((M + cM + 1) % M), M);
case 7:
return calcBack(((N + cN + 1) % N), ((M + cM + 1) % M), M);
}
System.err.println("SHould never happen!");
return 0;
}
/**
* Stop.
*/
public void stop() {
getState().put("Stopped", true);
}
/**
* Start.
*/
public void start() {
if (neighbors == null) {
neighbors = getState().get("neighbors", NEIGHBORTYPE);
}
final CycleState myState = getState().get("val_0", CYCLESTATETYPE);
final Params params = new Params();
params.add("alive", myState.isAlive());
params.add("cycle", 0);
for (final String neighbor : neighbors) {
final URI uri = URIUtil.create(neighbor);
try {
call(uri, "collect", params, null);
} catch (final IOException e) {
e.printStackTrace();
}
}
return;
}
/**
* Collect.
*
* @param alive
* the alive
* @param cycle
* the cycle
* @param neighbor
* the neighbor
*/
public void collect(@Name("alive") final boolean alive,
@Name("cycle") final int cycle, @Sender URI neighbor) {
if (neighbors == null) {
neighbors = getState().get("neighbors", NEIGHBORTYPE);
}
final CycleState state = new CycleState(cycle, alive);
getState().put(neighbor.toASCIIString() + "_" + state.getCycle(), state);
try {
calcCycle();
} catch (final URISyntaxException e) {
e.printStackTrace();
}
}
private void calcCycle() throws URISyntaxException {
final Integer currentCycle = getState().get("current_cycle",
INTEGERSTATE);
if (currentCycle != null && currentCycle != 0) {
int aliveNeighbors = 0;
int knownNeighbors = 0;
final String postfix = "_"+(currentCycle -1);
for (final String neighbor : neighbors) {
final CycleState nState = getState().get(
neighbor + postfix, CYCLESTATETYPE);
if (nState == null) {
return;
// continue;
} else if (nState.isAlive()) {
aliveNeighbors++;
}
knownNeighbors++;
}
if (knownNeighbors < 8) {
// System.out.println(getId()+"/"+currentCycle+" has seen: "+knownNeighbors+" neighbors.");
return;
}
final CycleState myState = getState().get(
"val" + postfix, CYCLESTATETYPE);
CycleState newState = null;
if (aliveNeighbors < 2 || aliveNeighbors > 3) {
newState = new CycleState(currentCycle, false);
} else if (aliveNeighbors == 3) {
newState = new CycleState(currentCycle, true);
} else {
newState = new CycleState(currentCycle, myState.isAlive());
}
if (getState()
.putIfUnchanged("val_" + currentCycle, newState, null)) {
getState().put("current_cycle", currentCycle + 1);
if (getState().get("Stopped", BOOLEANSTATE)) {
return;
}
final Params params = new Params();
params.add("alive", newState.isAlive());
params.add("cycle", currentCycle);
for (final String neighbor : neighbors) {
final URI uri = URIUtil.create(neighbor);
try {
call(uri, "collect", params, null);
} catch (final IOException e) {
e.printStackTrace();
}
}
for (final String neighbor : neighbors) {
getState().remove(neighbor + postfix);
}
calcCycle();
}
}
}
/**
* Gets the cycle state.
*
* @param cycle
* the cycle
* @return the cycle state
*/
public CycleState getCycleState(@Name("cycle") final int cycle) {
if (getState().containsKey("val_" + cycle)) {
return getState().get("val_" + cycle, CYCLESTATETYPE);
}
return null;
}
/**
* Gets the all cycle states.
*
* @return the all cycle states
*/
public ArrayList<CycleState> getAllCycleStates() {
final ArrayList<CycleState> result = new ArrayList<CycleState>();
int count = 0;
while (getState().containsKey("val_" + count)) {
result.add(getState().get("val_" + count, CYCLESTATETYPE));
count++;
}
return result;
}
}