diff --git a/.gitignore b/.gitignore index b63da455..9b3878c4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ build/ !**/src/test/**/build/ ### IntelliJ IDEA ### +.idea +.idea/workspace.xml .idea/modules.xml .idea/jarRepositories.xml .idea/compiler.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index e67596c4..00000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -java-lotto \ No newline at end of file diff --git a/.idea/aws.xml b/.idea/aws.xml deleted file mode 100644 index 99de319b..00000000 --- a/.idea/aws.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 611e7c8a..00000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index b9d0bedb..00000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index cc0a8fd0..00000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1672210428730 - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index ad6e6861..8004bec1 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,52 @@ * [텍스트와 이미지로 살펴보는 코드스쿼드의 온라인 코드 리뷰 과정](https://github.com/code-squad/codesquad-docs/blob/master/codereview/README.md) * [동영상으로 살펴보는 코드스쿼드의 온라인 코드 리뷰 과정](https://youtube.com/watch?v=lFinZfu3QO0&si=EnSIkaIECMiOmarE) + +## 요구사항 분석 + +- 사람과 사다리의 높이를 입력한다 + - 사람 `n`명과 사다리 높이 `m`을 입력한다 + - 입력값이 정수인지 검증한다 + - (추가) 사람의 이름을 입력한다 + - 사람의 이름이 5글자 이하인지 검증한다 + - 사람의 이름은 "`,`" 기준으로 구분한다 + +- 사다리의 라인을 랜덤하게 만든다 + - 라인은 랜덤값에 따라 있거나 없다 + - 라인이 존재한다면 "`-`"를 없으면 "` `"(공백문자)를 표시한다 + - 라인의 양 옆에는 "`|`"로 세로를 표시한다 + - (추가) 같은 높이에 라인이 겹치지 않아야 한다 +- 사다리 상태를 화면에 출력한다 + - (추가) 사람의 이름을 출력한다 + - 사람의 이름을 최대 5자로 정하므로 이에 따라 사다리의 폭도 넓게 출력한다 + +## 구현 계획 및 학습 계획 +### 3.7 화 오전 +-[x] 사다리를 나타낼 배열을 먼저 구현한다 +-[x] 사다리의 라인을 랜덤하게 구현한다 +### 3.7 화 오후 +-[x] 사다리의 모습을 출력한다 (3.7 화 오후까지) +-[x] 사람의 이름으로 사다리 배열을 구현한다 +### ~~3.8 수~~ 3.13 월 +-[ ] 같은 높이에서 라인이 겹치지 않으면서 랜덤하게 나타나도록 구현한다 +-[ ] 사람의 이름과 사다리를 출력한다 +-[ ] 로직을 구현하는 단위테스트를 추가한다 +### ~~3.8 목~~ 3.14 화 +-[ ] 한 라인의 좌표 값을 가지는 객체를 추가해 구현한다 +-[ ] 클래스들을 역할에 맞게 패키지 분리한다 +-[ ] 실행 결과 입력기능을 구현한다 +### ~~3.9 금~~ 3.15 수 +-[ ] 개인별 결과를 출력한다 +-[ ] 전체 참여자의 결과를 출력한다 +-[ ] 춘식이 입력시 프로그램을 종료하는 기능을 구현한다. +### 3.16 목 +-[ ] 자바 클린 코딩 기초 +-[ ] 자바 문자열 +-[ ] 리스트와 제네릭 +-[ ] Java Collection Framework +-[ ] TDD Basic +### 3.17 금 +-[ ] Java Exception +-[ ] Java Enum +-[ ] SparkJava +-[ ] JVM \ No newline at end of file diff --git a/src/main/java/kr/codesquad/Main.java b/src/main/java/kr/codesquad/Main.java index b5ec785b..1d627f89 100644 --- a/src/main/java/kr/codesquad/Main.java +++ b/src/main/java/kr/codesquad/Main.java @@ -1,7 +1,13 @@ package kr.codesquad; +import kr.codesquad.domain.LadderGame; +import kr.codesquad.domain.RandomLadderGenerator; +import kr.codesquad.view.Console; + public class Main { public static void main(String[] args) { - System.out.println("Hello world!"); + Console console = new Console(); + RandomLadderGenerator generator = new RandomLadderGenerator(); + new LadderGame(console, generator).run(); } } \ No newline at end of file diff --git a/src/main/java/kr/codesquad/domain/Ladder.java b/src/main/java/kr/codesquad/domain/Ladder.java new file mode 100644 index 00000000..3f831034 --- /dev/null +++ b/src/main/java/kr/codesquad/domain/Ladder.java @@ -0,0 +1,29 @@ +package kr.codesquad.domain; + +import java.util.ArrayList; + +public class Ladder { + private ArrayList ladder; + + public Ladder(ArrayList ladder) { + this.ladder = ladder; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (var line : ladder) { + sb.append(" "); // 사람 이름 5글자가 기준이므로 사다리 앞에 2칸의 간격 추가 + for (int i = 0; i < line.size(); i++) { + sb.append(line.hasBridge(i) ? "|-----" : "| "); + } + sb.append("\n"); + } + + return String.valueOf(sb); + } + + public ArrayList getLadder() { + return ladder; + } +} diff --git a/src/main/java/kr/codesquad/domain/LadderGame.java b/src/main/java/kr/codesquad/domain/LadderGame.java new file mode 100644 index 00000000..2526123e --- /dev/null +++ b/src/main/java/kr/codesquad/domain/LadderGame.java @@ -0,0 +1,87 @@ +package kr.codesquad.domain; + +import kr.codesquad.view.Console; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Optional; + +public class LadderGame implements Runnable { + private final int MIN_SIZE_OF_PEOPLE = 2; + private final int MIN_SIZE_OF_LADDER = 1; + private final int MAX_NAME_SIZE = 5; + private final Console console; + private final RandomLadderGenerator generator; + + public LadderGame(Console console, RandomLadderGenerator generator) { + this.console = console; + this.generator = generator; + } + + @Override + public void run() { + ArrayList players = inputNamesToPlayers(); + int ladderSize = inputNumberToLadderSize(); + + Ladder ladder = generator.generate(players.size(), ladderSize); + printPlayerList(players); // 사람 이름 출력 + System.out.println(ladder); // 사다리 출력 + } + + private void printPlayerList(ArrayList playerArrayList) { + StringBuilder sb = new StringBuilder(); + for (var player : playerArrayList) { + sb.append(alignCenter(player.getName())).append(" "); + } + System.out.println(sb); + } + + // 5글자를 기준으로 이름을 가운데로 정렬하는 메소드 + private String alignCenter(String name) { + StringBuilder sb = new StringBuilder(); + while (sb.length() < (MAX_NAME_SIZE - name.length()) / 2) sb.append(" "); + sb.append(name); + while (sb.length() < MAX_NAME_SIZE) sb.append(" "); + return sb.toString(); + } + + private ArrayList inputNamesToPlayers() { + var input = console.input("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"); + if (input.isEmpty()) return inputNamesToPlayers(); + var parsedValue = parsePlayerList(input.get()); + return parsedValue.orElseGet(this::inputNamesToPlayers); + } + + private Optional> parsePlayerList(String inputString) { + String[] playerNames = inputString.split(","); + if (!isValidPlayer(playerNames)) return Optional.empty(); + + ArrayList result = new ArrayList<>(); + for (int i = 0; i < playerNames.length; i++) { + result.add(new Player(playerNames[i], i)); + } + return Optional.of(result); + } + + private boolean isValidPlayer(String[] playerNames) { + if (playerNames.length < MIN_SIZE_OF_PEOPLE) return false; // 최소 플레이어수 확인 + return Arrays.stream(playerNames).allMatch(name -> name.length() < MAX_NAME_SIZE); + } + + private Optional parseLadderSize(String inputString) { + try { + int result = Integer.parseInt(inputString); + if (result < MIN_SIZE_OF_LADDER) return Optional.empty(); + return Optional.of(result); + } catch (NumberFormatException e) { + return Optional.empty(); + } + } + + private int inputNumberToLadderSize() { + var input = console.input("최대 사다리 높이는 몇 개인가요?"); + if (input.isEmpty()) return inputNumberToLadderSize(); + var parsedValue = parseLadderSize(input.get()); + return parsedValue.orElseGet(this::inputNumberToLadderSize); + } +} diff --git a/src/main/java/kr/codesquad/domain/Line.java b/src/main/java/kr/codesquad/domain/Line.java new file mode 100644 index 00000000..3f225e1c --- /dev/null +++ b/src/main/java/kr/codesquad/domain/Line.java @@ -0,0 +1,19 @@ +package kr.codesquad.domain; + +import java.util.ArrayList; + +public class Line { + private ArrayList points = new ArrayList<>(); + + public boolean hasBridge(int i) { + return points.get(i); + } + + public int size() { + return points.size(); + } + + public void add(boolean point) { + points.add(point); + } +} diff --git a/src/main/java/kr/codesquad/domain/Player.java b/src/main/java/kr/codesquad/domain/Player.java new file mode 100644 index 00000000..d5b114a3 --- /dev/null +++ b/src/main/java/kr/codesquad/domain/Player.java @@ -0,0 +1,33 @@ +package kr.codesquad.domain; + +public class Player { + private final String name; + private int col; + private int row; + + public Player(String name, int col) { + this.name = name; + this.col = col; + this.row = 0; + } + + public String getName() { + return name; + } + + public int getCol() { + return col; + } + + public int getRow() { + return row; + } + + public void setCol(int col) { + this.col = col; + } + + public void setRow(int row) { + this.row = row; + } +} diff --git a/src/main/java/kr/codesquad/domain/RandomLadderGenerator.java b/src/main/java/kr/codesquad/domain/RandomLadderGenerator.java new file mode 100644 index 00000000..ec187754 --- /dev/null +++ b/src/main/java/kr/codesquad/domain/RandomLadderGenerator.java @@ -0,0 +1,23 @@ +package kr.codesquad.domain; + +import java.util.ArrayList; +import java.util.Random; + +public class RandomLadderGenerator { + private final Random random = new Random(); + + public Ladder generate(int sizeOfPeople, int sizeOfLadder) { + ArrayList ladder = new ArrayList<>(); + while (sizeOfLadder-- > 0) { + Line line = new Line(); + line.add(random.nextBoolean()); + for (int col = 1; col < sizeOfPeople - 1; col++) { + // 이전 열의 라인 유무확인 + line.add(line.hasBridge(col - 1) ? false : random.nextBoolean()); + } + line.add(false); // 마지막 열은 라인이 없으므로 false를 저장한다. + ladder.add(line); + } + return new Ladder(ladder); + } +} diff --git a/src/main/java/kr/codesquad/view/Console.java b/src/main/java/kr/codesquad/view/Console.java new file mode 100644 index 00000000..9afee241 --- /dev/null +++ b/src/main/java/kr/codesquad/view/Console.java @@ -0,0 +1,23 @@ +package kr.codesquad.view; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Optional; + +public class Console { + private final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + + public Optional input(String prompt) { + System.out.println(prompt); + try { + return Optional.of(br.readLine()); + } catch (IOException e) { + return Optional.empty(); + } + } + + public void inputError() { + System.out.println("입력이 잘못되었습니다."); + } +} diff --git a/src/test/java/kr/codesquad/RandomLadderGeneratorTest.java b/src/test/java/kr/codesquad/RandomLadderGeneratorTest.java new file mode 100644 index 00000000..e8e86838 --- /dev/null +++ b/src/test/java/kr/codesquad/RandomLadderGeneratorTest.java @@ -0,0 +1,38 @@ +package kr.codesquad; + +import kr.codesquad.domain.Line; +import kr.codesquad.domain.RandomLadderGenerator; +import org.assertj.core.api.SoftAssertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +class RandomLadderGeneratorTest { + @Test + @DisplayName("랜덤하게 생성시 사다리의 라인은 겹치지 않습니다.") + public void testRandomGenerate() { + RandomLadderGenerator generator = new RandomLadderGenerator(); + var ladder = generator.generate(5, 5); + + SoftAssertions softAssertions = new SoftAssertions(); + for (Line line : ladder.getLadder()) { + for (int i = 1; i < line.size() - 1; i++) { + // 현재위치와 앞 또는 뒤 위치에 라인이 모두 있는 경우는 없습니다. + softAssertions.assertThat(line.hasBridge(i)).isTrue() + .isNotEqualTo(line.hasBridge(i - 1)) + .isNotEqualTo(line.hasBridge(i + 1)); + } + } + } + + @Test + @DisplayName("사다리의 마지막 열은 라인이 없습니다.") + public void testLastRow() { + RandomLadderGenerator generator = new RandomLadderGenerator(); + var ladder = generator.generate(5, 5); + for (Line line : ladder.getLadder()) { + assertFalse(line.hasBridge(4)); + } + } +} \ No newline at end of file