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
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,24 @@
# java-baseball-precourse
# java-baseball-precourse

## 구현 기능 목록

### core

- [x] 무작위 야구 게임 숫자 생성하는 기능
- [x] 컴퓨터 숫자, 사용자 숫자 비교해 결과 계산하는 기능
- [x] 야구 게임 숫자, 재시작 여부를 질의하고 게임 동작을 수행하는 기능

### io

- [x] 평문 string 입력을 야구 게임 숫자로 format 하는 기능
- [x] 평문 string 입력을 게임 재시작 Yes or no 로 format 하는 기능

- [x] 사용자 야구 게임 숫자 입력받는 기능
- [x] 사용자 게임 재시작 여부 입력받는 기능

- [x] 컴퓨터 숫자, 사용자 숫자 비교 결과 출력하는 기능

### app

- [x] 게임 시작, 재시작, 종료하는 기능
- [x] 게임 시작, 종료 구문 보여주는 기능
165 changes: 165 additions & 0 deletions src/main/java/com/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package com;

import com.core.Calculator;
import com.core.Game;
import com.core.NumberGenerator;
import com.dto.Number;
import com.dto.Score;
import com.io.NumberFormatter;
import com.io.NumberReceiver;
import com.io.Printer;
import com.io.YesOrNoFormatter;
import com.io.YesOrNoReceiver;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.security.SecureRandom;
import java.util.Random;
import java.util.logging.Logger;

public class Application {

private static final Random RANDOM;
private static final int
NUMBER_LOWER_BOUND_INCLUSIVE,
NUMBER_UPPER_BOUND_EXCLUSIVE;
private static final NumberGenerator NUMBER_GENERATOR;

private static final BufferedReader BUFFERED_READER;

private static final Printer PRINTER;
private static final NumberFormatter NUMBER_FORMATTER;
private static final NumberReceiver NUMBER_RECEIVER;
private static final YesOrNoFormatter YES_OR_NO_FORMATTER;
private static final YesOrNoReceiver YES_OR_NO_RECEIVER;

private static final Calculator CALCULATOR;

private static final Logger LOGGER;
private static final boolean DEBUG = false;

static {
RANDOM = new SecureRandom();

NUMBER_LOWER_BOUND_INCLUSIVE = 1;
NUMBER_UPPER_BOUND_EXCLUSIVE = 10;

NUMBER_GENERATOR = new NumberGenerator(
RANDOM,
NUMBER_LOWER_BOUND_INCLUSIVE, NUMBER_UPPER_BOUND_EXCLUSIVE
);

BUFFERED_READER = new BufferedReader(
new InputStreamReader(System.in)
);

PRINTER = new Printer();
NUMBER_FORMATTER = new NumberFormatter();
NUMBER_RECEIVER = new NumberReceiver(
BUFFERED_READER, NUMBER_FORMATTER
);
YES_OR_NO_FORMATTER = new YesOrNoFormatter();
YES_OR_NO_RECEIVER = new YesOrNoReceiver(
BUFFERED_READER, YES_OR_NO_FORMATTER
);

CALCULATOR = new Calculator();

LOGGER = Logger.getLogger(Application.class.getName());
}

public static void main(String[] args) {
printWelcomeMessage();

// game loop 를 만들어 게임을 시작, 종료, 재시작한다.
Internal gameLoop = new Internal();
while (true) {

System.out.print("\n------------------------------------------------------------\n");

boolean retryGame = gameLoop.runGame();

if (!retryGame) {
System.out.print("""

--<: 게임을 종료합니다. (^^)7
""");
break;
}
}

printGoodbyeMessage();
}

public static void printWelcomeMessage() {
System.out.print("""
------------------------------
| Hello world! |
------------------------------
""");
}

public static void printGoodbyeMessage() {
System.out.print("""
------------------------------------------------------------

--------------------------------
| Goodbye world! |
--------------------------------
""");
}

private static class Internal {

/**
* 한 게임을 진행하고 재시작 여부를 반환한다.
*
* @return 게임 재시작 여부
*/
private boolean runGame() {
System.out.print("""
\n--o: 게임 난수 생성중...
""");

// 새로운 게임을 생성한다.
Game game = this.initGame();

System.out.print("""
-->: 게임 난수 생성 완료. 숫자 야구 게임이 시작되었습니다!

""");

// 게임이 끝날때까지 진행한다.
while (true) {
Number userNumber = game.queryNumberInputAndReceive();

Score score = game.calcScoreAndPrintResult(userNumber);

if (score.isPerfect()) {
break;
}
}

// 게임 재시작 여부를 반환한다.
return game.queryRetryYesOrNoAndReceive();
}

/**
* 새로운 게임을 생성해 제공한다.
*/
private Game initGame() {
Number gameNumber = NUMBER_GENERATOR.getRandomNumber();

System.out.println(gameNumber);

if (DEBUG) {
LOGGER.info("Game number :" + gameNumber);
}

return new Game(
gameNumber, PRINTER,
NUMBER_RECEIVER, YES_OR_NO_RECEIVER,
CALCULATOR
);
}
}
}
61 changes: 61 additions & 0 deletions src/main/java/com/core/Calculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.core;

import com.dto.Number;
import com.dto.Score;

/**
* 두 {@link Number} 로 야구 게임 결과를 계산하는 클래스
*/
public class Calculator {

/**
* 컴퓨터, 유저 숫자를 비교해 결과를 계산해 제공한다.
*
* @param gameNumber 컴퓨터 숫자
* @param userNumber 유저 숫자
*/
public Score calcScore(Number gameNumber, Number userNumber) {
int numOfStrikes = this.calcNumOfStrikes(gameNumber, userNumber);
int numOfBalls = this.calcNumOfBalls(gameNumber, userNumber);
return new Score(numOfStrikes, numOfBalls);
}

private int calcNumOfStrikes(Number gameNumber, Number userNumber) {
int cnt = 0;

if (gameNumber.first() == userNumber.first()) {
cnt++;
}

if (gameNumber.second() == userNumber.second()) {
cnt++;
}

if (gameNumber.third() == userNumber.third()) {
cnt++;
}

return cnt;
}

private int calcNumOfBalls(Number gameNumber, Number userNumber) {
int cnt = 0;
int userNumFirst = userNumber.first();
int userNumSecond = userNumber.second();
int userNumThird = userNumber.third();

if (gameNumber.first() != userNumFirst && gameNumber.contains(userNumFirst)) {
cnt++;
}

if (gameNumber.second() != userNumSecond && gameNumber.contains(userNumSecond)) {
cnt++;
}

if (gameNumber.third() != userNumThird && gameNumber.contains(userNumThird)) {
cnt++;
}

return cnt;
}
}
84 changes: 84 additions & 0 deletions src/main/java/com/core/Game.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.core;

import com.dto.Number;
import com.dto.Score;
import com.io.NumberReceiver;
import com.io.Printer;
import com.io.YesOrNoReceiver;
import java.util.Optional;

/**
* 어느 한 야구 게임을 의미하는 클래스
*/
public class Game {

/**
* 게임 정답
*/
private final Number gameNumber;

private final Printer printer;
private final NumberReceiver numberReceiver;
private final YesOrNoReceiver yesOrNoReceiver;

private final Calculator calculator;

public Game(
Number gameNumber, Printer printer,
NumberReceiver numberReceiver, YesOrNoReceiver yesOrNoReceiver,
Calculator calculator
) {
this.gameNumber = gameNumber;
this.printer = printer;
this.numberReceiver = numberReceiver;
this.yesOrNoReceiver = yesOrNoReceiver;
this.calculator = calculator;
}

/**
* 콘솔로 숫자를 입력받아 제공한다.
* <p>
* 올바른 입력을 받을때까지 무한정 loop 한다.
*/
public Number queryNumberInputAndReceive() {
Number userNumber = null;

while (userNumber == null) {
// 숫자 입력해달라고 출력한다.
printer.printNumberInputQuery();

// 입력 받는다.
Optional<Number> opt = numberReceiver.receiveInput();

// 올바르지 않은 입력이다.
if (opt.isEmpty()) {
printer.printInvalidNumberInput();
continue;
}

userNumber = opt.get();
}

return userNumber;
}

/**
* 게임 정답과 비교해 결과를 생성하고 콘솔에 출력한다.
*/
public Score calcScoreAndPrintResult(Number userNumber) {
Score score = calculator.calcScore(gameNumber, userNumber);

printer.printScore(score);

return score;
}

/**
* 콘솔로 게임 재시작 여부 입력을 받는다.
*/
public boolean queryRetryYesOrNoAndReceive() {
printer.printGameRetryQuery();
return yesOrNoReceiver.receiveInput()
.orElse(false);
}
}
52 changes: 52 additions & 0 deletions src/main/java/com/core/NumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.core;

import com.dto.Number;
import java.util.Random;

/**
* 무작위 {@link Number} 생성해주는 클래스
*/
public class NumberGenerator {

private final Random rand;
private final int lowerBoundInclusive, upperBoundExclusive;

/**
* @param lowerBoundInclusive {@code bound <= (생성되는 수)}
* @param upperBoundExclusive {@code (생성되는 수) < bound}
*/
public NumberGenerator(Random rand, int lowerBoundInclusive, int upperBoundExclusive) {
this.rand = rand;
this.lowerBoundInclusive = lowerBoundInclusive;
this.upperBoundExclusive = upperBoundExclusive;

if (lowerBoundInclusive >= upperBoundExclusive) {
throw new IllegalArgumentException("Lower bound must less than upper bound");
}
}

/**
* 랜덤한 {@link Number} 를 생성해 제공한다.
*/
public Number getRandomNumber() {
boolean[] recordTable = new boolean[upperBoundExclusive - lowerBoundInclusive];

int first = getRandomNumberExcept(recordTable);
int second = getRandomNumberExcept(recordTable);
int third = getRandomNumberExcept(recordTable);

return new Number(first, second, third);
}

private int getRandomNumberExcept(boolean[] numberTable) {
while (true) {
int randNum = rand.nextInt(lowerBoundInclusive, upperBoundExclusive);
int i = randNum - lowerBoundInclusive;

if (!numberTable[i]) {
numberTable[i] = true;
return randNum;
}
}
}
}
Loading