Skip to content

Commit

Permalink
Merge pull request #101 from EntelectChallenge/develop
Browse files Browse the repository at this point in the history
Develop to master merge
  • Loading branch information
RohanChhipa committed Jun 22, 2018
2 parents bed0583 + a75191d commit 63e7923
Show file tree
Hide file tree
Showing 99 changed files with 2,126 additions and 350 deletions.
2 changes: 1 addition & 1 deletion game-engine-interface/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>za.co.entelect.challenge</groupId>
<artifactId>game-engine-interface</artifactId>
<version>1.0.1</version>
<version>2.0.0</version>

<distributionManagement>
<repository>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package za.co.entelect.challenge.game.contracts.exceptions;

public class MatchFailedException extends Exception {

public MatchFailedException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package za.co.entelect.challenge.game.contracts.exceptions;

public class TimeoutException extends Exception {

public TimeoutException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package za.co.entelect.challenge.game.contracts.game;

import za.co.entelect.challenge.game.contracts.exceptions.TimeoutException;
import za.co.entelect.challenge.game.contracts.map.GameMap;

public interface GameEngine {

boolean isGameComplete(GameMap gameMap);

boolean isGameComplete(GameMap gameMap) throws TimeoutException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package za.co.entelect.challenge.game.contracts.game;

public class GameResult {

public String matchId = "";
public boolean isComplete = false;
public boolean isSuccessful = false;
public int winner = 0;
public int playerOnePoints = 0;
public int playerTwoPoints = 0;
public int roundsPlayed = 0;
public boolean verificationRequired = true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import za.co.entelect.challenge.game.contracts.command.Command;
import za.co.entelect.challenge.game.contracts.command.RawCommand;
import za.co.entelect.challenge.game.contracts.exceptions.TimeoutException;
import za.co.entelect.challenge.game.contracts.game.GamePlayer;
import za.co.entelect.challenge.game.contracts.map.GameMap;

Expand Down Expand Up @@ -34,6 +35,8 @@
*/
public abstract class Player {

private int number;

private String name;

private GamePlayer gamePlayer;
Expand Down Expand Up @@ -66,11 +69,13 @@ public String toString() {

public abstract void gameEnded(GameMap gameMap);

public abstract void playerKilled(GameMap gameMap);

public abstract void playerCommandFailed(GameMap gameMap, String reason);
public int getNumber() {
return number;
}

public abstract void firstRoundFailed(GameMap gameMap, String reason);
public void setNumber(int number) {
this.number = number;
}

public String getName() {
return name;
Expand Down
4 changes: 2 additions & 2 deletions game-engine/core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>game-engine</artifactId>
<groupId>za.co.entelect.challenge</groupId>
<version>1.1.1</version>
<version>2.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand All @@ -27,7 +27,7 @@
<dependency>
<groupId>za.co.entelect.challenge</groupId>
<artifactId>domain</artifactId>
<version>1.1.1</version>
<version>1.1.3</version>
<scope>compile</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package za.co.entelect.challenge.core.engine;

public class GameReferee {

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,38 @@

import za.co.entelect.challenge.config.GameConfig;
import za.co.entelect.challenge.entities.TowerDefenseGameMap;
import za.co.entelect.challenge.entities.TowerDefensePlayer;
import za.co.entelect.challenge.game.contracts.exceptions.TimeoutException;
import za.co.entelect.challenge.game.contracts.game.GameEngine;
import za.co.entelect.challenge.game.contracts.map.GameMap;

import java.util.List;

public class TowerDefenseGameEngine implements GameEngine {

public TowerDefenseGameEngine(String configLocation) {
GameConfig.initConfig(configLocation);
}

@Override
public boolean isGameComplete(GameMap gameMap) {
public boolean isGameComplete(GameMap gameMap) throws TimeoutException {
TowerDefenseGameMap towerDefenseGameMap = (TowerDefenseGameMap) gameMap;

if (!playersInValidState(towerDefenseGameMap)) {
throw new TimeoutException("Too many do nothing commands received due to exceptions");
}

return gameMap.getCurrentRound() > GameConfig.getMaxRounds() || (towerDefenseGameMap.getDeadPlayers().size() > 0);
}

private boolean playersInValidState(TowerDefenseGameMap towerDefenseGameMap) {
List<TowerDefensePlayer> towerDefensePlayers = towerDefenseGameMap.getTowerDefensePlayers();
for (TowerDefensePlayer towerDefensePlayer : towerDefensePlayers) {
if (towerDefensePlayer.getConsecutiveDoNothings() >= GameConfig.getMaxDoNothings()) {
return false;
}
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package za.co.entelect.challenge.core.engine;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import za.co.entelect.challenge.commands.DeconstructBuildingCommand;
import za.co.entelect.challenge.commands.DoNothingCommand;
import za.co.entelect.challenge.commands.PlaceBuildingCommand;
import za.co.entelect.challenge.config.GameConfig;
import za.co.entelect.challenge.entities.Building;
import za.co.entelect.challenge.entities.Cell;
import za.co.entelect.challenge.entities.TowerDefenseGameMap;
import za.co.entelect.challenge.entities.TowerDefensePlayer;
import za.co.entelect.challenge.enums.BuildingType;
Expand All @@ -14,10 +16,9 @@
import za.co.entelect.challenge.game.contracts.game.GamePlayer;
import za.co.entelect.challenge.game.contracts.game.GameRoundProcessor;
import za.co.entelect.challenge.game.contracts.map.GameMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Map;
import java.util.stream.IntStream;
Expand All @@ -32,11 +33,18 @@ public class TowerDefenseRoundProcessor implements GameRoundProcessor {
public boolean processRound(GameMap gameMap, Hashtable<GamePlayer, RawCommand> commands) {
towerDefenseGameMap = (TowerDefenseGameMap) gameMap;
towerDefenseGameMap.clearErrorList();
towerDefenseGameMap.clearTeslaTargetList();

processCommands(commands);

constructBuildings();

try {
fireTeslaTowers();
} catch (Exception e) {
log.error(e);
}

createMissilesFromGuns();
calculateMissileMovement();
removeDeadEntities();
Expand All @@ -52,8 +60,7 @@ public ArrayList<String> getErrorList() {
}

private void constructBuildings() {
towerDefenseGameMap.getBuildings().stream()
.filter(b -> !b.isConstructed())
towerDefenseGameMap.getBuildings()
.forEach(Building::decreaseConstructionTimeLeft);
}

Expand All @@ -63,6 +70,34 @@ private void createMissilesFromGuns() {
.forEach(b -> towerDefenseGameMap.addMissileFromBuilding(b));
}

private void fireTeslaTowers() throws Exception {
ArrayList<Building> playerTeslaTowers = new ArrayList<>();
towerDefenseGameMap.getBuildings().stream()
.filter(b -> b.getBuildingType().equals(BuildingType.TESLA))
.filter(Building::isConstructed)
.forEach(b -> playerTeslaTowers.add(b));

//Oldest building first.
playerTeslaTowers.sort(Comparator.comparing(Building::getConstructionTimeLeft));

for (Building possibleFiringTeslaTower : playerTeslaTowers) {

TowerDefensePlayer currentPlayer = towerDefenseGameMap.getPlayer(possibleFiringTeslaTower.getPlayerType());
int playerEnergy = currentPlayer.getEnergy();

if (playerEnergy >= possibleFiringTeslaTower.getEnergyPerShot() && possibleFiringTeslaTower.getWeaponCooldownTimeLeft() == 0) {
currentPlayer.removeEnergy(possibleFiringTeslaTower.getEnergyPerShot());
towerDefenseGameMap.fireTeslaTower(possibleFiringTeslaTower);
possibleFiringTeslaTower.resetCooldown();
} else {

if (possibleFiringTeslaTower.getWeaponCooldownTimeLeft() > 0) {
possibleFiringTeslaTower.decreaseCooldown();
}
}
}
}

private void processCommands(Hashtable<GamePlayer, RawCommand> commands) {
for (Map.Entry<GamePlayer, RawCommand> gamePlayerCommandEntry : commands.entrySet()) {
GamePlayer player = gamePlayerCommandEntry.getKey();
Expand Down Expand Up @@ -102,83 +137,72 @@ private void removeDeadEntities() {
.forEach(p -> towerDefenseGameMap.removeMissile(p));
}

private static boolean positionMatch(Cell a, Cell b) {
return (a.getY() == b.getY()) && (a.getX() == b.getX());
}

private void calculateMissileMovement() {
towerDefenseGameMap.getMissiles()
.forEach(missile ->
IntStream.rangeClosed(1, missile.getSpeed()) // higher speed bullets
.forEach(i -> {
if (missile.getSpeed() > 0) {
towerDefenseGameMap.moveMissileSingleSpace(missile);
towerDefenseGameMap.getBuildings().stream()
.filter(b -> b.isConstructed()
&& positionMatch(missile, b)
&& !b.isPlayers(missile.getPlayerType())
&& b.getHealth() > 0)
.forEach(b -> {
TowerDefensePlayer missileOwner = null;
try {
missileOwner = towerDefenseGameMap.getPlayer(missile.getPlayerType());
} catch (Exception e) {
log.error(e);
}
b.damageSelf(missile, missileOwner);
});
}
})
int maxMissileSpeed = towerDefenseGameMap.getMissiles().stream()
.mapToInt(m -> m.getSpeed())
.max().orElse(0);

IntStream.rangeClosed(1, maxMissileSpeed)
.forEach(i -> new ArrayList<>(towerDefenseGameMap.getMissiles()).stream()
.filter(m -> m.getUnprocessedMovement() > 0)
.forEach(missile -> towerDefenseGameMap.moveMissileSingleSpace(missile))
);

towerDefenseGameMap.getMissiles()
.forEach(m -> m.resetUnprocessedMovement());
}

// Converts Raw Commands into Commands the game engine can understand
private void parseAndExecuteCommand(RawCommand command, GamePlayer player) {
String commandSting = command.getCommand();

String[] commandLine = commandSting.split(",");

DoNothingCommand doNothingCommand = new DoNothingCommand();

TowerDefensePlayer towerDefensePlayer = (TowerDefensePlayer) player;

if (commandLine.length == 1) {
doNothingCommand.performCommand(towerDefenseGameMap, player);
doNothingCommand.performCommand(towerDefenseGameMap, player, commandLine[0].equals("Exception"));
return;
}
if (commandLine.length != 3) {
doNothingCommand.performCommand(towerDefenseGameMap, player);
doNothingCommand.performCommand(towerDefenseGameMap, player, true);
towerDefenseGameMap.addErrorToErrorList(String.format(
"Unable to parse command expected 3 parameters, got %d", commandLine.length), towerDefensePlayer);
}

try {
int positionX = Integer.parseInt(commandLine[0]);
int positionY = Integer.parseInt(commandLine[1]);
BuildingType buildingType = BuildingType.values()[Integer.parseInt(commandLine[2])];

new PlaceBuildingCommand(positionX, positionY, buildingType).performCommand(towerDefenseGameMap, player);
towerDefensePlayer.setConsecutiveDoNothings(0);
if (buildingType == BuildingType.DECONSTRUCT) {
new DeconstructBuildingCommand(positionX, positionY).performCommand(towerDefenseGameMap, player);
} else {
new PlaceBuildingCommand(positionX, positionY, buildingType).performCommand(towerDefenseGameMap, player);
}
} catch (NumberFormatException e) {
doNothingCommand.performCommand(towerDefenseGameMap, player);
doNothingCommand.performCommand(towerDefenseGameMap, player, true);
towerDefenseGameMap.addErrorToErrorList(String.format(
"Unable to parse command entries, all parameters should be integers. Received:%s",
commandSting), towerDefensePlayer);

log.error(towerDefenseGameMap.getErrorList().get(towerDefenseGameMap.getErrorList().size() - 1));
} catch (IllegalArgumentException e) {
doNothingCommand.performCommand(towerDefenseGameMap, player);
doNothingCommand.performCommand(towerDefenseGameMap, player, true);
towerDefenseGameMap.addErrorToErrorList(String.format(
"Unable to parse building type: Expected 0[Defense], 1[Attack], 2[Energy]. Received:%s",
"Unable to parse building type: Expected 0[Defense], 1[Attack], 2[Energy], 3[Deconstruct], 4[Tesla]. Received:%s",
commandLine[2]), towerDefensePlayer);

log.error(towerDefenseGameMap.getErrorList().get(towerDefenseGameMap.getErrorList().size() - 1));
} catch (InvalidCommandException e) {
doNothingCommand.performCommand(towerDefenseGameMap, player);
doNothingCommand.performCommand(towerDefenseGameMap, player, true);
towerDefenseGameMap.addErrorToErrorList(
"Invalid command received: " + e.getMessage(), towerDefensePlayer);

log.error(towerDefenseGameMap.getErrorList().get(towerDefenseGameMap.getErrorList().size() - 1));
} catch (IndexOutOfBoundsException e) {
doNothingCommand.performCommand(towerDefenseGameMap, player);
doNothingCommand.performCommand(towerDefenseGameMap, player, true);
towerDefenseGameMap.addErrorToErrorList(String.format(
"Out of map bounds, X:%s Y: %s", commandLine[0], commandLine[1]), towerDefensePlayer);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ public GameDetails(int round) {
this.mapHeight = GameConfig.getMapHeight();
this.roundIncomeEnergy = GameConfig.getRoundIncomeEnergy();

Arrays.asList(BuildingType.values()).forEach(bt -> buildingPrices.put(bt, BuildingFactory.createBuildingStats(bt).price));
Arrays.stream(BuildingType.values())
.filter(bt -> bt != BuildingType.DECONSTRUCT)
.forEach(bt -> buildingPrices.put(bt, BuildingFactory.createBuildingStats(bt).price));

Arrays.stream(BuildingType.values()).forEach(bt -> buildingsStats.put(bt, BuildingFactory.createBuildingStats(bt)));
Arrays.stream(BuildingType.values())
.filter(bt -> bt != BuildingType.DECONSTRUCT)
.forEach(bt -> buildingsStats.put(bt, BuildingFactory.createBuildingStats(bt)));
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package za.co.entelect.challenge.core.entities;

import za.co.entelect.challenge.entities.Cell;

import java.util.ArrayList;
import java.util.List;

public class TowerDefenseJsonContainer {

protected GameDetails gameDetails;
protected PlayerData[] players;
protected CellStateContainer[][] gameMap;
protected ArrayList<List<Cell>> teslaHitList;

public TowerDefenseJsonContainer(CellStateContainer[][] gameMap, PlayerData[] players, int round){
gameDetails = new GameDetails(round);
public TowerDefenseJsonContainer(CellStateContainer[][] gameMap, PlayerData[] players, int round, ArrayList<List<Cell>> teslaHitList) {
this.gameDetails = new GameDetails(round);
this.players = players;
this.gameMap = gameMap;
this.teslaHitList = teslaHitList;
}
}
Loading

0 comments on commit 63e7923

Please sign in to comment.