diff --git a/.gitignore b/.gitignore index 492f742..bf433ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea/** .gradle/** -build/ +build/** +out/** diff --git a/README.md b/README.md index 42fb362..81b174b 100644 --- a/README.md +++ b/README.md @@ -1 +1,34 @@ -# java-ladder 게임 +# 사다리 게임 +## 기능 구현 목록 +### 참여자 이름 입력 +- [x] 여러 사람의 이름은 쉼표(,)를 기준으로 구분 +- [x] 참여자의 이름은 최대 5글자 +- [x] 앞 뒤 공백을 제거 +- [x] 중복된 이름이 있는 경우 예외 처리 +- [ ] 이름이 공백일 수 없음 + +### 실행결과 입력 +- [x] 여러 결과는 쉼표(,)를 기준으로 구분 +- [x] 결과는 최대 5글자 +- [x] 앞 뒤 공백을 제거 +- [ ] 결과가 공백일 수 없음 +- [ ] 참여자 수와 결과의 수가 같아야 함 + +### 사다리 높이 입력 +- [x] 사다리 높이는 1 이상이여야함 + +### 사다리 생성 +- [x] 사다리의 라인이 겹치지 않도록 사다리를 생성 +e.g. |-----|-----|  모양과 같이 가로 라인이 겹치는 경우 어느 방향으로 이동할 +지 결정할 수 없다. + +### 사다리 출력 +- [x] 사다리 위에 사람 이름을 출력 +- [x] 사람 이름을 5자 기준으로 출력 +- [x] 사다리 폭 또한 사람 이름을 기준으로 함 +- [ ] 사다리 아래에 결과를 출력 +- [ ] 결과를 5자 기준으로 출력 + +### 사다리게임 결과 출력 +- [ ] 'all'을 입력시 전체참여자의 실행결과를 출력 +- [ ] 개인의 이름을 입력시 해당하는 참여자의 실행결과를 출력 \ No newline at end of file diff --git a/build.gradle b/build.gradle index 12ee531..726ae4b 100644 --- a/build.gradle +++ b/build.gradle @@ -15,4 +15,5 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1' testCompile "org.assertj:assertj-core:3.14.0" + compile 'org.apache.commons:commons-lang3:3.9' } diff --git a/gradlew.bat b/gradlew.bat index 0f8d593..700e192 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -46,12 +46,12 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windows variants +@rem Get command-ladderLine arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args -@rem Slurp the command line arguments. +@rem Slurp the command ladderLine arguments. set CMD_LINE_ARGS= set _SKIP=2 @@ -61,7 +61,7 @@ if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute -@rem Setup the command line +@rem Setup the command ladderLine set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar diff --git a/settings.gradle b/settings.gradle index 4b94a0e..880a4b1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ -rootProject.name = 'java-racingcar' +rootProject.name = 'java-ladder' diff --git a/src/main/java/empty.txt b/src/main/java/empty.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/laddergame/LadderGame.java b/src/main/java/laddergame/LadderGame.java new file mode 100644 index 0000000..cb87853 --- /dev/null +++ b/src/main/java/laddergame/LadderGame.java @@ -0,0 +1,13 @@ +package laddergame; + +import laddergame.controller.LadderGameController; +import laddergame.view.InputView; +import laddergame.view.OutputView; + +public class LadderGame { + + public static void main(String[] args) { + LadderGameController ladderGameController = new LadderGameController(new InputView(), new OutputView()); + ladderGameController.run(); + } +} diff --git a/src/main/java/laddergame/controller/LadderGameController.java b/src/main/java/laddergame/controller/LadderGameController.java new file mode 100644 index 0000000..eff1a09 --- /dev/null +++ b/src/main/java/laddergame/controller/LadderGameController.java @@ -0,0 +1,39 @@ +package laddergame.controller; + +import laddergame.domain.ladder.Ladder; +import laddergame.domain.ladder.strategy.RandomLadderCreationStrategy; +import laddergame.domain.ladderheight.LadderHeight; +import laddergame.domain.player.Players; +import laddergame.domain.result.Results; +import laddergame.service.LadderGameService; +import laddergame.view.InputView; +import laddergame.view.OutputView; + +public class LadderGameController { + + private final InputView inputView; + private final OutputView outputView; + private final LadderGameService ladderGameService = new LadderGameService(); + + public LadderGameController(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() { + String playerNames = inputView.getPlayerNamesFromConsole(); + Players players = ladderGameService.createPlayers(playerNames); + + String gameResults = inputView.getGameResultFromConsole(); + Results results = ladderGameService.createResults(gameResults, players.size()); + + int ladderHeightInput = inputView.getLadderHeightFromConsole(); + LadderHeight ladderHeight = ladderGameService.createLadderHeight(ladderHeightInput); + + Ladder ladder = ladderGameService.createLadder(players, ladderHeight, new RandomLadderCreationStrategy()); + + //LadderGame ladderGame = ladderGameService.createLadderGame(players, results, ladder); + + outputView.printResult(players, ladder); + } +} diff --git a/src/main/java/laddergame/domain/ladder/Ladder.java b/src/main/java/laddergame/domain/ladder/Ladder.java new file mode 100644 index 0000000..61c7343 --- /dev/null +++ b/src/main/java/laddergame/domain/ladder/Ladder.java @@ -0,0 +1,34 @@ +package laddergame.domain.ladder; + +import laddergame.domain.ladder.strategy.LadderCreationStrategy; +import laddergame.domain.ladderheight.LadderHeight; +import laddergame.domain.player.Players; + +import java.util.ArrayList; +import java.util.List; + +public class Ladder { + + private final List lines; + + private Ladder(final List lines) { + this.lines = lines; + } + + public static Ladder with(final Players players, final LadderHeight ladderHeight, LadderCreationStrategy strategy) { + List lines = new ArrayList<>(); + int width = players.size(); + int height = ladderHeight.getHeight(); + + for (int i = 0; i < height; i++) { + LadderLine line = LadderLine.with(width, strategy); + lines.add(line); + } + + return new Ladder(lines); + } + + public List getLines() { + return lines; + } +} diff --git a/src/main/java/laddergame/domain/ladder/LadderLine.java b/src/main/java/laddergame/domain/ladder/LadderLine.java new file mode 100644 index 0000000..7735664 --- /dev/null +++ b/src/main/java/laddergame/domain/ladder/LadderLine.java @@ -0,0 +1,93 @@ +package laddergame.domain.ladder; + +import laddergame.domain.ladder.strategy.LadderCreationStrategy; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class LadderLine { + + public static final String POINT= "|"; + public static final String CONNECTION= "-----"; + public static final String NON_CONNECTION= " "; + + private final List points; + + public LadderLine(final List points) { + this.points = points; + } + + public static LadderLine with(final int width, LadderCreationStrategy strategy) { + List points = new ArrayList<>(); + Point previousPoint = null; + + for (int i = 0; i < width; i++) { + int remain = width - i; + Point point = generatePoint(previousPoint, remain, strategy); + points.add(point); + previousPoint = point; + } + + return new LadderLine(points); + } + + private static Point generatePoint(Point point, int remain, LadderCreationStrategy strategy) { + if (isFirstPoint(point)) { + return Point.generateFirstPoint(strategy); + } + + if (isLastPoint(remain)) { + return Point.generateLastPoint(point); + } + + return point.decideNextPoint(strategy); + } + + private static boolean isFirstPoint(Point point) { + return point == null; + } + + private static boolean isLastPoint(int remain) { + return remain == 1; + } + + public int size() { + return points.size(); + } + + public String lineToString() { + List points = getPoints(); + + String result = " "; + for (Point point : points) { + String connectionString = point.connectionToString(); + result += (POINT + connectionString); + } + return result; + } + + public List getPoints() { + return points; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LadderLine that = (LadderLine) o; + return Objects.equals(points, that.points); + } + + @Override + public int hashCode() { + return Objects.hash(points); + } + + @Override + public String toString() { + return "LadderLine{" + + "points=" + points + + '}'; + } +} diff --git a/src/main/java/laddergame/domain/ladder/Point.java b/src/main/java/laddergame/domain/ladder/Point.java new file mode 100644 index 0000000..daac6c1 --- /dev/null +++ b/src/main/java/laddergame/domain/ladder/Point.java @@ -0,0 +1,63 @@ +package laddergame.domain.ladder; + +import laddergame.domain.ladder.strategy.LadderCreationStrategy; + +public enum Point { + + LEFT(true, false), + RIGHT(false, true), + NONE(false, false); + + private boolean leftConnection; + private boolean rightConnection; + + Point(boolean leftConnection, boolean rightConnection) { + this.leftConnection = leftConnection; + this.rightConnection = rightConnection; + } + + public static Point generateFirstPoint(LadderCreationStrategy strategy) { + return generatePoint(false, strategy.isConnectable()); + } + + public static Point generateLastPoint(Point point) { + if (point.hasRightConnection()) { + return generatePoint(true, false); + } + return generatePoint(false, false); + } + + public Point decideNextPoint(LadderCreationStrategy strategy) { + if (rightConnection) { + return generatePoint(true, false ); + } + return generatePoint(false, strategy.isConnectable()); + } + + private static Point generatePoint(boolean leftConnection, boolean rightConnection) { + if (leftConnection && rightConnection) { + throw new IllegalArgumentException(); + } + + if (leftConnection) { + return LEFT; + } + + if (rightConnection) { + return RIGHT; + } + + return NONE; + } + + public boolean hasRightConnection() { + return rightConnection; + } + + public String connectionToString() { + if (hasRightConnection()) { + return LadderLine.CONNECTION; + } + return LadderLine.NON_CONNECTION; + } +} diff --git a/src/main/java/laddergame/domain/ladder/strategy/LadderCreationStrategy.java b/src/main/java/laddergame/domain/ladder/strategy/LadderCreationStrategy.java new file mode 100644 index 0000000..6674d98 --- /dev/null +++ b/src/main/java/laddergame/domain/ladder/strategy/LadderCreationStrategy.java @@ -0,0 +1,6 @@ +package laddergame.domain.ladder.strategy; + +public interface LadderCreationStrategy { + + boolean isConnectable(); +} diff --git a/src/main/java/laddergame/domain/ladder/strategy/ManualLadderCreationStrategy.java b/src/main/java/laddergame/domain/ladder/strategy/ManualLadderCreationStrategy.java new file mode 100644 index 0000000..5a4747d --- /dev/null +++ b/src/main/java/laddergame/domain/ladder/strategy/ManualLadderCreationStrategy.java @@ -0,0 +1,9 @@ +package laddergame.domain.ladder.strategy; + +public class ManualLadderCreationStrategy implements LadderCreationStrategy { + + @Override + public boolean isConnectable() { + return true; + } +} diff --git a/src/main/java/laddergame/domain/ladder/strategy/RandomLadderCreationStrategy.java b/src/main/java/laddergame/domain/ladder/strategy/RandomLadderCreationStrategy.java new file mode 100644 index 0000000..5292251 --- /dev/null +++ b/src/main/java/laddergame/domain/ladder/strategy/RandomLadderCreationStrategy.java @@ -0,0 +1,11 @@ +package laddergame.domain.ladder.strategy; + +import java.util.Random; + +public class RandomLadderCreationStrategy implements LadderCreationStrategy{ + + @Override + public boolean isConnectable() { + return new Random().nextBoolean(); + } +} diff --git a/src/main/java/laddergame/domain/ladderheight/LadderHeight.java b/src/main/java/laddergame/domain/ladderheight/LadderHeight.java new file mode 100644 index 0000000..7694952 --- /dev/null +++ b/src/main/java/laddergame/domain/ladderheight/LadderHeight.java @@ -0,0 +1,24 @@ +package laddergame.domain.ladderheight; + +public class LadderHeight { + + private static final int LADDER_HEIGHT_MIN_LENGTH = 1; + + private final int height; + + public LadderHeight(int height) { + if (!isLongerThanMinLength(height)) { + throw new IllegalArgumentException(); + } + + this.height = height; + } + + private boolean isLongerThanMinLength(int height) { + return height >= LADDER_HEIGHT_MIN_LENGTH; + } + + public int getHeight() { + return height; + } +} diff --git a/src/main/java/laddergame/domain/player/Player.java b/src/main/java/laddergame/domain/player/Player.java new file mode 100644 index 0000000..38078ae --- /dev/null +++ b/src/main/java/laddergame/domain/player/Player.java @@ -0,0 +1,39 @@ +package laddergame.domain.player; + +import java.util.Objects; + +public class Player { + + public static final int PLAYER_NAME_MAX_LENGTH = 5; + + private final String name; + + public Player(final String name) { + if (isLongerThanMaxLength(name)) { + throw new IllegalArgumentException(); + } + + this.name = name; + } + + private boolean isLongerThanMaxLength(final String name) { + return name.length() > PLAYER_NAME_MAX_LENGTH; + } + + public String getName() { + return name; + } + + @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 Objects.hash(name); + } +} diff --git a/src/main/java/laddergame/domain/player/Players.java b/src/main/java/laddergame/domain/player/Players.java new file mode 100644 index 0000000..0f4998f --- /dev/null +++ b/src/main/java/laddergame/domain/player/Players.java @@ -0,0 +1,73 @@ +package laddergame.domain.player; + +import org.apache.commons.lang3.StringUtils; + +import java.util.*; +import java.util.stream.Collectors; + +public class Players { + + private List players; + + public Players(final List players) { + if (hasDuplicatedNames(players)) { + throw new IllegalArgumentException(); + } + + this.players = players; + } + + private boolean hasDuplicatedNames(final List players) { + Set nameSet = new HashSet<>(players); + return players.size() != nameSet.size(); + } + + public int size() { + return players.size(); + } + + public static List createPlayerList(String[] playerNames) { + return Arrays.stream(playerNames) + .map(String::trim) + .map(Player::new) + .collect(Collectors.toList()); + } + + public String getAlignedPlayerNames() { + List playerNames = playerNamesToList(); + + List alignedPlayerNames = alignPlayerNames(playerNames); + + return playerNamesToString(alignedPlayerNames); + } + + private List playerNamesToList() { + List playerNames = new ArrayList<>(); + + players.forEach(player -> playerNames.add(player.getName())); + return playerNames; + } + + private List alignPlayerNames(List playerNames) { + List alignedPlayerNames = new ArrayList<>(); + + for (String playerName : playerNames) { + String alignedPlayerName = alignPlayerName(playerName); + alignedPlayerNames.add(alignedPlayerName); + } + return alignedPlayerNames; + } + + private String alignPlayerName(String playerName) { + return StringUtils.leftPad(playerName, Player.PLAYER_NAME_MAX_LENGTH + 1); + } + + private String playerNamesToString(List playerNames) { + String alignedPlayerNames =""; + + for (String playerName : playerNames) { + alignedPlayerNames += playerName; + } + return alignedPlayerNames; + } +} diff --git a/src/main/java/laddergame/domain/result/Result.java b/src/main/java/laddergame/domain/result/Result.java new file mode 100644 index 0000000..2012033 --- /dev/null +++ b/src/main/java/laddergame/domain/result/Result.java @@ -0,0 +1,24 @@ +package laddergame.domain.result; + +public class Result { + + public static final int RESULT_NAME_MAX_LENGTH = 5; + + private final String name; + + public Result(final String name) { + if (isLongerThanMaxLength(name)) { + throw new IllegalArgumentException(); + } + + this.name = name; + } + + private boolean isLongerThanMaxLength(final String name) { + return name.length() > RESULT_NAME_MAX_LENGTH; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/laddergame/domain/result/Results.java b/src/main/java/laddergame/domain/result/Results.java new file mode 100644 index 0000000..20694b9 --- /dev/null +++ b/src/main/java/laddergame/domain/result/Results.java @@ -0,0 +1,29 @@ +package laddergame.domain.result; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class Results { + + private final List results; + + public Results(final List results, int playerCount) { + if (!haveSameCount(results.size(), playerCount)) { + throw new IllegalArgumentException(); + } + + this.results = results; + } + + public static List createResultList(String[] resultNames) { + return Arrays.stream(resultNames) + .map(String::trim) + .map(Result::new) + .collect(Collectors.toList()); + } + + private boolean haveSameCount(int resultCount, int playerCount) { + return resultCount == playerCount; + } +} diff --git a/src/main/java/laddergame/service/LadderGameService.java b/src/main/java/laddergame/service/LadderGameService.java new file mode 100644 index 0000000..e9e1917 --- /dev/null +++ b/src/main/java/laddergame/service/LadderGameService.java @@ -0,0 +1,40 @@ +package laddergame.service; + +import laddergame.domain.ladder.Ladder; +import laddergame.domain.ladder.strategy.LadderCreationStrategy; +import laddergame.domain.ladderheight.LadderHeight; +import laddergame.domain.player.Player; +import laddergame.domain.player.Players; +import laddergame.domain.result.Result; +import laddergame.domain.result.Results; + +import java.util.List; + +public class LadderGameService { + + private static final String NAME_DELIMITER = ","; + + public Players createPlayers(String playerNames) { + String[] parsedPlayerNames = parse(playerNames); + List playerList = Players.createPlayerList(parsedPlayerNames); + return new Players(playerList); + } + + public Results createResults(String gameResults, int playerCount) { + String[] parsedResultNames = parse(gameResults); + List resultList = Results.createResultList(parsedResultNames); + return new Results(resultList, playerCount); + } + + private String[] parse(String names) { + return names.split(NAME_DELIMITER); + } + + public LadderHeight createLadderHeight(int ladderHeightInput) { + return new LadderHeight(ladderHeightInput); + } + + public Ladder createLadder(Players players, LadderHeight ladderHeight, LadderCreationStrategy strategy) { + return Ladder.with(players, ladderHeight, strategy); + } +} diff --git a/src/main/java/laddergame/view/InputView.java b/src/main/java/laddergame/view/InputView.java new file mode 100644 index 0000000..735b631 --- /dev/null +++ b/src/main/java/laddergame/view/InputView.java @@ -0,0 +1,26 @@ +package laddergame.view; + +import java.util.Scanner; + +public class InputView { + + private static final Scanner SCANNER = new Scanner(System.in); + private static final String MESSAGE_GET_PLAYER_NAMES = "참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"; + private static final String MESSAGE_GET_GAME_RESULTS = "실행 결과를 입력하세요. (결과는 쉼표(,)로 구분하세요)"; + private static final String MESSAGE_GET_LADDER_HEIGHT = "최대 사다리 높이는 몇 개인가요?"; + + public String getPlayerNamesFromConsole() { + System.out.println(MESSAGE_GET_PLAYER_NAMES); + return SCANNER.nextLine(); + } + + public String getGameResultFromConsole() { + System.out.println(MESSAGE_GET_GAME_RESULTS); + return SCANNER.nextLine(); + } + + public int getLadderHeightFromConsole() { + System.out.println(MESSAGE_GET_LADDER_HEIGHT); + return SCANNER.nextInt(); + } +} diff --git a/src/main/java/laddergame/view/OutputView.java b/src/main/java/laddergame/view/OutputView.java new file mode 100644 index 0000000..26d407c --- /dev/null +++ b/src/main/java/laddergame/view/OutputView.java @@ -0,0 +1,46 @@ +package laddergame.view; + +import laddergame.domain.ladder.Ladder; +import laddergame.domain.ladder.LadderLine; +import laddergame.domain.player.Players; + +import java.util.List; + +public class OutputView { + + private static final String MESSAGE_GAME_RESULT = "실행결과"; + + public void printResult(Players players, Ladder ladder) { + printResultMessage(); + + printPlayerNames(players); + + printLadder(ladder); + } + + private void printResultMessage() { + System.out.println(); + System.out.println(MESSAGE_GAME_RESULT); + System.out.println(); + } + + private void printPlayerNames(Players players) { + String playerNames = players.getAlignedPlayerNames(); + + System.out.println(playerNames); + } + + private void printLadder(Ladder ladder) { + List lines = ladder.getLines(); + + for (LadderLine line : lines) { + printLadderLine(line); + } + } + + private void printLadderLine(LadderLine line) { + String lineString = line.lineToString(); + + System.out.println(lineString); + } +} diff --git a/src/test/java/laddergame/domain/ladder/LadderLineTest.java b/src/test/java/laddergame/domain/ladder/LadderLineTest.java new file mode 100644 index 0000000..cacaa66 --- /dev/null +++ b/src/test/java/laddergame/domain/ladder/LadderLineTest.java @@ -0,0 +1,45 @@ +package laddergame.domain.ladder; + +import laddergame.domain.ladder.strategy.ManualLadderCreationStrategy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class LadderLineTest { + + private LadderLine ladderLine; + + @BeforeEach + void setUp() { + List points = new ArrayList<>(); + points.add(Point.RIGHT); + points.add(Point.LEFT); + points.add(Point.RIGHT); + points.add(Point.LEFT); + points.add(Point.NONE); + + ladderLine = new LadderLine(points); + } + + @Test + void with() { + //given + LadderLine actualLadderLine = LadderLine.with(5, new ManualLadderCreationStrategy()); + + //then + assertThat(actualLadderLine).isEqualTo(ladderLine); + } + + @Test + void size() { + // given + LadderLine actualLadderLine = LadderLine.with(5, new ManualLadderCreationStrategy()); + + // then + assertThat(actualLadderLine.size()).isEqualTo(ladderLine.size()); + } +} \ No newline at end of file diff --git a/src/test/java/laddergame/domain/ladder/PointTest.java b/src/test/java/laddergame/domain/ladder/PointTest.java new file mode 100644 index 0000000..954d504 --- /dev/null +++ b/src/test/java/laddergame/domain/ladder/PointTest.java @@ -0,0 +1,42 @@ +package laddergame.domain.ladder; + +import laddergame.domain.ladder.strategy.ManualLadderCreationStrategy; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class PointTest { + + @Test + void generateFirstPoint() { + //given + Point firstPoint = Point.generateFirstPoint(new ManualLadderCreationStrategy()); + Point expected = Point.RIGHT; + + //then + assertThat(firstPoint).isEqualTo(expected); + } + + @Test + void generateLastPoint() { + //given + Point LastPoint = Point.generateLastPoint(Point.RIGHT); + Point expected = Point.LEFT; + + //then + assertThat(LastPoint).isEqualTo(expected); + } + + @Test + void decideNextPoint() { + //given + Point nextOfLeft = Point.LEFT.decideNextPoint(new ManualLadderCreationStrategy()); + Point nextOfRight = Point.RIGHT.decideNextPoint(new ManualLadderCreationStrategy()); + Point nextOfNone = Point.NONE.decideNextPoint(new ManualLadderCreationStrategy()); + + //then + assertThat(nextOfLeft).isEqualTo(Point.RIGHT); + assertThat(nextOfRight).isEqualTo(Point.LEFT); + assertThat(nextOfNone).isEqualTo(Point.RIGHT); + } +} \ No newline at end of file