Skip to content

Commit 7a7f5d8

Browse files
Integerousboorownie
authored andcommitted
Step1 - 볼링 점수판(그리기) 리뷰 요청드립니다. (#37)
* [step1] docs : 구현 기능 목록(초벌) 작성 * [step1] feat : PlayerName(플레이어 이름) 객체 생성 * [step1] docs : 구현 기능 목록 수정 * [step1] feat : 플레이어 이름 유효성 체크 * [step1] feat : 입력받은 플레이어 이름을 대문자로 변환 * [step1] refactor : getter 대신 메세지 전달, 구현 기능 목록 수정 * [step1] docs : 구현 기능 목록 수정 * [step1] feat : main 메서드, InputView, OutputView 생성 * [step1] feat : ScoreBoard 객체 생성 * [step1] feat : Score 객체 생성 * [step1] refactor : Score 객체 삭제 * [step1] refactor : ScoreBoard에서 Scores 필드 제거 * [step1] refactor : Frame 객체 생성 (draft), 초기 스코어보드 출력 * [step1] refactor : PlayerName, View 제외하고 전부 삭제 * [step1] refactor : 객체 이름 변경 (PlayerName -> Player) * [step1] feat : Pins(쓰러진 핀 개수) 객체 생성 * [step1] feat : NormalFrame 객체 생성 * [step1] feat : NormalFrame 객체에 다음 프레임 생성 로직 추가 * [step1] refactor : 메서드 분리, 상수 사용 * [step1] feat : FinalFrame 객체 생성 * [step1] feat : 스트라이크가 아닌 초구와 2구의 합이 10이 넘는 예외 처리 * [step1] feat : BowlingGame 객체 생성 (미완성) * [step1] feat : 쓰러진 핀 입력받는 부분 추가 (미완성) * [step1] refactor : NormalFrame, FinalFrame 삭제 * [step1] feat : NormalFrame 객체 생성 * [step1] feat : Frame, State 인터페이스 생성 * [step1] feat : 쓰러진 핀의 개수를 입력받아 상태를 업데이트 하는 로직 추가 (미완성) * [step1] feat : NormalFrame에 index 필드 추가, FinalFrame 빈 객체 생성 * [step1] feat : 프레임 번호를 FrameIndex 객체로 포장 * [step1] refactor : NormalFrame의 index를 FrameIndex로 변경 * [step1] refactor : new 연산자 대신 팩토리메서드 사용 * [step1] feat : Strike, Hit 상태 클래스 생성 * [step1] refactor : 패키지 분리, Strike 객체 메서드 구현 * [step1] feat : Hit 상태 업데이트 메서드 구현 * [step1] feat : Spare 클래스 구현 * [step1] feat : Miss 클래스 구현 * [step1] feat : FinalFrame 클래스 구현 * [step1] refactor : GameOverException 추가 * [step1] feat : BowlingGame 객체 구현 * [step1] feat : Application 클래스 내부 구현 (미완성) * [step1] refactor : 프레임 번호 출력 수정 * [step1] feat : 모든 State에 상태 출력 메서드 추가 * [step1] feat : 마지막 프레임 상태출력을 위해 FinalState 구현 * [step1] feat : 볼링점수판 출력 일부 개선 (미완성)
1 parent 29b30da commit 7a7f5d8

32 files changed

+1136
-8
lines changed

README.md

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,46 @@
11
# 볼링 게임 점수판
2-
## 진행 방법
3-
* 볼링 게임 점수판 요구사항을 파악한다.
4-
* 요구사항에 대한 구현을 완료한 후 자신의 github 아이디에 해당하는 브랜치에 Pull Request(이하 PR)를 통해 코드 리뷰 요청을 한다.
5-
* 코드 리뷰 피드백에 대한 개선 작업을 하고 다시 PUSH한다.
6-
* 모든 피드백을 완료하면 다음 단계를 도전하고 앞의 과정을 반복한다.
7-
8-
## 온라인 코드 리뷰 과정
9-
* [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/next-step/nextstep-docs/tree/master/codereview)
2+
~~~
3+
플레이어 이름은(3 english letters)?: PJS
4+
| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 |
5+
| PJS | | | | | | | | | | |
6+
7+
1프레임 투구 : 10
8+
| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 |
9+
| PJS | X | | | | | | | | | |
10+
11+
2프레임 투구 : 8
12+
| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 |
13+
| PJS | X | 8 | | | | | | | | |
14+
15+
2프레임 투구 : 2
16+
| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 |
17+
| PJS | X | 8|/ | | | | | | | | |
18+
19+
3프레임 투구 : 7
20+
| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 |
21+
| PJS | X | 8|/ | 7 | | | | | | | |
22+
23+
3프레임 투구 : : 0
24+
| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 |
25+
| PJS | X | 8|/ | 7|- | | | | | | | |
26+
27+
...
28+
~~~
29+
30+
## 구현 기능 목록
31+
32+
1. [ ] 플레이어 이름 입력 받기
33+
- [x] 플레이어 (Player) 객체 생성
34+
- [x] 3자리 영어 이름이 아니면 예외 발생
35+
- [x] 숫자 혹은 한글이 포함되면 예외 발생
36+
- [x] 입력받은 이름을 대문자로 변환
37+
2. [ ] 사용자 이름이 포함된 프레임(1~10) 출력
38+
3. [ ] 각 라운드별 투구 결과 입력 받기
39+
- [ ] 1~9 라운드까지는 결과를 최소 1번, 최대 2번 입력 받기
40+
- [ ] 10 라운드에서는 결과를 최소 2번, 최대 3번 입력 받기
41+
4. [ ] 입력받은 투구 결과가 포함된 프레임(1~10)을 각 라운드마다 출력
42+
- [ ] NormalFrame 객체 생성 (1~9)
43+
- [ ] FinalFrame 객체 생성 (10)
44+
- [ ] Frame 객체 생성 (for 중복 제거)
45+
46+

src/main/java/Application.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import domain.BowlingGame;
2+
import domain.Pins;
3+
import domain.Player;
4+
import domain.frame.FrameIndex;
5+
import view.InputView;
6+
import view.OutputView;
7+
8+
public class Application {
9+
10+
public static void main(String[] args) {
11+
Player player = Player.from(InputView.askPlayerName());
12+
BowlingGame bowlingGame = BowlingGame.from(player);
13+
OutputView.printBoard(bowlingGame);
14+
15+
while(!bowlingGame.isGameOver()) {
16+
FrameIndex currentFrameIndex = bowlingGame.currentFrame().getIndex();
17+
Pins fallenPins = InputView.askFallenPins(currentFrameIndex);
18+
19+
bowlingGame.play(fallenPins);
20+
OutputView.printBoard(bowlingGame);
21+
}
22+
}
23+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package domain;
2+
3+
import domain.frame.Frame;
4+
import domain.frame.GameOverException;
5+
import domain.frame.NormalFrame;
6+
7+
import java.util.ArrayList;
8+
import java.util.Arrays;
9+
import java.util.Collections;
10+
import java.util.List;
11+
12+
public class BowlingGame {
13+
14+
private Player player;
15+
private List<Frame> frames;
16+
17+
private BowlingGame(Player player) {
18+
this.player = player;
19+
this.frames = new ArrayList<>(Arrays.asList(NormalFrame.initFrame()));
20+
}
21+
22+
public static BowlingGame from(Player player) {
23+
return new BowlingGame(player);
24+
}
25+
26+
public void play(Pins fallenPins) {
27+
if (isGameOver()) {
28+
throw new GameOverException();
29+
}
30+
Frame bowledFrame = currentFrame().fillFrame(fallenPins);
31+
32+
if (bowledFrame.getIndex().isSameIndex(currentFrame().getIndex())) {
33+
frames.set(lastFrameIndex(), bowledFrame);
34+
}
35+
if (!bowledFrame.getIndex().isSameIndex(currentFrame().getIndex())) {
36+
frames.add(bowledFrame);
37+
}
38+
}
39+
40+
public Frame currentFrame() {
41+
return frames.get(lastFrameIndex());
42+
}
43+
44+
public boolean isGameOver() {
45+
return frames.get(lastFrameIndex()).isGameOver();
46+
}
47+
48+
private int lastFrameIndex() {
49+
return frames.size() - 1;
50+
}
51+
52+
public Player getPlayer() {
53+
return player;
54+
}
55+
56+
public List<Frame> getFrames() {
57+
return Collections.unmodifiableList(frames);
58+
}
59+
}

src/main/java/domain/Pins.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package domain;
2+
3+
import java.util.Objects;
4+
5+
public class Pins {
6+
static final String ALERT_OUT_OF_PINS_RANGE = "쓰러진 핀의 개수는 최소 0개에서 최대 10개 입니다.";
7+
public static final int STRIKE_PINS = 10;
8+
public static final int GUTTER_PINS = 0;
9+
10+
private final int fallenPins;
11+
12+
private Pins(int fallenPins) {
13+
validationPins(fallenPins);
14+
this.fallenPins = fallenPins;
15+
}
16+
17+
private void validationPins(int fallenPins) {
18+
if (fallenPins < GUTTER_PINS || fallenPins > STRIKE_PINS) {
19+
throw new IllegalArgumentException(ALERT_OUT_OF_PINS_RANGE);
20+
}
21+
}
22+
23+
public static Pins from(int fallenPins) {
24+
return new Pins(fallenPins);
25+
}
26+
27+
public static Pins from(Pins fallenPins) {
28+
return new Pins(fallenPins.fallenPins);
29+
}
30+
31+
public boolean isStrike() {
32+
return this.fallenPins == STRIKE_PINS;
33+
}
34+
35+
public boolean isSpare(Pins secondFallenPins) {
36+
int sumOfPins = this.fallenPins + secondFallenPins.fallenPins;
37+
validationPins(sumOfPins);
38+
return sumOfPins == STRIKE_PINS;
39+
}
40+
41+
public boolean exceedMiss(Pins secondFallenPins) {
42+
return fallenPins + secondFallenPins.fallenPins >= STRIKE_PINS;
43+
}
44+
45+
public boolean isMatch(Pins pins) {
46+
return this.equals(pins);
47+
}
48+
49+
@Override
50+
public boolean equals(Object o) {
51+
if (this == o) return true;
52+
if (o == null || getClass() != o.getClass()) return false;
53+
Pins pins = (Pins) o;
54+
return fallenPins == pins.fallenPins;
55+
}
56+
57+
@Override
58+
public int hashCode() {
59+
return Objects.hash(fallenPins);
60+
}
61+
62+
@Override
63+
public String toString() {
64+
return String.valueOf(fallenPins);
65+
}
66+
}

src/main/java/domain/Player.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package domain;
2+
3+
public class Player {
4+
private static final String REGEX_FOR_PLAYER_NAME = "^[a-zA-Z]{3}$";
5+
static final String ALERT_INVALID_PLAYER_NAME = "플레이어의 이름은 세 자리의 영문만 가능합니다.";
6+
7+
private final String name;
8+
9+
private Player(String inputName) {
10+
validationPlayerName(inputName);
11+
this.name = inputName.toUpperCase();
12+
}
13+
14+
public static Player from(String inputName) {
15+
return new Player(inputName);
16+
}
17+
18+
private void validationPlayerName(String inputName) {
19+
if (!inputName.matches(REGEX_FOR_PLAYER_NAME)) {
20+
throw new IllegalArgumentException(ALERT_INVALID_PLAYER_NAME);
21+
}
22+
}
23+
24+
public boolean isSameName(String nameToCompare) {
25+
return nameToCompare.equals(name);
26+
}
27+
28+
public String getName() {
29+
return name;
30+
}
31+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package domain.frame;
2+
3+
import domain.Pins;
4+
import domain.state.FinalState;
5+
import domain.state.Miss;
6+
import domain.state.StandBy;
7+
import domain.state.State;
8+
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
12+
public class FinalFrame implements Frame {
13+
private static final int INDEX_OF_FINAL_FRAME = 10;
14+
private static final int MAXIMUM_BOWL_ORDER = 3;
15+
private static final int INITIAL_BOWL_ORDER = 0;
16+
17+
private int bowlOrder;
18+
private List<State> states = new ArrayList<>();
19+
20+
private FinalFrame() {
21+
this.bowlOrder = INITIAL_BOWL_ORDER;
22+
this.states.add(new StandBy());
23+
}
24+
25+
public static FinalFrame of() {
26+
return new FinalFrame();
27+
}
28+
29+
@Override
30+
public Frame fillFrame(Pins fallenPins) {
31+
if (isGameOver()) {
32+
throw new GameOverException();
33+
}
34+
bowlOrder++;
35+
36+
if (currentState().isClosed()) {
37+
this.states.add(new StandBy().update(fallenPins));
38+
return this;
39+
}
40+
State newState = currentState().update(fallenPins);
41+
states.set(getLastBowlOrder(), newState);
42+
43+
return this;
44+
}
45+
46+
@Override
47+
public boolean isGameOver() {
48+
return bowlOrder == MAXIMUM_BOWL_ORDER || isStateMiss();
49+
}
50+
51+
private boolean isStateMiss() {
52+
return states.get(getLastBowlOrder()) instanceof Miss;
53+
}
54+
55+
public State currentState() {
56+
return states.get(getLastBowlOrder());
57+
}
58+
59+
private int getLastBowlOrder() {
60+
return states.size() - 1;
61+
}
62+
63+
@Override
64+
public FrameIndex getIndex() {
65+
return FrameIndex.from(INDEX_OF_FINAL_FRAME);
66+
}
67+
68+
@Override
69+
public State getState() {
70+
return new FinalState(states);
71+
}
72+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package domain.frame;
2+
3+
import domain.Pins;
4+
import domain.state.State;
5+
6+
public interface Frame {
7+
8+
Frame fillFrame(Pins fallenPins);
9+
10+
boolean isGameOver();
11+
12+
FrameIndex getIndex();
13+
14+
State getState();
15+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package domain.frame;
2+
3+
import java.util.Objects;
4+
5+
public class FrameIndex {
6+
static final String ALERT_INVALID_FRAME_NUMBER = "프레임 번호는 1부터 10까지만 허용됩니다.";
7+
static final int MINIMUM_FRAME_INDEX = 1;
8+
static final int MAXIMUM_FRAME_INDEX = 10;
9+
static final int SECOND_TO_LAST_INDEX = 9;
10+
static final int INCREMENT_AMOUNT = 1;
11+
12+
private int frameIndex;
13+
14+
private FrameIndex(int frameNumber) {
15+
validationFrameNumber(frameNumber);
16+
this.frameIndex = frameNumber;
17+
}
18+
19+
public static FrameIndex from(int frameNumber) {
20+
return new FrameIndex(frameNumber);
21+
}
22+
23+
boolean isSecondToLastIndex() {
24+
return frameIndex == SECOND_TO_LAST_INDEX;
25+
}
26+
27+
FrameIndex increment() {
28+
return from(frameIndex + INCREMENT_AMOUNT);
29+
}
30+
31+
public boolean isSameIndex(FrameIndex target) {
32+
return frameIndex == target.frameIndex;
33+
}
34+
35+
private void validationFrameNumber(int frameNumber) {
36+
if (frameNumber < MINIMUM_FRAME_INDEX || frameNumber > MAXIMUM_FRAME_INDEX) {
37+
throw new IllegalArgumentException(ALERT_INVALID_FRAME_NUMBER);
38+
}
39+
}
40+
41+
public int getFrameIndex() {
42+
return frameIndex;
43+
}
44+
45+
@Override
46+
public boolean equals(Object o) {
47+
if (this == o) return true;
48+
if (o == null || getClass() != o.getClass()) return false;
49+
FrameIndex that = (FrameIndex) o;
50+
return frameIndex == that.frameIndex;
51+
}
52+
53+
@Override
54+
public int hashCode() {
55+
return Objects.hash(frameIndex);
56+
}
57+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package domain.frame;
2+
3+
public class GameOverException extends RuntimeException {
4+
5+
public GameOverException() {
6+
super("게임이 종료되었습니다. 게임을 더 진행하시려면 카운터에 문의하세요.");
7+
}
8+
}

0 commit comments

Comments
 (0)