diff --git a/src/main/java/nextstep/ladder/LadderGame.java b/src/main/java/nextstep/ladder/LadderGame.java index d54539c39f..0d513ab708 100644 --- a/src/main/java/nextstep/ladder/LadderGame.java +++ b/src/main/java/nextstep/ladder/LadderGame.java @@ -1,21 +1,17 @@ package nextstep.ladder; -import nextstep.ladder.domain.Ladder; -import nextstep.ladder.domain.Player; +import nextstep.ladder.domain.*; import nextstep.ladder.view.InputView; import nextstep.ladder.view.ResultView; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; public class LadderGame { - private static final String NAME_REX_PATTERN = ","; - - private final List players = new ArrayList<>(); - - private Ladder ladder; + private static final String ALL_PLAYER = "all"; public static void main(String[] args) { LadderGame game = new LadderGame(); @@ -24,30 +20,73 @@ public static void main(String[] args) { public void run() { - inputPlayers(); + Map players = addPlayers(InputView.inputPlayers()); + Map results = addResults(InputView.inputResult()); + + Ladder ladder = new Ladder(InputView.inputLadderHeight(), players.size()); + + ResultView.printLadderResult(players, ladder.getLines(), results); + + players = getPlayerResult(players, results, ladder); + + + printPlayerResult(players); - saveLadder(new Ladder(inputLadderHeight(), this.players.size())); + } - ResultView.printResult(this.players, ladder.getLines()); + private Map addPlayers(String inputPlayers) { + AtomicInteger index = new AtomicInteger(0); + return Arrays.stream(inputPlayers.split(",")) + .map(name -> new Player(new PlayerName(name), index.getAndIncrement())) + .collect(Collectors.toMap(Player::getName, player -> player, (x, y) -> y, LinkedHashMap::new)); + } + private Map addResults(String inputResults) { + AtomicLong index = new AtomicLong(0); + return Arrays.stream(inputResults.split(",")) + .map(value -> new Result(new ResultId(index.getAndIncrement()), value)) + .collect(Collectors.toMap(Result::getId, result -> result)); } - private void inputPlayers() { - addPlayers(InputView.inputPlayers()); + private Map getPlayerResult(Map players, Map results, Ladder ladder) { + Map newResults = new LinkedHashMap<>(); + for (PlayerName playerName : players.keySet()) { + Player player = players.get(playerName); + player.saveResult(results.get(calculateResultId(players.get(player.getName()), ladder))); + newResults.put(playerName, player); + } + return newResults; } - private void addPlayers(String players) { - Arrays.stream(players.split(NAME_REX_PATTERN)) - .map(Player::new) - .forEach(this.players::add); + private ResultId calculateResultId(Player player, Ladder ladder) { + int playerPosition = player.getStartPosition(); + for (Line line : ladder.getLines()) { + playerPosition = calculatePosition(line.getPoints(), playerPosition); + } + return new ResultId((long) playerPosition); } - private int inputLadderHeight() { - return InputView.inputLadderHeight(); + private int calculatePosition(List points, int playerPosition) { + if (points.get(playerPosition).getStatus().hasRight()) { + return playerPosition + 1; + } + if (points.get(playerPosition).getStatus().hasLeft()) { + return playerPosition - 1; + } + return playerPosition; } - private void saveLadder(Ladder ladder) { - this.ladder = ladder; + private void printPlayerResult(Map players) { + + String inputPlayer = ""; + + while (!ALL_PLAYER.equals(inputPlayer)) { + + inputPlayer = InputView.inputPlayer(); + + ResultView.printPlayerResult(inputPlayer, players); + + } } } diff --git a/src/main/java/nextstep/ladder/README.md b/src/main/java/nextstep/ladder/README.md index 3abdd4cffe..709876c688 100644 --- a/src/main/java/nextstep/ladder/README.md +++ b/src/main/java/nextstep/ladder/README.md @@ -1,12 +1,28 @@ -LadderGame -플레이어 추가 -사다이 추가 -라인 추가 +# 3단계 - 사다리(게임 실행) + +## 기능 요구사항 +- 사다리 실행 결과를 출력해야 한다. +- 개인별 이름을 입력하면 개인별 결과를 출력하고, "all"을 입력하면 전체 참여자의 실행 결과를 출력한다. + +## 프로그래밍 요구사항 +- 자바 8의 스트림과 람다를 적용해 프로그래밍한다. +- 규칙 6: 모든 엔티티를 작게 유지한다. +- 규칙 7: 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다. + +--- + +### LadderGame +-[X] 플레이어 추가 +-[X] 사다리 저장 +-[X] 플레이어 결과 구하기 InputView -참여할 사람 이름 입력 -최대 사다리 높이 입력 +-[X] 참여할 사람 이름 입력 +-[X] 최대 사다리 높이 입력 +-[X] 실행 결과 입력 +-[X] 결과를 보고 싶은 사람 입력 ResultView -플레이어 이름 출력 -사다리 출력 +-[X] 플레이어 이름 출력 +-[X] 사다리 출력 +-[X] 실행 결과 출력 diff --git a/src/main/java/nextstep/ladder/domain/Ladder.java b/src/main/java/nextstep/ladder/domain/Ladder.java index 3327d3aa1f..ed7183e680 100644 --- a/src/main/java/nextstep/ladder/domain/Ladder.java +++ b/src/main/java/nextstep/ladder/domain/Ladder.java @@ -1,30 +1,34 @@ package nextstep.ladder.domain; -import nextstep.ladder.util.RandomUtil; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.stream.IntStream; public class Ladder { + private static final String HEIGHT_ERROR_MESSAGE = "높이는 0보다 커야 합니다."; + private List lines; public Ladder(int height, int width) { - addLine(height, width); - } - - private void addLine(int height, int width) { - List lines = new ArrayList<>(); - IntStream.range(0, height) - .mapToObj(i -> new Line(width)) - .forEach(lines::add); - this.lines = lines; + if (1 > height) { + throw new IllegalArgumentException(HEIGHT_ERROR_MESSAGE); + } + this.lines = createLines(height, width); } public List getLines() { return Collections.unmodifiableList(this.lines); } + private List createLines(int height, int width) { + List lines = new ArrayList<>(); + for (int i = 0; i < height; i++) { + lines.add(new Line(width)); + } + return lines; + } + + } diff --git a/src/main/java/nextstep/ladder/domain/Line.java b/src/main/java/nextstep/ladder/domain/Line.java index e753896511..7b7db27083 100644 --- a/src/main/java/nextstep/ladder/domain/Line.java +++ b/src/main/java/nextstep/ladder/domain/Line.java @@ -3,30 +3,43 @@ import nextstep.ladder.util.RandomUtil; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.stream.IntStream; public class Line { - private final List points; + private List points; - public Line(int countOfPerson) { - List points = new ArrayList<>(); - IntStream.range(0, countOfPerson - 1) - .forEach(index -> addPoint(points, index)); - this.points = points; + public Line(int pointSize) { + this.points = createPoints(pointSize); } - private void addPoint(List points, int index) { - if (index == 0 || !points.get(index - 1)) { - points.add(RandomUtil.generator()); - return; - } - points.add(false); + public List getPoints() { + return Collections.unmodifiableList(points); } - public List getPoints() { + private List createPoints(int pointSize) { + List points = new ArrayList<>(); + for (int index = 0; index < pointSize; index++) { + addPoint(points, index, pointSize); + } return points; } + private void addPoint(List points, int index, int pointSize) { + int totalSizeBound = PointStatus.TOTAL_INDEX_SIZE; + int twoResultSizeBound = PointStatus.TWO_RESULT_SIZE; + if (index == 0) { + points.add(Point.createFirst(() -> RandomUtil.generator(twoResultSizeBound))); + return; + } + if (pointSize - 1 == index) { + points.add(Point.createLast(() -> RandomUtil.generator(twoResultSizeBound), points.get(index - 1))); + return; + } + Point point = Point.create(() -> RandomUtil.generator(totalSizeBound), points.get(index - 1)); + // LEFT 일때 points list 값 바꾸기 + points.add(point); + } + } diff --git a/src/main/java/nextstep/ladder/domain/Player.java b/src/main/java/nextstep/ladder/domain/Player.java index 3698c0bbcb..432a89ce7e 100644 --- a/src/main/java/nextstep/ladder/domain/Player.java +++ b/src/main/java/nextstep/ladder/domain/Player.java @@ -1,20 +1,48 @@ package nextstep.ladder.domain; +import java.util.Objects; + public class Player { + private final PlayerName name; + + private Result result; - private static final String NAME_OVER_LENGTH_ERROR_TEXT = "사람에 이름을 최대5글자까지 가능합니다."; - private static final int NAME_MAX_LENGTH = 5; - private final String name; + private final int startPosition; - public Player(String name) { - if (name.length() > NAME_MAX_LENGTH) { - throw new IllegalArgumentException(NAME_OVER_LENGTH_ERROR_TEXT); - } + public Player(PlayerName name, int startPosition) { this.name = name; + this.startPosition = startPosition; } - public String getName() { + public PlayerName getName() { return name; } + public int getStartPosition() { + return startPosition; + } + + public void saveResult(Result result) { + this.result = result; + } + + public Result getResult() { + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Player player = (Player) o; + + return Objects.equals(name, player.name); + } + + @Override + public int hashCode() { + return name != null ? name.hashCode() : 0; + } + } diff --git a/src/main/java/nextstep/ladder/domain/PlayerName.java b/src/main/java/nextstep/ladder/domain/PlayerName.java new file mode 100644 index 0000000000..949a61242d --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/PlayerName.java @@ -0,0 +1,38 @@ +package nextstep.ladder.domain; + +import java.util.Objects; + +public class PlayerName { + + private static final String NAME_OVER_LENGTH_ERROR_TEXT = "사람에 이름을 최대5글자까지 가능합니다."; + private static final int NAME_MAX_LENGTH = 5; + + private final String value; + + public PlayerName(String value) { + if (value.length() > NAME_MAX_LENGTH) { + throw new IllegalArgumentException(NAME_OVER_LENGTH_ERROR_TEXT); + } + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PlayerName that = (PlayerName) o; + + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return value != null ? value.hashCode() : 0; + } + +} diff --git a/src/main/java/nextstep/ladder/domain/Point.java b/src/main/java/nextstep/ladder/domain/Point.java new file mode 100644 index 0000000000..436803bd21 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Point.java @@ -0,0 +1,59 @@ +package nextstep.ladder.domain; + + +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +public class Point { + + private PointStatus status; + private final PointStatus beforeStatus; + + public Point(PointStatus status) { + this.status = status; + this.beforeStatus = PointStatus.NONE; + } + + private Point(PointStatus status, PointStatus beforeStatus) { + this.status = status; + this.beforeStatus = beforeStatus; + } + + public PointStatus getStatus() { + return status; + } + + public void saveStatus(PointStatus status) { + this.status = status; + } + + public static Point create(Supplier randomNumber, Point beforeStatus) { + if (beforeStatus.getStatus() == PointStatus.RIGHT) { + return new Point(PointStatus.LEFT, beforeStatus.getStatus()); + } + if (beforeStatus.getStatus() == PointStatus.LEFT) { + return new Point(PointStatus.NONE, beforeStatus.getStatus()); + } + PointStatus status = PointStatus.getRandomStatus(randomNumber.get()); + if (PointStatus.LEFT == status) { + beforeStatus.saveStatus(PointStatus.RIGHT); + } + return new Point(status, beforeStatus.getStatus()); + } + + public static Point createFirst(Supplier randomNumber) { + return new Point(PointStatus.getRandomStatusExceptLeft(randomNumber.get())); + } + + public static Point createLast(Supplier randomNumber, Point beforeStatus) { + if (beforeStatus.getStatus() == PointStatus.RIGHT) { + return new Point(PointStatus.LEFT, beforeStatus.getStatus()); + } + if (beforeStatus.getStatus() == PointStatus.LEFT) { + return new Point(PointStatus.NONE, beforeStatus.getStatus()); + } + return new Point(PointStatus.getRandomStatusExceptRight(randomNumber.get()), beforeStatus.getStatus()); + } + +} diff --git a/src/main/java/nextstep/ladder/domain/PointStatus.java b/src/main/java/nextstep/ladder/domain/PointStatus.java new file mode 100644 index 0000000000..d6e819258b --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/PointStatus.java @@ -0,0 +1,46 @@ +package nextstep.ladder.domain; + +import java.util.function.Supplier; + +public enum PointStatus { + + LEFT, + RIGHT, + NONE, + + ; + + public static final int TOTAL_INDEX_SIZE = values().length; + public static final int TWO_RESULT_SIZE = 2; + + public boolean hasLeft() { + return LEFT == this; + } + + public boolean hasRight() { + return RIGHT == this; + } + + public boolean hasNone() { + return NONE == this; + } + + public static PointStatus getRandomStatus(int number) { + return values()[number]; + } + + public static PointStatus getRandomStatusExceptRight(int number) { + if (number == 0) { + return NONE; + } + return LEFT; + } + + public static PointStatus getRandomStatusExceptLeft(int number) { + if (number == 0) { + return NONE; + } + return RIGHT; + } + +} diff --git a/src/main/java/nextstep/ladder/domain/Result.java b/src/main/java/nextstep/ladder/domain/Result.java new file mode 100644 index 0000000000..4ce4049df3 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Result.java @@ -0,0 +1,22 @@ +package nextstep.ladder.domain; + +public class Result { + + private final ResultId id; + + private String value; + + public Result(ResultId id, String value) { + this.id = id; + this.value = value; + } + + public ResultId getId() { + return id; + } + + public String getValue() { + return value; + } + +} diff --git a/src/main/java/nextstep/ladder/domain/ResultId.java b/src/main/java/nextstep/ladder/domain/ResultId.java new file mode 100644 index 0000000000..ad18176f16 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/ResultId.java @@ -0,0 +1,28 @@ +package nextstep.ladder.domain; + +import java.util.Objects; + +public class ResultId { + + private Long id; + + public ResultId(Long id) { + this.id = id; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ResultId resultId = (ResultId) o; + + return Objects.equals(id, resultId.id); + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } + +} diff --git a/src/main/java/nextstep/ladder/util/RandomUtil.java b/src/main/java/nextstep/ladder/util/RandomUtil.java index ffd07246a3..14f407facf 100644 --- a/src/main/java/nextstep/ladder/util/RandomUtil.java +++ b/src/main/java/nextstep/ladder/util/RandomUtil.java @@ -5,8 +5,9 @@ public class RandomUtil { private static final Random random = new Random(); - public static boolean generator() { - return random.nextBoolean(); + + public static int generator(int number) { + return random.nextInt(number); } } diff --git a/src/main/java/nextstep/ladder/view/InputView.java b/src/main/java/nextstep/ladder/view/InputView.java index af7bfb5c0b..970ac1d589 100644 --- a/src/main/java/nextstep/ladder/view/InputView.java +++ b/src/main/java/nextstep/ladder/view/InputView.java @@ -7,6 +7,8 @@ public class InputView { private static final Scanner scanner = new Scanner(System.in); private static final String INPUT_PLAYERS_NAME_TEXT = "참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"; private static final String INPUT_LADDER_HEIGHT_TEXT = "최대 사다리 높이는 몇 개인가요?"; + private static final String INPUT_RESULT_TEXT = "실행 결과를 입력하세요. (결과는 쉼표(,)로 구분하세요)"; + private static final String INPUT_PLAYER_TEXT = "결과를 보고 싶은 사람은?"; public static String inputPlayers() { System.out.println(INPUT_PLAYERS_NAME_TEXT); return scanner.nextLine(); @@ -17,4 +19,13 @@ public static int inputLadderHeight() { return Integer.parseInt(scanner.nextLine()); } + public static String inputResult() { + System.out.println(INPUT_RESULT_TEXT); + return scanner.nextLine(); + } + + public static String inputPlayer() { + System.out.println(INPUT_PLAYER_TEXT); + return scanner.nextLine(); + } } diff --git a/src/main/java/nextstep/ladder/view/ResultView.java b/src/main/java/nextstep/ladder/view/ResultView.java index a592e13004..10690da375 100644 --- a/src/main/java/nextstep/ladder/view/ResultView.java +++ b/src/main/java/nextstep/ladder/view/ResultView.java @@ -1,9 +1,11 @@ package nextstep.ladder.view; -import nextstep.ladder.domain.Line; -import nextstep.ladder.domain.Player; +import nextstep.ladder.domain.*; import java.util.List; +import java.util.Map; + +import static java.util.stream.Collectors.toList; public class ResultView { @@ -11,45 +13,57 @@ public class ResultView { private static final String SPACE_HORIZONTAL_LINE = " "; private static final String SPACE_START_HORIZONTAL_LINE = " "; private static final String VERTICAL_LINE = "|"; - private static final String NAME_LENGTH_FIVE_FORMAT_PATTERN = "%-5s"; + private static final String EMPTY = ""; + private static final String NAME_LENGTH_FIVE_FORMAT_PATTERN = "%-6s"; private static final String RESULT_TEXT = "실행결과"; + private static final String LADDER_RESULT_TEXT = "사다리 결과"; + private static final String PLAYER_NAME_AND_RESULT_TEXT = "%s : %s"; + private static final String ALL_PLAYER = "all"; - public static void printResult(List players, List lines) { - printResultText(); + public static void printLadderResult(Map players, List lines, Map result) { + System.out.println(LADDER_RESULT_TEXT); printPlayerName(players); - printLadderLine(lines); - } - - private static void printResultText() { - System.out.println(RESULT_TEXT); + printLadderResult(lines); + printResultValues(result); } - private static void printPlayerName(List playerList) { + private static void printPlayerName(Map players) { StringBuilder sb = new StringBuilder(); - playerList.stream().map(Player::getName).forEach(name -> sb.append(String.format(NAME_LENGTH_FIVE_FORMAT_PATTERN, name)).append(" ")); + players.keySet().forEach(name -> sb.append(String.format(NAME_LENGTH_FIVE_FORMAT_PATTERN, name.getValue()))); System.out.println(sb.toString()); } - private static void printLadderLine(List lines) { - lines.forEach(line -> System.out.println(getLineText(line.getPoints()))); + private static void printLadderResult(List lines) { + StringBuilder sb = new StringBuilder(); + for (int index = 0; index < lines.size(); index++) { + sb.setLength(0); + sb.append(VERTICAL_LINE); + sb.append(getLinePointsResult(lines.get(index).getPoints())); + System.out.println(sb.toString()); + } } - private static String getLineText(List pointList) { + private static String getLinePointsResult(List points) { StringBuilder sb = new StringBuilder(); - sb.append(SPACE_START_HORIZONTAL_LINE); - pointList.forEach(hasHorizontalLine -> getHorizontalLine(sb, hasHorizontalLine)); - sb.append(VERTICAL_LINE); + for (int index = 1; index < points.size(); index++) { + sb.append(PointStatus.LEFT == points.get(index).getStatus() ? HORIZONTAL_LINE : SPACE_HORIZONTAL_LINE).append(VERTICAL_LINE);; + } return sb.toString(); } - private static void getHorizontalLine(StringBuilder sb, boolean hasHorizontalLine) { - sb.append(VERTICAL_LINE); - if (hasHorizontalLine) { - sb.append(HORIZONTAL_LINE); - } - if (!hasHorizontalLine) { - sb.append(SPACE_HORIZONTAL_LINE); - } + private static void printResultValues(Map results) { + StringBuilder sb = new StringBuilder(); + results.keySet().forEach(resultId -> sb.append(String.format(NAME_LENGTH_FIVE_FORMAT_PATTERN, results.get(resultId).getValue())).append(" ")); + System.out.println(sb.toString()); } + public static void printPlayerResult(String inputPlayer, Map players) { + System.out.println(RESULT_TEXT); + if (ALL_PLAYER.equals(inputPlayer)) { + players.keySet().forEach(name -> System.out.println(String.format(PLAYER_NAME_AND_RESULT_TEXT, name.getValue(), players.get(name).getResult().getValue()))); + return; + } + Player player = players.get(new PlayerName(inputPlayer)); + System.out.println(player.getResult().getValue()); + } } diff --git a/src/test/java/nextstep/ladder/domain/LadderTest.java b/src/test/java/nextstep/ladder/domain/LadderTest.java deleted file mode 100644 index 303a26e5df..0000000000 --- a/src/test/java/nextstep/ladder/domain/LadderTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package nextstep.ladder.domain; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class LadderTest { - - @Test - void create() { - Ladder ladder = new Ladder(5, 4); - assertThat(ladder.getLines()).hasSize(5); - ladder.getLines().forEach(line -> { - assertThat(line.getPoints()).hasSize(3); - }); - Ladder ladder2 = new Ladder(8, 6); - assertThat(ladder2.getLines()).hasSize(8); - ladder2.getLines().forEach(line -> { - assertThat(line.getPoints()).hasSize(5); - }); - } - -} diff --git a/src/test/java/nextstep/ladder/domain/LineTest.java b/src/test/java/nextstep/ladder/domain/LineTest.java deleted file mode 100644 index 328d662f11..0000000000 --- a/src/test/java/nextstep/ladder/domain/LineTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package nextstep.ladder.domain; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class LineTest { - - @Test - void createLine() { - Line line1 = new Line(3); - assertThat(line1.getPoints()).hasSize(2); - Line line2 = new Line(5); - assertThat(line2.getPoints()).hasSize(4); - } - -} diff --git a/src/test/java/nextstep/ladder/domain/PlayerTest.java b/src/test/java/nextstep/ladder/domain/PlayerTest.java deleted file mode 100644 index 84dcfb0d3d..0000000000 --- a/src/test/java/nextstep/ladder/domain/PlayerTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package nextstep.ladder.domain; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; - -public class PlayerTest { - - @Test - void create() { - Player player1 = new Player("HI"); - assertThat(player1.getName()).isEqualTo("HI"); - assertThatIllegalArgumentException().isThrownBy(() -> { - Player player2 = new Player("HEELLO"); - }).withMessageMatching("사람에 이름을 최대5글자까지 가능합니다."); - } - -}