From c5bcbcc337821130348c486f045b0f71458b5529 Mon Sep 17 00:00:00 2001 From: "miguel.kim" Date: Thu, 17 Nov 2022 19:51:27 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EB=B3=BC=EB=A7=81=20=EC=A0=90?= =?UTF-8?q?=EC=88=98=ED=8C=90=20=EA=B8=B0=EB=B3=B8=20=EC=B6=94=EC=B6=9C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 마지막 프레임 관련 기능 제외 --- src/main/java/bowling/App.java | 22 +++++ src/main/java/bowling/domain/Frame.java | 59 ++++++++++++++ src/main/java/bowling/domain/FrameStatus.java | 11 +++ src/main/java/bowling/domain/Hit.java | 43 ++++++++++ src/main/java/bowling/domain/Name.java | 21 +++++ src/main/java/bowling/domain/Round.java | 53 ++++++++++++ src/main/java/bowling/view/InputView.java | 23 ++++++ src/main/java/bowling/view/ResultView.java | 50 ++++++++++++ src/test/java/bowling/domain/FrameTest.java | 81 +++++++++++++++++++ src/test/java/bowling/domain/HitTest.java | 40 +++++++++ src/test/java/bowling/domain/NameTest.java | 27 +++++++ src/test/java/bowling/domain/RoundTest.java | 49 +++++++++++ 12 files changed, 479 insertions(+) create mode 100644 src/main/java/bowling/App.java create mode 100644 src/main/java/bowling/domain/Frame.java create mode 100644 src/main/java/bowling/domain/FrameStatus.java create mode 100644 src/main/java/bowling/domain/Hit.java create mode 100644 src/main/java/bowling/domain/Name.java create mode 100644 src/main/java/bowling/domain/Round.java create mode 100644 src/main/java/bowling/view/InputView.java create mode 100644 src/main/java/bowling/view/ResultView.java create mode 100644 src/test/java/bowling/domain/FrameTest.java create mode 100644 src/test/java/bowling/domain/HitTest.java create mode 100644 src/test/java/bowling/domain/NameTest.java create mode 100644 src/test/java/bowling/domain/RoundTest.java diff --git a/src/main/java/bowling/App.java b/src/main/java/bowling/App.java new file mode 100644 index 0000000000..5a3f7de79e --- /dev/null +++ b/src/main/java/bowling/App.java @@ -0,0 +1,22 @@ +package bowling; + +import bowling.domain.Hit; +import bowling.domain.Name; +import bowling.domain.Round; +import bowling.view.InputView; +import bowling.view.ResultView; + +public class App { + + public static void main(String[] args) { + Name name = InputView.inputName(); + + Round round = new Round(); + ResultView.printRound(name, round); + while (!round.isEnd()) { + Hit hit = InputView.inputPins(round.getCurrentFrameNumber()); + round.hit(hit); + ResultView.printRound(name, round); + } + } +} diff --git a/src/main/java/bowling/domain/Frame.java b/src/main/java/bowling/domain/Frame.java new file mode 100644 index 0000000000..5587177b51 --- /dev/null +++ b/src/main/java/bowling/domain/Frame.java @@ -0,0 +1,59 @@ +package bowling.domain; + +public class Frame { + private Hit first; + private Hit second; + + public void firstThrow(Hit first) { + this.first = first; + if (first.isMax()) { + second = new Hit(0); + } + } + + public void secondThrow(Hit second) { + this.second = second; + } + + public FrameStatus getFirstStatus() { + if (first == null) { + return FrameStatus.BEFORE; + } + if (first.isMax()) { + return FrameStatus.STRIKE; + } + if (first.isMin()) { + return FrameStatus.GUTTER; + } + return FrameStatus.MISS; + } + + public FrameStatus getSecondStatus() { + if (first == null || second == null) { + return FrameStatus.BEFORE; + } + if (first.isMax()) { + return FrameStatus.SKIP; + } + Hit sum = first.plus(second); + if (sum.isMax()) { + return FrameStatus.SPARE; + } + if (second.isMin()) { + return FrameStatus.GUTTER; + } + return FrameStatus.MISS; + } + + public boolean isEnd() { + return first != null && second != null; + } + + public Hit getFirstHit() { + return first; + } + + public Hit getSecondHit() { + return second; + } +} diff --git a/src/main/java/bowling/domain/FrameStatus.java b/src/main/java/bowling/domain/FrameStatus.java new file mode 100644 index 0000000000..18608e195e --- /dev/null +++ b/src/main/java/bowling/domain/FrameStatus.java @@ -0,0 +1,11 @@ +package bowling.domain; + +public enum FrameStatus { + BEFORE, + PLAYING, + SKIP, + STRIKE, + SPARE, + MISS, + GUTTER; +} diff --git a/src/main/java/bowling/domain/Hit.java b/src/main/java/bowling/domain/Hit.java new file mode 100644 index 0000000000..4d568b99b4 --- /dev/null +++ b/src/main/java/bowling/domain/Hit.java @@ -0,0 +1,43 @@ +package bowling.domain; + +public class Hit { + public static final int MIN = 0; + public static final int MAX = 10; + private final int hit; + + + public Hit(int hit) { + validate(hit); + this.hit = hit; + } + + private void validate(int hit) { + if (hit < MIN || hit > MAX) { + throw new IllegalArgumentException("범위를 벗어나는 점수가 입력되었습니다."); + } + } + + public Hit plus(Hit target) { + return new Hit(hit + target.hit); + } + + public boolean isMin() { + return hit == MIN; + } + + public boolean isMax() { + return hit == MAX; + } + + public int getScore() { + return hit; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Hit)) { + return false; + } + return hit == ((Hit) obj).hit; + } +} diff --git a/src/main/java/bowling/domain/Name.java b/src/main/java/bowling/domain/Name.java new file mode 100644 index 0000000000..c48ae4a40f --- /dev/null +++ b/src/main/java/bowling/domain/Name.java @@ -0,0 +1,21 @@ +package bowling.domain; + +public class Name { + private final String name; + + + public Name(String name) { + validate(name); + this.name = name; + } + + private void validate(String name) { + if (name.length() != 3) { + throw new IllegalArgumentException("이름은 3글자로 입력되어야 합니다"); + } + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/bowling/domain/Round.java b/src/main/java/bowling/domain/Round.java new file mode 100644 index 0000000000..e7ae106b5d --- /dev/null +++ b/src/main/java/bowling/domain/Round.java @@ -0,0 +1,53 @@ +package bowling.domain; + +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class Round { + private static final int ROUND_COUNT = 10; + private final List frames; + + public Round() { + frames = IntStream.range(0, ROUND_COUNT) + .mapToObj(i -> new Frame()) + .collect(Collectors.toList()); + } + + public void hit(Hit hit) { + Frame last = getLastPlayingFrame() + .orElseThrow(() -> new IllegalStateException("모든 프레임이 종료되었습니다.")); + if (last.getFirstStatus() == FrameStatus.BEFORE) { + last.firstThrow(hit); + return; + } + last.secondThrow(hit); + } + + public boolean isEnd() { + Optional last = getLastPlayingFrame(); + return last.isEmpty(); + } + + public int getCurrentFrameNumber() { + Optional last = getLastPlayingFrame(); + if (last.isEmpty()) { + return ROUND_COUNT; + } + Frame currentFrame = last.get(); + return frames.indexOf(currentFrame) + 1; + + } + + private Optional getLastPlayingFrame() { + return frames.stream() + .filter(frame -> !frame.isEnd()) + .findFirst(); + } + + public void forEach(Consumer consumer) { + frames.forEach(consumer); + } +} diff --git a/src/main/java/bowling/view/InputView.java b/src/main/java/bowling/view/InputView.java new file mode 100644 index 0000000000..6428a712a6 --- /dev/null +++ b/src/main/java/bowling/view/InputView.java @@ -0,0 +1,23 @@ +package bowling.view; + +import bowling.domain.Name; +import bowling.domain.Hit; + +import java.util.Scanner; + +public class InputView { + private static final Scanner scanner = new Scanner(System.in); + + public static Name inputName() { + System.out.print("플레이어 이름은 (3 english letters)?: "); + + String name = scanner.nextLine(); + return new Name(name); + } + + public static Hit inputPins(int frame) { + System.out.printf("%d프레임 투구: ", frame); + + return new Hit(scanner.nextInt()); + } +} diff --git a/src/main/java/bowling/view/ResultView.java b/src/main/java/bowling/view/ResultView.java new file mode 100644 index 0000000000..a42ff0d558 --- /dev/null +++ b/src/main/java/bowling/view/ResultView.java @@ -0,0 +1,50 @@ +package bowling.view; + +import bowling.domain.*; + +public class ResultView { + + public static void printRound(Name name, Round round) { + System.out.println("| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 |"); + System.out.printf("| %s |", name.getName()); + + round.forEach(ResultView::printFrame); + System.out.println(); + } + + private static void printFrame(Frame frame) { + String frameString = " "; + FrameStatus firstStatus = frame.getFirstStatus(); + frameString += getFrameCharacter(firstStatus, frame.getFirstHit()); + FrameStatus secondStatus = frame.getSecondStatus(); + frameString += getSecondFrameCharacter(secondStatus, frame.getSecondHit()); + System.out.print(frameString); + } + + private static String getFrameCharacter(FrameStatus frameStatus, Hit hit) { + if (frameStatus == FrameStatus.BEFORE) { + return " "; + } + if (frameStatus == FrameStatus.STRIKE) { + return "X"; + } + if (frameStatus == FrameStatus.GUTTER) { + return "-"; + } + return Integer.toString(hit.getScore()); + } + + private static String getSecondFrameCharacter(FrameStatus frameStatus, Hit hit) { + if (frameStatus == FrameStatus.BEFORE + || frameStatus == FrameStatus.SKIP) { + return " |"; + } + if (frameStatus == FrameStatus.GUTTER) { + return "|- |"; + } + if (frameStatus == FrameStatus.SPARE) { + return "|/ |"; + } + return String.format("|%d |", hit.getScore()); + } +} diff --git a/src/test/java/bowling/domain/FrameTest.java b/src/test/java/bowling/domain/FrameTest.java new file mode 100644 index 0000000000..b32f1f6697 --- /dev/null +++ b/src/test/java/bowling/domain/FrameTest.java @@ -0,0 +1,81 @@ +package bowling.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class FrameTest { + + @Test + @DisplayName("스트라이크") + void strike() { + Frame frame = new Frame(); + frame.firstThrow(new Hit(10)); + + assertThat(frame.getFirstHit()).isEqualTo(new Hit(10)); + assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.STRIKE); + assertThat(frame.getSecondHit()).isEqualTo(new Hit(0)); + assertThat(frame.getSecondStatus()).isEqualTo(FrameStatus.SKIP); + assertThat(frame.isEnd()).isTrue(); + } + + @Test + @DisplayName("스페어") + void spare() { + Frame frame = new Frame(); + frame.firstThrow(new Hit(8)); + frame.secondThrow(new Hit(2)); + + assertThat(frame.getFirstHit()).isEqualTo(new Hit(8)); + assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.MISS); + assertThat(frame.getSecondHit()).isEqualTo(new Hit(2)); + assertThat(frame.getSecondStatus()).isEqualTo(FrameStatus.SPARE); + assertThat(frame.isEnd()).isTrue(); + } + + @Test + @DisplayName("거터") + void gutter() { + Frame frame = new Frame(); + frame.firstThrow(new Hit(0)); + frame.secondThrow(new Hit(5)); + + assertThat(frame.getFirstHit()).isEqualTo(new Hit(0)); + assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.GUTTER); + assertThat(frame.getSecondHit()).isEqualTo(new Hit(5)); + assertThat(frame.getSecondStatus()).isEqualTo(FrameStatus.MISS); + assertThat(frame.isEnd()).isTrue(); + } + + @Test + @DisplayName("두번의 투구 모두 거터") + void gutter_2() { + Frame frame = new Frame(); + frame.firstThrow(new Hit(0)); + frame.secondThrow(new Hit(0)); + + assertThat(frame.getFirstHit()).isEqualTo(new Hit(0)); + assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.GUTTER); + assertThat(frame.getSecondHit()).isEqualTo(new Hit(0)); + assertThat(frame.getSecondStatus()).isEqualTo(FrameStatus.GUTTER); + assertThat(frame.isEnd()).isTrue(); + } + + @Test + @DisplayName("종료되지 않은 상태 테스트") + void isEnd() { + Frame frame = new Frame(); + + assertThat(frame.isEnd()).isFalse(); + } + + @Test + @DisplayName("한번 던져서 스트라이크를 치지 않은 뒤 종료되지 않은 상태 테스트") + void isEnd_afterFirst() { + Frame frame = new Frame(); + frame.firstThrow(new Hit(5)); + + assertThat(frame.isEnd()).isFalse(); + } +} diff --git a/src/test/java/bowling/domain/HitTest.java b/src/test/java/bowling/domain/HitTest.java new file mode 100644 index 0000000000..7aaa3465af --- /dev/null +++ b/src/test/java/bowling/domain/HitTest.java @@ -0,0 +1,40 @@ +package bowling.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +public class HitTest { + + @Test + @DisplayName("최댓값 테스트") + void max() { + Hit hit = new Hit(Hit.MAX); + assertThat(hit.isMax()).isTrue(); + assertThat(hit.isMin()).isFalse(); + } + + @Test + @DisplayName("최솟값 테스트") + void min() { + Hit hit = new Hit(Hit.MIN); + assertThat(hit.isMax()).isFalse(); + assertThat(hit.isMin()).isTrue(); + } + + @Test + @DisplayName("범위 초과 테스트") + void range() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> new Hit(11)); + } + + @Test + @DisplayName("add에 의한 범위 초과 테스트") + void range_add() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> new Hit(3).plus(new Hit(8))); + } +} diff --git a/src/test/java/bowling/domain/NameTest.java b/src/test/java/bowling/domain/NameTest.java new file mode 100644 index 0000000000..c09142160a --- /dev/null +++ b/src/test/java/bowling/domain/NameTest.java @@ -0,0 +1,27 @@ +package bowling.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +public class NameTest { + + @Test + @DisplayName("기본기능 테스트") + void name() { + Name name = new Name("ASD"); + assertThat(name.getName()).isEqualTo("ASD"); + } + + @ParameterizedTest + @ValueSource(strings = {"AA", "DDDD"}) + @DisplayName("범위를 벗어나는 이름 테스트") + void name_range(String value) { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> new Name(value)); + } +} diff --git a/src/test/java/bowling/domain/RoundTest.java b/src/test/java/bowling/domain/RoundTest.java new file mode 100644 index 0000000000..1646043c76 --- /dev/null +++ b/src/test/java/bowling/domain/RoundTest.java @@ -0,0 +1,49 @@ +package bowling.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class RoundTest { + + @Test + @DisplayName("초기 상태 테스트") + void round_init() { + Round round = new Round(); + + assertThat(round.isEnd()).isFalse(); + assertThat(round.getCurrentFrameNumber()).isEqualTo(1); + } + + @Test + @DisplayName("스트라이크 투구 테스트") + void round_hit() { + Round round = new Round(); + round.hit(new Hit(10)); + round.hit(new Hit(5)); + + assertThat(round.isEnd()).isFalse(); + assertThat(round.getCurrentFrameNumber()).isEqualTo(2); + } + + @Test + @DisplayName("라운드 종료 테스트") + void round_end() { + Round round = new Round(); + round.hit(new Hit(10)); + round.hit(new Hit(10)); + round.hit(new Hit(10)); + round.hit(new Hit(10)); + round.hit(new Hit(10)); + round.hit(new Hit(10)); + round.hit(new Hit(10)); + round.hit(new Hit(10)); + round.hit(new Hit(10)); + round.hit(new Hit(5)); + round.hit(new Hit(4)); + + assertThat(round.isEnd()).isTrue(); + assertThat(round.getCurrentFrameNumber()).isEqualTo(10); + } +} From 32f8cde77d7a89b9e2eae8117bd9dcd2bcecdc29 Mon Sep 17 00:00:00 2001 From: "miguel.kim" Date: Thu, 17 Nov 2022 20:36:19 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=EB=A7=88=EC=A7=80=EB=A7=89=20?= =?UTF-8?q?=ED=94=84=EB=A0=88=EC=9E=84=20=EA=B4=80=EB=A0=A8=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/bowling/domain/Frame.java | 63 ++-------- src/main/java/bowling/domain/LastFrame.java | 112 ++++++++++++++++++ src/main/java/bowling/domain/NormalFrame.java | 81 +++++++++++++ src/main/java/bowling/domain/Round.java | 11 +- src/main/java/bowling/view/ResultView.java | 32 ++++- .../java/bowling/domain/LastFrameTest.java | 99 ++++++++++++++++ .../{FrameTest.java => NormalFrameTest.java} | 14 +-- 7 files changed, 345 insertions(+), 67 deletions(-) create mode 100644 src/main/java/bowling/domain/LastFrame.java create mode 100644 src/main/java/bowling/domain/NormalFrame.java create mode 100644 src/test/java/bowling/domain/LastFrameTest.java rename src/test/java/bowling/domain/{FrameTest.java => NormalFrameTest.java} (89%) diff --git a/src/main/java/bowling/domain/Frame.java b/src/main/java/bowling/domain/Frame.java index 5587177b51..b92b6ee6a3 100644 --- a/src/main/java/bowling/domain/Frame.java +++ b/src/main/java/bowling/domain/Frame.java @@ -1,59 +1,18 @@ package bowling.domain; -public class Frame { - private Hit first; - private Hit second; +public interface Frame { - public void firstThrow(Hit first) { - this.first = first; - if (first.isMax()) { - second = new Hit(0); - } - } + void firstThrow(Hit hit); + void secondThrow(Hit hit); + void thirdThrow(Hit hit); - public void secondThrow(Hit second) { - this.second = second; - } + FrameStatus getFirstStatus(); + FrameStatus getSecondStatus(); + FrameStatus getThirdStatus(); - public FrameStatus getFirstStatus() { - if (first == null) { - return FrameStatus.BEFORE; - } - if (first.isMax()) { - return FrameStatus.STRIKE; - } - if (first.isMin()) { - return FrameStatus.GUTTER; - } - return FrameStatus.MISS; - } + Hit getFirstHit(); + Hit getSecondHit(); + Hit getThirdHit(); - public FrameStatus getSecondStatus() { - if (first == null || second == null) { - return FrameStatus.BEFORE; - } - if (first.isMax()) { - return FrameStatus.SKIP; - } - Hit sum = first.plus(second); - if (sum.isMax()) { - return FrameStatus.SPARE; - } - if (second.isMin()) { - return FrameStatus.GUTTER; - } - return FrameStatus.MISS; - } - - public boolean isEnd() { - return first != null && second != null; - } - - public Hit getFirstHit() { - return first; - } - - public Hit getSecondHit() { - return second; - } + boolean isEnd(); } diff --git a/src/main/java/bowling/domain/LastFrame.java b/src/main/java/bowling/domain/LastFrame.java new file mode 100644 index 0000000000..be3cfbddde --- /dev/null +++ b/src/main/java/bowling/domain/LastFrame.java @@ -0,0 +1,112 @@ +package bowling.domain; + +public class LastFrame implements Frame { + private Hit first; + private Hit second; + private Hit third; + + @Override + public void firstThrow(Hit hit) { + this.first = hit; + } + + @Override + public void secondThrow(Hit hit) { + this.second = hit; + } + + @Override + public void thirdThrow(Hit hit) { + this.third = hit; + } + + @Override + public FrameStatus getFirstStatus() { + return getNormalStatus(first); + } + + @Override + public FrameStatus getSecondStatus() { + if (first == null || second == null) { + return FrameStatus.BEFORE; + } + if (first.isMax()) { + return getNormalStatus(second); + } + Hit sum = first.plus(second); + if (sum.isMax()) { + return FrameStatus.SPARE; + } + if (second.isMin()) { + return FrameStatus.GUTTER; + } + return FrameStatus.MISS; + } + + @Override + public FrameStatus getThirdStatus() { + if (first == null || second == null || third == null) { + return FrameStatus.BEFORE; + } + if (!canPlayThird()) { + return FrameStatus.SKIP; + } + if (third.isMax()) { + return FrameStatus.STRIKE; + } + if (third.isMin()) { + return FrameStatus.GUTTER; + } + if (second.isMax()) { + return FrameStatus.MISS; + } + Hit sum = second.plus(third); + if (sum.isMax()) { + return FrameStatus.SPARE; + } + return FrameStatus.MISS; + } + + private FrameStatus getNormalStatus(Hit hit) { + if (hit == null) { + return FrameStatus.BEFORE; + } + if (hit.isMax()) { + return FrameStatus.STRIKE; + } + if (hit.isMin()) { + return FrameStatus.GUTTER; + } + return FrameStatus.MISS; + } + + @Override + public Hit getFirstHit() { + return first; + } + + @Override + public Hit getSecondHit() { + return second; + } + + @Override + public Hit getThirdHit() { + return third; + } + + @Override + public boolean isEnd() { + if (first == null || second == null) { + return false; + } + if (third != null) { + return true; + } + return !canPlayThird(); + } + + private boolean canPlayThird() { + return getFirstStatus() == FrameStatus.STRIKE || getSecondStatus() == FrameStatus.SPARE; + } +} diff --git a/src/main/java/bowling/domain/NormalFrame.java b/src/main/java/bowling/domain/NormalFrame.java new file mode 100644 index 0000000000..667186785a --- /dev/null +++ b/src/main/java/bowling/domain/NormalFrame.java @@ -0,0 +1,81 @@ +package bowling.domain; + +public class NormalFrame implements Frame { + private Hit first; + private Hit second; + + @Override + public void firstThrow(Hit hit) { + this.first = hit; + if (hit.isMax()) { + second = new Hit(0); + } + } + + @Override + public void secondThrow(Hit hit) { + this.second = hit; + } + + @Override + public void thirdThrow(Hit hit) { + throw new IllegalStateException("실행할 수 없는 메소드를 실행했습니다."); + } + + @Override + public FrameStatus getFirstStatus() { + if (first == null) { + return FrameStatus.BEFORE; + } + if (first.isMax()) { + return FrameStatus.STRIKE; + } + if (first.isMin()) { + return FrameStatus.GUTTER; + } + return FrameStatus.MISS; + } + + @Override + public FrameStatus getSecondStatus() { + if (first == null || second == null) { + return FrameStatus.BEFORE; + } + if (first.isMax()) { + return FrameStatus.SKIP; + } + Hit sum = first.plus(second); + if (sum.isMax()) { + return FrameStatus.SPARE; + } + if (second.isMin()) { + return FrameStatus.GUTTER; + } + return FrameStatus.MISS; + } + + @Override + public FrameStatus getThirdStatus() { + return FrameStatus.SKIP; + } + + @Override + public Hit getFirstHit() { + return first; + } + + @Override + public Hit getSecondHit() { + return second; + } + + @Override + public Hit getThirdHit() { + return new Hit(0); + } + + @Override + public boolean isEnd() { + return first != null && second != null; + } +} diff --git a/src/main/java/bowling/domain/Round.java b/src/main/java/bowling/domain/Round.java index e7ae106b5d..86eb090f2f 100644 --- a/src/main/java/bowling/domain/Round.java +++ b/src/main/java/bowling/domain/Round.java @@ -11,9 +11,10 @@ public class Round { private final List frames; public Round() { - frames = IntStream.range(0, ROUND_COUNT) - .mapToObj(i -> new Frame()) + frames = IntStream.range(0, ROUND_COUNT - 1) + .mapToObj(i -> new NormalFrame()) .collect(Collectors.toList()); + frames.add(new LastFrame()); } public void hit(Hit hit) { @@ -23,7 +24,11 @@ public void hit(Hit hit) { last.firstThrow(hit); return; } - last.secondThrow(hit); + if (last.getSecondStatus() == FrameStatus.BEFORE) { + last.secondThrow(hit); + return; + } + last.thirdThrow(hit); } public boolean isEnd() { diff --git a/src/main/java/bowling/view/ResultView.java b/src/main/java/bowling/view/ResultView.java index a42ff0d558..56812d8f0a 100644 --- a/src/main/java/bowling/view/ResultView.java +++ b/src/main/java/bowling/view/ResultView.java @@ -13,11 +13,13 @@ public static void printRound(Name name, Round round) { } private static void printFrame(Frame frame) { - String frameString = " "; + String frameString = " "; FrameStatus firstStatus = frame.getFirstStatus(); frameString += getFrameCharacter(firstStatus, frame.getFirstHit()); FrameStatus secondStatus = frame.getSecondStatus(); frameString += getSecondFrameCharacter(secondStatus, frame.getSecondHit()); + FrameStatus thirdStatus = frame.getThirdStatus(); + frameString += getThirdFrameCharacter(thirdStatus, frame.getThirdHit()); System.out.print(frameString); } @@ -37,14 +39,34 @@ private static String getFrameCharacter(FrameStatus frameStatus, Hit hit) { private static String getSecondFrameCharacter(FrameStatus frameStatus, Hit hit) { if (frameStatus == FrameStatus.BEFORE || frameStatus == FrameStatus.SKIP) { - return " |"; + return " "; } if (frameStatus == FrameStatus.GUTTER) { - return "|- |"; + return "|-"; } if (frameStatus == FrameStatus.SPARE) { - return "|/ |"; + return "|/"; } - return String.format("|%d |", hit.getScore()); + if (frameStatus == FrameStatus.STRIKE) { + return "|X"; + } + return String.format("|%d", hit.getScore()); + } + + private static String getThirdFrameCharacter(FrameStatus frameStatus, Hit hit) { + if (frameStatus == FrameStatus.BEFORE + || frameStatus == FrameStatus.SKIP) { + return " |"; + } + if (frameStatus == FrameStatus.GUTTER) { + return "|-|"; + } + if (frameStatus == FrameStatus.SPARE) { + return "|/|"; + } + if (frameStatus == FrameStatus.STRIKE) { + return "|X|"; + } + return String.format("|%d|", hit.getScore()); } } diff --git a/src/test/java/bowling/domain/LastFrameTest.java b/src/test/java/bowling/domain/LastFrameTest.java new file mode 100644 index 0000000000..1634c3eddd --- /dev/null +++ b/src/test/java/bowling/domain/LastFrameTest.java @@ -0,0 +1,99 @@ +package bowling.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LastFrameTest { + + @Test + @DisplayName("첫번째 투구에 스트라이크를 친 경우 3회째 투구 가능") + void strike_first() { + Frame frame = new LastFrame(); + frame.firstThrow(new Hit(10)); + frame.secondThrow(new Hit(5)); + + assertThat(frame.getFirstHit()).isEqualTo(new Hit(10)); + assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.STRIKE); + assertThat(frame.getSecondHit()).isEqualTo(new Hit(5)); + assertThat(frame.getSecondStatus()).isEqualTo(FrameStatus.MISS); + assertThat(frame.isEnd()).isFalse(); + } + + @Test + @DisplayName("스페어인 경우 세번째 투구 가능") + void spare() { + Frame frame = new LastFrame(); + frame.firstThrow(new Hit(8)); + frame.secondThrow(new Hit(2)); + + assertThat(frame.getFirstHit()).isEqualTo(new Hit(8)); + assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.MISS); + assertThat(frame.getSecondHit()).isEqualTo(new Hit(2)); + assertThat(frame.getSecondStatus()).isEqualTo(FrameStatus.SPARE); + assertThat(frame.isEnd()).isFalse(); + } + + @Test + @DisplayName("3회 스트라이크") + void strike() { + Frame frame = new LastFrame(); + frame.firstThrow(new Hit(10)); + frame.secondThrow(new Hit(10)); + frame.thirdThrow(new Hit(10)); + + assertThat(frame.getFirstHit()).isEqualTo(new Hit(10)); + assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.STRIKE); + assertThat(frame.getSecondHit()).isEqualTo(new Hit(10)); + assertThat(frame.getSecondStatus()).isEqualTo(FrameStatus.STRIKE); + assertThat(frame.getThirdHit()).isEqualTo(new Hit(10)); + assertThat(frame.getThirdStatus()).isEqualTo(FrameStatus.STRIKE); + assertThat(frame.isEnd()).isTrue(); + } + + @Test + @DisplayName("거터") + void gutter() { + Frame frame = new LastFrame(); + frame.firstThrow(new Hit(0)); + frame.secondThrow(new Hit(5)); + + assertThat(frame.getFirstHit()).isEqualTo(new Hit(0)); + assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.GUTTER); + assertThat(frame.getSecondHit()).isEqualTo(new Hit(5)); + assertThat(frame.getSecondStatus()).isEqualTo(FrameStatus.MISS); + assertThat(frame.isEnd()).isTrue(); + } + + @Test + @DisplayName("두번의 투구 모두 거터") + void gutter_2() { + Frame frame = new LastFrame(); + frame.firstThrow(new Hit(0)); + frame.secondThrow(new Hit(0)); + + assertThat(frame.getFirstHit()).isEqualTo(new Hit(0)); + assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.GUTTER); + assertThat(frame.getSecondHit()).isEqualTo(new Hit(0)); + assertThat(frame.getSecondStatus()).isEqualTo(FrameStatus.GUTTER); + assertThat(frame.isEnd()).isTrue(); + } + + @Test + @DisplayName("종료되지 않은 상태 테스트") + void isEnd() { + Frame frame = new LastFrame(); + + assertThat(frame.isEnd()).isFalse(); + } + + @Test + @DisplayName("한번 던져서 스트라이크를 치지 않은 뒤 종료되지 않은 상태 테스트") + void isEnd_afterFirst() { + Frame frame = new LastFrame(); + frame.firstThrow(new Hit(5)); + + assertThat(frame.isEnd()).isFalse(); + } +} diff --git a/src/test/java/bowling/domain/FrameTest.java b/src/test/java/bowling/domain/NormalFrameTest.java similarity index 89% rename from src/test/java/bowling/domain/FrameTest.java rename to src/test/java/bowling/domain/NormalFrameTest.java index b32f1f6697..9324c84e59 100644 --- a/src/test/java/bowling/domain/FrameTest.java +++ b/src/test/java/bowling/domain/NormalFrameTest.java @@ -5,12 +5,12 @@ import static org.assertj.core.api.Assertions.assertThat; -public class FrameTest { +public class NormalFrameTest { @Test @DisplayName("스트라이크") void strike() { - Frame frame = new Frame(); + Frame frame = new NormalFrame(); frame.firstThrow(new Hit(10)); assertThat(frame.getFirstHit()).isEqualTo(new Hit(10)); @@ -23,7 +23,7 @@ void strike() { @Test @DisplayName("스페어") void spare() { - Frame frame = new Frame(); + Frame frame = new NormalFrame(); frame.firstThrow(new Hit(8)); frame.secondThrow(new Hit(2)); @@ -37,7 +37,7 @@ void spare() { @Test @DisplayName("거터") void gutter() { - Frame frame = new Frame(); + Frame frame = new NormalFrame(); frame.firstThrow(new Hit(0)); frame.secondThrow(new Hit(5)); @@ -51,7 +51,7 @@ void gutter() { @Test @DisplayName("두번의 투구 모두 거터") void gutter_2() { - Frame frame = new Frame(); + Frame frame = new NormalFrame(); frame.firstThrow(new Hit(0)); frame.secondThrow(new Hit(0)); @@ -65,7 +65,7 @@ void gutter_2() { @Test @DisplayName("종료되지 않은 상태 테스트") void isEnd() { - Frame frame = new Frame(); + Frame frame = new NormalFrame(); assertThat(frame.isEnd()).isFalse(); } @@ -73,7 +73,7 @@ void isEnd() { @Test @DisplayName("한번 던져서 스트라이크를 치지 않은 뒤 종료되지 않은 상태 테스트") void isEnd_afterFirst() { - Frame frame = new Frame(); + Frame frame = new NormalFrame(); frame.firstThrow(new Hit(5)); assertThat(frame.isEnd()).isFalse(); From fbb3f5648c733bc17b94bc6549ffa3e018b0fe52 Mon Sep 17 00:00:00 2001 From: "miguel.kim" Date: Thu, 17 Nov 2022 20:54:50 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor:=20=ED=88=AC=EA=B5=AC=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81=EC=9D=84=20?= =?UTF-8?q?play=EB=A1=9C=20=EB=B3=91=ED=95=A9,=20Frame=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/bowling/domain/Frame.java | 4 +--- src/main/java/bowling/domain/LastFrame.java | 20 ++++++++++++---- src/main/java/bowling/domain/NormalFrame.java | 18 +++++++------- src/main/java/bowling/domain/Round.java | 10 +------- .../java/bowling/domain/LastFrameTest.java | 24 +++++++++---------- .../java/bowling/domain/NormalFrameTest.java | 16 ++++++------- 6 files changed, 47 insertions(+), 45 deletions(-) diff --git a/src/main/java/bowling/domain/Frame.java b/src/main/java/bowling/domain/Frame.java index b92b6ee6a3..c0550618e1 100644 --- a/src/main/java/bowling/domain/Frame.java +++ b/src/main/java/bowling/domain/Frame.java @@ -2,9 +2,7 @@ public interface Frame { - void firstThrow(Hit hit); - void secondThrow(Hit hit); - void thirdThrow(Hit hit); + void play(Hit hit); FrameStatus getFirstStatus(); FrameStatus getSecondStatus(); diff --git a/src/main/java/bowling/domain/LastFrame.java b/src/main/java/bowling/domain/LastFrame.java index be3cfbddde..b2a516026a 100644 --- a/src/main/java/bowling/domain/LastFrame.java +++ b/src/main/java/bowling/domain/LastFrame.java @@ -6,17 +6,27 @@ public class LastFrame implements Frame { private Hit third; @Override - public void firstThrow(Hit hit) { + public void play(Hit hit) { + if (getFirstStatus() == FrameStatus.BEFORE) { + firstThrow(hit); + return; + } + if (getSecondStatus() == FrameStatus.BEFORE) { + secondThrow(hit); + return; + } + thirdThrow(hit); + } + + private void firstThrow(Hit hit) { this.first = hit; } - @Override - public void secondThrow(Hit hit) { + private void secondThrow(Hit hit) { this.second = hit; } - @Override - public void thirdThrow(Hit hit) { + private void thirdThrow(Hit hit) { this.third = hit; } diff --git a/src/main/java/bowling/domain/NormalFrame.java b/src/main/java/bowling/domain/NormalFrame.java index 667186785a..6812863df9 100644 --- a/src/main/java/bowling/domain/NormalFrame.java +++ b/src/main/java/bowling/domain/NormalFrame.java @@ -5,23 +5,25 @@ public class NormalFrame implements Frame { private Hit second; @Override - public void firstThrow(Hit hit) { + public void play(Hit hit) { + if (getFirstStatus() == FrameStatus.BEFORE) { + firstThrow(hit); + return; + } + secondThrow(hit); + } + + private void firstThrow(Hit hit) { this.first = hit; if (hit.isMax()) { second = new Hit(0); } } - @Override - public void secondThrow(Hit hit) { + private void secondThrow(Hit hit) { this.second = hit; } - @Override - public void thirdThrow(Hit hit) { - throw new IllegalStateException("실행할 수 없는 메소드를 실행했습니다."); - } - @Override public FrameStatus getFirstStatus() { if (first == null) { diff --git a/src/main/java/bowling/domain/Round.java b/src/main/java/bowling/domain/Round.java index 86eb090f2f..c78659c8d8 100644 --- a/src/main/java/bowling/domain/Round.java +++ b/src/main/java/bowling/domain/Round.java @@ -20,15 +20,7 @@ public Round() { public void hit(Hit hit) { Frame last = getLastPlayingFrame() .orElseThrow(() -> new IllegalStateException("모든 프레임이 종료되었습니다.")); - if (last.getFirstStatus() == FrameStatus.BEFORE) { - last.firstThrow(hit); - return; - } - if (last.getSecondStatus() == FrameStatus.BEFORE) { - last.secondThrow(hit); - return; - } - last.thirdThrow(hit); + last.play(hit); } public boolean isEnd() { diff --git a/src/test/java/bowling/domain/LastFrameTest.java b/src/test/java/bowling/domain/LastFrameTest.java index 1634c3eddd..85aea89884 100644 --- a/src/test/java/bowling/domain/LastFrameTest.java +++ b/src/test/java/bowling/domain/LastFrameTest.java @@ -11,8 +11,8 @@ public class LastFrameTest { @DisplayName("첫번째 투구에 스트라이크를 친 경우 3회째 투구 가능") void strike_first() { Frame frame = new LastFrame(); - frame.firstThrow(new Hit(10)); - frame.secondThrow(new Hit(5)); + frame.play(new Hit(10)); + frame.play(new Hit(5)); assertThat(frame.getFirstHit()).isEqualTo(new Hit(10)); assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.STRIKE); @@ -25,8 +25,8 @@ void strike_first() { @DisplayName("스페어인 경우 세번째 투구 가능") void spare() { Frame frame = new LastFrame(); - frame.firstThrow(new Hit(8)); - frame.secondThrow(new Hit(2)); + frame.play(new Hit(8)); + frame.play(new Hit(2)); assertThat(frame.getFirstHit()).isEqualTo(new Hit(8)); assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.MISS); @@ -39,9 +39,9 @@ void spare() { @DisplayName("3회 스트라이크") void strike() { Frame frame = new LastFrame(); - frame.firstThrow(new Hit(10)); - frame.secondThrow(new Hit(10)); - frame.thirdThrow(new Hit(10)); + frame.play(new Hit(10)); + frame.play(new Hit(10)); + frame.play(new Hit(10)); assertThat(frame.getFirstHit()).isEqualTo(new Hit(10)); assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.STRIKE); @@ -56,8 +56,8 @@ void strike() { @DisplayName("거터") void gutter() { Frame frame = new LastFrame(); - frame.firstThrow(new Hit(0)); - frame.secondThrow(new Hit(5)); + frame.play(new Hit(0)); + frame.play(new Hit(5)); assertThat(frame.getFirstHit()).isEqualTo(new Hit(0)); assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.GUTTER); @@ -70,8 +70,8 @@ void gutter() { @DisplayName("두번의 투구 모두 거터") void gutter_2() { Frame frame = new LastFrame(); - frame.firstThrow(new Hit(0)); - frame.secondThrow(new Hit(0)); + frame.play(new Hit(0)); + frame.play(new Hit(0)); assertThat(frame.getFirstHit()).isEqualTo(new Hit(0)); assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.GUTTER); @@ -92,7 +92,7 @@ void isEnd() { @DisplayName("한번 던져서 스트라이크를 치지 않은 뒤 종료되지 않은 상태 테스트") void isEnd_afterFirst() { Frame frame = new LastFrame(); - frame.firstThrow(new Hit(5)); + frame.play(new Hit(5)); assertThat(frame.isEnd()).isFalse(); } diff --git a/src/test/java/bowling/domain/NormalFrameTest.java b/src/test/java/bowling/domain/NormalFrameTest.java index 9324c84e59..7651b50c76 100644 --- a/src/test/java/bowling/domain/NormalFrameTest.java +++ b/src/test/java/bowling/domain/NormalFrameTest.java @@ -11,7 +11,7 @@ public class NormalFrameTest { @DisplayName("스트라이크") void strike() { Frame frame = new NormalFrame(); - frame.firstThrow(new Hit(10)); + frame.play(new Hit(10)); assertThat(frame.getFirstHit()).isEqualTo(new Hit(10)); assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.STRIKE); @@ -24,8 +24,8 @@ void strike() { @DisplayName("스페어") void spare() { Frame frame = new NormalFrame(); - frame.firstThrow(new Hit(8)); - frame.secondThrow(new Hit(2)); + frame.play(new Hit(8)); + frame.play(new Hit(2)); assertThat(frame.getFirstHit()).isEqualTo(new Hit(8)); assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.MISS); @@ -38,8 +38,8 @@ void spare() { @DisplayName("거터") void gutter() { Frame frame = new NormalFrame(); - frame.firstThrow(new Hit(0)); - frame.secondThrow(new Hit(5)); + frame.play(new Hit(0)); + frame.play(new Hit(5)); assertThat(frame.getFirstHit()).isEqualTo(new Hit(0)); assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.GUTTER); @@ -52,8 +52,8 @@ void gutter() { @DisplayName("두번의 투구 모두 거터") void gutter_2() { Frame frame = new NormalFrame(); - frame.firstThrow(new Hit(0)); - frame.secondThrow(new Hit(0)); + frame.play(new Hit(0)); + frame.play(new Hit(0)); assertThat(frame.getFirstHit()).isEqualTo(new Hit(0)); assertThat(frame.getFirstStatus()).isEqualTo(FrameStatus.GUTTER); @@ -74,7 +74,7 @@ void isEnd() { @DisplayName("한번 던져서 스트라이크를 치지 않은 뒤 종료되지 않은 상태 테스트") void isEnd_afterFirst() { Frame frame = new NormalFrame(); - frame.firstThrow(new Hit(5)); + frame.play(new Hit(5)); assertThat(frame.isEnd()).isFalse(); }