diff --git a/README.md b/README.md new file mode 100644 index 00000000..58b2c754 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# java-ladder-func-playground + +사다리 미션 + +## 개요 +사다리 미션은 간단한 콘솔 애플리케이션으로, 사용자로부터 참여할 사람의 이름과 실행 결과, 최대 사다리 높이를 입력 받습니다.\ +그리고 생성된 사다리에 대해 사용자는 참가자의 이름을 입력하여 해당 참가자에 대한 실행 결과를 확인할 수 있고, `all`을 입력하여 전체 결과를 확인할 수 있습니다. + +## 프로젝트 구조 +``` +controller/ +- LadderController.java : 전반적인 게임 Flow(입력, 실행, 출력)를 담당하는 클래스 +model/ +- ladder/ + - Connection.java : 사다리 가로 줄의 지점 간 연결을 정의하는 클래스 + - Ladder.java : 전체 사다리를 정의하는 클래스 + - LadderFactory.java : 사다리 생성을 담당하는 클래스 + - Line.java : 사다리의 가로 줄을 정의하는 클래스 +- Game.java : 게임 실행을 담당하는 클래스 +- GameConfiguration.java : 게임 설정을 정의하는 클래스 +- GameConfigurationBuilder.java : 게임 설정 Builder 클래스 +- GameResult.java : 게임 결과를 정의하는 클래스 +- Participant.java : 각 참가자를 정의하는 클래스 +view/ +- InputView.java : 사용자 입력 처리 기능 수행 +- OutputView.java : 사용자에게 정보 출력 기능 수행 +Main.java : Main entrypoint +``` diff --git a/src/main/java/.gitkeep b/src/main/java/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/io/suhan/ladder/Main.java b/src/main/java/io/suhan/ladder/Main.java new file mode 100644 index 00000000..4bd2c837 --- /dev/null +++ b/src/main/java/io/suhan/ladder/Main.java @@ -0,0 +1,10 @@ +package io.suhan.ladder; + +import io.suhan.ladder.controller.LadderController; + +public class Main { + public static void main(String[] args) { + LadderController controller = new LadderController(); + controller.run(); + } +} diff --git a/src/main/java/io/suhan/ladder/controller/LadderController.java b/src/main/java/io/suhan/ladder/controller/LadderController.java new file mode 100644 index 00000000..20b544ad --- /dev/null +++ b/src/main/java/io/suhan/ladder/controller/LadderController.java @@ -0,0 +1,71 @@ +package io.suhan.ladder.controller; + +import io.suhan.ladder.model.Game; +import io.suhan.ladder.model.GameConfiguration; +import io.suhan.ladder.model.GameConfigurationBuilder; +import io.suhan.ladder.model.GameResult; +import io.suhan.ladder.model.Outcome; +import io.suhan.ladder.model.Participant; +import io.suhan.ladder.view.InputView; +import io.suhan.ladder.view.OutputView; +import java.util.List; +import java.util.stream.Stream; + +public class LadderController { + public void run() { + try { + GameConfiguration configuration = readConfiguration(); + Game game = Game.of(configuration); + + GameResult result = game.execute(); + + OutputView.printLadderResult(game); + + handleOutcomeQuery(result); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + + private GameConfiguration readConfiguration() { + // method chaining으로 구성하려고 하였으나 Input 검증으로 인해 각각 따로 받음 + GameConfigurationBuilder builder = new GameConfigurationBuilder(); + + List participants = InputView.getParticipants().stream().map(Participant::new).toList(); + builder.participants(participants); + + List outcomes = InputView.getOutcomes().stream().map(Outcome::new).toList(); + builder.outcomes(outcomes); + + int height = InputView.getLadderHeight(); + builder.height(height); + + return builder.build(); + } + + private void handleOutcomeQuery(GameResult result) { + Stream.generate(InputView::getParticipantForResult) + .takeWhile((input) -> !input.equals("all")) + .forEach((input) -> handleSingleQuery(result, input)); + + OutputView.printGameResult(result); + } + + private void handleSingleQuery(GameResult result, String input) { + Participant participant = findParticipantByName(result, input); + + if (participant == null) { + System.out.println("존재하지 않는 참가자입니다."); + return; + } + + OutputView.printGameResultOf(participant, result); + } + + private Participant findParticipantByName(GameResult result, String name) { + return result.results().keySet().stream() + .filter((participant -> participant.name().equals(name))) + .findFirst() + .orElse(null); + } +} diff --git a/src/main/java/io/suhan/ladder/model/Game.java b/src/main/java/io/suhan/ladder/model/Game.java new file mode 100644 index 00000000..190a6af6 --- /dev/null +++ b/src/main/java/io/suhan/ladder/model/Game.java @@ -0,0 +1,81 @@ +package io.suhan.ladder.model; + +import io.suhan.ladder.model.ladder.Connection; +import io.suhan.ladder.model.ladder.Ladder; +import io.suhan.ladder.model.ladder.LadderFactory; +import io.suhan.ladder.model.ladder.Line; +import io.suhan.ladder.view.OutputView; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class Game { + private final Ladder ladder; + private final GameConfiguration configuration; + + private Game(GameConfiguration configuration, Ladder ladder) { + this.configuration = configuration; + this.ladder = ladder; + } + + public static Game of(GameConfiguration configuration) { + return new Game(configuration, LadderFactory.createLadder(configuration.width(), configuration.height())); + } + + public static Game of(GameConfiguration configuration, Ladder ladder) { + return new Game(configuration, ladder); + } + + public GameResult execute() { + List participants = configuration.participants(); + List outcomes = configuration.outcomes(); + Map result = new LinkedHashMap<>(); + + for (int start = 0; start < configuration.width(); start++) { + int end = traverse(start); + Participant participant = participants.get(start); + Outcome outcome = outcomes.get(end); + + result.put(participant, outcome); + } + + return new GameResult(result); + } + + private int traverse(int start) { + int col = start; + + for (Line line : ladder.lines()) { + col = findNextColumn(line, col); + } + + return col; + } + + private int findNextColumn(Line line, int col) { + Optional connected = line.connections().stream() + .filter((connection) -> connection.left() == col || connection.right() == col) + .findFirst(); + + return connected + .map((connection) -> getConnectedColumn(connection, col)) + .orElse(col); + } + + private int getConnectedColumn(Connection connection, int col) { + if (connection.left() == col) { + return connection.right(); + } + + return connection.left(); + } + + public Ladder getLadder() { + return ladder; + } + + public GameConfiguration getConfiguration() { + return configuration; + } +} diff --git a/src/main/java/io/suhan/ladder/model/GameConfiguration.java b/src/main/java/io/suhan/ladder/model/GameConfiguration.java new file mode 100644 index 00000000..c036d417 --- /dev/null +++ b/src/main/java/io/suhan/ladder/model/GameConfiguration.java @@ -0,0 +1,16 @@ +package io.suhan.ladder.model; + +import java.util.Collections; +import java.util.List; + +public record GameConfiguration(List participants, List outcomes, int width, int height) { + @Override + public List participants() { + return Collections.unmodifiableList(participants); + } + + @Override + public List outcomes() { + return Collections.unmodifiableList(outcomes); + } +} diff --git a/src/main/java/io/suhan/ladder/model/GameConfigurationBuilder.java b/src/main/java/io/suhan/ladder/model/GameConfigurationBuilder.java new file mode 100644 index 00000000..0a7cd112 --- /dev/null +++ b/src/main/java/io/suhan/ladder/model/GameConfigurationBuilder.java @@ -0,0 +1,51 @@ +package io.suhan.ladder.model; + +import java.util.List; + +public class GameConfigurationBuilder { + private List participants; + private List outcomes; + private int height; + + public GameConfigurationBuilder participants(List participants) { + if (participants == null) { + throw new IllegalStateException("참가자 목록이 설정되지 않았습니다."); + } + + if (participants.isEmpty()) { + throw new IllegalArgumentException("참가자 목록은 비어 있을 수 없습니다."); + } + + this.participants = participants; + + return this; + } + + public GameConfigurationBuilder outcomes(List outcomes) { + if (outcomes == null) { + throw new IllegalStateException("결과 목록이 설정되지 않았습니다."); + } + + if (outcomes.size() != participants.size()) { + throw new IllegalArgumentException("참가자의 수와 실행 결과의 수는 같아야 합니다."); + } + + this.outcomes = outcomes; + + return this; + } + + public GameConfigurationBuilder height(int height) { + if (height <= 0) { + throw new IllegalArgumentException("높이는 양수여야 합니다."); + } + + this.height = height; + + return this; + } + + public GameConfiguration build() { + return new GameConfiguration(participants, outcomes, participants.size(), height); + } +} diff --git a/src/main/java/io/suhan/ladder/model/GameResult.java b/src/main/java/io/suhan/ladder/model/GameResult.java new file mode 100644 index 00000000..377b4eca --- /dev/null +++ b/src/main/java/io/suhan/ladder/model/GameResult.java @@ -0,0 +1,15 @@ +package io.suhan.ladder.model; + +import java.util.Collections; +import java.util.Map; + +public record GameResult(Map results) { + @Override + public Map results() { + return Collections.unmodifiableMap(results); + } + + public Outcome getOutcome(Participant participant) { + return results.get(participant); + } +} diff --git a/src/main/java/io/suhan/ladder/model/Outcome.java b/src/main/java/io/suhan/ladder/model/Outcome.java new file mode 100644 index 00000000..7692c7ed --- /dev/null +++ b/src/main/java/io/suhan/ladder/model/Outcome.java @@ -0,0 +1,9 @@ +package io.suhan.ladder.model; + +public record Outcome(String value) { + public Outcome { + if (value.isBlank()) { + throw new IllegalArgumentException("결과는 공백일 수 없습니다."); + } + } +} diff --git a/src/main/java/io/suhan/ladder/model/Participant.java b/src/main/java/io/suhan/ladder/model/Participant.java new file mode 100644 index 00000000..07c27b46 --- /dev/null +++ b/src/main/java/io/suhan/ladder/model/Participant.java @@ -0,0 +1,15 @@ +package io.suhan.ladder.model; + +public record Participant(String name) { + public static final int PARTICIPANT_NAME_MAX_LENGTH = 5; + + public Participant { + if (name.length() > PARTICIPANT_NAME_MAX_LENGTH) { + throw new IllegalArgumentException("참가자의 이름은 최대 " + PARTICIPANT_NAME_MAX_LENGTH + "자만 가능합니다."); + } + + if (name.isBlank()) { + throw new IllegalArgumentException("참가자의 이름은 공백일 수 없습니다."); + } + } +} diff --git a/src/main/java/io/suhan/ladder/model/ladder/Connection.java b/src/main/java/io/suhan/ladder/model/ladder/Connection.java new file mode 100644 index 00000000..f243b5bc --- /dev/null +++ b/src/main/java/io/suhan/ladder/model/ladder/Connection.java @@ -0,0 +1,3 @@ +package io.suhan.ladder.model.ladder; + +public record Connection(int left, int right) { } diff --git a/src/main/java/io/suhan/ladder/model/ladder/Ladder.java b/src/main/java/io/suhan/ladder/model/ladder/Ladder.java new file mode 100644 index 00000000..7683904f --- /dev/null +++ b/src/main/java/io/suhan/ladder/model/ladder/Ladder.java @@ -0,0 +1,11 @@ +package io.suhan.ladder.model.ladder; + +import java.util.Collections; +import java.util.List; + +public record Ladder(List lines) { + @Override + public List lines() { + return Collections.unmodifiableList(lines); + } +} diff --git a/src/main/java/io/suhan/ladder/model/ladder/LadderFactory.java b/src/main/java/io/suhan/ladder/model/ladder/LadderFactory.java new file mode 100644 index 00000000..aa706e93 --- /dev/null +++ b/src/main/java/io/suhan/ladder/model/ladder/LadderFactory.java @@ -0,0 +1,52 @@ +package io.suhan.ladder.model.ladder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class LadderFactory { + private static final Random DEFAULT_RANDOM = new Random(); + + public static Ladder createLadder(int width, int height) { + return createLadder(width, height, DEFAULT_RANDOM); + } + + public static Ladder createLadder(int width, int height, Random random) { + List lines = createLines(width, height, random); + + return new Ladder(lines); + } + + private static List createLines(int width, int height, Random random) { + List lines = new ArrayList<>(); + + for (int i = 0; i < height; i++) { + lines.add(createLine(width, random)); + } + + return lines; + } + + private static Line createLine(int width, Random random) { + List connections = createConnections(width, random); + + return new Line(connections); + } + + private static List createConnections(int width, Random random) { + List connections = new ArrayList<>(); + + for (int i = 0; i < width - 1; i++) { + if (!shouldConnect(random)) continue; + + connections.add(new Connection(i, i + 1)); + i += 1; // skip the right next line + } + + return connections; + } + + private static boolean shouldConnect(Random random) { + return random.nextBoolean(); + } +} diff --git a/src/main/java/io/suhan/ladder/model/ladder/Line.java b/src/main/java/io/suhan/ladder/model/ladder/Line.java new file mode 100644 index 00000000..362edbbb --- /dev/null +++ b/src/main/java/io/suhan/ladder/model/ladder/Line.java @@ -0,0 +1,11 @@ +package io.suhan.ladder.model.ladder; + +import java.util.Collections; +import java.util.List; + +public record Line(List connections) { + @Override + public List connections() { + return Collections.unmodifiableList(connections); + } +} diff --git a/src/main/java/io/suhan/ladder/view/InputView.java b/src/main/java/io/suhan/ladder/view/InputView.java new file mode 100644 index 00000000..88ca82b1 --- /dev/null +++ b/src/main/java/io/suhan/ladder/view/InputView.java @@ -0,0 +1,38 @@ +package io.suhan.ladder.view; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + +public class InputView { + private static final Scanner scanner = new Scanner(System.in); + private static final String INPUT_DELIMITER = ","; + + public static List getParticipants() { + System.out.println(); + System.out.println("참여할 사람 이름을 입력하세요. (이름은 " + INPUT_DELIMITER + "로 구분하세요)"); + + return Arrays.asList(scanner.nextLine().split(INPUT_DELIMITER)); + } + + public static List getOutcomes() { + System.out.println(); + System.out.println("실행 결과를 입력하세요. (결과는 " + INPUT_DELIMITER + "로 구분하세요)"); + + return Arrays.asList(scanner.nextLine().split(INPUT_DELIMITER)); + } + + public static int getLadderHeight() { + System.out.println(); + System.out.println("최대 사다리 높이는 몇 개인가요?"); + + return scanner.nextInt(); + } + + public static String getParticipantForResult() { + System.out.println(); + System.out.println("결과를 보고 싶은 사람은?"); + + return scanner.nextLine(); + } +} diff --git a/src/main/java/io/suhan/ladder/view/OutputView.java b/src/main/java/io/suhan/ladder/view/OutputView.java new file mode 100644 index 00000000..90cf6dac --- /dev/null +++ b/src/main/java/io/suhan/ladder/view/OutputView.java @@ -0,0 +1,101 @@ +package io.suhan.ladder.view; + +import io.suhan.ladder.model.Game; +import io.suhan.ladder.model.GameResult; +import io.suhan.ladder.model.Outcome; +import io.suhan.ladder.model.Participant; +import io.suhan.ladder.model.ladder.Ladder; +import io.suhan.ladder.model.ladder.Line; +import java.util.List; +import java.util.stream.Collectors; + +public class OutputView { + private static final String CONNECTION_SEGMENT_CHAR = "-"; + private static final String CONNECTION_SEGMENT_EMPTY_CHAR = " "; + private static final int CONNECTION_SEGMENT_LENGTH = 5; + + public static void printLadderResult(Game game) { + System.out.println(); + System.out.println("사다리 결과"); + System.out.println(); + + printParticipants(game.getConfiguration().participants()); + + printLadder(game.getLadder(), game.getConfiguration().width()); + + printOutcomes(game.getConfiguration().outcomes()); + } + + public static void printGameResult(GameResult result) { + System.out.println(); + System.out.println("실행 결과"); + + result.results() + .forEach(((participant, outcome) -> System.out.println(participant.name() + " : " + outcome.value()))); + } + + public static void printGameResultOf(Participant target, GameResult result) { + System.out.println(); + System.out.println("실행 결과"); + System.out.println(result.getOutcome(target).value()); + } + + public static void printLadder(Ladder ladder, int width) { + for (Line line : ladder.lines()) { + String row = buildRow(line, width); + + System.out.println(row); + } + } + + private static void printParticipants(List participants) { + String line = participants.stream() + .map((participant -> centerAlign(participant.name(), Participant.PARTICIPANT_NAME_MAX_LENGTH))) + .collect(Collectors.joining(" ")); + + System.out.println(line); + } + + private static void printOutcomes(List outcomes) { + String line = outcomes.stream() + .map((outcome -> centerAlign(outcome.value(), Participant.PARTICIPANT_NAME_MAX_LENGTH))) + .collect(Collectors.joining(" ")); + + System.out.println(line); + } + + private static String buildRow(Line line, int width) { + StringBuilder builder = new StringBuilder(); + builder.append(" ".repeat(Participant.PARTICIPANT_NAME_MAX_LENGTH / 2)); + + for (int i = 0; i < width; i++) { + builder.append("|"); + + String connectionSegment = getConnectionSegment(line, i); + builder.append(connectionSegment); + } + + return builder.toString(); + } + + private static String getConnectionSegment(Line line, int index) { + if (isConnected(line, index)) { + return CONNECTION_SEGMENT_CHAR.repeat(CONNECTION_SEGMENT_LENGTH); + } + + return CONNECTION_SEGMENT_EMPTY_CHAR.repeat(CONNECTION_SEGMENT_LENGTH); + } + + private static boolean isConnected(Line line, int index) { + return line.connections().stream() + .anyMatch((connection) -> connection.left() == index); + } + + private static String centerAlign(String input, int width) { + int totalPadding = width - input.length(); + int leftPadding = totalPadding / 2; + int rightPadding = totalPadding - leftPadding; + + return " ".repeat(leftPadding) + input + " ".repeat(rightPadding); + } +} diff --git a/src/test/java/.gitkeep b/src/test/java/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/test/java/io/suhan/ladder/model/GameConfigurationBuilderTest.java b/src/test/java/io/suhan/ladder/model/GameConfigurationBuilderTest.java new file mode 100644 index 00000000..63fd1980 --- /dev/null +++ b/src/test/java/io/suhan/ladder/model/GameConfigurationBuilderTest.java @@ -0,0 +1,53 @@ +package io.suhan.ladder.model; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +public class GameConfigurationBuilderTest { + @Test + void GameConfiguration을_올바르게_생성할_수_있다() { + // given + List participants = List.of(new Participant("p1")); + List outcomes = List.of(new Outcome("o1")); + int height = 1; + + // when + GameConfiguration config = new GameConfigurationBuilder() + .participants(participants) + .outcomes(outcomes) + .height(height) + .build(); + + // then + SoftAssertions.assertSoftly((softly) -> { + softly.assertThat(config.participants()).isEqualTo(participants); + softly.assertThat(config.outcomes()).isEqualTo(outcomes); + softly.assertThat(config.height()).isEqualTo(height); + }); + } + + @Test + void 참가자의_수와_실행_결과의_수는_같아야_한다() { + // given + List participants = List.of(new Participant("p1")); + List outcomes = List.of(new Outcome("o1"), new Outcome("o2")); + int height = 1; + String expectedMessage = "참가자의 수와 실행 결과의 수는 같아야 합니다."; + + // when & then + assertThatThrownBy(() -> new GameConfigurationBuilder() + .participants(participants) + .outcomes(outcomes) + .height(height) + .build()).isInstanceOf(IllegalArgumentException.class) + .hasMessage(expectedMessage); + } +} diff --git a/src/test/java/io/suhan/ladder/model/GameResultTest.java b/src/test/java/io/suhan/ladder/model/GameResultTest.java new file mode 100644 index 00000000..8a41baa4 --- /dev/null +++ b/src/test/java/io/suhan/ladder/model/GameResultTest.java @@ -0,0 +1,20 @@ +package io.suhan.ladder.model; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Map; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +public class GameResultTest { + @Test + void 게임_결과는_수정할_수_없다() { + Map entry = Map.of(new Participant("p1"), new Outcome("o1")); + GameResult result = new GameResult(entry); + + assertThrows(UnsupportedOperationException.class, () -> result.results().put(new Participant("p2"), new Outcome("o2"))); + } +} diff --git a/src/test/java/io/suhan/ladder/model/GameTest.java b/src/test/java/io/suhan/ladder/model/GameTest.java new file mode 100644 index 00000000..734a01f4 --- /dev/null +++ b/src/test/java/io/suhan/ladder/model/GameTest.java @@ -0,0 +1,57 @@ +package io.suhan.ladder.model; + +import io.suhan.ladder.model.ladder.Ladder; +import io.suhan.ladder.model.ladder.LadderFactory; +import java.util.List; +import java.util.Random; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +public class GameTest { + @Test + void 참가자별_결과가_올바르게_매핑된다() { + // given + Random fixedRandom = new Random(3L); + + List participants = List.of( + new Participant("p1"), + new Participant("p2"), + new Participant("p3"), + new Participant("p4") + ); + + List outcomes = List.of( + new Outcome("o1"), + new Outcome("o2"), + new Outcome("o3"), + new Outcome("o4") + ); + + int height = 4; + + GameConfiguration config = new GameConfigurationBuilder() + .participants(participants) + .outcomes(outcomes) + .height(height) + .build(); + + Ladder ladder = LadderFactory.createLadder(config.width(), config.height(), fixedRandom); + + Game game = Game.of(config, ladder); + + // when + GameResult result = game.execute(); + + // then + SoftAssertions.assertSoftly((softly) -> { + softly.assertThat(result.getOutcome(participants.get(0)).value()).isEqualTo("o1"); + softly.assertThat(result.getOutcome(participants.get(1)).value()).isEqualTo("o2"); + softly.assertThat(result.getOutcome(participants.get(2)).value()).isEqualTo("o3"); + softly.assertThat(result.getOutcome(participants.get(3)).value()).isEqualTo("o4"); + }); + } +} diff --git a/src/test/java/io/suhan/ladder/model/OutcomeTest.java b/src/test/java/io/suhan/ladder/model/OutcomeTest.java new file mode 100644 index 00000000..271b50de --- /dev/null +++ b/src/test/java/io/suhan/ladder/model/OutcomeTest.java @@ -0,0 +1,16 @@ +package io.suhan.ladder.model; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +public class OutcomeTest { + @Test + void 결과_값은_공백일_수_없다() { + assertThrows(IllegalArgumentException.class, () -> new Outcome("")); + } +} diff --git a/src/test/java/io/suhan/ladder/model/ParticipantTest.java b/src/test/java/io/suhan/ladder/model/ParticipantTest.java new file mode 100644 index 00000000..c74d1a1d --- /dev/null +++ b/src/test/java/io/suhan/ladder/model/ParticipantTest.java @@ -0,0 +1,21 @@ +package io.suhan.ladder.model; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +public class ParticipantTest { + @Test + void 참가자_이름은_5자_이하만_가능하다() { + assertThrows(IllegalArgumentException.class, () -> new Participant("123456")); + } + + @Test + void 참가자의_이름은_공백일_수_없다() { + assertThrows(IllegalArgumentException.class, () -> new Participant("")); + } +} diff --git a/src/test/java/io/suhan/ladder/model/ladder/ConnectionTest.java b/src/test/java/io/suhan/ladder/model/ladder/ConnectionTest.java new file mode 100644 index 00000000..8cfaeb07 --- /dev/null +++ b/src/test/java/io/suhan/ladder/model/ladder/ConnectionTest.java @@ -0,0 +1,19 @@ +package io.suhan.ladder.model.ladder; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +public class ConnectionTest { + @Test + void Left와_Right_정보를_반환할_수_있다() { + Connection connection = new Connection(0, 1); + + assertEquals(0, connection.left()); + assertEquals(1, connection.right()); + } +} diff --git a/src/test/java/io/suhan/ladder/model/ladder/LadderFactoryTest.java b/src/test/java/io/suhan/ladder/model/ladder/LadderFactoryTest.java new file mode 100644 index 00000000..00a155f1 --- /dev/null +++ b/src/test/java/io/suhan/ladder/model/ladder/LadderFactoryTest.java @@ -0,0 +1,48 @@ +package io.suhan.ladder.model.ladder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Random; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +public class LadderFactoryTest { + @Test + void 사다리는_올바른_높이를_가진다() { + Ladder ladder = LadderFactory.createLadder(5, 4); + + assertEquals(4, ladder.lines().size()); + } + + @Test + void 사다리의_가로_라인이_겹치지_않는다() { + // given + Random fixedRandom = new Random(3L); + int width = 4; + int height = 5; + + // when + Ladder ladder = LadderFactory.createLadder(width, height, fixedRandom); + + // then + for (int row = 0; row < ladder.lines().size(); row++) { + Line line = ladder.lines().get(row); + List connections = line.connections(); + + for (int i = 0; i < connections.size() - 1; i++) { + Connection current = connections.get(i); + Connection next = connections.get(i + 1); + + int diff = next.left() - current.left(); + + // diff = 1 if overlaps + assertTrue(diff > 1); + } + } + } +} diff --git a/src/test/java/io/suhan/ladder/model/ladder/LadderTest.java b/src/test/java/io/suhan/ladder/model/ladder/LadderTest.java new file mode 100644 index 00000000..ef5f364c --- /dev/null +++ b/src/test/java/io/suhan/ladder/model/ladder/LadderTest.java @@ -0,0 +1,34 @@ +package io.suhan.ladder.model.ladder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +public class LadderTest { + @Test + void 라인들을_저장할_수_있다() { + List lines = List.of( + new Line(List.of(new Connection(0, 1))), + new Line(List.of(new Connection(1, 2))) + ); + + Ladder ladder = new Ladder(lines); + + assertEquals(lines.size(), ladder.lines().size()); + assertIterableEquals(lines, ladder.lines()); + } + + @Test + void 라인_목록은_수정할_수_없다() { + Ladder ladder = new Ladder(List.of(new Line(List.of(new Connection(0, 1))))); + + assertThrows(UnsupportedOperationException.class, () -> ladder.lines().add(new Line(List.of(new Connection(1, 2))))); + } +} diff --git a/src/test/java/io/suhan/ladder/model/ladder/LineTest.java b/src/test/java/io/suhan/ladder/model/ladder/LineTest.java new file mode 100644 index 00000000..cd854a3c --- /dev/null +++ b/src/test/java/io/suhan/ladder/model/ladder/LineTest.java @@ -0,0 +1,30 @@ +package io.suhan.ladder.model.ladder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +public class LineTest { + @Test + void 연결들을_저장할_수_있다() { + List connections = List.of(new Connection(0, 1)); + Line line = new Line(connections); + + assertEquals(connections.size(), line.connections().size()); + assertIterableEquals(connections, line.connections()); + } + + @Test + void 연결_목록은_수정할_수_없다() { + Line line = new Line(List.of(new Connection(0, 1))); + + assertThrows(UnsupportedOperationException.class, () -> line.connections().add(new Connection(1, 2))); + } +}