diff --git a/README.md b/README.md index b853592f59..49f14c5705 100644 --- a/README.md +++ b/README.md @@ -5,5 +5,17 @@ * 코드 리뷰 피드백에 대한 개선 작업을 하고 다시 PUSH한다. * 모든 피드백을 완료하면 다음 단계를 도전하고 앞의 과정을 반복한다. +## 기능 목록 +- [x] 쉼표로 구분된 참여할 사람을 입력 받는다. (단, 이름은 최대 5자까지 부여) +- [x] 최대 사다리 높이 개수를 입력 받는다. +- [x] 입력 받은 사람과 사다리 높이를 이용해 사다리를 출력한다. + +## 기능 요구 사항 +- 사다리 게임에 참여하는 사람에 이름을 최대5글자까지 부여할 수 있다. 사다리를 출력할 때 사람 이름도 같이 출력한다. +- 사람 이름은 쉼표(,)를 기준으로 구분한다. +- 사람 이름을 5자 기준으로 출력하기 때문에 사다리 폭도 넓어져야 한다. +- 사다리 타기가 정상적으로 동작하려면 라인이 겹치지 않도록 해야 한다. + - |-----|-----| 모양과 같이 가로 라인이 겹치는 경우 어느 방향으로 이동할지 결정할 수 없다. + ## 온라인 코드 리뷰 과정 -* [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/nextstep-step/nextstep-docs/tree/master/codereview) \ No newline at end of file +* [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/nextstep-step/nextstep-docs/tree/master/codereview) diff --git a/src/main/java/ladderapplication/LadderApplication.java b/src/main/java/ladderapplication/LadderApplication.java new file mode 100644 index 0000000000..43fae74ab6 --- /dev/null +++ b/src/main/java/ladderapplication/LadderApplication.java @@ -0,0 +1,16 @@ +package ladderapplication; + +import ladderapplication.models.Ladder; +import ladderapplication.models.requests.GameSettingRequest; +import ladderapplication.ui.Printer; + +public class LadderApplication { + + public static void main(String[] args) { + GameSettingRequest gameSettingRequest = Printer.requestGameSetting(); + + Ladder ladder = Ladder.from(gameSettingRequest); + ladder.print(); + } + +} diff --git a/src/main/java/ladderapplication/models/Ladder.java b/src/main/java/ladderapplication/models/Ladder.java new file mode 100644 index 0000000000..8825004bb6 --- /dev/null +++ b/src/main/java/ladderapplication/models/Ladder.java @@ -0,0 +1,57 @@ +package ladderapplication.models; + +import ladderapplication.models.requests.GameSettingRequest; +import ladderapplication.utils.DecoratingUtils; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class Ladder { + + private final List<Player> players; + private final List<Line> lines; + private final String drawing; + + private Ladder(List<Player> players, List<Line> lines, String drawing) { + this.players = players; + this.lines = lines; + this.drawing = drawing; + } + + public static Ladder from(GameSettingRequest gameSettingRequest) { + List<Player> newPlayers = gameSettingRequest.getPlayerRequests() + .stream() + .map(Player::from) + .collect(Collectors.toList()); + + int playerCount = gameSettingRequest.getPlayerRequests().size(); + int ladderHeight = gameSettingRequest.getLadderRequest().getHeight(); + List<Line> newLines = Stream.generate(() -> Line.of(playerCount)) + .limit(ladderHeight) + .collect(Collectors.toList()); + + String drawing = createDrawing(newPlayers, newLines); + return new Ladder(newPlayers, newLines, drawing); + } + + private static String createDrawing(List<Player> players, List<Line> lines) { + StringBuilder sb = new StringBuilder(); + addPlayerNames(sb, players); + addLines(sb, lines); + return sb.toString(); + } + + private static void addPlayerNames(StringBuilder sb, List<Player> players) { + players.forEach(player -> sb.append(DecoratingUtils.getDecoratedName(player.getName()))); + sb.append("\n"); + } + + private static void addLines(StringBuilder sb, List<Line> lines) { + lines.forEach(line -> sb.append(line.print()).append("\n")); + } + + public void print() { + System.out.println(this.drawing); + } +} diff --git a/src/main/java/ladderapplication/models/Line.java b/src/main/java/ladderapplication/models/Line.java new file mode 100644 index 0000000000..5ceb6f4fa5 --- /dev/null +++ b/src/main/java/ladderapplication/models/Line.java @@ -0,0 +1,57 @@ +package ladderapplication.models; + +import ladderapplication.utils.DecoratingUtils; + +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class Line { + private static final Random RANDOM_GENERATOR = new Random(); + private static final int HAS_STEP_BOUND = 4; + private static final int RANDOM_NUMBER_BOUND = 10; + private final List<Boolean> hasSteps; + + private Line(List<Boolean> hasSteps) { + this.hasSteps = hasSteps; + } + + public static Line of(int playerCount) { + List<Boolean> hasSteps = Stream.generate(() -> Boolean.FALSE) + .limit(playerCount - 1) + .collect(Collectors.toList()); + + IntStream.range(0, hasSteps.size()) + .forEach(index -> setHasStep(hasSteps, index)); + + return new Line(hasSteps); + } + + private static void setHasStep(List<Boolean> hasSteps, int index) { + if (prevLadderHasNotStep(hasSteps, index) && hasStep()) { + hasSteps.set(index, Boolean.TRUE); + } + } + + private static boolean prevLadderHasNotStep(List<Boolean> hasSteps, int index) { + return index == 0 || !hasSteps.get(index - 1); + } + + private static boolean hasStep() { + return RANDOM_GENERATOR.nextInt(RANDOM_NUMBER_BOUND) >= HAS_STEP_BOUND; + } + + public String print() { + StringBuilder sb = new StringBuilder(); + sb.append(" "); + hasSteps.forEach(hasStep -> { + sb.append("|") + .append(DecoratingUtils.getStep(hasStep)); + }); + + sb.append("|"); + return sb.toString(); + } +} diff --git a/src/main/java/ladderapplication/models/Player.java b/src/main/java/ladderapplication/models/Player.java new file mode 100644 index 0000000000..7f6063033b --- /dev/null +++ b/src/main/java/ladderapplication/models/Player.java @@ -0,0 +1,20 @@ +package ladderapplication.models; + +import ladderapplication.models.requests.PlayerRequest; + +public class Player { + + private final String name; + + private Player(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static Player from(PlayerRequest playerRequest) { + return new Player(playerRequest.getName()); + } +} diff --git a/src/main/java/ladderapplication/models/requests/GameSettingRequest.java b/src/main/java/ladderapplication/models/requests/GameSettingRequest.java new file mode 100644 index 0000000000..7b6c589132 --- /dev/null +++ b/src/main/java/ladderapplication/models/requests/GameSettingRequest.java @@ -0,0 +1,26 @@ +package ladderapplication.models.requests; + +import java.util.List; + +public class GameSettingRequest { + + private final LadderRequest ladderRequest; + private final List<PlayerRequest> playerRequests; + + private GameSettingRequest(LadderRequest ladderRequest, List<PlayerRequest> playerRequests) { + this.ladderRequest = ladderRequest; + this.playerRequests = playerRequests; + } + + public LadderRequest getLadderRequest() { + return ladderRequest; + } + + public List<PlayerRequest> getPlayerRequests() { + return playerRequests; + } + + public static GameSettingRequest of(LadderRequest ladderRequest, List<PlayerRequest> playerRequests) { + return new GameSettingRequest(ladderRequest, playerRequests); + } +} diff --git a/src/main/java/ladderapplication/models/requests/LadderRequest.java b/src/main/java/ladderapplication/models/requests/LadderRequest.java new file mode 100644 index 0000000000..b42be4f1c1 --- /dev/null +++ b/src/main/java/ladderapplication/models/requests/LadderRequest.java @@ -0,0 +1,19 @@ +package ladderapplication.models.requests; + +public class LadderRequest { + + private final int height; + + private LadderRequest(int height) { + this.height = height; + } + + public int getHeight() { + return height; + } + + public static LadderRequest of(int height) { + return new LadderRequest(height); + } + +} diff --git a/src/main/java/ladderapplication/models/requests/PlayerRequest.java b/src/main/java/ladderapplication/models/requests/PlayerRequest.java new file mode 100644 index 0000000000..a858183f5a --- /dev/null +++ b/src/main/java/ladderapplication/models/requests/PlayerRequest.java @@ -0,0 +1,18 @@ +package ladderapplication.models.requests; + +public class PlayerRequest { + + private final String name; + + private PlayerRequest(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static PlayerRequest of(String name) { + return new PlayerRequest(name); + } +} diff --git a/src/main/java/ladderapplication/ui/InputScanner.java b/src/main/java/ladderapplication/ui/InputScanner.java new file mode 100644 index 0000000000..f1cc554b2a --- /dev/null +++ b/src/main/java/ladderapplication/ui/InputScanner.java @@ -0,0 +1,17 @@ +package ladderapplication.ui; + +import java.util.Scanner; + +public class InputScanner { + + private static final Scanner SCANNER = new Scanner(System.in); + + public static String stringScan() { + return SCANNER.nextLine(); + } + + public static int intScan() { + return SCANNER.nextInt(); + } + +} diff --git a/src/main/java/ladderapplication/ui/Printer.java b/src/main/java/ladderapplication/ui/Printer.java new file mode 100644 index 0000000000..4b09d0f0f5 --- /dev/null +++ b/src/main/java/ladderapplication/ui/Printer.java @@ -0,0 +1,25 @@ +package ladderapplication.ui; + +import ladderapplication.models.requests.GameSettingRequest; +import ladderapplication.models.requests.LadderRequest; +import ladderapplication.models.requests.PlayerRequest; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class Printer { + + public static GameSettingRequest requestGameSetting() { + System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"); + String players = InputScanner.stringScan(); + String[] playerArr = players.split(","); + List<PlayerRequest> playerRequests = Stream.of(playerArr).map(PlayerRequest::of).collect(Collectors.toList()); + + System.out.println("최대 사다리 높이는 몇 개인가요?"); + int height = InputScanner.intScan(); + LadderRequest ladderRequest = LadderRequest.of(height); + + return GameSettingRequest.of(ladderRequest, playerRequests); + } +} diff --git a/src/main/java/ladderapplication/utils/DecoratingUtils.java b/src/main/java/ladderapplication/utils/DecoratingUtils.java new file mode 100644 index 0000000000..850bd9e4bf --- /dev/null +++ b/src/main/java/ladderapplication/utils/DecoratingUtils.java @@ -0,0 +1,32 @@ +package ladderapplication.utils; + +public class DecoratingUtils { + + private static final String EMPTY = " "; + private static final String STEP_PIECE = "-"; + private static final int NAME_SPACE = 6; + private static final int STANDARD_SIZE = 4; + private static final int STEP_LENGTH = 5; + + public static String getDecoratedName(String name) { + StringBuilder sb = new StringBuilder(); + if (name.length() < STANDARD_SIZE) { + sb.append(getRepeatCharacter(EMPTY, STANDARD_SIZE - name.length())); + } + sb.append(name) + .append(getRepeatCharacter(EMPTY, NAME_SPACE - sb.length())); + return sb.toString(); + } + + private static String getRepeatCharacter(String character, int repeatCount) { + return character.repeat(repeatCount); + } + + public static String getStep(boolean hasStep) { + if (hasStep) { + return getRepeatCharacter(STEP_PIECE, STEP_LENGTH); + } + + return getRepeatCharacter(EMPTY, STEP_LENGTH); + } +} diff --git a/src/main/java/ladderapplication/validator/PlayerRequestValidator.java b/src/main/java/ladderapplication/validator/PlayerRequestValidator.java new file mode 100644 index 0000000000..fbdf59f872 --- /dev/null +++ b/src/main/java/ladderapplication/validator/PlayerRequestValidator.java @@ -0,0 +1,15 @@ +package ladderapplication.validator; + +import ladderapplication.models.requests.PlayerRequest; + +public class PlayerRequestValidator { + + private static final String WRONG_NAME = "플레이어 이름음 5글자 이하 여야 합니다."; + + public static void validation(PlayerRequest playerRequest) { + if (playerRequest.getName().length() > 5) { + throw new IllegalArgumentException(WRONG_NAME); + } + } + +} diff --git a/src/test/java/ladderapplication/validator/PlayerRequestValidatorTest.java b/src/test/java/ladderapplication/validator/PlayerRequestValidatorTest.java new file mode 100644 index 0000000000..398605b74d --- /dev/null +++ b/src/test/java/ladderapplication/validator/PlayerRequestValidatorTest.java @@ -0,0 +1,35 @@ +package ladderapplication.validator; + +import ladderapplication.models.requests.PlayerRequest; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +class PlayerRequestValidatorTest { + + private static final String WRONG_NAME = "플레이어 이름음 5글자 이하 여야 합니다."; + private static final List<PlayerRequest> playerRequests = new ArrayList<>(); + + @BeforeAll + public static void setPlayerRequest() { + String players = "pobi,honux,crong,jk"; + String[] playerArr = players.split(","); + Stream.of(playerArr).forEach(name -> playerRequests.add(PlayerRequest.of(name))); + } + + @Test + @DisplayName("입력받은 사용자 이름이 5글자가 넘으면 Exception이 발생한다.") + void test1() { + PlayerRequest playerRequest = PlayerRequest.of("robinson"); + Exception exception = assertThrows(IllegalArgumentException.class, () -> PlayerRequestValidator.validation(playerRequest)); + assertThat(exception.getMessage()).isEqualTo(WRONG_NAME); + + } +}