diff --git a/src/main/java/bowling/App.java b/src/main/java/bowling/App.java index dfe4c49525..2783e33e19 100644 --- a/src/main/java/bowling/App.java +++ b/src/main/java/bowling/App.java @@ -17,7 +17,7 @@ public static void main(String[] args) { frameNumber = bowlingGame.pitchingBall(count); Frames frames = bowlingGame.getFrames(); - OutputView.printScoreScreen(username, frames); + OutputView.printScoreScreen(username, frames, bowlingGame); } } } diff --git a/src/main/java/bowling/BowlingGame.java b/src/main/java/bowling/BowlingGame.java index 1e3818b8f2..83340205f9 100644 --- a/src/main/java/bowling/BowlingGame.java +++ b/src/main/java/bowling/BowlingGame.java @@ -11,9 +11,6 @@ public BowlingGame() { } public FrameNumber pitchingBall(int hitCount) { - if (frameNumber.isSameNumber(FRAME_SIZE)) { - return frames.pitchingLast(frameNumber, hitCount); - } this.frameNumber = frames.pitching(frameNumber, hitCount); return this.frameNumber; } @@ -21,4 +18,8 @@ public FrameNumber pitchingBall(int hitCount) { public Frames getFrames() { return frames; } + + public FrameNumber getFrameNumber() { + return frameNumber; + } } diff --git a/src/main/java/bowling/FinalFrame.java b/src/main/java/bowling/FinalFrame.java new file mode 100644 index 0000000000..b610907bd6 --- /dev/null +++ b/src/main/java/bowling/FinalFrame.java @@ -0,0 +1,22 @@ +package bowling; + +public class FinalFrame extends Frame { + + @Override + public boolean strikeCondition() { + return (!hitRecords.hitTimes(HIT_TWICE) || hitRecords.isRecordAllStrike()) && bowlingPin.isZero(); + } + + @Override + public boolean finishFrame() { + boolean result = hitRecords.hitTimes(HIT_TRIPLE) || failedBounsFrame(); + if (clearAllFrame()) { + chargeBowlingPin(); + } + return result; + } + + private boolean failedBounsFrame() { + return hitRecords.hitTimes(HIT_TWICE) && !clearAllFrame(); + } +} diff --git a/src/main/java/bowling/Frame.java b/src/main/java/bowling/Frame.java index 170d15514f..bbb22ea62f 100644 --- a/src/main/java/bowling/Frame.java +++ b/src/main/java/bowling/Frame.java @@ -1,70 +1,45 @@ package bowling; -import java.util.ArrayList; import java.util.List; public class Frame { - private static final int HIT_ONCE = 1; - private static final int HIT_TWICE = 2; - private static final int HIT_TRIPLE = 3; + protected static final int HIT_TWICE = 2; + protected static final int HIT_TRIPLE = 3; - private BowlingPin bowlingPin; - private final List hitRecords; + protected BowlingPin bowlingPin; + protected final HitRecords hitRecords; public Frame() { this.bowlingPin = new BowlingPin(BowlingPin.MAX_PIN_NUMBER); - hitRecords = new ArrayList<>(); + this.hitRecords = new HitRecords(); } - public boolean finishLastFrame() { - return hitThreeTimes() || hitDouble() || failedBounsFrame(); - } - - private boolean failedBounsFrame() { - return hitTwice() && !clearAllFrame(); - } - - public void hitBowlingPin(int count) { + public BowilingTerm hitBowlingPin(int count) { bowlingPin = bowlingPin.hitPins(new BowlingPin(count)); - BowilingTerm bowilingTerm = BowilingTerm.MISS; - if ((hitRecords.isEmpty() || hitRecords.size() == HIT_TRIPLE || hitStrike()) && bowlingPin.isZero()) { - bowilingTerm = BowilingTerm.STRIKE; - hitRecords.add(new HitRecord(count, bowilingTerm)); - return ; + if (hitRecords.hitOnce() && bowlingPin.isZero()) { + hitRecords.addSpare(); + return BowilingTerm.SPARE; } - if (hitRecords.size() == HIT_ONCE && bowlingPin.isZero()) { - bowilingTerm = BowilingTerm.SPARE; - hitRecords.add(new HitRecord(count, bowilingTerm)); - return ; + if (strikeCondition()) { + hitRecords.addStrike(); + return BowilingTerm.STRIKE; } - if (count == 0) { - bowilingTerm = BowilingTerm.GUTTER; - hitRecords.add(new HitRecord(count, bowilingTerm)); - return ; + if (count == BowlingPin.ZERO) { + hitRecords.addGutter(); + return BowilingTerm.GUTTER; } - hitRecords.add(new HitRecord(count, bowilingTerm)); + hitRecords.addMiss(count); + return BowilingTerm.MISS; } - private boolean hitStrike() { - for (HitRecord hitRecord : hitRecords) { - if (!hitRecord.hitAll()) { - return false; - } - } - return true; - } - - public boolean finishFrame() { - return clearAllFrame() || hitTwice(); + protected boolean strikeCondition() { + return bowlingPin.isZero(); } - public boolean hitDouble() { - if (hitRecords.size() != HIT_TWICE) { - return false; - } - return hitStrike(); + protected boolean finishFrame() { + return clearAllFrame() || hitRecords.hitTimes(HIT_TWICE); } public void chargeBowlingPin() { @@ -72,18 +47,18 @@ public void chargeBowlingPin() { } public List getHitRecords() { - return hitRecords; + return hitRecords.getHitRecords(); } public boolean clearAllFrame() { return bowlingPin.isZero(); } - public boolean hitThreeTimes() { - return hitRecords.size() == HIT_TRIPLE; + public void calculateBonus(int hitCount) { + hitRecords.calculateBonus(hitCount); } - public boolean hitTwice() { - return hitRecords.size() == HIT_TWICE; + public HitRecords hitRecords() { + return hitRecords; } } diff --git a/src/main/java/bowling/Frames.java b/src/main/java/bowling/Frames.java index 370f03eb0f..9169ac1444 100644 --- a/src/main/java/bowling/Frames.java +++ b/src/main/java/bowling/Frames.java @@ -1,39 +1,36 @@ package bowling; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; +import java.util.ListIterator; public class Frames { - private final List frames; + private final LinkedList frames; public Frames(int size) { - this.frames = new ArrayList<>(); - for (int i = 0; i < size; i++) { + this.frames = new LinkedList<>(); + for (int i = 0; i < size - 1; i++) { this.frames.add(new Frame()); } + this.frames.add(new FinalFrame()); } public FrameNumber pitching(FrameNumber frameNumber, int hitCount) { Frame frame = frames.get(frameNumber.retrieveIndexNumber()); - frame.hitBowlingPin(hitCount); - if (frame.finishFrame()) { - return frameNumber.next(); + BowilingTerm bowilingTerm = frame.hitBowlingPin(hitCount); + + if (!bowilingTerm.equals(BowilingTerm.STRIKE)) { + ListIterator frameListIterator = frames.listIterator(frameNumber.retrieveIndexNumber()); + while (frameListIterator.hasPrevious()) { + Frame previous = frameListIterator.previous(); + previous.calculateBonus(hitCount); + } } - return frameNumber; - } - - public FrameNumber pitchingLast(FrameNumber frameNumber, int hitCount) { - Frame frame = frames.get(frameNumber.retrieveIndexNumber()); - frame.hitBowlingPin(hitCount); - if (frame.finishLastFrame()) { + if (frame.finishFrame()) { return frameNumber.next(); } - - if (frame.clearAllFrame()) { - frame.chargeBowlingPin(); - return frameNumber; - } return frameNumber; } diff --git a/src/main/java/bowling/HitRecord.java b/src/main/java/bowling/HitRecord.java index 7ad28a0828..45f8d77f2e 100644 --- a/src/main/java/bowling/HitRecord.java +++ b/src/main/java/bowling/HitRecord.java @@ -4,6 +4,10 @@ public class HitRecord { private final int hitCount; private final BowilingTerm bowilingTerm; + public static HitRecord of(int count, BowilingTerm bowilingTerm) { + return new HitRecord(count, bowilingTerm); + } + public HitRecord(int count, BowilingTerm bowilingTerm) { this.hitCount = count; this.bowilingTerm = bowilingTerm; diff --git a/src/main/java/bowling/HitRecords.java b/src/main/java/bowling/HitRecords.java index 2cc56a20f2..7c1874ac15 100644 --- a/src/main/java/bowling/HitRecords.java +++ b/src/main/java/bowling/HitRecords.java @@ -1,11 +1,72 @@ package bowling; +import java.util.ArrayList; import java.util.List; public class HitRecords { - private List hitRecords; + private static final int HIT_ONCE = 1; - public HitRecords(List hitRecords) { - this.hitRecords = hitRecords; + private final List hitRecords; + private Score score; + + public HitRecords() { + this.hitRecords = new ArrayList<>(); + this.score = Score.ofZero(); + } + + public boolean hitOnce() { + return hitRecords.size() == HIT_ONCE; + } + + + public void addRecord(HitRecord hitRecord) { + this.hitRecords.add(hitRecord); + } + + public void addStrike() { + this.hitRecords.add(HitRecord.of(10, BowilingTerm.STRIKE)); + score = Score.ofStrike(); + } + + public void addSpare() { + this.hitRecords.add(HitRecord.of(10, BowilingTerm.SPARE)); + score = Score.ofSpare(); + } + + public void addGutter() { + this.hitRecords.add(HitRecord.of(0, BowilingTerm.GUTTER)); + score = Score.ofMiss(hitSum()); + } + + public void addMiss(int count) { + this.hitRecords.add(HitRecord.of(count, BowilingTerm.MISS)); + score = Score.ofMiss(hitSum()); + } + + public boolean isRecordAllStrike() { + return hitRecords.stream() + .allMatch(HitRecord::hitAll); + } + + public boolean hitTimes(int hitTwice) { + return hitRecords.size() == hitTwice; + } + + public List getHitRecords() { + return hitRecords; + } + + private int hitSum() { + return hitRecords.stream().mapToInt(HitRecord::getHitCount).sum(); + } + + public void calculateBonus(int hitCount) { + if (score.remainBonus()) { + score = score.addBonusScore(hitCount); + } + } + + public Score getScore() { + return score; } } diff --git a/src/main/java/bowling/Score.java b/src/main/java/bowling/Score.java new file mode 100644 index 0000000000..2a68ef73d4 --- /dev/null +++ b/src/main/java/bowling/Score.java @@ -0,0 +1,47 @@ +package bowling; + +public class Score { + private final int hitCount; + private final int bonusCount; + + public static Score ofZero() { + return new Score(0, -1); + } + + public static Score ofStrike() { + return new Score(10, 2); + } + + public static Score ofSpare() { + return new Score(10, 1); + } + + public static Score ofMiss(int hitCount) { + return new Score(hitCount, 0); + } + + private Score(int hitCount, int bonusCount) { + this.hitCount = hitCount; + this.bonusCount = bonusCount; + } + + public boolean remainBonus() { + return bonusCount != 0; + } + + public Score addBonusScore(int hitCount) { + return new Score(this.hitCount + hitCount, bonusCount - 1); + } + + public int getHitCount() { + return hitCount; + } + + public int getBonusCount() { + return bonusCount; + } + + public boolean isMiss() { + return hitCount != 10; + } +} diff --git a/src/main/java/bowling/Scores.java b/src/main/java/bowling/Scores.java new file mode 100644 index 0000000000..ac0d1b86e0 --- /dev/null +++ b/src/main/java/bowling/Scores.java @@ -0,0 +1,12 @@ +package bowling; + +import java.util.ArrayList; +import java.util.List; + +public class Scores { + private final List scores; + + public Scores() { + this.scores = new ArrayList<>(); + } +} diff --git a/src/main/java/bowling/view/OutputView.java b/src/main/java/bowling/view/OutputView.java index 3100d9511d..6c8ce7af8f 100644 --- a/src/main/java/bowling/view/OutputView.java +++ b/src/main/java/bowling/view/OutputView.java @@ -6,6 +6,7 @@ public class OutputView { private static final String BASE_SCORE_SCREEN = "| NAME | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 |"; + private static final String START_SHAPE = "| |"; private static final String FORMAT_SHAPE = " %-4s|"; private static final String BONUS_SHAPE = " %-6s|"; private static final String SEPARATOR = "|"; @@ -15,7 +16,7 @@ private OutputView() { } - public static void printScoreScreen(Username username, Frames frames) { + public static void printScoreScreen(Username username, Frames frames, BowlingGame bowlingGame) { System.out.println(BASE_SCORE_SCREEN); StringBuilder sb = new StringBuilder(); @@ -29,11 +30,40 @@ public static void printScoreScreen(Username username, Frames frames) { } Frame lastFrame = framesList.get(framesList.size() - 1); sb.append(String.format(BONUS_SHAPE, makeFrameScoreShape(lastFrame.getHitRecords()))); + sb.append("\n"); + int totalScore = 0; + sb.append(START_SHAPE); + for (int i = 0; i < framesList.size() - 1; i++) { + Frame frame = framesList.get(i); + FrameNumber frameNumber = bowlingGame.getFrameNumber(); + String score = makeFrameScore(i, frame.hitRecords(), frameNumber, totalScore); + if (!score.equals("")) { + totalScore += Integer.parseInt(score); + score = String.valueOf(totalScore); + } + sb.append(String.format(FORMAT_SHAPE, score)); + } + FrameNumber frameNumber = bowlingGame.getFrameNumber(); + lastFrame = framesList.get(framesList.size() - 1); + String score = makeFrameScore(framesList.size() - 1, lastFrame.hitRecords(), frameNumber, totalScore); + if (!score.equals("")) { + totalScore += Integer.parseInt(score); + score = String.valueOf(totalScore); + } + sb.append(String.format(BONUS_SHAPE, score)); sb.append("\n"); System.out.println(sb); } + private static String makeFrameScore(int index, HitRecords hitRecords, FrameNumber frameNumber, int totalScore) { + Score score = hitRecords.getScore(); + if (score.remainBonus() || index + 1 >= frameNumber.getFrameNumber()) { + return ""; + } + return String.valueOf(score.getHitCount()); + } + private static String makeUserFormat(Username username) { return String.format(SEPARATOR + FORMAT_SHAPE, username.getName()); } diff --git a/src/test/java/bowling/FramesTest.java b/src/test/java/bowling/FramesTest.java index 969f84ef71..eaca8713db 100644 --- a/src/test/java/bowling/FramesTest.java +++ b/src/test/java/bowling/FramesTest.java @@ -1,11 +1,9 @@ package bowling; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.*; class FramesTest { @@ -42,27 +40,17 @@ void pitchingStrike() { void lastPitching() { int lastNumber = 10; Frames frames = new Frames(lastNumber); - FrameNumber frameNumber = frames.pitchingLast(FrameNumber.number(lastNumber), BowlingPin.MAX_PIN_NUMBER); + FrameNumber frameNumber = frames.pitching(FrameNumber.number(lastNumber), BowlingPin.MAX_PIN_NUMBER); assertThat(frameNumber).isEqualTo(FrameNumber.number(lastNumber)); } - @Test - @DisplayName("마지막 프레임의 스트라이크의 두번째 스트라이크는 프레임이 변함") - void lastPitchingDouble() { - int lastNumber = 10; - Frames frames = new Frames(lastNumber); - frames.pitchingLast(FrameNumber.number(lastNumber), BowlingPin.MAX_PIN_NUMBER); - FrameNumber frameNumber = frames.pitchingLast(FrameNumber.number(lastNumber), BowlingPin.MAX_PIN_NUMBER); - assertThat(frameNumber).isEqualTo(FrameNumber.number(lastNumber).next()); - } - @Test @DisplayName("마지막 프레임의 스페어는 프레임이 변하지 않음") void lastPitchingSpare() { int lastNumber = 10; Frames frames = new Frames(lastNumber); - frames.pitchingLast(FrameNumber.number(lastNumber), 5); - FrameNumber frameNumber = frames.pitchingLast(FrameNumber.number(lastNumber), 5); + frames.pitching(FrameNumber.number(lastNumber), 5); + FrameNumber frameNumber = frames.pitching(FrameNumber.number(lastNumber), 5); assertThat(frameNumber).isEqualTo(FrameNumber.number(lastNumber)); } @@ -71,8 +59,8 @@ void lastPitchingSpare() { void lastFrameMiss() { int lastNumber = 10; Frames frames = new Frames(lastNumber); - frames.pitchingLast(FrameNumber.number(lastNumber), 5); - FrameNumber frameNumber = frames.pitchingLast(FrameNumber.number(lastNumber), 4); + frames.pitching(FrameNumber.number(lastNumber), 5); + FrameNumber frameNumber = frames.pitching(FrameNumber.number(lastNumber), 4); assertThat(frameNumber).isEqualTo(FrameNumber.number(lastNumber).next()); } @@ -81,9 +69,9 @@ void lastFrameMiss() { void lastFramePitchingThreeTimes() { int lastNumber = 10; Frames frames = new Frames(lastNumber); - frames.pitchingLast(FrameNumber.number(lastNumber), 5); - frames.pitchingLast(FrameNumber.number(lastNumber), 5); - FrameNumber frameNumber = frames.pitchingLast(FrameNumber.number(lastNumber), 10); + frames.pitching(FrameNumber.number(lastNumber), 5); + frames.pitching(FrameNumber.number(lastNumber), 5); + FrameNumber frameNumber = frames.pitching(FrameNumber.number(lastNumber), 10); assertThat(frameNumber).isEqualTo(FrameNumber.number(lastNumber).next()); } } \ No newline at end of file