Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## 기능 목록

### 데이터 처리
- [x] 컴퓨터 수 생성
- [x] 1 ~ 9 범위의 랜덤 수 한 개 생성
- [x] 중복되지 않는 수 3개 생성
- [x] 생성된 수로 컴퓨터 수 생성
- [x] 비교 결과 생성
- [x] 볼, 스트라이크 개수 구하기
- [x] 공 한 개가 볼인지 스트라이크인지 판정하기

### 고민한 부분
- [x]
14 changes: 14 additions & 0 deletions src/main/java/baseball/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
package baseball;

import baseball.controller.BaseballGameController;
import baseball.model.BaseballGame;
import baseball.model.ComputerBallsGenerator;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
BaseballGameController baseballGameController = new BaseballGameController(baseballGame());
baseballGameController.run();
}

private static BaseballGame baseballGame() {
return new BaseballGame(computerBallsGenerator());
}

private static ComputerBallsGenerator computerBallsGenerator() {
return new ComputerBallsGenerator();
}
}
40 changes: 40 additions & 0 deletions src/main/java/baseball/controller/BaseballGameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package baseball.controller;

import baseball.model.BaseballGame;
import baseball.model.GameResult;
import baseball.view.InputView;
import baseball.view.OutputView;

import java.util.List;

public class BaseballGameController {
private final InputView inputView = InputView.getInstance();
private final OutputView outputView = OutputView.getInstance();
private final BaseballGame baseballGame;

public BaseballGameController(BaseballGame baseballGame) {
this.baseballGame = baseballGame;
}

public void run() {
initializeGame();
while (baseballGame.isContinuing()) {
playAGame();
baseballGame.checkRegame(inputView.inputGameCommand());
}
}

private void initializeGame() {
outputView.printStartingMessage();
baseballGame.init();
}

private void playAGame() {
while (baseballGame.isContinuing()) {
List<Integer> inputNumbers = inputView.inputNumbers();
GameResult gameResult = baseballGame.compare(inputNumbers);
outputView.printResult(gameResult);
}
outputView.printWinningMessage();
}
}
27 changes: 27 additions & 0 deletions src/main/java/baseball/model/Ball.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package baseball.model;

public class Ball {
private final BallNumber number;
private final Position position;

public Ball(BallNumber number, Position position) {
this.number = number;
this.position = position;
}

public static Ball from(int number, int position) {
return new Ball(BallNumber.valueOf(number), Position.valueOf(position));
}

public boolean isBall(Ball other) {
return !position.equals(other.position) && number.equals(other.number);
}

public boolean isStrike(Ball other) {
return position.equals(other.position) && number.equals(other.number);
}

public BallNumber getNumber() {
return number;
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Position을 따로 만들어준게 인상깊어요 👍
다만 클래스명이 볼카운트의 볼과 동일하여 혼동의 여지가 있을 것 같아요!

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

별 생각없이 이름 지었는데 Ball, Strike를 표현하는 객체로 오인할 수 있겠네요:)

41 changes: 41 additions & 0 deletions src/main/java/baseball/model/BallNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package baseball.model;

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

public class BallNumber {
public static final int LOWER_BOUNDS = 1;
public static final int UPPER_BOUNDS = 9;
private static final Map<Integer, BallNumber> CACHE = new HashMap<>();
private final int number;

static {
for (int number = LOWER_BOUNDS; number <= UPPER_BOUNDS; number++) {
CACHE.put(number, new BallNumber(number));
}
}

private BallNumber(int number) {
validate(number);
this.number = number;
}

private void validate(int ballNumber) {
if (ballNumber < LOWER_BOUNDS || ballNumber > UPPER_BOUNDS) {
throw new IllegalArgumentException();
}
}

public static BallNumber valueOf(int number) {
BallNumber ballNumber = CACHE.get(number);
if (Objects.isNull(ballNumber)) {
ballNumber = new BallNumber(number);
}
return ballNumber;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정적 팩터리 메서드를 볼 때 생성자를 호출하는 부분이 validate를 위한게 아니라 인스턴스를 생성한다는 느낌을 받았어요!
hashMap에서 값을 가져오기 전에 validate 메서드를 호출 하는건 어떨까요?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

처음에 그렇게 했는데
특이해보이면 더 좋아보이는 습성이 또 발동했던 것 같네요ㅋㅋㅋ
덕분에 인스턴스 캐싱의 장점을 많이 느낄 수 있었습니다👍

public int getNumber() {
return number;
}
}
68 changes: 68 additions & 0 deletions src/main/java/baseball/model/Balls.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package baseball.model;

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

public class Balls {
public static final int SIZE = 3;

private final List<Ball> balls;

public static Balls from(List<Integer> numbers) {
List<Ball> ballList = IntStream.range(0, numbers.size())
.mapToObj(i -> Ball.from(numbers.get(i), i))
.collect(Collectors.toList());
return new Balls(ballList);
}

public Balls(List<Ball> balls) {
validate(balls);
this.balls = balls;
}

private void validate(List<Ball> balls) {
validateSize(balls);
validateDuplication(balls);
}

private void validateSize(List<Ball> balls) {
if (balls.size() != SIZE) {
throw new IllegalArgumentException();
}
}

private void validateDuplication(List<Ball> balls) {
long uniqueBallsCount = balls.stream()
.map(Ball::getNumber)
.distinct()
.count();

if (uniqueBallsCount != SIZE) {
throw new IllegalArgumentException();
}
}

public GameResult compare(Balls other) {
int ballCount = countFiltered(other, this::ballPredicate);
int strikeCount = countFiltered(other, this::strikePredicate);
return GameResult.of(ballCount, strikeCount);
}

private int countFiltered(Balls other, Predicate<Ball> predicate) {
return (int) other.balls.stream()
.filter(predicate)
.count();
}

private boolean ballPredicate(Ball otherBall) {
return balls.stream()
.anyMatch(ball -> ball.isBall(otherBall));
}

private boolean strikePredicate(Ball otherBall) {
return balls.stream()
.anyMatch(ball -> ball.isStrike(otherBall));
}
}
42 changes: 42 additions & 0 deletions src/main/java/baseball/model/BaseballGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package baseball.model;

import java.util.List;

public class BaseballGame {
private final ComputerBallsGenerator computerBallsGenerator;
private GameStatus gameStatus;
private Balls computerBalls;

public BaseballGame(ComputerBallsGenerator computerBallsGenerator) {
this.computerBallsGenerator = computerBallsGenerator;
}

public void init() {
gameStatus = GameStatus.CONTINUING;
computerBalls = computerBallsGenerator.generate();
}

public boolean isContinuing() {
return gameStatus.isContinuing();
}

public GameResult compare(List<Integer> inputNumbers) {
Balls userBalls = Balls.from(inputNumbers);
GameResult result = computerBalls.compare(userBalls);
checkUserWin(result);
return result;
}

private void checkUserWin(GameResult result) {
if (result.isUserWin()) {
gameStatus = GameStatus.STOP;
}
}

public void checkRegame(GameCommand gameCommand) {
if (gameCommand.isRestart()) {
gameStatus = GameStatus.CONTINUING;
computerBalls = computerBallsGenerator.generate();
}
}
}
34 changes: 34 additions & 0 deletions src/main/java/baseball/model/ComputerBallsGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package baseball.model;

import camp.nextstep.edu.missionutils.Randoms;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.IntStream;

import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;

public class ComputerBallsGenerator {
public Balls generate() {
List<Integer> uniqueNumbers = generateUniqueNumbers();
return IntStream.rangeClosed(Position.LOWER_BOUNDS, Position.UPPER_BOUNDS)
.mapToObj(position -> Ball.from(uniqueNumbers.get(position), position))
.collect(collectingAndThen(toList(), Balls::new));
}

private List<Integer> generateUniqueNumbers() {
Set<Integer> uniqueNumbers = new HashSet<>();
while (hasEnough(uniqueNumbers)) {
int number = Randoms.pickNumberInRange(BallNumber.LOWER_BOUNDS, BallNumber.UPPER_BOUNDS);
uniqueNumbers.add(number);
}
return new ArrayList<>(uniqueNumbers);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Streamg.generate를 사용한다면 조금 더 간단하게 메서드를 작성할 수 있을 것 같아요!

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메서드 이름도 잘못지었네요😂
hasNotEnough인데..ㅋㅋ
감사합니다!

private static boolean hasEnough(Set<Integer> uniqueNumbers) {
return uniqueNumbers.size() < Balls.SIZE;
}
}
25 changes: 25 additions & 0 deletions src/main/java/baseball/model/GameCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package baseball.model;

import java.util.Arrays;

public enum GameCommand {
RESTART(1),
TERMINATION(2);

private final int code;

GameCommand(int code) {
this.code = code;
}

public static GameCommand from(int code) {
return Arrays.stream(values())
.filter(gameCommand -> gameCommand.code == code)
.findAny()
.orElseThrow(() -> new IllegalArgumentException());
}

public boolean isRestart() {
return this.equals(RESTART);
}
}
44 changes: 44 additions & 0 deletions src/main/java/baseball/model/GameResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package baseball.model;

import java.util.Arrays;

public enum GameResult {
STRIKE_3_BALL_0(3, 0, "3스트라이크"),
STRIKE_1_BALL_2(1, 2, "2볼 1스트라이크"),
STRIKE_0_BALL_3(0, 3, "3볼"),
STRIKE_2_BALL_0(2, 0, "2스트라이크"),
STRIKE_1_BALL_1(1, 1, "1볼 1스트라이크"),
STRIKE_0_BALL_2(0, 2, "2볼"),
STRIKE_1_BALL_0(1, 0, "1스트라이크"),
STRIKE_0_BALL_1(0, 1, "1볼"),
NOTHING(0, 0, "낫싱");

private final int strikeCount;
private final int ballCount;
private final String output;

GameResult(int strikeCount, int ballCount, String output) {
this.strikeCount = strikeCount;
this.ballCount = ballCount;
this.output = output;
}

public static GameResult of(int ballCount, int strikeCount) {
return Arrays.stream(values())
.filter(gameResult -> gameResult.matches(ballCount, strikeCount))
.findAny()
.orElseThrow(() -> new IllegalArgumentException());
}

private boolean matches(int ballCount, int strikeCount) {
return this.strikeCount == strikeCount && this.ballCount == ballCount;
}

public boolean isUserWin() {
return this.equals(STRIKE_3_BALL_0);
}

public String getOutput() {
return output;
}
}
10 changes: 10 additions & 0 deletions src/main/java/baseball/model/GameStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package baseball.model;

public enum GameStatus {
CONTINUING,
STOP;

public boolean isContinuing() {
return this.equals(CONTINUING);
}
}
Loading