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
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,78 @@
# java-racingcar-precourse

# 기능 목록
1. 자동차 이름을 입력받기
2. 자동차이름 예외사항 예외처리하기(5자 제한 필요)
3. 시도할 횟수를 입력 받기위한 출력문 출력, 입력 받기 (정수가 아니면 잘못된 입력으로 예외처리)
4. 시도횟수 만큼 각 자동차의 주사위를 던지고, 전진시키기, 전진 횟수를 Map에 반영하기
5. Map을 기반으로 우승자 선별하기, 출력하기

# NsTest
NsTest에 정의된 init
```java
@BeforeEach //모든 테스트 수행전에 실행 할것
protected final void init() {
this.standardOut = System.out; //기존 표준 출력을 저장해놓는다 나중에 여기에 한번에 출력하기 위해서
this.captor = new ByteArrayOutputStream(); // 출력을 가로채서 담을 ByteStream을 정의한다, 출력 내용을 검증하기 위해서
System.setOut(new PrintStream(this.captor)); //출력을 가로챈다!
}
```
NsTest에 정의된 run
```java
protected final void run(String... args) {
try {
this.command(args); //입력 stream 설정 (배열로 전달 된 여러 입력 값을 개행 시켜서 실제 입력처럼 만들기)
this.runMain(); //NsTest를 상속한 클래스에서 구현한 메인 Test를 실행해라
} finally {
Console.close(); //Console 클래스 안의 Scanner 자원 종료
}

}
```
NsTest에 정의된 command
```java
private void command(String... args) {
byte[] buf = String.join("\n", args).getBytes();
System.setIn(new ByteArrayInputStream(buf));
}
```
1. 배열로 전달된 배열을 개행시켜서 하나의 문자열로 만들기
2. String -> Byte
3. System.in에 설정 실제 사용자가 입력한것 처럼
Console 라이브러리는 System.in에서 입력을 받아오기 때문에 System.setIn을 하면 실제 사용자가 입력한것과 같은 효과

```java
public class Console {
private static Scanner scanner;

private Console() {
}

public static String readLine() {
return getInstance().nextLine();
}

public static void close() {
if (scanner != null) {
scanner.close();
scanner = null;
}

}

private static Scanner getInstance() {
if (scanner == null) {
scanner = new Scanner(System.in); // => 요기서
}

return scanner;
}
}
```
NsTest에 정의된 output

```java
protected final String output() {
return this.captor.toString().trim(); //가로챈 출력을 반환
}
```
7 changes: 6 additions & 1 deletion src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package racingcar;


import camp.nextstep.edu.missionutils.Console;

public class Application {
private final static RacingCarGame racingCarGame = new RacingCarGame();

public static void main(String[] args) {
// TODO: 프로그램 구현
racingCarGame.play();
}
}
93 changes: 93 additions & 0 deletions src/main/java/racingcar/RacingCarGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package racingcar;

import camp.nextstep.edu.missionutils.Console;
import camp.nextstep.edu.missionutils.Randoms;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class RacingCarGame {
Map<String, Integer> carMap = new HashMap<>();

public Map<String, Integer> getCarMap() {
return carMap;
}

public void play() {
readCarNames();
int numOfGames = receiceNumOfGames();
System.out.println("\n실행결과");
for (int i = 0; i < numOfGames; i++) {
playOneRound();
}
announceWinners();
}

public void playOneRound() {
for (Map.Entry<String, Integer> entry : carMap.entrySet()) {
int numOfDice = Randoms.pickNumberInRange(0, 9);
moveForward(entry.getKey(), numOfDice);
System.out.println(entry.getKey() + " : " + "-".repeat(carMap.get(entry.getKey())));
}
System.out.println("\n");
}

private void moveForward(String carName, int numOfDice) {
if (numOfDice >= 4) {
carMap.put(carName, carMap.get(carName) + 1);
}
}


public void readCarNames() {
System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)");
String input = Console.readLine();
String[] carNames = input.split(",");
for (String carName : carNames) {
validateCarName(carName);
carMap.put(carName, 0);
}
}

private void validateCarName(String carName) {
if (carName.length() > 5) {
throw new IllegalArgumentException("자동차 이름은 5자 이하만 가능합니다.");
}
}

public int receiceNumOfGames() {
System.out.println("시도할 횟수는 몇 회인가요?");
String numOfGames = Console.readLine();
return validateNumOfGames(numOfGames);
}

private int validateNumOfGames(String numOfGames) {
try {
return Integer.parseInt(numOfGames);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("정수가 아닌 값을 입력했습니다.");
}
}

private List<String> calculateWinners() {
if (carMap.isEmpty()) return Collections.emptyList();
int max = carMap.values().stream().mapToInt(Integer::intValue).max().orElse(0);
return carMap.entrySet().stream()
.filter(e -> e.getValue() == max)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}

public String getWinnersLine() {
List<String> winners = calculateWinners();
return "최종 우승자 : " + String.join(", ", winners);
}


public void announceWinners() {
System.out.println();
System.out.println(getWinnersLine());
}
}
77 changes: 77 additions & 0 deletions src/test/java/racingcar/RacingCarGameTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package racingcar;

import camp.nextstep.edu.missionutils.Console;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Map;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class RacingCarGameTest {

private PrintStream standardOut;
private OutputStream captor;

RacingCarGame racingCarGame = new RacingCarGame();

@BeforeEach
void beforeEach() {
this.standardOut = System.out;
this.captor = new ByteArrayOutputStream();
System.setOut(new PrintStream(this.captor));
}

@AfterEach
void afterEach() {
Console.close(); // Console 내부 Scanner 닫기
System.setOut(this.standardOut);
}


@Test
void 자동차이름_읽기_테스트() {
setInput("pobi,woni");
racingCarGame.readCarNames();
Assertions.assertThat(output()).contains("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)");
Map<String, Integer> carMap = racingCarGame.getCarMap();
Assertions.assertThat(carMap).hasSize(2);
Assertions.assertThat(carMap).containsEntry("pobi", 0);
Assertions.assertThat(carMap).containsEntry("woni", 0);
System.setOut(this.standardOut);
}

@Test
void 자동차이름_읽기_예외테스트() {
setInput("pobi,sonnii");
Assertions.assertThatThrownBy(() -> racingCarGame.readCarNames())
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("자동차 이름은 5자 이하만 가능합니다.");
}

@Test
void 게임횟수_읽기_테스트() {
setInput("5");
Assertions.assertThat(racingCarGame.receiceNumOfGames()).isEqualTo(5);
}

@Test
void 게임횟수_읽기_예외_테스트() {
setInput("ㅅ");
Assertions.assertThatThrownBy(() -> racingCarGame.receiceNumOfGames())
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("정수가 아닌 값을 입력했습니다.");
}

private static void setInput(String... args) {
byte[] buf = String.join("\n", args).getBytes();
System.setIn(new ByteArrayInputStream(buf));
}

private final String output() {
return this.captor.toString().trim();
}
}