Skip to content
Open

Greem #204

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
29 changes: 29 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# top-most EditorConfig file
root = true

[*]
# [encoding-utf8]
charset = utf-8

# [newline-lf]
end_of_line = lf

# [newline-eof]
insert_final_newline = true

[*.bat]
end_of_line = crlf

[*.java]
# [indentation-tab]
indent_style = tab

# [4-spaces-tab]
indent_size = 4
tab_width = 4

# [no-trailing-spaces]
trim_trailing_whitespace = true

[line-length-120]
max_line_length = 120
10 changes: 10 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.c text eol=lf
*.cpp text eol=lf
*.h text eol=lf

# exception for visual studio project configuration
*.sln text eol=crlf
*.vs text eol=crlf
*.csproj eol=crlf
*.props eol=crlf
*.filters eol=crlf
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,69 @@
# java-baseball-precourse
# java-baseball-precourse

> 컴퓨터가 제시하는 임의의 숫자 3개를 모두 맞히는게 목표인 게임
>
> 맞힐 때마다 라운드가 진행되며, 라운드 제한은 없음
>
> 라운드 진행 후, 각 세부 규칙마다 볼, 스트라이크를 출력
> 스트라이크 = 같은 수 + 같은 자리
>
> 볼 = 같은 수 + 다른 자리

## 핵심 로직

- 정답 생성: 1에서 9까지 서로 다른 임의의 수 3개 생성.

- 숫자 비교: 사용자가 입력한 수와 컴퓨터의 수를 비교.

- 같은 수 + 같은 자리 = 스트라이크

- 같은 수 + 다른 자리 = 볼

- 일치하는 숫자가 전혀 없음 = 낫싱

- 결과 출력: 계산된 힌트(스트라이크, 볼, 낫싱)를 형식에 맞춰 출력.

- 승리 조건: 3개의 숫자를 모두 맞히면 게임 종료 및 재시작/종료 선택창 출력.

### 인터페이스 제약 사항

**입력**

- 서로 다른 3자리의 수 (각 자리는 1~9)
- 게임 재시작/종료 선택 시 1(재시작) 또는 2(종료)
- 잘못된 값 입력 시 `IllegalArgumentException` 발생

**출력**

- 입력한 수에 대한 결과를 볼, 스트라이크 개수로 표시
- 하나도 없는 경우 "낫싱"
- 3개의 숫자를 모두 맞힐 경우 게임 종료 문구 출력

## 커밋 계획

### 사전 구성

1. style: 코드 포맷팅 구성 ✅
2. docs: 기능 요구 사항 정리 및 구현 계획 작성 ✅
3. chore: junit 학습 테스트 실행 및 적용 ✅

### 핵심 로직 (Domain)

4. feat: 1~9 서로 다른 임의의 수 3개 생성 구현 (Green) ✅
5. test: 볼, 스트라이크 비교 로직 테스트 ✅
6. feat: 볼, 스트라이크 비교 및 판별 로직 구현 (Green) ✅
7. refactor: 비교 로직 가독성 개선 (Refactor) ✅

### 게임 진행 (Controller)

8. feat: 게임 흐름 제어 구현 ✅
9. feat: 게임 메인 플레이 로직 및 도메인 생성 ✅
11. refactor: 전체 구조 리팩토링 및 뷰 컨트롤러 - 뷰 간 메소드 분리 (Refactor)

### 입출력 및 유효성 검사 (View/Validation)

12. test: 유저 숫자 입력 유효성 검사 (3자리 숫자, 중복 확인) ✅
13. feat: 유저 입력 파싱 및 예외 처리 구현 (Green) ✅
14. test: 게임 재시작/종료 입력 검증 ✅
15. feat: 재시작(1)/종료(2) 입력 처리 구현 (Green) ✅

6 changes: 5 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
plugins {
id 'java'
}

// 방법1
compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'
group = 'camp.nextstep.edu'
version = '1.0-SNAPSHOT'

Expand All @@ -18,8 +20,10 @@ repositories {
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
testImplementation 'org.assertj:assertj-core:3.25.3'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.0'
}


test {
useJUnitPlatform()
}
12 changes: 12 additions & 0 deletions src/main/java/baseball/BaseBall.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package baseball;

import baseball.coordinator.MainCoordinator;

public class BaseBall {

public static void main(String[] args) {
MainCoordinator mainCoordinator = new MainCoordinator();
mainCoordinator.start();
}
}

45 changes: 45 additions & 0 deletions src/main/java/baseball/DIContainer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package baseball;

import java.util.List;

import baseball.domain.number_generator.INumberGenerator;
import baseball.domain.number_generator.IntNumberGenerator;
import baseball.domain.score_calculator.BallCalculator;
import baseball.domain.score_calculator.IScoreCalculator;
import baseball.domain.score_calculator.StrikeCalculator;
import baseball.domain.strike_zone_calculator.IStrikeZoneCalculator;
import baseball.domain.strike_zone_calculator.StrikeZoneCalculator;

public class DIContainer {
public static DIContainer shared = new DIContainer();

public final Domain domain;
public final Util util;

private DIContainer() {
this.util = new Util();
this.domain = new Domain(util);
}

public static class Util {
final public int generateNumber = 3;

Util() {

}
}

public static class Domain {
final public INumberGenerator<List<Integer>> integerNumberGenerator;
final public IStrikeZoneCalculator strikeZoneCalculator;
final public IScoreCalculator strikeCalculator;
final public IScoreCalculator ballCalculator;

Domain(Util util) {
integerNumberGenerator = new IntNumberGenerator();
strikeCalculator = new StrikeCalculator();
ballCalculator = new BallCalculator(strikeCalculator);
strikeZoneCalculator = new StrikeZoneCalculator(this.strikeCalculator, this.ballCalculator);
}
}
}
9 changes: 9 additions & 0 deletions src/main/java/baseball/coordinator/Coordinator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package baseball.coordinator;

import java.util.List;

public interface Coordinator {
List<Coordinator> childCoordinators = List.of();

void start();
}
54 changes: 54 additions & 0 deletions src/main/java/baseball/coordinator/MainCoordinator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package baseball.coordinator;

import baseball.DIContainer;
import baseball.view_controller.home_view_controller.HomeViewController;
import baseball.view_controller.home_view_controller.HomeViewControllerDelegate;
import baseball.view_controller.play_view_controller.PlayViewController;
import baseball.view_controller.play_view_controller.PlayViewControllerDelegate;

public class MainCoordinator implements Coordinator, PlayViewControllerDelegate, HomeViewControllerDelegate {
private final DIContainer diContainer = DIContainer.shared;
private HomeViewController homeViewController;
private PlayViewController playViewController;

// --- Coordinator 구현 ---
@Override
public void start() {
showPlay();
}

// --- HomeViewControllerDelegate 구현 ---
@Override
public void userPlaySelected() {
showPlay();
}

@Override
public void userExitSelected() {
homeViewController = null;
playViewController = null;
}

// --- PlayViewControllerDelegate 구현 ---
@Override
public void playFinished() {
showHome();
}

private void showHome() {
this.homeViewController = new HomeViewController();
this.homeViewController.delegate = this;
this.homeViewController.render();
}

private void showPlay() {
// PlayViewController에 필요한 의존성을 여기서 생성하고 주입합니다.
this.playViewController = new PlayViewController(
diContainer.util.generateNumber,
diContainer.domain.integerNumberGenerator,
diContainer.domain.strikeZoneCalculator
);
this.playViewController.delegate = this;
this.playViewController.render();
}
}
34 changes: 34 additions & 0 deletions src/main/java/baseball/domain/entity/BallCountEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package baseball.domain.entity;

import java.util.Objects;

public class BallCountEntity {
public int ball;
public int strike;

public BallCountEntity(int strikeCount, int ballCount) {
this.strike = strikeCount;
this.ball = ballCount;
}

@Override
public boolean equals(Object o) {
// 1. 자기 자신과 비교하면 true
if (this == o)
return true;

// 2. null이거나 클래스 종류가 다르면 false
if (o == null || getClass() != o.getClass())
return false;

// 3. 값을 비교 (strike와 ball이 모두 같아야 true)
BallCountEntity that = (BallCountEntity)o;
return ball == that.ball && strike == that.strike;
}

@Override
public int hashCode() {
// 객체의 값을 기반으로 고유 번호 생성 (HashMap 등에서 필수)
return Objects.hash(ball, strike);
}
}
67 changes: 67 additions & 0 deletions src/main/java/baseball/domain/entity/StrikeZoneResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package baseball.domain.entity;

import java.util.Objects;

public class StrikeZoneResult {
private final Type type;
private final BallCountEntity ballCount;

// 생성자들을 private으로 막고 Factory 메서드 사용
private StrikeZoneResult(Type type, BallCountEntity ballCount) {
this.type = type;
this.ballCount = ballCount;
}

// Factory 메서드들 (Swift의 case 생성자 역할)
public static StrikeZoneResult nothing() {
return new StrikeZoneResult(Type.NOTHING, new BallCountEntity(0, 0));
}

public static StrikeZoneResult playing(BallCountEntity ballCount) {
return new StrikeZoneResult(Type.PLAYING, ballCount);
}

public static StrikeZoneResult complete(BallCountEntity ballCount) {
return new StrikeZoneResult(Type.COMPLETE, ballCount);
}

// Getter
public Type getType() {
return type;
}

public BallCountEntity get() {
return this.ballCount;
}

public int getBall() {
return this.ballCount.ball;
}

public int getStrike() {
return this.ballCount.strike;
}

@Override
public boolean equals(Object o) {
// 1. 주소값이 같으면 같은 객체
if (this == o)
return true;
// 2. null이거나 클래스 타입이 다르면 다른 객체
if (o == null || getClass() != o.getClass())
return false;

// 3. 타입 캐스팅 후 필드값 비교
StrikeZoneResult that = (StrikeZoneResult)o;
return type == that.type && Objects.equals(ballCount, that.ballCount);
}

@Override
public int hashCode() {
// equals에 사용된 필드들로 해시코드 생성
return Objects.hash(type, ballCount);
}

// 내부 Enum 정의 (종류 구분용)
public enum Type {NOTHING, PLAYING, COMPLETE}
}
3 changes: 3 additions & 0 deletions src/main/java/baseball/domain/entity/UserPlayMode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package baseball.domain.entity;

public enum UserPlayMode {Start, Finish}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package baseball.domain.number_generator;

import java.util.Date;

public interface INumberGenerator<T> {
T execute(Date date, Integer generateNumberCount);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package baseball.domain.number_generator;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

public class IntNumberGenerator implements INumberGenerator<List<Integer>> {

@Override
public List<Integer> execute(Date date, Integer generateNumberCount) {
Random random = new Random(date.getTime());
List<Integer> numbers = new ArrayList<>();
while (numbers.size() < generateNumberCount) {
int number = random.nextInt(9) + 1;
if (numbers.contains(number))
continue;
numbers.add(number);
}
return numbers;
}
}
Loading