Skip to content

Commit

Permalink
[2단계 - 사다리 게임 실행] 허브(방대의) 미션 제출합니다. (#234)
Browse files Browse the repository at this point in the history
* refactor: 사다리높이 입력 예외 메시지가 자세한 내용을 출력하도록 수정

* refactor: 반환할 때만 불변 리스트로 생성하도록 변경

* feat: 가로 라인(Line)을 생성하는 정적 팩터리 메서드 추가

* refactor: 가로 라인(Line) 정적 팩터리 메서드 적용

* feat: 사다리(Ladder)를 생성하는 정적 팩터리 메서드 추가

* feat: 사다리를 생성하는 정적 팩터리 메서드 적용

* refactor: Players 생성할 때 정적 팩터리 메서드 적용

* style: booleanGenerator 매개변수명 변경

* refactor: OutputView 가독성 개선

* refactor: LadderGame 정적 팩터리 메서드 적용

* refactor: 불필요한 코드 제거

* docs: 2단계 요구사항 추가

* refactor: 참가자의 이름에 대한 예외 메시지 수정

* feat: 실행 결과(Item)를 표현하는 클래스 추가

* feat: Player이 이름 검증시 null check 기능 추가

* feat: Item의 일급 컬렉션 추가

* feat: 실행결과 입력 기능 추가

* refactor: final 빠진 부분 추가

* feat: LadderGame Items 필드 추가

* feat: 위치를 표현하는 클래스 추가

* feat: 이름을 표현하는 Name 클래스 추가

* feat: 참가자와 실행결과에 Position 적용

* feat: 참가자수 상한선 설정

* feat: 이전 위치, 다음 위치를 반환하는 기능 추가

* feat: 입력한 값까지의 Position을 반환하는 메서드 추가

* feat: 가로라인 한 줄에 대한 사다리 게임을 진행하는 기능 추가

* feat: 한 위치에 대한 사다리 게임 진행 기능 추가

* style: 메서드 위치 변경

* feat: 게임 결과를 위한 클래스 추가

* refactor: Items, Players 저장시 Map을 사용하도록 수정

* refactor: 게임 결과의 타입 변경

* feat: 예약어는 이름으로 사용하지 못하도록 하는 기능 추가

* feat: 모든 참가자에 대한 사다리 게임을 진행하는 기능 추가

* feat: 사다리의 출력 문자열을 생성하는 클래스 추가

* feat: 실행결과 출력 기능 추가

* feat: 사다리게임 결과 출력 기능 추가

* refactor: 문자열 비교 위치 변경

* style: equals랑 getter 순서 변경

* feat: 사다리 높이 원시값 포장

* style: 공백 제거

* feat: 사용자의 이름을 원시값 포장

* refactor: players가 리스트를 사용하도록 수정

* feat: Item 이름에 대한 원시값 포장 클래스 추가

* refactor: Item 이름에 대한 원시값 포장 클래스 적용

* feat: 입력받은 위치와 같은 위치인지 확인하는 메서드 추가

* feat: 참가자가 사다리를 받아 사다리 타기를 진행하는 기능 추가

* feat: 모든 참가자가 사다리 타기를 진행하는 기능 추가

* refactor: LadderGameResult의 타입 변경

- Player, Item -> String, String

* refactor: players에 ladder를 넘겨주는 방법으로 사다리 게임 진행 변경

* remove: 사용하지 않는 메서드 제거

* style: 도메인 패키지 분리

* refactor: 사다리 게임 결과 타입 재변경

* remove: 사용하지 않는 equals & hashcode 제거

* docs: 요구사항 정리

* refactor: final 빠진 부분 추가

* refactor: Items를 생성하는 정적팩터리 메서드명 of로 변경

* refactor: OutputView로 객체 바로 전달하도록 수정

* refactor: Position의 생성 책임을 Items에서 Item에게 할당

* refactor: Position의 생성 책임을 Players에서 Player에게 할당

* refactor: 이름을 확인할 때 getter가 아닌 메시지를 보내도록 수정

* refactor: 깊은 고민없이 패키지 분리한 부분 다시 원래대로 변경

* refactor: BooleanGenerator와 구현체 util 패키지로 이동

* refactor: 이름에 대한 테스트명 수정

* refactor: 예외 메시지 package-private으로 변경

* fix: 결과 확인할 때 존재하지 않는 이름 입력시 종료되는 부분 수정

* refactor: 결과를 위한 입력 검증 부분 최적화

* refactor: 불필요한 this 제거

* refactor: LadderMessageGenerator 인스턴스생성 불가능 하도록 수정

* refactor: LadderGameCommand 분리

* test: DisplayNameGeneration 빠진 부분 추가

* refactor: Position을 사용하는 입장에서 명확하도록 변경
  • Loading branch information
greeng00se authored Feb 27, 2023
1 parent 6d246ae commit 40b9a10
Show file tree
Hide file tree
Showing 36 changed files with 1,345 additions and 212 deletions.
65 changes: 50 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,43 +14,78 @@ graph TD
LadderGameController --> LadderGame
LadderGame --> Ladder
LadderGame --> Players
LadderGame --> Items
Players --> Player
Ladder --> Line
Line --> LineStatus
Line --> Position
Players --> Ladder
Player --> Ladder
Item --> Position
Player --> Position
LadderGame --> LadderGameResult
Items --> Item --> ItemName
Players --> Player --> PlayerName
LadderGameController --> InputView
LadderGameController --> OutputView
Ladder --> Line
Line --> LineStatus
OutputView --> LadderMessageGenerator
```

### 참가자

- [x] 참가자는 이름을 가진다.
- [x] 이름은 1자 이상, 5자 이하의 길이를 가진다.
- [x] 이름이 없거나, 5자를 초과하는 경우 예외를 던진다.
- [x] 이름이 없거나, 5자를 초과하는 경우 예외를 던진다.
- [x] 참가자들의 이름은 중복될 수 없다.
- [x] 중복된 이름이 존재하는 경우 예외를 던진다.
- [x] 참가자는 최소 2명 이어야 한다.
- [x] 참가자가 1명 이하인 경우 예외를 던진다.
- [x] 참가자들의 이름은 중복될 수 없다.
- [x] 중복된 이름이 존재하는 경우 예외를 던진다.
- [x] 참가자는 최대 20명 이어야 한다.
- [x] 참가자가 20명을 초과하는 경우 예외를 던진다.
- [x] 참가자는 위치를 가진다.
- [x] 위치 값은 0부터 시작한다.

### 아이템(실행결과)

- [x] 아이템은 이름을 가진다.
- [x] 이름 길이는 참가자의 이름과 동일하게 1자 이상, 5자 이하의 길이를 가진다.
- [x] 아이템은 참가인원과 동일한 개수가 있어야 한다.
- [x] 아이템은 위치를 가진다.
- [x] 위치 값은 0부터 시작한다.

### 사다리
### 사다리 게임

- [x] 사다리는 높이를 가진다.
- [x] 사다리 가로 라인을 가진다.
- [x] 사다리 가로 라인의 상태가 연속으로 `연결됨` 상태일 수 없다.
- [x] 각각의 가로 라인은 상태를 가진다.
- [x] 연결됨, 연결되지 않음으로 구분된다.
- [x] 높이는 1이상, 100이하의 값이어야 한다.
- [x] 사다리의 높이만큼 가로 라인을 가진다.
- [x] 각각의 가로 라인은 `사다리 게임 참가자 - 1` 너비만큼의 연결 상태를 가진다.
- [x] `사다리 게임 참가자 - 1` 만큼의 위치값을 가지고 각 위치에 연결상태가 존재한다.
- [x] 연결 상태는 연결됨, 연결되지 않음으로 구분된다.
- [x] 가로 라인의 연결 상태가 연속으로 `연결됨` 일 수 없다.
- [x] 사다리 게임을 진행한다.
- [x] 모든 참가자에 대한 결과를 반환한다.

### 입력

- [x] 사람 이름은 쉼표(,)를 기준으로 구분하여 입력받는다.
- [x] 이름의 앞뒤 공백을 제거한다.
- [x] 참가자 이름은 쉼표(,)를 기준으로 구분하여 입력받는다.
- [x] 이름의 앞뒤 공백을 제거한다.
- [x] 결과명은 쉼표(,)를 기준으로 구분하여 입력받는다.
- [x] 결과명의 앞뒤 공백을 제거한다.
- [x] 최대 사다리 높이를 입력받는다.
- [x] 1보다 작은 값을 입력하는 경우 예외를 던진다.
- [x] 1보다 작은 값을 입력 받거나, 100보다 큰 값을 입력받는 경우 예외를 던진다.
- [x] 실행결과를 보고 싶은 참가자명을 입력받는다.

### 출력

- [x] 최대 사람이름을 기준으로 사다리 폭이 넓어진다.
- [x] 가장 긴 사람 이름을 기준으로 사다리 폭이 넓어진다. 예) 가장 긴 사람이름 5, 사다리 폭 5
- [x] 실행 결과를 이용하여 해당 참가자에 대한 결과를 출력한다.


20 changes: 4 additions & 16 deletions src/main/java/ladder/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import java.util.Scanner;
import ladder.controller.LadderGameController;
import ladder.domain.RandomBooleanGenerator;
import ladder.util.RandomBooleanGenerator;
import ladder.view.InputView;
import ladder.view.OutputView;

Expand All @@ -11,23 +11,11 @@ public class Application {
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
final LadderGameController ladderGameController = new LadderGameController(
booleanGenerator(),
inputView(scanner),
outputView()
new RandomBooleanGenerator(),
new InputView(scanner),
new OutputView()
);
ladderGameController.run();
}
}

private static RandomBooleanGenerator booleanGenerator() {
return new RandomBooleanGenerator();
}

private static OutputView outputView() {
return new OutputView();
}

private static InputView inputView(final Scanner scanner) {
return new InputView(scanner);
}
}
19 changes: 19 additions & 0 deletions src/main/java/ladder/controller/LadderGameCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ladder.controller;

enum LadderGameCommand {
MULTIPLE,
SINGLE;

private static final String MULTIPLE_RESULT_RESERVED_NAME = "all";

public static LadderGameCommand from(final String name) {
if (MULTIPLE_RESULT_RESERVED_NAME.equals(name)) {
return MULTIPLE;
}
return SINGLE;
}

public boolean isContinued() {
return this == SINGLE;
}
}
50 changes: 29 additions & 21 deletions src/main/java/ladder/controller/LadderGameController.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package ladder.controller;

import java.util.List;
import ladder.domain.BooleanGenerator;
import java.util.function.Supplier;
import ladder.domain.Height;
import ladder.domain.Items;
import ladder.domain.LadderGame;
import ladder.domain.Line;
import ladder.domain.LadderGameResult;
import ladder.domain.Players;
import ladder.util.BooleanGenerator;
import ladder.view.InputView;
import ladder.view.OutputView;

Expand All @@ -26,35 +28,41 @@ public LadderGameController(

public void run() {
final LadderGame ladderGame = initialize();
final List<String> players = ladderGame.getPlayers();
final List<Line> ladder = ladderGame.getLadder();

outputView.printResult(players, ladder);
outputView.printLadderResult(ladderGame);
final LadderGameResult ladderGameResult = ladderGame.play();
printLadderGameResult(ladderGameResult);
}

private LadderGame initialize() {
final Players players = readPlayers();
final int height = readHeight();

return new LadderGame(booleanGenerator, players, height);
final Players players = repeatUntilGetValidInput(() -> Players.from(inputView.readPlayerNames()));
final Items items = repeatUntilGetValidInput(() -> Items.of(inputView.readItemNames(), players.count()));
final Height height = repeatUntilGetValidInput(() -> new Height(inputView.readLadderHeight()));

return LadderGame.initialize(players, booleanGenerator, height, items);
}

private Players readPlayers() {
private <T> T repeatUntilGetValidInput(final Supplier<T> inputReader) {
try {
final List<String> names = inputView.readPlayerNames();
return new Players(names);
return inputReader.get();
} catch (IllegalArgumentException e) {
outputView.printError(e.getMessage());
return readPlayers();
return repeatUntilGetValidInput(inputReader);
}
}

private int readHeight() {
try {
return inputView.readLadderHeight();
} catch (IllegalArgumentException e) {
outputView.printError(e.getMessage());
return readHeight();
private void printLadderGameResult(final LadderGameResult ladderGameResult) {
LadderGameCommand command = LadderGameCommand.SINGLE;
while (command.isContinued()) {
final String name = repeatUntilGetValidInput(() -> getValidPlayerName(ladderGameResult));
command = LadderGameCommand.from(name);
outputView.printLadderGameResult(ladderGameResult, name);
}
}

private String getValidPlayerName(final LadderGameResult ladderGameResult) {
final String name = inputView.readPlayerName();
ladderGameResult.validatePlayerName(name);
return name;
}

}
41 changes: 41 additions & 0 deletions src/main/java/ladder/domain/Height.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package ladder.domain;

public class Height {
private static final int HEIGHT_LOWER_BOUND = 1;
private static final int HEIGHT_UPPER_BOUND = 100;
static final String INVALID_HEIGHT_MESSAGE =
"높이는 " + HEIGHT_LOWER_BOUND + "이상, " + HEIGHT_UPPER_BOUND + "이하의 값이어야 합니다.";

private final int value;

public Height(final String value) {
this(parseInt(value));
}

private Height(final int value) {
validate(value);
this.value = value;
}

private static int parseInt(final String value) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(INVALID_HEIGHT_MESSAGE);
}
}

private static void validate(final int value) {
if (isInvalidHeight(value)) {
throw new IllegalArgumentException(INVALID_HEIGHT_MESSAGE);
}
}

private static boolean isInvalidHeight(final int value) {
return value < HEIGHT_LOWER_BOUND || HEIGHT_UPPER_BOUND < value;
}

public int getValue() {
return value;
}
}
23 changes: 23 additions & 0 deletions src/main/java/ladder/domain/Item.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package ladder.domain;

public class Item {
private final ItemName name;
private final Position position;

private Item(final ItemName name, final Position position) {
this.name = name;
this.position = position;
}

public static Item of(final String name, final int index) {
return new Item(new ItemName(name), Position.valueOf(index));
}

public boolean isSamePosition(final Position position) {
return this.position == position;
}

public String getName() {
return name.getValue();
}
}
28 changes: 28 additions & 0 deletions src/main/java/ladder/domain/ItemName.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ladder.domain;

public class ItemName {
private static final int NAME_LENGTH_UPPER_BOUND = 5;
static final String INVALID_NAME_LENGTH_MESSAGE =
"실행 결과명은 1자 이상, " + NAME_LENGTH_UPPER_BOUND + "자 이하여야 합니다.";

private final String value;

public ItemName(final String name) {
validate(name);
this.value = name;
}

private void validate(final String name) {
if (isInvalidNameLength(name)) {
throw new IllegalArgumentException(INVALID_NAME_LENGTH_MESSAGE);
}
}

private boolean isInvalidNameLength(final String name) {
return name == null || name.isBlank() || NAME_LENGTH_UPPER_BOUND < name.length();
}

public String getValue() {
return value;
}
}
44 changes: 44 additions & 0 deletions src/main/java/ladder/domain/Items.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ladder.domain;

import static java.util.stream.Collectors.toUnmodifiableList;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Items {
static final String INVALID_ITEM_COUNT_MESSAGE = "참가인원과 동일한 개수의 실행결과를 입력해야 합니다.";
static final String INVALID_ITEM_MESSAGE = "해당 위치에 있는 아이템이 존재하지 않습니다.";

private final List<Item> items;

private Items(final List<Item> items) {
this.items = items;
}

public static Items of(final List<String> names, int playerCount) {
validateItemCount(names, playerCount);
return IntStream.range(0, names.size())
.mapToObj(index -> Item.of(names.get(index), index))
.collect(Collectors.collectingAndThen(Collectors.toList(), Items::new));
}

private static void validateItemCount(final List<String> items, final int playerCount) {
if (items.size() != playerCount) {
throw new IllegalArgumentException(INVALID_ITEM_COUNT_MESSAGE);
}
}

public Item findByPosition(final Position position) {
return items.stream()
.filter(item -> item.isSamePosition(position))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(INVALID_ITEM_MESSAGE));
}

public List<String> getNames() {
return items.stream()
.map(Item::getName)
.collect(toUnmodifiableList());
}
}
Loading

0 comments on commit 40b9a10

Please sign in to comment.