Skip to content

Commit

Permalink
[2단계 - 사다리 게임] 에버(손채영) 미션 제출합니다. (#408)
Browse files Browse the repository at this point in the history
* docs: 결과 관련 요구사항 목록 추가

* test: 결과 도메인 생성 테스트 코드 작성

* feat: 결과 도메인 생성 및 검증

* test: 에러메시지 수정 및 매직넘버 상수값 재사용

* test: 결과들 도메인 관련 테스트 코드 작성

* feat: 결과들 도메인 생성

* test: 게임 도메인 테스트 코드 작성

* feat: 사다리 게임 결과 매칭 로직 추가

* feat: 결과 및 타겟 입력받기

* refactor: 재사용 변수를 인스턴스 변수로 묶어 사용

* refactor: Results 객체 가공 로직 추가를 위해 정적 팩토리 메서드 구조 채택

* feat: 매핑할 결과 입력받는 로직 컨트롤러에 추가

* refactor: Results 객체 Game 클래스 내 인스턴스 변수로 관리

* feat: 사다리 결과에 매칭될 결과 출력 로직 추가

* feat: 사다리 결과 출력 시 불필요한 개행 제거

* feat: 참여자의 이름을 통해 Member 객체 찾기

* refactor: GameResult 클래스 도입하여 게임 결과 관리

* feat: 게임 실행 결과 출력 로직 추가

* feat: 해당 이름을 가진 참여자가 존재하는지 체크하는 메서드 추가

* feat: 결과 도출 및 출력 로직 컨트롤러에 합치기

* fix: 비정상적인 게임 결과 도출하는 메서드 수정

* test: 사다리 게임 테스트 케이스 추가

* refactor: 게임 결과 출력 시 입력받은 사용자 순서로 출력하기 위해 LinkedHashMap 사용

* feat: 게임 결과 최대 50번까지만 출력 가능하도록 변경

* refactor: Name 클래스 MemberName으로 이름 변경

* refactor: Lines 클래스 Ladder 클래스로 이름 변경

* refactor: Height 클래스 제거 후 Ladder 클래스에서 높이 검증

* refactor: 문자열 파싱 역할 도메인에서 분리

* refactor: name으로 Member 객체 만드는 함수 stream 사용하도록 수정

* refactor: Results 클래스 validate 메서드 호출 위치 변경

* refactor: Ladder 객체 생성 메서드 파라미터 순서 변경

* refactor: Line 객체 생성 메서드 이름 변경

* refactor: Game 클래스 결과 도출 메서드 역할 분리

* refactor: 참여자 이름으로 존재하는 참여자인지 확인 후 이름을 반환하도록 수정

* refactor: 들여쓰기 줄이기 위해 for문 stream 사용하도록 변경

* test: 대상 객체 관련 테스트 코드 작성

* feat: 결과를 보고 싶은 대상 참여자 도메인 생성

* refactor: 결과를 보고 싶은 대상에 대한 검증 로직 Target 클래스로 분리 후 의존 코드 수정

* refactor: Target 클래스 ResultTarget 으로 이름 변경

* refactor: while문 깊이 줄이기 위해 메서드 분리 및 역할 조정

* refactor: 게임 결과를 개인 및 전체 참여자의 경우 모두 하나의 메서드 내에서 출력

* fix: 결과 출력 대상 입력 메서드 에러 핸들링 과정 추가

* refactor: target 입력 받고 객체 생성하는 메서드명 수정

* test: StringParser 클래스 테스트 코드 작성

* test: 연결상태 관련 테스트 코드 작성

* test: 도메인 클래스 내 상수 재사용 및 에러 메시지 체크 로직 구체화

* test: 상수 재사용 및 에러 메시지 출력 검증 로직 구체화

* test: 리스트 길이 검증 로직 hasSize() 사용하도록 수정

* test: Results 객체 생성 성공 로직 검증 구체화

* test: Result 클래스 테스트 객체 생성 성공 및 에러메시지 검증 로직 구체화

* refactor: 결과 출력 메서드 private으로 변경 및 gameResult 전달 시 Member 대신 String 타입으로 전달

* feat: 참여자 이름이 될 수 없는 형태 검증 로직 추가

* refactor: 매직넘버 상수 처리 및 ResultTarget의 변수 이름 변경

* docs: 구현 완료된 기능 요구사항 체크 표시

* refactor: 불필요한 인스턴스 변수 제거

* refactor: 이름 동일 여부 비교하는 역할 Member 객체로 전달

* refactor: 가독성을 위해 부정문 줄이기

* refactor: 명확한 의미 전달을 위해 두 개의 변수로 분리

* refactor: 메서드 위치 기준 통일

* feat: 게임 결과 전달을 위한 클래스 생성

* refactor: DTO를 사용하여 게임 결과 반환 및 원활한 테스트를 위해 equals 재정의

* refactor: 가독성을 위해 메서드 추출

* refactor: 문자열 분리 및 타입 변환 메서드 호출 역할 컨트롤러에서 뷰로 이전

* refactor: do-while문을 사용하여 코드의 중복 제거

* refactor: 테스트를 위해 열어두었던 상수의 접근제어자 닫기 및 관련 테스트 코드 수정

* refactor: 모든 멤버의 결과 반환하는 메서드 불필요한 코드 정리

* refactor: getName 메서드 재사용하도록 수정

* refactor: lines -> ladder 메서드명 수정 보완

* refactor: 일급컬렉션 수정 불가능하도록 반환

* refactor: 구분자 상수화 및 스캐너 생성자 선언

* refactor: 메서드 순서 변경 및 stringBuilder 사용하도록 수정

* refactor: 매칭될 결과 클래스 이름 변경
  • Loading branch information
helenason authored Mar 5, 2024
1 parent 654d675 commit d0cac60
Show file tree
Hide file tree
Showing 28 changed files with 833 additions and 193 deletions.
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,38 @@
### 사다리
- [x] 사다리 가로줄의 생성 여부는 랜덤으로 결정된다.
- [x] 사다리 가로줄 라인이 겹치지 않도록 해야 한다.
- [x] `|-----|-----|` 와 같은 모양은 허용하지 않는다.
- [x] `|-----|-----|` 와 같은 모양은 허용하지 않는다.
- [x] 사다리 높이는 1부터 20까지 가능하다.

### 게임
- [x] 참여자와 결과를 사다리를 통해 매칭한다.
- [x] 높이 횟수만큼 아래의 동작을 반복한다.
- [x] 가로줄이 있는 경우 해당 방향으로 한 칸 이동한다.
- [x] 가로줄이 없는 경우 밑으로 한 칸 이동한다.

### 결과
- [x] 결과는 1글자부터 5글자까지 부여할 수 있다.
- [x] 결과는 쉼표(,)를 기준으로 구분한다.
- [x] 결과의 수는 참여자의 수와 일치하다.

### 입력
- [x] 참여할 사람의 이름을 쉼표(,)로 구분하여 입력받는다.
- [x] 참여할 사람의 이름은 "all"이 될 수 없다.
- [x] 최대 사다리 높이를 입력받는다.
- [x] 잘못된 형태로 입력된 경우 다시 입력받는다.
- [x] 참여자가 매칭될 결과를 입력받는다.
- [x] 결과를 보고 싶은 사람을 입력받는다. (개인 이름 또는 all)
- [x] 모든 입력에 대해 잘못된 형태로 입력된 경우 다시 입력받는다.

### 출력
- [x] 사람 이름이 출력되고, 그 다음줄부터 사다리가 출력된다.
- [x] 사람 이름이 5자 미만인 경우 이름의 마지막 글자가 사다리 세로줄의 바로 왼쪽 열에 위치하게 한다.
- [x] 사람 이름이 5자인 경우 이름의 마지막 글자가 사다리 세로줄과 같은 열에 위치하게 한다.
- [x] 사다리 각 행의 가장 왼쪽에는 4칸의 공백이 있다.
- [x] 사다리 세로줄 간의 간격은 5칸으로 한다.
- [x] 개인별 실행 결과를 출력한다.
- [x] 전체 참여자의 실행 결과를 출력한다.
- [x] 입력받은 참여자 순서대로 출력한다.

### 컨트롤러
- [x] "all"을 통해 전체 참여자가 출력되면 게임을 종료한다.
- [x] 실행 결과 출력은 최대 50번까지만 허용한다.
68 changes: 50 additions & 18 deletions src/main/java/controller/GameController.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
package controller;

import domain.Game;
import domain.Lines;
import domain.GameResult;
import domain.GameResultDto;
import domain.Ladder;
import domain.Members;
import domain.StringParser;
import domain.ResultTarget;
import domain.Rewards;
import error.ErrorHandler;
import java.util.List;
import strategy.RandomConnectionStrategy;
import view.InputView;
import view.OutputView;

public class GameController {

private static final int MAX_RESULT_COUNT = 50;

private final InputView inputView;
private final OutputView outputView;
private final ErrorHandler errorHandler;
Expand All @@ -23,26 +29,52 @@ public GameController(InputView inputView, OutputView outputView, ErrorHandler e

public void run() {

Members members = makeMembers();
Members members = errorHandler.readUntilNoError(this::makeMembers);

Rewards rewards = errorHandler.readUntilNoError(() -> makeRewards(members));

Ladder ladder = errorHandler.readUntilNoError(() -> makeLadder(members));

Game game = new Game(members, ladder, rewards);
outputView.printLadder(game);

Lines lines = makeLines(members);
GameResult gameResult = game.matchResult();

Game game = new Game(members, lines);
outputView.printResult(game);
manageResult(members, gameResult);
}

private Members makeMembers() {
return errorHandler.readUntilNoError(() -> {
String rawNames = inputView.readNames();
return Members.from(rawNames);
});
}

private Lines makeLines(Members members) {
return errorHandler.readUntilNoError(() -> {
String rawHeight = inputView.readHeight();
int height = StringParser.stringToInt(rawHeight);
return Lines.of(members.getCount(), height, new RandomConnectionStrategy());
});
List<String> names = inputView.readNames();
return Members.from(names);
}

private Rewards makeRewards(Members members) {
List<String> rewards = inputView.readRewards();
return Rewards.of(rewards, members.getCount());
}

private Ladder makeLadder(Members members) {
int height = inputView.readHeight();
return Ladder.of(height, members.getCount(), new RandomConnectionStrategy());
}

private void manageResult(Members members, GameResult gameResult) {
int count = MAX_RESULT_COUNT;
ResultTarget resultTarget;
do {
resultTarget = showResult(members, gameResult);
} while (--count > 0 && !resultTarget.isAllMembers());
}

private ResultTarget showResult(Members members, GameResult gameResult) {
ResultTarget resultTarget = errorHandler.readUntilNoError(() -> makeResultTarget(members));
GameResultDto result = gameResult.getResultByTarget(resultTarget);
outputView.printResult(result);
return resultTarget;
}

private ResultTarget makeResultTarget(Members members) {
String rawTargetName = inputView.readTarget();
return ResultTarget.of(rawTargetName, members.getMembers());
}
}
64 changes: 59 additions & 5 deletions src/main/java/domain/Game.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,74 @@
package domain;

import java.util.List;

public class Game {

private final Members members;
private final Lines lines;
private final Ladder ladder;
private final Rewards rewards;
private final GameResult gameResult;

public Game(Members members, Lines lines) {
public Game(Members members, Ladder ladder, Rewards rewards) {
this.members = members;
this.lines = lines;
this.ladder = ladder;
this.rewards = rewards;
this.gameResult = new GameResult();
}

public GameResult matchResult() {
for (Member member : members.getMembers()) {
int firstPosition = members.findPositionOfMember(member);
int finalPosition = tryMoveAll(firstPosition);
Reward reward = rewards.findResultByPosition(finalPosition);
gameResult.addGameResult(member, reward);
}
return gameResult;
}

private int tryMoveAll(int index) {
for (Line line : ladder.getLines()) {
List<Connection> connections = line.getConnections();
index = tryMove(connections, index);
}
return index;
}

private int tryMove(List<Connection> connections, int index) {
if (canMoveLeft(connections, index)) {
return index - 1;
}
if (canMoveRight(connections, index)) {
return index + 1;
}
return index;
}

private boolean canMoveLeft(List<Connection> connections, int index) {
if (index <= 0) {
return false;
}
Connection left = connections.get(index - 1);
return left.equals(Connection.CONNECTED);
}

private boolean canMoveRight(List<Connection> connections, int index) {
if (index > connections.size() - 1) {
return false;
}
Connection right = connections.get(index);
return right.equals(Connection.CONNECTED);
}

public Members getMembers() {
return members;
}

public Lines getLines() {
return lines;
public Ladder getLadder() {
return ladder;
}

public Rewards getResults() {
return rewards;
}
}
39 changes: 39 additions & 0 deletions src/main/java/domain/GameResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package domain;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map.Entry;

public class GameResult {

private final HashMap<Member, Reward> gameResult;

public GameResult() {
this.gameResult = new LinkedHashMap<>();
}

public void addGameResult(Member member, Reward reward) {
gameResult.put(member, reward);
}

public GameResultDto getResultByTarget(ResultTarget target) {
if (target.isAllMembers()) {
return getResultOfAllMembers();
}
return getResultOfMember(target.getValue());
}

private GameResultDto getResultOfAllMembers() {
return new GameResultDto(gameResult);
}

private GameResultDto getResultOfMember(String memberName) {
HashMap<Member, Reward> resolvedResult = new LinkedHashMap<>();
Entry<Member, Reward> memberResult = gameResult.entrySet().stream()
.filter(entry -> entry.getKey().hasSameNameWith(memberName))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("해당 이름을 가진 참여자가 없습니다."));
resolvedResult.put(memberResult.getKey(), memberResult.getValue());
return new GameResultDto(resolvedResult);
}
}
17 changes: 17 additions & 0 deletions src/main/java/domain/GameResultDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package domain;

import java.util.HashMap;
import java.util.LinkedHashMap;

public class GameResultDto {

private final HashMap<Member, Reward> gameResult;

public GameResultDto(HashMap<Member, Reward> gameResult) {
this.gameResult = new LinkedHashMap<>(gameResult);
}

public HashMap<Member, Reward> getGameResult() {
return gameResult;
}
}
24 changes: 0 additions & 24 deletions src/main/java/domain/Height.java

This file was deleted.

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import strategy.ConnectionStrategy;

public class Ladder {

private static final int MIN_HEIGHT = 1;
private static final int MAX_HEIGHT = 20;

private final List<Line> lines;

private Ladder(List<Line> lines) {
validateHeight(lines.size());
this.lines = lines;
}

private void validateHeight(int height) {
if (height < MIN_HEIGHT || height > MAX_HEIGHT) {
throw new IllegalArgumentException(MIN_HEIGHT + " 이상 " + MAX_HEIGHT + " 이하의 숫자를 입력해 주세요.");
}
}

public static Ladder of(int height, int memberCount, ConnectionStrategy connectionStrategy) {
List<Line> lines = new ArrayList<>();
for (int i = 0; i < height; i++) {
lines.add(Line.of(memberCount, connectionStrategy));
}
return new Ladder(lines);
}

public List<Line> getLines() {
return Collections.unmodifiableList(lines);
}
}
5 changes: 3 additions & 2 deletions src/main/java/domain/Line.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package domain;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import strategy.ConnectionStrategy;

Expand All @@ -12,7 +13,7 @@ private Line(List<Connection> connections) {
this.connections = connections;
}

public static Line from(int memberCount, ConnectionStrategy connectionStrategy) {
public static Line of(int memberCount, ConnectionStrategy connectionStrategy) {
List<Connection> connections = new ArrayList<>();
connections.add(connectionStrategy.generateConnection());

Expand All @@ -25,6 +26,6 @@ public static Line from(int memberCount, ConnectionStrategy connectionStrategy)
}

public List<Connection> getConnections() {
return connections;
return Collections.unmodifiableList(connections);
}
}
27 changes: 0 additions & 27 deletions src/main/java/domain/Lines.java

This file was deleted.

Loading

0 comments on commit d0cac60

Please sign in to comment.