diff --git a/src/main/java/bossmonster/Main.java b/src/main/java/bossmonster/Main.java index d9488c65..936a27a5 100644 --- a/src/main/java/bossmonster/Main.java +++ b/src/main/java/bossmonster/Main.java @@ -1,7 +1,11 @@ package bossmonster; +import bossmonster.controller.MainController; +import bossmonster.repository.MemoryRepository; +import bossmonster.service.RepositoryService; + public class Main { public static void main(String[] args) { - System.out.println("Hello world!"); + new MainController(new RepositoryService(MemoryRepository.getInstance())).run(); } } diff --git a/src/main/java/bossmonster/TypeQualifier.java b/src/main/java/bossmonster/TypeQualifier.java new file mode 100644 index 00000000..89b5d999 --- /dev/null +++ b/src/main/java/bossmonster/TypeQualifier.java @@ -0,0 +1,14 @@ +package bossmonster; + +import bossmonster.domain.creatures.Creature; + +public class TypeQualifier { + public static boolean checkCreatureType(Class type, Creature checked) { + try { + type.cast(checked); + } catch (ClassCastException e) { + return false; + } + return true; + } +} diff --git a/src/main/java/bossmonster/controller/BattleController.java b/src/main/java/bossmonster/controller/BattleController.java new file mode 100644 index 00000000..10be3e2c --- /dev/null +++ b/src/main/java/bossmonster/controller/BattleController.java @@ -0,0 +1,92 @@ +package bossmonster.controller; + +import bossmonster.TypeQualifier; +import bossmonster.domain.attack.AttackType; +import bossmonster.domain.battle.BattleField; +import bossmonster.domain.battle.GameState; +import bossmonster.domain.creatures.Creature; +import bossmonster.domain.creatures.Player; +import bossmonster.dto.BattleDTO; +import bossmonster.service.BattleService; +import bossmonster.view.InputView; +import bossmonster.view.OutputView; +import bossmonster.view.outputview.BossSprite; +import bossmonster.view.outputview.ProgressOutputView; + +import java.util.Map; + +import static bossmonster.controller.Parameter.*; + +public final class BattleController implements Controller { + private final BattleService battleService; + private final InputView inputView; + private final OutputView outputView; + private BattleField battle; + + public BattleController(BattleService battleService, InputView inputView, OutputView outputView) { + this.battleService = battleService; + this.inputView = inputView; + this.outputView = outputView; + } + + + @Override + public GameState process(Map param, Map model) { + battle = (BattleField) model.get(BATTLE.getName()); + battle.increaseBattleCount(); + for (Creature attacker : battleService.getAttackOrders(battle)) { + getAttack(attacker, param, model); + outputView.show(param, model); + } + return checkBattleEnd(param, model); + } + + + private void getAttack(Creature attacker, Map param, Map model) { + if (TypeQualifier.checkCreatureType(Player.class, attacker)) { + model.put(PLAYER_ATTACK.getName(), selectAttack(param, model)); + return; + } + model.put(BOSS_ATTACK.getName(), battleService.attackByBoss(battle, AttackType.Boss.BOSS)); + } + + private BattleDTO selectAttack(Map param, Map model) { + getPlayerAttackParam(param, model); + int attackNumber = Integer.parseInt(param.get(PLAYER_ATTACK.getName())); + AttackType.Player attackType = AttackType.createPlayerAttack(attackNumber); + try { + return battleService.attackByPlayer(battle, attackType); + } catch (IllegalArgumentException e) { + param.put(ERROR.getName(), e.getMessage()); + return selectAttack(param, model); + } + } + + private void getPlayerAttackParam(Map param, Map model) { + param.put(PLAYER_ATTACK_TYPE.getName(), null); + model.put(PLAYER_ATTACK_SELECT.getName(), AttackType.Player.class); + try { + outputView.show(param, model); + inputView.readLine(param, model); + } catch (IllegalArgumentException e) { + param.put(ERROR.getName(), e.getMessage()); + getPlayerAttackParam(param, model); + } + } + + private GameState checkBattleEnd(Map param, Map model) { + if (battle.isBattleEnd()) { + return GameState.RESULT; + } + getViewBattleState(new ProgressOutputView(), param, model); + return GameState.PLAYING; + } + + private void getViewBattleState(OutputView outputView, Map param, + Map model) { + model.put(BOSS_SPRITE.getName(), BossSprite.BOSS_DAMAGED); + model.put(BOSS.getName(), battle.getBoss()); + model.put(PLAYER.getName(), battle.getPlayer()); + outputView.show(param, model); + } +} diff --git a/src/main/java/bossmonster/controller/ControlConfig.java b/src/main/java/bossmonster/controller/ControlConfig.java new file mode 100644 index 00000000..a4884f56 --- /dev/null +++ b/src/main/java/bossmonster/controller/ControlConfig.java @@ -0,0 +1,31 @@ +package bossmonster.controller; + +import bossmonster.service.BattleService; +import bossmonster.service.CreatureService; +import bossmonster.service.ResultService; +import bossmonster.view.inputview.BattleInputView; +import bossmonster.view.inputview.StartInputView; +import bossmonster.view.outputview.BattleOutputView; +import bossmonster.view.outputview.ErrorOutputView; +import bossmonster.view.outputview.ResultOutputView; +import bossmonster.view.outputview.StartOutputView; + +public class ControlConfig { + public static Controller controlCreature() { + return new CreatureController( + new CreatureService(), + new StartInputView(), + new StartOutputView(new ErrorOutputView())); + } + + public static Controller controlBattle() { + return new BattleController(new BattleService(), + new BattleInputView(), + new BattleOutputView(new ErrorOutputView())); + } + + public static Controller controlResult() { + return new ResultController(new ResultService(), + new ResultOutputView()); + } +} diff --git a/src/main/java/bossmonster/controller/Controller.java b/src/main/java/bossmonster/controller/Controller.java new file mode 100644 index 00000000..33d3e32a --- /dev/null +++ b/src/main/java/bossmonster/controller/Controller.java @@ -0,0 +1,9 @@ +package bossmonster.controller; + +import bossmonster.domain.battle.GameState; + +import java.util.Map; + +public interface Controller { + GameState process(Map param, Map model); +} diff --git a/src/main/java/bossmonster/controller/CreatureController.java b/src/main/java/bossmonster/controller/CreatureController.java new file mode 100644 index 00000000..c09c54bf --- /dev/null +++ b/src/main/java/bossmonster/controller/CreatureController.java @@ -0,0 +1,85 @@ +package bossmonster.controller; + +import bossmonster.domain.battle.GameState; +import bossmonster.domain.creatures.Boss; +import bossmonster.domain.creatures.Player; +import bossmonster.service.CreatureService; +import bossmonster.view.InputView; +import bossmonster.view.OutputView; +import bossmonster.view.outputview.BossSprite; +import bossmonster.view.outputview.ProgressOutputView; + +import java.util.Map; + +import static bossmonster.controller.Parameter.*; + +public class CreatureController implements Controller { + private final CreatureService creatureService; + private final InputView inputView; + private final OutputView outputView; + + + public CreatureController(CreatureService creatureService, InputView inputView, OutputView outputView) { + this.creatureService = creatureService; + this.inputView = inputView; + this.outputView = outputView; + } + + @Override + public GameState process(Map param, Map model) { + model.put(BOSS.getName(), getBoss(param, model)); + model.put(PLAYER.getName(), getPlayer(param, model)); + getViewBattleState(new ProgressOutputView(), param, model); + return GameState.PLAYING; + } + + private Boss getBoss(Map param, Map model) { + try { + param.put(BOSS_HP.getName(), null); + outputView.show(param, model); + inputView.readLine(param, model); + return createBoss(param); + } catch (IllegalArgumentException e) { + param.put(ERROR.getName(), e.getMessage()); + return getBoss(param, model); + } + } + + private Boss createBoss(Map param) { + int bossHp = Integer.parseInt(param.get(BOSS_HP.getName())); + return creatureService.createBoss(bossHp); + } + + private Player getPlayer(Map param, Map model) { + try { + getPlayerName(param, model); + getPlayerStat(param, model); + return createPlayer(param); + } catch (IllegalArgumentException e) { + param.put(ERROR.getName(), e.getMessage()); + return getPlayer(param, model); + } + } + + private void getPlayerName(Map param, Map model) { + param.put(PLAYER_NAME.getName(), null); + outputView.show(param, model); + inputView.readLine(param, model); + } + + private void getPlayerStat(Map param, Map model) { + param.put(PLAYER_HP.getName(), null); + outputView.show(param, model); + inputView.readLine(param, model); + } + + private Player createPlayer(Map param) { + return creatureService.createPlayer(param); + } + + private void getViewBattleState(OutputView outputView, Map param, Map + model) { + model.put(BOSS_SPRITE.getName(), BossSprite.BOSS_IDLE); + outputView.show(param, model); + } +} diff --git a/src/main/java/bossmonster/controller/MainController.java b/src/main/java/bossmonster/controller/MainController.java new file mode 100644 index 00000000..96a0375c --- /dev/null +++ b/src/main/java/bossmonster/controller/MainController.java @@ -0,0 +1,57 @@ +package bossmonster.controller; + +import bossmonster.domain.battle.BattleField; +import bossmonster.domain.battle.GameState; +import bossmonster.domain.creatures.Boss; +import bossmonster.domain.creatures.Player; +import bossmonster.service.RepositoryService; +import bossmonster.view.InputView; +import bossmonster.view.OutputView; + +import java.util.HashMap; +import java.util.Map; + +import static bossmonster.controller.Parameter.*; + +public class MainController { + private final RepositoryService repositoryService; + + + private InputView inputView; + private OutputView outputView; + private final Map controllerMap = new HashMap<>(); + + public MainController(RepositoryService repositoryService) { + this.repositoryService = repositoryService; + controllerMap.put(GameState.START, ControlConfig.controlCreature()); + controllerMap.put(GameState.PLAYING, ControlConfig.controlBattle()); + controllerMap.put(GameState.RESULT, ControlConfig.controlResult()); + } + + public void run() { + GameState gameState = GameState.START; + Long battleId = initGame(gameState); + gameState = GameState.PLAYING; + while (gameState != GameState.FINISH) { + gameState = accessController(battleId, gameState); + } + } + + private Long initGame(GameState state) { + Map param = new HashMap<>(); + Map model = new HashMap<>(); + controllerMap.get(state).process(param, model); + + return repositoryService.saveBattle( + (Player) model.get(PLAYER.getName()), + (Boss) model.get(BOSS.getName())); + } + + private GameState accessController(Long battleId, GameState state) { + Map param = new HashMap<>(); + Map model = new HashMap<>(); + BattleField battle = repositoryService.getBattleById(battleId); + model.put(BATTLE.getName(), battle); + return controllerMap.get(state).process(param, model); + } +} diff --git a/src/main/java/bossmonster/controller/Parameter.java b/src/main/java/bossmonster/controller/Parameter.java new file mode 100644 index 00000000..4307495f --- /dev/null +++ b/src/main/java/bossmonster/controller/Parameter.java @@ -0,0 +1,31 @@ +package bossmonster.controller; + +public enum Parameter { + BOSS("boss"), + BOSS_HP("bossHp"), + BOSS_ATTACK("bossAttack"), + BOSS_SPRITE("bossSprite"), + PLAYER_ATTACK_SELECT("attackSelect"), + PLAYER_ATTACK_TYPE("attackType"), + PLAYER("player"), + PLAYER_NAME("playerName"), + PLAYER_HP("playerHp"), + PLAYER_ATTACK("playerAttack"), + PLAYER_MP("playerMp"), + BATTLE("battle"), + BOSS_WIN("bossWin"), + PLAYER_WIN("playerWin"), + BATTLE_COUNT("battleCount"), + ERROR("error"); + private final String name; + + Parameter(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + +} diff --git a/src/main/java/bossmonster/controller/ResultController.java b/src/main/java/bossmonster/controller/ResultController.java new file mode 100644 index 00000000..b247b234 --- /dev/null +++ b/src/main/java/bossmonster/controller/ResultController.java @@ -0,0 +1,61 @@ +package bossmonster.controller; + +import bossmonster.TypeQualifier; +import bossmonster.domain.battle.BattleField; +import bossmonster.domain.battle.GameState; +import bossmonster.domain.creatures.Creature; +import bossmonster.domain.creatures.Player; +import bossmonster.service.ResultService; +import bossmonster.view.OutputView; +import bossmonster.view.outputview.BossSprite; +import bossmonster.view.outputview.ProgressOutputView; + +import java.util.Map; + +import static bossmonster.controller.Parameter.*; + +public class ResultController implements Controller { + private final ResultService resultService; + private final OutputView outputView; + private BattleField battle; + + public ResultController(ResultService resultService, OutputView outputView) { + this.resultService = resultService; + this.outputView = outputView; + } + + @Override + public GameState process(Map param, Map model) { + battle = (BattleField) model.get(BATTLE.getName()); + moveToWinnerView(param, model); + return GameState.FINISH; + } + + private void moveToWinnerView(Map param, Map model) { + Creature winner = resultService.checkWinner(battle); + if (TypeQualifier.checkCreatureType(Player.class, winner)) { + getPlayerWinView(param, model); + return; + } + getBossWinView(param, model); + } + + private void getPlayerWinView(Map param, Map model) { + model.put(PLAYER_WIN.getName(), battle.getPlayer()); + outputView.show(param, model); + } + + private void getBossWinView(Map param, Map model) { + model.put(BOSS_WIN.getName(), battle.getPlayer()); + getViewBattleState(new ProgressOutputView(), param, model); + outputView.show(param, model); + } + + private void getViewBattleState(OutputView outputView, Map param, + Map model) { + model.put(BOSS_SPRITE.getName(), BossSprite.BOSS_WIN); + model.put(BOSS.getName(), battle.getBoss()); + model.put(PLAYER.getName(), battle.getPlayer()); + outputView.show(param, model); + } +} diff --git a/src/main/java/bossmonster/domain/attack/AttackEntity.java b/src/main/java/bossmonster/domain/attack/AttackEntity.java new file mode 100644 index 00000000..42ae8c67 --- /dev/null +++ b/src/main/java/bossmonster/domain/attack/AttackEntity.java @@ -0,0 +1,49 @@ +package bossmonster.domain.attack; + +import bossmonster.domain.creatures.Creature; + +public final class AttackEntity { + private final Creature attacker; + + private final Creature attacked; + private final int damage; + private final int mpCost; + + public AttackEntity(Creature attacker, Creature attacked, int damage, int mpCost) { + this.attacker = attacker; + this.attacked = attacked; + this.damage = damage; + this.mpCost = mpCost; + } + + public AttackEntity(Creature attacker, Creature attacked, AttackType.Player attackType) { + this.attacker = attacker; + this.attacked = attacked; + this.damage = attackType.getDamage(); + this.mpCost = attackType.getMpCost(); + } + + public Creature getAttacker() { + return attacker; + } + + public Creature getAttacked() { + return attacked; + } + + public int getDamage() { + return damage; + } + + public int getMpCost() { + return mpCost; + } + + public int attack() { + if (attacker.getHp() <= 0 || attacked.getHp() <= 0) return 0; + + this.attacker.decreaseMpAs(mpCost); + this.attacked.damaged(damage); + return damage; + } +} diff --git a/src/main/java/bossmonster/domain/attack/AttackRandomNumberGenerator.java b/src/main/java/bossmonster/domain/attack/AttackRandomNumberGenerator.java new file mode 100644 index 00000000..af9d580d --- /dev/null +++ b/src/main/java/bossmonster/domain/attack/AttackRandomNumberGenerator.java @@ -0,0 +1,13 @@ +package bossmonster.domain.attack; + +import java.time.LocalDateTime; +import java.util.Random; + +public class AttackRandomNumberGenerator implements RandomNumberGenerator { + @Override + public int generate(int start, int end) { + Random random = new Random(); + random.setSeed(LocalDateTime.now().getSecond()); + return start + random.nextInt(end + 1); + } +} diff --git a/src/main/java/bossmonster/domain/attack/AttackType.java b/src/main/java/bossmonster/domain/attack/AttackType.java new file mode 100644 index 00000000..ec935387 --- /dev/null +++ b/src/main/java/bossmonster/domain/attack/AttackType.java @@ -0,0 +1,87 @@ +package bossmonster.domain.attack; + +import java.util.Arrays; + +public enum AttackType { + ; + private static final int BOSS_MAX_DAMAGE = 20; + + private static final int BOSS_MIN_DAMAGE = 0; + private AttackType.Boss boss; + private AttackType.Player player; + + public static AttackType.Boss createBossAttack() { + return Boss.BOSS; + } + + public static AttackType.Player createPlayerAttack(int number) { + return Player.getAttackTypeByNumber(number); + } + + + public enum Boss { + BOSS("보스", setDamage()); + + + private final String attackName; + + private final int damage; + + Boss(String attackName, int damage) { + this.attackName = attackName; + this.damage = damage; + } + + public String getAttackName() { + return attackName; + } + + public int getDamage() { + return damage; + } + + public static int setDamage() { + return new AttackRandomNumberGenerator() + .generate(BOSS_MIN_DAMAGE, BOSS_MAX_DAMAGE); + } + } + + public enum Player { + NORMAL("물리 공격", 1, 10, -10), + MAGIC("마법 공격", 2, 20, 30); + private final String attackName; + private final int typeNumber; + private final int damage; + private final int mpCost; + + Player(String attackName, int typeNumber, int damage, int mpCost) { + this.attackName = attackName; + this.typeNumber = typeNumber; + this.damage = damage; + this.mpCost = mpCost; + } + + public String getAttackName() { + return attackName; + } + + public int getTypeNumber() { + return typeNumber; + } + + public int getDamage() { + return damage; + } + + public int getMpCost() { + return mpCost; + } + + public static AttackType.Player getAttackTypeByNumber(int typeNumber) { + return Arrays.stream(AttackType.Player.values()) + .filter(value -> value.getTypeNumber() == typeNumber) + .findAny() + .orElse(AttackType.Player.NORMAL); + } + } +} diff --git a/src/main/java/bossmonster/domain/attack/RandomNumberGenerator.java b/src/main/java/bossmonster/domain/attack/RandomNumberGenerator.java new file mode 100644 index 00000000..c56ce381 --- /dev/null +++ b/src/main/java/bossmonster/domain/attack/RandomNumberGenerator.java @@ -0,0 +1,5 @@ +package bossmonster.domain.attack; + +public interface RandomNumberGenerator { + int generate(int start, int end); +} diff --git a/src/main/java/bossmonster/domain/battle/BattleField.java b/src/main/java/bossmonster/domain/battle/BattleField.java new file mode 100644 index 00000000..8b0f3e09 --- /dev/null +++ b/src/main/java/bossmonster/domain/battle/BattleField.java @@ -0,0 +1,68 @@ +package bossmonster.domain.battle; + +import bossmonster.domain.creatures.Boss; +import bossmonster.domain.creatures.Creature; +import bossmonster.domain.creatures.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.TreeMap; + +public final class BattleField { + private static final int PLAYER_ATTACK_PRIORITY = 0; + private static final int BOSS_ATTACK_PRIORITY = 1; + public List attackOrder; + private final Boss boss; + private final Player player; + + private final Long id; + private int battleCount = 0; + + public BattleField(Long id, Player player, Boss boss) { + this.id = id; + this.player = player; + this.boss = boss; + + attackOrder = setAttackOrders(); + } + + public void increaseBattleCount() { + battleCount++; + } + + public List getBattleResult() { + return new ArrayList<>(List.of(player, boss)); + } + + public int getBattleCount() { + return battleCount; + } + + public Player getPlayer() { + return player; + } + + public Boss getBoss() { + return boss; + } + + public boolean isBattleEnd() { + return (player.getHp() <= 0 || boss.getHp() <= 0); + } + + public Long getId() { + return id; + } + + public List getAttackOrder() { + return attackOrder; + } + + private List setAttackOrders() { + TreeMap orderMap = new TreeMap<>(); + orderMap.put(BOSS_ATTACK_PRIORITY, boss); + orderMap.put(PLAYER_ATTACK_PRIORITY, player); + + return new ArrayList<>(orderMap.values()); + } +} diff --git a/src/main/java/bossmonster/domain/battle/GameState.java b/src/main/java/bossmonster/domain/battle/GameState.java new file mode 100644 index 00000000..7c71e752 --- /dev/null +++ b/src/main/java/bossmonster/domain/battle/GameState.java @@ -0,0 +1,5 @@ +package bossmonster.domain.battle; + +public enum GameState { + START, PLAYING, RESULT, FINISH; +} diff --git a/src/main/java/bossmonster/domain/creatures/Boss.java b/src/main/java/bossmonster/domain/creatures/Boss.java new file mode 100644 index 00000000..83d21f27 --- /dev/null +++ b/src/main/java/bossmonster/domain/creatures/Boss.java @@ -0,0 +1,19 @@ +package bossmonster.domain.creatures; + +public final class Boss extends Creature { + + private static final int BOSS_MIN_HP = 100; + private static final int BOSS_MAX_HP = 300; + + public Boss(int totalHp, int totalMp) { + super(totalHp, totalMp); + validateHp(totalHp); + } + + + private void validateHp(int totalHp) { + if (totalHp < BOSS_MIN_HP || totalHp > BOSS_MAX_HP) { + throw new IllegalArgumentException("보스의 HP는 100 ~ 300 사이로 설정해야합니다."); + } + } +} diff --git a/src/main/java/bossmonster/domain/creatures/Creature.java b/src/main/java/bossmonster/domain/creatures/Creature.java new file mode 100644 index 00000000..25d42139 --- /dev/null +++ b/src/main/java/bossmonster/domain/creatures/Creature.java @@ -0,0 +1,52 @@ +package bossmonster.domain.creatures; + +public abstract class Creature { + protected final int totalHp; + protected final int totalMp; + protected int hp; + protected int mp; + + public Creature(int totalHp, int totalMp) { + this.totalHp = totalHp; + this.totalMp = totalMp; + this.hp = totalHp; + this.mp = totalMp; + } + + + public int getTotalHp() { + return totalHp; + } + + public int getTotalMp() { + return totalMp; + } + + public int getHp() { + return hp; + } + + public int getMp() { + return mp; + } + + public void damaged(int amount) { + hp -= amount; + if (hp < 0) { + hp = 0; + } + + } + + public void decreaseMpAs(int mpCost) { + validatePossibleAttack(mpCost); + mp -= mpCost; + if (mp >= totalHp) mp = totalMp; + } + + private void validatePossibleAttack(int mpCost) { + if (mp - mpCost < 0) { + throw new IllegalArgumentException("mp가 부족해서 해당 공격을 사용할 수 없습니다."); + } + } +} \ No newline at end of file diff --git a/src/main/java/bossmonster/domain/creatures/Player.java b/src/main/java/bossmonster/domain/creatures/Player.java new file mode 100644 index 00000000..80d5f70c --- /dev/null +++ b/src/main/java/bossmonster/domain/creatures/Player.java @@ -0,0 +1,31 @@ +package bossmonster.domain.creatures; + +public final class Player extends Creature { + private static final int HP_MP_SUM = 200; + private static final int PLAYER_NAME_MAX_LENGTH = 5; + private final String name; + + public Player(int totalHp, int totalMp, String name) { + super(totalHp, totalMp); + this.name = name; + validateStatSum(totalHp, totalMp); + validateNameLength(name); + } + + public String getName() { + return name; + } + + + private void validateNameLength(String name) { + if (name.length() > PLAYER_NAME_MAX_LENGTH) { + throw new IllegalArgumentException("[ERROR] 이름은 5글자 이하로 입력해야 합니다."); + } + } + + private void validateStatSum(int hp, int mp) { + if (hp + mp != HP_MP_SUM) { + throw new IllegalArgumentException("[ERROR] Hp와 MP의 합은 200이어야 합니다."); + } + } +} diff --git a/src/main/java/bossmonster/dto/BattleDTO.java b/src/main/java/bossmonster/dto/BattleDTO.java new file mode 100644 index 00000000..a7f50743 --- /dev/null +++ b/src/main/java/bossmonster/dto/BattleDTO.java @@ -0,0 +1,27 @@ +package bossmonster.dto; + +public class BattleDTO { + private int damage; + private String attackName; + + public void setDamage(int damage) { + this.damage = damage; + } + + public BattleDTO(int damage, String attackName) { + this.damage = damage; + this.attackName = attackName; + } + + public void setAttackName(String attackName) { + this.attackName = attackName; + } + + public int getDamage() { + return damage; + } + + public String getAttackName() { + return attackName; + } +} diff --git a/src/main/java/bossmonster/repository/MemoryRepository.java b/src/main/java/bossmonster/repository/MemoryRepository.java new file mode 100644 index 00000000..31bde183 --- /dev/null +++ b/src/main/java/bossmonster/repository/MemoryRepository.java @@ -0,0 +1,70 @@ +package bossmonster.repository; + +import bossmonster.domain.battle.BattleField; +import bossmonster.domain.creatures.Boss; +import bossmonster.domain.creatures.Player; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static bossmonster.controller.Parameter.BATTLE; + +public class MemoryRepository { + private static final Map> store = new HashMap<>(); + + private Long sequence = 0L; + + private static MemoryRepository instance; + + private MemoryRepository() { + store.put(BATTLE.getName(), new ArrayList<>()); + } + + public static MemoryRepository getInstance() { + if (instance == null) { + instance = new MemoryRepository(); + } + return instance; + } + + public Long getSequence() { + return sequence; + } + + public Long saveBattle(BattleField battleField) { + store.get(BATTLE.getName()).add(battleField); + sequence++; + return battleField.getId(); + } + + public BattleField findBattleById(Long id) { + List battleField = store.get(BATTLE.getName()); + + return (BattleField) battleField.stream() + .filter(object -> isMatch(object, id)) + .findAny() + .orElse(null); + } + + public List findAll() { + List battleField = store.get(BATTLE.getName()); + return battleField.stream().map(e -> (BattleField) e).toList(); + } + + public Player getPlayer(Long battleId) { + BattleField battle = findBattleById(battleId); + return battle.getPlayer(); + } + + public Boss getBoss(Long battleId) { + BattleField battle = findBattleById(battleId); + return battle.getBoss(); + } + + private boolean isMatch(Object object, long targetId) { + BattleField battle = (BattleField) object; + return (battle != null && battle.getId() == targetId); + } +} diff --git a/src/main/java/bossmonster/service/BattleService.java b/src/main/java/bossmonster/service/BattleService.java new file mode 100644 index 00000000..ba2339bf --- /dev/null +++ b/src/main/java/bossmonster/service/BattleService.java @@ -0,0 +1,29 @@ +package bossmonster.service; + +import bossmonster.domain.attack.AttackEntity; +import bossmonster.domain.attack.AttackType; +import bossmonster.domain.battle.BattleField; +import bossmonster.domain.creatures.Creature; +import bossmonster.dto.BattleDTO; + +import java.util.List; + + +public class BattleService { + public List getAttackOrders(BattleField battleField) { + return battleField.getAttackOrder(); + } + + public BattleDTO attackByPlayer(BattleField battleField, AttackType.Player type) { + AttackEntity entity = new AttackEntity(battleField.getPlayer(), battleField.getBoss(), type); + int damage = entity.attack(); + return new BattleDTO(damage, type.getAttackName()); + } + + public BattleDTO attackByBoss(BattleField battleField, AttackType.Boss type) { + int damage = AttackType.Boss.setDamage(); + AttackEntity entity = new AttackEntity(battleField.getBoss(), battleField.getPlayer(), damage, 0); + entity.attack(); + return new BattleDTO(entity.getDamage(), type.getAttackName()); + } +} diff --git a/src/main/java/bossmonster/service/CreatureService.java b/src/main/java/bossmonster/service/CreatureService.java new file mode 100644 index 00000000..4ed1c595 --- /dev/null +++ b/src/main/java/bossmonster/service/CreatureService.java @@ -0,0 +1,23 @@ +package bossmonster.service; + +import bossmonster.domain.creatures.Boss; +import bossmonster.domain.creatures.Player; + +import java.util.Map; + +import static bossmonster.controller.Parameter.*; + +public class CreatureService { + + public Boss createBoss(int bossHp) { + return new Boss(bossHp, 0); + } + + public Player createPlayer(Map param) { + String name = param.get(PLAYER_NAME.getName()); + int hp = Integer.parseInt(param.get(PLAYER_HP.getName())); + int mp = Integer.parseInt(param.get(PLAYER_MP.getName())); + + return new Player(hp, mp, name); + } +} diff --git a/src/main/java/bossmonster/service/RepositoryService.java b/src/main/java/bossmonster/service/RepositoryService.java new file mode 100644 index 00000000..2d6e7e7b --- /dev/null +++ b/src/main/java/bossmonster/service/RepositoryService.java @@ -0,0 +1,30 @@ +package bossmonster.service; + +import bossmonster.domain.battle.BattleField; +import bossmonster.domain.creatures.Boss; +import bossmonster.domain.creatures.Player; +import bossmonster.repository.MemoryRepository; + +import java.util.List; + +public class RepositoryService { + private final MemoryRepository repository; + + public RepositoryService(MemoryRepository repository) { + this.repository = repository; + } + + public Long saveBattle(Player player, Boss boss) { + BattleField battleField = new BattleField(repository.getSequence(), player, boss); + return repository.saveBattle(battleField); + } + + public Long getLatestBattleId() { + List battles = repository.findAll(); + return battles.get(battles.size() - 1).getId(); + } + + public BattleField getBattleById(Long id) { + return repository.findBattleById(id); + } +} diff --git a/src/main/java/bossmonster/service/ResultService.java b/src/main/java/bossmonster/service/ResultService.java new file mode 100644 index 00000000..7426f024 --- /dev/null +++ b/src/main/java/bossmonster/service/ResultService.java @@ -0,0 +1,17 @@ +package bossmonster.service; + +import bossmonster.domain.battle.BattleField; +import bossmonster.domain.creatures.Boss; +import bossmonster.domain.creatures.Creature; +import bossmonster.domain.creatures.Player; + +public class ResultService { + public Creature checkWinner(BattleField battleField) { + Boss boss = battleField.getBoss(); + Player player = battleField.getPlayer(); + if (player.getHp() > 0 && boss.getHp() <= 0) { + return player; + } + return boss; + } +} diff --git a/src/main/java/bossmonster/view/InputValidator.java b/src/main/java/bossmonster/view/InputValidator.java new file mode 100644 index 00000000..6f4786e8 --- /dev/null +++ b/src/main/java/bossmonster/view/InputValidator.java @@ -0,0 +1,41 @@ +package bossmonster.view; + +import bossmonster.domain.attack.AttackType; + +import java.lang.constant.Constable; +import java.util.Arrays; + +public class InputValidator { + public static void isNumeric(String input) { + boolean allMatch = input.chars().allMatch(e -> ('0' <= e && e <= '9')); + if (!allMatch) { + throw new IllegalArgumentException("[ERROR] 숫자만 입력 가능합니다"); + } + } + + public static void hasSpace(String input) { + boolean isMatch = input.chars().anyMatch(e -> e == ' '); + if (isMatch) { + throw new IllegalArgumentException("[ERROR] 이름에는 공백이 포함되면 안됩니다."); + } + } + + public static void isCorrectSize(String input, String delim, int targetSize) { + String[] split = input.split(delim); + if (targetSize != split.length) { + throw new IllegalArgumentException("[ERROR]" + "(" + delim + ")" + "를 통해 " + targetSize + "개를 입력해야 합니다."); + } + } + + public static void isValidAttackNumber(String input, Object obj) { + int number = Integer.parseInt(input); + Constable type = AttackType.createPlayerAttack(number); + + boolean isValid = Arrays.stream(AttackType.Player.values()). + anyMatch(attack -> attack.getTypeNumber() == number); + + if (!isValid) { + throw new IllegalArgumentException("[ERROR] 유효하지 않은 번호입니다."); + } + } +} diff --git a/src/main/java/bossmonster/view/InputView.java b/src/main/java/bossmonster/view/InputView.java new file mode 100644 index 00000000..5f343c89 --- /dev/null +++ b/src/main/java/bossmonster/view/InputView.java @@ -0,0 +1,7 @@ +package bossmonster.view; + +import java.util.Map; + +public interface InputView { + void readLine(Map param, Map model); +} diff --git a/src/main/java/bossmonster/view/OutputView.java b/src/main/java/bossmonster/view/OutputView.java new file mode 100644 index 00000000..12db8c55 --- /dev/null +++ b/src/main/java/bossmonster/view/OutputView.java @@ -0,0 +1,7 @@ +package bossmonster.view; + +import java.util.Map; + +public interface OutputView { + void show(Map param, Map model); +} diff --git a/src/main/java/bossmonster/view/inputview/BattleInputView.java b/src/main/java/bossmonster/view/inputview/BattleInputView.java new file mode 100644 index 00000000..8c318806 --- /dev/null +++ b/src/main/java/bossmonster/view/inputview/BattleInputView.java @@ -0,0 +1,25 @@ +package bossmonster.view.inputview; + +import bossmonster.view.InputValidator; +import bossmonster.view.InputView; + +import java.util.Map; + +import static bossmonster.controller.Parameter.PLAYER_ATTACK; +import static bossmonster.controller.Parameter.PLAYER_ATTACK_TYPE; + +public class BattleInputView implements InputView { + + @Override + public void readLine(Map param, Map model) { + getReadAttackNumber(param, model); + } + + private void getReadAttackNumber(Map param, Map model) { + String input = InputReader.read(); + InputValidator.isNumeric(input); + InputValidator.isValidAttackNumber(input, model.get(PLAYER_ATTACK_TYPE.getName())); + param.put(PLAYER_ATTACK.getName(), input); + } + +} diff --git a/src/main/java/bossmonster/view/inputview/InputReader.java b/src/main/java/bossmonster/view/inputview/InputReader.java new file mode 100644 index 00000000..cea2425a --- /dev/null +++ b/src/main/java/bossmonster/view/inputview/InputReader.java @@ -0,0 +1,28 @@ +package bossmonster.view.inputview; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public final class InputReader { + public static String read() { + BufferedReader br = null; + try { + br = new BufferedReader(new InputStreamReader(System.in)); + return br.readLine(); + } catch (IOException e) { + closeReader(br); + return read(); + } + } + + private static void closeReader(BufferedReader br) { + try { + if (br != null) { + br.close(); + } + } catch (IOException e) { + System.out.println(e.getStackTrace()); + } + } +} diff --git a/src/main/java/bossmonster/view/inputview/StartInputView.java b/src/main/java/bossmonster/view/inputview/StartInputView.java new file mode 100644 index 00000000..36b9dd90 --- /dev/null +++ b/src/main/java/bossmonster/view/inputview/StartInputView.java @@ -0,0 +1,67 @@ +package bossmonster.view.inputview; + +import bossmonster.view.InputValidator; +import bossmonster.view.InputView; + +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import static bossmonster.controller.Parameter.*; + +public class StartInputView implements InputView { + private static final String DELIM = ","; + private static final int PLAYER_PARAM_SIZE = 2; + private final Map>> inputMap = Map.ofEntries( + new AbstractMap.SimpleEntry>>(BOSS_HP.getName(), readBossHp()), + new AbstractMap.SimpleEntry>>(PLAYER_NAME.getName(), readPlayerName()), + new AbstractMap.SimpleEntry>>(PLAYER_HP.getName(), readPlayerStat()) + ); + + @Override + public void readLine(Map param, Map model) { + for (String name : param.keySet()) { + if (inputMap.containsKey(name) && param.get(name) == null) { + inputMap.get(name).accept(param); + return; + } + } + + } + + private Consumer> readBossHp() { + return param -> { + String input = InputReader.read(); + InputValidator.isNumeric(input); + param.put(BOSS_HP.getName(), input); + }; + } + + + private Consumer> readPlayerName() { + return param -> { + String input = InputReader.read(); + InputValidator.hasSpace(input); + param.put(PLAYER_NAME.getName(), input); + }; + } + + private Consumer> readPlayerStat() { + return param -> { + String input = InputReader.read(); + InputValidator.isCorrectSize(input, DELIM, PLAYER_PARAM_SIZE); + List split = splitInput(input, DELIM); + split.forEach(InputValidator::isNumeric); + param.put(PLAYER_HP.getName(), split.get(0)); + param.put(PLAYER_MP.getName(), split.get(1)); + }; + } + + private List splitInput(String input, String delim) { + return Arrays.stream(input.split(delim)) + .map(String::trim) + .toList(); + } +} diff --git a/src/main/java/bossmonster/view/outputview/BattleOutputView.java b/src/main/java/bossmonster/view/outputview/BattleOutputView.java new file mode 100644 index 00000000..e216ef96 --- /dev/null +++ b/src/main/java/bossmonster/view/outputview/BattleOutputView.java @@ -0,0 +1,76 @@ +package bossmonster.view.outputview; + +import bossmonster.domain.attack.AttackType; +import bossmonster.dto.BattleDTO; +import bossmonster.view.OutputView; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Consumer; + +import static bossmonster.controller.Parameter.*; + +public class BattleOutputView implements OutputView { + private final ErrorOutputView errorOutputView; + private final Map> textMap = Map.ofEntries + ( + Map.entry(PLAYER_ATTACK_SELECT.getName(), viewSelectAttack()), + Map.entry(BOSS_ATTACK.getName(), viewBossAttack()), + Map.entry(PLAYER_ATTACK.getName(), viewPlayerAttack()) + ); + + + public BattleOutputView(ErrorOutputView errorOutputView) { + this.errorOutputView = errorOutputView; + } + + @Override + public void show(Map param, Map model) { + if (param.containsKey(ERROR.getName())) { + viewError(param, model); + } + + textMap.forEach((name, value) -> { + if (model.containsKey(name)) { + value.accept(model.get(name)); + model.remove(name); + } + }); + } + + private void viewError(Map param, Map model) { + errorOutputView.show(param, model); + param.remove(ERROR.getName()); + } + + private Consumer viewSelectAttack() { + return param -> { + System.out.println("어떤 공격을 하시겠습니까?"); + Arrays.stream(AttackType.Player.values()).forEach(this::printAttack); + }; + } + + private void printAttack(AttackType.Player type) { + System.out.println(type.getTypeNumber() + ". " + type.getAttackName()); + } + + private Consumer viewPlayerAttack() { + return param -> + { + BattleDTO dto = (BattleDTO) param; + String name = dto.getAttackName(); + int damage = dto.getDamage(); + System.out.println(name + "을 했습니다. (입힌 데미지 : " + damage + ")"); + }; + } + + private Consumer viewBossAttack() { + return param -> + { + BattleDTO dto = (BattleDTO) param; + String name = dto.getAttackName(); + int damage = dto.getDamage(); + System.out.println(name + "가 공격 했습니다. (입힌 데미지 : " + damage + ")"); + }; + } +} diff --git a/src/main/java/bossmonster/view/outputview/BossSprite.java b/src/main/java/bossmonster/view/outputview/BossSprite.java new file mode 100644 index 00000000..83c774cc --- /dev/null +++ b/src/main/java/bossmonster/view/outputview/BossSprite.java @@ -0,0 +1,36 @@ +package bossmonster.view.outputview; + +public enum BossSprite { + BOSS_IDLE(""" + ^-^ + / 0 0 \\ + ( " ) + \\ - / + - ^ - + """), + + BOSS_WIN(""" + ^-^ + / ^ ^ \\ + ( " ) + \\ 3 / + - ^ - + """), + + BOSS_DAMAGED(""" + ^-^ + / x x \\ + ( "\\ ) + \\ ^ / + - ^ - + """); + private final String sprite; + + BossSprite(String sprite) { + this.sprite = sprite; + } + + public String getSprite() { + return sprite; + } +} diff --git a/src/main/java/bossmonster/view/outputview/ErrorOutputView.java b/src/main/java/bossmonster/view/outputview/ErrorOutputView.java new file mode 100644 index 00000000..c4920226 --- /dev/null +++ b/src/main/java/bossmonster/view/outputview/ErrorOutputView.java @@ -0,0 +1,15 @@ +package bossmonster.view.outputview; + +import bossmonster.view.OutputView; + +import java.util.Map; + +import static bossmonster.controller.Parameter.ERROR; + +public class ErrorOutputView implements OutputView { + @Override + public void show(Map param, Map model) { + System.out.println(param.get(ERROR.getName())); + param.remove(ERROR.getName()); + } +} diff --git a/src/main/java/bossmonster/view/outputview/OutputWriter.java b/src/main/java/bossmonster/view/outputview/OutputWriter.java new file mode 100644 index 00000000..e6a51eaa --- /dev/null +++ b/src/main/java/bossmonster/view/outputview/OutputWriter.java @@ -0,0 +1,43 @@ +package bossmonster.view.outputview; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; + +public final class OutputWriter { + private static BufferedWriter bw; + + private OutputWriter() { + } + + public static void write(String text) { + try { + if (bw == null) { + bw = new BufferedWriter(new OutputStreamWriter(System.out)); + } + bw.write(text); + } catch (IOException e) { + closeWriter(); + } + } + + public static void writeln(String text) { + write(text + "\n"); + } + + public static void print() { + try { + bw.flush(); + } catch (IOException e) { + closeWriter(); + } + } + + private static void closeWriter() { + try { + bw.close(); + } catch (IOException e) { + System.out.println(e.getStackTrace()); + } + } +} diff --git a/src/main/java/bossmonster/view/outputview/ProgressOutputView.java b/src/main/java/bossmonster/view/outputview/ProgressOutputView.java new file mode 100644 index 00000000..360ec281 --- /dev/null +++ b/src/main/java/bossmonster/view/outputview/ProgressOutputView.java @@ -0,0 +1,54 @@ +package bossmonster.view.outputview; + +import bossmonster.domain.creatures.Boss; +import bossmonster.domain.creatures.Player; +import bossmonster.view.OutputView; + +import java.util.Map; + +import static bossmonster.controller.Parameter.*; +import static bossmonster.view.outputview.OutputWriter.*; + +public final class ProgressOutputView implements OutputView { + + static { + writeln("보스 레이드를 시작합니다!"); + } + + @Override + public void show(Map param, Map model) { + writeln("============================"); + printBossStat((Boss) model.get(BOSS.getName())); + printBossSprite((BossSprite) model.get(BOSS_SPRITE.getName())); + printPlayerState((Player) model.get(PLAYER.getName())); + writeln("============================"); + + print(); + + } + + private void printBossStat(Boss boss) { + if (boss == null) { + throw new IllegalArgumentException("[Error] Boss 예외 발생"); + } + writeln("BOSS HP [" + boss.getHp() + "/" + boss.getTotalHp() + "]"); + } + + private void printBossSprite(BossSprite bossSprite) { + if (bossSprite == null) { + throw new IllegalArgumentException("[ERROR] BossSprite 예외 발생"); + } + writeln("____________________________"); + writeln(bossSprite.getSprite()); + writeln("____________________________"); + } + + private void printPlayerState(Player player) { + if (player == null) { + throw new IllegalArgumentException("[ERROR] player 예외 발생"); + } + write(player.getName() + " "); + write(" HP [" + player.getHp() + "/" + player.getTotalHp() + "]"); + writeln(" MP [" + player.getMp() + "/" + player.getTotalMp() + "]"); + } +} diff --git a/src/main/java/bossmonster/view/outputview/ResultOutputView.java b/src/main/java/bossmonster/view/outputview/ResultOutputView.java new file mode 100644 index 00000000..2559e645 --- /dev/null +++ b/src/main/java/bossmonster/view/outputview/ResultOutputView.java @@ -0,0 +1,58 @@ +package bossmonster.view.outputview; + +import bossmonster.domain.battle.BattleField; +import bossmonster.view.OutputView; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import static bossmonster.controller.Parameter.*; + +public class ResultOutputView implements OutputView { + + private final Map>> resultMap = Map.ofEntries( + Map.entry(BOSS_WIN.getName(), viewBossWinResult()), + Map.entry(PLAYER_WIN.getName(), viewPlayerWinResult()) + ); + + @Override + public void show(Map param, Map model) { + Map paramMap = initParam(model); + model.keySet().forEach(key -> { + if (resultMap.containsKey(key)) { + resultMap.get(key).accept(paramMap); + } + }); + } + + private Map initParam(Map model) { + Map map = new HashMap<>(); + BattleField battle = (BattleField) model.get(BATTLE.getName()); + map.put(PLAYER_NAME.getName(), battle.getPlayer().getName()); + map.put(BATTLE_COUNT.getName(), String.valueOf(battle.getBattleCount())); + return map; + } + + private Consumer> viewPlayerWinResult() { + return param -> + { + String name = param.get(PLAYER_NAME.getName()); + String count = param.get(BATTLE_COUNT.getName()); + + OutputWriter.writeln(name + "님이 " + + count + "번의 전투 끝에 보스 몬스터를 잡았습니다."); + OutputWriter.print(); + }; + } + + private Consumer> viewBossWinResult() { + return param -> + { + String name = param.get(PLAYER_NAME.getName()); + OutputWriter.writeln(name + "의 HP가 0이 되었습니다."); + OutputWriter.writeln("보스 레이드에 실패했습니다."); + OutputWriter.print(); + }; + } +} diff --git a/src/main/java/bossmonster/view/outputview/StartOutputView.java b/src/main/java/bossmonster/view/outputview/StartOutputView.java new file mode 100644 index 00000000..ff542cfa --- /dev/null +++ b/src/main/java/bossmonster/view/outputview/StartOutputView.java @@ -0,0 +1,43 @@ +package bossmonster.view.outputview; + +import bossmonster.view.OutputView; + +import java.util.LinkedHashMap; +import java.util.Map; + +import static bossmonster.controller.Parameter.*; + +public class StartOutputView implements OutputView { + private static final Map textMap = new LinkedHashMap<>(); + private final ErrorOutputView errorOutputView; + + public StartOutputView(ErrorOutputView errorOutputView) { + this.errorOutputView = errorOutputView; + initTextConfig(); + } + + private void initTextConfig() { + textMap.put(BOSS_HP.getName(), + () -> System.out.println("보스 몬스터의 HP를 입력해주세요.")); + + textMap.put(PLAYER_NAME.getName(), + () -> System.out.println("플레이어의 이름을 입력해주세요")); + + textMap.put(PLAYER_HP.getName(), + () -> System.out.println("플레이어의 HP와 MP를 입력해주세요.(,로 구분)")); + } + + @Override + public void show(Map param, Map model) { + if (param.containsKey(ERROR.getName())) { + errorOutputView.show(param, model); + } + for (String name : param.keySet()) { + if (textMap.containsKey(name) && param.get(name) == null) { + textMap.get(name).run(); + return; + } + } + } + +} diff --git a/src/test/java/bossmonster/TypeQualifierTest.java b/src/test/java/bossmonster/TypeQualifierTest.java new file mode 100644 index 00000000..f2f4162c --- /dev/null +++ b/src/test/java/bossmonster/TypeQualifierTest.java @@ -0,0 +1,54 @@ +package bossmonster; + +import bossmonster.domain.creatures.Boss; +import bossmonster.domain.creatures.Player; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class TypeQualifierTest { + @Test + @DisplayName("Boss를 Boss class로 검사하면 True로 반환") + void 보스_테스트_정상() { + //given + Boss boss = new Boss(100, 0); + //when + boolean result = TypeQualifier.checkCreatureType(Boss.class, boss); + //then + assertThat(result).isEqualTo(true); + } + + @Test + @DisplayName("Boss를 Player class로 검사하면 False 반환") + void 보스_테스트_실패_반환() { + //given + Boss boss = new Boss(100, 0); + //when + boolean result = TypeQualifier.checkCreatureType(Player.class, boss); + //then + assertThat(result).isEqualTo(false); + } + + @Test + @DisplayName("player를 Player class로 검사하면 true 반환") + void 플레이어_테스트_성공() { + //given + Player player = new Player(100, 100, "test"); + //when + boolean result = TypeQualifier.checkCreatureType(Player.class, player); + //then + assertThat(result).isEqualTo(true); + } + + @Test + @DisplayName("player를 boss class로 검사하면 False 반환") + void 플레이어_테스트_실패_반환() { + //given + Player player = new Player(100, 100, "test"); + //when + boolean result = TypeQualifier.checkCreatureType(Boss.class, player); + //then + assertThat(result).isEqualTo(false); + } +} \ No newline at end of file diff --git a/src/test/java/bossmonster/controller/CreatureControllerTest.java b/src/test/java/bossmonster/controller/CreatureControllerTest.java new file mode 100644 index 00000000..68d53ac9 --- /dev/null +++ b/src/test/java/bossmonster/controller/CreatureControllerTest.java @@ -0,0 +1,31 @@ +package bossmonster.controller; + +import bossmonster.service.CreatureService; +import bossmonster.view.inputview.StartInputView; +import bossmonster.view.outputview.ErrorOutputView; +import bossmonster.view.outputview.StartOutputView; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +class CreatureControllerTest { + private final CreatureController creatureController = + new CreatureController( + new CreatureService(), + new StartInputView(), + new StartOutputView(new ErrorOutputView())); + Map param = new HashMap<>(); + Map model = new HashMap<>(); + + @Test + @DisplayName("") + void CreatureControllerTest() { + Map param = new HashMap<>(); + //given + //when + //then + } + +} \ No newline at end of file diff --git a/src/test/java/bossmonster/domain/RandomNumberGeneratorTest.java b/src/test/java/bossmonster/domain/RandomNumberGeneratorTest.java new file mode 100644 index 00000000..e780a79c --- /dev/null +++ b/src/test/java/bossmonster/domain/RandomNumberGeneratorTest.java @@ -0,0 +1,24 @@ +package bossmonster.domain; + +import bossmonster.domain.attack.AttackRandomNumberGenerator; +import bossmonster.domain.attack.RandomNumberGenerator; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; + +class RandomNumberGeneratorTest { + + private final RandomNumberGenerator randomNumberGenerator = new AttackRandomNumberGenerator(); + + @Test + @DisplayName("0 ~ 20 사이로 나오도록 설정한 올바르게 나오면 성공한다") + @RepeatedTest(20) + void 랜덤_데미지_성공_테스트() { + //given + int number = randomNumberGenerator.generate(0, 20); + //when, then + Assertions.assertThat(number).isBetween(0, 20); + } + +} \ No newline at end of file diff --git a/src/test/java/bossmonster/domain/creatures/BossTest.java b/src/test/java/bossmonster/domain/creatures/BossTest.java new file mode 100644 index 00000000..1d32ab71 --- /dev/null +++ b/src/test/java/bossmonster/domain/creatures/BossTest.java @@ -0,0 +1,29 @@ +package bossmonster.domain.creatures; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +class BossTest { + @Test + @DisplayName("보스 hp가 100 미만이면 예외가 발생한다.") + void 보스_hp_100_미만() { + assertThrows(IllegalArgumentException.class, () -> new Boss(99)); + } + + @Test + @DisplayName("보스 hp가 300 초과면 예외가 발생한다.") + void 보스_hp_300_초과() { + assertThrows(IllegalArgumentException.class, () -> new Boss(301)); + } + + @ParameterizedTest + @CsvSource(value = {"100", "200", "300"}) + @DisplayName("보스 체력이 100 ~ 300 사이라면 통과한다.") + void BossTest(int input) { + new Boss(input); + } +} \ No newline at end of file diff --git a/src/test/java/bossmonster/domain/creatures/PlayerTest.java b/src/test/java/bossmonster/domain/creatures/PlayerTest.java new file mode 100644 index 00000000..8d78d4a8 --- /dev/null +++ b/src/test/java/bossmonster/domain/creatures/PlayerTest.java @@ -0,0 +1,65 @@ +package bossmonster.domain.creatures; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class PlayerTest { + @Test + @DisplayName("hp와 mp의 합이 200을 초과하면 예외가 발생한다.") + void 스탯합_200_초과() { + assertThrows(IllegalArgumentException.class, + () -> new Player(100, 200, "TEST1")); + } + + @Test + @DisplayName("hp와 mp이 200합이 미만이면 예외가 발생한다.") + void 스탯합_200_미만() { + assertThrows(IllegalArgumentException.class, + () -> new Player(100, 99, "TEST2")); + } + + @Test + @DisplayName("예외가 발생하지 않으면 통과한다.") + void 스탯합_200_정상() { + new Player(60, 140, "TES"); + } + + + @Test + @DisplayName("5글자를 입력하면 통과한다.") + void 다섯글자_이름_통과() { + new Player(60, 140, "5글자이름"); + } + + @Test + @DisplayName("6글자를 입력하면 예외가 발생한다.") + void 다섯글자_이름_초과() { + assertThrows(IllegalArgumentException.class, + () -> new Player(60, 140, "여섯글자이름")); + } + + @Test + @DisplayName("최대 MP 100을 초과해서 회복되지 않는다.") + void Mp_초과시_그대로() { + //given + Player player = new Player(100, 100, "test1"); + //when + //player.addMpAs(10); + //then + assertThat(player.getMp()).isEqualTo(100); + } + + @Test + @DisplayName("최대 MP 100을 초과해서 회복되지 않는다.") + void Mp_정상적으로_감소() { + //given + Player player = new Player(100, 100, "test1"); + //when + player.decreaseMpAs(-10); + //then + assertThat(player.getMp()).isEqualTo(100); + } +} \ No newline at end of file diff --git a/src/test/java/bossmonster/view/outputview/ProgressOutputViewTest.java b/src/test/java/bossmonster/view/outputview/ProgressOutputViewTest.java new file mode 100644 index 00000000..bf0e87bd --- /dev/null +++ b/src/test/java/bossmonster/view/outputview/ProgressOutputViewTest.java @@ -0,0 +1,65 @@ +package bossmonster.view.outputview; + +import bossmonster.domain.creatures.Boss; +import bossmonster.domain.creatures.Player; +import bossmonster.view.OutputView; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +import java.util.HashMap; +import java.util.Map; + +class ProgressOutputViewTest { + private OutputView outputView = new ProgressOutputView(); + private Map model = new HashMap<>(); + + @BeforeEach + void create() { + Player player = new Player(100, 100, "test"); + Boss boss = new Boss(100, 0); + model.put("player", player); + model.put("boss", boss); + } + + @AfterEach + void clear() { + model.clear(); + } + +// @Test +// @DisplayName("BOSS_DAMAGED") +// void BOSS_ATTACKED() { +// //given +// create(); +// //when +// BossSprite sprite = BossSprite.BOSS_DAMAGED; +// model.put("bossSprite", sprite); +// outputView.show(model); +// //then +// } + +// @Test +// @DisplayName("BOSS_WIN") +// void BOSS_WIN() { +// //given +// create(); +// //when +// BossSprite sprite = BossSprite.BOSS_WIN; +// model.put("bossSprite", sprite); +// outputView.show(model); +// //then +// } +// +// @Test +// @DisplayName("BOSS_IDLE") +// void BOSS_IDLE() { +// //given +// create(); +// //when +// BossSprite sprite = BossSprite.BOSS_IDLE; +// model.put("bossSprite", sprite); +// outputView.show(model); +// //then +// } + +} \ No newline at end of file