diff --git a/README.md b/README.md index 90a5236..2b5e2a2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,52 @@ # 미션 - 자동차 경주 게임 +## 📋 기능 구현 목록 +- 사용자 입력 요구문 출력 +- 사용자 입력 + - 예외 처리 + - 플레이어 생성 + +- 경주 게임 실행 + - 무작위 수 생성 + - 4이상 인지 비교 + - 1초 간격두고 라운드 결과 출력 + +- 우승자 판별 + - 우승자 출력 + +## 🚬 클래스와 메서드? +- Application +- Receiver + - receiveName + - receiveNumber +- Printer + - printMessage +- GamePlayer + - run + - judgeToMove + - makeArrayAfterGettingName + - makeCountAfterGettingNumber + - launchAllRound + - checkWhoIsWinner +- Generator + - generateRandomNumber +- Car + - getPosition + - getName + - moveForward +- Validate + - validateName(s) + - inputNothing(s) + - inputCommaInARow(s) + - inputCharactersOtherThanName(s) + - startWithComma(s) + - endWithComma(s) + - over5Characters(s) + - inputSameName(s)) + - validateNumber(s) +- Message + + ## 🚀 기능 요구사항 - 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. diff --git a/build.gradle b/build.gradle index a40e892..5730ddc 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ repositories { dependencies { testCompile("org.assertj:assertj-core:3.14.0") - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' + testCompile('org.junit.jupiter:junit-jupiter:5.6.0') testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' } diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 0000000..cc58b44 --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,8 @@ +import domain.GamePlayer; + +public class Application { + public static void main(String[] args){ + GamePlayer gamePlayer = new GamePlayer(); + gamePlayer.run(); + } +} diff --git a/src/main/java/domain/Car.java b/src/main/java/domain/Car.java index e5b4a0a..f3e992f 100644 --- a/src/main/java/domain/Car.java +++ b/src/main/java/domain/Car.java @@ -8,5 +8,15 @@ public Car(String name) { this.name = name; } - // 추가 기능 구현 + public int getPosition() { + return this.position; + } + + public String getName() { + return this.name; + } + + public void moveForward() { + this.position++; + } } diff --git a/src/main/java/domain/CarManager.java b/src/main/java/domain/CarManager.java new file mode 100644 index 0000000..5f37ddc --- /dev/null +++ b/src/main/java/domain/CarManager.java @@ -0,0 +1,19 @@ +package domain; + +import io.Receiver; + +public class CarManager { + private final Receiver receiver = new Receiver(); + + public Car[] produceCar(){ + String[] listOfName = receiver.receiveName(); + + Car[] cars = new Car[listOfName.length]; + for (int i = 0; i < listOfName.length; i++) + cars[i] = new Car(listOfName[i]); + + return cars; + } + + +} diff --git a/src/main/java/domain/GamePlayer.java b/src/main/java/domain/GamePlayer.java new file mode 100644 index 0000000..dcb5a95 --- /dev/null +++ b/src/main/java/domain/GamePlayer.java @@ -0,0 +1,54 @@ +package domain; + +import io.Printer; +import io.Receiver; + +import java.util.ArrayList; +import java.util.List; + +public class GamePlayer { + private final int WINNER_CONDITION = 4; + private final Printer printer = new Printer(); + private final Receiver receiver = new Receiver(); + private final Generator generator= new Generator(); + private final CarManager carManager = new CarManager(); + private final Referee referee = new Referee(); + + public void judgeToMove(Car car, int randomNumber) { + if (randomNumber >= WINNER_CONDITION) + car.moveForward(); + } + + public int receiveRepeatCount() { + printer.printGeneralMessage("INPUT_COUNT"); + return receiver.receiveNumber(); + } + + public void launchAllRound(Car[] cars, int countRound) { + for (int i = 0; i < countRound; i++) { + for (Car car : cars) { + judgeToMove(car, generator.generateRandomNumber()); + printer.printProgressWithSymbol(car.getName(), car.getPosition()); + } + printer.printGeneralMessage("DEFAULT_SPACE"); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + public void run() { + printer.printGeneralMessage("INPUT_NAMEOFCAR"); + Car[] cars = carManager.produceCar(); + int countRound = receiveRepeatCount(); + + printer.printGeneralMessage("OPERATION_RESULT"); + launchAllRound(cars, countRound); + + List winner = referee.checkWinner(cars); + printer.printWinner(printer.makeWinnerToString(winner)); + } +} diff --git a/src/main/java/domain/Generator.java b/src/main/java/domain/Generator.java new file mode 100644 index 0000000..cca5a5a --- /dev/null +++ b/src/main/java/domain/Generator.java @@ -0,0 +1,9 @@ +package domain; + +public class Generator { + private static final int MAX_LIMIT_NUMBER = 10; + + public int generateRandomNumber() { + return (int) ((Math.random() * 10000) % MAX_LIMIT_NUMBER); + } +} diff --git a/src/main/java/domain/Referee.java b/src/main/java/domain/Referee.java new file mode 100644 index 0000000..9055474 --- /dev/null +++ b/src/main/java/domain/Referee.java @@ -0,0 +1,32 @@ +package domain; + +import java.util.ArrayList; +import java.util.List; + +public class Referee { + private int maxNumber = 0; + private final List result = new ArrayList<>(); + + public List checkWinner(Car[] cars) { + findMaxNumber(cars); + judgeWinner(cars); + return result; + } + + public void findMaxNumber(Car[] cars) { + int firstPlacePosition = maxNumber; + + for (Car car : cars) { + if (car.getPosition() > firstPlacePosition) + firstPlacePosition = car.getPosition(); + } + maxNumber = firstPlacePosition; + } + + public void judgeWinner(Car[] cars) { + for (Car car : cars) { + if (car.getPosition() == maxNumber) + result.add(car); + } + } +} diff --git a/src/main/java/domain/Validator.java b/src/main/java/domain/Validator.java new file mode 100644 index 0000000..2c7dce5 --- /dev/null +++ b/src/main/java/domain/Validator.java @@ -0,0 +1,103 @@ +package domain; + +import io.Printer; + +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; + +public class Validator implements ValidatorInterface { + private static final int MAX_NAME_SIZE = 5; + private static final String ONLY_NUMBER = "^[0-9]+$"; + private static final String COMMA_IN_A_ROW = "^(,,)+$"; + private static final String OTHER_THAN_CHARACTER = "^[a-zA-Z,]+$"; + + Printer printer = new Printer(); + + @Override + public boolean isCorrectName(String s) { + return inputNothing(s) + && inputCommaInARow(s) + && inputOtherThanCharacter(s) + && startWithComma(s) + && endWithComma(s) + && overSizeCharacters(s) + && inputSameName(s); + } + + @Override + public boolean validateNumber(String s) { + return s.matches(ONLY_NUMBER); + } + + @Override + public boolean inputNothing(String s) { + if (s.equals("")) { + printer.printExceptionMessage("INPUT_NOTHING"); + return false; + } + return true; + } + + @Override + public boolean inputCommaInARow(String s) { + if (Pattern.matches(COMMA_IN_A_ROW, s)) { + printer.printExceptionMessage("INPUT_COMMA_IN_A_ROW"); + return false; + } + return true; + } + + @Override + public boolean startWithComma(String s) { + if (s.charAt(0) == ',') { + printer.printExceptionMessage("START_WITH_COMMA"); + return false; + } + return true; + } + + @Override + public boolean endWithComma(String s) { + if (s.charAt(s.length() - 1) == ',') { + printer.printExceptionMessage("END_WITH_COMMA"); + return false; + } + return true; + } + + @Override + public boolean inputOtherThanCharacter(String s) { + if (!Pattern.matches(OTHER_THAN_CHARACTER, s)) { + printer.printExceptionMessage("INPUT_OTHER_THAN_CHARACTERS"); + return false; + } + return true; + } + + @Override + public boolean inputSameName(String s) { + List CAR_NAME_LIST = Arrays.asList(s.split(",")); + int sizeOfNameList = CAR_NAME_LIST.size(); + + for (int i = 0; i < sizeOfNameList; i++) { + if (CAR_NAME_LIST.subList(i + 1, sizeOfNameList).contains(CAR_NAME_LIST.get(i))) { + printer.printExceptionMessage("INPUT_SAME_NAME"); + return false; + } + } + return true; + } + + @Override + public boolean overSizeCharacters(String s) { + String[] splitName = s.split(","); + for (String i : splitName) + if (i.length() > MAX_NAME_SIZE) { + printer.printExceptionMessage("OVER_SIZE_CHARACTERS"); + return false; + } + return true; + } + +} diff --git a/src/main/java/domain/ValidatorInterface.java b/src/main/java/domain/ValidatorInterface.java new file mode 100644 index 0000000..365a78f --- /dev/null +++ b/src/main/java/domain/ValidatorInterface.java @@ -0,0 +1,21 @@ +package domain; + +public interface ValidatorInterface { + boolean isCorrectName(String s); + + boolean validateNumber(String s); + + boolean inputNothing(String s); + + boolean inputCommaInARow(String s); + + boolean startWithComma(String s); + + boolean endWithComma(String s); + + boolean inputOtherThanCharacter(String s); + + boolean inputSameName(String s); + + boolean overSizeCharacters(String s); +} diff --git a/src/main/java/empty.txt b/src/main/java/empty.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/io/Message.java b/src/main/java/io/Message.java new file mode 100644 index 0000000..fd404c6 --- /dev/null +++ b/src/main/java/io/Message.java @@ -0,0 +1,42 @@ +package io; + +import java.util.HashMap; +import java.util.Map; + +public class Message implements MessageInterface { + Map generalMessageList = new HashMap(); + Map exceptionMessageList = new HashMap(); + + public Message() { + this.exceptionMessageList(); + this.generalMessageList(); + } + private void generalMessageList() { + generalMessageList.put("INPUT_NAMEOFCAR", "경주할 자동차 이름을 입력하세요. (이름은 쉼표(,) 기준으로 구분)"); + generalMessageList.put("INPUT_COUNT", "시도할 회수는 몇회인가요?"); + generalMessageList.put("OPERATION_RESULT", "실행 결과"); + generalMessageList.put("FIANL_WINNER", "가 최종 우승했습니다."); + generalMessageList.put("DEFAULT_SPACE", ""); + } + + private void exceptionMessageList() { + exceptionMessageList.put("INPUT_NOTHING", "아무 문자도 입력되지 않았습니다."); + exceptionMessageList.put("INPUT_COMMA_IN_A_ROW", "쉼표(,)가 연속으로 입력되었습니다."); + exceptionMessageList.put("INPUT_OTHER_THAN_CHARACTERS", "영어와 쉼표(,) 이외의 다른 문자가 입력되었습니다."); + exceptionMessageList.put("START_WITH_COMMA", "쉼표(,)로 시작합니다."); + exceptionMessageList.put("END_WITH_COMMA", "쉼표(,)로 끝납니다."); + exceptionMessageList.put("INPUT_SAME_NAME", "같은 이름이 입력되었습니다."); + exceptionMessageList.put("OVER_SIZE_CHARACTERS", "이름이 5자 이상입니다."); + exceptionMessageList.put("INPUT_CHARACTERS_OTHER_THAN_DIGIT", "숫자 이외의 다른 문자가 입력되었습니다."); + } + + @Override + public String selectMessageFromGeneral(String Keyword) { + return generalMessageList.get(Keyword); + } + + @Override + public String selectMessageFromException(String Keyword) { + return exceptionMessageList.get(Keyword); + } +} \ No newline at end of file diff --git a/src/main/java/io/MessageInterface.java b/src/main/java/io/MessageInterface.java new file mode 100644 index 0000000..e1b5c90 --- /dev/null +++ b/src/main/java/io/MessageInterface.java @@ -0,0 +1,14 @@ +package io; + +public interface MessageInterface { + + private void generalMessageList() { + } + + private void exceptionMessageList() { + } + + String selectMessageFromGeneral(String Keyword); + + String selectMessageFromException(String Keyword); +} diff --git a/src/main/java/io/Printer.java b/src/main/java/io/Printer.java new file mode 100644 index 0000000..8225381 --- /dev/null +++ b/src/main/java/io/Printer.java @@ -0,0 +1,43 @@ +package io; + +import domain.Car; + +import java.util.List; + +public class Printer { + private Message message = new Message(); + + private static String DEFAULT_SYMBOL = "-"; + + public void printProgressWithSymbol(String name, int position) { + String result = ""; + for(int i = 0;i < position;i++) + result += DEFAULT_SYMBOL; + + System.out.printf("%5s : %s\n", name, result); + } + + public String makeWinnerToString(List cars) { + String winner = cars.get(0).getName(); + + if(cars.size() > 1) { + for(int i = 1;i cars = new ArrayList<>(); + Car car1 = new Car("kim"); + Car car2 = new Car("park"); + cars.add(car1); + cars.add(car2); + Car[] carArray = new Car[]{car1, car2}; + + assertThat(cars).isEqualTo(gameplayer.checkWhoIsWinner(carArray)); + } + + + @Test + void makeCountAfterGettingNumberTest() { + String input = "5"; + InputStream stdin = System.in; + int result; + + try { + System.setIn(new ByteArrayInputStream(input.getBytes())); + gameplayer = new GamePlayer(); + result = gameplayer.receiveRepeatCount(); + } finally { + System.setIn(stdin); + } + + int expected = 5; + assertThat(expected).isEqualTo(result); + } +} \ No newline at end of file diff --git a/src/test/java/domain/GeneratorTest.java b/src/test/java/domain/GeneratorTest.java new file mode 100644 index 0000000..fcc70d2 --- /dev/null +++ b/src/test/java/domain/GeneratorTest.java @@ -0,0 +1,20 @@ +package domain; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class GeneratorTest { + Generator generator; + + @BeforeEach + void setUp() { + generator = new Generator(); + } + + @Test + void generateRandomNumberTest() { + assertThat(generator.generateRandomNumber()).isBetween(0, 9); + } +} \ No newline at end of file diff --git a/src/test/java/domain/ValidatorTest.java b/src/test/java/domain/ValidatorTest.java new file mode 100644 index 0000000..3348019 --- /dev/null +++ b/src/test/java/domain/ValidatorTest.java @@ -0,0 +1,82 @@ +package domain; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class ValidatorTest { + + private final String NORMAL_INPUT_FOR_NAME = "Park,Song,Kim,Jay"; + private final String NORMAL_INPUT_FOR_NUMBER = "5"; + Validator vd; + + @BeforeEach + void setup() { + vd = new Validator(); + } + + @ParameterizedTest + @ValueSource(strings = {"123,!@a,BDs5", ",,asd,asd,", NORMAL_INPUT_FOR_NAME}) + //이름 유효성 체크 + void validateNameTest(String s) { + assertThat(vd.isCorrectName(s)).isEqualTo(false); + } + + @ParameterizedTest + @ValueSource(strings = {"it's not a number", "what else?", NORMAL_INPUT_FOR_NUMBER}) + //숫자 유효성 체크 + void validateNumberTest(String s) { + assertThat(vd.validateNumber(s)).isEqualTo(false); + } + + @ParameterizedTest + @ValueSource(strings = {"", NORMAL_INPUT_FOR_NAME}) + //입력에 아무것도 없을 경우 + void inputNothingTest(String s) { + assertThat(vd.inputNothing(s)).isEqualTo(false); + } + + @ParameterizedTest + @ValueSource(strings = {",,,,,,", "kim,song,,park", NORMAL_INPUT_FOR_NAME}) + //쉼표(,)가 연속으로 나올 경우 + void inputCommaInARowTest(String s) { + assertThat(vd.inputCommaInARow(s)).isEqualTo(false); + } + + @ParameterizedTest + @ValueSource(strings = {",Kim,Park,Song", ",,Park,Song,Kim", NORMAL_INPUT_FOR_NAME}) + //쉼표(,)로 시작할 경우 + void startWithCommaTest(String s) { + assertThat(vd.startWithComma(s)).isEqualTo(false); + } + + @ParameterizedTest + @ValueSource(strings = {"hobihobi,hoho,hoho,", "asdfgg,asdfghj,zxcvbn,,,", NORMAL_INPUT_FOR_NAME}) + //쉼표(,)로 끝날 경우 + void endWithCommaTest(String s) { + assertThat(vd.endWithComma(s)).isEqualTo(false); + } + + @ParameterizedTest + @ValueSource(strings = {"hobㅁ,hoho,hoho", "asd5,asdfghj,zxcvbn", NORMAL_INPUT_FOR_NAME}) + //입력에 영문, 쉼표(,) 이외의 문자가 있을 경우 + void inputCharactersOtherThanNameTest(String s) { + assertThat(vd.inputOtherThanCharacter(s)).isEqualTo(false); + } + + @ParameterizedTest + @ValueSource(strings = {"hobihobi,hoho,hoho", "asdfgg,asdfgg,zxcvbn", NORMAL_INPUT_FOR_NAME}) + //같은 이름이 있을 경우 + void inputSameNameTest(String s) { + assertThat(vd.inputSameName(s)).isEqualTo(false); + } + + @ParameterizedTest + //이름이 5글자 이상을 경우 + @ValueSource(strings = {"hobihobi,hoho,hoho", "asdfgg,asdfghj,zxcvbn", NORMAL_INPUT_FOR_NAME}) + void overSizeCharacterTest(String s) { + assertThat(vd.overSizeCharacters(s)).isEqualTo(false); + } +} \ No newline at end of file diff --git a/src/test/java/empty.txt b/src/test/java/empty.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/test/java/io/MessageTest.java b/src/test/java/io/MessageTest.java new file mode 100644 index 0000000..93c99e3 --- /dev/null +++ b/src/test/java/io/MessageTest.java @@ -0,0 +1,28 @@ +package io; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class MessageTest { + + Message message; + + @BeforeEach + void setUp() { + message = new Message(); + } + + @Test + void selectMessageFromGeneral() { + String result = "경주할 자동차 이름을 입력하세요. (이름은 쉼표(,) 기준으로 구분)"; + assertThat(message.selectMessageFromGeneral("INPUT_NAMEOFCAR")).isEqualTo(result); + } + + @Test + void selectMessageFromException() { + String result = "아무 문자도 입력되지 않았습니다."; + assertThat(message.selectMessageFromException("INPUT_NOTHING")).isEqualTo(result); + } +} \ No newline at end of file diff --git a/src/test/java/io/PrinterTest.java b/src/test/java/io/PrinterTest.java new file mode 100644 index 0000000..1f08a85 --- /dev/null +++ b/src/test/java/io/PrinterTest.java @@ -0,0 +1,62 @@ +package io; + +import domain.Car; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PrinterTest { + private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); + Printer printer; + List cars = new ArrayList<>(); + + @BeforeEach + void setUp() { + System.setOut(new PrintStream(outputStreamCaptor)); + printer = new Printer(); + } + + @Test + void printProgressWithSymbolTest() { + printer.printProgressWithSymbol("kim", 5); + assertEquals("kim : -----", outputStreamCaptor.toString().trim()); + } + + @Test + void makeWinnerToStringTest() { + Car car1 = new Car("kim"); + Car car2 = new Car("park"); + cars.add(car1); + cars.add(car2); + + String result = printer.makeWinnerToString(cars); + assertThat(result).isEqualTo("kim, park"); + } + + @Test + void printWinnerTest() { + printer.printWinner("hobi, nonus"); + assertEquals("hobi, nonus가 최종 우승했습니다.", outputStreamCaptor.toString().trim()); + + } + + @Test + void printGeneralMessageTest() { + printer.printGeneralMessage("INPUT_COUNT"); + assertEquals("시도할 회수는 몇회인가요?", outputStreamCaptor.toString().trim()); + + } + + @Test + void printExceptionMessageTest() { + printer.printExceptionMessage("INPUT_NOTHING"); + assertEquals("아무 문자도 입력되지 않았습니다.", outputStreamCaptor.toString().trim()); + } +} \ No newline at end of file diff --git a/src/test/java/io/ReceiverTest.java b/src/test/java/io/ReceiverTest.java new file mode 100644 index 0000000..6919d06 --- /dev/null +++ b/src/test/java/io/ReceiverTest.java @@ -0,0 +1,46 @@ +package io; + +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ReceiverTest { + @Test + void receiveNameTest() { + String input = "kim,park,song"; + InputStream stdin = System.in; + String[] result; + + try { + System.setIn(new ByteArrayInputStream(input.getBytes())); + Receiver receiver = new Receiver(); + result = receiver.receiveName(); + } finally { + System.setIn(stdin); + } + + String[] expected = {"kim", "park", "song"}; + assertThat(expected).isEqualTo(result); + } + + @Test + void receiveNumberTest() { + String input = "5"; + InputStream stdin = System.in; + int result; + + try { + System.setIn(new ByteArrayInputStream(input.getBytes())); + Receiver receiver = new Receiver(); + result = receiver.receiveNumber(); + } finally { + System.setIn(stdin); + } + + int expected = 5; + assertThat(expected).isEqualTo(result); + } +} \ No newline at end of file