done
This commit is contained in:
61
src/Arena.java
Normal file
61
src/Arena.java
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public final class Arena<C extends Character> {
|
||||||
|
|
||||||
|
private final Set<C> characters =
|
||||||
|
new HashSet<>();
|
||||||
|
|
||||||
|
public void add(C character) {
|
||||||
|
characters.add(
|
||||||
|
Objects.requireNonNull(
|
||||||
|
character,
|
||||||
|
"Character must not be null"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean remove(C character) {
|
||||||
|
return characters.remove(character);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attackAll(Attack attack) {
|
||||||
|
Objects.requireNonNull(
|
||||||
|
attack,
|
||||||
|
"Attack must not be null"
|
||||||
|
);
|
||||||
|
|
||||||
|
for (C character : characters) {
|
||||||
|
attack.execute(character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return characters.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return characters.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<C> getCharacters() {
|
||||||
|
return Set.copyOf(characters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<C> getLivingCharacters() {
|
||||||
|
return characters.stream()
|
||||||
|
.filter(character ->
|
||||||
|
!character.isDefeated()
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<C> getDefeatedCharacters() {
|
||||||
|
return characters.stream()
|
||||||
|
.filter(Character::isDefeated)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
445
src/ArenaTest.java
Normal file
445
src/ArenaTest.java
Normal file
@@ -0,0 +1,445 @@
|
|||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@DisplayName("Generische Arena")
|
||||||
|
class ArenaTest {
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Spieler-Arena")
|
||||||
|
class PlayerArenaTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"Ein Angriff wird auf alle Spieler angewendet"
|
||||||
|
)
|
||||||
|
void attackAllAttacksEveryPlayer() {
|
||||||
|
// Arrange
|
||||||
|
Arena<Player> arena = new Arena<>();
|
||||||
|
|
||||||
|
Player warrior = new Player(
|
||||||
|
"Krieger",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
Player mage = new Player(
|
||||||
|
"Magier",
|
||||||
|
70
|
||||||
|
);
|
||||||
|
|
||||||
|
arena.add(warrior);
|
||||||
|
arena.add(mage);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
arena.attackAll(new SwordAttack());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertAll(
|
||||||
|
() -> assertEquals(
|
||||||
|
70,
|
||||||
|
warrior.getHitPoints()
|
||||||
|
),
|
||||||
|
() -> assertEquals(
|
||||||
|
40,
|
||||||
|
mage.getHitPoints()
|
||||||
|
),
|
||||||
|
() -> assertFalse(
|
||||||
|
warrior.isDefeated()
|
||||||
|
),
|
||||||
|
() -> assertFalse(
|
||||||
|
mage.isDefeated()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"Dasselbe Objekt wird im Set nicht doppelt gespeichert"
|
||||||
|
)
|
||||||
|
void samePlayerIsNotAddedTwice() {
|
||||||
|
// Arrange
|
||||||
|
Arena<Player> arena = new Arena<>();
|
||||||
|
|
||||||
|
Player player = new Player(
|
||||||
|
"Held",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
arena.add(player);
|
||||||
|
arena.add(player);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEquals(
|
||||||
|
1,
|
||||||
|
arena.size()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Gegner-Arena")
|
||||||
|
class EnemyArenaTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"Double Dispatch wirkt für jeden Gegnertyp unterschiedlich"
|
||||||
|
)
|
||||||
|
void fireSpellAffectsEnemyTypesDifferently() {
|
||||||
|
// Arrange
|
||||||
|
Arena<Enemy> arena = new Arena<>();
|
||||||
|
|
||||||
|
Skeleton skeleton = new Skeleton(
|
||||||
|
"Knochensoldat",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
IceGolem iceGolem = new IceGolem(
|
||||||
|
"Frostwächter",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
arena.add(skeleton);
|
||||||
|
arena.add(iceGolem);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
arena.attackAll(new FireSpell());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertAll(
|
||||||
|
() -> assertEquals(
|
||||||
|
75,
|
||||||
|
skeleton.getHitPoints()
|
||||||
|
),
|
||||||
|
() -> assertEquals(
|
||||||
|
40,
|
||||||
|
iceGolem.getHitPoints()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"Ein Schwert wirkt ebenfalls abhängig vom Gegnertyp"
|
||||||
|
)
|
||||||
|
void swordAttackAffectsEnemyTypesDifferently() {
|
||||||
|
// Arrange
|
||||||
|
Arena<Enemy> arena = new Arena<>();
|
||||||
|
|
||||||
|
Skeleton skeleton = new Skeleton(
|
||||||
|
"Knochensoldat",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
IceGolem iceGolem = new IceGolem(
|
||||||
|
"Frostwächter",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
arena.add(skeleton);
|
||||||
|
arena.add(iceGolem);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
arena.attackAll(new SwordAttack());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertAll(
|
||||||
|
() -> assertEquals(
|
||||||
|
60,
|
||||||
|
skeleton.getHitPoints()
|
||||||
|
),
|
||||||
|
() -> assertEquals(
|
||||||
|
85,
|
||||||
|
iceGolem.getHitPoints()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Gemischte Arena")
|
||||||
|
class MixedArenaTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"Spieler und Gegner können gemeinsam angegriffen werden"
|
||||||
|
)
|
||||||
|
void mixedArenaAttacksAllCharacterTypes() {
|
||||||
|
// Arrange
|
||||||
|
Arena<Character> arena = new Arena<>();
|
||||||
|
|
||||||
|
Player player = new Player(
|
||||||
|
"Held",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
Skeleton skeleton = new Skeleton(
|
||||||
|
"Knochensoldat",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
IceGolem iceGolem = new IceGolem(
|
||||||
|
"Frostwächter",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
arena.add(player);
|
||||||
|
arena.add(skeleton);
|
||||||
|
arena.add(iceGolem);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
arena.attackAll(new FireSpell());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertAll(
|
||||||
|
() -> assertEquals(
|
||||||
|
70,
|
||||||
|
player.getHitPoints()
|
||||||
|
),
|
||||||
|
() -> assertEquals(
|
||||||
|
75,
|
||||||
|
skeleton.getHitPoints()
|
||||||
|
),
|
||||||
|
() -> assertEquals(
|
||||||
|
40,
|
||||||
|
iceGolem.getHitPoints()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"Lebende und besiegte Figuren werden getrennt"
|
||||||
|
)
|
||||||
|
void livingAndDefeatedCharactersAreSeparated() {
|
||||||
|
// Arrange
|
||||||
|
Arena<Character> arena = new Arena<>();
|
||||||
|
|
||||||
|
Player player = new Player(
|
||||||
|
"Held",
|
||||||
|
20
|
||||||
|
);
|
||||||
|
|
||||||
|
Skeleton skeleton = new Skeleton(
|
||||||
|
"Schwaches Skelett",
|
||||||
|
20
|
||||||
|
);
|
||||||
|
|
||||||
|
IceGolem iceGolem = new IceGolem(
|
||||||
|
"Frostwächter",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
arena.add(player);
|
||||||
|
arena.add(skeleton);
|
||||||
|
arena.add(iceGolem);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
arena.attackAll(new FireSpell());
|
||||||
|
|
||||||
|
List<Character> living =
|
||||||
|
arena.getLivingCharacters();
|
||||||
|
|
||||||
|
List<Character> defeated =
|
||||||
|
arena.getDefeatedCharacters();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertAll(
|
||||||
|
() -> assertEquals(
|
||||||
|
1,
|
||||||
|
living.size()
|
||||||
|
),
|
||||||
|
() -> assertTrue(
|
||||||
|
living.contains(iceGolem)
|
||||||
|
),
|
||||||
|
() -> assertEquals(
|
||||||
|
2,
|
||||||
|
defeated.size()
|
||||||
|
),
|
||||||
|
() -> assertTrue(
|
||||||
|
defeated.contains(player)
|
||||||
|
),
|
||||||
|
() -> assertTrue(
|
||||||
|
defeated.contains(skeleton)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"Besiegte Figuren können entfernt werden"
|
||||||
|
)
|
||||||
|
void defeatedCharactersCanBeRemoved() {
|
||||||
|
// Arrange
|
||||||
|
Arena<Character> arena = new Arena<>();
|
||||||
|
|
||||||
|
Player player = new Player(
|
||||||
|
"Held",
|
||||||
|
20
|
||||||
|
);
|
||||||
|
|
||||||
|
IceGolem iceGolem = new IceGolem(
|
||||||
|
"Frostwächter",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
arena.add(player);
|
||||||
|
arena.add(iceGolem);
|
||||||
|
|
||||||
|
arena.attackAll(new FireSpell());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
for (Character defeated
|
||||||
|
: arena.getDefeatedCharacters()) {
|
||||||
|
arena.remove(defeated);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertAll(
|
||||||
|
() -> assertEquals(
|
||||||
|
1,
|
||||||
|
arena.size()
|
||||||
|
),
|
||||||
|
() -> assertTrue(
|
||||||
|
arena.getCharacters()
|
||||||
|
.contains(iceGolem)
|
||||||
|
),
|
||||||
|
() -> assertFalse(
|
||||||
|
arena.getCharacters()
|
||||||
|
.contains(player)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@DisplayName("Verwaltung der Arena")
|
||||||
|
class ArenaManagementTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"remove entfernt eine vorhandene Figur"
|
||||||
|
)
|
||||||
|
void removeDeletesCharacter() {
|
||||||
|
// Arrange
|
||||||
|
Arena<Player> arena = new Arena<>();
|
||||||
|
|
||||||
|
Player player = new Player(
|
||||||
|
"Held",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
arena.add(player);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
boolean removed =
|
||||||
|
arena.remove(player);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertAll(
|
||||||
|
() -> assertTrue(removed),
|
||||||
|
() -> assertTrue(
|
||||||
|
arena.isEmpty()
|
||||||
|
),
|
||||||
|
() -> assertEquals(
|
||||||
|
0,
|
||||||
|
arena.size()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"remove liefert false für eine unbekannte Figur"
|
||||||
|
)
|
||||||
|
void removingUnknownCharacterReturnsFalse() {
|
||||||
|
// Arrange
|
||||||
|
Arena<Player> arena = new Arena<>();
|
||||||
|
|
||||||
|
Player storedPlayer = new Player(
|
||||||
|
"Held",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
Player unknownPlayer = new Player(
|
||||||
|
"Fremder",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
arena.add(storedPlayer);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
boolean removed =
|
||||||
|
arena.remove(unknownPlayer);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertAll(
|
||||||
|
() -> assertFalse(removed),
|
||||||
|
() -> assertEquals(
|
||||||
|
1,
|
||||||
|
arena.size()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"getCharacters liefert ein unveränderliches Set"
|
||||||
|
)
|
||||||
|
void returnedCharacterSetIsUnmodifiable() {
|
||||||
|
// Arrange
|
||||||
|
Arena<Player> arena = new Arena<>();
|
||||||
|
|
||||||
|
arena.add(
|
||||||
|
new Player(
|
||||||
|
"Held",
|
||||||
|
100
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
Set<Player> characters =
|
||||||
|
arena.getCharacters();
|
||||||
|
|
||||||
|
// Act und Assert
|
||||||
|
assertThrows(
|
||||||
|
UnsupportedOperationException.class,
|
||||||
|
characters::clear
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"null kann nicht hinzugefügt werden"
|
||||||
|
)
|
||||||
|
void nullCharacterCannotBeAdded() {
|
||||||
|
Arena<Character> arena = new Arena<>();
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
NullPointerException.class,
|
||||||
|
() -> arena.add(null)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName(
|
||||||
|
"attackAll akzeptiert keinen null-Angriff"
|
||||||
|
)
|
||||||
|
void nullAttackIsRejected() {
|
||||||
|
Arena<Character> arena = new Arena<>();
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
NullPointerException.class,
|
||||||
|
() -> arena.attackAll(null)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/Attack.java
Normal file
20
src/Attack.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
public abstract class Attack {
|
||||||
|
|
||||||
|
private final int baseDamage;
|
||||||
|
|
||||||
|
protected Attack(int baseDamage) {
|
||||||
|
if (baseDamage <= 0) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Base damage must be positive"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.baseDamage = baseDamage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getBaseDamage() {
|
||||||
|
return baseDamage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void execute(Character target);
|
||||||
|
}
|
||||||
11
src/Character.java
Normal file
11
src/Character.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
public interface Character {
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
int getHitPoints();
|
||||||
|
|
||||||
|
boolean isDefeated();
|
||||||
|
|
||||||
|
void receive(FireSpell attack);
|
||||||
|
void receive(SwordAttack attack);
|
||||||
|
}
|
||||||
61
src/Enemy.java
Normal file
61
src/Enemy.java
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
public abstract class Enemy implements Character {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private int hitPoints;
|
||||||
|
|
||||||
|
protected Enemy(String name, int hitPoints) {
|
||||||
|
if (name == null || name.isBlank()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Name must not be blank"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hitPoints <= 0) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Hit points must be positive"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = name;
|
||||||
|
this.hitPoints = hitPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int getHitPoints() {
|
||||||
|
return hitPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isDefeated() {
|
||||||
|
return hitPoints == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void takeDamage(int damage) {
|
||||||
|
if (damage < 0) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Damage must not be negative"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
hitPoints = Math.max(
|
||||||
|
0,
|
||||||
|
hitPoints - damage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String toString() {
|
||||||
|
return "%s{name='%s', hitPoints=%d, defeated=%s}"
|
||||||
|
.formatted(
|
||||||
|
getClass().getSimpleName(),
|
||||||
|
name,
|
||||||
|
hitPoints,
|
||||||
|
isDefeated()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/FireSpell.java
Normal file
20
src/FireSpell.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public final class FireSpell extends Attack {
|
||||||
|
|
||||||
|
private static final int BASE_DAMAGE = 30;
|
||||||
|
|
||||||
|
public FireSpell() {
|
||||||
|
super(BASE_DAMAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Character target) {
|
||||||
|
Objects.requireNonNull(
|
||||||
|
target,
|
||||||
|
"Target must not be null"
|
||||||
|
);
|
||||||
|
|
||||||
|
target.receive(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/IceGolem.java
Normal file
34
src/IceGolem.java
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public final class IceGolem extends Enemy {
|
||||||
|
|
||||||
|
public IceGolem(String name, int hitPoints) {
|
||||||
|
super(name, hitPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receive(SwordAttack attack) {
|
||||||
|
Objects.requireNonNull(
|
||||||
|
attack,
|
||||||
|
"Attack must not be null"
|
||||||
|
);
|
||||||
|
|
||||||
|
int damage =
|
||||||
|
attack.getBaseDamage() / 2;
|
||||||
|
|
||||||
|
takeDamage(damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receive(FireSpell attack) {
|
||||||
|
Objects.requireNonNull(
|
||||||
|
attack,
|
||||||
|
"Attack must not be null"
|
||||||
|
);
|
||||||
|
|
||||||
|
int damage =
|
||||||
|
attack.getBaseDamage() * 2;
|
||||||
|
|
||||||
|
takeDamage(damage);
|
||||||
|
}
|
||||||
|
}
|
||||||
166
src/Main.java
Normal file
166
src/Main.java
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
public final class Main {
|
||||||
|
|
||||||
|
private Main() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
demonstratePlayerArena();
|
||||||
|
demonstrateEnemyArena();
|
||||||
|
demonstrateMixedArena();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Homogene Arena:
|
||||||
|
* Es dürfen ausschließlich Player-Objekte
|
||||||
|
* hinzugefügt werden.
|
||||||
|
*/
|
||||||
|
private static void demonstratePlayerArena() {
|
||||||
|
Arena<Player> arena = new Arena<>();
|
||||||
|
|
||||||
|
Player warrior = new Player(
|
||||||
|
"Krieger",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
Player mage = new Player(
|
||||||
|
"Magier",
|
||||||
|
70
|
||||||
|
);
|
||||||
|
|
||||||
|
arena.add(warrior);
|
||||||
|
arena.add(mage);
|
||||||
|
|
||||||
|
System.out.println("Spieler-Arena vor dem Angriff:");
|
||||||
|
printArena(arena);
|
||||||
|
|
||||||
|
arena.attackAll(new SwordAttack());
|
||||||
|
|
||||||
|
System.out.println("Spieler-Arena nach dem Schwertangriff:");
|
||||||
|
printArena(arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Arena mit unterschiedlichen Gegnerklassen.
|
||||||
|
*
|
||||||
|
* Skeleton und IceGolem sind beide Enemy,
|
||||||
|
* reagieren aber unterschiedlich auf FireSpell.
|
||||||
|
*/
|
||||||
|
private static void demonstrateEnemyArena() {
|
||||||
|
Arena<Enemy> arena = new Arena<>();
|
||||||
|
|
||||||
|
Skeleton skeleton = new Skeleton(
|
||||||
|
"Knochensoldat",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
IceGolem iceGolem = new IceGolem(
|
||||||
|
"Frostwächter",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
arena.add(skeleton);
|
||||||
|
arena.add(iceGolem);
|
||||||
|
|
||||||
|
System.out.println("Gegner-Arena vor dem Angriff:");
|
||||||
|
printArena(arena);
|
||||||
|
|
||||||
|
arena.attackAll(new FireSpell());
|
||||||
|
|
||||||
|
System.out.println("Gegner-Arena nach dem Feuerzauber:");
|
||||||
|
printArena(arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gemischte Arena:
|
||||||
|
* Spieler und Gegner sind gemeinsam möglich,
|
||||||
|
* weil alle Character implementieren.
|
||||||
|
*/
|
||||||
|
private static void demonstrateMixedArena() {
|
||||||
|
Arena<Character> arena = new Arena<>();
|
||||||
|
|
||||||
|
Player player = new Player(
|
||||||
|
"Held",
|
||||||
|
50
|
||||||
|
);
|
||||||
|
|
||||||
|
Skeleton skeleton = new Skeleton(
|
||||||
|
"Schwaches Skelett",
|
||||||
|
20
|
||||||
|
);
|
||||||
|
|
||||||
|
IceGolem iceGolem = new IceGolem(
|
||||||
|
"Frostwächter",
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
arena.add(player);
|
||||||
|
arena.add(skeleton);
|
||||||
|
arena.add(iceGolem);
|
||||||
|
|
||||||
|
System.out.println("Gemischte Arena vor dem Angriff:");
|
||||||
|
printArena(arena);
|
||||||
|
|
||||||
|
arena.attackAll(new FireSpell());
|
||||||
|
|
||||||
|
System.out.println("Gemischte Arena nach dem Feuerzauber:");
|
||||||
|
printArena(arena);
|
||||||
|
|
||||||
|
System.out.println("Lebende Figuren:");
|
||||||
|
printCharacters(arena.getLivingCharacters());
|
||||||
|
|
||||||
|
System.out.println("Besiegte Figuren:");
|
||||||
|
printCharacters(arena.getDefeatedCharacters());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Besiegte Figuren entfernen.
|
||||||
|
*
|
||||||
|
* getDefeatedCharacters() liefert eine neue Liste,
|
||||||
|
* daher wird das Set nicht während seiner Iteration
|
||||||
|
* verändert.
|
||||||
|
*/
|
||||||
|
for (Character character
|
||||||
|
: arena.getDefeatedCharacters()) {
|
||||||
|
arena.remove(character);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Arena nach dem Entfernen besiegter Figuren:");
|
||||||
|
printArena(arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generische Hilfsmethode:
|
||||||
|
* Funktioniert für Arena<Player>,
|
||||||
|
* Arena<Enemy> und Arena<Character>.
|
||||||
|
*/
|
||||||
|
private static <C extends Character> void printArena(
|
||||||
|
Arena<C> arena
|
||||||
|
) {
|
||||||
|
System.out.printf(
|
||||||
|
"Größe: %d, lebend: %d, besiegt: %d%n",
|
||||||
|
arena.size(),
|
||||||
|
arena.getLivingCharacters().size(),
|
||||||
|
arena.getDefeatedCharacters().size()
|
||||||
|
);
|
||||||
|
|
||||||
|
printCharacters(arena.getCharacters());
|
||||||
|
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterable<? extends Character> erlaubt sowohl
|
||||||
|
* Set<C> als auch List<C>.
|
||||||
|
*/
|
||||||
|
private static void printCharacters(
|
||||||
|
Iterable<? extends Character> characters
|
||||||
|
) {
|
||||||
|
for (Character character : characters) {
|
||||||
|
System.out.printf(
|
||||||
|
"%s: %d HP, besiegt: %s%n",
|
||||||
|
character.getName(),
|
||||||
|
character.getHitPoints(),
|
||||||
|
character.isDefeated()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/Player.java
Normal file
54
src/Player.java
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
public class Player implements Character {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private int hitPoints;
|
||||||
|
|
||||||
|
public Player(String name, int hitPoints) {
|
||||||
|
if (name == null || name.isBlank()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Name must not be null or blank"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hitPoints <= 0) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Hit points must be positive"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = name;
|
||||||
|
this.hitPoints = hitPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHitPoints() {
|
||||||
|
return hitPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDefeated() {
|
||||||
|
return hitPoints <= 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receive(FireSpell attack) {
|
||||||
|
hitPoints = Math.max(
|
||||||
|
0,
|
||||||
|
hitPoints - attack.getBaseDamage()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receive(SwordAttack attack) {
|
||||||
|
hitPoints = Math.max(
|
||||||
|
0,
|
||||||
|
hitPoints - attack.getBaseDamage()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/Skeleton.java
Normal file
41
src/Skeleton.java
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public final class Skeleton extends Enemy {
|
||||||
|
|
||||||
|
private static final int SWORD_BONUS_DAMAGE = 10;
|
||||||
|
private static final int FIRE_RESISTANCE = 5;
|
||||||
|
|
||||||
|
public Skeleton(String name, int hitPoints) {
|
||||||
|
super(name, hitPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receive(SwordAttack attack) {
|
||||||
|
Objects.requireNonNull(
|
||||||
|
attack,
|
||||||
|
"Attack must not be null"
|
||||||
|
);
|
||||||
|
|
||||||
|
int damage =
|
||||||
|
attack.getBaseDamage()
|
||||||
|
+ SWORD_BONUS_DAMAGE;
|
||||||
|
|
||||||
|
takeDamage(damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receive(FireSpell attack) {
|
||||||
|
Objects.requireNonNull(
|
||||||
|
attack,
|
||||||
|
"Attack must not be null"
|
||||||
|
);
|
||||||
|
|
||||||
|
int damage = Math.max(
|
||||||
|
0,
|
||||||
|
attack.getBaseDamage()
|
||||||
|
- FIRE_RESISTANCE
|
||||||
|
);
|
||||||
|
|
||||||
|
takeDamage(damage);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/SwordAttack.java
Normal file
20
src/SwordAttack.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public final class SwordAttack extends Attack {
|
||||||
|
|
||||||
|
private static final int BASE_DAMAGE = 30;
|
||||||
|
|
||||||
|
public SwordAttack() {
|
||||||
|
super(BASE_DAMAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Character target) {
|
||||||
|
Objects.requireNonNull(
|
||||||
|
target,
|
||||||
|
"Target must not be null"
|
||||||
|
);
|
||||||
|
|
||||||
|
target.receive(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user