Skip to content

Commit

Permalink
[사다리 게임] 베디 미션 제출합니다 (#25)
Browse files Browse the repository at this point in the history
* feat: README.md 문자열 덧셈 계산기 요구사항 및 기능 추가

* feat: StringSplitter.class 추가

split(string numbers) 메소드가 문자열을 받아서 구분자를 기준으로 문자열을 분리해서 리턴.
만약 문자열 앞에 커스텀 구분자가 있으면 커스텀 구분자로 분리

* feat: Positive.class, StringCalculator.class 추가

양수를 Positive.class 를 통하여 포장, StringCalculator.class 의 calculate 메소드를 통하여 입력받은 문자열을 숫자로 변환하여 더해서 반환해준다.

* feat: StringSplitter 를 이용한 StringCalculator 테스트 추가

* feat: calculatorTodo.md 추가

* refactor: Positive.class 인스턴스 변수 final 추가

Value Object 는 불변이 특징

* refactor: StringSplitter.class 수정 (정규패턴 구분자로 인식)

* feat: README.md 사다리 게임 -1단계 요구사항 및 기능 추가

* docs: ladderToDo.md, 사다리 게임 TDD를 위한 ToDo 작성

* feat: Player.class 생성

Player.class 생성 및 이름 길이에 따른 유효성 검사

* refactor: Player.class name 에 공백처리 추가.

* feat: PlayGenerator.class 추가

입력받은 문자열을 쉼표(,)를 기준으로 구분후 List Player를 생성해 준다.

* refactor: Player.class 이름 유효성 검사 별도의 메소드로 추출

validateName 메소드를 추출해서 이름의 유효성 검사

* feat: GamePlayers.class 추가

Player들을 관리하는 일급 컬렉션
사이즈 체크를 위한 validate 메소드 추가

* feat: Line.class 추가

사이즈가 0인지, subLine이 겹치는(연속 true) 유효성 검사

* refactor: Line.class equals 메소드 추가

* feat: SubLineGenerator 인터페이스, SubLineRandomGenerator.class 추가

각각의 Line에 대해서 subLine의 List를 random하게 생성

* feat: Ladder.class 추가

- 높이와 사용자 수에 따른 유효성 검사
- SubLineRandomGenerator로 부터 생성된 subLine의 List를 가지는 Line의 List를 생성

* fix: SubLineRandomGenerator 버그 해결

- subLines의 수가 countOfPlayer보다 많이 생성되는 경우
- true가 연달아 삽입되는 경우

* feat: OutputConsoleView.class, LadderConsoleApp.class 추가

사다리 결과물 출력

* refactor: OutputConsoleView.class 사다리 출력 변경

* refactor: SubLineRandomGenerator.class Stack을 사용하여 subLine 생성 수정

* refactor: Line 생성시 직접 generator를 받는 방식으로 수정

* feat: LadderGame.class 추가

Ladder.class 와 GamePlayers.class 를 가지고 게임을 관리

* feat: LadderGameController.class 추가

console game 진행 추가

* feat: GameResult.class, GameResultGenerator.class 추가

- GameResult.class: 실행결과 관리
- GameResultGenerator.class : 입력에따라 GameResult.class 를 생성

* refactor: GameResults.class -> PlayRewards.class 로 이름 변경

* refactor: LineGenerator.class 추가

List<Line> 를 생성하기 위해 LineGenerator.class 사용

* 임시 push

* refactor: generator 패키지 생성

* feat: LadderGame.class 에 findPlayerReward method 추가

사용자 입력에 따라 reward 출력

* feat: LadderGame.class 삭제, GameResult.class 추가

GamePlayer, Ladder, PlayerRewards 객체를 가지고 결과 생성
기존의 LadderGame 은 생설할 때 생성된 Ladder를 직접 받는게 아닌 사람 수와 높이를 받아서 Ladder를 직접 생성하는데, 이렇게 하면 너무 복잡해서 삭제
이와 비슷하지만 조금 더 간단하고 역할이 이름에 분명히 드러나는 GameResult 생성

* feat: LadderGameController.class 결과보기 기능 출력

* feat: GamePlayers.class 이름 중복 체크 추가

* feat: OutputConsoleView.printResult() 없는 사람일 경우 예외처리

결과를 보고 싶은 사람은?에서 없는 사람 입력할 경우
없는 사용자입니다. 출력

* feat: Player.name 예외처리 추가

Player의 name에 all 이 들어올 경우 예외로 처리

* refactor: 상수 생성

결과 입력시, 없는 사용자일 경우 상수로 추출

* refactor: Player.class

Set을 이용한 허용될 수 없는 이름검증

* Update README.md

* refactor: controller 삭제 후 main이 대신 기능하게 수행하게 수정

* refactor: LadderConsoleApp 의미 없는 메소드 추출한 부분들 수정

* fix: PlayerGeneartor 상수 접근제어자 때문에 테스트가 안되는 문제 해결

* feat: Direction.class 추가

* refactor: Direction.last() 수정

파라미터 받던 것을 안받는 것으로 수정.

* feat: DirectionGenerator.class 추가

* refactor: 원시값 boolean subLine을 Direction.class로 변경

* feat: DirectionsGenerator 팩토리 패턴 구현

subLine(가로줄)을 Direction으로 변경해주면서
기존의 SubLineGenerator와 LineGenerator는 삭제하고 List<Direction>를 생성해주는 DirectionsGenerator를 생성해주고
DirectionsGenerator를 생성해주는 팩토리를 구현.
팩토리 패턴으

* refactor: DirectionGenerator -> DirectionsGenerator 이름 변경

* refactor: Player.class postion:int 필드 추가

* refactor GamePlayers.class players 타입 set으로 변경

Player에 position을 추가해줘서 List의 인덱스로 position을 찾을 필요가 없어져서
중복 제거 차원에서 Set으로 변경

* feat: Ladder.play(GamePlayer, PlayerRewards) 구현

GamePlayer와 PlayerRewards로 게임의 결과를 구해주는 Ladder.play()

* feat: Ladder.class 사람수와 결과 사이즈 같은지 검사 추가

* refactor: GameResult는 값만 가지고 있는 vo 역할로 수정

* refactor: 기존 코드의 변경에 따른 OutputConsoleView 변경

* refactor: PlayerRewards.class -> Rewards.class로 이름 변경

* refactor: GamePlayers.class -> Players.class 로 이름 변경

* refactor: PlayersGenerator.generate() 에서 직접 Players 인스턴스를 반환하게 변경

기존에는 List<Player>를 반환 해서 그 값으로 외부에서 Players 인스턴스를 생성했지만,
더 복잡한거 같아서 한번에 Players 인스턴스 생성 후 반환하게 변경

* refactor: 일급컬렉션의 컬렉션 getter 할 때 새로 복사해서 return하게 변경

일급컬렉션의 컬렉션은 불변이 보장되어야 하는데 그대로 return 해주면 같은 주소를 참조한다.
그래서 외부에서 값 변경시 일급컬렉션의 컬렉션도 같이 변경 되서 변경

* style: 자동 포맷팅
  • Loading branch information
dpudpu authored and pobiconan committed May 20, 2019
1 parent 1dd4127 commit aa58382
Show file tree
Hide file tree
Showing 34 changed files with 1,166 additions and 1 deletion.
68 changes: 67 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,70 @@
사다리타기 미션 저장소

## 우아한테크코스 코드리뷰
* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)
* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)

# 문자열 덧셈 계산기

## 요구사항

- 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환 (예: “” => 0, "1,2" => 3, "1,2,3" => 6, “1,2:3” => 6)
- 앞의 기본 구분자(쉼표, 콜론)외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 “//”와 “\n” 사이에 위치하는 문자를 커스텀 구분자로 사용한다. 예를 들어 “//;\n1;2;3”과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다.
- 문자열 계산기에 숫자 이외의 값 또는 음수를 전달하는 경우 RuntimeException 예외를 throw한다.

## 기능

- 구분자를 기준으로 숫자를 분리한다.
- 커스텀 구분자를 기준으로 숫자를 분리한다.
- 분리된 숫자들을 더해준다.
- 숫자 이외의 값 또는 음수가 있을 경우 RuntimeException을 throw한다.

# 사다리 게임 - 1단계

## 요구사항

- 사다리 게임에 참여하는 사람에 이름을 최대5글자까지 부여할 수 있다. 사다리를 출력할 때 사람 이름도 같이 출력한다.
- 사람 이름은 쉼표(,)를 기준으로 구분한다.
- 사람 이름을 5자 기준으로 출력하기 때문에 사다리 폭도 넓어져야 한다.
- 사다리 타기가 정상적으로 동작하려면 라인이 겹치지 않도록 해야 한다.
- `|-----|-----|` 모양과 같이 가로 라인이 겹치는 경우 어느 방향으로 이동할지 결정할 수 없다

## 필요한 기능

- 문자열을 입력받아 객체를 생성
- 문자열을 입력받는다.
- 입력받은 문자열을 쉼표(',')를 기준으로 구분한다.
- 객체를 생성한다.
- 일급 컬렉션에 담아준다.
- 사다리 높이를 입력받아 사다리를 만든다.
- 사다리 높이를 입력받는다.
- 사다리 높이 만큼 세로 라인 길이를 설정한다.
- 사람의 수 만큼 가로 라인을 설정한다.
- 겹치지 않게 가로 라인을 만든다.
- 사다리를 출력해준다.

## 가능한 예외

문자열을 입력받아 객체를 생성

- 문자열을 입력받아 객체를 생성
- 쉼표로 구분하지 않는 경우
- 구분된 문자열의 길이가 5를 초과하는 경우
- 문자열이 공백인 경우
- 문자열이 NULL인 경우
- 사다리 높이를 입력받아 사다리를 만든다.
- 높이가 음수인 경우
- 가로라인이 연속되는 경우


-----------------------------------------------

1. 사람의 이름을 입력받는다.
2. 입력 받은 이름을 컴마(,) 기준으로 나눠준다.
3. 사람 객체를 생성한다.
4. 사람 컨테이너 객체를 만든다.
5. 사다리의 높이를 입력 받는다.
6. 사다리 객체를 사람수와 사다리 높이를 인자로 받아서 생성하다.
7. 사다리 높이만큼 Line 클래스 리스트를 만들어준다.
1. Line은 가로라인을 boolean List로 가지고 있는다 (사람 수 - 1)
2. 가로 라인을 그어준다. 이 때 연속되면 안된다
8. 사다리를 출력해준다.
20 changes: 20 additions & 0 deletions src/main/java/calculator/Positive.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package calculator;

public class Positive {
private final int number;

public Positive(final String number) {
this(Integer.parseInt(number));
}

public Positive(final int number) {
if (number < 0) {
throw new RuntimeException();
}
this.number = number;
}

int getNumber() {
return number;
}
}
26 changes: 26 additions & 0 deletions src/main/java/calculator/StringCalculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package calculator;

public class StringCalculator {
static int calculate(String[] numbers) {
if (isValidSize(numbers)) {
return 0;
}
return plus(numbers);
}

private static boolean isValidSize(String[] numbers) {
return numbers == null || numbers.length == 0;
}

private static int plus(String[] numbers) {
int sum = 0;
for (String number : numbers) {
sum += toPositive(number).getNumber();
}
return sum;
}

private static Positive toPositive(String number) {
return new Positive(number);
}
}
24 changes: 24 additions & 0 deletions src/main/java/calculator/StringSplitter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package calculator;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StringSplitter {
private static final String CUSTOM_DELIMITER_REGEX = "//(.)\n(.*)";
private static final String DEFAULT_DELIMITER = ",|;";

static String[] split(String numbers) {
Matcher m = Pattern.compile(CUSTOM_DELIMITER_REGEX).matcher(numbers);

return m.find() ? splitCustom(m) : splitDefault(numbers);
}

private static String[] splitDefault(String numbers) {
return numbers.split(DEFAULT_DELIMITER);
}

private static String[] splitCustom(Matcher m) {
String customDelimiter = m.group(1);
return m.group(2).split(Pattern.quote(customDelimiter));
}
}
Empty file removed src/main/java/empty.txt
Empty file.
36 changes: 36 additions & 0 deletions src/main/java/ladder/LadderConsoleApp.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ladder;

import ladder.domain.GameResult;
import ladder.domain.Ladder;
import ladder.domain.Players;
import ladder.domain.Rewards;
import ladder.domain.generator.DirectionsGeneratorFactory;
import ladder.domain.generator.PlayersGenerator;
import ladder.domain.generator.RewardsGenerator;
import ladder.view.InputConsoleView;
import ladder.view.OutputConsoleView;

public class LadderConsoleApp {
public static void main(String[] args) {
Players players = generatePlayers(InputConsoleView.inputNames());
Rewards rewards = generateRewards(InputConsoleView.inputRewards());
Ladder ladder = new Ladder(InputConsoleView.inputHeight(), DirectionsGeneratorFactory.getInstance(players.size()));
GameResult gameResult = new GameResult(ladder.play(players, rewards));

OutputConsoleView.printLadderGame(ladder, players, rewards);

String name;
while (!(name = InputConsoleView.inputResultName()).equals("all")) {
OutputConsoleView.printResult(gameResult, name);
}
OutputConsoleView.printResult(gameResult);
}

private static Players generatePlayers(String names) {
return new PlayersGenerator(names).generate();
}

private static Rewards generateRewards(String results) {
return new RewardsGenerator(results).generate();
}
}
67 changes: 67 additions & 0 deletions src/main/java/ladder/domain/Direction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package ladder.domain;

import java.util.Objects;

public class Direction {
private static final int MOVE_LEFT = -1;
private static final int MOVE_RIGHT = 1;
private static final int MOVE_STRAIGHT = 0;

private final boolean left;
private final boolean right;

private Direction(final boolean left, final boolean right) {
if (left && right) {
throw new IllegalArgumentException("연속 true 불가능");
}
this.left = left;
this.right = right;
}

public static Direction of(final boolean left, final boolean right) {
return new Direction(left, right);
}

public static Direction first(final boolean right) {
return of(false, right);
}

public Direction last() {
return of(this.right, false);
}

public Direction next(final boolean right) {
if (this.right) {
return of(true, false);
}
return of(false, right);
}

public int move() {
if (left) {
return MOVE_LEFT;
}
if (right) {
return MOVE_RIGHT;
}
return MOVE_STRAIGHT;
}

public boolean isRight() {
return right;
}

@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Direction direction = (Direction) o;
return left == direction.left &&
right == direction.right;
}

@Override
public int hashCode() {
return Objects.hash(left, right);
}
}
20 changes: 20 additions & 0 deletions src/main/java/ladder/domain/GameResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ladder.domain;

import java.util.HashMap;
import java.util.Map;

public class GameResult {
private final Map<String, String> results;

public GameResult(final Map<String, String> results) {
this.results = results;
}

public String get(String playerName) {
return results.get(playerName);
}

public Map<String, String> getAll() {
return new HashMap<>(results);
}
}
62 changes: 62 additions & 0 deletions src/main/java/ladder/domain/Ladder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package ladder.domain;

import ladder.domain.generator.DirectionsGenerator;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public final class Ladder {
private static final int MIN_HEIGHT = 1;

private final List<Line> lines;

public Ladder(int height, DirectionsGenerator directionsGenerator) {
validate(height);
this.lines = generateLines(height, directionsGenerator);
}

private void validate(int height) {
validateHeight(height);
}

private void validateHeight(int height) {
if (height < MIN_HEIGHT) {
throw new IllegalArgumentException("높이는 1이상 이어야 합니다.");
}
}

private List<Line> generateLines(final int height, final DirectionsGenerator directionsGenerator) {
List<Line> lines = new ArrayList<>();
for (int i = 0; i < height; i++) {
lines.add(new Line(directionsGenerator.generate()));
}
return lines;
}

public List<Line> getLines() {
return new ArrayList<>(lines);
}

public Map<String, String> play(final Players players, final Rewards rewards) {
validate(players.size(), rewards.size());
Map<String, String> result = new HashMap<>(players.size());

for (final Player player : players.getPlayers()) {
int position = player.getPosition();
for (final Line line : lines) {
position += line.move(position);
}
result.put(player.getName(), rewards.getReward(position));
}

return result;
}

private void validate(final int playersSize, final int rewardsSize) {
if (playersSize != rewardsSize){
throw new IllegalArgumentException("사람 수와 결과의 수가 다릅니다.");
}
}
}
33 changes: 33 additions & 0 deletions src/main/java/ladder/domain/Line.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package ladder.domain;

import java.util.List;
import java.util.Objects;

public final class Line {
private final List<Direction> directions;

public Line(final List<Direction> directions) {
this.directions = directions;
}

public int move(int position) {
return directions.get(position).move();
}

public List<Direction> getDirections() {
return directions;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Line line = (Line) o;
return Objects.equals(directions, line.directions);
}

@Override
public int hashCode() {
return Objects.hash(directions);
}
}
Loading

0 comments on commit aa58382

Please sign in to comment.