diff --git a/README.md b/README.md index b853592f59..31786f79f2 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,48 @@ # 사다리 게임 + ## 진행 방법 + * 사다리 게임 게임 요구사항을 파악한다. * 요구사항에 대한 구현을 완료한 후 자신의 github 아이디에 해당하는 브랜치에 Pull Request(이하 PR)를 통해 코드 리뷰 요청을 한다. * 코드 리뷰 피드백에 대한 개선 작업을 하고 다시 PUSH한다. * 모든 피드백을 완료하면 다음 단계를 도전하고 앞의 과정을 반복한다. ## 온라인 코드 리뷰 과정 -* [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](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) + +--- + +## Step 2. 사다리(생성) + +### 기능 요구사항 + +- 사다리 게임에 참여하는 사람에 이름을 최대5글자까지 부여할 수 있다. 사다리를 출력할 때 사람 이름도 같이 출력한다. +- 사람 이름은 쉼표(,)를 기준으로 구분한다. +- 사람 이름을 5자 기준으로 출력하기 때문에 사다리 폭도 넓어져야 한다. +- 사다리 타기가 정상적으로 동작하려면 라인이 겹치지 않도록 해야 한다. +- |-----|-----| 모양과 같이 가로 라인이 겹치는 경우 어느 방향으로 이동할지 결정할 수 없다. + +### 프로그래밍 요구사항 + +- 자바 8의 스트림과 람다를 적용해 프로그래밍한다. +- 규칙 6: 모든 엔티티를 작게 유지한다. + +### 요구사항 분석 + +- [X] 참가자는 이름을 갖는다. + - [X] 이름은 최대 5자이다. + - [X] 이름이 비어있거나 6자 이상이면 예외가 발생한다. +- [ ] 사다리 + - [X] 사다리의 가로 라인을 수를 설정할 수 있다. (높이) + - [X] 사다리의 세로 라인은 사람 수 만큼 생성된다. (너비) + - [X] 사다리의 각 가로 라인에는 사람 수 만큼 포인트(점)가 생성된다. + - [X] 좌, 우에 포인트가 존재하는 경우 이동할 수 있다. + - [X] 가장 왼쪽 포인트에서는 좌로 이동 불가하다. + - [X] 가장 오른쪽 포인트에서는 우로 이동 불가하다. + - [X] 연속으로 이동 가능한 포인트는 존재할 수 없다. -> 좌 우 모두 이동 가능한 포인트는 존재할 수 없다 +- [ ] 포인트 + - [X] 좌, 우 모두 이동 가능한 포인트는 존재할 수 없다. 세 가지 경우만 존재한다. + - [X] 좌 이동 불가, 우 이동 불가 + - [X] 좌 이동 가능, 우 이동 불가 + - [X] 좌 이동 불가, 우 이동 가능 diff --git a/build.gradle b/build.gradle index db8d2fdbd8..b7e31be58a 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'java' apply plugin: 'eclipse' version = '1.0.0' -sourceCompatibility = 1.8 +sourceCompatibility = 11 repositories { mavenCentral() diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 290541c738..8c41e30626 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Sat Apr 06 16:04:18 KST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/nextstep/ladder/LadderGameApplication.java b/src/main/java/nextstep/ladder/LadderGameApplication.java new file mode 100644 index 0000000000..aebe120cdd --- /dev/null +++ b/src/main/java/nextstep/ladder/LadderGameApplication.java @@ -0,0 +1,18 @@ +package nextstep.ladder; + +import nextstep.ladder.domain.LadderGame; +import nextstep.ladder.util.ConsoleUtil; + +import java.util.List; + +public class LadderGameApplication { + + public static void main(String[] args) { + List playerNames = ConsoleUtil.inputPlayerNames(); + int height = ConsoleUtil.inputHeight(); + + LadderGame ladderGame = LadderGame.start(playerNames, height); + + ConsoleUtil.printGameResults(ladderGame); + } +} diff --git a/src/main/java/nextstep/ladder/domain/LadderGame.java b/src/main/java/nextstep/ladder/domain/LadderGame.java new file mode 100644 index 0000000000..ce80ea5d17 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/LadderGame.java @@ -0,0 +1,29 @@ +package nextstep.ladder.domain; + +import java.util.List; + +public class LadderGame { + + private final Players players; + private final Lines lines; + + private LadderGame(final Players players, final Lines lines) { + this.players = players; + this.lines = lines; + } + + public static LadderGame start(List playerNames, int height) { + Players players = Players.of(playerNames); + LinesGenerator linesGenerator = new RandomLinesGenerator(players.count(), height); + + return new LadderGame(players, linesGenerator.generate()); + } + + public Players getPlayers() { + return players; + } + + public Lines getLines() { + return lines; + } +} diff --git a/src/main/java/nextstep/ladder/domain/Line.java b/src/main/java/nextstep/ladder/domain/Line.java new file mode 100644 index 0000000000..8035f269e1 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Line.java @@ -0,0 +1,96 @@ +package nextstep.ladder.domain; + +import java.util.ArrayList; +import java.util.List; + +public class Line { + private static final int MIN_POINT_SIZE = 2; + + private final List points; + + private Line(List points) { + validate(points); + this.points = points; + } + + private void validate(List points) { + validatePointsListSize(points); + validatePointMovements(points); + } + + private void validatePointsListSize(List points) { + if (points == null || points.isEmpty()) { + throw new IllegalArgumentException("점이 없습니다."); + } + + if (points.size() < MIN_POINT_SIZE) { + throw new IllegalArgumentException("점이 " + MIN_POINT_SIZE + "개 이상 있어야 합니다."); + } + } + + private void validatePointMovements(List points) { + if (firstPoint(points).canMoveLeft() || lastPoint(points).canMoveRight()) { + throw new IllegalArgumentException("첫 점에서는 왼쪽으로 이동할 수 없고, 마지막 점에서는 오른쪽으로 이동할 수 없습니다."); + } + + for (int i = 0; i < points.size() - 1; i++) { + assertConsecutivePointsAreMovable(points, i); + } + } + + private void assertConsecutivePointsAreMovable(List points, int i) { + if (points.get(i).canMoveRight() && !points.get(i + 1).canMoveLeft()) { + throw new IllegalArgumentException("연속된 점에서는 서로 이동 가능해야 합니다."); + } + } + + private Point firstPoint(List points) { + return points.get(0); + } + + private Point lastPoint(List points) { + return points.get(points.size() - 1); + } + + public int width() { + return points.size(); + } + + public Point getPoint(int index) { + return points.stream() + .filter(point -> point.sameIndex(index)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("포인트가 없습니다. index: " + index)); + } + + static class LineBuilder { + private final List points; + + public LineBuilder() { + this.points = new ArrayList<>(); + } + + public LineBuilder point(boolean canMoveRight) { + if (points.isEmpty()) { + points.add(Point.createLeftmost(canMoveRight)); + return this; + } + points.add(lastPoint().createNext(canMoveRight)); + return this; + } + + public Line build() { + if (points.isEmpty()) { + throw new IllegalStateException("점이 없습니다."); + } + + points.add(lastPoint().createRightmost()); + + return new Line(points); + } + + private Point lastPoint() { + return points.get(points.size() - 1); + } + } +} diff --git a/src/main/java/nextstep/ladder/domain/Lines.java b/src/main/java/nextstep/ladder/domain/Lines.java new file mode 100644 index 0000000000..112732c8e2 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Lines.java @@ -0,0 +1,26 @@ +package nextstep.ladder.domain; + +import java.util.List; + +public class Lines { + private final List lines; + + public Lines(List lines) { + validate(lines); + this.lines = lines; + } + + private void validate(List lines) { + if (lines == null || lines.isEmpty()) { + throw new IllegalArgumentException("사다리 높이는 1 이상이어야 합니다."); + } + } + + public int getHeight() { + return lines.size(); + } + + public Line getLine(int targetHeight) { + return lines.get(targetHeight); + } +} diff --git a/src/main/java/nextstep/ladder/domain/LinesGenerator.java b/src/main/java/nextstep/ladder/domain/LinesGenerator.java new file mode 100644 index 0000000000..4a1a8a1b1d --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/LinesGenerator.java @@ -0,0 +1,5 @@ +package nextstep.ladder.domain; + +public interface LinesGenerator { + Lines generate(); +} diff --git a/src/main/java/nextstep/ladder/domain/Player.java b/src/main/java/nextstep/ladder/domain/Player.java new file mode 100644 index 0000000000..1fc79b1316 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Player.java @@ -0,0 +1,40 @@ +package nextstep.ladder.domain; + +public class Player { + private static final int MAX_NAME_LENGTH = 5; + private static final int MIN_INDEX = 0; + + private final int index; + private final String name; + + public Player(int index, String name) { + validate(index); + validate(name); + this.index = index; + this.name = name; + } + + private static void validate(int index) { + if (index < MIN_INDEX) { + throw new IllegalArgumentException("인덱스는 " + MIN_INDEX + "보다 작을 수 없습니다."); + } + } + + private void validate(String name) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("이름은 비어있을 수 없습니다."); + } + + if (name.length() > MAX_NAME_LENGTH) { + throw new IllegalArgumentException("이름은 " + MAX_NAME_LENGTH + "자 이하여야 합니다."); + } + } + + public String getName() { + return name; + } + + public boolean sameIndex(int index) { + return this.index == index; + } +} diff --git a/src/main/java/nextstep/ladder/domain/Players.java b/src/main/java/nextstep/ladder/domain/Players.java new file mode 100644 index 0000000000..2c8b49dad3 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Players.java @@ -0,0 +1,39 @@ +package nextstep.ladder.domain; + +import java.util.List; +import java.util.stream.IntStream; + +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.toList; + +public class Players { + private final List players; + + private Players(List players) { + validate(players); + this.players = players; + } + + private void validate(List players) { + if (players == null || players.isEmpty()) { + throw new IllegalArgumentException("참가자가 없습니다."); + } + } + + public static Players of(List playerNames) { + return IntStream.range(0, playerNames.size()) + .mapToObj(index -> new Player(index, playerNames.get(index))) + .collect(collectingAndThen(toList(), Players::new)); + } + + public int count() { + return players.size(); + } + + public Player getPlayer(int index) { + return players.stream() + .filter(player -> player.sameIndex(index)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("참가자가 없습니다. index: " + index)); + } +} 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..7a6f0c2d6d --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Point.java @@ -0,0 +1,68 @@ +package nextstep.ladder.domain; + +import java.util.Objects; + +public class Point { + private static final int MIN_INDEX = 0; + + private final int index; + private final boolean left; + private final boolean right; + + public Point(int index, boolean left, boolean right) { + validate(index); + validate(left, right); + this.index = index; + this.left = left; + this.right = right; + } + + private void validate(int index) { + if (index < MIN_INDEX) { + throw new IllegalArgumentException("인덱스는 " + MIN_INDEX + "보다 작을 수 없습니다."); + } + } + + private void validate(boolean left, boolean right) { + if (left && right) { + throw new IllegalArgumentException("좌 우 모두 이동 가능한 Point 생성 불가"); + } + } + + public static Point createLeftmost(boolean canMoveRight) { + return new Point(MIN_INDEX, false, canMoveRight); + } + + public Point createRightmost() { + return new Point(this.index + 1, this.right, false); + } + + public Point createNext(boolean canMoveRight) { + return new Point(this.index + 1, this.right, !this.right && canMoveRight); + } + + public boolean canMoveLeft() { + return left; + } + + public boolean canMoveRight() { + return right; + } + + public boolean sameIndex(int index) { + return this.index == index; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Point point = (Point) o; + return index == point.index && left == point.left && right == point.right; + } + + @Override + public int hashCode() { + return Objects.hash(index, left, right); + } +} diff --git a/src/main/java/nextstep/ladder/domain/RandomLinesGenerator.java b/src/main/java/nextstep/ladder/domain/RandomLinesGenerator.java new file mode 100644 index 0000000000..068e3f0244 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/RandomLinesGenerator.java @@ -0,0 +1,36 @@ +package nextstep.ladder.domain; + +import java.util.Random; +import java.util.stream.IntStream; + +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.toList; + +public class RandomLinesGenerator implements LinesGenerator { + private static final Random RANDOM = new Random(); + private static final int SYSTEM_CREATE_FINAL_POINT_COUNT = 1; + + private final int numberOfPlayers; + private final int height; + + public RandomLinesGenerator(int numberOfPlayers, int height) { + this.numberOfPlayers = numberOfPlayers; + this.height = height; + } + + @Override + public Lines generate() { + return IntStream.range(0, height) + .mapToObj(i -> generateLineWithRandomPoints(numberOfPlayers)) + .collect(collectingAndThen(toList(), Lines::new)); + } + + private Line generateLineWithRandomPoints(int numberOfPlayers) { + Line.LineBuilder builder = new Line.LineBuilder(); + for (int i = 0; i < numberOfPlayers - SYSTEM_CREATE_FINAL_POINT_COUNT; i++) { + builder.point(RANDOM.nextBoolean()); + } + + return builder.build(); + } +} diff --git a/src/main/java/nextstep/ladder/util/ConsoleUtil.java b/src/main/java/nextstep/ladder/util/ConsoleUtil.java new file mode 100644 index 0000000000..a62ffb5b23 --- /dev/null +++ b/src/main/java/nextstep/ladder/util/ConsoleUtil.java @@ -0,0 +1,74 @@ +package nextstep.ladder.util; + +import nextstep.ladder.domain.*; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class ConsoleUtil { + private static final String VERTICAL_LINE = "|"; + private static final String MOVEABLE_FORMAT = "-----"; + private static final String LINE_BREAK = "\n"; + private static final String PRINT_FORMAT = "%6s"; + private static final String PLAYER_NAME_SPLIT_DELIMITER = ","; + + private static final Scanner SCANNER = new Scanner(System.in); + + public static List inputPlayerNames() { + System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"); + + String input = SCANNER.nextLine(); // ex) pobi,honux,crong,jk + return splitNames(input); + + } + + private static List splitNames(String playerNames) { + return Arrays.stream(playerNames.split(PLAYER_NAME_SPLIT_DELIMITER)) + .collect(Collectors.toList()); + } + + public static int inputHeight() { + System.out.println("최대 사다리 높이는 몇 개인가요?"); + return SCANNER.nextInt(); + } + + public static void printGameResults(LadderGame ladderGame) { + System.out.println("실행결과"); + printPlayers(ladderGame.getPlayers()); + printLines(ladderGame.getLines()); + } + + private static void printPlayers(Players players) { + String playerNames = IntStream.range(0, players.count()) + .mapToObj(players::getPlayer) + .map(player -> String.format(PRINT_FORMAT, player.getName())) + .collect(Collectors.joining()); + + System.out.println(playerNames); + } + + private static void printLines(Lines lines) { + String linesString = IntStream.range(0, lines.getHeight()) + .mapToObj(targetHeight -> lineToString(lines.getLine(targetHeight))) + .collect(Collectors.joining(LINE_BREAK)); + + System.out.println(linesString); + } + + private static String lineToString(Line line) { + return IntStream.range(0, line.width()) + .mapToObj(index -> pointToString(line.getPoint(index))) + .collect(Collectors.joining()); + } + + private static String pointToString(Point point) { + if (point.canMoveLeft()) { + return String.format(PRINT_FORMAT, MOVEABLE_FORMAT + VERTICAL_LINE); + } + + return String.format(PRINT_FORMAT, VERTICAL_LINE); + } +} diff --git a/src/test/java/nextstep/ladder/domain/LadderGameTest.java b/src/test/java/nextstep/ladder/domain/LadderGameTest.java new file mode 100644 index 0000000000..6b6d72d643 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/LadderGameTest.java @@ -0,0 +1,20 @@ +package nextstep.ladder.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThatCode; + +class LadderGameTest { + + @DisplayName("참여할 사람 이름과 사다리 높이를 받아 사다리 게임을 생성한다") + @Test + void create() { + List playerNames = List.of("pobi", "honux", "crong", "jk"); + int height = 5; + + assertThatCode(() -> LadderGame.start(playerNames, height)).doesNotThrowAnyException(); + } +} diff --git a/src/test/java/nextstep/ladder/domain/LineTest.java b/src/test/java/nextstep/ladder/domain/LineTest.java new file mode 100644 index 0000000000..66f09e3203 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/LineTest.java @@ -0,0 +1,37 @@ +package nextstep.ladder.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LineTest { + + @DisplayName("포인트를 받아 라인을 생성한다") + @Test + void create() { + Line line = new Line.LineBuilder() + .point(true) + .build(); + + assertThat(line.width()).isEqualTo(2); + } + + @DisplayName("포인트가 비어있으면 예외가 발생한다") + @Test + void emptyPoints() { + assertThatThrownBy(() -> new Line.LineBuilder().build()) + .isInstanceOf(IllegalStateException.class); + } + + @DisplayName("인덱스를 받아 포인트를 반환한다") + @Test + void getPoint() { + Line line = new Line.LineBuilder() + .point(true) + .build(); + + assertThat(line.getPoint(0)).isEqualTo(Point.createLeftmost(true)); + } +} diff --git a/src/test/java/nextstep/ladder/domain/LinesTest.java b/src/test/java/nextstep/ladder/domain/LinesTest.java new file mode 100644 index 0000000000..335e9b6297 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/LinesTest.java @@ -0,0 +1,71 @@ +package nextstep.ladder.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class LinesTest { + + @DisplayName("라인의 높이를 반환한다") + @Test + void getHeight() { + Lines lines = new RandomLinesGenerator(5, 5).generate(); + assertThat(lines.getHeight()).isEqualTo(5); + } + + @DisplayName("지정한 특정 높이의 라인을 반환한다") + @Test + void getLine() { + Line firstLine = new Line.LineBuilder() + .point(false) + .point(true) + .point(false) + .build(); + + Line secondLine = new Line.LineBuilder() + .point(true) + .point(false) + .point(true) + .build(); + + Lines lines = new ManualLinesGenerator(4, 2, List.of(firstLine, secondLine)) + .generate(); + + assertThat(lines.getLine(0)).isEqualTo(firstLine); + } + + static class ManualLinesGenerator implements LinesGenerator { + private final int numberOfPlayers; + private final int height; + private final List lines; + + public ManualLinesGenerator(int numberOfPlayers, int height, List lines) { + validate(numberOfPlayers, height, lines); + + this.numberOfPlayers = numberOfPlayers; + this.height = height; + this.lines = lines; + } + + private static void validate(int numberOfPlayers, int height, List lines) { + if (lines.size() != height) { + throw new IllegalArgumentException("사다리 높이와 라인의 개수가 일치하지 않습니다."); + } + if (containsInvalidLineWidth(numberOfPlayers, lines)) { + throw new IllegalArgumentException("사다리의 너비와 라인의 너비가 일치하지 않습니다."); + } + } + + private static boolean containsInvalidLineWidth(int numberOfPlayers, List lines) { + return lines.stream().anyMatch(line -> line.width() != numberOfPlayers); + } + + @Override + public Lines generate() { + return new Lines(lines); + } + } +} diff --git a/src/test/java/nextstep/ladder/domain/PlayerTest.java b/src/test/java/nextstep/ladder/domain/PlayerTest.java new file mode 100644 index 0000000000..b682f90d98 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/PlayerTest.java @@ -0,0 +1,42 @@ +package nextstep.ladder.domain; + +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 org.junit.jupiter.params.provider.NullAndEmptySource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class PlayerTest { + + @DisplayName("이름은 5글자 이하만 가능하다") + @Test + void tooLongName() { + assertThatThrownBy(() -> new Player(0, "123456")).isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("이름은 비어있을 수 없다") + @ParameterizedTest(name = "name = {0}") + @NullAndEmptySource + void emptyName(String name) { + assertThatThrownBy(() -> new Player(0, name)).isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("인덱스는 0보다 작을 수 없다") + @Test + void negativeIndex() { + assertThatThrownBy(() -> new Player(-1, "hello")).isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("인덱스가 같은지 비교") + @ParameterizedTest(name = "index = {0}, targetIndex = {1}, expected = {2}") + @CsvSource(value = {"0,0,true", "1,0,false"}) + void sameIndex(int index, int targetIndex, boolean expected) { + Player player = new Player(index, "hello"); + + assertThat(player.sameIndex(targetIndex)).isEqualTo(expected); + } + +} diff --git a/src/test/java/nextstep/ladder/domain/PlayersTest.java b/src/test/java/nextstep/ladder/domain/PlayersTest.java new file mode 100644 index 0000000000..20e8ce1b99 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/PlayersTest.java @@ -0,0 +1,42 @@ +package nextstep.ladder.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +class PlayersTest { + + @DisplayName("참여자 이름을 받아 참여자를 생성한다") + @Test + void create() { + List playerNames = List.of("pobi", "crong", "honux"); + Players players = Players.of(playerNames); + + assertThat(players.count()).isEqualTo(playerNames.size()); + } + + @DisplayName("참가자는 1명 이상이다") + @Test + void invalidPlayerCount() { + assertThatThrownBy(() -> Players.of(List.of())) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("인덱스를 받아 참가자를 반환한다") + @Test + void getPlayer() { + List playerNames = List.of("pobi", "crong", "honux"); + Players players = Players.of(playerNames); + + assertSoftly(softAssertions -> { + softAssertions.assertThat(players.getPlayer(0).getName()).isEqualTo("pobi"); + softAssertions.assertThat(players.getPlayer(1).getName()).isEqualTo("crong"); + softAssertions.assertThat(players.getPlayer(2).getName()).isEqualTo("honux"); + }); + } +} diff --git a/src/test/java/nextstep/ladder/domain/PointTest.java b/src/test/java/nextstep/ladder/domain/PointTest.java new file mode 100644 index 0000000000..585108014a --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/PointTest.java @@ -0,0 +1,61 @@ +package nextstep.ladder.domain; + +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.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class PointTest { + + @DisplayName("인덱스는 0보다 작을 수 없다") + @Test + void negativeIndex() { + assertThatThrownBy(() -> new Point(-1, false, false)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("좌 우 모두 이동 가능한 Point는 생성할 수 없다") + @Test + void invalidPoint() { + assertThatThrownBy(() -> new Point(1, true, true)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("Point는 좌 우 이동 가능 여부를 가진다") + @ParameterizedTest(name = "좌 이동: {0}, 우 이동: {1})") + @CsvSource(value = {"true,false", "false,true", "false,false"}) + void create(boolean left, boolean right) { + Point point = new Point(1, left, right); + + assertThat(point.canMoveLeft()).isEqualTo(left); + assertThat(point.canMoveRight()).isEqualTo(right); + } + + @DisplayName("가장 왼쪽 포인트는 왼쪽으로 이동할 수 없다") + @Test + void createLeftmost() { + Point point = Point.createLeftmost(true); + + assertThat(point.canMoveLeft()).isFalse(); + } + + @DisplayName("가장 오른쪽 포인트는 오른쪽으로 이동할 수 없다") + @Test + void createRightmost() { + Point point = new Point(1, false, true).createRightmost(); + + assertThat(point.canMoveRight()).isFalse(); + } + + @DisplayName("인덱스가 같은지 비교") + @ParameterizedTest(name = "index = {0}, targetIndex = {1}, expected = {2}") + @CsvSource(value = {"0,0,true", "1,0,false"}) + void sameIndex(int index, int targetIndex, boolean expected) { + Point point = new Point(index, false, false); + + assertThat(point.sameIndex(targetIndex)).isEqualTo(expected); + } +}