diff --git a/README.md b/README.md new file mode 100644 index 00000000..d5da63b7 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# java-ladder + +# 기능요구사항 +## 참여자 +- [x] 참여할 사람의 이름을 입력받는다. +- [x] 참여하는 사람을 따로 관리한다. +## 사다리 결과 +- [x] 실행결과를 입력받는다. +- [x] 실행결과를 따로 관리한다. +## 사다리 +- [x] 최대 사다리 높이를 입력받는다. +- [x] 최대 사다리 높이 만큼 사다리를 생성한다. +- [x] 사다리를 출력한다. +## 실행 결과 +- [x] 사람마다 실행 결과를 따로 저장한다. +- [x] 특정 사람의 실행 결과를 출력한다. +## 종료 +- [x] "all"이 입력될 경우 실행을 종료한다. + +# 예외처리 +- [x] 참가자 이름이 5자 넘어가는 경우 오류 생성 +- [x] 특정 사용자 결과를 찾는 데 특정 사용자가 없는 경우 예외 발생 diff --git a/src/main/java/LadderApplication.java b/src/main/java/LadderApplication.java new file mode 100644 index 00000000..7e208afc --- /dev/null +++ b/src/main/java/LadderApplication.java @@ -0,0 +1,8 @@ +import controller.LadderController; + +public class LadderApplication { + public static void main(String[] args) { + LadderController controller = new LadderController(); + controller.playLadderGame(); + } +} diff --git a/src/main/java/controller/LadderController.java b/src/main/java/controller/LadderController.java new file mode 100644 index 00000000..b8fa5741 --- /dev/null +++ b/src/main/java/controller/LadderController.java @@ -0,0 +1,76 @@ +package controller; + +import model.ladder.Ladder; +import model.ladder.LadderRow; +import model.player.Player; +import model.player.PlayerResultDto; +import model.player.PlayerResults; +import model.player.Players; +import model.tool.Splitter; +import view.InputView; +import view.OutputView; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + + +public class LadderController { + static final InputView inputView = new InputView(); + static final Splitter splitter = new Splitter(); + static final OutputView outputView = new OutputView(); + + private Players createPlayers() { + String playerNamesBeforeSplit = inputView.inputPlayers(); + String[] playerNames = splitter.splitWithComma(playerNamesBeforeSplit); + Players players = new Players(playerNames); + return players; + } + + private List createLadderResults() { + String trialResultsBeforeSplit = inputView.inputResults(); + String[] trialResults = splitter.splitWithComma(trialResultsBeforeSplit); + List ladderResults = Arrays.asList(trialResults); + return ladderResults; + } + + private Ladder createLadder(int columnSize) { + List ladderResults = createLadderResults(); + int rowSize = inputView.inputLadderHeight(); + List ladderRows = new ArrayList<>(); + for (int i = 0; i < rowSize; i++) { + LadderRow ladderRow = new LadderRow(columnSize); + ladderRows.add(ladderRow); + } + Ladder ladder = new Ladder(ladderRows, ladderResults); + return ladder; + } + + private PlayerResults createPlayerResults(Players players, Ladder ladder) { + List playerInventory = players.getPlayerInventory(); + List playerResultsInventory = ladder.decidePlayerResults(playerInventory); + PlayerResults playerResults = new PlayerResults(playerResultsInventory); + return playerResults; + } + + private void printLadder(Players players, Ladder ladder) { + List ladderRows = ladder.getLadderRows(); + List ladderResults = ladder.getLadderResults(); + List playerInventory = players.getPlayerInventory(); + OutputView outputView = new OutputView(); + outputView.outputLadder(playerInventory, ladderRows, ladderResults); + } + + public void playLadderGame() { + Players players = createPlayers(); + Ladder ladder = createLadder(players.getPlayerInventory().size() - 1); + PlayerResults playerResults = createPlayerResults(players, ladder); + printLadder(players, ladder); + String playerName; + do { + playerName = inputView.inputPlayerToWantResult(); + outputView.outputPlayerResult(playerResults, playerName); + } while (!playerName.equals("all")); + } +} diff --git a/src/main/java/model/ladder/Ladder.java b/src/main/java/model/ladder/Ladder.java new file mode 100644 index 00000000..5e5fc126 --- /dev/null +++ b/src/main/java/model/ladder/Ladder.java @@ -0,0 +1,62 @@ +package model.ladder; + +import model.player.Player; +import model.player.PlayerResultDto; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Ladder { + private List ladderRows; + private List ladderResults; + + public Ladder(List ladderRows, List ladderResults) { + this.ladderRows = ladderRows; + this.ladderResults = ladderResults; + } + + private LadderMove changePosition(List points, int columnPosition) { + if (columnPosition > 0 && points.get(columnPosition - 1)) { + return LadderMove.LEFT; + } + if (columnPosition < points.size() && points.get(columnPosition)) { + return LadderMove.RIGHT; + } + return LadderMove.DOWN; + } + + private int moveColumn(List points, int columnPosition) { + LadderMove moveDirection = changePosition(points, columnPosition); + return columnPosition + moveDirection.getOffset(); + } + + private int moveRow(List ladderRows, int columnPosition) { + for (LadderRow ladderRow : ladderRows) { + List points = ladderRow.getPoints(); + columnPosition = moveColumn(points, columnPosition); + } + return columnPosition; + } + + public List decidePlayerResults(List playerInventory) { + List playersInventory = new ArrayList<>(); + for (Player player : playerInventory) { + int playerPosition = player.getPosition(); + int columnPosition = moveRow(ladderRows, playerPosition); + String playerName = player.getName(); + String playerResult = ladderResults.get(columnPosition); + playersInventory.add(new PlayerResultDto(playerName, playerResult)); + } + return playersInventory; + } + + public List getLadderRows() { + return ladderRows; + } + + public List getLadderResults() { + return ladderResults; + } +} diff --git a/src/main/java/model/ladder/LadderMove.java b/src/main/java/model/ladder/LadderMove.java new file mode 100644 index 00000000..1ce685f2 --- /dev/null +++ b/src/main/java/model/ladder/LadderMove.java @@ -0,0 +1,17 @@ +package model.ladder; + +public enum LadderMove { + RIGHT(1), + LEFT(-1), + DOWN(0); + + private final int offset; + + LadderMove(int offset) { + this.offset = offset; + } + + public int getOffset() { + return offset; + } +} diff --git a/src/main/java/model/ladder/LadderPoint.java b/src/main/java/model/ladder/LadderPoint.java new file mode 100644 index 00000000..a02f830a --- /dev/null +++ b/src/main/java/model/ladder/LadderPoint.java @@ -0,0 +1,16 @@ +package model.ladder; + +public enum LadderPoint { + CONNECTED(true), + DISCONNECTED(false); + + private final Boolean isConnected; + + LadderPoint(Boolean isConnected) { + this.isConnected = isConnected; + } + + public Boolean getConnected() { + return isConnected; + } +} diff --git a/src/main/java/model/ladder/LadderRow.java b/src/main/java/model/ladder/LadderRow.java new file mode 100644 index 00000000..4c15256c --- /dev/null +++ b/src/main/java/model/ladder/LadderRow.java @@ -0,0 +1,32 @@ +package model.ladder; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class LadderRow { + private List points = new ArrayList<>(); + + public LadderRow(int columnSize) { + for (int columnPosition = 0; columnPosition < columnSize; columnPosition++) { + boolean randomBoolean = chooseLadderPoint(columnPosition); + this.points.add(columnPosition, randomBoolean); + } + } + + private LadderPoint randomConnectedOrDisconnected() { + if (ThreadLocalRandom.current().nextDouble() < 0.5) return LadderPoint.CONNECTED; + return LadderPoint.DISCONNECTED; + } + + public boolean chooseLadderPoint(int columnPosition) { + if (columnPosition > 0 && points.get(columnPosition - 1)) + return LadderPoint.DISCONNECTED.getConnected(); + return randomConnectedOrDisconnected().getConnected(); + + } + + public List getPoints() { + return points; + } +} diff --git a/src/main/java/model/player/Player.java b/src/main/java/model/player/Player.java new file mode 100644 index 00000000..dde71505 --- /dev/null +++ b/src/main/java/model/player/Player.java @@ -0,0 +1,24 @@ +package model.player; + +public class Player { + private static final int NAME_LENGTH_LIMIT = 5; + + private int position; + private String name; + + public Player(int position, String name) { + if (name.length() > NAME_LENGTH_LIMIT) { + throw new IllegalArgumentException("참가자 이름이 " + NAME_LENGTH_LIMIT + "자 초과입니다."); + } + this.position = position; + this.name = name; + } + + public int getPosition() { + return position; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/model/player/PlayerResultDto.java b/src/main/java/model/player/PlayerResultDto.java new file mode 100644 index 00000000..73c92954 --- /dev/null +++ b/src/main/java/model/player/PlayerResultDto.java @@ -0,0 +1,19 @@ +package model.player; + +public class PlayerResultDto { + private String playerName; + private String playerResult; + + public PlayerResultDto(String playerName, String playerResult) { + this.playerName = playerName; + this.playerResult = playerResult; + } + + public String getPlayerName() { + return playerName; + } + + public String getPlayerResult() { + return playerResult; + } +} diff --git a/src/main/java/model/player/PlayerResults.java b/src/main/java/model/player/PlayerResults.java new file mode 100644 index 00000000..d95bf093 --- /dev/null +++ b/src/main/java/model/player/PlayerResults.java @@ -0,0 +1,24 @@ +package model.player; + +import java.util.List; +import java.util.Map; + +public class PlayerResults { + private List PlayerResultsInventory; + + public PlayerResults(List playerResultsInventory) { + PlayerResultsInventory = playerResultsInventory; + } + + public String getPlayerResult(String playerName) { + String result = null; + for (PlayerResultDto playerResultDto : PlayerResultsInventory) { + if (playerName.equals(playerResultDto.getPlayerName())) result = playerResultDto.getPlayerResult(); + } + return result; + } + + public List getPlayerResultsInventory() { + return PlayerResultsInventory; + } +} diff --git a/src/main/java/model/player/Players.java b/src/main/java/model/player/Players.java new file mode 100644 index 00000000..98068ebd --- /dev/null +++ b/src/main/java/model/player/Players.java @@ -0,0 +1,21 @@ +package model.player; + +import model.tool.Splitter; + +import java.util.ArrayList; +import java.util.List; + +public class Players { + private List playerInventory= new ArrayList<>(); + + public Players(String [] playerNames){ + for (int i = 0; i < playerNames.length; i++) { + Player player = new Player(i, playerNames[i]); + playerInventory.add(player); + } + } + + public List getPlayerInventory() { + return playerInventory; + } +} diff --git a/src/main/java/model/tool/Splitter.java b/src/main/java/model/tool/Splitter.java new file mode 100644 index 00000000..c5e6a8ba --- /dev/null +++ b/src/main/java/model/tool/Splitter.java @@ -0,0 +1,13 @@ +package model.tool; + +public class Splitter { + final static private String SPLIT_SIGN = ","; + + public String[] splitWithComma(String beforeSplit) { + String[] afterSplit = beforeSplit.split(SPLIT_SIGN); + for (int i = 0; i < afterSplit.length; i++) { + afterSplit[i] = afterSplit[i].trim(); + } + return afterSplit; + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 00000000..be4949e3 --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,27 @@ +package view; + +import java.util.Scanner; + +public class InputView { + private static final Scanner scanner = new Scanner(System.in); + + public static String inputPlayers(){ + System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"); + return scanner.nextLine(); + } + + public static String inputResults(){ + System.out.println("\n"+"실행 결과를 입력하세요. (결과는 쉼표(,)로 구분하세요)"); + return scanner.nextLine(); + } + + public static Integer inputLadderHeight(){ + System.out.println("\n"+"최대 사다리 높이는 몇 개인가요?"); + return Integer.parseInt(scanner.nextLine()); + } + + public static String inputPlayerToWantResult(){ + System.out.println("\n"+"결과를 보고 싶은 사람은?"); + return scanner.nextLine(); + } +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 00000000..c0b31aa6 --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,84 @@ +package view; + +import model.player.Player; +import model.ladder.LadderRow; +import model.player.PlayerResultDto; +import model.player.PlayerResults; + +import java.util.List; +import java.util.Map; + +public class OutputView { + private static final int COLUMN_LENGTH = 5; + + private static void outputPlayer(List playerInventory) { + for (Player player : playerInventory) { + String playerName = player.getName(); + System.out.printf("%" + COLUMN_LENGTH + "s", playerName); + System.out.printf(" "); + } + } + + private static void outputLadderRow(List points) { + System.out.println(); + System.out.print(" |"); + for (int i = 0; i < points.size(); i++) { + outputLadderColumn(points.get(i)); + } + } + + private static void outputLadderColumn(Boolean bool) { + if (bool == true) { + System.out.print("-----|"); + } + if (bool == false) { + System.out.print(" |"); + } + } + + public static void outputLadder(List playerInventory, List ladderRows, List trialResults) { + System.out.println("\n" + "사다리 결과" + "\n"); + outputPlayer(playerInventory); + for (int i = 0; i < ladderRows.size(); i++) { + List points = ladderRows.get(i).getPoints(); + outputLadderRow(points); + } + outputResults(trialResults); + System.out.println(); + } + + private static void outputResults(List trialResults) { + System.out.println(); + for (String trialResult : trialResults) { + System.out.printf("%" + COLUMN_LENGTH + "s", trialResult); + System.out.printf(" "); + } + } + + private static void outputAllPlayersResult(PlayerResults playerResults) { + List PlayerResultsInventory = playerResults.getPlayerResultsInventory(); + for (PlayerResultDto playerResultDto : PlayerResultsInventory) { + String playerName = playerResultDto.getPlayerName(); + String playerResult = playerResultDto.getPlayerResult(); + System.out.println(playerName + " : " + playerResult); + } + } + + private static void outputCertainPlayerResult(PlayerResults playerResults, String playerName) { + String playerResult = playerResults.getPlayerResult(playerName); + if (playerResult == null) { + System.out.println("존재하지 않는 사용자입니다."); + } else if (playerResult != null) { + System.out.println(playerName + " : " + playerResult); + } + } + + public static void outputPlayerResult(PlayerResults playerResults, String playerName) { + System.out.println("\n" + "실행결과"); + if (playerName.equals("all")) { + outputAllPlayersResult(playerResults); + return; + } + outputCertainPlayerResult(playerResults, playerName); + } +} diff --git a/src/test/java/model/PlayerTest.java b/src/test/java/model/PlayerTest.java new file mode 100644 index 00000000..f125e2fc --- /dev/null +++ b/src/test/java/model/PlayerTest.java @@ -0,0 +1,23 @@ +package model; + +import model.player.Player; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +public class PlayerTest { + @Test + void 이름이_5자_이하일_때_오류가_안_발생하는_지_확인(){ + //Given & When & Then + assertDoesNotThrow(() -> new Player(5,"abcd")); + } + + @Test + void 이름이_5자_초과일_때_오류가_발생하는_지_확인() { + //Given & When & Then + Assertions.assertThatThrownBy(() -> new Player(5,"abcdef")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("참가자 이름이 5자 초과입니다."); + } +} diff --git a/src/test/java/model/SplitterTest.java b/src/test/java/model/SplitterTest.java new file mode 100644 index 00000000..3aec1868 --- /dev/null +++ b/src/test/java/model/SplitterTest.java @@ -0,0 +1,26 @@ +package model; + +import model.tool.Splitter; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.Test; + +public class SplitterTest { + @Test + void 이름을_구분자_기준으로_잘_자르는_지_확인(){ + //Given + Splitter splitter = new Splitter(); + SoftAssertions softly = new SoftAssertions(); + String beforeSplit = "참여자1, 참여자2, 참여자3,참여자4"; + + //When + String[] afterSplit = splitter.splitWithComma(beforeSplit); + + //Then + softly.assertThat(afterSplit.length).isEqualTo(4); + softly.assertThat(afterSplit[0]).isEqualTo("참여자1"); + softly.assertThat(afterSplit[1]).isEqualTo("참여자2"); + softly.assertThat(afterSplit[2]).isEqualTo("참여자3"); + softly.assertThat(afterSplit[3]).isEqualTo("참여자4"); + softly.assertAll(); + } +}