diff --git a/README.md b/README.md new file mode 100644 index 00000000..50d9bebc --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +#사다리게임 + +##1단계 +- 4X4 크기의 사다리를 출력한다. +- 가로라인이 겹치지 않게 랜덤으로 줄을 긋는다. + +##2단계 +-사다리 넓이와 높이를 입력받아, 그 크기의 사다리를 출력한다. + +##3단계 +-0부터 인덱스 번호를 부여해 사다리 타고 난 후 결과를 출력하낟. + +##4단계 +-사다리 게임에 참여하는 사람에 이름을 최대 5글자까지 부여할 수 있다. 사다리를 출력할 때 사람 이름도 같이 출력한다. +사람 이름은 쉼표(,)를 기준으로 구분한다. +-개인별 이름을 입력하면 개인별 결과를 출력하고, "all"을 입력하면 전체 참여자의 실행 결과를 출력한다. + +##5단계 +-리팩토링 diff --git a/build.gradle b/build.gradle index 239f9e78..497cfe54 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ plugins { id 'java' + id 'application' } group = 'cholog' 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/Main.java b/src/main/java/Main.java new file mode 100644 index 00000000..df439cf2 --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,7 @@ +import controller.LadderController; + +public class Main { + public static void main(String[] args) { + new LadderController().gameStart(); + } +} diff --git a/src/main/java/controller/LadderController.java b/src/main/java/controller/LadderController.java new file mode 100644 index 00000000..14f8f13c --- /dev/null +++ b/src/main/java/controller/LadderController.java @@ -0,0 +1,76 @@ +package controller; + +import model.Ladder; +import model.LadderFactory; +import model.LadderGame; +import model.LadderSize; +import model.Line; +import model.Participants; +import model.Results; +import model.BuildLine; +import view.InputView; +import view.OutputView; + +import java.util.ArrayList; +import java.util.List; + +public class LadderController { + private final LadderFactory factory = new LadderFactory(); + private final OutputView outputView = new OutputView(); + private final InputView inputView = new InputView(); + + public void gameStart() { + Participants participants = new Participants(inputView.inputParticipants()); + int width = participants.size(); + + Results results; + while (true) { + results = new Results(inputView.inputResults()); + if (participants.size() == results.size()) { + break; + } + outputView.printError("참여자 수와 결과 수가 일치하지 않습니다."); + } + + int height; + while (true) { + height = inputView.heightSize(); + if (height >= 1) { + break; + } + outputView.printError("높이는 1이상이어야 합니다."); + } + + LadderSize size = new LadderSize(width, height); + Ladder ladder = factory.create(size, width); + + List lines = new ArrayList<>(); + for (Line line : ladder.lines()) { + lines.add(BuildLine.build(line)); + } + + outputView.printLadder(participants.getNames(), lines, results.getValues()); + + LadderGame game = new LadderGame(ladder, participants.size()); + gameResult(game, participants, results); + } + + private void gameResult(LadderGame game, Participants participants, Results results) { + while (true) { + String queryName = inputView.inputQueryName(); + + if ("all".equals(queryName)) { + outputView.printAllResults(game.playAll(participants, results)); + break; + } + + if (!participants.contains(queryName)) { + outputView.printError("존재하지 않는 이름입니다."); + continue; + } + + String result = game.getResult(queryName, participants, results); + outputView.printSingleResult(queryName, result); + } + } +} diff --git a/src/main/java/model/BuildLine.java b/src/main/java/model/BuildLine.java new file mode 100644 index 00000000..58fb01b7 --- /dev/null +++ b/src/main/java/model/BuildLine.java @@ -0,0 +1,20 @@ +package model; + +import java.util.Objects; + +public final class BuildLine { + + public static final int LADDER_LINE = 5; + + public static String build(Line line) { + Objects.requireNonNull(line); + StringBuilder b = new StringBuilder(); + for (Point p : line.points()) { + b.append("|"); + String fill = p.isConnected() ? "-".repeat(LADDER_LINE) : " ".repeat(LADDER_LINE); + b.append(fill); + } + b.append("|"); + return b.toString(); + } +} diff --git a/src/main/java/model/Ladder.java b/src/main/java/model/Ladder.java new file mode 100644 index 00000000..93facb97 --- /dev/null +++ b/src/main/java/model/Ladder.java @@ -0,0 +1,16 @@ +package model; + +import java.util.List; +import java.util.Objects; + +public class Ladder { + private final List lines; + + public Ladder(List lines) { + this.lines = Objects.requireNonNull(lines); + } + + public List lines() { + return lines; + } +} diff --git a/src/main/java/model/LadderFactory.java b/src/main/java/model/LadderFactory.java new file mode 100644 index 00000000..c841d71f --- /dev/null +++ b/src/main/java/model/LadderFactory.java @@ -0,0 +1,33 @@ +package model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class LadderFactory { + private final Random random = new Random(); + + public Ladder create(LadderSize size, int width) { + List lines = new ArrayList<>(); + for (int i = 0; i < size.height(); i++) { + lines.add(createLine(width)); + } + return new Ladder(lines); + } + + private Line createLine(int width) { + int pointsCount = width - 1; + List points = new ArrayList<>(); + boolean prevConnected = false; + + for (int i = 0; i < pointsCount; i++) { + boolean connect = false; + if (!prevConnected) { + connect = random.nextBoolean(); + } + points.add(new Point(connect)); + prevConnected = connect; + } + return new Line(points); + } +} diff --git a/src/main/java/model/LadderGame.java b/src/main/java/model/LadderGame.java new file mode 100644 index 00000000..56a7eeb5 --- /dev/null +++ b/src/main/java/model/LadderGame.java @@ -0,0 +1,53 @@ +package model; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class LadderGame { + private final Ladder ladder; + + public LadderGame(Ladder ladder, int width) { + this.ladder = ladder; + } + + public Map playAll(Participants participants, Results results) { + Map allResults = new LinkedHashMap<>(); + for (int i = 0; i < participants.size(); i++) { + int finalPosition = play(i); + allResults.put(participants.get(i), results.get(finalPosition)); + } + return allResults; + } + + public String getResult(String name, Participants participants, Results results) { + int startIndex = participants.indexOf(name); + int finalPosition = play(startIndex); + return results.get(finalPosition); + } + + public int play(int startPosition) { + int currentPosition = startPosition; + List lines = ladder.lines(); + + for (Line line : lines) { + currentPosition = moveOnLine(currentPosition, line); + } + + return currentPosition; + } + + private int moveOnLine(int position, Line line) { + List points = line.points(); + + if (position > 0 && points.get(position - 1).isConnected()) { + return position - 1; + } + + if (position < points.size() && points.get(position).isConnected()) { + return position + 1; + } + + return position; + } +} diff --git a/src/main/java/model/LadderSize.java b/src/main/java/model/LadderSize.java new file mode 100644 index 00000000..327e1ae4 --- /dev/null +++ b/src/main/java/model/LadderSize.java @@ -0,0 +1,22 @@ +package model; + +public class LadderSize { + private final int height; + private final int width; + + public LadderSize(int width, int height) { + validateHeight(height); + this.height = height; + this.width = width; + } + + private void validateHeight(int height) { + if (height <= 0) { + throw new IllegalArgumentException("사다리 높이는 1 이상이어야 합니다."); + } + } + + public int height() { + return height; + } +} diff --git a/src/main/java/model/Line.java b/src/main/java/model/Line.java new file mode 100644 index 00000000..38950b48 --- /dev/null +++ b/src/main/java/model/Line.java @@ -0,0 +1,16 @@ +package model; + +import java.util.List; +import java.util.Objects; + +public class Line { + private final List points; + + public Line(List points) { + this.points = Objects.requireNonNull(points); + } + + public List points() { + return points; + } +} diff --git a/src/main/java/model/Participants.java b/src/main/java/model/Participants.java new file mode 100644 index 00000000..82c05438 --- /dev/null +++ b/src/main/java/model/Participants.java @@ -0,0 +1,39 @@ +package model; + +import java.util.List; +import java.util.Objects; + +public class Participants { + private final List names; + + public Participants(List names) { + validateNames(names); + this.names = Objects.requireNonNull(names); + } + + private void validateNames(List names) { + if (names == null || names.isEmpty()) { + throw new IllegalArgumentException("참여자는 최소 1명 이상이어야 합니다."); + } + } + + public int size() { + return names.size(); + } + + public boolean contains(String name) { + return names.contains(name); + } + + public int indexOf(String name) { + return names.indexOf(name); + } + + public String get(int index) { + return names.get(index); + } + + public List getNames() { + return names; + } +} diff --git a/src/main/java/model/Point.java b/src/main/java/model/Point.java new file mode 100644 index 00000000..f3adcd88 --- /dev/null +++ b/src/main/java/model/Point.java @@ -0,0 +1,13 @@ +package model; + +public class Point { + private final boolean connected; + + public Point(boolean connected) { + this.connected = connected; + } + + public boolean isConnected() { + return connected; + } +} diff --git a/src/main/java/model/Results.java b/src/main/java/model/Results.java new file mode 100644 index 00000000..c0206949 --- /dev/null +++ b/src/main/java/model/Results.java @@ -0,0 +1,25 @@ +package model; + + +import java.util.List; +import java.util.Objects; + +public class Results { + private final List values; + + public Results(List values) { + this.values = Objects.requireNonNull(values); + } + + public int size() { + return values.size(); + } + + public String get(int index) { + return values.get(index); + } + + public List getValues() { + return values; + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 00000000..45e6d696 --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,34 @@ +package view; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + +public class InputView { + Scanner scanner = new Scanner(System.in); + + public List inputParticipants() { + System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"); + String input = scanner.nextLine(); + return Arrays.asList(input.split(",\\s*")); + } + + public List inputResults() { + System.out.println("실행 결과를 입력하세요. (결과는 쉼표(,)로 구분하세요)"); + String input = scanner.nextLine(); + return Arrays.asList(input.split(",\\s*")); + } + + public int heightSize() { + System.out.println("최대 사다리 높이는 몇 개인가요?"); + int height = scanner.nextInt(); + scanner.nextLine(); + return height; + } + + public String inputQueryName() { + System.out.println(); + System.out.println("결과를 보고 싶은 사람은?--all을 입력하면 전체 결과를 확인하고 종료됩니다."); + return scanner.nextLine().trim(); + } +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 00000000..0e900d09 --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,43 @@ +package view; + +import java.util.List; +import java.util.Map; + +public class OutputView { + public void printLadder(List participants, List lines, List results) { + System.out.println(); + System.out.println("실행결과"); + + for (String name : participants) { + System.out.print(String.format("%-5s", name)); + } + System.out.println(); + + for (String line : lines) { + System.out.println(line); + } + + for (String result : results) { + System.out.print(String.format("%-5s", result)); + } + System.out.println(); + } + + public void printSingleResult(String name, String result) { + System.out.println(); + System.out.println("실행결과"); + System.out.println(result); + } + + public void printAllResults(Map results) { + System.out.println(); + System.out.println("실행결과"); + for (Map.Entry entry : results.entrySet()) { + System.out.println(entry.getKey() + " : " + entry.getValue()); + } + } + + public void printError(String message) { + System.out.println(message); + } +} 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/LadderTest.java b/src/test/java/LadderTest.java new file mode 100644 index 00000000..1605f1b9 --- /dev/null +++ b/src/test/java/LadderTest.java @@ -0,0 +1,61 @@ +import model.Ladder; +import model.LadderFactory; +import model.LadderGame; +import model.LadderSize; +import model.Line; +import model.Participants; +import model.Point; +import model.Results; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@DisplayName("사다리 테스트") +public class LadderTest { + + @Test + @DisplayName("높이가 0 이하면 예외가 발생한다") + void ladderSizeThrowsExceptionForInvalidHeight() { + assertThrows(IllegalArgumentException.class, () -> new LadderSize(4, 0)); + assertThrows(IllegalArgumentException.class, () -> new LadderSize(4, -1)); + } + + @Test + @DisplayName("가로줄은 서로 연결될 수 없다") + void factoryPreventsConsecutiveConnections() { + LadderFactory factory = new LadderFactory(); + LadderSize size = new LadderSize(5, 10); + + Ladder ladder = factory.create(size, 5); + + for (Line line : ladder.lines()) { + List points = line.points(); + for (int i = 0; i < points.size() - 1; i++) { + if (points.get(i).isConnected()) { + assertFalse(points.get(i + 1).isConnected()); + } + } + } + } + + @Test + @DisplayName("LadderGame - 연결선이 없으면 그대로 내려간다") + void gameMovesDownWithoutConnection() { + List line = Arrays.asList(new Point(false), new Point(false), new Point(false)); + Ladder ladder = new Ladder(Arrays.asList(new Line(line))); + + Participants participants = new Participants(Arrays.asList("neo", "brown", "brie", "tommy")); + Results results = new Results(Arrays.asList("꽝", "5000", "꽝2", "3000")); + + LadderGame game = new LadderGame(ladder, 4); + + assertEquals("꽝", game.getResult("neo", participants, results)); + assertEquals("5000", game.getResult("brown", participants, results)); + } +} diff --git a/src/test/java/ResultTest.java b/src/test/java/ResultTest.java new file mode 100644 index 00000000..a0161768 --- /dev/null +++ b/src/test/java/ResultTest.java @@ -0,0 +1,73 @@ +import model.Ladder; +import model.LadderGame; +import model.Line; +import model.Participants; +import model.Point; +import model.Results; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("입출력 결과 테스트") +public class ResultTest { + + @Test + @DisplayName("참여자 이름을 쉼표로 구분하여 List로 변환한다") + void parseParticipantNames() { + String input = "neo,brown,brie,tommy"; + List names = Arrays.asList(input.split(",")); + Participants participants = new Participants(names); + + assertEquals(4, participants.size()); + assertEquals("neo", participants.get(0)); + assertEquals("brown", participants.get(1)); + assertEquals("brie", participants.get(2)); + assertEquals("tommy", participants.get(3)); + } + + @Test + @DisplayName("실행 결과를 쉼표로 구분하여 List로 변환한다") + void parseResults() { + String input = "꽝,5000,꽝,3000"; + List values = Arrays.asList(input.split(",")); + Results results = new Results(values); + + assertEquals(4, results.size()); + assertEquals("꽝", results.get(0)); + assertEquals("5000", results.get(1)); + assertEquals("꽝", results.get(2)); + assertEquals("3000", results.get(3)); + } + + @Test + @DisplayName("존재하지 않는 참여자 이름은 -1 인덱스를 반환한다") + void nonExistentParticipantReturnsNegativeIndex() { + Participants participants = new Participants(Arrays.asList("neo", "brown", "brie")); + + int index = participants.indexOf("unknown"); + + assertEquals(-1, index); + } + + @Test + @DisplayName("참여자 수와 결과 수가 일치하지 않으면 예외가 발생한다") + void participantAndResultCountMustMatch() { + List line = Arrays.asList(new Point(false), new Point(false)); + Ladder ladder = new Ladder(Arrays.asList(new Line(line))); + + Participants participants = new Participants(Arrays.asList("a", "b", "c")); + Results results = new Results(Arrays.asList("1", "2")); + + LadderGame game = new LadderGame(ladder, 3); + + assertThrows( + IndexOutOfBoundsException.class, () -> { + game.playAll(participants, results); + }); + } +} +