diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3be8e77 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.project/ +.gradle/ +.idea/ +bin/ +build/ +.settings +.classpath +out/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index 1b85b74..2242c59 100755 --- a/build.gradle +++ b/build.gradle @@ -11,4 +11,9 @@ repositories { dependencies { testCompile('org.junit.jupiter:junit-jupiter:5.4.2') testCompile('org.assertj:assertj-core:3.11.1') + implementation 'junit:junit:4.12' +} + +test { + useJUnitPlatform() } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 290541c..96038ea 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Thu Feb 20 23:25:10 KST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/BowlingApplication.java b/src/main/java/BowlingApplication.java new file mode 100644 index 0000000..743e51b --- /dev/null +++ b/src/main/java/BowlingApplication.java @@ -0,0 +1,30 @@ +import domain.BowlingGame; +import domain.FallingPin; +import domain.Player; +import domain.Players; +import input.BowlingGameInputtable; +import input.ConsoleBowlingGameInputInterface; +import output.ConsoleBowlingScorePresenter; + +import java.util.Collections; + +public class BowlingApplication { + private static final int DEFAULT_BOWLING_MAX_FRAME = 10; + private static BowlingGameInputtable inputManager = new ConsoleBowlingGameInputInterface(); + + public static void main(String[] args) throws IllegalAccessException { + String playerName = inputManager.getPlayerName(); + Player player = new Player(playerName); + Players playerList = new Players(Collections.singletonList(player)); + BowlingGame bowlingGame = new BowlingGame(DEFAULT_BOWLING_MAX_FRAME, playerList); + + while (bowlingGame.hasNext()) { + Player thisTurnPlayer = bowlingGame.nextPlayer(); + FallingPin fallingPinCount = inputManager.getFallingPint(); + thisTurnPlayer.bowl(fallingPinCount); + ConsoleBowlingScorePresenter.print(bowlingGame); + } + + System.out.println("--볼링 게임 끝--"); + } +} \ No newline at end of file diff --git a/src/main/java/domain/BowlingGame.java b/src/main/java/domain/BowlingGame.java new file mode 100644 index 0000000..d230b85 --- /dev/null +++ b/src/main/java/domain/BowlingGame.java @@ -0,0 +1,37 @@ +package domain; + +public class BowlingGame { + public final int MAX_FRAME; + private final Players players; + private int nowFrameNumber; + + public BowlingGame(int maxFrame, Players players) { + this.MAX_FRAME = maxFrame; + this.players = players; + } + + public boolean hasNext() { + return MAX_FRAME > nowFrameNumber; + } + + public Player nextPlayer() { + if (players.isLastPlayer()) { + nowFrameNumber++; + } + + return players.getCurrentPlayer(); + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("| NAME |"); + for (int i = 0; i < MAX_FRAME; i++) { + stringBuilder.append(String.format(" %2d |", i + 1)); + } + + stringBuilder.append("\n").append(players); + + return stringBuilder.toString() + "\n"; + } +} diff --git a/src/main/java/domain/FallingPin.java b/src/main/java/domain/FallingPin.java new file mode 100644 index 0000000..e83de30 --- /dev/null +++ b/src/main/java/domain/FallingPin.java @@ -0,0 +1,49 @@ +package domain; + +public class FallingPin { + public final static FallingPin MISS = new FallingPin(0); + public final static FallingPin NONE = new FallingPin(-1); + private int fallingPinCount; + + public FallingPin(int fallingPinCount) { + if (fallingPinCount > Frame.DEFAULT_BOWLING_PIN) { + throw new IllegalArgumentException("10개 이하"); + } + this.fallingPinCount = fallingPinCount; + } + + public int value() { + if(this.equals(NONE)) { + return 0; + } + return fallingPinCount; + } + + public String getSymbol() { + if (this.equals(MISS)) { + return Frame.FrameStatus.MISS.symbol; + } + + if (this.equals(NONE)) { + return Frame.FrameStatus.NONE.symbol; + } + + return String.valueOf(fallingPinCount); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FallingPin that = (FallingPin) o; + + return fallingPinCount == that.fallingPinCount; + + } + + @Override + public int hashCode() { + return fallingPinCount; + } +} diff --git a/src/main/java/domain/Frame.java b/src/main/java/domain/Frame.java new file mode 100644 index 0000000..ca77914 --- /dev/null +++ b/src/main/java/domain/Frame.java @@ -0,0 +1,123 @@ +package domain; + +import java.util.Objects; + +import static domain.Frame.FrameStatus.*; + +public class Frame implements Scorable { + public final static int DEFAULT_BOWLING_PIN = 10; + public final static Frame NONE_FRAME = new Frame(); + + private FallingPin first = FallingPin.NONE; + private FallingPin second = FallingPin.NONE; + + private Frame nextFrame = NONE_FRAME; + + public void fall(FallingPin pins) throws IllegalAccessException { + if (first.equals(FallingPin.NONE)) { + this.first = pins; + return; + } + this.second = pins; + + if (pinCount() > DEFAULT_BOWLING_PIN) { + throw new IllegalAccessException(); + } + } + + private int pinCount() { + return first.value() + second.value(); + } + + public void setNextFrame(Frame nextFrame) { + this.nextFrame = nextFrame; + } + + public boolean isEnd() { + if (FrameStatus.of(this).equals(STRIKE)) { + return true; + } + return !second.equals(FallingPin.NONE); + } + + @Override + public Score getScore() { // 다듬을 여지 있음 + if (Objects.isNull(nextFrame) || !isEnd()) { + return Score.NOT_DETERMINED; + } + + Score nextFrameScore = Score.of(0); + + if (FrameStatus.of(this).equals(STRIKE)) { + if (!nextFrame.isEnd()) { + return Score.NOT_DETERMINED; + } + nextFrameScore = nextFrame.getFallingPinCount(); + } + + if (FrameStatus.of(this).equals(SPARE)) { + if (nextFrame.isEndFirstTry()) { + return Score.NOT_DETERMINED; + } + nextFrameScore = nextFrame.getFallingPinCountAtFirstTry(); + } + + return getFallingPinCount().add(nextFrameScore); + } + + private boolean isEndFirstTry() { + return first.equals(FallingPin.NONE); + } + + private Score getFallingPinCountAtFirstTry() { + return Score.of(first.value()); + } + + private Score getFallingPinCount() { + return Score.of(first.value() + second.value()); + } + + @Override + public String toString() { + switch (FrameStatus.of(this)) { + case STRIKE: + return " " + STRIKE.symbol + " "; + + case SPARE: + return " " + first.getSymbol() + "|" + SPARE.symbol + " "; + + default: + return " " + first.getSymbol() + "|" + second.getSymbol() + " "; + } + } + + public enum FrameStatus { + STRIKE("X"), SPARE("/"), MISS("-"), HIT(""), NONE(" "); + + String symbol; + + FrameStatus(String symbol) { + this.symbol = symbol; + } + + public static FrameStatus of(Frame frame) { + FallingPin first = frame.first; + + if (first.equals(FallingPin.NONE)) { + return NONE; + } + + if (first.value() == DEFAULT_BOWLING_PIN) { + return STRIKE; + } + + FallingPin second = frame.second; + + if (first.value() + second.value() == DEFAULT_BOWLING_PIN) { + return SPARE; + } + + return HIT; + } + } +} diff --git a/src/main/java/domain/FrameCollection.java b/src/main/java/domain/FrameCollection.java new file mode 100644 index 0000000..f44af30 --- /dev/null +++ b/src/main/java/domain/FrameCollection.java @@ -0,0 +1,52 @@ +package domain; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +public class FrameCollection { + private LinkedList frameList = new LinkedList<>(); + + // 현재 유효한 Frame 반환 + public Frame getCurrent() { + Frame currentFrame; + if (frameList.isEmpty()) { // 비어있으면 List에 새 Frame 추가 후 반환 + currentFrame = new Frame(); + frameList.add(currentFrame); + return currentFrame; + } + + // 비어있지 않지만 + currentFrame = frameList.getLast(); + if (currentFrame.isEnd()) { // 마지막 Frame이 끝났으면 새 Frame 추가 후 반환 + Frame nextFrame = new Frame(); + currentFrame.setNextFrame(nextFrame); + frameList.add(nextFrame); + return nextFrame; + } + + return currentFrame; // 안끝났으면 해당 Frame 반환 + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + frameList.stream() + .map(Frame::toString) + .forEach(s -> stringBuilder.append(s).append("|")); + for (int i = 0; i < Frame.DEFAULT_BOWLING_PIN - frameList.size(); i++) { + stringBuilder.append(" |"); + } + + return stringBuilder.toString(); + } + + public String getScore() { + StringBuilder stringBuilder = new StringBuilder(); + frameList.stream() + .map(Frame::getScore) + .forEach(s -> stringBuilder.append(s).append(" | ")); + return stringBuilder.toString(); + } +} diff --git a/src/main/java/domain/Player.java b/src/main/java/domain/Player.java new file mode 100644 index 0000000..d195651 --- /dev/null +++ b/src/main/java/domain/Player.java @@ -0,0 +1,29 @@ +package domain; + +public class Player { + private String name; + private FrameCollection frames = new FrameCollection(); + + public Player(String playerName) { + this.name = playerName; + } + + public void bowl(FallingPin pins) throws IllegalAccessException { + Frame currentFrame = frames.getCurrent(); + currentFrame.fall(pins); + } + + public boolean hasChance() { + Frame currentFrame = frames.getCurrent(); + return !currentFrame.isEnd(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("| ").append(name).append(" |").append(frames).append("\n"); + sb.append(" | ").append(frames.getScore()); + + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/domain/Players.java b/src/main/java/domain/Players.java new file mode 100644 index 0000000..29add0f --- /dev/null +++ b/src/main/java/domain/Players.java @@ -0,0 +1,41 @@ +package domain; + +import java.util.List; + +public class Players { + private List players; + private int currentPlayerIndex; + + public Players(List players) { + this.players = players; + } + + public Player getCurrentPlayer() { + Player currentPlayer = currentPlayer(); + if (currentPlayer.hasChance()) { + return currentPlayer; + } + + return nextPlayer(); + } + + private Player nextPlayer() { + currentPlayerIndex = currentPlayerIndex % players.size(); + return players.get(currentPlayerIndex++); + } + + public boolean isLastPlayer() { + return currentPlayerIndex == players.size(); + } + + private Player currentPlayer() { + return players.get(currentPlayerIndex); + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + players.stream().map(Player::toString).forEach(stringBuilder::append); + return stringBuilder.toString(); + } +} diff --git a/src/main/java/domain/Scorable.java b/src/main/java/domain/Scorable.java new file mode 100644 index 0000000..0966d59 --- /dev/null +++ b/src/main/java/domain/Scorable.java @@ -0,0 +1,5 @@ +package domain; + +public interface Scorable { + Score getScore(); +} diff --git a/src/main/java/domain/Score.java b/src/main/java/domain/Score.java new file mode 100644 index 0000000..27b2bc7 --- /dev/null +++ b/src/main/java/domain/Score.java @@ -0,0 +1,29 @@ +package domain; + +public class Score { + public final static Score NOT_DETERMINED = Score.of(0); + + private int score; + + private Score(int score) { + this.score = score; + } + + public static Score of(int score) { + return new Score(score); + } + + public Score add(Score score) { + this.score += score.score; + return Score.of(this.score); + } + + @Override + public String toString() { + if (this.equals(NOT_DETERMINED)) { + return " "; + } + + return String.format("%2d", score); + } +} diff --git a/src/main/java/empty.txt b/src/main/java/empty.txt deleted file mode 100755 index e69de29..0000000 diff --git a/src/main/java/input/BowlingGameInputtable.java b/src/main/java/input/BowlingGameInputtable.java new file mode 100644 index 0000000..b67d6cd --- /dev/null +++ b/src/main/java/input/BowlingGameInputtable.java @@ -0,0 +1,8 @@ +package input; + +import domain.FallingPin; + +public interface BowlingGameInputtable { + String getPlayerName(); + FallingPin getFallingPint(); +} diff --git a/src/main/java/input/ConsoleBowlingGameInputInterface.java b/src/main/java/input/ConsoleBowlingGameInputInterface.java new file mode 100644 index 0000000..12700e7 --- /dev/null +++ b/src/main/java/input/ConsoleBowlingGameInputInterface.java @@ -0,0 +1,21 @@ +package input; + +import domain.FallingPin; + +import java.util.Scanner; + +public class ConsoleBowlingGameInputInterface implements BowlingGameInputtable { + private Scanner scanner = new Scanner(System.in); + + @Override + public String getPlayerName() { + System.out.println("이름: "); + return scanner.nextLine(); + } + + @Override + public FallingPin getFallingPint() { + System.out.println("쓰러트린 핀: "); + return new FallingPin(scanner.nextInt()); + } +} diff --git a/src/main/java/output/BowlingScorePresentable.java b/src/main/java/output/BowlingScorePresentable.java new file mode 100644 index 0000000..0f79448 --- /dev/null +++ b/src/main/java/output/BowlingScorePresentable.java @@ -0,0 +1,11 @@ +package output; + +public interface BowlingScorePresentable { + /*void show(final List players); + + void show(Player player); + + void show(Frame frame); + + void show(FallingPin fallingPin);*/ +} diff --git a/src/main/java/output/ConsoleBowlingScorePresenter.java b/src/main/java/output/ConsoleBowlingScorePresenter.java new file mode 100644 index 0000000..b8fa12d --- /dev/null +++ b/src/main/java/output/ConsoleBowlingScorePresenter.java @@ -0,0 +1,53 @@ +package output; + +import domain.BowlingGame; + +public class ConsoleBowlingScorePresenter implements BowlingScorePresentable { + + private final static String STRIKE_SYMBOL = "X"; + private final static String SPARE_SYMBOL = "/"; + private final static String MISS_SYMBOL = "-"; + + public static void print(BowlingGame bowlingGame) { + + } + + /*@Override + public void show(final List players) { + // print about player info (score) + } + + @Override + public void show(Player player) { + + } + + @Override + public void show(Frame frame) { + StringBuilder stringBuilder = new StringBuilder(); + + + switch (Frame.FrameStatus.of(frame)) { + case STRIKE: + break; + } + + + System.out.println(stringBuilder); + } + + @Override + public void show(FallingPin fallingPin) { + + } + + public static void main(String[] args) { + String s = "5|/"; + String s1 = "X"; + // String s2 = "5|/"; + System.out.print("|"); + + System.out.print(String.format("%-6s|%-6s|%-6s", s, s1, s)); + System.out.print("|"); + }*/ +} diff --git a/src/test/java/empty.txt b/src/test/java/empty.txt deleted file mode 100755 index e69de29..0000000 diff --git a/src/test/java/ouput/ConsoleBowlingScorePresenterTest.java b/src/test/java/ouput/ConsoleBowlingScorePresenterTest.java new file mode 100644 index 0000000..929e049 --- /dev/null +++ b/src/test/java/ouput/ConsoleBowlingScorePresenterTest.java @@ -0,0 +1,56 @@ +package ouput; + +import domain.*; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +public class ConsoleBowlingScorePresenterTest { + @Test + @DisplayName("콘솔 출력 확인") + void scorePrintTest() throws IllegalAccessException { + + + /* FrameCollection frameCollection = new FrameCollection(); + frameCollection.add(new Frame(new FallingPin(10), new FallingPin(0))); + frameCollection.add(new Frame(new FallingPin(3), new FallingPin(7))); + frameCollection.add(new Frame(new FallingPin(4), new FallingPin(4))); + frameCollection.add(new Frame(new FallingPin(4), new FallingPin(0))); + frameCollection.add(new Frame(new FallingPin(0), new FallingPin(4))); + frameCollection.add(new Frame(new FallingPin(0), new FallingPin(0)));*/ + + Player player = new Player("LSH"); + BowlingGame bowlingGame = new BowlingGame(10, new Players(Collections.singletonList(player))); + + player = bowlingGame.nextPlayer(); + + player.bowl(new FallingPin(10)); + System.out.println(bowlingGame); + + player.bowl(new FallingPin(0)); + System.out.println(bowlingGame); + player.bowl(new FallingPin(3)); + System.out.println(bowlingGame); + + player.bowl(new FallingPin(7)); + System.out.println(bowlingGame); + player.bowl(new FallingPin(3)); + System.out.println(bowlingGame); + + player.bowl(new FallingPin(4)); + System.out.println(bowlingGame); + player.bowl(new FallingPin(4)); + System.out.println(bowlingGame); + + player.bowl(new FallingPin(0)); + System.out.println(bowlingGame); + player.bowl(new FallingPin(4)); + System.out.println(bowlingGame); + + player.bowl(new FallingPin(4)); + System.out.println(bowlingGame); + player.bowl(new FallingPin(0)); + System.out.println(bowlingGame); + } +}