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:
parent
d007778f4a
commit
a0ced36a12
23 changed files with 642 additions and 95 deletions
|
@ -45,13 +45,17 @@ public class FarbotIO {
|
|||
}
|
||||
|
||||
private static void out(String out) {
|
||||
log(out);
|
||||
logln(out);
|
||||
System.out.println(out);
|
||||
}
|
||||
|
||||
public static void log(String log) {
|
||||
public static void logln(String log) {
|
||||
GameState currentState = ai.getCurrentState();
|
||||
String prefix = String.format("[%3d]: ", currentState.getRound());
|
||||
System.err.println(prefix+log);
|
||||
}
|
||||
|
||||
public static void log(String log) {
|
||||
System.err.print(log);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,87 @@
|
|||
package farbot.ai;
|
||||
|
||||
import farbot.FarbotIO;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
|
||||
import farbot.game.GameState;
|
||||
import farbot.game.MoveResult;
|
||||
import farbot.game.map.Move;
|
||||
|
||||
import static farbot.FarbotIO.logln;
|
||||
|
||||
public class AI {
|
||||
private GameState currentState;
|
||||
private int treeDepth = 20;
|
||||
|
||||
private MoveValue lastMove;
|
||||
|
||||
public AI() {
|
||||
this.currentState = new GameState();
|
||||
}
|
||||
|
||||
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) {
|
||||
Parser.parse(update, currentState);
|
||||
FarbotIO.log(currentState.toString());
|
||||
}
|
||||
|
||||
public GameState getCurrentState() {
|
||||
return currentState;
|
||||
}
|
||||
|
||||
private class MoveValue {
|
||||
private Move move;
|
||||
private Double value;
|
||||
|
||||
private MoveValue(Move move, Double value) {
|
||||
this.move = move;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
83
farbot/src/farbot/ai/GameTree.java
Normal file
83
farbot/src/farbot/ai/GameTree.java
Normal 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;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package farbot.ai;
|
||||
|
||||
import java.awt.Point;
|
||||
import farbot.game.map.Point;
|
||||
|
||||
import farbot.game.GameState;
|
||||
import farbot.game.Settings;
|
||||
|
|
|
@ -1,29 +1,59 @@
|
|||
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.Move;
|
||||
import farbot.game.token.Bug;
|
||||
import farbot.game.token.Code;
|
||||
import farbot.game.token.Mine;
|
||||
|
||||
/**
|
||||
* Manager for the GameState.
|
||||
*
|
||||
* Any data retrieved from GameState belongs to the caller.
|
||||
*/
|
||||
public class GameState {
|
||||
private Integer round;
|
||||
|
||||
private Player self;
|
||||
private Player opponent;
|
||||
private Integer round;
|
||||
private GameMap map;
|
||||
|
||||
private boolean forked;
|
||||
|
||||
public GameState() {
|
||||
Settings s = Settings.getInstance();
|
||||
this.self = new Player(new Point(0,0), s.getSelfId());
|
||||
this.opponent = new Player(new Point(0,0), s.getOpponentId());
|
||||
this.map = new GameMap();
|
||||
this(new Player(new Point(0,0), Settings.getInstance().getSelfId()),
|
||||
new Player(new Point(0,0), Settings.getInstance().getOpponentId()),
|
||||
new GameMap(),
|
||||
0);
|
||||
}
|
||||
|
||||
public GameState(Player self, Player opponent, Integer round) {
|
||||
this.self = self;
|
||||
this.opponent = opponent;
|
||||
public GameState(Player self, Player opponent, GameMap map, Integer round) {
|
||||
this(self, opponent, map, round, false);
|
||||
}
|
||||
|
||||
protected GameState(Player self, Player opponent, GameMap map, Integer round, Boolean forked) {
|
||||
this.self = forked ? self.clone(): self;
|
||||
this.opponent = forked ? opponent.clone(): opponent;
|
||||
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) {
|
||||
|
@ -43,32 +73,61 @@ public class GameState {
|
|||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
getPlayer(playerId).setPosition(position);
|
||||
Settings s = Settings.getInstance();
|
||||
if (playerId == s.getSelfId()) self.setPosition(position);
|
||||
else opponent.setPosition(position);
|
||||
}
|
||||
|
||||
public Player getPlayer(String name) {
|
||||
Settings s = Settings.getInstance();
|
||||
if (name.equals(s.getSelfName())) return getSelf();
|
||||
else return getOpponent();
|
||||
public Point getSelfPosition() {
|
||||
return self.getPosition();
|
||||
}
|
||||
public Player getPlayer(Integer id) {
|
||||
Settings s = Settings.getInstance();
|
||||
if (id == s.getSelfId()) return getSelf();
|
||||
else return getOpponent();
|
||||
|
||||
public MoveResult moveSelf(Point position) {
|
||||
return makeMove(self, position);
|
||||
}
|
||||
public Player getSelf() {
|
||||
return self;
|
||||
|
||||
public MoveResult moveSelf(Move move) {
|
||||
return makeMove(self, calcMovePoint(self.getPosition(), move));
|
||||
}
|
||||
public Player getOpponent() {
|
||||
return opponent;
|
||||
|
||||
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() {
|
||||
|
@ -78,6 +137,30 @@ public class GameState {
|
|||
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
|
||||
public String toString() {
|
||||
return "GameState [self=" + self + ", opponent=" + opponent + ", round=" + round + ", map=" + map + "]";
|
||||
|
|
27
farbot/src/farbot/game/MoveResult.java
Normal file
27
farbot/src/farbot/game/MoveResult.java
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
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 snippets;
|
||||
private Point position;
|
||||
|
@ -31,6 +31,12 @@ public class Player {
|
|||
protected void setSnippets(Integer snippets) {
|
||||
this.snippets = snippets;
|
||||
}
|
||||
protected void addSnippets(Integer snippets) {
|
||||
this.snippets += snippets;
|
||||
}
|
||||
protected void removeSnippets(Integer snippets) {
|
||||
this.snippets -= snippets;
|
||||
}
|
||||
public Point getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
@ -46,4 +52,9 @@ public class Player {
|
|||
protected void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player clone() {
|
||||
return new Player(this.bombs, this.snippets, new Point(this.position), this.id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +1,51 @@
|
|||
package farbot.game.map;
|
||||
|
||||
import java.awt.Point;
|
||||
import farbot.game.map.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import farbot.game.Settings;
|
||||
import farbot.game.token.Bug;
|
||||
import farbot.game.token.Code;
|
||||
import farbot.game.token.Mine;
|
||||
import farbot.game.token.Token;
|
||||
|
||||
public class GameMap {
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
private static Field[][] base; // contains invariant elements
|
||||
|
||||
private boolean forked;
|
||||
|
||||
private List<Bug> bugs;
|
||||
private Map<Point, List<Bug>> bugMap;
|
||||
private boolean dirtyBugs;
|
||||
|
||||
private List<Code> codes;
|
||||
private Map<Point, List<Code>> codeMap;
|
||||
private boolean dirtyCodes;
|
||||
|
||||
private List<Mine> mines;
|
||||
private Map<Point, List<Mine>> mineMap;
|
||||
private boolean dirtyMines;
|
||||
|
||||
private Settings s;
|
||||
|
||||
public GameMap() {
|
||||
Settings s = Settings.getInstance();
|
||||
s = Settings.getInstance();
|
||||
this.bugs = new ArrayList<>();
|
||||
this.codes = new ArrayList<>();
|
||||
this.mines = new ArrayList<>();
|
||||
|
@ -34,23 +54,96 @@ public class GameMap {
|
|||
this.codeMap = new HashMap<>();
|
||||
this.mineMap = new HashMap<>();
|
||||
|
||||
this.setForked(false);
|
||||
|
||||
if(base == null) {
|
||||
base = new Field[s.getFieldWidth()][s.getFieldHeight()];
|
||||
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) {
|
||||
if(dirtyBugs) {
|
||||
bugs = new ArrayList<>(bugs);
|
||||
bugMap = new HashMap<>(bugMap);
|
||||
dirtyBugs = false;
|
||||
}
|
||||
|
||||
bugs.add(bug);
|
||||
bugMap.computeIfAbsent(bug.getPosition(), k -> new ArrayList<>()).add(bug);
|
||||
}
|
||||
|
||||
public void putToken(Code code) {
|
||||
if(dirtyCodes) {
|
||||
codes = new ArrayList<>(codes);
|
||||
codeMap = new HashMap<>(codeMap);
|
||||
dirtyBugs = false;
|
||||
}
|
||||
codes.add(code);
|
||||
codeMap.computeIfAbsent(code.getPosition(), k -> new ArrayList<>()).add(code);
|
||||
}
|
||||
|
||||
public void putToken(Mine mine) {
|
||||
if(dirtyMines) {
|
||||
mines = new ArrayList<>(mines);
|
||||
mineMap = new HashMap<>(mineMap);
|
||||
dirtyBugs = false;
|
||||
}
|
||||
mines.add(mine);
|
||||
mineMap.computeIfAbsent(mine.getPosition(), k -> new ArrayList<>()).add(mine);
|
||||
}
|
||||
|
@ -63,10 +156,24 @@ public class GameMap {
|
|||
this.bugMap = new HashMap<>();
|
||||
this.codeMap = 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() {
|
||||
Settings s = Settings.getInstance();
|
||||
assert(s.getFieldWidth() == 19);
|
||||
assert(s.getFieldHeight() == 15);
|
||||
|
||||
|
@ -116,4 +223,81 @@ public class GameMap {
|
|||
return "GameMap [bugs=" + bugs + ", bugMap=" + bugMap + ", codes=" + codes + ", codeMap=" + codeMap + ", mines="
|
||||
+ 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);
|
||||
}
|
||||
}
|
||||
|
|
12
farbot/src/farbot/game/map/Move.java
Normal file
12
farbot/src/farbot/game/map/Move.java
Normal 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; }
|
||||
}
|
17
farbot/src/farbot/game/map/Point.java
Normal file
17
farbot/src/farbot/game/map/Point.java
Normal 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 + ")";
|
||||
}
|
||||
}
|
|
@ -7,16 +7,37 @@ public class Bug extends Token {
|
|||
|
||||
private BugAI aiType;
|
||||
|
||||
public Bug(Bug bug) {
|
||||
this(bug.position.x, bug.position.y, bug.aiType);
|
||||
}
|
||||
|
||||
public Bug(int x, int y, BugAI ai) {
|
||||
super(x,y);
|
||||
this.setAiType(ai);
|
||||
this.aiType = ai;
|
||||
}
|
||||
|
||||
public BugAI getAiType() {
|
||||
return aiType;
|
||||
}
|
||||
|
||||
public void setAiType(BugAI aiType) {
|
||||
this.aiType = aiType;
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package farbot.game.token;
|
||||
|
||||
public class Code extends Token {
|
||||
public Code(Code code) {
|
||||
this(code.position.x, code.position.y);
|
||||
}
|
||||
|
||||
public Code(int x, int y) {
|
||||
super(x, y);
|
||||
}
|
||||
|
|
|
@ -3,25 +3,44 @@ package farbot.game.token;
|
|||
public class Mine extends Token {
|
||||
private Integer timeToExplode;
|
||||
|
||||
public Mine(Mine mine) {
|
||||
this(mine.position.x, mine.position.y, mine.timeToExplode);
|
||||
}
|
||||
|
||||
public Mine(int x, int y) {
|
||||
this(x, y, -1);
|
||||
}
|
||||
|
||||
public Mine(int x, int y, int timeToExplode) {
|
||||
super(x, y);
|
||||
this.setTimeToExplode(timeToExplode);
|
||||
this.timeToExplode = timeToExplode;
|
||||
}
|
||||
|
||||
public Integer getTimeToExplode() {
|
||||
return timeToExplode;
|
||||
}
|
||||
|
||||
public void setTimeToExplode(Integer timeToExplode) {
|
||||
this.timeToExplode = timeToExplode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package farbot.game.token;
|
||||
|
||||
import java.awt.Point;
|
||||
import farbot.game.map.Point;
|
||||
|
||||
public abstract class Token {
|
||||
private Point position;
|
||||
Point position;
|
||||
|
||||
public Token(int x, int y) {
|
||||
setPosition(x, y);
|
||||
|
@ -20,4 +20,23 @@ public abstract class Token {
|
|||
public String toString() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
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
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package io.riddles.hackman2.game.board;
|
||||
|
||||
import java.awt.Point;
|
||||
import farbot.game.map.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package io.riddles.hackman2.game.enemy;
|
||||
|
||||
import java.awt.Point;
|
||||
import farbot.game.map.Point;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.riddles.hackman2.game.board.HackMan2Board;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package io.riddles.hackman2.game.item;
|
||||
|
||||
import java.awt.Point;
|
||||
import farbot.game.map.Point;
|
||||
|
||||
import io.riddles.hackman2.game.HackMan2Object;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package io.riddles.hackman2.game.item;
|
||||
|
||||
import java.awt.Point;
|
||||
import farbot.game.map.Point;
|
||||
|
||||
import io.riddles.hackman2.game.HackMan2Object;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package io.riddles.hackman2.game.move;
|
||||
|
||||
import java.awt.Point;
|
||||
import farbot.game.map.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package io.riddles.hackman2.game.processor;
|
||||
|
||||
import java.awt.Point;
|
||||
import farbot.game.map.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ package io.riddles.hackman2.game.board
|
|||
import io.riddles.hackman2.game.item.Bomb
|
||||
import spock.lang.Specification
|
||||
|
||||
import java.awt.Point
|
||||
import farbot.game.map.Point
|
||||
|
||||
/**
|
||||
* io.riddles.hackman2.game.board.HackMan2BoardSpec - Created on 13-6-17
|
||||
|
@ -65,13 +65,13 @@ class HackMan2BoardSpec extends Specification {
|
|||
ArrayList<String> explosions = board.explodeBombs()
|
||||
|
||||
then:
|
||||
explosions.toString() == "[java.awt.Point[x=0,y=0], java.awt.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], " +
|
||||
"java.awt.Point[x=3,y=2], java.awt.Point[x=4,y=2], java.awt.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], " +
|
||||
"java.awt.Point[x=0,y=5], java.awt.Point[x=18,y=0], java.awt.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], " +
|
||||
"java.awt.Point[x=18,y=5], java.awt.Point[x=17,y=0], java.awt.Point[x=16,y=0]]"
|
||||
explosions.toString() == "[farbot.game.map.Point[x=0,y=0], farbot.game.map.Point[x=1,y=0], " +
|
||||
"farbot.game.map.Point[x=2,y=0], farbot.game.map.Point[x=2,y=1], farbot.game.map.Point[x=2,y=2], " +
|
||||
"farbot.game.map.Point[x=3,y=2], farbot.game.map.Point[x=4,y=2], farbot.game.map.Point[x=0,y=1], " +
|
||||
"farbot.game.map.Point[x=0,y=2], farbot.game.map.Point[x=0,y=3], farbot.game.map.Point[x=0,y=4], " +
|
||||
"farbot.game.map.Point[x=0,y=5], farbot.game.map.Point[x=18,y=0], farbot.game.map.Point[x=18,y=1], " +
|
||||
"farbot.game.map.Point[x=18,y=2], farbot.game.map.Point[x=18,y=3], farbot.game.map.Point[x=18,y=4], " +
|
||||
"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
|
||||
}
|
||||
}
|
||||
|
|
1
javainterface
Submodule
1
javainterface
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 1628e73ebddad23d96ce07ea0d49cf1a955d3f93
|
Loading…
Reference in a new issue