-
Notifications
You must be signed in to change notification settings - Fork 0
숫자 야구게임 구현 #2
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
base: main
Are you sure you want to change the base?
숫자 야구게임 구현 #2
Changes from all commits
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,26 +1,38 @@ | ||
| ## [NEXTSTEP 플레이그라운드의 미션 진행 과정](https://github.com/next-step/nextstep-docs/blob/master/playground/README.md) | ||
|
|
||
| --- | ||
| ## 학습 효과를 높이기 위해 추천하는 미션 진행 방법 | ||
| ## 목표 | ||
| - 객체에게 적절한 책임을 분배한다. | ||
| - 객체간에 메시지를 통해서만 서로 소통한다. | ||
| - TDD 개발 방법을 경험한다. | ||
|
|
||
| --- | ||
| 1. 피드백 강의 전까지 미션 진행 | ||
| > 피드백 강의 전까지 혼자 힘으로 미션 진행. 미션을 진행하면서 하나의 작업이 끝날 때 마다 add, commit | ||
| > 예를 들어 다음 숫자 야구 게임의 경우 0, 1, 2단계까지 구현을 완료한 후 push | ||
|
|
||
|  | ||
|
|
||
| --- | ||
| 2. 피드백 앞 단계까지 미션 구현을 완료한 후 피드백 강의를 학습한다. | ||
|
|
||
| --- | ||
| 3. Git 브랜치를 master 또는 main으로 변경한 후 피드백을 반영하기 위한 새로운 브랜치를 생성한 후 처음부터 다시 미션 구현을 도전한다. | ||
| ## 요구사항 | ||
| - 1 ~ 9까지 서로 다른 수로 이루어진 세자리 수를 맞추는 게임이다. | ||
| - 3 스트라이크를 달성하면 게임이 종료된다. | ||
| - 위치, 숫자 모두 맞춘경우 -> 스트라이크 | ||
| - 위치는 틀리고 숫자만 맞춘 경우 -> 볼 | ||
| - 위치, 숫자 모두 틀린 경우 -> 낫싱 | ||
| - 게임을 종료 한 후 게임을 다시 시작하거나 완전히 종료 할 수 있다. | ||
|
|
||
| ``` | ||
| git branch -a // 모든 로컬 브랜치 확인 | ||
| git checkout master // 기본 브랜치가 master인 경우 | ||
| git checkout main // 기본 브랜치가 main인 경우 | ||
| ## 구현사항 | ||
| - 컴퓨터는 랜덤 숫자 3개를 입력한다. | ||
| - 1 ~ 9 까지의 서로 다른 숫자이다. | ||
| - 플레이어는 숫자를 입력한다. | ||
| - 1 ~ 9까지의 서로 다른 숫자이다. | ||
| - 입력받은 숫자를 비교한다. | ||
| - 위치, 숫자 모두 맞춘경우 -> 스트라이크 | ||
| - 위치는 틀리고 숫자만 맞춘 경우 -> 볼 | ||
| - 위치, 숫자 모두 틀린 경우 -> 낫싱 | ||
| - 결과를 출력한다. | ||
| - 결과에 따라 UI를 분기한다. | ||
| - 3 스트라이크가 아니라면? -> 게임 재시작 | ||
| - 3 스트라이크라면? -> 새로 시작 Or 게임 종료 선택 UI | ||
|
|
||
| ## 객체 | ||
| - RandomNumberGenerator : 1 ~ 9 까지의 RandomNumber를 생성한다. | ||
| - InputNumber : 입력값의 정합성을 보장한다. ( 1 ~ 9 까지의 숫자 ) | ||
| - InputNumbers : 입력값의 정합성을 보장한다. ( 서로 다른 숫자 ) | ||
| - Game : 게임의 진행을 담당한다. | ||
| - Input : 게임 진행중 모든 입력을 담당한다. | ||
| - Output : 게임 진행중 모든 출력을 담당한다. | ||
|
|
||
| git checkout -b 브랜치이름 | ||
| ex) git checkout -b apply-feedback | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import java.util.Objects; | ||
|
|
||
| public class Ball { | ||
|
|
||
| private final int position; | ||
|
|
||
| private final int number; | ||
|
|
||
| public Ball(int position, int number) { | ||
| this.position = position; | ||
| this.number = number; | ||
| } | ||
|
|
||
| public int getPosition() { | ||
| return position; | ||
| } | ||
|
|
||
| public int getNumber() { | ||
| return number; | ||
| } | ||
|
|
||
| public BallStatus compare(Ball userBall) { | ||
| if (this.equals(userBall)) { | ||
| return BallStatus.STRIKE; | ||
| } | ||
|
|
||
| if (position != userBall.getPosition() && number == userBall.getNumber()) { | ||
| return BallStatus.BALL; | ||
| } | ||
|
|
||
| return BallStatus.NOTHING; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) { | ||
| return true; | ||
| } | ||
| if (o == null || getClass() != o.getClass()) { | ||
| return false; | ||
| } | ||
| Ball ball = (Ball) o; | ||
| return position == ball.position && number == ball.number; | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(position, number); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| public enum BallStatus { | ||
|
|
||
| STRIKE, BALL, NOTHING; | ||
|
|
||
| public static boolean isNotNothing(BallStatus status) { | ||
| return status != BallStatus.NOTHING; | ||
| } | ||
|
|
||
| public static boolean isStrike(BallStatus status) { | ||
| return status == BallStatus.STRIKE; | ||
| } | ||
|
|
||
| public static boolean isBall(BallStatus status) { | ||
| return status == BallStatus.BALL; | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
|
|
||
| public class Balls { | ||
|
|
||
| private final List<Ball> balls; | ||
|
|
||
| public Balls(InputNumbers inputNumbers) { | ||
| balls = new ArrayList<>(); | ||
|
Member
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. 생성자에서는 단순한 값의 초기화만 이루어져야 좋다고 생각해요. |
||
|
|
||
| validateInputNumbers(inputNumbers); | ||
| convertToBalls(inputNumbers); | ||
| } | ||
|
|
||
| public List<Ball> getBalls() { | ||
| return balls; | ||
| } | ||
|
|
||
| private void validateInputNumbers(InputNumbers inputNumbers) { | ||
| if (Objects.isNull(inputNumbers)) { | ||
| throw new IllegalArgumentException("입력값이 없습니다."); | ||
| } | ||
| } | ||
|
|
||
| private void convertToBalls(InputNumbers inputNumbers) { | ||
| List<InputNumber> inputNumberList = inputNumbers.getInputNumbers(); | ||
| for (int i = 0; i < inputNumberList.size(); i++) { | ||
| balls.add(new Ball(i, inputNumberList.get(i).getNumber())); | ||
| } | ||
| } | ||
|
|
||
| public Score play(InputNumbers userInputNumbers) { | ||
| Score score = new Score(); | ||
|
|
||
| Balls userBalls = new Balls(userInputNumbers); | ||
| if (this.balls.equals(userBalls)) { | ||
| return new Score(3, 0); | ||
| } | ||
|
|
||
| for (Ball userBall: userBalls.getBalls()) { | ||
| BallStatus status = compareWithUserBall(userBall); | ||
| score.calculate(status); | ||
| } | ||
|
|
||
| return score; | ||
| } | ||
|
|
||
| private BallStatus compareWithUserBall(Ball userBall) { | ||
| return balls.stream() | ||
| .map(ball -> ball.compare(userBall)) | ||
| .filter(BallStatus::isNotNothing) | ||
| .findFirst() | ||
| .orElse(BallStatus.NOTHING); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) { | ||
| return true; | ||
| } | ||
| if (o == null || getClass() != o.getClass()) { | ||
| return false; | ||
| } | ||
| Balls balls1 = (Balls) o; | ||
| return balls.equals(balls1.balls); | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(balls); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import java.util.Arrays; | ||
|
|
||
| public enum ConfirmCommand { | ||
| CONTINUE(1), | ||
| EXIT(2); | ||
|
|
||
| int value; | ||
|
|
||
| ConfirmCommand(int value) { | ||
| this.value = value; | ||
| } | ||
|
|
||
| public static ConfirmCommand findConfirmCommand(String input) { | ||
| return Arrays.stream(ConfirmCommand.values()) | ||
| .filter(confirmCommand -> confirmCommand.getValue() == Integer.parseInt(input)) | ||
| .findFirst() | ||
| .orElseThrow(() -> new IllegalArgumentException("잘못된 커맨드입니다.")); | ||
| } | ||
|
|
||
| public int getValue() { | ||
| return value; | ||
| } | ||
|
|
||
| public boolean isExit() { | ||
| return this.equals(ConfirmCommand.EXIT); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import java.util.Scanner; | ||
|
|
||
| public class ConsoleInput extends GameInput{ | ||
|
|
||
| public static final String REQUEST_INPUT_NUMBERS_MESSAGE = "숫자를 입력해 주세요. : "; | ||
|
|
||
| private Scanner scanner; | ||
|
|
||
| public ConsoleInput() { | ||
| this.scanner = new Scanner(System.in); | ||
| } | ||
|
|
||
| @Override | ||
| public String input() { | ||
| System.out.print(REQUEST_INPUT_NUMBERS_MESSAGE); | ||
| return scanner.nextLine(); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| public class ConsoleOutput extends GameOutput { | ||
|
|
||
| @Override | ||
| protected void output(String message) { | ||
| System.out.println(message); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| public class Game { | ||
|
|
||
| public static final int MAX_NUMBER_COUNT = 3; | ||
| private final RandomNumberGenerator generator; | ||
|
|
||
| private final GameInput input; | ||
|
|
||
| private final GameOutput output; | ||
|
|
||
| public Game(RandomNumberGenerator generator, GameInput input, GameOutput output) { | ||
| this.generator = generator; | ||
| this.input = input; | ||
| this.output = output; | ||
| } | ||
|
|
||
| public void start() { | ||
|
|
||
| // 랜덤 숫자 3개 입력받기 By Computer | ||
| InputNumbers computerInputNumbers = getRandomNumbers(); | ||
|
|
||
| while (true) { | ||
| // 숫자 3개를 입력받는다. By User | ||
| InputNumbers userInputNumbers = input.getUserInputNumbers(); | ||
|
|
||
| // 입력받은 두 값을 비교해서 점수를 계산한다. | ||
| Balls computerBalls = new Balls(computerInputNumbers); | ||
| Score score = computerBalls.play(userInputNumbers); | ||
|
|
||
| // 결과를 출력한다. | ||
| output.output(score.makeResultMessage()); | ||
|
|
||
| // 결과에 따라 UI를 이동한다. | ||
| if (score.getStrikeCount() != 3) { | ||
| continue; | ||
| } | ||
|
|
||
| output.confirmMessage(); | ||
|
|
||
| ConfirmCommand confirmCommand = input.inputConfirmCommand(); | ||
| if (confirmCommand.isExit()) { | ||
| return; | ||
| } | ||
|
|
||
| start(); // 재귀사용 | ||
| return; | ||
| } | ||
|
Comment on lines
+39
to
+46
Collaborator
Author
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. 재원님이 말씀해주셨던 피드백중에 "재귀식"에 대한 내용이 기억나서 |
||
|
|
||
| } | ||
|
|
||
| private InputNumbers getRandomNumbers() { | ||
| InputNumbers inputNumbers = new InputNumbers(); | ||
| while (inputNumbers.getInputNumbers().size() < MAX_NUMBER_COUNT) { | ||
| try { | ||
| inputNumbers.addInputNumber(generator.generate()); | ||
| } catch (IllegalArgumentException e) { } | ||
| } | ||
| return inputNumbers; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| public class GameApplication { | ||
|
|
||
| public static void main(String[] args) { | ||
| GameInput consoleInput = new ConsoleInput(); | ||
| GameOutput consoleOutput = new ConsoleOutput(); | ||
| RandomNumberGenerator generator = new RandomNumberGenerator(); | ||
|
|
||
| Game game = new Game(generator, consoleInput, consoleOutput); | ||
| game.start(); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| public abstract class GameInput { | ||
|
|
||
| public static final String REQUEST_RE_INPUT_MESSAGE = "다시 입력해주세요."; | ||
|
|
||
| public InputNumbers getUserInputNumbers() { | ||
|
|
||
| String inputStr = inputStr = input(); | ||
| List<Integer> inputs = convertToIntegerList(inputStr); | ||
|
|
||
| InputNumbers inputNumbers = null; | ||
| try { | ||
| inputNumbers = new InputNumbers(inputs); | ||
|
|
||
| } catch (IllegalArgumentException e) { | ||
| System.out.println(REQUEST_RE_INPUT_MESSAGE); | ||
| getUserInputNumbers(); | ||
| } | ||
|
|
||
| return inputNumbers; | ||
| } | ||
|
|
||
| public ConfirmCommand inputConfirmCommand() { | ||
| ConfirmCommand confirmCommand = null; | ||
|
|
||
| try { | ||
| confirmCommand = ConfirmCommand.findConfirmCommand(input()); | ||
| } catch (IllegalArgumentException e) { | ||
| System.out.println(REQUEST_RE_INPUT_MESSAGE); | ||
| inputConfirmCommand(); | ||
| } | ||
|
|
||
| return confirmCommand; | ||
| } | ||
|
|
||
| private List<Integer> convertToIntegerList(String inputStr) { | ||
| return Arrays.stream(inputStr.split("")) | ||
| .map(Integer::parseInt) | ||
| .collect(Collectors.toList()); | ||
| } | ||
|
|
||
| protected abstract String input(); | ||
|
|
||
| } | ||
|
Comment on lines
+4
to
+47
Collaborator
Author
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. 입력 UI를 구현할 때, TemplateMethodPattern을 적용해봤습니다. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| public abstract class GameOutput { | ||
|
|
||
| public static final String GAME_EXIT_OR_CONTINUE_CONFIRM_MESSAGE = "3개의 숫자를 모두 맞히셨습니다! 게임 종료\n 게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."; | ||
|
|
||
| public void confirmMessage() { | ||
| output(GAME_EXIT_OR_CONTINUE_CONFIRM_MESSAGE); | ||
| } | ||
|
|
||
| protected abstract void output(String message); | ||
|
|
||
| } |
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.
getter가 특별한 이유 없이 public으로 노출되고 있어요 🤔