Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e1bebd5
feat: add requirement
sinsehwan Jan 28, 2026
2e4f30a
feat(model): add basic baseballgameNumber model
sinsehwan Jan 28, 2026
5237c9d
feat(model): implement gameAnswerType
sinsehwan Jan 28, 2026
17d9bb9
feat(util): implement make random 3-digit logic
sinsehwan Jan 28, 2026
d4ff314
feat(view): add basic GameView
sinsehwan Jan 28, 2026
38396df
feat(controller): implement basic gameController
sinsehwan Feb 2, 2026
041bcd8
chore(util): remove duplicated number in random generator
sinsehwan Feb 7, 2026
ea82c20
chore(model): add ball duplicated check logic
sinsehwan Feb 7, 2026
e6c2386
chore(model): refactor baseballGameNumber compare logic
sinsehwan Feb 7, 2026
4c60365
chore(view): delegate print turn msg logic from controller to view
sinsehwan Feb 7, 2026
4daedf0
chore(view): move errormsg print logic into view
sinsehwan Feb 7, 2026
0f282cf
feat(view): implement Enum ErrorMessage
sinsehwan Feb 7, 2026
6b238b9
chore(controller): refactor game turn logic
sinsehwan Feb 7, 2026
ffae489
feat(controller): add exception handling for positive number check & …
sinsehwan Feb 7, 2026
5c3c019
feat(view): introduce inputView
sinsehwan Feb 8, 2026
ba269d7
chore: add final
sinsehwan Feb 8, 2026
0a8aa89
feat(view): refactor parseUserInput logic
sinsehwan Feb 8, 2026
d906209
feat(model): add baseballGameNumber model test code
sinsehwan Feb 8, 2026
c4ad49e
feat(model): fix space trim
sinsehwan Feb 8, 2026
ac18f72
fix: test class name
sinsehwan Feb 8, 2026
64d8b53
feat: add GameAnswerTypeTest, IntegerParserTest
sinsehwan Feb 8, 2026
a9bb0fa
feat(util): implement randomNumberGeneratorTest
sinsehwan Feb 8, 2026
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
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
# java-baseball-precourse
# java-baseball-precourse

## 구현할 기능 목록

- [x] 임의의 난수 3자리 생성하기
- [x] 사용자 응답 처리
- [x] 같은 수 & 같은 자리 (스트라이크) 분기 처리
- [x] 같은 수 & 다른 자리 (볼) 분기 처리
- [x] 같은 수 없는 경우 (포볼, 낫싱) 분기 처리
- [x] depth 최대 2가 되도록 리팩토링
- [x] 단위 테스트 코드 추가하기
84 changes: 84 additions & 0 deletions src/main/java/controller/GameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package controller;

import model.BaseballGameNumber;
import model.GameAnswerType;
import util.IntegerParser;
import util.RandomNumberGenerator;
import view.ErrorMessage;
import view.GameView;
import view.InputView;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Objects;

public class GameController {
private static final String RESTART_COMMAND = "1";
private static final String EXIT_COMMAND = "2";

private RandomNumberGenerator randomNumberGenerator;
private final GameView gameView;
private final InputView inputView;
private final BufferedReader br;

public GameController() {
this.randomNumberGenerator = new RandomNumberGenerator();
this.gameView = new GameView();
this.inputView = new InputView(gameView);
br = new BufferedReader(new InputStreamReader(System.in));
}

public static void main(String[] args) {
try {
new GameController().run();
}
catch (IOException e) {
System.out.println(ErrorMessage.SYSTEM_IO_ERROR.getMsg());
}
}

public void run() throws IOException {
String gameEndUserResponse;

do {
gameEndUserResponse = playSingleGame();
} while(Objects.equals(gameEndUserResponse, RESTART_COMMAND));

if (!Objects.equals(gameEndUserResponse, EXIT_COMMAND)) {
gameView.printErrorMsg(ErrorMessage.ABNORMAL_EXIT.getMsg());
return;
}

gameView.printGameEndMsg();

br.close();
gameView.releaseResource();
}

public String playSingleGame() throws IOException {
int answer = randomNumberGenerator.makeRand3digit();
BaseballGameNumber answerNumber = new BaseballGameNumber(IntegerParser.toIntArray(answer));

int userInput = -1;
while (answer != userInput) {
userInput = playTurn(answerNumber);
}

gameView.printSingleGameEndMsg();
return br.readLine();
}

private int playTurn(BaseballGameNumber answerNumber) throws IOException {
int userInput;
gameView.printTurnMsg();
userInput = inputView.getUserInput();

BaseballGameNumber userNumber = new BaseballGameNumber(IntegerParser.toIntArray(userInput));

gameView.printResult(userNumber.compare(answerNumber));
return userInput;
}
}
69 changes: 69 additions & 0 deletions src/main/java/model/BaseballGameNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package model;

import java.util.ArrayList;
import java.util.Objects;

public class BaseballGameNumber {
private final ArrayList<Integer> number;

public BaseballGameNumber(ArrayList<Integer> arr) {
this.number = arr;
}

public GameAnswerType compare(BaseballGameNumber other) {
int strikes = calStrikes(other);
int balls = calBalls(strikes, other);

return new GameAnswerType(strikes, balls);
}

private int calStrikes(BaseballGameNumber other) {
int strike = 0;
for (int i = 0; i < this.number.size(); i++) {
if(isStrike(i, other)) {
strike += 1;
}
}

return strike;
}

private int calBalls(int strikes, BaseballGameNumber other) {
return this.calMatchCount(other) - strikes;
}

private boolean isStrike(int index, BaseballGameNumber other) {
return Objects.equals(this.number.get(index), other.number.get(index));
}

private int calMatchCount(BaseballGameNumber other) {
boolean[] originFlags = this.getNumberFlags();
boolean[] otherFlags = other.getNumberFlags();

int matchCount = 0;

for (int i = 0; i < originFlags.length; i++) {
matchCount = getMatchCount(originFlags, i, otherFlags, matchCount);
}

return matchCount;
}

private static int getMatchCount(boolean[] originFlags, int i, boolean[] otherFlags, int matchCount) {
if(originFlags[i] && otherFlags[i]) {
matchCount += 1;
}
return matchCount;
}

private boolean[] getNumberFlags() {
boolean[] flags = new boolean[10];

for (int num : this.number) {
flags[num] = true;
}

return flags;
}

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

public class GameAnswerType {
private final int strikeCount;
private final int ballCount;

public GameAnswerType() {
this.strikeCount = 0;
this.ballCount = 0;
}

public GameAnswerType(int strikeCount, int ballCount){
this.strikeCount = strikeCount;
this.ballCount = ballCount;
}

public int getStrikeCount() {
return strikeCount;
}

public int getBallCount() {
return ballCount;
}

public String getAnswer() {
if (strikeCount + ballCount == 0) {
return "낫싱";
}
StringBuilder sb = new StringBuilder();

if (strikeCount > 0) {
sb.append(strikeCount);
sb.append("스트라이크 ");
}

if (ballCount > 0) {
sb.append(ballCount);
sb.append("볼 ");
}

return sb.toString().trim();
}
}
18 changes: 18 additions & 0 deletions src/main/java/util/IntegerParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package util;

import java.util.ArrayList;
import java.util.Collections;

public class IntegerParser {
public static ArrayList<Integer> toIntArray(int number) {
ArrayList<Integer> arr = new ArrayList<>();

while (number > 0) {
arr.add(number % 10);
number /= 10;
}
Collections.reverse(arr);

return arr;
}
}
34 changes: 34 additions & 0 deletions src/main/java/util/RandomNumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package util;

import java.util.Random;

public class RandomNumberGenerator {
private final Random random;

public RandomNumberGenerator() {
this.random = new Random();
}

public int makeRand3digit() {
boolean[] isDuplicated = new boolean[10];

int answer = 0;

for (int i = 0; i < 3; i++) {
answer *= 10;
answer += getNonDuplicatedNumber(isDuplicated);
}

return answer;
}

private int getNonDuplicatedNumber(boolean[] isDuplicated) {
while(true) {
int randNum = random.nextInt(9) + 1;
if (!isDuplicated[randNum]) {
isDuplicated[randNum] = true;
return randNum;
}
}
}
}
21 changes: 21 additions & 0 deletions src/main/java/view/ErrorMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package view;

public enum ErrorMessage {
SYSTEM_IO_ERROR("System IO Error"),
ABNORMAL_EXIT("Abnormal Exit"),
INVALID_INPUT_NUMBER("숫자만 입력해주세요."),
INVALID_INPUT_LENGTH("3자리의 숫자를 입력해주세요."),
CONTAINS_ZERO("0이 포함된 숫자는 입력할 수 없습니다."),
NOT_POSITIVE("양수로 입력해주세요.");

private static final String ERROR_PREFIX_MSG = "[ERROR]: ";
private final String msg;

ErrorMessage(String msg) {
this.msg = msg;
}

public String getMsg() {
return ERROR_PREFIX_MSG + msg;
}
}
49 changes: 49 additions & 0 deletions src/main/java/view/GameView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package view;

import model.GameAnswerType;

import java.io.*;

public class GameView {
private static final String TURN_MSG = "숫자를 입력해주세요 : ";
private static final String SINGLE_GAME_END_MSG = "3개의 숫자를 모두 맞히셨습니다! 게임 끝\n 게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.\n";
private static final String GAME_END_MSG = "게임을 완전히 종료합니다.";

private final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

public void printTurnMsg() throws IOException {
bw.write(TURN_MSG);
bw.flush();
}

public void printResult(GameAnswerType result) throws IOException {
bw.write(result.getAnswer());
bw.newLine();
bw.flush();
}

public void printSingleGameEndMsg() throws IOException {
bw.write(SINGLE_GAME_END_MSG);
bw.flush();
}

public void printGameEndMsg() throws IOException {
bw.write(GAME_END_MSG);
bw.flush();
}

public void printErrorMsg(String message) {
try {
bw.write(message);
bw.newLine();
bw.flush();
}
catch (IOException e) {
System.out.println(message);
}
}

public void releaseResource() throws IOException {
bw.close();
}
}
Loading