From f23426e0f9378de23994e44617116525e82d3bbc Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Wed, 28 Jan 2026 17:23:38 +0900 Subject: [PATCH 01/16] set Readme --- README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d7e8aee..096cac92 100644 --- a/README.md +++ b/README.md @@ -1 +1,53 @@ -# java-baseball-precourse \ No newline at end of file +# 숫자 야구 게임 설계 문서 + +이 문서는 숫자 야구 게임의 핵심 로직과 상태 패턴(State Pattern)을 이용한 흐름 제어 설계를 포함합니다. + +--- + +## 1. 도메인 모델 + +### BaseballNumbers +사용자 또는 컴퓨터의 숫자를 관리하는 객체입니다. +- **검증**: 1~9 사이의 서로 다른 3자리 숫자인지 검증합니다. +- **비교**: 상대방의 `BaseballNumbers`와 비교하여 스트라이크와 볼의 개수를 계산합니다. + +### GameResult +계산된 스트라이크/볼 결과를 처리하는 객체입니다. +- **결과 저장**: 계산된 스트라이크와 볼의 개수를 보관합니다. +- **메시지 생성**: 결과에 따른 출력 메시지(예: `1볼 2스트라이크`, `낫싱`)를 생성합니다. +- **판단**: 3 스트라이크인지 여부를 확인하여 게임 종료 조건을 체크합니다. + +--- + +## 2. 컨트롤러 및 실행 (Game Controller) + +### Game +전체적인 게임 인스턴스와 흐름을 관리합니다. +- **상태 관리**: 현재의 `GameState`를 유지합니다. +- **게임 루프**: 현재 상태가 종료 상태(`EndState`)가 될 때까지 실행을 반복합니다. +- **유연성**: 상태 패턴을 통해 게임 종료 후 새로운 게임을 시작하거나 완전히 종료하는 로직을 매끄럽게 연결합니다. + +--- + +## 3. 상태 인터페이스 및 구현 (State Pattern) + +상태 패턴을 활용하여 각 상황에 맞는 로직을 캡슐화하고 흐름을 제어합니다. + + + +| 클래스명 | 역할 및 주요 기능 | 다음 상태 전이 (Next State) | +| :--- | :--- | :--- | +| **ProgressState** | - "숫자를 입력해주세요" 프롬프트 출력
- 사용자 입력을 받아 `BaseballNumbers`와 비교 후 결과 출력 | - 정답이 아니면: `this` (유지)
- 정답이면: `GameOverState` 반환 | +| **GameOverState** | - "3개의 숫자를 모두 맞히셨습니다! 게임 끝" 및 재시작 안내 출력
- 사용자 입력(1: 재시작, 2: 종료) 대기 | - `1` 입력 시: 새로운 정답을 가진 `ProgressState`
- `2` 입력 시: `EndState` | +| **EndState** | - 게임 루프를 종료해야 함을 `Game`에게 알림 | - 없음 (시스템 종료) | + +--- + +## 4. 게임 실행 흐름 (Game Flow) + +1. **초기화**: `Game` 객체가 생성될 때 컴퓨터의 숫자를 생성하고 `ProgressState`로 시작합니다. +2. **진행**: 사용자가 정답을 맞힐 때까지 `ProgressState` 내에서 입력을 반복합니다. +3. **완료**: 3 스트라이크 달성 시 `GameOverState`로 전환되어 축하 메시지를 출력합니다. +4. **분기**: + - 사용자가 `1`을 입력하면 새로운 숫자를 생성하여 다시 `ProgressState`로 돌아갑니다. + - 사용자가 `2`를 입력하면 `EndState`로 전환되어 루프가 종료됩니다. \ No newline at end of file From 52f24c83f566cf5eb8d838f80ca8942f0028d761 Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Wed, 28 Jan 2026 17:28:34 +0900 Subject: [PATCH 02/16] =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EB=94=94=EB=A0=89?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EA=B5=AC=EC=A1=B0=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/GameController.java | 4 ++++ src/main/java/model/BaseballNumber.java | 5 +++++ src/main/java/model/GameResult.java | 4 ++++ src/main/java/model/NumberGenerator.java | 4 ++++ src/main/java/state/EndState.java | 4 ++++ src/main/java/state/GameOverState.java | 4 ++++ src/main/java/state/GameState.java | 4 ++++ src/main/java/state/ProgressState.java | 4 ++++ src/main/java/view/Inputview.java | 4 ++++ src/main/java/view/OutputView.java | 4 ++++ 10 files changed, 41 insertions(+) create mode 100644 src/main/java/controller/GameController.java create mode 100644 src/main/java/model/BaseballNumber.java create mode 100644 src/main/java/model/GameResult.java create mode 100644 src/main/java/model/NumberGenerator.java create mode 100644 src/main/java/state/EndState.java create mode 100644 src/main/java/state/GameOverState.java create mode 100644 src/main/java/state/GameState.java create mode 100644 src/main/java/state/ProgressState.java create mode 100644 src/main/java/view/Inputview.java create mode 100644 src/main/java/view/OutputView.java diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java new file mode 100644 index 00000000..c3ff3bd4 --- /dev/null +++ b/src/main/java/controller/GameController.java @@ -0,0 +1,4 @@ +package controller; + +public class GameController { +} diff --git a/src/main/java/model/BaseballNumber.java b/src/main/java/model/BaseballNumber.java new file mode 100644 index 00000000..4deab8a6 --- /dev/null +++ b/src/main/java/model/BaseballNumber.java @@ -0,0 +1,5 @@ +package model; + +public class BaseballNumber { + +} diff --git a/src/main/java/model/GameResult.java b/src/main/java/model/GameResult.java new file mode 100644 index 00000000..c4415ba3 --- /dev/null +++ b/src/main/java/model/GameResult.java @@ -0,0 +1,4 @@ +package model; + +public class GameResult { +} diff --git a/src/main/java/model/NumberGenerator.java b/src/main/java/model/NumberGenerator.java new file mode 100644 index 00000000..4b2c3b15 --- /dev/null +++ b/src/main/java/model/NumberGenerator.java @@ -0,0 +1,4 @@ +package model; + +public class NumberGenerator { +} diff --git a/src/main/java/state/EndState.java b/src/main/java/state/EndState.java new file mode 100644 index 00000000..21739e01 --- /dev/null +++ b/src/main/java/state/EndState.java @@ -0,0 +1,4 @@ +package state; + +public class EndState { +} diff --git a/src/main/java/state/GameOverState.java b/src/main/java/state/GameOverState.java new file mode 100644 index 00000000..f7e725a8 --- /dev/null +++ b/src/main/java/state/GameOverState.java @@ -0,0 +1,4 @@ +package state; + +public class GameOverState { +} diff --git a/src/main/java/state/GameState.java b/src/main/java/state/GameState.java new file mode 100644 index 00000000..6f5419a6 --- /dev/null +++ b/src/main/java/state/GameState.java @@ -0,0 +1,4 @@ +package state; + +public class GameState { +} diff --git a/src/main/java/state/ProgressState.java b/src/main/java/state/ProgressState.java new file mode 100644 index 00000000..2425d975 --- /dev/null +++ b/src/main/java/state/ProgressState.java @@ -0,0 +1,4 @@ +package state; + +public class ProgressState { +} diff --git a/src/main/java/view/Inputview.java b/src/main/java/view/Inputview.java new file mode 100644 index 00000000..2f71376e --- /dev/null +++ b/src/main/java/view/Inputview.java @@ -0,0 +1,4 @@ +package view; + +public class Inputview { +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 00000000..d8f9743c --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,4 @@ +package view; + +public class OutputView { +} From 2d007a8bc0e569d50494f53211f0d73b74a640f8 Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Wed, 28 Jan 2026 17:32:15 +0900 Subject: [PATCH 03/16] =?UTF-8?q?mode/Game=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/BaseballNumber.java | 4 +++- src/main/java/model/Game.java | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 src/main/java/model/Game.java diff --git a/src/main/java/model/BaseballNumber.java b/src/main/java/model/BaseballNumber.java index 4deab8a6..705bcd4b 100644 --- a/src/main/java/model/BaseballNumber.java +++ b/src/main/java/model/BaseballNumber.java @@ -1,5 +1,7 @@ package model; public class BaseballNumber { - + + + } diff --git a/src/main/java/model/Game.java b/src/main/java/model/Game.java new file mode 100644 index 00000000..4fe5b9e8 --- /dev/null +++ b/src/main/java/model/Game.java @@ -0,0 +1,4 @@ +package model; + +public class Game { +} From 4a9e55b1cddf1e882a843e2b7554f8a52e0e4f9c Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Wed, 28 Jan 2026 17:50:23 +0900 Subject: [PATCH 04/16] =?UTF-8?q?baseballNumber=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/BaseballNumber.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/model/BaseballNumber.java b/src/main/java/model/BaseballNumber.java index 705bcd4b..32cb2e4c 100644 --- a/src/main/java/model/BaseballNumber.java +++ b/src/main/java/model/BaseballNumber.java @@ -1,7 +1,9 @@ package model; public class BaseballNumber { - - + private int number; + boolean isValid(){ + return number >= 111 && number <= 999; + } } From 3e408b810261de0d3c71786dc6e020ca7772cc06 Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Wed, 28 Jan 2026 18:22:25 +0900 Subject: [PATCH 05/16] =?UTF-8?q?baseballNumber=20List=EB=A1=9C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Main.java | 7 ++++++ src/main/java/controller/GameController.java | 10 ++++++++ src/main/java/generator/NumberGenerator.java | 12 ++++++++++ src/main/java/model/BaseballNumber.java | 25 +++++++++++++++++--- src/main/java/model/Game.java | 5 ++++ src/main/java/state/GameState.java | 1 + src/main/java/state/ProgressState.java | 3 ++- 7 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 src/main/java/Main.java create mode 100644 src/main/java/generator/NumberGenerator.java diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 00000000..7ead160b --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,7 @@ +import controller.GameController; + +public class Main{ + public static void main(String[] args) { + GameController gameController; + } +} \ No newline at end of file diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index c3ff3bd4..36efc5ad 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -1,4 +1,14 @@ package controller; +import model.BaseballNumber; +import model.Game; +import model.NumberGenerator; +import state.GameState; +import state.ProgressState; + public class GameController { + + + + } diff --git a/src/main/java/generator/NumberGenerator.java b/src/main/java/generator/NumberGenerator.java new file mode 100644 index 00000000..3c1c6e09 --- /dev/null +++ b/src/main/java/generator/NumberGenerator.java @@ -0,0 +1,12 @@ +package generator; + +import model.BaseballNumber; + +import java.util.Set; + +public class NumberGenerator { + + public BaseballNumber generate(){ + + } +} diff --git a/src/main/java/model/BaseballNumber.java b/src/main/java/model/BaseballNumber.java index 32cb2e4c..5a9048c2 100644 --- a/src/main/java/model/BaseballNumber.java +++ b/src/main/java/model/BaseballNumber.java @@ -1,9 +1,28 @@ package model; +import java.sql.Array; +import java.util.ArrayList; +import java.util.List; + public class BaseballNumber { - private int number; + private final List numbers; - boolean isValid(){ - return number >= 111 && number <= 999; + public BaseballNumber(int num){ + this.numbers = new ArrayList<>(); + numbers.add(num/100); + numbers.add(num/10 % 10); + numbers.add(num % 10); } + + public boolean isValid(){ + if(numbers.get(0) < 0 || numbers.get(0) >= 10) return false; + if(numbers.get(1) < 0 || numbers.get(1) >= 10) return false; + if(numbers.get(2) < 0 || numbers.get(2) >= 10) return false; + + if(numbers.get(0).equals(numbers.get(1)) || numbers.get(1).equals(numbers.get(2)) || numbers.get(0).equals(numbers.get(2))) return false; + + return true; + } + + } diff --git a/src/main/java/model/Game.java b/src/main/java/model/Game.java index 4fe5b9e8..6ea419a7 100644 --- a/src/main/java/model/Game.java +++ b/src/main/java/model/Game.java @@ -1,4 +1,9 @@ package model; +import state.GameState; + public class Game { + GameState gameState; + + } diff --git a/src/main/java/state/GameState.java b/src/main/java/state/GameState.java index 6f5419a6..97b4f781 100644 --- a/src/main/java/state/GameState.java +++ b/src/main/java/state/GameState.java @@ -1,4 +1,5 @@ package state; public class GameState { + } diff --git a/src/main/java/state/ProgressState.java b/src/main/java/state/ProgressState.java index 2425d975..b65bd6af 100644 --- a/src/main/java/state/ProgressState.java +++ b/src/main/java/state/ProgressState.java @@ -1,4 +1,5 @@ package state; -public class ProgressState { +public class ProgressState implements GameState{ + } From d61763e858a6d01e8611dc2da73bd8bced6c5e8f Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Wed, 28 Jan 2026 18:31:36 +0900 Subject: [PATCH 06/16] =?UTF-8?q?number=20generate=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/GameController.java | 17 ++++++++++++++++- src/main/java/generator/NumberGenerator.java | 12 ------------ src/main/java/model/BaseballNumber.java | 7 ++----- src/main/java/model/NumberGenerator.java | 18 ++++++++++++++++++ src/main/java/state/GameState.java | 2 +- src/main/java/state/ProgressState.java | 5 +++++ 6 files changed, 42 insertions(+), 19 deletions(-) delete mode 100644 src/main/java/generator/NumberGenerator.java diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index 36efc5ad..5263e8e1 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -7,8 +7,23 @@ import state.ProgressState; public class GameController { - + public Game game; + public NumberGenerator numberGenerator; + public void run(){ + initialize(); + play(); + } + public void initialize(){ + this.numberGenerator = new NumberGenerator(); + BaseballNumber baseballNumber = numberGenerator.generate(); + GameState initialGameState = new ProgressState(baseballNumber); + this.game = new Game(InitialGameState); + } + + public void play(){ + + } } diff --git a/src/main/java/generator/NumberGenerator.java b/src/main/java/generator/NumberGenerator.java deleted file mode 100644 index 3c1c6e09..00000000 --- a/src/main/java/generator/NumberGenerator.java +++ /dev/null @@ -1,12 +0,0 @@ -package generator; - -import model.BaseballNumber; - -import java.util.Set; - -public class NumberGenerator { - - public BaseballNumber generate(){ - - } -} diff --git a/src/main/java/model/BaseballNumber.java b/src/main/java/model/BaseballNumber.java index 5a9048c2..8cc4aceb 100644 --- a/src/main/java/model/BaseballNumber.java +++ b/src/main/java/model/BaseballNumber.java @@ -7,11 +7,8 @@ public class BaseballNumber { private final List numbers; - public BaseballNumber(int num){ - this.numbers = new ArrayList<>(); - numbers.add(num/100); - numbers.add(num/10 % 10); - numbers.add(num % 10); + public BaseballNumber(List numbers){ + this.numbers = numbers; } public boolean isValid(){ diff --git a/src/main/java/model/NumberGenerator.java b/src/main/java/model/NumberGenerator.java index 4b2c3b15..983a5463 100644 --- a/src/main/java/model/NumberGenerator.java +++ b/src/main/java/model/NumberGenerator.java @@ -1,4 +1,22 @@ package model; +import model.BaseballNumber; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + public class NumberGenerator { + + public BaseballNumber generate(){ + List digits = IntStream.rangeClosed(1, 9) + .boxed() + .collect(Collectors.toList()); + Collections.shuffle(digits); + + return new BaseballNumber(digits); + } } diff --git a/src/main/java/state/GameState.java b/src/main/java/state/GameState.java index 97b4f781..4b6bcdf2 100644 --- a/src/main/java/state/GameState.java +++ b/src/main/java/state/GameState.java @@ -1,5 +1,5 @@ package state; -public class GameState { +public interface GameState { } diff --git a/src/main/java/state/ProgressState.java b/src/main/java/state/ProgressState.java index b65bd6af..495a0ac3 100644 --- a/src/main/java/state/ProgressState.java +++ b/src/main/java/state/ProgressState.java @@ -1,5 +1,10 @@ package state; +import model.BaseballNumber; + public class ProgressState implements GameState{ + public ProgressState(BaseballNumber baseballNumber){ + + } } From 02ee24f56d229107fe528a502bebe39465fba1b7 Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Sat, 7 Feb 2026 18:14:18 +0900 Subject: [PATCH 07/16] =?UTF-8?q?feat:=20=EC=83=81=ED=83=9C=ED=8C=A8?= =?UTF-8?q?=ED=84=B4=20=EA=B2=8C=EC=9E=84=20=EB=A3=A8=ED=94=84=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/GameController.java | 4 +++- src/main/java/model/Game.java | 21 +++++++++++++++++++- src/main/java/state/EndState.java | 6 +++++- src/main/java/state/GameOverState.java | 6 +++++- src/main/java/state/GameState.java | 2 +- src/main/java/state/ProgressState.java | 5 +++++ 6 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index 5263e8e1..feb6a827 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -20,10 +20,12 @@ public void initialize(){ BaseballNumber baseballNumber = numberGenerator.generate(); GameState initialGameState = new ProgressState(baseballNumber); - this.game = new Game(InitialGameState); + this.game = new Game(initialGameState); } public void play(){ + while(game.isRunning()){ + } } } diff --git a/src/main/java/model/Game.java b/src/main/java/model/Game.java index 6ea419a7..8b0668df 100644 --- a/src/main/java/model/Game.java +++ b/src/main/java/model/Game.java @@ -3,7 +3,26 @@ import state.GameState; public class Game { - GameState gameState; + private GameState state; + private boolean running = true; + public Game(GameState initialState) { + this.state = initialState; + } + public void changeState(GameState state) { + this.state = state; + } + + public void update() { + state.handle(this); + } + + public void stop() { + this.running = false; + } + + public boolean isRunning() { + return running; + } } diff --git a/src/main/java/state/EndState.java b/src/main/java/state/EndState.java index 21739e01..d6069431 100644 --- a/src/main/java/state/EndState.java +++ b/src/main/java/state/EndState.java @@ -1,4 +1,8 @@ package state; -public class EndState { +public class EndState implements GameState{ + @Override + public void handle(){ + + } } diff --git a/src/main/java/state/GameOverState.java b/src/main/java/state/GameOverState.java index f7e725a8..74e55851 100644 --- a/src/main/java/state/GameOverState.java +++ b/src/main/java/state/GameOverState.java @@ -1,4 +1,8 @@ package state; -public class GameOverState { +public class GameOverState implements GameState{ + @Override + public void handle(){ + + } } diff --git a/src/main/java/state/GameState.java b/src/main/java/state/GameState.java index 4b6bcdf2..aea1d1b0 100644 --- a/src/main/java/state/GameState.java +++ b/src/main/java/state/GameState.java @@ -1,5 +1,5 @@ package state; public interface GameState { - + public void handle(); } diff --git a/src/main/java/state/ProgressState.java b/src/main/java/state/ProgressState.java index 495a0ac3..004b2a49 100644 --- a/src/main/java/state/ProgressState.java +++ b/src/main/java/state/ProgressState.java @@ -7,4 +7,9 @@ public class ProgressState implements GameState{ public ProgressState(BaseballNumber baseballNumber){ } + + @Override + public void handle(){ + + } } From 0bb16ff8502f75f9c3882ea07cef3e244f2ebe32 Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Sat, 7 Feb 2026 18:30:06 +0900 Subject: [PATCH 08/16] =?UTF-8?q?feat:=20=EC=83=81=ED=83=9C=ED=8C=A8?= =?UTF-8?q?=ED=84=B4=20=EA=B0=9C=EC=84=A0=20/=20=EC=88=AB=EC=9E=90=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=A1=9C=EC=A7=81=20=EB=8B=A8=EC=88=9C?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Main.java | 5 +++-- src/main/java/controller/GameController.java | 2 +- src/main/java/model/NumberGenerator.java | 20 ++++++++++---------- src/main/java/state/EndState.java | 6 ++++-- src/main/java/state/GameOverState.java | 4 +++- src/main/java/state/GameState.java | 4 +++- src/main/java/state/ProgressState.java | 3 ++- 7 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 7ead160b..e3287045 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -2,6 +2,7 @@ public class Main{ public static void main(String[] args) { - GameController gameController; + GameController gameController = new GameController(); + gameController.run(); } -} \ No newline at end of file +} diff --git a/src/main/java/controller/GameController.java b/src/main/java/controller/GameController.java index feb6a827..14dcfed5 100644 --- a/src/main/java/controller/GameController.java +++ b/src/main/java/controller/GameController.java @@ -25,7 +25,7 @@ public void initialize(){ public void play(){ while(game.isRunning()){ - + game.update(); } } } diff --git a/src/main/java/model/NumberGenerator.java b/src/main/java/model/NumberGenerator.java index 983a5463..d79b9200 100644 --- a/src/main/java/model/NumberGenerator.java +++ b/src/main/java/model/NumberGenerator.java @@ -1,22 +1,22 @@ package model; -import model.BaseballNumber; - import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.IntStream; public class NumberGenerator { public BaseballNumber generate(){ - List digits = IntStream.rangeClosed(1, 9) - .boxed() - .collect(Collectors.toList()); - Collections.shuffle(digits); + List nums = new ArrayList<>(); + for (int i = 1; i <= 9; i++) { + nums.add(i); + } + Collections.shuffle(nums); - return new BaseballNumber(digits); + List pick = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + pick.add(nums.get(i)); + } + return new BaseballNumber(pick); } } diff --git a/src/main/java/state/EndState.java b/src/main/java/state/EndState.java index d6069431..5c024f19 100644 --- a/src/main/java/state/EndState.java +++ b/src/main/java/state/EndState.java @@ -1,8 +1,10 @@ package state; +import model.Game; + public class EndState implements GameState{ @Override - public void handle(){ - + public void handle(Game game){ + game.stop(); } } diff --git a/src/main/java/state/GameOverState.java b/src/main/java/state/GameOverState.java index 74e55851..4dd8138c 100644 --- a/src/main/java/state/GameOverState.java +++ b/src/main/java/state/GameOverState.java @@ -1,8 +1,10 @@ package state; +import model.Game; + public class GameOverState implements GameState{ @Override - public void handle(){ + public void handle(Game game){ } } diff --git a/src/main/java/state/GameState.java b/src/main/java/state/GameState.java index aea1d1b0..fe1bc76c 100644 --- a/src/main/java/state/GameState.java +++ b/src/main/java/state/GameState.java @@ -1,5 +1,7 @@ package state; +import model.Game; + public interface GameState { - public void handle(); + void handle(Game game); } diff --git a/src/main/java/state/ProgressState.java b/src/main/java/state/ProgressState.java index 004b2a49..fb1f2b44 100644 --- a/src/main/java/state/ProgressState.java +++ b/src/main/java/state/ProgressState.java @@ -1,6 +1,7 @@ package state; import model.BaseballNumber; +import model.Game; public class ProgressState implements GameState{ @@ -9,7 +10,7 @@ public ProgressState(BaseballNumber baseballNumber){ } @Override - public void handle(){ + public void handle(Game game){ } } From 9b9edb894e98c00ca27104973e73412be28e2e6b Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Sat, 7 Feb 2026 18:51:08 +0900 Subject: [PATCH 09/16] =?UTF-8?q?chore:=20README=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EB=B2=94=EC=9C=84=20=EB=AC=B8=EA=B5=AC=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 096cac92..d4befb08 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,21 @@ --- +## 구현 범위 + +이번 과제에서 최종적으로 구현할 항목은 아래와 같습니다. + +- 1~9 사이의 서로 다른 3자리 컴퓨터 숫자 생성 +- 사용자 숫자 입력 처리 및 입력값 검증(길이, 숫자 여부, 범위, 중복) +- 잘못된 입력 시 `[ERROR]` 메시지 출력 후 재입력 +- 스트라이크/볼/낫싱 계산과 결과 출력 +- 3스트라이크 달성 시 게임 종료 메시지 출력 +- 게임 종료 후 재시작(1) / 종료(2) 분기 처리 +- 상태 패턴(`ProgressState`, `GameOverState`, `EndState`) 기반 흐름 제어 +- 도메인 로직 단위 테스트(`BaseballNumber`, `NumberGenerator`, `GameResult`) + +--- + ## 1. 도메인 모델 ### BaseballNumbers @@ -50,4 +65,4 @@ 3. **완료**: 3 스트라이크 달성 시 `GameOverState`로 전환되어 축하 메시지를 출력합니다. 4. **분기**: - 사용자가 `1`을 입력하면 새로운 숫자를 생성하여 다시 `ProgressState`로 돌아갑니다. - - 사용자가 `2`를 입력하면 `EndState`로 전환되어 루프가 종료됩니다. \ No newline at end of file + - 사용자가 `2`를 입력하면 `EndState`로 전환되어 루프가 종료됩니다. From 1dd03e1f6efc1bda7e778df995d91ac5e5f67050 Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Sat, 7 Feb 2026 19:09:08 +0900 Subject: [PATCH 10/16] =?UTF-8?q?feat:=20=EC=9E=85=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/view/Inputview.java | 11 +++++++++++ src/main/java/view/OutputView.java | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/main/java/view/Inputview.java b/src/main/java/view/Inputview.java index 2f71376e..b0c71027 100644 --- a/src/main/java/view/Inputview.java +++ b/src/main/java/view/Inputview.java @@ -1,4 +1,15 @@ package view; +import java.util.Scanner; + public class Inputview { + private final Scanner scanner = new Scanner(System.in); + + public String readNumber() { + return scanner.nextLine(); + } + + public String readRestart() { + return scanner.nextLine(); + } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index d8f9743c..1e4fc650 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,4 +1,23 @@ package view; public class OutputView { + public void printInputGuide() { + System.out.print("숫자를 입력해주세요 : "); + } + + public void printResult(String msg) { + System.out.println(msg); + } + + public void printGameEnd() { + System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 시마이"); + } + + public void printRestartGuide() { + System.out.println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."); + } + + public void printError(String msg) { + System.out.println("[ERROR] " + msg); + } } From 05ab5738f819beea30a580144fce1b7d37b72b98 Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Sat, 7 Feb 2026 19:22:50 +0900 Subject: [PATCH 11/16] =?UTF-8?q?feat:=20baseballNumber=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=ED=8C=8C=EC=8B=B1=20=EB=B0=8F=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/BaseballNumber.java | 52 ++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/src/main/java/model/BaseballNumber.java b/src/main/java/model/BaseballNumber.java index 8cc4aceb..6777afe0 100644 --- a/src/main/java/model/BaseballNumber.java +++ b/src/main/java/model/BaseballNumber.java @@ -1,6 +1,5 @@ package model; -import java.sql.Array; import java.util.ArrayList; import java.util.List; @@ -8,18 +7,59 @@ public class BaseballNumber { private final List numbers; public BaseballNumber(List numbers){ - this.numbers = numbers; + this.numbers = new ArrayList<>(numbers); + validateNumbers(); + } + + public BaseballNumber(String input){ + this.numbers = parse(input); + validateNumbers(); } public boolean isValid(){ - if(numbers.get(0) < 0 || numbers.get(0) >= 10) return false; - if(numbers.get(1) < 0 || numbers.get(1) >= 10) return false; - if(numbers.get(2) < 0 || numbers.get(2) >= 10) return false; + if(numbers.size() != 3) return false; + if(!isRangeValid()) return false; + if(hasDuplicate()) return false; + return true; + } + + public List getNumbers(){ + return new ArrayList<>(numbers); + } - if(numbers.get(0).equals(numbers.get(1)) || numbers.get(1).equals(numbers.get(2)) || numbers.get(0).equals(numbers.get(2))) return false; + private List parse(String input){ + if(input == null) throw new IllegalArgumentException("입력값이 비어 있습니다."); + if(input.length() != 3) throw new IllegalArgumentException("3자리 숫자를 입력해야 합니다."); + List parsed = new ArrayList<>(); + for (int i = 0; i < input.length(); i++) { + char ch = input.charAt(i); + if(!Character.isDigit(ch)) throw new IllegalArgumentException("숫자만 입력해야 합니다."); + parsed.add(ch - '0'); + } + return parsed; + } + + private void validateNumbers(){ + if(isValid()) return; + throw new IllegalArgumentException("1~9의 서로 다른 3자리 숫자를 입력해야 합니다."); + } + + private boolean isRangeValid(){ + for (int i = 0; i < numbers.size(); i++) { + int number = numbers.get(i); + if(number < 1 || number > 9) return false; + } return true; } + private boolean hasDuplicate(){ + for (int i = 0; i < numbers.size(); i++) { + for (int j = i + 1; j < numbers.size(); j++) { + if(numbers.get(i).equals(numbers.get(j))) return true; + } + } + return false; + } } From 26d97766558eadc47c96614da1fc31b6072dd3de Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Sat, 7 Feb 2026 20:09:38 +0900 Subject: [PATCH 12/16] =?UTF-8?q?feat:=20strike=20ball=20=ED=8C=90?= =?UTF-8?q?=EC=A0=95=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/model/BaseballNumber.java | 27 +++++++++++++++++++++++++ src/main/java/model/GameResult.java | 26 ++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/main/java/model/BaseballNumber.java b/src/main/java/model/BaseballNumber.java index 6777afe0..0c7ce006 100644 --- a/src/main/java/model/BaseballNumber.java +++ b/src/main/java/model/BaseballNumber.java @@ -27,6 +27,33 @@ public List getNumbers(){ return new ArrayList<>(numbers); } + public GameResult compare(BaseballNumber target){ + int strike = countStrike(target); + int ball = countBall(target); + return new GameResult(strike, ball); + } + + private int countStrike(BaseballNumber target){ + List targetNumbers = target.getNumbers(); + int strike = 0; + for (int i = 0; i < numbers.size(); i++) { + if(numbers.get(i).equals(targetNumbers.get(i))) strike++; + } + return strike; + } + + private int countBall(BaseballNumber target){ + List targetNumbers = target.getNumbers(); + int ball = 0; + for (int i = 0; i < numbers.size(); i++) { + for (int j = 0; j < targetNumbers.size(); j++) { + if(i == j) continue; + if(numbers.get(i).equals(targetNumbers.get(j))) ball++; + } + } + return ball; + } + private List parse(String input){ if(input == null) throw new IllegalArgumentException("입력값이 비어 있습니다."); if(input.length() != 3) throw new IllegalArgumentException("3자리 숫자를 입력해야 합니다."); diff --git a/src/main/java/model/GameResult.java b/src/main/java/model/GameResult.java index c4415ba3..67cb7123 100644 --- a/src/main/java/model/GameResult.java +++ b/src/main/java/model/GameResult.java @@ -1,4 +1,30 @@ package model; public class GameResult { + private final int strike; + private final int ball; + + public GameResult(int strike, int ball){ + this.strike = strike; + this.ball = ball; + } + + public int getStrike(){ + return strike; + } + + public int getBall(){ + return ball; + } + + public boolean isThreeStrike(){ + return strike == 3; + } + + public String getMessage(){ + if(strike == 0 && ball == 0) return "낫싱"; + if(strike == 0) return ball + "볼"; + if(ball == 0) return strike + "스트라이크"; + return strike + "스트라이크 " + ball + "볼"; + } } From 0e572744ab04805cab2fd9cc1a087951b752d0c5 Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Sat, 7 Feb 2026 20:33:06 +0900 Subject: [PATCH 13/16] =?UTF-8?q?feat:=20progressState=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=EC=B2=98=EB=A6=AC=EC=99=80=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=20=ED=9D=90=EB=A6=84=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/state/ProgressState.java | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/java/state/ProgressState.java b/src/main/java/state/ProgressState.java index fb1f2b44..11ea03c3 100644 --- a/src/main/java/state/ProgressState.java +++ b/src/main/java/state/ProgressState.java @@ -2,15 +2,38 @@ import model.BaseballNumber; import model.Game; +import model.GameResult; +import view.Inputview; +import view.OutputView; public class ProgressState implements GameState{ + private final BaseballNumber answer; + private final Inputview inputview; + private final OutputView outputView; public ProgressState(BaseballNumber baseballNumber){ - + this.answer = baseballNumber; + this.inputview = new Inputview(); + this.outputView = new OutputView(); } @Override public void handle(Game game){ + outputView.printInputGuide(); + String input = inputview.readNumber(); + playOneTurn(input, game); + } + private void playOneTurn(String input, Game game){ + try { + BaseballNumber userNumber = new BaseballNumber(input); + GameResult gameResult = answer.compare(userNumber); + outputView.printResult(gameResult.getMessage()); + if(!gameResult.isThreeStrike()) return; + outputView.printGameEnd(); + game.changeState(new GameOverState()); + } catch (IllegalArgumentException e){ + outputView.printError(e.getMessage()); + } } } From 259159d4e6f849c4117d48f924302fe6a9f18c3d Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Sat, 7 Feb 2026 22:09:09 +0900 Subject: [PATCH 14/16] =?UTF-8?q?feat:=20GameOverState=20=EC=9E=AC?= =?UTF-8?q?=EC=8B=9C=EC=9E=91=20=EC=A2=85=EB=A3=8C=20=EB=B6=84=EA=B8=B0=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/state/GameOverState.java | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/main/java/state/GameOverState.java b/src/main/java/state/GameOverState.java index 4dd8138c..719c6050 100644 --- a/src/main/java/state/GameOverState.java +++ b/src/main/java/state/GameOverState.java @@ -1,10 +1,37 @@ package state; +import model.BaseballNumber; import model.Game; +import model.NumberGenerator; +import view.Inputview; +import view.OutputView; public class GameOverState implements GameState{ + private final Inputview inputview = new Inputview(); + private final OutputView outputView = new OutputView(); + @Override public void handle(Game game){ + outputView.printRestartGuide(); + String input = inputview.readRestart(); + changeState(input, game); + } + + private void changeState(String input, Game game){ + if("1".equals(input)) { + restart(game); + return; + } + if("2".equals(input)) { + game.changeState(new EndState()); + return; + } + outputView.printError("1 또는 2를 입력해야 합니다."); + } + private void restart(Game game){ + NumberGenerator numberGenerator = new NumberGenerator(); + BaseballNumber baseballNumber = numberGenerator.generate(); + game.changeState(new ProgressState(baseballNumber)); } } From 4b3dfae87855330862fe4d9ff33f1d166dd482c9 Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Sat, 7 Feb 2026 22:43:44 +0900 Subject: [PATCH 15/16] =?UTF-8?q?test:=20BaseballNumber=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/model/BaseballNumberTest.java | 55 +++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/test/java/model/BaseballNumberTest.java diff --git a/src/test/java/model/BaseballNumberTest.java b/src/test/java/model/BaseballNumberTest.java new file mode 100644 index 00000000..aa4cfbac --- /dev/null +++ b/src/test/java/model/BaseballNumberTest.java @@ -0,0 +1,55 @@ +package model; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class BaseballNumberTest { + + @Test + void 문자열로_숫자야구_숫자를_생성한다() { + BaseballNumber baseballNumber = new BaseballNumber("123"); + + assertThat(baseballNumber.getNumbers()).containsExactly(1, 2, 3); + } + + @Test + void 길이가_3이_아니면_예외가_발생한다() { + assertThatThrownBy(() -> new BaseballNumber("12")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("3자리"); + } + + @Test + void 숫자가_아닌_문자가_포함되면_예외가_발생한다() { + assertThatThrownBy(() -> new BaseballNumber("12a")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("숫자만"); + } + + @Test + void 범위를_벗어난_숫자가_포함되면_예외가_발생한다() { + assertThatThrownBy(() -> new BaseballNumber("120")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("1~9"); + } + + @Test + void 중복_숫자가_포함되면_예외가_발생한다() { + assertThatThrownBy(() -> new BaseballNumber("112")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("서로 다른"); + } + + @Test + void 비교_결과로_스트라이크와_볼을_계산한다() { + BaseballNumber answer = new BaseballNumber("425"); + BaseballNumber user = new BaseballNumber("456"); + + GameResult gameResult = answer.compare(user); + + assertThat(gameResult.getStrike()).isEqualTo(1); + assertThat(gameResult.getBall()).isEqualTo(1); + } +} From 26a63b6d8420c0b33f9b935ab0a8ade8ac09b8ff Mon Sep 17 00:00:00 2001 From: zhongdan-b Date: Sat, 7 Feb 2026 23:26:44 +0900 Subject: [PATCH 16/16] =?UTF-8?q?test:=20NumberGenerator=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EA=B7=9C=EC=B9=99=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/model/NumberGeneratorTest.java | 39 ++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/test/java/model/NumberGeneratorTest.java diff --git a/src/test/java/model/NumberGeneratorTest.java b/src/test/java/model/NumberGeneratorTest.java new file mode 100644 index 00000000..97ece531 --- /dev/null +++ b/src/test/java/model/NumberGeneratorTest.java @@ -0,0 +1,39 @@ +package model; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class NumberGeneratorTest { + + @Test + void 생성된_숫자는_3자리다() { + NumberGenerator numberGenerator = new NumberGenerator(); + + BaseballNumber baseballNumber = numberGenerator.generate(); + + assertThat(baseballNumber.getNumbers()).hasSize(3); + } + + @Test + void 생성된_숫자는_1부터_9_사이이고_중복이_없다() { + NumberGenerator numberGenerator = new NumberGenerator(); + + for (int i = 0; i < 100; i++) { + List numbers = numberGenerator.generate().getNumbers(); + assertThat(numbers).hasSize(3); + assertThat(numbers).doesNotHaveDuplicates(); + assertThat(isRange(numbers)).isTrue(); + } + } + + private boolean isRange(List numbers) { + for (int i = 0; i < numbers.size(); i++) { + int number = numbers.get(i); + if (number < 1 || number > 9) return false; + } + return true; + } +}