diff --git a/src/main/java/ladder/Application.java b/src/main/java/ladder/Application.java index 7fe80989..0ad13aae 100644 --- a/src/main/java/ladder/Application.java +++ b/src/main/java/ladder/Application.java @@ -7,6 +7,7 @@ import ladder.domain.LadderGameResult; import ladder.domain.LadderHeight; import ladder.domain.LadderResults; +import ladder.domain.Lines; import ladder.domain.Participants; import ladder.view.InputView; import ladder.view.OutputView; @@ -21,7 +22,9 @@ public static void main(String[] args) { LadderHeight height = InputView.inputHeight(); - Ladder ladder = Ladder.of(participants, height, RANDOM::nextBoolean); + Lines lines = Lines.generate(participants.size(), height, RANDOM::nextBoolean); + + Ladder ladder = Ladder.of(participants, lines); Map path = ladder.generateResults(); @@ -29,7 +32,5 @@ public static void main(String[] args) { OutputView.printLadder(participants, ladder, results); LadderGameController.runInquiry(gameResult); - } - } diff --git a/src/main/java/ladder/domain/Ladder.java b/src/main/java/ladder/domain/Ladder.java index 74f98717..4693fbd6 100644 --- a/src/main/java/ladder/domain/Ladder.java +++ b/src/main/java/ladder/domain/Ladder.java @@ -1,54 +1,40 @@ package ladder.domain; -import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; -import java.util.function.BooleanSupplier; +import java.util.stream.Collectors; +import java.util.stream.IntStream; public class Ladder { - private final List lines; + private final Lines lines; private final int width; - private Ladder(List lines, int width) { + private Ladder(Lines lines, int width) { this.lines = lines; this.width = width; } - public static Ladder of(Participants participants, LadderHeight height, BooleanSupplier strategy) { - int participantCount = participants.size(); - List lines = generateLines(participantCount, height, strategy); - - return new Ladder(lines, participantCount); + public static Ladder of(Participants participants, Lines lines) { + return new Ladder(lines, participants.size()); } - public int climb(int startIndex) { - int currentIndex = startIndex; - for (Line line : lines) { - currentIndex = line.move(currentIndex); - } - return currentIndex; + int climb(int startIndex) { + return lines.move(startIndex); } public Map generateResults() { - Map results = new LinkedHashMap<>(); - for (int i = 0; i < width; i++) { - results.put(i, climb(i)); - } - return results; - } - - private static List generateLines(int participantCount, LadderHeight height, BooleanSupplier strategy) { - List lines = new ArrayList<>(); - int pointCount = participantCount - 1; - for (int i = 0; i < height.getValue(); i++) { - lines.add(Line.from(pointCount, strategy)); - } - return lines; + return IntStream.range(0, width) + .boxed() + .collect(Collectors.toMap( + index -> index, + this::climb, + (oldValue, newValue) -> newValue, + LinkedHashMap::new + )); } - public List getLines() { + public Lines getLines() { return lines; } } diff --git a/src/main/java/ladder/domain/LadderResults.java b/src/main/java/ladder/domain/LadderResults.java index 94c44ec7..65c43161 100644 --- a/src/main/java/ladder/domain/LadderResults.java +++ b/src/main/java/ladder/domain/LadderResults.java @@ -1,5 +1,6 @@ package ladder.domain; +import java.util.Collections; import java.util.List; public class LadderResults { @@ -21,6 +22,6 @@ public static LadderResults of(List rawResults, int participantCount) { } public List getValues() { - return results; + return Collections.unmodifiableList(results); } } diff --git a/src/main/java/ladder/domain/Line.java b/src/main/java/ladder/domain/Line.java index 3923a493..ba36c53a 100644 --- a/src/main/java/ladder/domain/Line.java +++ b/src/main/java/ladder/domain/Line.java @@ -1,47 +1,24 @@ package ladder.domain; -import java.util.ArrayList; -import java.util.List; import java.util.function.BooleanSupplier; public class Line { - private final List points; + private final Points points; - private Line(List points) { + private Line(Points points) { this.points = points; } - public static Line from(int size, BooleanSupplier strategy) { - return new Line(generatePoints(size, strategy)); - } - - public List getPoints() { - return points; + public static Line generate(int size, BooleanSupplier strategy) { + return new Line(Points.generate(size, strategy)); } public int move(int index) { - if (index < points.size() && points.get(index).hasBridge()) { - return index + 1; - } - if (index > 0 && points.get(index - 1).hasBridge()) { - return index - 1; - } - return index; + return points.move(index); } - private static List generatePoints(int size, BooleanSupplier strategy) { - List points = new ArrayList<>(); - boolean lastBridge = false; - for (int i = 0; i < size; i++) { - lastBridge = addPoint(points, lastBridge, strategy); - } + public Points getPoints() { return points; } - - private static boolean addPoint(List points, boolean lastBridge, BooleanSupplier strategy) { - boolean currentBridge = !lastBridge && strategy.getAsBoolean(); - points.add(Point.from(currentBridge)); - return currentBridge; - } } diff --git a/src/main/java/ladder/domain/Lines.java b/src/main/java/ladder/domain/Lines.java new file mode 100644 index 00000000..d95cf29a --- /dev/null +++ b/src/main/java/ladder/domain/Lines.java @@ -0,0 +1,45 @@ +package ladder.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.function.BooleanSupplier; + +public class Lines implements Iterable { + private final List lines; + + private Lines(List lines) { + this.lines = lines; + } + + public static Lines generate(int participantCount, LadderHeight height, BooleanSupplier strategy) { + List lines = new ArrayList<>(); + int pointCount = participantCount - 1; + for (int i = 0; i < height.getValue(); i++) { + lines.add(Line.generate(pointCount, strategy)); + } + return new Lines(lines); + } + + public int move(int startIndex) { + int currentIndex = startIndex; + for (Line line : lines) { + currentIndex = line.move(currentIndex); + } + return currentIndex; + } + + public Line get(int index) { + return lines.get(index); + } + + public int size() { + return lines.size(); + } + + @Override + public Iterator iterator() { + return lines.iterator(); + } +} diff --git a/src/main/java/ladder/domain/Participants.java b/src/main/java/ladder/domain/Participants.java index e40fa9e1..2ee81076 100644 --- a/src/main/java/ladder/domain/Participants.java +++ b/src/main/java/ladder/domain/Participants.java @@ -15,7 +15,7 @@ private Participants(List participants) { public static Participants from(List names) { List nameList = names.stream() .map(Name::from) - .collect(Collectors.toList()); + .toList(); return new Participants(nameList); } diff --git a/src/main/java/ladder/domain/Point.java b/src/main/java/ladder/domain/Point.java index a613c2a2..d2fbec5a 100644 --- a/src/main/java/ladder/domain/Point.java +++ b/src/main/java/ladder/domain/Point.java @@ -1,24 +1,18 @@ package ladder.domain; -public class Point { - - private static final Point BRIDGE = new Point(true); - private static final Point EMPTY = new Point(false); +public enum Point { + BRIDGE(true), + EMPTY(false); private final boolean hasBridge; - private Point(boolean hasBridge) { + Point(boolean hasBridge) { this.hasBridge = hasBridge; } public static Point from(boolean hasBridge) { - if (hasBridge) { - return BRIDGE; - } - return EMPTY; + return hasBridge ? BRIDGE : EMPTY; } - public boolean hasBridge() { - return hasBridge; - } + public boolean hasBridge() { return hasBridge; } } diff --git a/src/main/java/ladder/domain/Points.java b/src/main/java/ladder/domain/Points.java new file mode 100644 index 00000000..6193b69d --- /dev/null +++ b/src/main/java/ladder/domain/Points.java @@ -0,0 +1,65 @@ +package ladder.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.function.BooleanSupplier; + +public class Points implements Iterable { + private final List points; + + private Points(List points) { + this.points = points; + } + + public static Points generate(int size, BooleanSupplier strategy) { + List points = new ArrayList<>(); + boolean lastBridge = false; + for (int i = 0; i < size; i++) { + lastBridge = addPoint(points, lastBridge, strategy); + } + return new Points(points); + } + + private static boolean addPoint(List points, boolean lastBridge, BooleanSupplier strategy) { + boolean currentBridge = !lastBridge && strategy.getAsBoolean(); + points.add(Point.from(currentBridge)); + return currentBridge; + } + + public int move(int index) { + if (isRightStep(index)) { + return index + 1; + } + if (isLeftStep(index)) { + return index - 1; + } + return index; + } + + private boolean isRightStep(int index) { + return index < points.size() && points.get(index).hasBridge(); + } + + private boolean isLeftStep(int index) { + return index > 0 && points.get(index - 1).hasBridge(); + } + + public Point get(int index) { + return points.get(index); + } + + public int size() { + return points.size(); + } + + public List getValues() { + return Collections.unmodifiableList(points); + } + + @Override + public Iterator iterator() { + return points.iterator(); + } +} diff --git a/src/test/java/ladder/domain/LadderTest.java b/src/test/java/ladder/domain/LadderTest.java index ecc2f149..aa25dcec 100644 --- a/src/test/java/ladder/domain/LadderTest.java +++ b/src/test/java/ladder/domain/LadderTest.java @@ -38,10 +38,11 @@ void createLadder_WidthCheck() { void createLadder_StrategyCheck() { Ladder ladder = createLadder(NAMES_3, 1); Line firstLine = ladder.getLines().get(0); + Points points = firstLine.getPoints(); assertAll( - () -> assertThat(firstLine.getPoints().get(0).hasBridge()).isTrue(), - () -> assertThat(firstLine.getPoints().get(1).hasBridge()).isFalse() + () -> assertThat(points.get(0).hasBridge()).isTrue(), + () -> assertThat(points.get(1).hasBridge()).isFalse() ); } @@ -72,10 +73,11 @@ void generateResults() { } private Ladder createLadder(List names, int height) { - return Ladder.of( - Participants.from(names), - LadderHeight.from(height), - () -> true - ); + Participants participants = Participants.from(names); + LadderHeight ladderHeight = LadderHeight.from(height); + + Lines lines = Lines.generate(participants.size(), ladderHeight, () -> true); + + return Ladder.of(participants, lines); } } diff --git a/src/test/java/ladder/domain/LineTest.java b/src/test/java/ladder/domain/LineTest.java index 410e9d4e..638e1b5d 100644 --- a/src/test/java/ladder/domain/LineTest.java +++ b/src/test/java/ladder/domain/LineTest.java @@ -15,8 +15,8 @@ void createLine_AlwaysTrueStrategy() { // given int size = 3; // when - Line line = Line.from(size, () -> true); - List points = line.getPoints(); + Line line = Line.generate(size, () -> true); + Points points = line.getPoints(); // then assertAll( @@ -32,7 +32,7 @@ void createLine_AlwaysFalseStrategy() { // given int size = 5; // when - Line line = Line.from(size, () -> false); + Line line = Line.generate(size, () -> false); // then assertThat(line.getPoints()) @@ -44,14 +44,14 @@ void createLine_AlwaysFalseStrategy() { @Test void createLine_SizeCheck() { int size = 10; - Line line = Line.from(size, () -> true); + Line line = Line.generate(size, () -> true); assertThat(line.getPoints()).hasSize(size); } @DisplayName("다리 유무에 따라 인덱스가 좌, 우로 이동하거나 그대로 유지된다.") @Test void move() { - Line line = Line.from(3, () -> true); + Line line = Line.generate(3, () -> true); assertAll( // 0번 기둥: 0번 포인트가 T이므로 오른쪽(1)으로 이동 diff --git a/src/test/java/ladder/domain/LinesTest.java b/src/test/java/ladder/domain/LinesTest.java new file mode 100644 index 00000000..18305418 --- /dev/null +++ b/src/test/java/ladder/domain/LinesTest.java @@ -0,0 +1,56 @@ +package ladder.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class LinesTest { + + @DisplayName("참가자 수와 높이에 맞는 라인 묶음이 생성된다.") + @Test + void createLines() { + int participantCount = 4; + int height = 5; + Lines lines = Lines.generate(participantCount, LadderHeight.from(height), () -> false); + + assertAll( + () -> assertThat(lines.size()).isEqualTo(height), + () -> assertThat(lines.get(0).getPoints().size()).isEqualTo(participantCount - 1) ); + } + + @DisplayName("다리가 하나도 없을 때, 시작 인덱스와 도착 인덱스는 동일하다.") + @Test + void move_noBridge() { + // given + Lines lines = Lines.generate(3, LadderHeight.from(3), () -> false); + + assertAll( + () -> assertThat(lines.move(0)).isEqualTo(0), + () -> assertThat(lines.move(1)).isEqualTo(1), + () -> assertThat(lines.move(2)).isEqualTo(2) + ); + } + + @DisplayName("다리가 모든 층에 가로로 놓여있을 때, 인덱스가 지그재그로 이동한다.") + @Test + void move_withBridges() { + // given: 2명이 참여하고 1층 높이인데 다리가 있는 경우 + Lines lines = Lines.generate(2, LadderHeight.from(1), () -> true); + + assertThat(lines.move(0)).isEqualTo(1); + assertThat(lines.move(1)).isEqualTo(0); + } + + @DisplayName("여러 층을 거쳐 최종 위치를 정확히 계산한다.") + @Test + void move_multipleFloors() { + // given: 2명이 참여하고 2층 높이인데 모든 층에 다리가 있는 경우 (0 -> 1 -> 0) + Lines lines = Lines.generate(2, LadderHeight.from(2), () -> true); + + // then + assertThat(lines.move(0)).isEqualTo(0); + } + +} diff --git a/src/test/java/ladder/domain/PointsTest.java b/src/test/java/ladder/domain/PointsTest.java new file mode 100644 index 00000000..afbcfbd2 --- /dev/null +++ b/src/test/java/ladder/domain/PointsTest.java @@ -0,0 +1,66 @@ +package ladder.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class PointsTest { + + @DisplayName("설정한 크기만큼 포인트 묶음이 생성된다.") + @Test + void generate_SizeCheck() { + int size = 5; + Points points = Points.generate(size, () -> true); + + assertThat(points.size()).isEqualTo(size); + } + + @DisplayName("전략이 항상 true여도 다리는 연속해서 생성되지 않는다.") + @Test + void generate_NoConsecutiveBridges() { + // given + Points points = Points.generate(3, () -> true); + + assertAll( + () -> assertThat(points.get(0).hasBridge()).isTrue(), + () -> assertThat(points.get(1).hasBridge()).isFalse(), + () -> assertThat(points.get(2).hasBridge()).isTrue() + ); + } + + @DisplayName("현재 위치에 다리가 있으면 오른쪽(index + 1)으로 이동한다.") + @Test + void move_Right() { + // given + Points points = Points.generate(1, () -> true); + + // when & then + assertThat(points.move(0)).isEqualTo(1); + } + + @DisplayName("이전 위치에 다리가 있으면 왼쪽(index - 1)으로 이동한다.") + @Test + void move_Left() { + // given + Points points = Points.generate(1, () -> true); + + // when & then + assertThat(points.move(1)).isEqualTo(0); + } + + @DisplayName("좌우에 다리가 없으면 현재 위치를 유지한다.") + @Test + void move_Stay() { + // given + Points points = Points.generate(3, () -> false); + + // when & then + assertAll( + () -> assertThat(points.move(0)).isEqualTo(0), + () -> assertThat(points.move(1)).isEqualTo(1), + () -> assertThat(points.move(2)).isEqualTo(2) + ); + } +}