Skip to content
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,20 @@
# java-baseball-precourse
# 카테캠 미니과제 1 - 숫자 야구
## 개요
- 카카오 테크 캠퍼스 1차 미니과제
- 1부터 9까지 서로 다른 수로 이루어진 3자리 숫자를 맞히는 게임
- 사용자는 컴퓨터가 무작위로 선택한 숫자를 맞히기 위해 입력을 반복, 입력 결과에 따른 힌트 부여

## 기술 스택
- 언어: Java 17
- 테스트 라이브러리: JUnit 5, AssertJ

## 기능 목록
- [x] **숫자 생성** - 게임 시작 시 1부터 9까지 서로 다른 수 3개를 무작위로 생성
- [x] **사용자 입력** - 사용자는 3자리 숫자를 입력
- [x] **입력 검증** - 입력받은 숫자가 3자리이며, 각 숫자가 1부터 9까지이고, 중복되지 않는지 확인
- [x] **결과 반환** - 입력받은 숫자와 컴퓨터가 생성한 숫자를 비교하여 힌트/결과 반환
- [x] **게임 반복** - 숫자를 맞혔을 경우, 사용자는 게임을 새로 시작할지 또는 종료할지 선택

## 개발 규칙 및 스타일 가이드
- **코드 스타일**: Google Java Style Guide을 기반으로 하되, 들여쓰기는 4 spaces를 사용
- **커밋 메시지**: AngularJS Git Commit Message Conventions을 기반으로 함
68 changes: 68 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Set;

public class Application {
private NumberGenerator numberGenerator = new NumberGenerator();
private InputHandler inputHandler = new InputHandler();
private InputValidator inputValidator = new InputValidator();
private ResultCalculator resultCalculator = new ResultCalculator();
private BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

public void startGame() {
boolean mainGameContinues = true;

while (mainGameContinues) {
Set<Integer> computerNumbers = numberGenerator.generateRandomNumbers();
boolean roundContinues = true;

while (roundContinues) {
roundContinues = playRound(computerNumbers);
}

mainGameContinues = askToContinue();
}
}

private boolean playRound(Set<Integer> computerNumbers) {
try {
String userInput = inputHandler.getUserInput();
inputValidator.validateInput(userInput);
String result = resultCalculator.calculateResult(computerNumbers, userInput);
System.out.println(result);

if ("3스트라이크".equals(result)) {
System.out.println("3개의 숫자를 모두 맞히셨습니다! 게임 종료");
return false;
}
return true;
} catch (IllegalArgumentException | IOException e) {
System.out.println("에러: " + e.getMessage());
return false; // 에러 발생 시 게임 종료
}
}

private boolean askToContinue() {
System.out.print("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요: ");
try {
String decision = br.readLine();
if ("1".equals(decision)) {
return true;
} else if ("2".equals(decision)) {
return false;
} else {
System.out.println("잘못된 입력입니다. 게임을 종료합니다.");
return false;
}
} catch (IOException e) {
System.out.println("입력 도중 오류가 발생했습니다: " + e.getMessage());
return false;
}
}

public static void main(String[] args) {
Application game = new Application();
game.startGame();
}
}
12 changes: 12 additions & 0 deletions src/main/java/InputHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class InputHandler {
private BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

public String getUserInput() throws IOException {
System.out.print("숫자를 입력해 주세요: ");
return br.readLine();
}
}
22 changes: 22 additions & 0 deletions src/main/java/InputValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import java.util.HashSet;
import java.util.Set;

public class InputValidator {
public void validateInput(String input) {
if (input == null || input.length() != 3) {
throw new IllegalArgumentException("입력은 정확히 3자리 숫자여야 합니다.");
}

Set<Character> seenDigits = new HashSet<>();

for (char ch : input.toCharArray()) {
if (!Character.isDigit(ch) || ch == '0') {
throw new IllegalArgumentException("입력은 1부터 9까지의 숫자여야 합니다.");
}

if (!seenDigits.add(ch)) {
throw new IllegalArgumentException("모든 숫자는 서로 달라야 합니다.");
}
}
}
}
16 changes: 16 additions & 0 deletions src/main/java/NumberGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Random;
import java.util.Set;

public class NumberGenerator {
public Set<Integer> generateRandomNumbers() {
Set<Integer> numbers = new LinkedHashSet<>();
Random random = new Random();
while (numbers.size() < 3) {
int number = 1 + random.nextInt(9); // 1 to 9
numbers.add(number);
}
return numbers;
}
}
31 changes: 31 additions & 0 deletions src/main/java/ResultCalculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class ResultCalculator {
public String calculateResult(Set<Integer> computerNumbers, String userInput) {
int strikes = 0;
int balls = 0;
List<Integer> computerList = new ArrayList<>(computerNumbers);
int[] userNumbers = userInput.chars().map(c -> c - '0').toArray();

for (int i = 0; i < 3; i++) {
if (userNumbers[i] == computerList.get(i)) {
strikes++;
} else if (computerList.contains(userNumbers[i])) {
balls++;
}
}

if (balls > 0 && strikes > 0) {
return balls + "볼 " + strikes + "스트라이크";
}
if (balls > 0) {
return balls + "볼";
}
if (strikes > 0) {
return strikes + "스트라이크";
}
return "낫싱";
}
}
19 changes: 19 additions & 0 deletions src/test/java/InputValidatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;

class InputValidatorTest {
@Test
void validateInput_ValidInput_ShouldNotThrowException() {
InputValidator validator = new InputValidator();
assertThatCode(() -> validator.validateInput("123")).doesNotThrowAnyException();
}

@Test
void validateInput_InvalidInput_ShouldThrowIllegalArgumentException() {
InputValidator validator = new InputValidator();
assertThatThrownBy(() -> validator.validateInput("112")).isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("모든 숫자는 서로 달라야 합니다.");
assertThatThrownBy(() -> validator.validateInput("abc")).isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("1부터 9까지의 숫자여야 합니다.");
}
}
16 changes: 16 additions & 0 deletions src/test/java/NumberGeneratorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;

import java.util.Set;

class NumberGeneratorTest {
@Test
void generateRandomNumbers_ShouldGenerateThreeUniqueNumbersBetweenOneAndNine() {
NumberGenerator generator = new NumberGenerator();
Set<Integer> numbers = generator.generateRandomNumbers();

assertThat(numbers).hasSize(3);
assertThat(numbers).allMatch(num -> num >= 1 && num <= 9);
assertThat(numbers).doesNotHaveDuplicates();
}
}
31 changes: 31 additions & 0 deletions src/test/java/ResultCalculatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;

import java.util.LinkedHashSet;
import java.util.Set;

class ResultCalculatorTest {
@Test
void calculateResult_WhenAllMatch_ShouldReturnThreeStrikes() {
ResultCalculator calculator = new ResultCalculator();
Set<Integer> computerNumbers = new LinkedHashSet<>();
computerNumbers.add(1);
computerNumbers.add(2);
computerNumbers.add(3);
String result = calculator.calculateResult(computerNumbers, "123");

assertThat(result).isEqualTo("3스트라이크");
}

@Test
void calculateResult_NoMatches_ShouldReturnNoting() {
ResultCalculator calculator = new ResultCalculator();
Set<Integer> computerNumbers = new LinkedHashSet<>();
computerNumbers.add(4);
computerNumbers.add(5);
computerNumbers.add(6);
String result = calculator.calculateResult(computerNumbers, "123");

assertThat(result).isEqualTo("낫싱");
}
}