-
Notifications
You must be signed in to change notification settings - Fork 429
Step1 - 볼링 점수판(그리기) 리뷰 요청드립니다. #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f75c463
4f43503
d97a2d7
c91ccdd
a527bef
b08f003
06e6e27
8689424
01f2c62
3b9de76
900551a
a8d9487
9c301d3
b35d56f
4111c53
c67cb3c
38f0992
5bf2a61
75b4abc
b3fa4e5
1bc898d
c3963d0
962783a
4e903dc
6497c2c
fe3d527
7660217
04f93c2
42f9c87
6610cab
9ee2177
97488f4
f846d9b
b242a51
9ddccdc
765a22c
f3ac7c9
769a7a7
e0a43e8
cc22913
c82476f
9d02fb1
e77607e
c8e04eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,46 @@ | ||
# 볼링 게임 점수판 | ||
## 진행 방법 | ||
* 볼링 게임 점수판 요구사항을 파악한다. | ||
* 요구사항에 대한 구현을 완료한 후 자신의 github 아이디에 해당하는 브랜치에 Pull Request(이하 PR)를 통해 코드 리뷰 요청을 한다. | ||
* 코드 리뷰 피드백에 대한 개선 작업을 하고 다시 PUSH한다. | ||
* 모든 피드백을 완료하면 다음 단계를 도전하고 앞의 과정을 반복한다. | ||
|
||
## 온라인 코드 리뷰 과정 | ||
* [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/next-step/nextstep-docs/tree/master/codereview) | ||
~~~ | ||
플레이어 이름은(3 english letters)?: PJS | ||
| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | | ||
| PJS | | | | | | | | | | | | ||
|
||
1프레임 투구 : 10 | ||
| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | | ||
| PJS | X | | | | | | | | | | | ||
|
||
2프레임 투구 : 8 | ||
| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | | ||
| PJS | X | 8 | | | | | | | | | | ||
|
||
2프레임 투구 : 2 | ||
| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | | ||
| PJS | X | 8|/ | | | | | | | | | | ||
|
||
3프레임 투구 : 7 | ||
| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | | ||
| PJS | X | 8|/ | 7 | | | | | | | | | ||
|
||
3프레임 투구 : : 0 | ||
| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | | ||
| PJS | X | 8|/ | 7|- | | | | | | | | | ||
|
||
... | ||
~~~ | ||
|
||
## 구현 기능 목록 | ||
|
||
1. [ ] 플레이어 이름 입력 받기 | ||
- [x] 플레이어 (Player) 객체 생성 | ||
- [x] 3자리 영어 이름이 아니면 예외 발생 | ||
- [x] 숫자 혹은 한글이 포함되면 예외 발생 | ||
- [x] 입력받은 이름을 대문자로 변환 | ||
2. [ ] 사용자 이름이 포함된 프레임(1~10) 출력 | ||
3. [ ] 각 라운드별 투구 결과 입력 받기 | ||
- [ ] 1~9 라운드까지는 결과를 최소 1번, 최대 2번 입력 받기 | ||
- [ ] 10 라운드에서는 결과를 최소 2번, 최대 3번 입력 받기 | ||
4. [ ] 입력받은 투구 결과가 포함된 프레임(1~10)을 각 라운드마다 출력 | ||
- [ ] NormalFrame 객체 생성 (1~9) | ||
- [ ] FinalFrame 객체 생성 (10) | ||
- [ ] Frame 객체 생성 (for 중복 제거) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import domain.BowlingGame; | ||
import domain.Pins; | ||
import domain.Player; | ||
import domain.frame.FrameIndex; | ||
import view.InputView; | ||
import view.OutputView; | ||
|
||
public class Application { | ||
|
||
public static void main(String[] args) { | ||
Player player = Player.from(InputView.askPlayerName()); | ||
BowlingGame bowlingGame = BowlingGame.from(player); | ||
OutputView.printBoard(bowlingGame); | ||
|
||
while(!bowlingGame.isGameOver()) { | ||
FrameIndex currentFrameIndex = bowlingGame.currentFrame().getIndex(); | ||
Pins fallenPins = InputView.askFallenPins(currentFrameIndex); | ||
|
||
bowlingGame.play(fallenPins); | ||
OutputView.printBoard(bowlingGame); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package domain; | ||
|
||
import domain.frame.Frame; | ||
import domain.frame.GameOverException; | ||
import domain.frame.NormalFrame; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
public class BowlingGame { | ||
|
||
private Player player; | ||
private List<Frame> frames; | ||
|
||
private BowlingGame(Player player) { | ||
this.player = player; | ||
this.frames = new ArrayList<>(Arrays.asList(NormalFrame.initFrame())); | ||
} | ||
|
||
public static BowlingGame from(Player player) { | ||
return new BowlingGame(player); | ||
} | ||
|
||
public void play(Pins fallenPins) { | ||
if (isGameOver()) { | ||
throw new GameOverException(); | ||
} | ||
Frame bowledFrame = currentFrame().fillFrame(fallenPins); | ||
|
||
if (bowledFrame.getIndex().isSameIndex(currentFrame().getIndex())) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. List frames 의 일급컬렉션을 만들어 if분의 분기를 일급컬렉션으로 분리해보면 좋을 것 같습니다 :) |
||
frames.set(lastFrameIndex(), bowledFrame); | ||
} | ||
if (!bowledFrame.getIndex().isSameIndex(currentFrame().getIndex())) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Frame에서 Index를 꺼내와서 비교하는 로직이네요~ |
||
frames.add(bowledFrame); | ||
} | ||
} | ||
|
||
public Frame currentFrame() { | ||
return frames.get(lastFrameIndex()); | ||
} | ||
|
||
public boolean isGameOver() { | ||
return frames.get(lastFrameIndex()).isGameOver(); | ||
} | ||
|
||
private int lastFrameIndex() { | ||
return frames.size() - 1; | ||
} | ||
|
||
public Player getPlayer() { | ||
return player; | ||
} | ||
|
||
public List<Frame> getFrames() { | ||
return Collections.unmodifiableList(frames); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package domain; | ||
|
||
import java.util.Objects; | ||
|
||
public class Pins { | ||
static final String ALERT_OUT_OF_PINS_RANGE = "쓰러진 핀의 개수는 최소 0개에서 최대 10개 입니다."; | ||
public static final int STRIKE_PINS = 10; | ||
public static final int GUTTER_PINS = 0; | ||
|
||
private final int fallenPins; | ||
|
||
private Pins(int fallenPins) { | ||
validationPins(fallenPins); | ||
this.fallenPins = fallenPins; | ||
} | ||
|
||
private void validationPins(int fallenPins) { | ||
if (fallenPins < GUTTER_PINS || fallenPins > STRIKE_PINS) { | ||
throw new IllegalArgumentException(ALERT_OUT_OF_PINS_RANGE); | ||
} | ||
} | ||
|
||
public static Pins from(int fallenPins) { | ||
return new Pins(fallenPins); | ||
} | ||
|
||
public static Pins from(Pins fallenPins) { | ||
return new Pins(fallenPins.fallenPins); | ||
} | ||
|
||
public boolean isStrike() { | ||
return this.fallenPins == STRIKE_PINS; | ||
} | ||
|
||
public boolean isSpare(Pins secondFallenPins) { | ||
int sumOfPins = this.fallenPins + secondFallenPins.fallenPins; | ||
validationPins(sumOfPins); | ||
return sumOfPins == STRIKE_PINS; | ||
} | ||
|
||
public boolean exceedMiss(Pins secondFallenPins) { | ||
return fallenPins + secondFallenPins.fallenPins >= STRIKE_PINS; | ||
} | ||
|
||
public boolean isMatch(Pins pins) { | ||
return this.equals(pins); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
Pins pins = (Pins) o; | ||
return fallenPins == pins.fallenPins; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(fallenPins); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return String.valueOf(fallenPins); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package domain; | ||
|
||
public class Player { | ||
private static final String REGEX_FOR_PLAYER_NAME = "^[a-zA-Z]{3}$"; | ||
static final String ALERT_INVALID_PLAYER_NAME = "플레이어의 이름은 세 자리의 영문만 가능합니다."; | ||
|
||
private final String name; | ||
|
||
private Player(String inputName) { | ||
validationPlayerName(inputName); | ||
this.name = inputName.toUpperCase(); | ||
} | ||
|
||
public static Player from(String inputName) { | ||
return new Player(inputName); | ||
} | ||
|
||
private void validationPlayerName(String inputName) { | ||
if (!inputName.matches(REGEX_FOR_PLAYER_NAME)) { | ||
throw new IllegalArgumentException(ALERT_INVALID_PLAYER_NAME); | ||
} | ||
} | ||
|
||
public boolean isSameName(String nameToCompare) { | ||
return nameToCompare.equals(name); | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package domain.frame; | ||
|
||
import domain.Pins; | ||
import domain.state.FinalState; | ||
import domain.state.Miss; | ||
import domain.state.StandBy; | ||
import domain.state.State; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class FinalFrame implements Frame { | ||
private static final int INDEX_OF_FINAL_FRAME = 10; | ||
private static final int MAXIMUM_BOWL_ORDER = 3; | ||
private static final int INITIAL_BOWL_ORDER = 0; | ||
|
||
private int bowlOrder; | ||
private List<State> states = new ArrayList<>(); | ||
|
||
private FinalFrame() { | ||
this.bowlOrder = INITIAL_BOWL_ORDER; | ||
this.states.add(new StandBy()); | ||
} | ||
|
||
public static FinalFrame of() { | ||
return new FinalFrame(); | ||
} | ||
|
||
@Override | ||
public Frame fillFrame(Pins fallenPins) { | ||
if (isGameOver()) { | ||
throw new GameOverException(); | ||
} | ||
bowlOrder++; | ||
|
||
if (currentState().isClosed()) { | ||
this.states.add(new StandBy().update(fallenPins)); | ||
return this; | ||
} | ||
State newState = currentState().update(fallenPins); | ||
states.set(getLastBowlOrder(), newState); | ||
|
||
return this; | ||
} | ||
|
||
@Override | ||
public boolean isGameOver() { | ||
return bowlOrder == MAXIMUM_BOWL_ORDER || isStateMiss(); | ||
} | ||
|
||
private boolean isStateMiss() { | ||
return states.get(getLastBowlOrder()) instanceof Miss; | ||
} | ||
|
||
public State currentState() { | ||
return states.get(getLastBowlOrder()); | ||
} | ||
|
||
private int getLastBowlOrder() { | ||
return states.size() - 1; | ||
} | ||
|
||
@Override | ||
public FrameIndex getIndex() { | ||
return FrameIndex.from(INDEX_OF_FINAL_FRAME); | ||
} | ||
|
||
@Override | ||
public State getState() { | ||
return new FinalState(states); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package domain.frame; | ||
|
||
import domain.Pins; | ||
import domain.state.State; | ||
|
||
public interface Frame { | ||
|
||
Frame fillFrame(Pins fallenPins); | ||
|
||
boolean isGameOver(); | ||
|
||
FrameIndex getIndex(); | ||
|
||
State getState(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package domain.frame; | ||
|
||
import java.util.Objects; | ||
|
||
public class FrameIndex { | ||
static final String ALERT_INVALID_FRAME_NUMBER = "프레임 번호는 1부터 10까지만 허용됩니다."; | ||
static final int MINIMUM_FRAME_INDEX = 1; | ||
static final int MAXIMUM_FRAME_INDEX = 10; | ||
static final int SECOND_TO_LAST_INDEX = 9; | ||
static final int INCREMENT_AMOUNT = 1; | ||
|
||
private int frameIndex; | ||
|
||
private FrameIndex(int frameNumber) { | ||
validationFrameNumber(frameNumber); | ||
this.frameIndex = frameNumber; | ||
} | ||
|
||
public static FrameIndex from(int frameNumber) { | ||
return new FrameIndex(frameNumber); | ||
} | ||
|
||
boolean isSecondToLastIndex() { | ||
return frameIndex == SECOND_TO_LAST_INDEX; | ||
} | ||
|
||
FrameIndex increment() { | ||
return from(frameIndex + INCREMENT_AMOUNT); | ||
} | ||
|
||
public boolean isSameIndex(FrameIndex target) { | ||
return frameIndex == target.frameIndex; | ||
} | ||
|
||
private void validationFrameNumber(int frameNumber) { | ||
if (frameNumber < MINIMUM_FRAME_INDEX || frameNumber > MAXIMUM_FRAME_INDEX) { | ||
throw new IllegalArgumentException(ALERT_INVALID_FRAME_NUMBER); | ||
} | ||
} | ||
|
||
public int getFrameIndex() { | ||
return frameIndex; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
FrameIndex that = (FrameIndex) o; | ||
return frameIndex == that.frameIndex; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(frameIndex); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package domain.frame; | ||
|
||
public class GameOverException extends RuntimeException { | ||
|
||
public GameOverException() { | ||
super("게임이 종료되었습니다. 게임을 더 진행하시려면 카운터에 문의하세요."); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BowlingGame에 대한 단위 테스트가 있으면 좋을 것 같아요 :)