diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..7e25fbe --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +World.java \ No newline at end of file diff --git a/README.md b/README.md index cccb987..4457fb8 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The player object is a representation of the user and it's agency in the game wo -- Warrior -- Mage - Spells -- World generation +- World.World generation -- Terrain types - Loot - Equipment diff --git a/pom.xml b/pom.xml index 4954a6f..f3d4870 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,7 @@ 23 UTF-8 + org.junit.jupiter @@ -33,5 +34,5 @@ test + - \ No newline at end of file diff --git a/src/main/java/Attack.java b/src/main/java/Attack.java deleted file mode 100644 index a292f3e..0000000 --- a/src/main/java/Attack.java +++ /dev/null @@ -1,22 +0,0 @@ -public abstract class Attack { - //Namnet och bägge dessa siffror är helt lösryckta - private static final String DEFAULT_NAME = "DEFAULT_NAME"; - private static final double DEFAULT_ENERGY_COST = 5d; - private static final double DEFAULT_DAMAGE = 5d; - - private String name; - private double energyCost; - private double damage; - - public Attack() { - name = DEFAULT_NAME; - energyCost = DEFAULT_ENERGY_COST; - damage = DEFAULT_DAMAGE; - } - - public Attack(String name, double energyCost, double damage) { - this.name = name; - this.energyCost = energyCost; - this.damage = damage; - } -} diff --git a/src/main/java/Character.java b/src/main/java/Character.java deleted file mode 100644 index 3125774..0000000 --- a/src/main/java/Character.java +++ /dev/null @@ -1,77 +0,0 @@ -import Entity.Position; - -//Vill inte göra så mycket med den här klassen, då jag vill påverka implementeringen av Player så lite som möjligt -public abstract class Character { - private static final double DEFAULT_HEALTH = 50d; - private static final double DEFAULT_LEVEL = 1.0; - private static final double DEFAULT_ENERGY = 50d; //Detta borde kanske egentligen beräknas och sättas automatiskt genom en algoritm som tar health och level i beaktskap?? Eller något sådant - private static final Position DEFAULT_POSITION = new Position(0,0); //Är detta en bra idé?? Mest för att kunna ha defaultkonstruktor - - // Borde jag bara sätta allt till default direkt här???? - private double health; - private double level; - private double energy; //Borde kanske beräknas genom en algoritm istället för att kunna sättas i konstruktorn... Så det alltid blir balanserat - private Position position; - - // Hur många varianter på konstruktorn behövs? - public Character() { - this.health = DEFAULT_HEALTH; - this.level = DEFAULT_LEVEL; - this.energy = DEFAULT_ENERGY; - this.position = DEFAULT_POSITION; - } - - // Jag antar att den som instansierar massa monster i världen ansvarar - // för att kolla att "position" har ett tillåtet värde. - // Just denna variant är bara för testning i min implementation av Monster - public Character(Position position) { - this.health = DEFAULT_HEALTH; - this.level = DEFAULT_LEVEL; - this.energy = DEFAULT_ENERGY; - this.position = position; - } - - public Character(double health, double level, double energy, Position position) { - this.health = health; - this.level = level; - this.energy = energy; - this.position = position; - } - - //Returnerar true om attacken gick igenom, annars false - //Tänker att positionen som skickas in är karaktärens egna - //Kommer antagligen behöva diverse hjälpmetoder i implementeringen då det är många krav som måste uppfyllas för att attacken ska gå igenom - abstract boolean attack(Position position, Character character); - - public double getHealth() { - return health; - } - - public void setHealth(int newHealth) { - health = newHealth; - } - - public double getLevel() { - return level; - } - - public void setLevel(int newLevel) { - level = newLevel; - } - - public double getEnergy() { - return energy; - } - - public void setEnergy(double newEnergy) { - energy = newEnergy; - } - - public Position getPosition() { - return position; - } - - public void setPosition(Position newPosition) { - position = newPosition; - } -} diff --git a/src/main/java/Monster.java b/src/main/java/Monster.java deleted file mode 100644 index 9e1d6d7..0000000 --- a/src/main/java/Monster.java +++ /dev/null @@ -1,33 +0,0 @@ -import Entity.Position; -import Terrain.Biome; - -import java.util.*; - -public abstract class Monster extends Character{ - - private final List habitat = new ArrayList<>(); - - public Monster() { - habitat.addAll(Arrays.asList(Biome.GRASSLAND, Biome.MOUNTAIN, Biome.COAST, Biome.FOREST)); - } - - public Monster(Position position) { - super(position); - habitat.addAll(Arrays.asList(Biome.GRASSLAND, Biome.MOUNTAIN, Biome.COAST, Biome.FOREST)); - } - - public Monster(double health, double level, double energy, Position position) { - super(health, level, energy, position); - habitat.addAll(Arrays.asList(Biome.GRASSLAND, Biome.MOUNTAIN, Biome.COAST, Biome.FOREST)); - } - - public Monster(double health, double level, double energy, Position position, List habitat) { - super(health, level, energy, position); - this.habitat.addAll(habitat); - } - - //Är detta bra??? Med unmodifiableList dvs - public List getHabitat() { - return Collections.unmodifiableList(habitat); - } -} diff --git a/src/main/java/Monster/Attacks.java b/src/main/java/Monster/Attacks.java new file mode 100644 index 0000000..6abda4a --- /dev/null +++ b/src/main/java/Monster/Attacks.java @@ -0,0 +1,13 @@ +package Monster; + +public enum Attacks { + CHILL(3, 2), STOMP(7, 5); + + public int damage; + public int cost; + + Attacks(int damage, int cost) { + this.damage = damage; + this.cost = cost; + } +} diff --git a/src/main/java/Monster/Biomes.java b/src/main/java/Monster/Biomes.java new file mode 100644 index 0000000..aeb46e3 --- /dev/null +++ b/src/main/java/Monster/Biomes.java @@ -0,0 +1,5 @@ +package Monster; + +public enum Biomes { + GRASSLAND, MOUNTAIN, COAST, FOREST //Är inte fäst vid dessa +} \ No newline at end of file diff --git a/src/main/java/Monster/CanAttack.java b/src/main/java/Monster/CanAttack.java new file mode 100644 index 0000000..76f92e5 --- /dev/null +++ b/src/main/java/Monster/CanAttack.java @@ -0,0 +1,7 @@ +package Monster; + +import Entity.Player; + +public interface CanAttack { + abstract boolean performAttack(Attacks attack, Player player); +} diff --git a/src/main/java/Monster/CanMove.java b/src/main/java/Monster/CanMove.java new file mode 100644 index 0000000..c9a8dd6 --- /dev/null +++ b/src/main/java/Monster/CanMove.java @@ -0,0 +1,10 @@ +package Monster; +import World.*; +import Entity.Position; +//Är detta ett ok namn? +public interface CanMove { + + abstract boolean move(World world); + + abstract boolean moveTo(Position destination, World world); +} diff --git a/src/main/java/Monster/Monster.java b/src/main/java/Monster/Monster.java new file mode 100644 index 0000000..7b1384a --- /dev/null +++ b/src/main/java/Monster/Monster.java @@ -0,0 +1,53 @@ +package Monster; + +import Entity.*; + +public abstract class Monster implements CanMove { + public static final Position DEFAULT_POSITION = new Position(1,1); + public static final int MIN_HEALTH = 0; + public static final int MIN_ENERGY = 0; + + protected int health; + protected int energy; //Borde kanske beräknas genom en algoritm istället för att kunna sättas i konstruktorn... Så det alltid blir balanserat + protected Position position; + protected boolean isAlive; + + public Monster(int health, int energy, Position position) { + this.health = health; + this.energy = energy; + this.position = position; + + isAlive = true; + } + + public void kill() { + health = 0; + energy = 0; + isAlive = false; + } + + public int getHealth() { + return health; + } + + public int getEnergy() { + return energy; + } + + public Position getPosition() { + return position; + } + + public boolean isAlive() { + return isAlive; + } + + public boolean isDead() { + return !isAlive; + } + + abstract boolean heal(); + + abstract boolean takeDamage(int amount); + +} diff --git a/src/main/java/Monster/MovementPatterns.java b/src/main/java/Monster/MovementPatterns.java new file mode 100644 index 0000000..e4f8ea6 --- /dev/null +++ b/src/main/java/Monster/MovementPatterns.java @@ -0,0 +1,34 @@ +package Monster; + +import Entity.Position; +import java.util.*; + +public enum MovementPatterns { + ONE_DIAGONAL_STEP(1, 1), TWO_STEPS_IN_STRAIGHT_LINE(2, 2); + + public int cost; + private int id; + + MovementPatterns(int cost, int id) { + this.cost = cost; + this.id = id; + } + + public List findLegalDestinations(Position position) { + List legalDestinations = new ArrayList<>(); + int x = position.x(); + int y = position.y(); + + switch(id) { + case 1: + legalDestinations.addAll(Arrays.asList(new Position(x - 1, y + 1), new Position(x + 1, y + 1), + new Position(x - 1, y - 1), new Position(x + 1, y - 1))); + break; + case 2: + legalDestinations.addAll(Arrays.asList(new Position(x, y + 2), new Position(x, y - 2), + new Position(x + 2, y), new Position(x - 2, y))); + } + + return legalDestinations; + } +} diff --git a/src/main/java/Monster/Shade.java b/src/main/java/Monster/Shade.java new file mode 100644 index 0000000..51c376c --- /dev/null +++ b/src/main/java/Monster/Shade.java @@ -0,0 +1,160 @@ +package Monster; +import Entity.*; +import World.*; +import java.util.*; +import static java.util.Objects.*; + +public class Shade extends Monster implements CanMove, CanAttack { + public static final List HABITAT = Collections.unmodifiableList(Arrays.asList(Biomes.COAST, Biomes.FOREST, Biomes.GRASSLAND, Biomes.MOUNTAIN)); + public static final List MOVES = Collections.unmodifiableList(Arrays.asList(MovementPatterns.ONE_DIAGONAL_STEP)); + public static final List ATTACKS = Collections.unmodifiableList(Arrays.asList(Attacks.CHILL)); + public static final int MAX_HEALTH = 20; + public static final int MAX_ENERGY = 14; + + private static final int HEALTH_PER_HEAL = 2; + private static final int ENERGY_COST_PER_HEAL = 1; + + private Random random = new Random(); + private List validDestinations = new ArrayList<>(); + + public Shade() { + super(MAX_HEALTH, MAX_ENERGY, DEFAULT_POSITION); + } + + public Shade(int health, int energy, Position position) { + super(health, energy, position); + enforceHealthValueBoundries(); + enforceEnergyValueBoundries(); + } + + public boolean heal() { + if (isDead()) { + return false; + } + if (health == MIN_HEALTH) { + return false; + } + if (health == MAX_HEALTH) { + return false; + } + if (energy < ENERGY_COST_PER_HEAL) { + return false; + } + + health += HEALTH_PER_HEAL; + energy -= ENERGY_COST_PER_HEAL; + enforceHealthValueBoundries(); + + return true; + } + + public boolean takeDamage(int amount) { + if (amount < 0) { + return false; + } + if (health - amount <= 0) { + kill(); + return true; + } + else { + health =- amount; + return true; + } + } + + public boolean performAttack(Attacks attack, Player player) { + if (isDead()) { + return false; + } + if (attack.cost > energy) { + return false; + } + if (attackIsNotInArsenal(attack)) { + return false; + } + player.setHealth(player.getHealth() - attack.cost); + return true; + } + + public boolean move(World world) { + updateDestinations(position, world); + + if (isDead()) { + return false; + } + if (validDestinations.isEmpty()) { + return false; + } + position = validDestinations.get(random.nextInt(validDestinations.size())); + return true; + } + + public boolean moveTo(Position destination, World world) { + updateDestinations(position, world); + + if (isDead()) { + return false; + } + if (playerIsAtPosition(destination, world)) { + return false; + } + if (validDestinations.isEmpty()) { + return false; + } + position = destination; + return true; + } + + private void enforceHealthValueBoundries() { + if (health > MAX_HEALTH) { + health = MAX_HEALTH; + } + else if (health <= MIN_HEALTH) { + health = 1; + } + } + + private void enforceEnergyValueBoundries() { + if (energy > MAX_ENERGY) { + energy = MAX_ENERGY; + } + else if (energy < MIN_ENERGY) { + energy = MIN_ENERGY; + } + } + + private void updateDestinations(Position position, World world) { + validDestinations.clear(); + for (MovementPatterns move : MOVES) { + if (move.cost <= energy) { + List destinations = move.findLegalDestinations(position); + for (Position destination : destinations) { + if (noPlayerIsAtPosition(destination, world)) { + validDestinations.add(destination); + } + } + } + } + } + + private boolean playerIsAtPosition(Position position, World world) { + List entitiesAtPosition = world.getPositionEntityMap().get(position); + if (isNull(entitiesAtPosition)) { + return false; + } + for (Entity entity : entitiesAtPosition) { + if (entity instanceof Player) { + return true; + } + } + return false; + } + + private boolean noPlayerIsAtPosition(Position position, World world) { + return !playerIsAtPosition(position, world); + } + + private boolean attackIsNotInArsenal(Attacks attack) { + return !ATTACKS.contains(attack); + } +} diff --git a/src/main/java/Monster/Troll.java b/src/main/java/Monster/Troll.java new file mode 100644 index 0000000..4489103 --- /dev/null +++ b/src/main/java/Monster/Troll.java @@ -0,0 +1,157 @@ +package Monster; + +import Entity.Entity; +import Entity.Player; +import Entity.Position; +import World.*; + +import java.util.*; + +import static java.util.Objects.isNull; + +public class Troll extends Monster implements CanMove, CanAttack { + public static final List HABITAT = Collections.unmodifiableList(Arrays.asList(Biomes.FOREST, Biomes.MOUNTAIN)); + public static final List MOVES = Collections.unmodifiableList(Arrays.asList(MovementPatterns.ONE_DIAGONAL_STEP, MovementPatterns.TWO_STEPS_IN_STRAIGHT_LINE)); + public static final List ATTACKS = Collections.unmodifiableList(Arrays.asList(Attacks.STOMP)); + public static final int MAX_HEALTH = 80; + public static final int MAX_ENERGY = 100; + + private static final int HEALTH_PER_HEAL = 6; + private static final int ENERGY_COST_PER_HEAL = 3; + + private Random random = new Random(); + private List validDestinations = new ArrayList<>(); + + public Troll() { + super(MAX_HEALTH, MAX_ENERGY, DEFAULT_POSITION); + } + + public Troll(int health, int energy, Position position) { + super(health, energy, position); + enforceHealthValueBoundries(); + enforceEnergyValueBoundries(); + } + + public boolean heal() { + if (isDead()) { + return false; + } + if (health == MIN_HEALTH) { + return false; + } + if (health == MAX_HEALTH) { + return false; + } + if (energy < ENERGY_COST_PER_HEAL) { + return false; + } + + health += HEALTH_PER_HEAL; + energy -= ENERGY_COST_PER_HEAL; + enforceHealthValueBoundries(); + + return true; + } + + public boolean takeDamage(int amount) { + if (amount < 0) { + return false; + } + if (health - amount <= 0) { + kill(); + return true; + } + else { + health =- amount; + return true; + } + } + + public boolean performAttack(Attacks attack, Player player) { + if (isDead()) { + return false; + } + if (attack.cost > energy) { + return false; + } + if (attackIsNotInArsenal(attack)) { + return false; + } + player.setHealth(player.getHealth() - attack.cost); + return true; + } + + public boolean move(World world) { + updateDestinations(position, world); + + if (isDead()) { + return false; + } + if (validDestinations.isEmpty()) { + return false; + } + position = validDestinations.get(random.nextInt(validDestinations.size())); + return true; + } + + public boolean moveTo(Position destination, World world) { + updateDestinations(position, world); + + if (isDead()) { + return false; + } + if (tileHasWrongHabitat(destination, world)) { + return false; + } + if (validDestinations.isEmpty()) { + return false; + } + position = destination; + return true; + } + + private void updateDestinations(Position position, World world) { + validDestinations.clear(); + for (MovementPatterns move : MOVES) { + if (move.cost <= energy) { + List destinations = move.findLegalDestinations(position); + validDestinations.addAll(destinations); + } + } + } + + + private void enforceHealthValueBoundries() { + if (health > MAX_HEALTH) { + health = MAX_HEALTH; + } + else if (health <= MIN_HEALTH) { + health = 1; + } + } + + private void enforceEnergyValueBoundries() { + if (energy > MAX_ENERGY) { + energy = MAX_ENERGY; + } + else if (energy < MIN_ENERGY) { + energy = MIN_ENERGY; + } + } + + private boolean attackIsNotInArsenal(Attacks attack) { + return !ATTACKS.contains(attack); + } + + private boolean tileHasWrongHabitat(Position position, World world) { + Tile destinationTile = world.getTileAtPosition(position); + if (Objects.isNull(destinationTile)) { + return false; + } + boolean hasWrongBiome = !HABITAT.contains(destinationTile.getBiome()); + if (Objects.isNull(hasWrongBiome)) { + return false; + } + return hasWrongBiome; + } +} diff --git a/src/main/java/World/MapGenerator.java b/src/main/java/World/MapGenerator.java new file mode 100644 index 0000000..f9f21ca --- /dev/null +++ b/src/main/java/World/MapGenerator.java @@ -0,0 +1,51 @@ +package World; + +import java.util.Random; +import Entity.*; +import Monster.Biomes; + +public class MapGenerator { + private int x; + private int y; + private World gameWorld; + + public MapGenerator(int x, int y) { + this.x = x; + this.y = y; + if (x <= 0 || y <= 0) { + throw new IllegalArgumentException("X and Y coordinates must be positive"); + } + this.gameWorld = new World(x, y); + } + + public void randomWorldGeneration() { + Random rand = new Random(); + for (int i = 0; i < x; i++) { + for (int j = 0; j < y; j++) { + int tileDice = rand.nextInt(4); + if (tileDice == 0) { + Tile newTile = new Tile(Biomes.FOREST, 3, "Future development"); + gameWorld.addTile(newTile, new Position(i, j)); + } else if (tileDice == 1) { + Tile newTile = new Tile(Biomes.GRASSLAND, 1, "Future development"); + gameWorld.addTile(newTile, new Position(i, j)); + } else if (tileDice == 2) { + Tile newTile = new Tile(Biomes.MOUNTAIN, 2, "Future development"); + gameWorld.addTile(newTile, new Position(i, j)); + } else if (tileDice == 3) { + Tile newTile = new Tile(Biomes.COAST, 4, "Future development"); + gameWorld.addTile(newTile, new Position(i, j)); + } + } + } + } + + public void addTile(Biomes biome, int staminaCost, String description, Position position) { + Tile newTile = new Tile(biome, staminaCost, description); + gameWorld.addTile(newTile, position); + } + + public World getWorld() { + return gameWorld; + } +} diff --git a/src/main/java/World/Tile.java b/src/main/java/World/Tile.java new file mode 100644 index 0000000..58aed8e --- /dev/null +++ b/src/main/java/World/Tile.java @@ -0,0 +1,27 @@ +package World; + +import Monster.Biomes; + +public class Tile { + private Biomes biome; + private int staminaCost; + private String tileID; + + public Tile(Biomes biome, int staminaCost, String tileID) { + this.biome = biome; + this.staminaCost = staminaCost; + this.tileID = tileID; + } + public void setStaminaCost(int newStaminaCost) { + staminaCost = newStaminaCost; + } + public Biomes getBiome() { + return biome; + } + public int getStaminaCost() { + return staminaCost; + } + public String getTileID() { + return tileID; + } +} diff --git a/src/main/java/World/World.java b/src/main/java/World/World.java new file mode 100644 index 0000000..0edacab --- /dev/null +++ b/src/main/java/World/World.java @@ -0,0 +1,129 @@ +package World; + +import Entity.*; +import java.util.*; + +public class World { + + private HashMap tileMap = new HashMap<>(); + private HashMap entityMap = new HashMap<>(); + private String[][] map; + private int width; + private int height; + + public World(int x, int y) { + this.map = new String[(x * 2) + 1][y + 2]; + addWorldToMap(); + width = x; + height = y; + } + + private void addWorldToMap() { + for (int i = 0; i < map.length; i++) { + for (int j = 0; j < map[i].length; j++) { + if (i == 0 || j == 0 || i == map.length || j == map[j].length) { + map[i][j] = "@"; + } else if (j % 2 == 1) { + map[i][j] = " "; + } else { + map[i][j] = "|"; + } + } + } + } + + public void printWorld() { + for (Entity e : entityMap.keySet()) { + Position pos = entityMap.get(e); + if (e instanceof Player) { + map[pos.x()][pos.y()] = "X"; + } /* else if (e instanceof Monster) { + map[pos.x()][pos.y()] = "M"; + */ + } + for (int i = 0; i < map.length; i++) { + System.out.println(); + for (int j = 0; j < map[i].length; j++) { + System.out.print(map[i][j]); + } + } + } + + + + public void addEntityToMap(Entity e) { + entityMap.put(e, e.getPosition()); + } + public void addTile(Tile tile, Position pos) { + tileMap.put(pos, tile); + } + /* + M in array is monster + C in array is chest + X in array is player. + */ + public void printMap() { + for (int i = 0; i < map.length; i++) { + for (int j = 0; j < map[i].length; j++) { + System.out.print(map[i][j]); + } + } + } + + public String[][] getMap() { + return map; + } + + public HashMap getEntityList() { + return entityMap; + } + public HashMap getTileMap() { + return tileMap; + } + + public Tile getTileAtPosition(Position pos) { + return tileMap.get(pos); + } + + public Entity getEntityAtPosition(Position position) { + for (HashMap.Entry entry : entityMap.entrySet()) { + Position pos = entry.getValue(); + Entity e = entry.getKey(); + if (pos.equals(position)) { + return e; + } + } + return null; + } + + //Har inte testat denna så den kanske inte alls funkar + private Map> invertEntityMap() { + Map> entitysByPosition = new HashMap<>(); + + for (Position position : entityMap.values()) { + entitysByPosition.put(position, new ArrayList<>()); + } + + for (Entity entity : entityMap.keySet()) { + Position position = entityMap.get(entity); + entitysByPosition.get(position).add(entity); + } + return entitysByPosition; + } + + public Map> getPositionEntityMap() { + return invertEntityMap(); + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public void changePosition(Entity Character, Position newPosition) { + entityMap.put(Character, newPosition); + } +} diff --git a/src/test/java/MapGeneratorTest.java b/src/test/java/MapGeneratorTest.java new file mode 100644 index 0000000..c6ef57b --- /dev/null +++ b/src/test/java/MapGeneratorTest.java @@ -0,0 +1,38 @@ +import Monster.Biomes; +import World.MapGenerator; +import World.Tile; +import World.World; +import Entity.*; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; + +import static org.junit.jupiter.api.Assertions.*; + +class MapGeneratorTest { + private final int X_FIVE = 5; + private final int Y_FIVE = 5; + @Test + void mapBeingGeneratedTest() { + MapGenerator mapGenerate = new MapGenerator(X_FIVE, Y_FIVE); + mapGenerate.randomWorldGeneration(); + World world = mapGenerate.getWorld(); + HashMap map = world.getTileMap(); + assertEquals(map.size(), X_FIVE * Y_FIVE); + } + // Valid klass + @Test + void addTileAtPositionTest() { + MapGenerator mapGenerate = new MapGenerator(X_FIVE, Y_FIVE); + mapGenerate.addTile(Biomes.MOUNTAIN, 10, "test description", new Position(2, 2)); + World testWorld = mapGenerate.getWorld(); + assertTrue(testWorld.getTileAtPosition(new Position(2, 2)) instanceof Tile); + assertFalse(testWorld.getTileAtPosition(new Position(2, 1)) instanceof Tile); + } + // Invalid klass + @Test + void negativeCoordinatesTest() { + assertThrows(IllegalArgumentException.class, () -> new MapGenerator(-1, 5)); + assertThrows(IllegalArgumentException.class, () -> new MapGenerator(5, 0)); + } +} \ No newline at end of file diff --git a/src/test/java/MonsterTest.java b/src/test/java/MonsterTest.java index cc75cd4..6acf0bb 100644 --- a/src/test/java/MonsterTest.java +++ b/src/test/java/MonsterTest.java @@ -1,50 +1,319 @@ -import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.Test; +import Entity.*; +import Monster.*; +import World.*; +import org.junit.jupiter.api.*; + +import java.util.*; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.*; public class MonsterTest { - @Test - void monster_cannot_attack_when_dead() { - /* - Monster someMonster = new Monster(); - someMonster.setHealth(-5d); - Assert(false, someMonster.attack(someMonster.getPosition(), character)); - */ + private Shade defaultShade; + private Troll defaultTroll; + private Position defaultDestination = new Position(2, 2); + private MapGenerator worldGenerator = new MapGenerator(5, 5); + + private Player mockPlayer = mock(Player.class); + private World mockWorld = mock(World.class); + private Tile mockTile = mock(Tile.class); + + @BeforeEach + void reset() { + defaultShade = new Shade(); + defaultTroll = new Troll(); } @Test - void monster_cannot_be_attacked_when_dead() { - /* - Monster attackingMonster = new Monster(); - Monster deadMonster = new Monster(); - deadMonster.setHealth(-4d); - Assert(false, attackingMonster.attack(attackingMonster.getPosition(), deadMonster)); - */ + void shade_cannot_move_after_death() { + defaultShade.kill(); + assertThat(false, equalTo(defaultShade.moveTo(defaultDestination, mockWorld))); } @Test - void monster_cannot_attack_character_at_different_position() { - /* - Monster monsterAtOnePosition = new Monster(new Position(0,0)); - Monster monsterAtDifferentPosition = new Monster(new Position(1,1)); - Assert(false, monsterAtOnePosition.attack(monsterAtOnePosition.getPosition(), monsterAtDifferentPosition); - */ + void troll_cannot_move_after_death() { + defaultTroll.kill(); + assertThat(false, equalTo(defaultTroll.moveTo(defaultDestination, mockWorld))); } @Test - void monster_is_instantiated_correctly_from_default_constructor() { - /* - AssertAll???? - */ + void shade_cannot_attack_after_death() { + defaultShade.kill(); + assertThat(false, equalTo(defaultShade.performAttack(Attacks.CHILL, mockPlayer))); } @Test - void monster_is_instantiated_correctly_from_position_only_constructor() { - + void troll_cannot_attack_after_death() { + defaultTroll.kill(); + assertThat(false, equalTo(defaultTroll.performAttack(Attacks.STOMP, mockPlayer))); } @Test - void monster_is_instantiated_correctly_from_full_constructor() { - + void reaching_zero_health_triggers_shade_to_die() { + defaultShade.takeDamage(Shade.MAX_HEALTH); + assertThat(false, equalTo(defaultShade.isAlive())); } + + @Test + void reaching_zero_health_triggers_troll_to_die() { + defaultTroll.takeDamage(Troll.MAX_HEALTH); + assertThat(false, equalTo(defaultTroll.isAlive())); + } + + @Test + void shade_cannot_heal_after_death() { + defaultShade.kill(); + assertThat(false, equalTo(defaultShade.heal())); + } + + @Test + void troll_cannot_heal_after_death() { + defaultTroll.kill(); + assertThat(false, equalTo(defaultTroll.heal())); + } + + @Test + void method_move_places_shade_in_legal_position() { + Position startingPosition = defaultShade.getPosition(); + List legalDestinations = new ArrayList<>(); + List moves = defaultShade.MOVES; + + for (MovementPatterns move : moves) { + legalDestinations.addAll(move.findLegalDestinations(startingPosition)); + } + + defaultShade.move(mockWorld); + assertThat(legalDestinations, hasItem(defaultShade.getPosition())); + } + + @Test + void method_move_places_troll_in_legal_position() { + Position startingPosition = defaultTroll.getPosition(); + List legalDestinations = new ArrayList<>(); + List moves = defaultTroll.MOVES; + + for (MovementPatterns move : moves) { + legalDestinations.addAll(move.findLegalDestinations(startingPosition)); + } + + defaultTroll.move(mockWorld); + assertThat(legalDestinations, hasItem(defaultTroll.getPosition())); + } + + @Test + void method_moveTo_wont_move_shade_to_same_position_as_player() { + worldGenerator.randomWorldGeneration(); + World world = worldGenerator.getWorld(); + + Position destination = new Position(2, 2); + when(mockPlayer.getPosition()).thenReturn(destination); + world.addEntityToMap(mockPlayer); + + assertThat(defaultShade.moveTo(destination, world), equalTo(false)); + } + + + @Test + void method_move_wont_move_shade_to_same_position_as_player() { + worldGenerator.randomWorldGeneration(); + World world = worldGenerator.getWorld(); + + Position destination = new Position(2, 2); + when(mockPlayer.getPosition()).thenReturn(destination); + world.addEntityToMap(mockPlayer); + + defaultShade.move(world); + assertThat(defaultShade.getPosition(), not(mockPlayer.getPosition())); + } + + @Test + void shade_cannot_heal_when_out_of_energy() { + Shade shade = new Shade(Shade.MAX_HEALTH, Shade.MIN_ENERGY, Shade.DEFAULT_POSITION); + assertThat(shade.heal(), equalTo(false)); + } + + @Test + void troll_cannot_heal_when_out_of_energy() { + Troll troll = new Troll(Troll.MAX_HEALTH, Troll.MIN_ENERGY, Troll.DEFAULT_POSITION); + assertThat(troll.heal(), equalTo(false)); + } + + @Test + void shade_cannot_move_when_out_of_energy() { + Shade shade = new Shade(Shade.MAX_HEALTH, Shade.MIN_ENERGY, Shade.DEFAULT_POSITION); + assertThat(shade.move(mockWorld), equalTo(false)); + } + + @Test + void troll_cannot_move_when_out_of_energy() { + Troll troll = new Troll(Troll.MAX_HEALTH, Troll.MIN_ENERGY, Troll.DEFAULT_POSITION); + assertThat(troll.move(mockWorld), equalTo(false)); + } + + @Test + void shade_cannot_attack_when_out_of_energy() { + Shade shade = new Shade(Shade.MAX_HEALTH, Shade.MIN_ENERGY, Shade.DEFAULT_POSITION); + assertThat(shade.performAttack(Attacks.CHILL, mockPlayer), equalTo(false)); + } + + @Test + void troll_cannot_attack_when_out_of_energy() { + Troll troll = new Troll(Troll.MAX_HEALTH, Troll.MIN_ENERGY, Troll.DEFAULT_POSITION); + assertThat(troll.performAttack(Attacks.STOMP, mockPlayer), equalTo(false)); + } + + @Test + void only_moves_within_energy_budget_are_performed() { + Troll troll = new Troll(Troll.MAX_HEALTH, 1, Troll.DEFAULT_POSITION); + List movesWithinBudget = MovementPatterns.ONE_DIAGONAL_STEP.findLegalDestinations(troll.getPosition()); + troll.move(mockWorld); + + assertThat(movesWithinBudget, hasItem(troll.getPosition())); + } + + @Test + void attack_rejected_when_not_in_arsenal_troll() { + assertThat(false, equalTo(defaultTroll.performAttack(Attacks.CHILL, mockPlayer))); + } + + @Test + void attack_rejected_when_not_in_arsenal_shade() { + assertThat(false, equalTo(defaultShade.performAttack(Attacks.STOMP, mockPlayer))); + } + + @Test + void troll_can_attack_player() { + assertThat(true, equalTo(defaultTroll.performAttack(Attacks.STOMP, mockPlayer))); + } + + @Test + void shade_can_attack_player() { + assertThat(true, equalTo(defaultShade.performAttack(Attacks.CHILL, mockPlayer))); + } + + @Test + void troll_wont_move_to_tile_with_wrong_biome() { + when(mockTile.getBiome()).thenReturn(Biomes.COAST); + Position destination = new Position(1, 3); + + worldGenerator.randomWorldGeneration(); + World world = worldGenerator.getWorld(); + world.addTile(mockTile, destination); + + assertThat(defaultTroll.moveTo(destination, world), equalTo(false)); + } + + @Test + void troll_can_move_to_tile_within_its_habitat() { + when(mockTile.getBiome()).thenReturn(Biomes.FOREST); + Position destination = new Position(1, 3); + + worldGenerator.randomWorldGeneration(); + World world = worldGenerator.getWorld(); + world.addTile(mockTile, destination); + + assertThat(defaultTroll.moveTo(destination, world), equalTo(true)); + } + + @Test + void get_health_returns_correct_value() { + defaultShade = new Shade(8, Shade.MAX_ENERGY, Shade.DEFAULT_POSITION); + assertThat(defaultShade.getHealth(), equalTo(8)); + } + + @Test + void get_energy_returns_correct_value() { + defaultShade = new Shade(Shade.MAX_HEALTH, 10, Shade.DEFAULT_POSITION); + assertThat(defaultShade.getEnergy(), equalTo(10)); + } + + @Test + void shade_can_heal() { + defaultShade = new Shade(8, Shade.MAX_ENERGY, Shade.DEFAULT_POSITION); + assertThat(defaultShade.heal(), equalTo(true)); + } + + @Test + void troll_can_heal() { + defaultTroll = new Troll(8, Troll.MAX_ENERGY, Troll.DEFAULT_POSITION); + assertThat(defaultTroll.heal(), equalTo(true)); + } + + @Test + void shade_can_take_damage() { + assertThat(defaultShade.takeDamage(5), equalTo(true)); + } + + @Test + void troll_can_take_damage() { + assertThat(defaultTroll.takeDamage(5), equalTo(true)); + } + + @Test + void troll_wont_heal_when_it_doesnt_have_enough_energy() { + defaultTroll = new Troll(8, 2, Troll.DEFAULT_POSITION); + assertThat(defaultTroll.heal(), equalTo(false)); + } + + // HÄR BÖRJAR BESLUTSTABELLSTESTERNA + + @Test + void decision_table_t1() { + defaultTroll.kill(); + assertThat(defaultTroll.moveTo(defaultDestination, mockWorld), equalTo(false)); + } + + @Test + void decision_table_t2() { + Monster shade = new Shade(Shade.MAX_HEALTH, 0, Shade.DEFAULT_POSITION); + assertThat(shade.moveTo(defaultDestination, mockWorld), equalTo(false)); + } + + @Test + void decision_table_t3() { + worldGenerator.randomWorldGeneration(); + World world = worldGenerator.getWorld(); + + when(mockPlayer.getPosition()).thenReturn(defaultDestination); + world.addEntityToMap(mockPlayer); + + assertThat(defaultShade.moveTo(defaultDestination, world), equalTo(false)); + } + + @Test + void decision_table_t4() { + worldGenerator.randomWorldGeneration(); + World world = worldGenerator.getWorld(); + + when(mockPlayer.getPosition()).thenReturn(new Position(6, 8)); + world.addEntityToMap(mockPlayer); + + assertThat(defaultShade.moveTo(defaultDestination, world), equalTo(true)); + } + + @Test + void decision_table_t5() { + when(mockTile.getBiome()).thenReturn(Biomes.COAST); + + worldGenerator.randomWorldGeneration(); + World world = worldGenerator.getWorld(); + world.addTile(mockTile, defaultDestination); + + assertThat(defaultTroll.moveTo(defaultDestination, world), equalTo(false)); + } + + @Test + void decision_table_t6() { + when(mockTile.getBiome()).thenReturn(Biomes.FOREST); + when(mockPlayer.getPosition()).thenReturn(defaultDestination); + + worldGenerator.randomWorldGeneration(); + World world = worldGenerator.getWorld(); + world.addTile(mockTile, defaultDestination); + + assertThat(defaultTroll.moveTo(defaultDestination, world), equalTo(true)); + } + } diff --git a/src/test/java/TileTest.java b/src/test/java/TileTest.java new file mode 100644 index 0000000..c053d0a --- /dev/null +++ b/src/test/java/TileTest.java @@ -0,0 +1,33 @@ +import Monster.*; +import World.Tile; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; +class TileTest { + private final Biomes BIOME = Biomes.MOUNTAIN; + private final int STAMINA_COST = 1; + private final String ID_STRING = "Mountain test"; + @Test + void getBiomeTest() { + Tile tileTest = new Tile(BIOME, STAMINA_COST, ID_STRING); + Biomes tileNameTwo = tileTest.getBiome(); + assertEquals(BIOME, tileNameTwo); + } + @Test + void getStaminaCostTest() { + Tile tileTest = new Tile(BIOME, STAMINA_COST, ID_STRING); + assertEquals(STAMINA_COST, tileTest.getStaminaCost()); + } + @Test + void printTileStringTest() { + Tile tileTest = new Tile(BIOME, STAMINA_COST, ID_STRING); + assertEquals(ID_STRING, tileTest.getTileID()); + } + @Test + void setStaminaCostTest() { + Tile tileTest = new Tile(BIOME, STAMINA_COST, ID_STRING); + tileTest.setStaminaCost(10); + int newStaminaCost = tileTest.getStaminaCost(); + assertEquals(10, newStaminaCost); + } +} \ No newline at end of file diff --git a/src/test/java/WorldTest.java b/src/test/java/WorldTest.java new file mode 100644 index 0000000..9ca607b --- /dev/null +++ b/src/test/java/WorldTest.java @@ -0,0 +1,43 @@ + +import World.Tile; +import World.World; +import org.junit.jupiter.api.Test; +import Entity.*; + +import java.util.HashMap; + +import static org.junit.jupiter.api.Assertions.*; +class WorldTest { + @Test + void addEntityToMapTest() { + World test = new World(5, 5); + Entity character = new Player("Name"); + character.setPosition(new Position(2, 2)); + test.addEntityToMap(character); + } + @Test + void getTileAtPositionTest() { + World test = new World(5, 5); + HashMap tileMap = new HashMap<>(); + } + @Test + void worldCreationTest() { + World test = new World(5, 5); + String[][] mapTest = test.getMap(); + int counter = 0; + for (int i = 0; i < mapTest.length; i++) { + for (int j = 0; j < mapTest[i].length; j++) { + counter += 1; + } + } + assertEquals(counter, 5 * 5); + } + @Test + void mapCreationTest() { + World test = new World(5, 5); + String[][] mapTest = test.getMap(); + assertEquals(mapTest[1][1], " "); + assertEquals(mapTest[1][2], "|"); + assertEquals(mapTest[0][0], "@"); + } +} \ No newline at end of file