GameTree based AI

Implemented the first version of a GameTree based AI. Comparable to best
Scala version, but a little weaker still.
This commit is contained in:
armin 2018-01-08 23:11:28 +01:00
parent d007778f4a
commit a0ced36a12
23 changed files with 642 additions and 95 deletions

View file

@ -45,13 +45,17 @@ public class FarbotIO {
} }
private static void out(String out) { private static void out(String out) {
log(out); logln(out);
System.out.println(out); System.out.println(out);
} }
public static void log(String log) { public static void logln(String log) {
GameState currentState = ai.getCurrentState(); GameState currentState = ai.getCurrentState();
String prefix = String.format("[%3d]: ", currentState.getRound()); String prefix = String.format("[%3d]: ", currentState.getRound());
System.err.println(prefix+log); System.err.println(prefix+log);
} }
public static void log(String log) {
System.err.print(log);
}
} }

View file

@ -1,25 +1,87 @@
package farbot.ai; package farbot.ai;
import farbot.FarbotIO; import java.util.ArrayList;
import java.util.Set;
import farbot.game.GameState; import farbot.game.GameState;
import farbot.game.MoveResult;
import farbot.game.map.Move;
import static farbot.FarbotIO.logln;
public class AI { public class AI {
private GameState currentState; private GameState currentState;
private int treeDepth = 20;
private MoveValue lastMove;
public AI() { public AI() {
this.currentState = new GameState(); this.currentState = new GameState();
} }
public String calcMove(Integer time) { public String calcMove(Integer time) {
return "up"; logln("Calculating move in "+time+"ms");
Set<Move> validMoves = currentState.selfMoves();
logln("Valid moves: "+validMoves.toString());
ArrayList<MoveValue> valuedMoves = new ArrayList<>();
for(Move m: validMoves) {
logln("Checking move: "+m.name());
GameState fork = currentState.fork();
MoveResult mr = fork.moveSelf(m);
GameTree gt = new GameTree(fork);
gt.build(treeDepth);
Double value = gt.getValue();
value += mr.getCollectedCodes()*Math.pow((treeDepth+1), 1.6);
value -= mr.getKilledBugs()*8*Math.pow((treeDepth+1), 1.6);
logln("Move '"+m.name()+"': "+value);
valuedMoves.add(new MoveValue(m, value));
}
valuedMoves.sort((mv1, mv2) -> -mv1.value.compareTo(mv2.value));
MoveValue maxMove = valuedMoves.get(0);
if(this.lastMove != null && this.lastMove.value <= maxMove.value) {
switch(this.lastMove.move) {
case LEFT:
if(maxMove.move == Move.RIGHT) maxMove = valuedMoves.get(1);
break;
case RIGHT:
if(maxMove.move == Move.LEFT) maxMove = valuedMoves.get(1);
break;
case UP:
if(maxMove.move == Move.DOWN) maxMove = valuedMoves.get(1);
break;
case DOWN:
if(maxMove.move == Move.UP) maxMove = valuedMoves.get(1);
break;
default:
assert(false);
}
}
this.lastMove = maxMove;
logln("Max.move '"+maxMove.move+"': "+maxMove.value);
return maxMove.move.name().toLowerCase();
} }
public void parseUpdate(String[] update) { public void parseUpdate(String[] update) {
Parser.parse(update, currentState); Parser.parse(update, currentState);
FarbotIO.log(currentState.toString());
} }
public GameState getCurrentState() { public GameState getCurrentState() {
return currentState; return currentState;
} }
private class MoveValue {
private Move move;
private Double value;
private MoveValue(Move move, Double value) {
this.move = move;
this.value = value;
}
}
} }

View file

@ -0,0 +1,83 @@
package farbot.ai;
import static farbot.FarbotIO.logln;
import farbot.game.map.Point;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
import farbot.game.GameState;
import farbot.game.MoveResult;
public class GameTree {
private GameState node;
private Double value;
private HashSet<Point> visited;
private Integer totalLevel;
public GameTree(GameState root) {
this.node = root;
this.value = 0.0;
this.totalLevel = 1;
this.visited = new HashSet<>();
}
public void build(int level) {
this.totalLevel = level;
Queue<GameState> children = makeChildren(node, this, level);
while(level > 0) {
level--;
Queue<GameState> nextChildren = new LinkedList<>();
while(!children.isEmpty()) {
GameState child = children.poll();
nextChildren.addAll(makeChildren(child, this, level));
}
children = nextChildren;
nextChildren = new LinkedList<>();
}
}
/**
* Get the value of a root node. Not 0.0 only for nodes
* from which a GameTree was built (i.e. nodes on which
* build() was called)
*
* @return Value of a root node. Always 0.0 if not the node
* upon which build() was called.
*/
public Double getValue() {
return value;
}
private Queue<GameState> makeChildren(GameState state, GameTree gtRoot, int level) {
Set<Point> selfMoves = state.selfMovePoints();
selfMoves.removeAll(gtRoot.visited);
LinkedList<GameState> children = new LinkedList<>();
for(Point selfMove: selfMoves) {
GameState child = state.fork();
MoveResult mr = child.moveSelf(selfMove);
if(mr.getCollectedCodes() != 0){
logln("Got "+mr.getCollectedCodes()+" codes at "+selfMove.toString()+" in level "+level);
gtRoot.value += Math.pow(level*mr.getCollectedCodes(), 1.3);
}
if(mr.getKilledBugs() != 0) {
logln("Killed "+mr.getKilledBugs()+" bugs at "+selfMove.toString()+" in level "+level);
gtRoot.value -= Math.pow(level*4*mr.getKilledBugs(), level/totalLevel);
}
gtRoot.visited.add(selfMove);
children.add(child);
}
return children;
}
}

View file

@ -1,6 +1,6 @@
package farbot.ai; package farbot.ai;
import java.awt.Point; import farbot.game.map.Point;
import farbot.game.GameState; import farbot.game.GameState;
import farbot.game.Settings; import farbot.game.Settings;

View file

@ -1,76 +1,135 @@
package farbot.game; package farbot.game;
import java.awt.Point; import farbot.game.map.Point;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import farbot.game.map.GameMap; import farbot.game.map.GameMap;
import farbot.game.map.Move;
import farbot.game.token.Bug; import farbot.game.token.Bug;
import farbot.game.token.Code; import farbot.game.token.Code;
import farbot.game.token.Mine; import farbot.game.token.Mine;
/**
* Manager for the GameState.
*
* Any data retrieved from GameState belongs to the caller.
*/
public class GameState { public class GameState {
private Integer round;
private Player self; private Player self;
private Player opponent; private Player opponent;
private Integer round;
private GameMap map; private GameMap map;
private boolean forked;
public GameState() { public GameState() {
Settings s = Settings.getInstance(); this(new Player(new Point(0,0), Settings.getInstance().getSelfId()),
this.self = new Player(new Point(0,0), s.getSelfId()); new Player(new Point(0,0), Settings.getInstance().getOpponentId()),
this.opponent = new Player(new Point(0,0), s.getOpponentId()); new GameMap(),
this.map = new GameMap(); 0);
}
public GameState(Player self, Player opponent, GameMap map, Integer round) {
this(self, opponent, map, round, false);
} }
public GameState(Player self, Player opponent, Integer round) { protected GameState(Player self, Player opponent, GameMap map, Integer round, Boolean forked) {
this.self = self; this.self = forked ? self.clone(): self;
this.opponent = opponent; this.opponent = forked ? opponent.clone(): opponent;
this.round = round; this.round = round;
this.map = map;
this.forked = forked;
}
public List<GameState> fork(Integer times) {
ArrayList<GameState> forks = new ArrayList<>();
for (int i=0; i<times; i++) forks.add(fork());
return forks;
} }
public GameState fork() {
GameState fork = new GameState(self.clone(), opponent.clone(), map.fork(), round, true);
fork.map.setForked(true);
return fork;
}
public void putToken(Bug bug) { public void putToken(Bug bug) {
map.putToken(bug); map.putToken(bug);
} }
public void putToken(Code code) { public void putToken(Code code) {
map.putToken(code); map.putToken(code);
} }
public void putToken(Mine mine) { public void putToken(Mine mine) {
map.putToken(mine); map.putToken(mine);
} }
public void clearTokens() { public void clearTokens() {
map.clearTokens(); map.clearTokens();
} }
public void setSnippets(String playerName, Integer snippets) { public void setSnippets(String playerName, Integer snippets) {
getPlayer(playerName).setSnippets(snippets); Settings s = Settings.getInstance();
if (playerName == s.getSelfName()) self.setSnippets(snippets);
else opponent.setSnippets(snippets);
} }
public Integer getSelfSnippets() {
return self.getSnippets();
}
public Integer getOpponentSnippets() {
return opponent.getSnippets();
}
public void setBombs(String playerName, Integer bombs) { public void setBombs(String playerName, Integer bombs) {
getPlayer(playerName).setBombs(bombs); Settings s = Settings.getInstance();
if (playerName == s.getSelfName()) self.setBombs(bombs);
else opponent.setBombs(bombs);
} }
public void setPlayerPosition(Integer playerId, Point position) { public void setPlayerPosition(Integer playerId, Point position) {
getPlayer(playerId).setPosition(position); Settings s = Settings.getInstance();
if (playerId == s.getSelfId()) self.setPosition(position);
else opponent.setPosition(position);
} }
public Player getPlayer(String name) { public Point getSelfPosition() {
Settings s = Settings.getInstance(); return self.getPosition();
if (name.equals(s.getSelfName())) return getSelf();
else return getOpponent();
}
public Player getPlayer(Integer id) {
Settings s = Settings.getInstance();
if (id == s.getSelfId()) return getSelf();
else return getOpponent();
}
public Player getSelf() {
return self;
}
public Player getOpponent() {
return opponent;
} }
public MoveResult moveSelf(Point position) {
return makeMove(self, position);
}
public MoveResult moveSelf(Move move) {
return makeMove(self, calcMovePoint(self.getPosition(), move));
}
public MoveResult moveOpponent(Point position) {
return makeMove(opponent, position);
}
public MoveResult moveOpponent(Move move) {
return makeMove(self, calcMovePoint(opponent.getPosition(), move));
}
private MoveResult makeMove(Player player, Point position) {
player.setPosition(position);
List<Code> codes = map.removeCodesAt(position);
List<Bug> bugs = map.removeBugsAt(position);
player.addSnippets(codes.size());
player.removeSnippets(bugs.size()*4);
return new MoveResult(player.clone(), codes.size(), bugs.size());
}
public Integer getRound() { public Integer getRound() {
return round; return round;
} }
@ -78,6 +137,30 @@ public class GameState {
this.round = round; this.round = round;
} }
public boolean isForked() {
return forked;
}
public Set<Point> selfMovePoints(){
return map.validMovePoints(self.getPosition());
}
public Set<Move> selfMoves(){
return map.validMoves(self.getPosition());
}
public Set<Point> opponentMovePoints(){
return map.validMovePoints(opponent.getPosition());
}
public Set<Move> opponentMoves(){
return map.validMoves(opponent.getPosition());
}
public Point calcMovePoint(Point from, Move move) {
return map.calcMovePoint(from, move);
}
@Override @Override
public String toString() { public String toString() {
return "GameState [self=" + self + ", opponent=" + opponent + ", round=" + round + ", map=" + map + "]"; return "GameState [self=" + self + ", opponent=" + opponent + ", round=" + round + ", map=" + map + "]";

View file

@ -0,0 +1,27 @@
package farbot.game;
public class MoveResult {
private Player player;
private int killedBugs;
private int collectedCodes;
public MoveResult(Player player, int collectedCodes, int killedBugs) {
this.player = player;
this.killedBugs = killedBugs;
this.collectedCodes = collectedCodes;
}
public Player getPlayer() {
return player;
}
public int getKilledBugs() {
return killedBugs;
}
public int getCollectedCodes() {
return collectedCodes;
}
}

View file

@ -1,8 +1,8 @@
package farbot.game; package farbot.game;
import java.awt.Point; import farbot.game.map.Point;
public class Player { public class Player implements Cloneable {
private Integer bombs; private Integer bombs;
private Integer snippets; private Integer snippets;
private Point position; private Point position;
@ -31,6 +31,12 @@ public class Player {
protected void setSnippets(Integer snippets) { protected void setSnippets(Integer snippets) {
this.snippets = snippets; this.snippets = snippets;
} }
protected void addSnippets(Integer snippets) {
this.snippets += snippets;
}
protected void removeSnippets(Integer snippets) {
this.snippets -= snippets;
}
public Point getPosition() { public Point getPosition() {
return position; return position;
} }
@ -46,4 +52,9 @@ public class Player {
protected void setId(Integer id) { protected void setId(Integer id) {
this.id = id; this.id = id;
} }
@Override
public Player clone() {
return new Player(this.bombs, this.snippets, new Point(this.position), this.id);
}
} }

View file

@ -1,93 +1,200 @@
package farbot.game.map; package farbot.game.map;
import java.awt.Point; import farbot.game.map.Point;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import farbot.game.Settings; import farbot.game.Settings;
import farbot.game.token.Bug; import farbot.game.token.Bug;
import farbot.game.token.Code; import farbot.game.token.Code;
import farbot.game.token.Mine; import farbot.game.token.Mine;
import farbot.game.token.Token;
/**
* Represents the game map. Can be safely used in forks
* after setting setForked(true): Makes GameMap lazily
* immutable (creates deep copies of compound types on
* modification only).
*
* Any data retrieved from GameMap belongs to the caller.
*/
public final class GameMap {
public class GameMap {
private static Field[][] base; // contains invariant elements private static Field[][] base; // contains invariant elements
private boolean forked;
private List<Bug> bugs; private List<Bug> bugs;
private Map<Point, List<Bug>> bugMap; private Map<Point, List<Bug>> bugMap;
private boolean dirtyBugs;
private List<Code> codes; private List<Code> codes;
private Map<Point, List<Code>> codeMap; private Map<Point, List<Code>> codeMap;
private boolean dirtyCodes;
private List<Mine> mines; private List<Mine> mines;
private Map<Point, List<Mine>> mineMap; private Map<Point, List<Mine>> mineMap;
private boolean dirtyMines;
private Settings s;
public GameMap() { public GameMap() {
Settings s = Settings.getInstance(); s = Settings.getInstance();
this.bugs = new ArrayList<>(); this.bugs = new ArrayList<>();
this.codes = new ArrayList<>(); this.codes = new ArrayList<>();
this.mines = new ArrayList<>(); this.mines = new ArrayList<>();
this.bugMap = new HashMap<>(); this.bugMap = new HashMap<>();
this.codeMap = new HashMap<>(); this.codeMap = new HashMap<>();
this.mineMap = new HashMap<>(); this.mineMap = new HashMap<>();
this.setForked(false);
if(base == null) { if(base == null) {
base = new Field[s.getFieldWidth()][s.getFieldHeight()]; base = new Field[s.getFieldWidth()][s.getFieldHeight()];
parseBase(); parseBase();
} }
} }
private GameMap(GameMap father) {
s = Settings.getInstance();
this.bugs = father.bugs;
this.codes = father.codes;
this.mines = father.mines;
this.bugMap = father.bugMap;
this.codeMap = father.codeMap;
this.mineMap = father.mineMap;
this.setForked(true);
}
/**
* Forks the father map. Forks are fast and
* lazily deep copies (i.e. create deep field
* copies only if needed on write access).
*
*/
public GameMap fork() {
return new GameMap(this);
}
public Set<Move> validMoves(Point from) {
EnumSet<Move> moves;
switch(base[from.x][from.y]) {
case GATE_L:
case GATE_R:
moves = EnumSet.of(Move.LEFT, Move.RIGHT);
break;
default:
moves = EnumSet.allOf(Move.class);
moves.removeIf( move -> from.x+move.x < 0
|| from.y+move.y < 0
|| from.x+move.x >= s.getFieldWidth()
|| from.y+move.y >= s.getFieldHeight()
|| base[from.x+move.x][from.y+move.y] == Field.WALL );
break;
}
return moves;
}
public Set<Point> validMovePoints(Point from){
return validMoves(from).stream()
.map(move -> calcMovePoint(from, move))
.collect(Collectors.toSet());
}
public Point calcMovePoint(Point from, Move move) {
return new Point(Math.floorMod(from.x+move.x, s.getFieldWidth()),
Math.floorMod(from.y+move.y, s.getFieldHeight()));
}
public void putToken(Bug bug) { public void putToken(Bug bug) {
if(dirtyBugs) {
bugs = new ArrayList<>(bugs);
bugMap = new HashMap<>(bugMap);
dirtyBugs = false;
}
bugs.add(bug); bugs.add(bug);
bugMap.computeIfAbsent(bug.getPosition(), k -> new ArrayList<>()).add(bug); bugMap.computeIfAbsent(bug.getPosition(), k -> new ArrayList<>()).add(bug);
} }
public void putToken(Code code) { public void putToken(Code code) {
if(dirtyCodes) {
codes = new ArrayList<>(codes);
codeMap = new HashMap<>(codeMap);
dirtyBugs = false;
}
codes.add(code); codes.add(code);
codeMap.computeIfAbsent(code.getPosition(), k -> new ArrayList<>()).add(code); codeMap.computeIfAbsent(code.getPosition(), k -> new ArrayList<>()).add(code);
} }
public void putToken(Mine mine) { public void putToken(Mine mine) {
if(dirtyMines) {
mines = new ArrayList<>(mines);
mineMap = new HashMap<>(mineMap);
dirtyBugs = false;
}
mines.add(mine); mines.add(mine);
mineMap.computeIfAbsent(mine.getPosition(), k -> new ArrayList<>()).add(mine); mineMap.computeIfAbsent(mine.getPosition(), k -> new ArrayList<>()).add(mine);
} }
public void clearTokens() { public void clearTokens() {
this.bugs = new ArrayList<>(); this.bugs = new ArrayList<>();
this.codes = new ArrayList<>(); this.codes = new ArrayList<>();
this.mines = new ArrayList<>(); this.mines = new ArrayList<>();
this.bugMap = new HashMap<>(); this.bugMap = new HashMap<>();
this.codeMap = new HashMap<>(); this.codeMap = new HashMap<>();
this.mineMap = new HashMap<>(); this.mineMap = new HashMap<>();
this.dirtyBugs = false;
this.dirtyCodes = false;
this.dirtyMines = false;
} }
public boolean isForked() {
return forked;
}
public void setForked(boolean forked) {
this.forked = forked;
this.dirtyBugs = forked;
this.dirtyCodes = forked;
this.dirtyMines = forked;
}
private void parseBase() { private void parseBase() {
Settings s = Settings.getInstance();
assert(s.getFieldWidth() == 19); assert(s.getFieldWidth() == 19);
assert(s.getFieldHeight() == 15); assert(s.getFieldHeight() == 15);
String baseString = ".,.,.,x,.,.,.,.,.,.,.,.,.,.,.,x,.,.,.," String baseString = ".,.,.,x,.,.,.,.,.,.,.,.,.,.,.,x,.,.,.,"
+ ".,x,.,x,.,x,x,x,x,.,x,x,x,x,.,x,.,x,.," + ".,x,.,x,.,x,x,x,x,.,x,x,x,x,.,x,.,x,.,"
+ ".,x,.,.,.,x,.,.,.,.,.,.,.,x,.,.,.,x,.," + ".,x,.,.,.,x,.,.,.,.,.,.,.,x,.,.,.,x,.,"
+ ".,x,x,x,.,x,.,x,x,x,x,x,.,x,.,x,x,x,.," + ".,x,x,x,.,x,.,x,x,x,x,x,.,x,.,x,x,x,.,"
+ ".,x,.,.,.,x,.,.,.,.,.,.,.,x,.,.,.,x,.," + ".,x,.,.,.,x,.,.,.,.,.,.,.,x,.,.,.,x,.,"
+ ".,.,.,x,.,x,.,x,x,.,x,x,.,x,.,x,.,.,.," + ".,.,.,x,.,x,.,x,x,.,x,x,.,x,.,x,.,.,.,"
+ "x,.,x,x,.,.,.,x,x,.,x,x,.,.,.,x,x,.,x," + "x,.,x,x,.,.,.,x,x,.,x,x,.,.,.,x,x,.,x,"
+ "Gl,.,x,x,.,x,x,x,x,.,x,x,x,x,.,x,x,.,Gr," + "Gl,.,x,x,.,x,x,x,x,.,x,x,x,x,.,x,x,.,Gr,"
+ "x,.,x,x,.,.,.,.,.,.,.,.,.,.,.,x,x,.,x," + "x,.,x,x,.,.,.,.,.,.,.,.,.,.,.,x,x,.,x,"
+ ".,.,.,x,.,x,x,x,x,x,x,x,x,x,.,x,.,.,.," + ".,.,.,x,.,x,x,x,x,x,x,x,x,x,.,x,.,.,.,"
+ ".,x,.,.,.,.,.,.,x,x,x,.,.,.,.,.,.,x,.," + ".,x,.,.,.,.,.,.,x,x,x,.,.,.,.,.,.,x,.,"
+ ".,x,.,x,x,.,x,.,.,.,.,.,x,.,x,x,.,x,.," + ".,x,.,x,x,.,x,.,.,.,.,.,x,.,x,x,.,x,.,"
+ ".,x,.,x,x,.,x,x,x,x,x,x,x,.,x,x,.,x,.," + ".,x,.,x,x,.,x,x,x,x,x,x,x,.,x,x,.,x,.,"
+ ".,x,.,x,x,.,x,.,.,.,.,.,x,.,x,x,.,x,.," + ".,x,.,x,x,.,x,.,.,.,.,.,x,.,x,x,.,x,.,"
+ ".,.,.,.,.,.,.,.,x,x,x,.,.,.,.,.,.,.,."; + ".,.,.,.,.,.,.,.,x,x,x,.,.,.,.,.,.,.,.";
String[] baseSplit = baseString.split(","); String[] baseSplit = baseString.split(",");
for (int i = 0; i < 19; i++) { for (int i = 0; i < 19; i++) {
for (int j = 0; j < 15; j++) { for (int j = 0; j < 15; j++) {
String b = baseSplit[i+(s.getFieldWidth()*j)]; String b = baseSplit[i+(s.getFieldWidth()*j)];
@ -116,4 +223,81 @@ public class GameMap {
return "GameMap [bugs=" + bugs + ", bugMap=" + bugMap + ", codes=" + codes + ", codeMap=" + codeMap + ", mines=" return "GameMap [bugs=" + bugs + ", bugMap=" + bugMap + ", codes=" + codes + ", codeMap=" + codeMap + ", mines="
+ mines + ", mineMap=" + mineMap + "]"; + mines + ", mineMap=" + mineMap + "]";
} }
public List<Bug> getBugs() {
return Collections.unmodifiableList(bugs);
}
public List<Bug> getBugsAt(Point position){
return Collections.unmodifiableList(bugMap.get(position));
}
public List<Bug> removeBugsAt(Point position) {
if(dirtyBugs && bugMap.containsKey(position)) {
bugs = new ArrayList<>(bugs);
bugMap = new HashMap<>(bugMap);
dirtyBugs = false;
}
List<Bug> rmBugs = bugMap.remove(position);
if(rmBugs != null) rmBugs.forEach(bugs::remove);
else rmBugs = new ArrayList<>();
return rmBugs;
}
public List<Code> getCodes() {
return Collections.unmodifiableList(codes);
}
public List<Code> getCodesAt(Point position){
return Collections.unmodifiableList(codeMap.get(position));
}
public List<Code> removeCodesAt(Point position){
if(dirtyCodes && codeMap.containsKey(position)) {
codes = new ArrayList<>(codes);
codeMap = new HashMap<>(codeMap);
dirtyCodes = false;
}
List<Code> rmCodes = codeMap.remove(position);
if(rmCodes != null) rmCodes.forEach(codes::remove);
else rmCodes = new ArrayList<>();
return rmCodes;
}
public List<Mine> getMines() {
return Collections.unmodifiableList(mines);
}
public List<Mine> getMinesAt(Point position){
return Collections.unmodifiableList(mineMap.get(position));
}
public List<Mine> removeMinesAt(Point position){
if(dirtyMines && mineMap.containsKey(position)) {
mines = new ArrayList<>(mines);
mineMap = new HashMap<>(mineMap);
dirtyMines = false;
}
List<Mine> rmMines = mineMap.remove(position);
if(rmMines != null) rmMines.forEach(mines::remove);
else rmMines = new ArrayList<>();
return rmMines;
}
public List<Token> getTokensAt(Point position){
ArrayList<Token> tokens = new ArrayList<>();
tokens.addAll(getBugsAt(position));
tokens.addAll(getCodesAt(position));
tokens.addAll(getMinesAt(position));
return Collections.unmodifiableList(tokens);
}
} }

View file

@ -0,0 +1,12 @@
package farbot.game.map;
public enum Move {
UP (0,-1),
DOWN (0,1),
LEFT (-1,0),
RIGHT (1,0);
protected final int x;
protected final int y;
Move(int x, int y) { this.x = x; this.y = y; }
}

View file

@ -0,0 +1,17 @@
package farbot.game.map;
public class Point extends java.awt.Point{
private static final long serialVersionUID = -4718250851453184212L;
public Point() { super(); }
public Point(Point p) { super(p); }
public Point(int x, int y) { super(x,y); }
@Override
public String toString() {
return "(x=" + x + ",y=" + y + ")";
}
}

View file

@ -7,16 +7,37 @@ public class Bug extends Token {
private BugAI aiType; private BugAI aiType;
public Bug(Bug bug) {
this(bug.position.x, bug.position.y, bug.aiType);
}
public Bug(int x, int y, BugAI ai) { public Bug(int x, int y, BugAI ai) {
super(x,y); super(x,y);
this.setAiType(ai); this.aiType = ai;
} }
public BugAI getAiType() { public BugAI getAiType() {
return aiType; return aiType;
} }
public void setAiType(BugAI aiType) { @Override
this.aiType = aiType; public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((aiType == null) ? 0 : aiType.hashCode());
return result;
} }
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!super.equals(obj)) return false;
if (getClass() != obj.getClass()) return false;
Bug other = (Bug) obj;
return aiType == other.aiType;
}
} }

View file

@ -1,6 +1,10 @@
package farbot.game.token; package farbot.game.token;
public class Code extends Token { public class Code extends Token {
public Code(Code code) {
this(code.position.x, code.position.y);
}
public Code(int x, int y) { public Code(int x, int y) {
super(x, y); super(x, y);
} }

View file

@ -3,25 +3,44 @@ package farbot.game.token;
public class Mine extends Token { public class Mine extends Token {
private Integer timeToExplode; private Integer timeToExplode;
public Mine(Mine mine) {
this(mine.position.x, mine.position.y, mine.timeToExplode);
}
public Mine(int x, int y) { public Mine(int x, int y) {
this(x, y, -1); this(x, y, -1);
} }
public Mine(int x, int y, int timeToExplode) { public Mine(int x, int y, int timeToExplode) {
super(x, y); super(x, y);
this.setTimeToExplode(timeToExplode); this.timeToExplode = timeToExplode;
} }
public Integer getTimeToExplode() { public Integer getTimeToExplode() {
return timeToExplode; return timeToExplode;
} }
public void setTimeToExplode(Integer timeToExplode) {
this.timeToExplode = timeToExplode;
}
@Override @Override
public String toString() { public String toString() {
return "Mine [timeToExplode=" + timeToExplode + ", getPosition()=" + getPosition() + "]"; return "Mine [timeToExplode=" + timeToExplode + ", getPosition()=" + getPosition() + "]";
} }
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((timeToExplode == null) ? 0 : timeToExplode.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!super.equals(obj)) return false;
if (getClass() != obj.getClass()) return false;
Mine other = (Mine) obj;
return timeToExplode.equals(other.timeToExplode);
}
} }

View file

@ -1,9 +1,9 @@
package farbot.game.token; package farbot.game.token;
import java.awt.Point; import farbot.game.map.Point;
public abstract class Token { public abstract class Token {
private Point position; Point position;
public Token(int x, int y) { public Token(int x, int y) {
setPosition(x, y); setPosition(x, y);
@ -20,4 +20,23 @@ public abstract class Token {
public String toString() { public String toString() {
return "Token [position=" + position + "]"; return "Token [position=" + position + "]";
} }
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((position == null) ? 0 : position.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Token other = (Token) obj;
return position.equals(other.position);
}
} }

View file

@ -19,7 +19,7 @@
package io.riddles.hackman2.game; package io.riddles.hackman2.game;
import java.awt.Point; import farbot.game.map.Point;
/** /**
* io.riddles.hackman2.game.board.Item - Created on 12-6-17 * io.riddles.hackman2.game.board.Item - Created on 12-6-17

View file

@ -19,7 +19,7 @@
package io.riddles.hackman2.game.board; package io.riddles.hackman2.game.board;
import java.awt.Point; import farbot.game.map.Point;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;

View file

@ -19,7 +19,7 @@
package io.riddles.hackman2.game.enemy; package io.riddles.hackman2.game.enemy;
import java.awt.Point; import farbot.game.map.Point;
import java.util.ArrayList; import java.util.ArrayList;
import io.riddles.hackman2.game.board.HackMan2Board; import io.riddles.hackman2.game.board.HackMan2Board;

View file

@ -19,7 +19,7 @@
package io.riddles.hackman2.game.item; package io.riddles.hackman2.game.item;
import java.awt.Point; import farbot.game.map.Point;
import io.riddles.hackman2.game.HackMan2Object; import io.riddles.hackman2.game.HackMan2Object;

View file

@ -19,7 +19,7 @@
package io.riddles.hackman2.game.item; package io.riddles.hackman2.game.item;
import java.awt.Point; import farbot.game.map.Point;
import io.riddles.hackman2.game.HackMan2Object; import io.riddles.hackman2.game.HackMan2Object;

View file

@ -19,7 +19,7 @@
package io.riddles.hackman2.game.move; package io.riddles.hackman2.game.move;
import java.awt.Point; import farbot.game.map.Point;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;

View file

@ -19,7 +19,7 @@
package io.riddles.hackman2.game.processor; package io.riddles.hackman2.game.processor;
import java.awt.Point; import farbot.game.map.Point;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;

View file

@ -22,7 +22,7 @@ package io.riddles.hackman2.game.board
import io.riddles.hackman2.game.item.Bomb import io.riddles.hackman2.game.item.Bomb
import spock.lang.Specification import spock.lang.Specification
import java.awt.Point import farbot.game.map.Point
/** /**
* io.riddles.hackman2.game.board.HackMan2BoardSpec - Created on 13-6-17 * io.riddles.hackman2.game.board.HackMan2BoardSpec - Created on 13-6-17
@ -65,13 +65,13 @@ class HackMan2BoardSpec extends Specification {
ArrayList<String> explosions = board.explodeBombs() ArrayList<String> explosions = board.explodeBombs()
then: then:
explosions.toString() == "[java.awt.Point[x=0,y=0], java.awt.Point[x=1,y=0], " + explosions.toString() == "[farbot.game.map.Point[x=0,y=0], farbot.game.map.Point[x=1,y=0], " +
"java.awt.Point[x=2,y=0], java.awt.Point[x=2,y=1], java.awt.Point[x=2,y=2], " + "farbot.game.map.Point[x=2,y=0], farbot.game.map.Point[x=2,y=1], farbot.game.map.Point[x=2,y=2], " +
"java.awt.Point[x=3,y=2], java.awt.Point[x=4,y=2], java.awt.Point[x=0,y=1], " + "farbot.game.map.Point[x=3,y=2], farbot.game.map.Point[x=4,y=2], farbot.game.map.Point[x=0,y=1], " +
"java.awt.Point[x=0,y=2], java.awt.Point[x=0,y=3], java.awt.Point[x=0,y=4], " + "farbot.game.map.Point[x=0,y=2], farbot.game.map.Point[x=0,y=3], farbot.game.map.Point[x=0,y=4], " +
"java.awt.Point[x=0,y=5], java.awt.Point[x=18,y=0], java.awt.Point[x=18,y=1], " + "farbot.game.map.Point[x=0,y=5], farbot.game.map.Point[x=18,y=0], farbot.game.map.Point[x=18,y=1], " +
"java.awt.Point[x=18,y=2], java.awt.Point[x=18,y=3], java.awt.Point[x=18,y=4], " + "farbot.game.map.Point[x=18,y=2], farbot.game.map.Point[x=18,y=3], farbot.game.map.Point[x=18,y=4], " +
"java.awt.Point[x=18,y=5], java.awt.Point[x=17,y=0], java.awt.Point[x=16,y=0]]" "farbot.game.map.Point[x=18,y=5], farbot.game.map.Point[x=17,y=0], farbot.game.map.Point[x=16,y=0]]"
board.getBombs().size() == 1 board.getBombs().size() == 1
} }
} }

1
javainterface Submodule

@ -0,0 +1 @@
Subproject commit 1628e73ebddad23d96ce07ea0d49cf1a955d3f93