
agents.andySloane.WorldState Maven / Gradle / Ivy
package agents.andySloane;
import java.util.HashMap;
import java.util.Vector;
public final class WorldState {
public int[][] map;
public int[] heightmap;
public int MapX, MapY;
// List of currently known enemies; maintained sorted by x coordinate
public Vector enemies, addqueue;
WorldState pred = null;
HashMap succ; // successor map
// hash key comparator
private class WSHashKey {
static final int MOD_NONE = 0;
static final int MOD_REMOVETILE = 1;
static final int MOD_STOMP = 2;
public int modType = MOD_NONE;
public int modTile = 0;
public SpriteState modEnemy = null;
WSHashKey() {
modType = MOD_NONE;
}
WSHashKey(int _modTile) {
modType = MOD_REMOVETILE;
modTile = _modTile;
}
@Override
public int hashCode() {
switch (modType) {
case MOD_NONE:
return 0;
case MOD_REMOVETILE:
return 1 + modTile * 4;
case MOD_STOMP:
return 2 + modEnemy.hashCode() * 4;
}
return -1;
}
@Override
public boolean equals(Object _o) {
WSHashKey o = (WSHashKey) _o;
if (o.modType != modType)
return false;
if (o.modTile != modTile)
return false;
return o.modEnemy == modEnemy;
}
}
public WorldState(int[][] _map, MarioState ms, float[] enemyPosition) {
map = _map;
MapX = (int) ms.x / 16 - 8;
MapY = (int) ms.y / 16 - 8;
succ = new HashMap();
enemies = new Vector();
buildHeightMap();
syncEnemies(this, enemyPosition, ms);
}
WorldState() {
}
public WorldState clone() {
WorldState w = new WorldState();
w.map = map;
w.MapX = MapX;
w.MapY = MapY;
w.heightmap = heightmap;
w.succ = new HashMap();
w.enemies = enemies; // share enemies vector by default
w.addqueue = addqueue;
return w;
}
// nondestructive step
@SuppressWarnings("unchecked")
public WorldState step() {
WSHashKey h = new WSHashKey();
WorldState s = succ.get(h);
if (s == null) {
s = clone();
s.enemies = (Vector) enemies.clone();
s.stepEnemies();
succ.put(h, s);
}
return s;
}
// destructive update, but returns new worldstate. bleh, it's a mess.
public void sync(WorldState prevws, int[][] _map, MarioState ms, float[] enemyPosition) {
map = _map;
MapX = (int) ms.x / 16 - 8;
MapY = (int) ms.y / 16 - 8;
buildHeightMap();
succ.clear();
syncEnemies(prevws, enemyPosition, ms);
}
void buildHeightMap() {
heightmap = new int[16];
// System.out.printf("heightmap: ");
for (int i = 0; i < 16; i++) {
int j;
for (j = 15; j >= 0; j--) // find the first block from the bottom
if (map[j][i] != 0)
break;
if (j < 0 || j + MapY < 8) { // this is probably a ceiling
heightmap[i] = 22;
} else {
for (; j >= 0; j--)
if (map[j][i] == 0 || map[j][i] == 1)
break; // 1 is mario, 0 is blank
heightmap[i] = j + 1;
}
// System.out.printf("%02d ", heightmap[i]);
}
// System.out.printf("\n");
}
//////////////////////////////////////////////
// destructive operations
void _removeTile(WSHashKey h, int x, int y) {
int[][] newmap = new int[16][16];
for (int j = 0; j < 16; j++)
for (int i = 0; i < 16; i++)
newmap[i][j] = map[i][j];
newmap[x][y] = 0;
map = newmap;
}
private static class EnemyObservation implements Comparable {
int type;
float x, y;
EnemyObservation(int type, float x, float y) {
this.type = type;
this.x = x;
this.y = y;
}
public int compareTo(EnemyObservation b) {
return x < b.x ? -1 : x > b.x ? 1 : 0;
}
}
// this function is terrible and slow, but it only needs to be done once per
// real frame.
public void syncEnemies(WorldState prevws, float[] enemyObs, MarioState ms) {
// when we get a new observation, sort the observation by x and filter
// through the list, using the nearest enemy of the same type and comparing
// predicted states with actual
EnemyObservation[] obs = new EnemyObservation[enemyObs.length / 3];
for (int i = 0; i < enemyObs.length; i += 3)
obs[i / 3] = new EnemyObservation((int) enemyObs[i], enemyObs[i + 1], enemyObs[i + 2]);
Vector newenemies = new Vector(enemies.size() + 2);
Vector oldenemies = prevws.enemies;
// merge enemy observations into our internal enemy array
for (EnemyObservation eobs : obs) {
SpriteState closest = null;
float closestdist = Float.POSITIVE_INFINITY;
int closest_idx = 0;
for (int i = 0; i < enemies.size(); i++) {
SpriteState s = enemies.get(i);
if (s.type != eobs.type)
continue;
float ex = s.x - eobs.x;
float ey = s.y - eobs.y;
float dist = ex * ex + ey * ey;
if (closest == null || dist < closestdist) {
closest = s;
closestdist = dist;
closest_idx = i;
}
}
if (closest == null || closestdist > 64) { // allow a slop of 8 pixels
// assume new enemy
closest = SpriteState.newEnemy(eobs.x, eobs.y, eobs.type, ms);
} else {
if (closestdist != 0) {
if (closest_idx >= oldenemies.size()) {
// if this was newly created but incorrectly i guess we
// have to force a recreation
closest = SpriteState.newEnemy(eobs.x, eobs.y, eobs.type, ms);
} else {
SpriteState prev = oldenemies.get(closest_idx);
closest.resync(eobs.x, eobs.y, prev.x, prev.y);
}
}
}
if (closest != null)
newenemies.add(closest);
}
enemies = newenemies;
}
public void stepEnemies() {
for (int i = 0; i < enemies.size(); i++) {
SpriteState e = enemies.get(i).clone();
boolean keep = e.move(this);
if (keep) {
enemies.set(i, e);
} else {
enemies.remove(i);
i--;
}
}
}
// interact with mario after everyone does their move step
// destructively updates MarioState, but non-destructively returns updated
// WorldState
public WorldState interact(MarioState ms, boolean verbose) {
WorldState ws = this;
ws.addqueue = new Vector();
int i;
if (verbose)
System.out.printf("--interact\n");
for (i = 0; i < ws.enemies.size(); i++) {
ws = ws.enemies.get(i).collideCheck(ws, ms);
}
// now bring in the added stuff
for (SpriteState s : ws.addqueue) {
if (verbose) {
System.out.printf("interact: new e t=%d xy=%f,%f xaya=%f,%f deadTime=%d\n", s.type, s.x, s.y, s.xa,
s.ya, s.deadTime);
}
ws.enemies.add(s);
}
ws.addqueue = null;
return ws;
}
public void addShell(float x, float y) {
ShellState s = new ShellState(x, y, true);
s.move(this);
addqueue.add(s);
}
//////////////////////////////////////////////
// functional operations
WorldState removeTile(int x, int y) {
x -= MapX;
y -= MapY;
if (x < 0 || x >= 16 || y < 0 || y >= 16)
return this;
WSHashKey h = new WSHashKey(x * 16 + y);
WorldState s = succ.get(h);
if (s == null) {
s = clone();
s._removeTile(h, x, y);
succ.put(h, s);
}
return s;
}
final int getBlock(int x, int y) {
// move x,y world coordinates to the 22x22 reference frame
x -= MapX;
y -= MapY;
if (x < 0 || x >= 16 || y < 0 || y >= 16)
return 0;
return map[x][y];
}
final boolean isBlocking(int x, int y, float xa, float ya) {
int block = getBlock(x, y);
if (block == 1)
return false; // mario; ignore
if (block == 34)
return false; // coin
if (block == -11)
return ya > 0; // platform
return block != 0;
}
@SuppressWarnings({"rawtypes", "unchecked"})
final WorldState stomp(SpriteState e, MarioState ms) {
// destructively modify mario
ms.stomp(e);
// clone us, and clone e, and splice e in the array
WorldState ws = clone();
ws.enemies = (Vector) enemies.clone();
ws.enemies.set(ws.enemies.indexOf(e), e.stomp(this, ms));
return ws;
}
final WorldState bump(int x, int y, boolean big) {
// System.out.printf("bumping tile @%d,%d = %d\n", x,y,getBlock(x,y));
if (big) {
switch (getBlock(x, y)) {
case 16: // regular brick
// unfortunately it could also be a hidden coin or
// something else
return removeTile(x, y);
}
}
return this;
}
// this is destructive, done during interact(), unlike bump and stomp
// (which are mario-initiated actions)
final void checkShellCollide(ShellState s) {
}
final void kick(ShellState s) {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy