From 995a6f85befcea7181464158cac472540557d1fe Mon Sep 17 00:00:00 2001 From: David Date: Tue, 12 Jan 2021 17:41:01 -0600 Subject: [PATCH] Add DFS algorithm for battling players --- .../model/cade/BlockadeTimeMachine.java | 12 ++ .../cadesim/server/model/player/Player.java | 25 +++++ .../server/model/player/PlayerManager.java | 47 +++++++- .../server/model/player/ai/NPC_Type1.java | 24 +++- .../server/model/player/ai/NPC_Type2.java | 23 +++- .../server/model/player/ai/NPC_Type3.java | 23 +++- .../server/model/player/ai/NPC_Type4.java | 103 ++++-------------- .../model/player/ai/util/AStarNode.java | 5 +- .../model/player/ai/util/AStarSearch.java | 87 +++++++++++---- .../server/model/player/ai/util/DFS.java | 90 +++++++++++++++ .../model/player/ai/util/MoveState.java | 31 ++++++ .../model/player/vessel/VesselFace.java | 20 ++-- 12 files changed, 357 insertions(+), 133 deletions(-) create mode 100644 server/src/com/benberi/cadesim/server/model/player/ai/util/DFS.java create mode 100644 server/src/com/benberi/cadesim/server/model/player/ai/util/MoveState.java diff --git a/server/src/com/benberi/cadesim/server/model/cade/BlockadeTimeMachine.java b/server/src/com/benberi/cadesim/server/model/cade/BlockadeTimeMachine.java index 2a81f97..fe655c9 100644 --- a/server/src/com/benberi/cadesim/server/model/cade/BlockadeTimeMachine.java +++ b/server/src/com/benberi/cadesim/server/model/cade/BlockadeTimeMachine.java @@ -2,6 +2,7 @@ import com.benberi.cadesim.server.config.Constants; import com.benberi.cadesim.server.config.ServerConfiguration; +import com.benberi.cadesim.server.model.player.Player; import com.benberi.cadesim.server.ServerContext; public class BlockadeTimeMachine { @@ -119,6 +120,17 @@ else if (timeUntilBreak == 0) { } else { // not enabled } + if(turnTime == 250 && !context.getPlayerManager().listBots().isEmpty()) { + try { + context.getPlayerManager().AILogic(); + for (Player p : context.getPlayerManager().listRegisteredPlayers()) { + context.getPlayerManager().sendMoveBar(p); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } // if turn ended if ( diff --git a/server/src/com/benberi/cadesim/server/model/player/Player.java b/server/src/com/benberi/cadesim/server/model/player/Player.java index 9dbec9d..77e692d 100644 --- a/server/src/com/benberi/cadesim/server/model/player/Player.java +++ b/server/src/com/benberi/cadesim/server/model/player/Player.java @@ -7,6 +7,7 @@ import com.benberi.cadesim.server.model.cade.Team; import com.benberi.cadesim.server.model.cade.map.BlockadeMap; import com.benberi.cadesim.server.model.cade.map.flag.Flag; +import com.benberi.cadesim.server.model.player.ai.util.AStarNode; import com.benberi.cadesim.server.model.player.ai.util.NPC_Type; import com.benberi.cadesim.server.model.player.collision.PlayerCollisionStorage; import com.benberi.cadesim.server.model.player.domain.JobbersQuality; @@ -142,6 +143,9 @@ public void setFirstEntry(boolean firstEntry) { private boolean enteredSafeLandside = false; // reason why private boolean enteredSafeOceanside = false; // " + private List path; + private Position goal; + /** * when the player was last seen alive */ @@ -958,4 +962,25 @@ public NPC_Type getType() { public void setType(NPC_Type typeValue) { type = typeValue; } + + public void setPath(List path) { + this.path = path; + + } + + public void clearPath() { + if(this.path != null) this.path.clear(); + } + + public List getPath() { + return this.path; + } + + public void setGoal(Position goal) { + this.goal = goal; + } + + public Position getGoal() { + return this.goal; + } } diff --git a/server/src/com/benberi/cadesim/server/model/player/PlayerManager.java b/server/src/com/benberi/cadesim/server/model/player/PlayerManager.java index 0eced26..b076394 100644 --- a/server/src/com/benberi/cadesim/server/model/player/PlayerManager.java +++ b/server/src/com/benberi/cadesim/server/model/player/PlayerManager.java @@ -12,7 +12,9 @@ import com.benberi.cadesim.server.model.player.ai.NPC_Type2; import com.benberi.cadesim.server.model.player.ai.NPC_Type3; import com.benberi.cadesim.server.model.player.ai.NPC_Type4; +import com.benberi.cadesim.server.model.player.ai.util.AStarNode; import com.benberi.cadesim.server.model.player.ai.util.AStarSearch; +import com.benberi.cadesim.server.model.player.ai.util.DFS; import com.benberi.cadesim.server.model.player.ai.util.NPC_Type; import com.benberi.cadesim.server.model.player.collision.CollisionCalculator; import com.benberi.cadesim.server.model.player.domain.JobbersQuality; @@ -264,6 +266,7 @@ public void setGameEnded(boolean gameEnded) { this.gameEnded = gameEnded; } + public DFS dfs; /** * constructor */ @@ -271,6 +274,7 @@ public PlayerManager(ServerContext context) { this.context = context; this.collision = new CollisionCalculator(context, this); this.algorithm = new AStarSearch(context); + this.dfs = new DFS(context); resetTemporarySettings(); } @@ -1211,6 +1215,17 @@ private void sendTime() { } } + static List> chopped(List list, final int L) { + List> parts = new ArrayList>(); + final int N = list.size(); + for (int i = 0; i < N; i += L) { + parts.add(new ArrayList( + list.subList(i, Math.min(N, i + L))) + ); + } + return parts; + } + public void sendAfterTurn() { // deal with sunk/unspawned ships first for (Player p : listRegisteredPlayers()) { @@ -1222,18 +1237,34 @@ public void sendAfterTurn() { p.giveLife(); } } +// dfs.search(listRegisteredPlayers().get(0)); +// dfs.search(listRegisteredPlayers().get(1)); // then deal with individual players for (Player p : listRegisteredPlayers()) { p.getPackets().sendPositions(); sendMoveBar(p); p.getPackets().sendFlags(); } - - for(Player other : listBots()) { + } + + public void AILogic() { + for(Player other : listBots()) { + other.clearPath(); + Position destination = getMaxTilePoints(other); + if(destination == null || this.equals(destination)) { + return; + } + List path = getAlgorithm().findPath(other, destination); + if(path != null && !path.isEmpty()) { + other.setPath(path); + } other.performLogic(); } + for (Player p : listRegisteredPlayers()) { + sendMoveBar(p); + } } - + public int getPointsDefender() { return pointsDefender; } @@ -1792,6 +1823,9 @@ public Map checkTilePoints(Player player) { public Position getMaxTilePoints(Player player) { Map sortedMap = new TreeMap(Comparator.reverseOrder()); sortedMap.putAll(checkTilePoints(player)); + if(sortedMap.size() == 0) { + return null; + } return (Position)sortedMap.values().toArray()[0]; } @@ -1819,9 +1853,10 @@ public void spawnAI() { int playerListSize = listRegisteredPlayers().size(); switch(ServerConfiguration.getAISetting()) { case "off": + sendTeamInfo(); return; case "easy": - for (int i =0; i < playerListSize ; i++) { + for (int i =0; i < playerListSize * 3; i++) { createBot(NPC_Type.TYPE4, randomBotName(), listRegisteredPlayers().get(0).getVessel().getID(), getBotTeam(), null, getBotFace(), 0.0f); } // createBot(NPC_Type.TYPE3, randomBotName(), 0, getBotTeam(), null, getBotFace(), 0.0f); @@ -1829,6 +1864,7 @@ public void spawnAI() { // createBot(NPC_Type.TYPE2, randomBotName(), 2, getBotTeam(), null, getBotFace(), 0.0f); // createBot(NPC_Type.TYPE3, randomBotName(), 8, getBotTeam(), null, getBotFace(), 0.0f); respawnAI(); + sendTeamInfo(); return; case "medium": @@ -1839,6 +1875,7 @@ public void spawnAI() { createBot(NPC_Type.TYPE2, randomBotName(), 3, getBotTeam(), null, getBotFace(), 0.0f); createBot(NPC_Type.TYPE3, randomBotName(), 4, getBotTeam(), null, getBotFace(), 0.0f); respawnAI(); + sendTeamInfo(); return; case "hard": for (int i =0; i < playerListSize * 2 ; i++) { @@ -1850,10 +1887,12 @@ public void spawnAI() { createBot(NPC_Type.TYPE3, randomBotName(), 8, getBotTeam(), null, getBotFace(), 0.0f); createBot(NPC_Type.TYPE3, randomBotName(), 8, getBotTeam(), null, getBotFace(), 0.0f); respawnAI(); + sendTeamInfo(); return; default: return; } + } private void printTeams(Player pl, boolean verbose) { diff --git a/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type1.java b/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type1.java index ac3eec2..638ab74 100644 --- a/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type1.java +++ b/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type1.java @@ -3,8 +3,6 @@ import com.benberi.cadesim.server.ServerContext; import com.benberi.cadesim.server.model.player.Player; import com.benberi.cadesim.server.model.player.ai.util.NPC_Type; -import com.benberi.cadesim.server.model.player.move.MoveType; - import io.netty.channel.Channel; /* @@ -25,12 +23,30 @@ public NPC_Type1(ServerContext ctx, Channel c) { @Override public void calculateRoute() { - +// if(getPath() != null) { +// if(getVessel().has3Moves()) { +// +// for(int slot = 0; slot < 4; slot++) { //moves to enter +// if(slot == 0) { +// continue; +// } +// if(slot < getPath().size()) { +// getMoves().setMove(slot, getPath().get(slot-1).move); +// } +// } +// }else { +// for(int slot = 0; slot < 4; slot++) { //moves to enter +// if(slot < getPath().size()) { +// getMoves().setMove(slot, getPath().get(slot).move); +// } +// } +// } +// } } @Override public void performLogic() { - getMoves().setMove(0,MoveType.FORWARD); + calculateRoute(); } @Override diff --git a/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type2.java b/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type2.java index f5a67be..6dcfc4c 100644 --- a/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type2.java +++ b/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type2.java @@ -3,8 +3,6 @@ import com.benberi.cadesim.server.ServerContext; import com.benberi.cadesim.server.model.player.Player; import com.benberi.cadesim.server.model.player.ai.util.NPC_Type; -import com.benberi.cadesim.server.model.player.move.MoveType; - import io.netty.channel.Channel; /* @@ -25,12 +23,29 @@ public NPC_Type2(ServerContext ctx, Channel c) { @Override public void calculateRoute() { - +// if(getPath() != null) { +// if(getVessel().has3Moves()) { +// for(int slot = 0; slot < 4; slot++) { //moves to enter +// if(slot == 0) { +// continue; +// } +// if(slot < getPath().size()) { +// getMoves().setMove(slot, getPath().get(slot-1).move); +// } +// } +// }else { +// for(int slot = 0; slot < 4; slot++) { //moves to enter +// if(slot < getPath().size()) { +// getMoves().setMove(slot, getPath().get(slot).move); +// } +// } +// } +// } } @Override public void performLogic() { - getMoves().setMove(0,MoveType.FORWARD); + calculateRoute(); } @Override diff --git a/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type3.java b/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type3.java index 58c86f2..b7a95fa 100644 --- a/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type3.java +++ b/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type3.java @@ -3,8 +3,6 @@ import com.benberi.cadesim.server.ServerContext; import com.benberi.cadesim.server.model.player.Player; import com.benberi.cadesim.server.model.player.ai.util.NPC_Type; -import com.benberi.cadesim.server.model.player.move.MoveType; - import io.netty.channel.Channel; /* @@ -25,12 +23,29 @@ public NPC_Type3(ServerContext ctx, Channel c) { @Override public void calculateRoute() { - +// if(getPath() != null) { +// if(getVessel().has3Moves()) { +// for(int slot = 0; slot < 4; slot++) { //moves to enter +// if(slot == 0) { +// continue; +// } +// if(slot < getPath().size()) { +// getMoves().setMove(slot, getPath().get(slot-1).move); +// } +// } +// }else { +// for(int slot = 0; slot < 4; slot++) { //moves to enter +// if(slot < getPath().size()) { +// getMoves().setMove(slot, getPath().get(slot).move); +// } +// } +// } +// } } @Override public void performLogic() { - getMoves().setMove(0, MoveType.FORWARD); + calculateRoute(); } @Override diff --git a/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type4.java b/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type4.java index 300e475..9759b14 100644 --- a/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type4.java +++ b/server/src/com/benberi/cadesim/server/model/player/ai/NPC_Type4.java @@ -1,15 +1,8 @@ package com.benberi.cadesim.server.model.player.ai; -import java.util.List; - import com.benberi.cadesim.server.ServerContext; import com.benberi.cadesim.server.model.player.Player; -import com.benberi.cadesim.server.model.player.ai.util.AStarNode; import com.benberi.cadesim.server.model.player.ai.util.NPC_Type; -import com.benberi.cadesim.server.model.player.move.MoveType; -import com.benberi.cadesim.server.model.player.vessel.VesselFace; -import com.benberi.cadesim.server.util.Position; - import io.netty.channel.Channel; /* @@ -17,8 +10,8 @@ * Priority - shooting, flag points */ public class NPC_Type4 extends Player { + @SuppressWarnings("unused") private ServerContext context; - private List path = null; public NPC_Type4(ServerContext ctx, Channel c) { super(ctx,c); @@ -30,81 +23,25 @@ public NPC_Type4(ServerContext ctx, Channel c) { @Override public void calculateRoute() { - //Position destination = new Position (5,3); //test position - Position destination = context.getPlayerManager().getMaxTilePoints(this); - if(this.equals(destination)) { - return; - } - path = context.getPlayerManager().getAlgorithm().findPath(getFace(), this, destination); - VesselFace face = getFace(); - if(path != null) { - if(path.size() > 0) { - int numberOfMoves = getVessel().has3Moves() ? 3 : 4; - Position currentPosition = this.copy(); - for(int slot = 0; slot <= numberOfMoves; slot++) { //moves to enter - int positionIndex = (path.size() - 1) - (slot); //subtract slot to allow multiple moves - if(positionIndex < 0 || path.size() < slot) { // make sure it doesn't count too far - return; - } - Position pos = path.get(positionIndex).position; - Position left = MoveType.LEFT.getFinalPosition(currentPosition, face); - Position right = MoveType.RIGHT.getFinalPosition(currentPosition, face); - Position forward = MoveType.FORWARD.getFinalPosition(currentPosition, face); - - if(left.equals(pos)) { - currentPosition.add(left.getX() - getX(), left.getY() - getY()); - getMoves().setMove(slot, MoveType.LEFT); - - switch(face) { - case NORTH: - face = VesselFace.WEST; - break; - case SOUTH: - face = VesselFace.EAST; - break; - case WEST: - face = VesselFace.SOUTH; - break; - case EAST: - face = VesselFace.NORTH; - break; - } - }else if(right.equals(pos)) { - currentPosition.add(right.getX() - getX(), right.getY() - getY()); - getMoves().setMove(slot, MoveType.RIGHT); - switch(face) { - case NORTH: - face = VesselFace.EAST; - break; - case SOUTH: - face = VesselFace.WEST; - break; - case WEST: - face = VesselFace.NORTH; - break; - case EAST: - face = VesselFace.SOUTH; - break; - } - }else if(forward.equals(pos)){ - currentPosition.add(forward.getX() - getX(), forward.getY() - getY()); - getMoves().setMove(slot, MoveType.FORWARD); - switch(face) { - case NORTH: - face = VesselFace.NORTH; - break; - case SOUTH: - face = VesselFace.SOUTH; - break; - case WEST: - face = VesselFace.WEST; - break; - case EAST: - face = VesselFace.EAST; - break; - } - } - } + if(getPath() != null) { + if(getVessel().has3Moves()) { + for(int slot = 0; slot < 4; slot++) { //moves to enter + if(slot - 1 < 0 && getPath().size() == 0) { + return; + } + if(slot == 0) { + continue; + } + if(slot < getPath().size()) { + getMoves().setMove(slot, getPath().get(slot-1).move); + } + } + }else { + for(int slot = 0; slot < 4; slot++) { //moves to enter + if(slot < getPath().size()) { + getMoves().setMove(slot, getPath().get(slot).move); + } + } } } } diff --git a/server/src/com/benberi/cadesim/server/model/player/ai/util/AStarNode.java b/server/src/com/benberi/cadesim/server/model/player/ai/util/AStarNode.java index 448719a..78e6c97 100644 --- a/server/src/com/benberi/cadesim/server/model/player/ai/util/AStarNode.java +++ b/server/src/com/benberi/cadesim/server/model/player/ai/util/AStarNode.java @@ -1,5 +1,6 @@ package com.benberi.cadesim.server.model.player.ai.util; +import com.benberi.cadesim.server.model.player.move.MoveType; import com.benberi.cadesim.server.model.player.vessel.VesselFace; import com.benberi.cadesim.server.util.Position; @@ -7,12 +8,14 @@ public class AStarNode { public Position position; public VesselFace face; + public MoveType move; public AStarNode parent; public double fCost, gCost, hCost; - public AStarNode(Position position, VesselFace face, AStarNode parent, double gCost, double hCost) { + public AStarNode(Position position, VesselFace face, MoveType move, AStarNode parent, double gCost, double hCost) { this.position = position; this.face = face; + this.move = move; this.parent = parent; this.gCost = gCost; this.hCost = hCost; diff --git a/server/src/com/benberi/cadesim/server/model/player/ai/util/AStarSearch.java b/server/src/com/benberi/cadesim/server/model/player/ai/util/AStarSearch.java index 0016411..36bc451 100644 --- a/server/src/com/benberi/cadesim/server/model/player/ai/util/AStarSearch.java +++ b/server/src/com/benberi/cadesim/server/model/player/ai/util/AStarSearch.java @@ -3,6 +3,7 @@ import com.benberi.cadesim.server.ServerContext; import com.benberi.cadesim.server.model.player.Player; +import com.benberi.cadesim.server.model.player.move.MoveType; import com.benberi.cadesim.server.model.player.vessel.VesselFace; import com.benberi.cadesim.server.util.Position; @@ -16,6 +17,9 @@ public class AStarSearch { private ServerContext context; private List openList; private List closedList; + + private double ORTHOGONAL_COST = 1.0; + private double DIAGONAL_COST = ORTHOGONAL_COST * Math.sqrt(2.0); public AStarSearch(ServerContext context) { this.context = context; @@ -32,11 +36,14 @@ public int compare(AStarNode n0, AStarNode n1) { }; - public List findPath(VesselFace startFace, Position start, Position goal){ + public List findPath(Player bot, Position goal){ openList = new ArrayList(); closedList = new ArrayList(); List neighbors = new ArrayList(); - AStarNode current = new AStarNode(start, startFace, null, 0, start.distance(goal)); + int leftAmount = bot.getMoveTokens().countLeftMoves(); + int forwardAmount = bot.getMoveTokens().countForwardMoves(); + int rightAmount = bot.getMoveTokens().countRightMoves(); + AStarNode current = new AStarNode(bot, bot.getFace(), MoveType.NONE, null, 0, bot.distance(goal)); openList.add(current); while(openList.size() > 0) { Collections.sort(openList, nodeSorter); @@ -49,6 +56,7 @@ public List findPath(VesselFace startFace, Position start, Position g } openList.clear(); closedList.clear(); + Collections.reverse(path); return path; } openList.remove(current); @@ -58,36 +66,57 @@ public List findPath(VesselFace startFace, Position start, Position g int y = current.position.getY(); switch (current.face) { case NORTH: - neighbors.add(new AStarNode(new Position(x-1, y+1), VesselFace.WEST,current,0,0)); - neighbors.add(new AStarNode(new Position(x, y+1), VesselFace.NORTH,current,0,0)); - neighbors.add(new AStarNode(new Position(x+1, y+1), VesselFace.EAST,current,0,0)); + neighbors.add(new AStarNode(new Position(x, y), VesselFace.NORTH, MoveType.NONE,current,0,0)); + neighbors.add(new AStarNode(new Position(x, y+1), VesselFace.NORTH, MoveType.FORWARD,current,0,0)); + neighbors.add(new AStarNode(new Position(x-1, y+1), VesselFace.WEST, MoveType.LEFT,current,0,0)); + neighbors.add(new AStarNode(new Position(x+1, y+1), VesselFace.EAST, MoveType.RIGHT,current,0,0)); break; case EAST: - neighbors.add(new AStarNode(new Position(x+1, y+1), VesselFace.NORTH,current,0,0)); - neighbors.add(new AStarNode(new Position(x+1, y), VesselFace.EAST,current,0,0)); - neighbors.add(new AStarNode(new Position(x+1, y-1), VesselFace.SOUTH,current,0,0)); + neighbors.add(new AStarNode(new Position(x, y), VesselFace.EAST, MoveType.NONE,current,0,0)); + neighbors.add(new AStarNode(new Position(x+1, y), VesselFace.EAST, MoveType.FORWARD,current,0,0)); + neighbors.add(new AStarNode(new Position(x+1, y+1), VesselFace.NORTH, MoveType.LEFT,current,0,0)); + neighbors.add(new AStarNode(new Position(x+1, y-1), VesselFace.SOUTH, MoveType.RIGHT,current,0,0)); break; case SOUTH: - neighbors.add(new AStarNode(new Position(x-1, y-1), VesselFace.WEST,current,0,0)); - neighbors.add(new AStarNode(new Position(x, y-1), VesselFace.SOUTH,current,0,0)); - neighbors.add(new AStarNode(new Position(x+1, y-1), VesselFace.EAST,current,0,0)); + neighbors.add(new AStarNode(new Position(x, y), VesselFace.SOUTH, MoveType.NONE,current,0,0)); + neighbors.add(new AStarNode(new Position(x, y-1), VesselFace.SOUTH, MoveType.FORWARD,current,0,0)); + neighbors.add(new AStarNode(new Position(x-1, y-1), VesselFace.WEST, MoveType.RIGHT,current,0,0)); + neighbors.add(new AStarNode(new Position(x+1, y-1), VesselFace.EAST, MoveType.LEFT,current,0,0)); break; case WEST: - neighbors.add(new AStarNode(new Position(x-1, y+1), VesselFace.NORTH,current,0,0)); - neighbors.add(new AStarNode(new Position(x-1, y), VesselFace.WEST,current,0,0)); - neighbors.add(new AStarNode(new Position(x-1, y-1), VesselFace.SOUTH,current,0,0)); + neighbors.add(new AStarNode(new Position(x, y), VesselFace.WEST, MoveType.NONE,current,0,0)); + neighbors.add(new AStarNode(new Position(x-1, y), VesselFace.WEST, MoveType.FORWARD,current,0,0)); + neighbors.add(new AStarNode(new Position(x-1, y+1), VesselFace.NORTH, MoveType.RIGHT,current,0,0)); + neighbors.add(new AStarNode(new Position(x-1, y-1), VesselFace.SOUTH, MoveType.LEFT,current,0,0)); break; } for(AStarNode neighborNode : neighbors) { + //skip if out of particular move + if((leftAmount == 0 && neighborNode.move == MoveType.LEFT) || + (rightAmount == 0 && neighborNode.move == MoveType.RIGHT) || + (forwardAmount == 0 && neighborNode.move == MoveType.FORWARD)) { + continue; + } + // Compute the cost to get *to* the action tile. + double costToReach = current.position.distance(neighborNode.position); + if(neighborNode.move == MoveType.FORWARD) { + costToReach += 0.5; + } int at = context.getMap().getTile(neighborNode.position.getX(), neighborNode.position.getY()); - if(at > 2){ // special action tiles - //TODO - allow ship to navigate through action tiles by looking ahead -// neighborNode.position = context.getMap().getNextActionTilePositionForTile(neighborNode.position, at); + if(context.getMap().isWind(at)){ // special action tiles + neighborNode.position = context.getMap().getNextActionTilePositionForTile(neighborNode.position, at); + costToReach += getActionCost(neighborNode.position, at); } + if(context.getMap().isWhirlpool(at)) { + neighborNode.position = context.getMap().getFinalActionTilePosition(at, neighborNode.position, 0); + neighborNode.face = neighborNode.face.getNext(); + costToReach += getActionCost(neighborNode.position, at); + } if(at == 1 || at == 2) continue; // ignore rocks - double gCost = current.gCost + current.position.distance(neighborNode.position); - double hCost = neighborNode.position.distance(goal); - AStarNode node = new AStarNode(neighborNode.position, neighborNode.face, current, gCost, hCost); + if(context.getPlayerManager().getPlayerByPosition(neighborNode.position.getX(), neighborNode.position.getY()) != null) continue; // skip if player + double gCost = current.gCost + costToReach; + double hCost = heuristicDistance(neighborNode.position,goal); + AStarNode node = new AStarNode(neighborNode.position, neighborNode.face,neighborNode.move, current, gCost, hCost); if(positionInList(closedList, neighborNode.position) && gCost >= node.gCost) continue; if(!positionInList(openList, neighborNode.position) || gCost < node.gCost) openList.add(node); } @@ -96,6 +125,24 @@ public List findPath(VesselFace startFace, Position start, Position g return null; } + private double getActionCost(Position node, int currentTile) { + if(currentTile > 3 && currentTile < 11) { + return 0.2; + }else { + return 1; + } + } + + private double heuristicDistance(Position current, Position goal) { + int xDifference = Math.abs(goal.getX() - current.getX()); + int yDifference = Math.abs(goal.getY() - current.getY()); + + int diagonal = Math.min(xDifference, yDifference); + int orthogonal = xDifference + yDifference - 2 * diagonal; + + return orthogonal * ORTHOGONAL_COST + diagonal * DIAGONAL_COST; + } + private boolean positionInList(List list, Position position) { for(AStarNode n : list) { if(n.position.equals(position)) return true; diff --git a/server/src/com/benberi/cadesim/server/model/player/ai/util/DFS.java b/server/src/com/benberi/cadesim/server/model/player/ai/util/DFS.java new file mode 100644 index 0000000..87ef6f3 --- /dev/null +++ b/server/src/com/benberi/cadesim/server/model/player/ai/util/DFS.java @@ -0,0 +1,90 @@ +package com.benberi.cadesim.server.model.player.ai.util; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.Stack; + +import com.benberi.cadesim.server.ServerContext; +import com.benberi.cadesim.server.model.player.Player; +import com.benberi.cadesim.server.model.player.move.MoveType; +import com.benberi.cadesim.server.model.player.vessel.VesselFace; +import com.benberi.cadesim.server.util.Position; + +public class DFS { + private ServerContext context; + public Hashtable stateList; + + public DFS(ServerContext context) { + this.context = context; + } + + public void search(Player player) { + Position playerPosition = player.copy(); + List neighbors = new ArrayList<>(); + stateList = new Hashtable(); + MoveState currentState = new MoveState(playerPosition, player.getFace(), MoveType.NONE, 0); + Stack stack = new Stack(); + stack.add(currentState); + currentState.visited = true; + while (!stack.isEmpty()) + { + MoveState state = stack.pop(); + if(state.position.getX() < 0 || state.position.getY() < 0) { + continue; + } + stateList.put(state.position, state); + if(state.time < 4) { + int x = state.position.getX(); + int y = state.position.getY(); + switch (state.face) { + case NORTH: + neighbors.add(new MoveState(new Position(x,y), VesselFace.NORTH, MoveType.NONE,state.time + 1)); + neighbors.add(new MoveState(new Position(x,y+1), VesselFace.NORTH, MoveType.FORWARD,state.time + 1)); + neighbors.add(new MoveState(new Position(x-1,y+1), VesselFace.WEST, MoveType.LEFT, state.time + 1)); + neighbors.add(new MoveState(new Position(x+1,y+1), VesselFace.EAST, MoveType.RIGHT,state.time + 1)); + break; + case EAST: + neighbors.add(new MoveState(new Position(x,y), VesselFace.EAST, MoveType.NONE,state.time + 1)); + neighbors.add(new MoveState(new Position(x+1,y), VesselFace.EAST, MoveType.FORWARD,state.time + 1)); + neighbors.add(new MoveState(new Position(x+1,y+1), VesselFace.NORTH, MoveType.LEFT,state.time + 1)); + neighbors.add(new MoveState(new Position(x+1,y-1), VesselFace.SOUTH, MoveType.RIGHT,state.time + 1)); + break; + case SOUTH: + neighbors.add(new MoveState(new Position(x,y), VesselFace.SOUTH, MoveType.NONE,state.time + 1)); + neighbors.add(new MoveState(new Position(x,y-1), VesselFace.SOUTH, MoveType.FORWARD,state.time + 1)); + neighbors.add(new MoveState(new Position(x-1,y-1), VesselFace.WEST, MoveType.RIGHT,state.time + 1)); + neighbors.add(new MoveState(new Position(x+1,y+1), VesselFace.EAST, MoveType.LEFT,state.time + 1)); + break; + case WEST: + neighbors.add(new MoveState(new Position(x,y), VesselFace.WEST, MoveType.NONE,state.time + 1)); + neighbors.add(new MoveState(new Position(x-1,y), VesselFace.WEST, MoveType.FORWARD,state.time + 1)); + neighbors.add(new MoveState(new Position(x-1,y+1), VesselFace.NORTH, MoveType.RIGHT,state.time + 1)); + neighbors.add(new MoveState(new Position(x-1,y-1), VesselFace.SOUTH, MoveType.LEFT,state.time + 1)); + break; + } + } + for(MoveState neighborState : neighbors) { + if(neighborState != null ) { + if(stateList.values().contains(neighborState)) { + continue; + } + int at = context.getMap().getTile(neighborState.position.getX(), neighborState.position.getY()); + if(at == 1 || at == 2) continue; // ignore rocks + if(context.getMap().isWind(at)){ // special action tiles + neighborState.position = context.getMap().getNextActionTilePositionForTile(neighborState.position, at); + } + if(context.getMap().isWhirlpool(at)) { + neighborState.position = context.getMap().getFinalActionTilePosition(at, neighborState.position, 0); + neighborState.face = neighborState.face.getNext(); + } + if(!neighborState.visited) + { + stack.add(neighborState); + neighborState.visited = true; + } + } + } + } + } +} diff --git a/server/src/com/benberi/cadesim/server/model/player/ai/util/MoveState.java b/server/src/com/benberi/cadesim/server/model/player/ai/util/MoveState.java new file mode 100644 index 0000000..6df83e3 --- /dev/null +++ b/server/src/com/benberi/cadesim/server/model/player/ai/util/MoveState.java @@ -0,0 +1,31 @@ +package com.benberi.cadesim.server.model.player.ai.util; + +import com.benberi.cadesim.server.model.player.move.MoveType; +import com.benberi.cadesim.server.model.player.vessel.VesselFace; +import com.benberi.cadesim.server.util.Position; + +public class MoveState { + public Position position; + public VesselFace face; + public MoveType move; + public int time; + public boolean visited = false; + + public MoveState(Position position, VesselFace face, MoveType move, int time) { + this.position = position; + this.face = face; + this.move = move; + this.time = time; + visited = false; + } + + @Override + public boolean equals(Object o) { + if(o instanceof MoveState) { + MoveState other = (MoveState) o; + return o == this || this.position.equals(other.position) && this.move == other.move && + this.time == other.time && this.face == other.face; + } + return super.equals(o); + } +} diff --git a/server/src/com/benberi/cadesim/server/model/player/vessel/VesselFace.java b/server/src/com/benberi/cadesim/server/model/player/vessel/VesselFace.java index 57e22ad..bb2e384 100644 --- a/server/src/com/benberi/cadesim/server/model/player/vessel/VesselFace.java +++ b/server/src/com/benberi/cadesim/server/model/player/vessel/VesselFace.java @@ -3,10 +3,11 @@ public enum VesselFace { NORTH(14), - SOUTH(6), WEST(10), + SOUTH(6), EAST(2); + private static VesselFace[] values = values(); private int directionId; VesselFace(int dir) { @@ -16,20 +17,13 @@ public enum VesselFace { public int getDirectionId() { return this.directionId; } + + public VesselFace getPrev() { + return values[(ordinal() + 1) % values.length]; + } public VesselFace getNext() { - switch (this) { - case NORTH: - return EAST; - case EAST: - return SOUTH; - case SOUTH: - return WEST; - case WEST: - return NORTH; - } - - return NORTH; + return values[(ordinal() - 1 + values.length) % values.length]; } public static VesselFace forId(int id) {