diff --git a/.gitignore b/.gitignore index 6c018781..687f03d8 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,147 @@ out/ ### VS Code ### .vscode/ + + +# Created by https://www.toptal.com/developers/gitignore/api/macos,intellij +# Edit at https://www.toptal.com/developers/gitignore?templates=macos,intellij + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# End of https://www.toptal.com/developers/gitignore/api/macos,intellij \ No newline at end of file diff --git a/README.md b/README.md index 03ba7ed3..f45b8b57 100644 --- a/README.md +++ b/README.md @@ -1 +1,119 @@ -# java-blackjack \ No newline at end of file +# ๐Ÿ“Œ ์—ฐ๋ฃŒ ์ฃผ์ž… + +## ๊ธฐ๋Šฅ ์š”๊ตฌ ์‚ฌํ•ญ +์šฐ๋ฆฌ ํšŒ์‚ฌ๋Š” ๋ Œํ„ฐ์นด๋ฅผ ์šด์˜ํ•˜๊ณ  ์žˆ๋‹ค. +ํ˜„์žฌ ๋ณด์œ ํ•˜๊ณ  ์žˆ๋Š” ์ฐจ๋Ÿ‰์€ Sonata 2๋Œ€, Avante 1๋Œ€, K5 2๋Œ€๋กœ ์ด 5๋Œ€์˜ ์ฐจ๋Ÿ‰์„ ๋ณด์œ ํ•˜๊ณ  ์žˆ๋‹ค. +๊ณ ๊ฐ์ด ์ธํ„ฐ๋„ท์œผ๋กœ๋ถ€ํ„ฐ ์˜ˆ์•ฝํ•  ๋•Œ ์—ฌํ–‰ํ•  ๋ชฉ์ ์ง€์˜ ๋Œ€๋žต์ ์ธ ์ด๋™๊ฑฐ๋ฆฌ๋ฅผ ์ž…๋ ฅ ๋ฐ›๋Š”๋‹ค. +์ด ์ด๋™๊ฑฐ๋ฆฌ๋ฅผ ํ™œ์šฉํ•ด ์ฐจ๋Ÿ‰ ๋ณ„๋กœ ํ•„์š”ํ•œ ์—ฐ๋ฃŒ๋ฅผ ์ฃผ์ž…ํ•œ๋‹ค. ์ฐจ๋Ÿ‰ ๋ณ„๋กœ ์ฃผ์ž…ํ•ด์•ผ ํ•  ์—ฐ๋ฃŒ๋Ÿ‰์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋ณด๊ณ ์„œ๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค. + +๊ฐ ์ฐจ๋Ÿ‰๋ณ„ ์—ฐ๋น„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. + +* Sonata : 10km/๋ฆฌํ„ฐ +* Avante : 15km/๋ฆฌํ„ฐ +* K5 : 13km/๋ฆฌํ„ฐ + + +## ๊ตฌํ˜„ ์‚ฌํ•ญ +- [x] ์ž…๋ ฅ (e.g. carName1:distance, carName2:distance) + - [x] ๋ฌธ์ž ์‚ฌ์ด์— ๋ถˆํ•„์š”ํ•œ ๊ณต๋ฐฑ์„ ์ œ๊ฑฐํ•œ๋‹ค. + +- [x] ํŒŒ์„œ - Parser + - [x] ์ž๋™์ฐจ๋Š” `,` ๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค. + - [x] ์ž๋™์ฐจ์˜ ์ด๋ฆ„๊ณผ ์†๋ ฅ์€ `:` ๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค. + - [x] ๊ฒ€์ฆ + - [x] ๋นˆ ๊ฐ’์ด ๋“ค์–ด์˜ฌ ์ˆ˜ ์—†๋‹ค. + - [x] ์ด๋ฆ„์ด๋‚˜ ๊ฑฐ๋ฆฌ์— ๋นˆ ๊ฐ’์ด ๋“ค์–ด์˜ฌ ์ˆ˜ ์—†๋‹ค. + - [x] ์ž…๋ ฅ ํ˜•์‹์— ๋งž๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. + +- [x] ๋ Œํ„ฐ์นด ํšŒ์‚ฌ - RentCompany + - [x] ์ž๋™์ฐจ ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„ ์˜ˆ์•ฝ์„ ์Šน์ธํ•œ๋‹ค. - acceptReservation(List rentInfos) + - [x] ๋ณด์œ ํ•œ ๋ Œํ„ฐ์นด์˜ ์ˆ˜๋ฅผ ๋„˜์ง€ ์•Š๋Š”๋‹ค. - validateOverMinTripDistance + - [x] ์ฐจ๋Ÿ‰ ๋ณ„๋กœ ์ฃผ์ž…ํ•ด์•ผ ํ•  ์—ฐ๋ฃŒ๋Ÿ‰์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋ณด๊ณ ์„œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. - createReport + +- [x] ์ž๋™์ฐจ - abstract + - [x] ์ฃผ์ž…ํ•ด์•ผ ํ•  ์—ฐ๋ฃŒ๋Ÿ‰์„ ๊ตฌํ•œ๋‹ค. - getChargeQuantity + +- [x] ๋ ŒํŠธ์นด -RentCar + - [x] ์—ฌํ–‰ ๊ฑฐ๋ฆฌ์™€ ํƒ€์ž…์„ ๋ฐ›์•„ Car ๊ฐ์ฒด ์ƒ์„ฑ. + - [x] ๊ฒ€์ฆ + - [x] ์ž๋™์ฐจ ์ด๋ฆ„ ํ™•์ธ(๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ X) + - [x] ๊ฑฐ๋ฆฌ๋Š” 1 ์ด์ƒ์ด์–ด์•ผ ํ•œ๋‹ค. + +- [x] ์ถœ๋ ฅ + - [x] ์ƒ์„ฑํ•œ ๋ณด๊ณ ์„œ ์ถœ๋ ฅ. + + +# ๐Ÿ“Œ ๋ธ”๋ž™์žญ + +## ๊ธฐ๋Šฅ ์š”๊ตฌ ์‚ฌํ•ญ +๋ธ”๋ž™์žญ ๊ฒŒ์ž„์„ ๋ณ€ํ˜•ํ•œ ํ”„๋กœ๊ทธ๋žจ์„ ๊ตฌํ˜„ํ•œ๋‹ค. ๋ธ”๋ž™์žญ ๊ฒŒ์ž„์€ `๋”œ๋Ÿฌ`์™€ `ํ”Œ๋ ˆ์ด์–ด` ์ค‘ ์นด๋“œ์˜ ํ•ฉ์ด 21 ๋˜๋Š” 21์— ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ˆซ์ž๋ฅผ ๊ฐ€์ง€๋Š” ์ชฝ์ด ์ด๊ธฐ๋Š” ๊ฒŒ์ž„์ด๋‹ค. + +์นด๋“œ์˜ ์ˆซ์ž ๊ณ„์‚ฐ์€ ์นด๋“œ ์ˆซ์ž๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ํ•˜๋ฉฐ, ์˜ˆ์™ธ๋กœ `Ace๋Š” 1 ๋˜๋Š” 11`๋กœ ๊ณ„์‚ฐํ•œ๋‹ค. +- King, Queen, Jack์€ ๊ฐ๊ฐ 10์œผ๋กœ ๊ณ„์‚ฐํ•œ๋‹ค. +- ๊ฒŒ์ž„์„ ์‹œ์ž‘ํ•˜๋ฉด ํ”Œ๋ ˆ์ด์–ด๋Š” ๋‘ ์žฅ์˜ ์นด๋“œ๋ฅผ ์ง€๊ธ‰ ๋ฐ›๋Š”๋‹ค. +- ๋‘ ์žฅ์˜ ์นด๋“œ ์ˆซ์ž๋ฅผ ํ•ฉ์ณ 21์„ ์ดˆ๊ณผํ•˜์ง€ ์•Š์œผ๋ฉด์„œ 21์— ๊ฐ€๊น๊ฒŒ ๋งŒ๋“ค๋ฉด ์ด๊ธด๋‹ค. 21์„ ๋„˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์›ํ•œ๋‹ค๋ฉด ์–ผ๋งˆ๋“ ์ง€ ์นด๋“œ๋ฅผ ๊ณ„์† ๋ฝ‘์„ ์ˆ˜ ์žˆ๋‹ค. +- ๋”œ๋Ÿฌ๋Š” ์ฒ˜์Œ์— ๋ฐ›์€ 2์žฅ์˜ ํ•ฉ๊ณ„๊ฐ€ 16์ดํ•˜์ด๋ฉด ๋ฐ˜๋“œ์‹œ 1์žฅ์˜ ์นด๋“œ๋ฅผ ์ถ”๊ฐ€๋กœ ๋ฐ›์•„์•ผ ํ•˜๊ณ , 17์  ์ด์ƒ์ด๋ฉด ์ถ”๊ฐ€๋กœ ๋ฐ›์„ ์ˆ˜ ์—†๋‹ค. +- ๊ฒŒ์ž„์„ ์™„๋ฃŒํ•œ ํ›„ ๊ฐ ํ”Œ๋ ˆ์ด์–ด๋ณ„๋กœ ์ŠนํŒจ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค. + - ๋”œ๋Ÿฌ์™€ ํ”Œ๋ ˆ์ด์–ด์˜ ์นด๋“œ ํ•ฉ์ด ๊ฐ™์„ ๊ฒฝ์šฐ, ๋”œ๋Ÿฌ๊ฐ€ ์Šน๋ฆฌํ•œ๋‹ค. + - ๋”œ๋Ÿฌ์™€ ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๋ชจ๋‘ 21์„ ๋„˜์—ˆ์„ ๋•Œ, ๋”œ๋Ÿฌ๊ฐ€ ์Šน๋ฆฌํ•œ๋‹ค. + +- ์นด๋“œ : ์ŠคํŽ˜์ด๋“œ, ํด๋กœ๋ฒ„, ํ•˜ํŠธ, ๋‹ค์ด์•„ 1~10, JQK + +## ๊ตฌํ˜„ ์‚ฌํ•ญ. + +- [x] InputView + - [x] ํ”Œ๋ ˆ์ด์–ด์˜ ์ด๋ฆ„์„ ๋ฐ›๋Š”๋‹ค. (`,`๋กœ ๊ตฌ๋ถ„) + - [x] ์นด๋“œ๋ฅผ ๋ฐ›์„ ์ง€์— ๋Œ€ํ•œ ์—ฌ๋ถ€ ์ž…๋ ฅ (`y`, `n`์œผ๋กœ ๊ตฌ๋ถ„) + - [x] 'y' ์™€ 'n'์ด ์•„๋‹ ๊ฒฝ์šฐ exception์ด ๋ฐœ์ƒํ•œ๋‹ค. + +- [x] Parser + - [x] ํ”Œ๋ ˆ์ด์–ด ์ด๋ฆ„์ด ๋น„์–ด์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. + +- [x] Deck + - [x] ์นด๋“œ ๋ถ„๋ฐฐ + +- [x] Cards + - [x] Cards(list: pattern, rank) + - [x] ์นด๋“œ ๋ฝ‘๊ธฐ - draw + - [x] ๋นˆ ๋ฑ์ด๋ผ๋ฉด ์˜ˆ์™ธ ๋ฐœ์ƒ + - [x] ์นด๋“œ ์ œ๊ฑฐ - Cards remove(Card card) + +- [x] Card + - [x] ACE ์ธ์ง€ ํ™•์ธ. + - [x] ์ด๋ฆ„(patter + rank) + +- [x] Hands + - [x] ๋‘ ์žฅ์˜ ์นด๋“œ๋กœ ์ดˆ๊ธฐํ™” + - [x] ์นด๋“œ ๊ด€๋ฆฌ + - [x] ํ˜„์žฌ ์ ์ˆ˜ ๊ด€๋ฆฌ + - [x] ACE์˜ ๊ฐ’์„ ๊ฒฐ์ •ํ•œ๋‹ค. + +- [x] Score + - [x] Card์˜ ํ•ฉ + - [x] ๊ธฐ์กด ํ•ฉ์— ํ•œ์žฅ์˜ Card์˜ ๊ฐ’์„ ๋”ํ•œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + +- [x] Judge + - [x] ์ŠนํŒจ ๊ตฌํ•˜๊ธฐ + - [x] ๋”œ๋Ÿฌ์™€ ํ”Œ๋ ˆ์ด์–ด์˜ ์นด๋“œ ํ•ฉ์ด ๊ฐ™์„ ๊ฒฝ์šฐ, ๋”œ๋Ÿฌ๊ฐ€ ์Šน๋ฆฌํ•œ๋‹ค. + - [x] ๋”œ๋Ÿฌ์™€ ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๋ชจ๋‘ 21์„ ๋„˜์—ˆ์„ ๋•Œ, ๋”œ๋Ÿฌ๊ฐ€ ์Šน๋ฆฌํ•œ๋‹ค. + +- [x] abstract Participant (๋”œ๋Ÿฌ, ํ”Œ๋ ˆ์ด์–ด) + - [x] ์ด๋ฆ„์„ ๊ฐ€์ง„๋‹ค. + - [x] ์ž์‹ ์˜ ํŒจ๋ฅผ ๊ฐ€์ง„๋‹ค. + - [x] ์นด๋“œํŒจ์˜ ํ•ฉ์‚ฐ์„ ๊ตฌํ•œ๋‹ค. (๊ตฌํ˜„) + - [x] ์นด๋“œ๋ฅผ ๋” ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š”์ง€. + +- [x] Player extends Participant + - [x] ํ”Œ๋ ˆ์ด์–ด ์ด๋ฆ„์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. (Override) + - [x] ์นด๋“œ๋ฅผ ๋” ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š”์ง€. (Override) -> ์นด๋“œ์˜ ํ•ฉ์ด 21์„ ์ดˆ๊ณผํ•˜๋ฉด ํ„ด์„ ์ข…๋ฃŒํ•œ๋‹ค. + +- [x] Dealer extends Participant + - [x] '๋”œ๋Ÿฌ' ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. (Override) + - [x] ์นด๋“œ๋ฅผ ๋” ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š”์ง€. (Override) -> ์นด๋“œ์˜ ํ•ฉ์ด 16์„ ์ดˆ๊ณผํ•˜๋ฉด ํ„ด์„ ์ข…๋ฃŒํ•œ๋‹ค. + +- [x] OutputView + - [x] ์ฐธ๊ฐ€์ž ํŒจ + - [x] ์ฐธ๊ฐ€์ž ์นด๋“œ ๋ถ„๋ฐฐ + - [x] ์ฐธ๊ฐ€์ž ์ตœ์ข… ๊ฒฐ๊ณผ + - [x] ์ฐธ๊ฐ€์ž ์ตœ์ข… ์ŠนํŒจ + - [x] ๋”œ๋Ÿฌ๋Š” 16์ดํ•˜๋ผ ํ•œ์žฅ์˜ ์นด๋“œ๋ฅผ ๋” ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. diff --git a/src/main/java/blackjack/BlackJackApplication.java b/src/main/java/blackjack/BlackJackApplication.java new file mode 100644 index 00000000..5a313309 --- /dev/null +++ b/src/main/java/blackjack/BlackJackApplication.java @@ -0,0 +1,63 @@ +package blackjack; + +import blackjack.domain.judge.Judge; +import blackjack.domain.card.Deck; +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Participant; +import blackjack.domain.participant.Player; +import blackjack.util.Parser; +import blackjack.view.InputView; +import blackjack.view.OutputView; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class BlackJackApplication { + + public static void main(String[] args) { + final Deck deck = new Deck(); + + final List playerNames = Parser.parse(InputView.inputPlayerNames()); + final Participant dealer = new Dealer(deck.dealCards()); + final List players = playerNames.stream() + .map(name -> new Player(name, deck.dealCards())) + .collect(Collectors.toList()); + + OutputView.printStartStatus(combine(dealer, players)); + + drawCardsIfWant(deck, players); + drawCardIfCan(deck, dealer); + + final Judge judge = new Judge(); + OutputView.printGameResult(combine(dealer, players), judge.getWinOrLose(dealer, players)); + } + + private static void drawCardsIfWant(final Deck deck, final List players) { + players.forEach(player -> additionalDraw(deck, player)); + } + + private static void additionalDraw(final Deck deck, final Participant player) { + while (player.canDraw() && isAgree(player)) { + player.addCard(deck.draw()); + OutputView.printParticipantsStatus(Collections.singletonList(player)); + } + } + + private static boolean isAgree(final Participant player) { + return InputView.inputDrawFlag(player.getName()).equalsIgnoreCase("y"); + } + + private static void drawCardIfCan(final Deck deck, final Participant dealer) { + if (dealer.canDraw()) { + dealer.addCard(deck.draw()); + OutputView.printDealerDrawMessage(); + } + } + + private static List combine(final Participant dealer, final List participants) { + return Collections.unmodifiableList( + Stream.concat(Stream.of(dealer), participants.stream()) + .collect(Collectors.toList())); + } +} diff --git a/src/main/java/blackjack/domain/card/Card.java b/src/main/java/blackjack/domain/card/Card.java new file mode 100644 index 00000000..bd547642 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Card.java @@ -0,0 +1,43 @@ +package blackjack.domain.card; + +import java.util.Objects; + +public class Card { + + private final Pattern pattern; + private final Rank rank; + + public Card(final Pattern pattern, final Rank rank) { + this.pattern = pattern; + this.rank = rank; + } + + public boolean isAce() { + return rank == Rank.ACE; + } + + public String getName() { + return rank.getName() + pattern.getName(); + } + + public int getRank() { + return rank.getValue(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Card card = (Card) o; + return pattern == card.pattern && rank == card.rank; + } + + @Override + public int hashCode() { + return Objects.hash(pattern, rank); + } +} diff --git a/src/main/java/blackjack/domain/card/Cards.java b/src/main/java/blackjack/domain/card/Cards.java new file mode 100644 index 00000000..c5097997 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Cards.java @@ -0,0 +1,45 @@ +package blackjack.domain.card; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class Cards { + + private static final String EMPTY_DECK_EXCEPTION_MESSAGE = "[ERROR] ๋ฑ์— ๋‚จ์€ ์นด๋“œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."; + private static final int SELECTED_CARD = 0; + + private final List cards; + + public Cards() { + this(Arrays.stream(Pattern.values()) + .flatMap(pattern -> + Arrays.stream(Rank.values()) + .map(rank -> new Card(pattern, rank)) + .collect(Collectors.toList()).stream()) + .collect(Collectors.toList())); + } + + private Cards(final List cards) { + this.cards = new ArrayList<>(cards); + } + + public Card draw() { + checkDeckIsEmpty(); + Collections.shuffle(cards); + return cards.get(SELECTED_CARD); + } + + private void checkDeckIsEmpty() { + if (cards.isEmpty()) { + throw new IllegalArgumentException(EMPTY_DECK_EXCEPTION_MESSAGE); + } + } + + public Cards remove(final Card card) { + cards.remove(card); + return new Cards(cards); + } +} diff --git a/src/main/java/blackjack/domain/card/Deck.java b/src/main/java/blackjack/domain/card/Deck.java new file mode 100644 index 00000000..fe96b7f5 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Deck.java @@ -0,0 +1,28 @@ +package blackjack.domain.card; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class Deck { + + private static final int NUMBER_OF_INIT_HANDS = 2; + + private final Cards cards; + + public Deck() { + this.cards = new Cards(); + } + + public Card draw() { + final Card card = cards.draw(); + cards.remove(card); + return card; + } + + public List dealCards() { + return IntStream.range(0, NUMBER_OF_INIT_HANDS) + .mapToObj(i -> draw()) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/blackjack/domain/card/Hands.java b/src/main/java/blackjack/domain/card/Hands.java new file mode 100644 index 00000000..8dec7f04 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Hands.java @@ -0,0 +1,57 @@ +package blackjack.domain.card; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class Hands { + + private static final int DECISION_VALUE = 10; + private static final int ACE_VALUE_1 = 1; + private static final int ACE_VALUE_11 = 11; + + private final List cards; + private Score score; + + public Hands(final List cards) { + this.cards = new ArrayList<>(); + this.score = new Score(); + + initAllField(cards); + } + + private void initAllField(final List cards) { + cards.forEach(this::add); + } + + public void add(final Card card) { + this.cards.add(card); + this.score = this.score.sum(getRank(card)); + } + + private int getRank(final Card card) { + if (card.isAce()) { + return getAceValue(); + } + return card.getRank(); + } + + private int getAceValue() { + if (this.score.getValue() > DECISION_VALUE) { + return ACE_VALUE_1; + } + return ACE_VALUE_11; + } + + public boolean isUnderScore(final int value) { + return score.isUnder(value); + } + + public int getScore() { + return score.getValue(); + } + + public List getCardNames() { + return cards.stream().map(Card::getName).collect(Collectors.toList()); + } +} diff --git a/src/main/java/blackjack/domain/card/Pattern.java b/src/main/java/blackjack/domain/card/Pattern.java new file mode 100644 index 00000000..563719f0 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Pattern.java @@ -0,0 +1,19 @@ +package blackjack.domain.card; + +public enum Pattern { + + SPADE("์ŠคํŽ˜์ด๋“œ"), + CLUB("ํด๋กœ๋ฒ„"), + HEART("ํ•˜ํŠธ"), + DIAMOND("๋‹ค์ด์•„๋ชฌ๋“œ"); + + private final String name; + + Pattern(final String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/blackjack/domain/card/Rank.java b/src/main/java/blackjack/domain/card/Rank.java new file mode 100644 index 00000000..f11a8355 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Rank.java @@ -0,0 +1,24 @@ +package blackjack.domain.card; + +public enum Rank { + + ACE("A", 11), TWO("2", 2), THREE("3", 3), FOUR("4", 4), FIVE("5", 5), + SIX("6", 6), SEVEN("7", 7), EIGHT("8", 8), NINE("9", 9), TEN("10", 10), + JACK("J", 10), QUEEN("Q", 10), KING("K", 10); + + private final String name; + private final int value; + + Rank(final String name, final int value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public int getValue() { + return value; + } +} diff --git a/src/main/java/blackjack/domain/card/Score.java b/src/main/java/blackjack/domain/card/Score.java new file mode 100644 index 00000000..b8a7c100 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Score.java @@ -0,0 +1,28 @@ +package blackjack.domain.card; + +public class Score { + + private static final int INIT_VALUE = 0; + + private final int value; + + public Score() { + this(INIT_VALUE); + } + + public Score(final int value) { + this.value = value; + } + + public Score sum(final int value) { + return new Score(this.value + value); + } + + public int getValue() { + return value; + } + + public boolean isUnder(final int value) { + return this.value < value; + } +} diff --git a/src/main/java/blackjack/domain/judge/Judge.java b/src/main/java/blackjack/domain/judge/Judge.java new file mode 100644 index 00000000..858823a5 --- /dev/null +++ b/src/main/java/blackjack/domain/judge/Judge.java @@ -0,0 +1,50 @@ +package blackjack.domain.judge; + +import blackjack.domain.participant.Participant; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class Judge { + + private static final int LIMIT_SCORE = 21; + private static final String PLAYER_WIN = "์Šน"; + private static final String PLAYER_LOSE = "ํŒจ"; + private static final String DEALER_WIN_OR_LOSE_FORMAT = "%d์Šน %dํŒจ"; + + public Map getWinOrLose(final Participant dealer, final List players) { + final Map winOrLose = new LinkedHashMap<>(); + + players.forEach(player -> winOrLose.put( + player.getName(), + getWinOrLosePerPlayer(player.getScore(), dealer.getScore()))); + + final int dealerWinCount = getDealerWinCount(winOrLose); + winOrLose.put(dealer.getName(), + String.format(DEALER_WIN_OR_LOSE_FORMAT, + dealerWinCount, getDealerLoseCount(winOrLose, dealerWinCount))); + + return winOrLose; + } + + private String getWinOrLosePerPlayer(final int playerScore, final int dealerScore) { + if (playerScore <= LIMIT_SCORE && dealerScore > LIMIT_SCORE) { + return PLAYER_WIN; + } + if (playerScore > LIMIT_SCORE || playerScore <= dealerScore) { + return PLAYER_LOSE; + } + + return PLAYER_WIN; + } + + private int getDealerWinCount(Map result) { + return (int) result.values().stream() + .filter(v -> v.equals(PLAYER_LOSE)) + .count(); + } + + private int getDealerLoseCount(Map winOrLose, int dealerWinCount) { + return winOrLose.size() - dealerWinCount; + } +} diff --git a/src/main/java/blackjack/domain/participant/Dealer.java b/src/main/java/blackjack/domain/participant/Dealer.java new file mode 100644 index 00000000..73df0eca --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Dealer.java @@ -0,0 +1,25 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Hands; +import java.util.List; + +public class Dealer extends Participant { + + private static final String DEALER = "๋”œ๋Ÿฌ"; + private static final int DRAWABLE_SCORE_LIMIT = 17; + + public Dealer(final List cards) { + super(new Hands(cards)); + } + + @Override + public boolean canDraw() { + return hands.isUnderScore(DRAWABLE_SCORE_LIMIT); + } + + @Override + public String getName() { + return DEALER; + } +} diff --git a/src/main/java/blackjack/domain/participant/Participant.java b/src/main/java/blackjack/domain/participant/Participant.java new file mode 100644 index 00000000..7d6e1d65 --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Participant.java @@ -0,0 +1,30 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Hands; +import java.util.List; + +public abstract class Participant { + + protected final Hands hands; + + Participant(final Hands hands) { + this.hands = hands; + } + + abstract public boolean canDraw(); + + abstract public String getName(); + + public int getScore() { + return hands.getScore(); + } + + public List getCardNames() { + return hands.getCardNames(); + } + + public void addCard(final Card card) { + hands.add(card); + } +} diff --git a/src/main/java/blackjack/domain/participant/Player.java b/src/main/java/blackjack/domain/participant/Player.java new file mode 100644 index 00000000..64416a4f --- /dev/null +++ b/src/main/java/blackjack/domain/participant/Player.java @@ -0,0 +1,27 @@ +package blackjack.domain.participant; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Hands; +import java.util.List; + +public class Player extends Participant { + + private static final int DRAWABLE_SCORE_LIMIT = 21; + + private final String name; + + public Player(final String name, List cards) { + super(new Hands(cards)); + this.name = name; + } + + @Override + public boolean canDraw() { + return hands.isUnderScore(DRAWABLE_SCORE_LIMIT); + } + + @Override + public String getName() { + return name; + } +} diff --git a/src/main/java/blackjack/util/Parser.java b/src/main/java/blackjack/util/Parser.java new file mode 100644 index 00000000..ee9af0d5 --- /dev/null +++ b/src/main/java/blackjack/util/Parser.java @@ -0,0 +1,30 @@ +package blackjack.util; + +import java.util.Arrays; +import java.util.List; +import java.util.function.BooleanSupplier; +import java.util.stream.Collectors; + +public class Parser { + + private static final String NAME_SPLIT_REGEX = ","; + private static final String EMPTY_NAME_EXCEPTION_MESSAGE = "[ERROR] ํ”Œ๋ ˆ์ด์–ด์˜ ์ด๋ฆ„์„ ๋นˆ ๊ฐ’์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."; + + public static List parse(final String input) { + checkNameIsEmpty(() -> input.endsWith(NAME_SPLIT_REGEX)); + + return Arrays.stream(input.split(NAME_SPLIT_REGEX)) + .map(name -> { + final String trimName = name.trim(); + checkNameIsEmpty(trimName::isEmpty); + return trimName; + }) + .collect(Collectors.toList()); + } + + private static void checkNameIsEmpty(final BooleanSupplier condition) { + if (condition.getAsBoolean()) { + throw new IllegalArgumentException(EMPTY_NAME_EXCEPTION_MESSAGE); + } + } +} diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java new file mode 100644 index 00000000..ad8c06f9 --- /dev/null +++ b/src/main/java/blackjack/view/InputView.java @@ -0,0 +1,36 @@ +package blackjack.view; + +import java.util.Scanner; + +public class InputView { + + private static final String PLAYERS_INPUT_MESSAGE = "๊ฒŒ์ž„์— ์ฐธ์—ฌํ•  ์‚ฌ๋žŒ์˜ ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”.(์‰ผํ‘œ ๊ธฐ์ค€์œผ๋กœ ๋ถ„๋ฆฌ)"; + private static final String DRAW_FLAG_INPUT_MESSAGE_FORMAT = "%s. ํ•œ์žฅ์˜ ์นด๋“œ๋ฅผ ๋” ๋ฐ›์œผ์‹ค๊ฑด๊ฐ€์š”?(์˜ˆ๋Š” y, ์•„๋‹ˆ์˜ค๋Š” n)%n"; + private static final String DRAW_FLAG_REGEX = "[ynYN]"; + private static final String INVALID_DRAW_FLAG_EXCEPTION_MESSAGE = "[ERROR] y ๋˜๋Š” n๋งŒ ์ž…๋ ฅ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค"; + + private InputView() {} + + public static String inputPlayerNames() { + System.out.println(PLAYERS_INPUT_MESSAGE); + return input(); + } + + public static String inputDrawFlag(final String name) { + System.out.printf(DRAW_FLAG_INPUT_MESSAGE_FORMAT, name); + final String input = input(); + validateDrawFlag(input); + return input; + } + + private static void validateDrawFlag(final String input) { + if (!input.matches(DRAW_FLAG_REGEX)) { + throw new IllegalArgumentException(INVALID_DRAW_FLAG_EXCEPTION_MESSAGE); + } + } + + private static String input() { + Scanner scanner = new Scanner(System.in); + return scanner.nextLine().trim(); + } +} diff --git a/src/main/java/blackjack/view/OutputView.java b/src/main/java/blackjack/view/OutputView.java new file mode 100644 index 00000000..18ec7990 --- /dev/null +++ b/src/main/java/blackjack/view/OutputView.java @@ -0,0 +1,91 @@ +package blackjack.view; + +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Participant; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class OutputView { + + private static final String DEAL_MESSAGE_FORMAT = "%s์—๊ฒŒ ์นด๋“œ๋ฅผ %d์žฅ์”ฉ ๋ถ„๋ฐฐํ•˜์˜€์Šต๋‹ˆ๋‹ค.%n"; + private static final int NUMBER_OF_INIT_HANDS = 2; + private static final String DELIMITER = ", "; + private static final String PARTICIPANT_STATUS_FORMAT = "%s์นด๋“œ: %s%n"; + private static final int FIRST_CARD = 0; + private static final String PARTICIPANT_FINAL_STATUS_FORMAT = "%s์นด๋“œ: %s - ๊ฒฐ๊ณผ: %d%n"; + private static final String WINNING_OR_LOSE_HEADER_MESSAGE = "## ์ตœ์ข… ์ŠนํŒจ\n"; + private static final String WINNING_OR_LOSE_FORMAT = "%s: %s%n"; + private static final String DEALER_DRAW_MESSAGE = "๋”œ๋Ÿฌ๋Š” 16์ดํ•˜๋ผ ํ•œ์žฅ์˜ ์นด๋“œ๋ฅผ ๋” ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.\n"; + + private OutputView() {} + + public static void printStartStatus(final List participants) { + printDealResult(participants); + printParticipantsStatus(participants); + } + + private static void printDealResult(final List participants) { + System.out.printf(DEAL_MESSAGE_FORMAT, getNames(participants), NUMBER_OF_INIT_HANDS); + } + + private static String getNames(final List participants) { + return participants.stream() + .map(Participant::getName) + .collect(Collectors.joining(DELIMITER)); + } + + public static void printParticipantsStatus(final List participants) { + StringBuilder status = new StringBuilder(); + participants.forEach(participant -> + status.append(String.format( + PARTICIPANT_STATUS_FORMAT, participant.getName(), getStartCardNames(participant)))); + + System.out.println(status); + } + + private static String getStartCardNames(final Participant participant) { + if (participant instanceof Dealer) { + return getOneCard(participant); + } + return getCardNames(participant); + } + + private static String getOneCard(Participant participant) { + return participant.getCardNames().get(FIRST_CARD); + } + + private static String getCardNames(final Participant participant) { + return participant.getCardNames().stream() + .collect(Collectors.joining(DELIMITER)); + } + + public static void printGameResult(List participants, Map result) { + printFinalStatus(participants); + printWinOrLose(result); + } + + private static void printFinalStatus(List participants) { + StringBuilder status = new StringBuilder(); + participants.forEach(participant -> + status.append(String.format( + PARTICIPANT_FINAL_STATUS_FORMAT, participant.getName(), getCardNames(participant), + participant.getScore()))); + + System.out.println(status); + } + + private static void printWinOrLose(Map results) { + StringBuilder winOrLose = new StringBuilder(WINNING_OR_LOSE_HEADER_MESSAGE); + + results.forEach((name, result) -> + winOrLose.append(String.format( + WINNING_OR_LOSE_FORMAT, name, result))); + + System.out.println(winOrLose); + } + + public static void printDealerDrawMessage() { + System.out.println(DEALER_DRAW_MESSAGE); + } +} diff --git a/src/main/java/empty.txt b/src/main/java/empty.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/oilinjection/OilInjectionApplication.java b/src/main/java/oilinjection/OilInjectionApplication.java new file mode 100644 index 00000000..ed5d9989 --- /dev/null +++ b/src/main/java/oilinjection/OilInjectionApplication.java @@ -0,0 +1,19 @@ +package oilinjection; + +import java.util.List; +import oilinjection.domain.RentCompany; +import oilinjection.domain.vo.RentInfo; +import oilinjection.util.Parser; +import oilinjection.view.InputView; +import oilinjection.view.ResultView; + +public class OilInjectionApplication { + + public static void main(String[] args) { + final List rentInfos = Parser.parse(InputView.inputReservationInfo()); + + final RentCompany rentCompany = new RentCompany(); + rentCompany.acceptReservation(rentInfos); + ResultView.print(rentCompany.createReport()); + } +} diff --git a/src/main/java/oilinjection/domain/RentCar.java b/src/main/java/oilinjection/domain/RentCar.java new file mode 100644 index 00000000..7d491c11 --- /dev/null +++ b/src/main/java/oilinjection/domain/RentCar.java @@ -0,0 +1,52 @@ +package oilinjection.domain; + +import java.util.Arrays; +import java.util.function.Function; +import oilinjection.domain.car.Avante; +import oilinjection.domain.car.Car; +import oilinjection.domain.car.K5; +import oilinjection.domain.car.Sonata; + +public enum RentCar { + AVANTE("AVANTE", 1, Avante::new), + SONATA("SONATA", 2, Sonata::new), + K5("K5", 2, K5::new); + + private static final double MIN_TRIP_DISTANCE = 1D; + private static final String UNDER_MIN_TRIP_DISTANCE_EXCEPTION_MESSAGE = "[ERROR] ํ•ด๋‹นํ•˜๋Š” ์ฐจ์ข…์ด ์—†์Šต๋‹ˆ๋‹ค."; + private static final String NOT_FOUND_NAME_EXCEPTION_MESSAGE = "[ERROR] ํ•ด๋‹นํ•˜๋Š” ์ฐจ์ข…์ด ์—†์Šต๋‹ˆ๋‹ค."; + + private final String type; + private final int quantity; + private final Function createCar; + + RentCar(final String type, final int quantity, final Function createCar) { + this.type = type; + this.quantity = quantity; + this.createCar = createCar; + } + + public static Car rent(final String type, final double tripDistance) { + validateOverMinTripDistance(tripDistance); + + return Arrays.stream(values()) + .filter(car -> car.type.equals(type.toUpperCase())) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(NOT_FOUND_NAME_EXCEPTION_MESSAGE)) + .createCar(tripDistance); + } + + private static void validateOverMinTripDistance(final double tripDistance) { + if (tripDistance < MIN_TRIP_DISTANCE) { + throw new IllegalArgumentException(UNDER_MIN_TRIP_DISTANCE_EXCEPTION_MESSAGE); + } + } + + private Car createCar(final double tripDistance) { + return this.createCar.apply(tripDistance); + } + + public boolean isImpossibleReservation(final int count) { + return count > this.quantity; + } +} diff --git a/src/main/java/oilinjection/domain/RentCompany.java b/src/main/java/oilinjection/domain/RentCompany.java new file mode 100644 index 00000000..2b5b34b0 --- /dev/null +++ b/src/main/java/oilinjection/domain/RentCompany.java @@ -0,0 +1,59 @@ +package oilinjection.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import oilinjection.domain.car.Car; +import oilinjection.domain.vo.RentInfo; + +public class RentCompany { + + private static final String QUANTITY_OVER_EXCEPTION_MESSAGE_FORMAT = "[ERROR] %s์˜ ์˜ˆ์•ฝ ๊ฐ€๋Šฅํ•œ ์ฐจ๋Ÿ‰์ด ์—†์Šต๋‹ˆ๋‹ค."; + private static final String REPORT_FORMAT = "%s : %.0f๋ฆฌํ„ฐ%n"; + + private final List rents; + + public RentCompany() { + this.rents = new ArrayList<>(); + } + + public void acceptReservation(final List rentInfos) { + validateIsPossibleReservation(rentInfos); + + final List rentCars = rentInfos.stream() + .map(this::rent) + .collect(Collectors.toList()); + + rents.addAll(Collections.unmodifiableList(rentCars)); + } + + private Car rent(RentInfo rentInfo) { + return RentCar.rent(rentInfo.getType(), rentInfo.getTripDistance()); + } + + private void validateIsPossibleReservation(final List rentInfos) { + rentInfos.stream() + .collect(Collectors.groupingBy(RentInfo::getType, Collectors.summingInt(x -> 1))) + .forEach(this::validatePerCarType); + } + + private void validatePerCarType(final String type, final int count) { + if (findRentCar(type).isImpossibleReservation(count)) { + throw new IllegalArgumentException( + String.format(QUANTITY_OVER_EXCEPTION_MESSAGE_FORMAT, type)); + } + } + + private RentCar findRentCar(final String type) { + return RentCar.valueOf(type.toUpperCase()); + } + + public String createReport() { + StringBuilder report = new StringBuilder(); + rents.forEach(car -> report.append( + String.format(REPORT_FORMAT, car.getType(), car.getChargeQuantity()))); + + return report.toString(); + } +} diff --git a/src/main/java/oilinjection/domain/car/Avante.java b/src/main/java/oilinjection/domain/car/Avante.java new file mode 100644 index 00000000..c812ef46 --- /dev/null +++ b/src/main/java/oilinjection/domain/car/Avante.java @@ -0,0 +1,21 @@ +package oilinjection.domain.car; + +public class Avante extends Car { + + private static final String CAR_TYPE = "Avante"; + private static final double AVANTE_DISTANCE_PER_LITER = 15D; + + public Avante(final double tripDistance) { + super(tripDistance); + } + + @Override + double getDistancePerLiter() { + return AVANTE_DISTANCE_PER_LITER; + } + + @Override + public String getType() { + return CAR_TYPE; + } +} diff --git a/src/main/java/oilinjection/domain/car/Car.java b/src/main/java/oilinjection/domain/car/Car.java new file mode 100644 index 00000000..a23e5baa --- /dev/null +++ b/src/main/java/oilinjection/domain/car/Car.java @@ -0,0 +1,41 @@ +package oilinjection.domain.car; + +import java.util.Objects; + +public abstract class Car { + + final double tripDistance; + + Car(final double tripDistance) { + this.tripDistance = tripDistance; + } + + abstract double getDistancePerLiter(); + + public abstract String getType(); + + double getTripDistance() { + return this.tripDistance; + } + + public double getChargeQuantity() { + return Math.ceil(getTripDistance() / getDistancePerLiter()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Car car = (Car) o; + return Double.compare(car.tripDistance, tripDistance) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(tripDistance); + } +} \ No newline at end of file diff --git a/src/main/java/oilinjection/domain/car/K5.java b/src/main/java/oilinjection/domain/car/K5.java new file mode 100644 index 00000000..1e0ab90f --- /dev/null +++ b/src/main/java/oilinjection/domain/car/K5.java @@ -0,0 +1,21 @@ +package oilinjection.domain.car; + +public class K5 extends Car { + + private static final String CAR_TYPE = "K5"; + private static final double K5_DISTANCE_PER_LITER = 13D; + + public K5(final double tripDistance) { + super(tripDistance); + } + + @Override + double getDistancePerLiter() { + return K5_DISTANCE_PER_LITER; + } + + @Override + public String getType() { + return CAR_TYPE; + } +} diff --git a/src/main/java/oilinjection/domain/car/Sonata.java b/src/main/java/oilinjection/domain/car/Sonata.java new file mode 100644 index 00000000..e08a411e --- /dev/null +++ b/src/main/java/oilinjection/domain/car/Sonata.java @@ -0,0 +1,21 @@ +package oilinjection.domain.car; + +public class Sonata extends Car { + + private static final String CAR_TYPE = "Sonata"; + private static final double SONATA_DISTANCE_PER_LITER = 10D; + + public Sonata(final double tripDistance) { + super(tripDistance); + } + + @Override + double getDistancePerLiter() { + return SONATA_DISTANCE_PER_LITER; + } + + @Override + public String getType() { + return CAR_TYPE; + } +} diff --git a/src/main/java/oilinjection/domain/vo/RentInfo.java b/src/main/java/oilinjection/domain/vo/RentInfo.java new file mode 100644 index 00000000..6aa69431 --- /dev/null +++ b/src/main/java/oilinjection/domain/vo/RentInfo.java @@ -0,0 +1,40 @@ +package oilinjection.domain.vo; + +import java.util.Objects; + +public class RentInfo { + + private final String type; + private final double tripDistance; + + public RentInfo(final String type, final double tripDistance) { + this.type = type; + this.tripDistance = tripDistance; + } + + public String getType() { + return type; + } + + public double getTripDistance() { + return tripDistance; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RentInfo rent = (RentInfo) o; + return Double.compare(rent.tripDistance, tripDistance) == 0 + && Objects.equals(type, rent.type); + } + + @Override + public int hashCode() { + return Objects.hash(type, tripDistance); + } +} diff --git a/src/main/java/oilinjection/util/Parser.java b/src/main/java/oilinjection/util/Parser.java new file mode 100644 index 00000000..5829ea8a --- /dev/null +++ b/src/main/java/oilinjection/util/Parser.java @@ -0,0 +1,41 @@ +package oilinjection.util; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import oilinjection.domain.vo.RentInfo; + +public class Parser { + + private static final String RESERVATION_DELIMITER = ","; + private static final String INFO_DELIMITER = ":"; + private static final int TYPE = 0; + private static final int TRIP_DISTANCE = 1; + private static final String INPUT_FORMAT_REGEX = "[a-zA-z0-9]+:[0-9]+"; + private static final String INVALID_FORMAT_EXCEPTION_MESSAGE = "[ERROR] ์ž…๋ ฅ ํ˜•์‹์ด ๋งž์ง€ ์•Š์Šต๋‹ˆ๋‹ค(eg.${carType}:${tripDistance})"; + + public static List parse(final String input) { + + final String[] rentInfos = input.split(RESERVATION_DELIMITER); + + return Collections.unmodifiableList( + Arrays.stream(rentInfos) + .map(rentInfo -> { + validateInfoFormat(rentInfo); + return createRentInfo(rentInfo); + }) + .collect(Collectors.toList())); + } + + private static void validateInfoFormat(final String rent) { + if (!rent.matches(INPUT_FORMAT_REGEX)) { + throw new IllegalArgumentException(INVALID_FORMAT_EXCEPTION_MESSAGE); + } + } + + private static RentInfo createRentInfo(final String rentInfo) { + final String[] split = rentInfo.split(INFO_DELIMITER); + return new RentInfo(split[TYPE], Double.parseDouble(split[TRIP_DISTANCE])); + } +} diff --git a/src/main/java/oilinjection/view/InputView.java b/src/main/java/oilinjection/view/InputView.java new file mode 100644 index 00000000..7108845f --- /dev/null +++ b/src/main/java/oilinjection/view/InputView.java @@ -0,0 +1,26 @@ +package oilinjection.view; + +import java.util.Scanner; + +public class InputView { + + private static final Scanner SCANNER = new Scanner(System.in); + private static final String RESERVATION_INFO_INPUT_MESSAGE = "๋Œ€์—ฌํ•  ์ž๋™์ฐจ์™€ ์˜ˆ์ƒ ๊ฑฐ๋ฆฌ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”. (eg. carType1:distance, carType2:distance)"; + private static final String ALL_SPACE = "\\s+"; + private static final String EMPTY_STRING = ""; + + private InputView() {} + + public static String inputReservationInfo() { + System.out.println(RESERVATION_INFO_INPUT_MESSAGE); + return input(); + } + + private static String input() { + return removeSpace(SCANNER.nextLine()); + } + + private static String removeSpace(final String input) { + return input.replaceAll(ALL_SPACE, EMPTY_STRING); + } +} diff --git a/src/main/java/oilinjection/view/ResultView.java b/src/main/java/oilinjection/view/ResultView.java new file mode 100644 index 00000000..b83268be --- /dev/null +++ b/src/main/java/oilinjection/view/ResultView.java @@ -0,0 +1,8 @@ +package oilinjection.view; + +public class ResultView { + + public static void print(final String report) { + System.out.print(report); + } +} diff --git a/src/test/java/blackjack/domain/card/CardTest.java b/src/test/java/blackjack/domain/card/CardTest.java new file mode 100644 index 00000000..d4062440 --- /dev/null +++ b/src/test/java/blackjack/domain/card/CardTest.java @@ -0,0 +1,27 @@ +package blackjack.domain.card; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CardTest { + + @DisplayName("์นด๋“œ ์ด๋ฆ„์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.") + @Test + void When์นด๋“œ์ด๋ฆ„_์–ป๊ธฐ_Then์ด๋ฆ„_๋ฐ˜ํ™˜() { + assertThat(new Card(Pattern.SPADE, Rank.SEVEN).getName()).isEqualTo("7์ŠคํŽ˜์ด๋“œ"); + } + + @DisplayName("ACE์ด๋ฉด true ๋ฐ˜ํ™˜ํ•œ๋‹ค.") + @Test + void Given์—์ด์Šค_WhenํŒ๋‹จ_Then์ฐธ() { + assertThat(new Card(Pattern.SPADE, Rank.ACE).isAce()).isTrue(); + } + + @DisplayName("ACE๊ฐ€ ์•„๋‹ˆ๋ฉด false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.") + @Test + void Given์—์ด์Šค_์ด์™ธ์˜_์นด๋“œ_WhenํŒ๋‹จ_Then๊ฑฐ์ง“() { + assertThat(new Card(Pattern.SPADE, Rank.KING).isAce()).isFalse(); + } +} diff --git a/src/test/java/blackjack/domain/card/CardsTest.java b/src/test/java/blackjack/domain/card/CardsTest.java new file mode 100644 index 00000000..ae1eba7a --- /dev/null +++ b/src/test/java/blackjack/domain/card/CardsTest.java @@ -0,0 +1,68 @@ +package blackjack.domain.card; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CardsTest { + + private Cards sut; + + @BeforeEach + void before() { + sut = new Cards(); + } + + @DisplayName("์นด๋“œ ํ•œ์žฅ์„ ๋ฝ‘๋Š”๋‹ค.") + @Test + void GivenNothing_When์นด๋“œ_๋ฝ‘๊ธฐ_Then์นด๋“œ() { + // When + final Card card = sut.draw(); + + // Then + assertThat(card).isNotNull(); + } + + @DisplayName("์นด๋“œ๋ฅผ ๋ฝ‘์„๋•Œ ๋‚จ์€ ์นด๋“œ๊ฐ€ ์—†์œผ๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.") + @Test + void Given๋นˆ_๋ฑ_When์นด๋“œ_๋ฝ‘๊ธฐ_Then์˜ˆ์™ธ_๋ฐœ์ƒ() { + // Given + final List cards = Arrays.stream(Pattern.values()) + .flatMap(pattern -> + Arrays.stream(Rank.values()) + .map(rank -> new Card(pattern, rank)) + .collect(Collectors.toList()).stream()) + .collect(Collectors.toList()); + + for (Card card : cards) { + sut = sut.remove(card); + } + + // When & Then + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> sut.draw()); + } + + @DisplayName("์นด๋“œ๋ฅผ ์ „๋ถ€ ๋ฝ‘์•„ ๋ฑ์ด ์ œ๋Œ€๋กœ ์ƒ์„ฑ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.") + @Test + void GivenNothing_When์นด๋“œ_์ „๋ถ€_๋ฝ‘๊ธฐ_Then์ค‘๋ณต๋˜์ง€_์•Š์€_๋ชจ๋“ _์นด๋“œ() { + // When + final Set cards = IntStream.range(0, 52).mapToObj(i -> { + Card card = sut.draw(); + sut.remove(card); + return card; + }) + .collect(Collectors.toSet()); + + // Then + assertThat(cards.size()).isEqualTo(52); + } +} diff --git a/src/test/java/blackjack/domain/card/DeckTest.java b/src/test/java/blackjack/domain/card/DeckTest.java new file mode 100644 index 00000000..41d54a86 --- /dev/null +++ b/src/test/java/blackjack/domain/card/DeckTest.java @@ -0,0 +1,48 @@ +package blackjack.domain.card; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class DeckTest { + + private Deck sut; + + @BeforeEach + void before() { + sut = new Deck(); + } + + @DisplayName("์นด๋“œ๋ฅผ ํ•œ์žฅ ๋ฝ‘๋Š”๋‹ค.") + @Test + void GivenNothing_When_Then() { + assertThat(sut.draw()).isNotNull(); + } + + @DisplayName("์ฐธ๊ฐ€์ž๋“ค์˜ ์ดˆ๊ธฐ ํŒจ๋ฅผ ๋ฝ‘๋Š”๋‹ค.") + @Test + void Given_When_Then() { + assertThat(sut.dealCards().size()).isEqualTo(2); + } + + @DisplayName("์ค‘๋ณต์—†์ด ๋ชจ๋“  ์นด๋“œ๊ฐ€ ๋ฝ‘ํžŒ๋‹ค.") + @Test + void GivenNothing_When์นด๋“œ_์ „๋ถ€_๋ฝ‘๊ธฐ_Then์ค‘๋ณต๋˜์ง€_์•Š์€_๋ชจ๋“ _์นด๋“œ() { + + // When + final Set cards = IntStream.range(0, 52) + .mapToObj(i -> { + Card card = sut.draw(); + return card; + }) + .collect(Collectors.toSet()); + + // Then + assertThat(cards.size()).isEqualTo(52); + } +} diff --git a/src/test/java/blackjack/domain/card/HandsTest.java b/src/test/java/blackjack/domain/card/HandsTest.java new file mode 100644 index 00000000..d99fcb02 --- /dev/null +++ b/src/test/java/blackjack/domain/card/HandsTest.java @@ -0,0 +1,76 @@ +package blackjack.domain.card; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class HandsTest { + + @DisplayName("์ฐธ๊ฐ€์ž ํŒจ์˜ ์นด๋“œ ๊ฐ’์„ ๋”ํ•ด ๋ฐ˜ํ™˜ํ•œ๋‹ค.") + @Test + void Given์นด๋“œ_2์žฅ_Whenํ•ฉ_๊ตฌํ•˜๊ธฐ_Thenํ•ฉ_๋ฐ˜ํ™˜() { + // Given + final Hands hands = new Hands(Arrays.asList( + new Card(Pattern.CLUB, Rank.TEN), + new Card(Pattern.SPADE, Rank.TEN))); + + // Then + assertThat(hands.getScore()).isEqualTo(20); + } + + @DisplayName("์ดˆ๊ธฐ์— ACE๋ฅผ ํ•œ์žฅ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ ACE๋ฅผ 11๋กœ ๊ณ„์‚ฐํ•œ๋‹ค.") + @Test + void GivenAce_ํ•œ_์žฅ_ํฌํ•จ๋œ_์นด๋“œ_2์žฅ_Whenํ•ฉ_๊ตฌํ•˜๊ธฐ_Thenํ•ฉ_๋ฐ˜ํ™˜() { + // Given + final Hands hands = new Hands(Arrays.asList( + new Card(Pattern.CLUB, Rank.ACE), + new Card(Pattern.SPADE, Rank.TEN))); + + // Then + assertThat(hands.getScore()).isEqualTo(21); + } + + @DisplayName("์ดˆ๊ธฐ์— ACE๋ฅผ 2์žฅ ๊ฐ–๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ, ํ•ฉ์„ 12๋กœ ๊ณ„์‚ฐํ•œ๋‹ค.") + @Test + void GivenAce_2์žฅ_Whenํ•ฉ_๊ตฌํ•˜๊ธฐ_Thenํ•ฉ_๋ฐ˜ํ™˜() { + // Given + final Hands hands = new Hands(Arrays.asList( + new Card(Pattern.CLUB, Rank.ACE), + new Card(Pattern.SPADE, Rank.ACE))); + + // Then + assertThat(hands.getScore()).isEqualTo(12); + } + + @DisplayName("์ฐธ๊ฐ€์ž์˜ ์นด๋“œ ํŒจ์— ACE๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ 11๋กœ ๋”ํ•ด๋„ 21์„ ๋„˜์ง€ ์•Š์œผ๋ฉด ACE๋ฅผ 11๋กœ ๊ณ„์‚ฐํ•œ๋‹ค.") + @Test + void GivenAce_Whenํ•ฉ_๊ตฌํ•˜๊ธฐ_Then11์„_๋”ํ•œ_์Šค์ฝ”์–ด() { + // Given + final Hands hands = new Hands(Arrays.asList( + new Card(Pattern.CLUB, Rank.TWO), + new Card(Pattern.SPADE, Rank.TWO))); + + // When + hands.add(new Card(Pattern.CLUB, Rank.ACE)); + + // Then + assertThat(hands.getScore()).isEqualTo(15); + } + + @DisplayName("์ฐธ๊ฐ€์ž์˜ ์นด๋“œ ํŒจ์— ACE๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ 11์„ ๋”ํ—€์„ ๋•Œ, 21์„ ๋„˜์œผ๋ฉด ACE๋ฅผ 1๋กœ ๊ณ„์‚ฐํ•œ๋‹ค.") + @Test + void GivenAce_Whenํ•ฉ_๊ตฌํ•˜๊ธฐ_Then1์„_๋”ํ•œ_์Šค์ฝ”์–ด() { + // Given + final Hands hands = new Hands(Arrays.asList( + new Card(Pattern.CLUB, Rank.TEN), + new Card(Pattern.SPADE, Rank.TEN))); + + // When + hands.add(new Card(Pattern.CLUB, Rank.ACE)); + + // Then + assertThat(hands.getScore()).isEqualTo(21); + } +} diff --git a/src/test/java/blackjack/domain/card/ScoreTest.java b/src/test/java/blackjack/domain/card/ScoreTest.java new file mode 100644 index 00000000..9d83989f --- /dev/null +++ b/src/test/java/blackjack/domain/card/ScoreTest.java @@ -0,0 +1,31 @@ +package blackjack.domain.card; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class ScoreTest { + + @DisplayName("ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๊ฐ’์ด ์—†์œผ๋ฉด ์ดˆ๊ธฐ Score๋กœ ์„ค์ •ํ•œ๋‹ค.") + @Test + void GivenNothing_When๊ฐ์ฒด_์ƒ์„ฑ_Then_์ดˆ๊ธฐ_๊ฐ’์„_๊ฐ€์ง„_๊ฐ์ฒด() { + assertThat(new Score().getValue()).isEqualTo(0); + } + + @DisplayName("๊ฐ’์„ ๋ฐ›์•„ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.") + @Test + void Given๊ฐ’_When๊ฐ์ฒด_์ƒ์„ฑ_Then๋„˜๊ฒจ๋ฐ›์€_๊ฐ’์„_๊ฐ€์ง„_๊ฐ์ฒด() { + assertThat(new Score(7).getValue()).isEqualTo(7); + } + + @DisplayName("๊ฐ’์„ ๋ฐ›์•„ ๊ธฐ์กด์˜ ๊ฐ’๊ณผ ๋”ํ•œ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.") + @Test + void Given๊ฐ’_When๊ฐ์ฒด_์ƒ์„ฑ_Then๊ธฐ์กด_๊ฐ’๊ณผ_๋“ค์–ด์˜จ_๊ฐ’์„_๊ฐ€์ง„_๊ฐ์ฒด() { + // Given + final Score score = new Score(1); + + final Score newScore = score.sum(7); + assertThat(newScore.getValue()).isEqualTo(8); + } +} diff --git a/src/test/java/blackjack/domain/judge/JudgeTest.java b/src/test/java/blackjack/domain/judge/JudgeTest.java new file mode 100644 index 00000000..dcbf565c --- /dev/null +++ b/src/test/java/blackjack/domain/judge/JudgeTest.java @@ -0,0 +1,101 @@ +package blackjack.domain.judge; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Pattern; +import blackjack.domain.card.Rank; +import blackjack.domain.participant.Dealer; +import blackjack.domain.participant.Player; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class JudgeTest { + + private final List score16 = Arrays.asList( + new Card(Pattern.CLUB, Rank.TEN), + new Card(Pattern.HEART, Rank.SIX)); + + private final List score21 = Arrays.asList( + new Card(Pattern.DIAMOND, Rank.ACE), + new Card(Pattern.HEART, Rank.TEN)); + + private final List score26 = Arrays.asList( + new Card(Pattern.DIAMOND, Rank.TEN), + new Card(Pattern.HEART, Rank.SIX), + new Card(Pattern.HEART, Rank.TEN)); + + private final Judge judge = new Judge(); + + private final Map playerWin = new HashMap() { + { + put("๋”œ๋Ÿฌ", "0์Šน 1ํŒจ"); + put("jason", "์Šน"); + } + }; + + private final Map dealerWin = new HashMap() { + { + put("๋”œ๋Ÿฌ", "1์Šน 0ํŒจ"); + put("jason", "ํŒจ"); + } + }; + + @DisplayName("ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๋”œ๋Ÿฌ๋ณด๋‹ค ์ ์ˆ˜๊ฐ€ ๋†’์œผ๋ฉด ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์Šน๋ฆฌํ•œ๋‹ค.") + @Test + void Givenํ”Œ๋ ˆ์ด์–ด_๋”œ๋Ÿฌ_์ ์ˆ˜_Whenํ”Œ๋ ˆ์ด์–ด๊ฐ€_๋†’์€_Thenํ”Œ๋ ˆ์ด์–ด_์Šน๋ฆฌ() { + final Dealer dealer = new Dealer(score16); + final Player player = new Player("jason", score21); + + assertThat(judge.getWinOrLose(dealer, Arrays.asList(player))).isEqualTo(playerWin); + } + + @DisplayName("ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๋”œ๋Ÿฌ๋ณด๋‹ค ์ ์ˆ˜๊ฐ€ ๋‚ฎ์œผ๋ฉด ๋”œ๋Ÿฌ๊ฐ€ ์Šน๋ฆฌํ•œ๋‹ค.") + @Test + void Givenํ”Œ๋ ˆ์ด์–ด_๋”œ๋Ÿฌ_์ ์ˆ˜_When๋”œ๋Ÿฌ๊ฐ€_๋†’์€_The๋”œ๋Ÿฌ_์Šน๋ฆฌ() { + final Dealer dealer = new Dealer(score21); + final Player player = new Player("jason", score16); + + assertThat(judge.getWinOrLose(dealer, Arrays.asList(player))).isEqualTo(dealerWin); + } + + @DisplayName("๋”œ๋Ÿฌ์™€ ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๋ชจ๋‘ 21์„ ๋„˜์—ˆ์„ ๋•Œ, ๋”œ๋Ÿฌ๊ฐ€ ์Šน๋ฆฌํ•œ๋‹ค.") + @Test + void Givenํ”Œ๋ ˆ์ด์–ด_๋”œ๋Ÿฌ_์ ์ˆ˜_When๋‘˜๋‹ค_21_์ดˆ๊ณผ_The๋”œ๋Ÿฌ_์Šน๋ฆฌ() { + final Dealer dealer = new Dealer(score26); + final Player player = new Player("jason", score26); + + assertThat(judge.getWinOrLose(dealer, Arrays.asList(player))).isEqualTo(dealerWin); + } + + @DisplayName("๋”œ๋Ÿฌ๋Š” 21์„ ๋„˜๊ณ , ํ”Œ๋ ˆ์ด์–ด๋Š” ๋„˜์ง€ ์•Š์•˜์œผ๋ฉด ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์Šน๋ฆฌํ•œ๋‹ค.") + @Test + void Givenํ”Œ๋ ˆ์ด์–ด_๋”œ๋Ÿฌ_์ ์ˆ˜When๋”œ๋Ÿฌ_21_์ดˆ๊ณผ_Theํ”Œ๋ ˆ์ด์–ด_์Šน๋ฆฌ() { + final Dealer dealer = new Dealer(score26); + final Player player = new Player("jason", score16); + + assertThat(judge.getWinOrLose(dealer, Arrays.asList(player))).isEqualTo(playerWin); + } + + @DisplayName("ํ”Œ๋ ˆ์ด์–ด๋Š” 21์„ ๋„˜๊ณ , ๋”œ๋Ÿฌ๋Š” ๋„˜์ง€ ์•Š์•˜์œผ๋ฉด ๋”œ๋Ÿฌ๊ฐ€ ์Šน๋ฆฌํ•œ๋‹ค.") + @Test + void Givenํ”Œ๋ ˆ์ด์–ด_๋”œ๋Ÿฌ_์ ์ˆ˜Whenํ”Œ๋ ˆ์ด์–ด_21_์ดˆ๊ณผ_The๋”œ๋Ÿฌ_์Šน๋ฆฌ() { + final Dealer dealer = new Dealer(score21); + final Player player = new Player("jason", score26); + + assertThat(judge.getWinOrLose(dealer, Arrays.asList(player))).isEqualTo(dealerWin); + } + + @DisplayName("ํ”Œ๋ ˆ์ด์–ด์™€ ๋”œ๋Ÿฌ์˜ ์ ์ˆ˜๊ฐ€ ๊ฐ™์œผ๋ฉด ๋”œ๋Ÿฌ๊ฐ€ ์Šน๋ฆฌํ•œ๋‹ค.") + @Test + void Givenํ”Œ๋ ˆ์ด์–ด_๋”œ๋Ÿฌ_์ ์ˆ˜When๊ฐ™๋‹ค_The๋”œ๋Ÿฌ_์Šน๋ฆฌ() { + final Dealer dealer = new Dealer(score21); + final Player player = new Player("jason", score21); + + assertThat(judge.getWinOrLose(dealer, Arrays.asList(player))).isEqualTo(dealerWin); + } +} diff --git a/src/test/java/blackjack/domain/participant/DealerTest.java b/src/test/java/blackjack/domain/participant/DealerTest.java new file mode 100644 index 00000000..8dbb33b6 --- /dev/null +++ b/src/test/java/blackjack/domain/participant/DealerTest.java @@ -0,0 +1,47 @@ +package blackjack.domain.participant; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Hands; +import blackjack.domain.card.Pattern; +import blackjack.domain.card.Rank; +import java.util.Arrays; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class DealerTest { + + @DisplayName("ํ”Œ๋ ˆ์ด์–ด์˜ ํ˜„์žฌ ์ ์ˆ˜๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.") + @Test + void Given์นด๋“œ๋“ค_When์ ์ˆ˜_์–ป๊ธฐ_Then์ ์ˆ˜_ํ•ฉ_๋ฐ˜ํ™˜() { + final Dealer dealer = new Dealer( + Arrays.asList( + new Card(Pattern.CLUB, Rank.EIGHT), + new Card(Pattern.SPADE, Rank.EIGHT))); + + assertThat(dealer.getScore()).isEqualTo(16); + } + + @DisplayName("์นด๋“œ๋ฅผ ํ•œ์žฅ ๋” ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.") + @Test + void Given์นด๋“œ๋“ค_When์ ์ˆ˜๊ฐ€_17๋ฏธ๋งŒ_Then์ฐธ_๋ฐ˜ํ™˜() { + final Dealer dealer = new Dealer( + Arrays.asList( + new Card(Pattern.CLUB, Rank.EIGHT), + new Card(Pattern.HEART, Rank.FIVE))); + + assertThat(dealer.canDraw()).isTrue(); + } + + @DisplayName("์นด๋“œ๋ฅผ ํ•œ์žฅ ๋” ๋ฐ›์„ ์ˆ˜ ์—†๋‹ค.") + @Test + void Given์นด๋“œ๋“ค_When์ ์ˆ˜๊ฐ€_17์ด์ƒ_Then๊ฑฐ์ง“_๋ฐ˜ํ™˜() { + final Dealer dealer = new Dealer( + Arrays.asList( + new Card(Pattern.CLUB, Rank.TEN), + new Card(Pattern.HEART, Rank.ACE))); + + assertThat(dealer.canDraw()).isFalse(); + } +} diff --git a/src/test/java/blackjack/domain/participant/PlayerTest.java b/src/test/java/blackjack/domain/participant/PlayerTest.java new file mode 100644 index 00000000..38db73be --- /dev/null +++ b/src/test/java/blackjack/domain/participant/PlayerTest.java @@ -0,0 +1,46 @@ +package blackjack.domain.participant; + +import static org.assertj.core.api.Assertions.assertThat; + +import blackjack.domain.card.Card; +import blackjack.domain.card.Pattern; +import blackjack.domain.card.Rank; +import java.util.Arrays; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PlayerTest { + + @DisplayName("ํ”Œ๋ ˆ์ด์–ด์˜ ํ˜„์žฌ ์ ์ˆ˜๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.") + @Test + void Given์นด๋“œ๋“ค_When์ ์ˆ˜_์–ป๊ธฐ_Then์ ์ˆ˜_ํ•ฉ_๋ฐ˜ํ™˜() { + final Player player = new Player("jason", + Arrays.asList( + new Card(Pattern.CLUB, Rank.EIGHT), + new Card(Pattern.SPADE, Rank.EIGHT))); + + assertThat(player.getScore()).isEqualTo(16); + } + + @DisplayName("์นด๋“œ๋ฅผ ํ•œ์žฅ ๋” ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.") + @Test + void Given์นด๋“œ๋“ค_When์ ์ˆ˜๊ฐ€_21๋ฏธ๋งŒ_Then์ฐธ_๋ฐ˜ํ™˜() { + final Player player = new Player("jason", + Arrays.asList( + new Card(Pattern.CLUB, Rank.EIGHT), + new Card(Pattern.HEART, Rank.FIVE))); + + assertThat(player.canDraw()).isTrue(); + } + + @DisplayName("์นด๋“œ๋ฅผ ํ•œ์žฅ ๋” ๋ฐ›์„ ์ˆ˜ ์—†๋‹ค.") + @Test + void Given์นด๋“œ๋“ค_When์ ์ˆ˜๊ฐ€_21์ด์ƒ_Then๊ฑฐ์ง“_๋ฐ˜ํ™˜() { + final Player player = new Player("jason", + Arrays.asList( + new Card(Pattern.CLUB, Rank.TEN), + new Card(Pattern.HEART, Rank.ACE))); + + assertThat(player.canDraw()).isFalse(); + } +} diff --git a/src/test/java/blackjack/util/ParserTest.java b/src/test/java/blackjack/util/ParserTest.java new file mode 100644 index 00000000..4f580b40 --- /dev/null +++ b/src/test/java/blackjack/util/ParserTest.java @@ -0,0 +1,29 @@ +package blackjack.util; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Arrays; +import java.util.List; +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; + +class ParserTest { + + @DisplayName("ํ”Œ๋ ˆ์ด์–ด์˜ ์ด๋ฆ„์„ ์ •์ƒ์ ์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.") + @Test + void Given์œ ํšจํ•œ๊ฐ’_WhenํŒŒ์‹ฑ_Then์ด๋ฆ„_๋ฐ˜ํ™˜() { + final List names = Parser.parse(" json, pobi "); + assertThat(names).isEqualTo(Arrays.asList("json","pobi")); + } + + @DisplayName("ํ”Œ๋ ˆ์ด์–ด์˜ ์ด๋ฆ„์ด ํ•˜๋‚˜๋ผ๋„ ๋นˆ ๊ฐ’์ด๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์นธ๋‹ค.") + @ValueSource(strings = {"kim, ,jun", "", ",", " , ", "kim,", ",kim", ",,kim", "kim,,", "kim, ,"}) + @ParameterizedTest + void Given๋นˆ_๊ฐ’_WhenํŒŒ์‹ฑ_Then์˜ˆ์™ธ_๋ฐœ์ƒ(String input) { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> Parser.parse(input)); + } +} diff --git a/src/test/java/empty.txt b/src/test/java/empty.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/src/test/java/oilinjection/domain/RentCarTest.java b/src/test/java/oilinjection/domain/RentCarTest.java new file mode 100644 index 00000000..9a3a7efa --- /dev/null +++ b/src/test/java/oilinjection/domain/RentCarTest.java @@ -0,0 +1,59 @@ +package oilinjection.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import oilinjection.domain.car.Avante; +import oilinjection.domain.car.Car; +import oilinjection.domain.car.K5; +import oilinjection.domain.car.Sonata; +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; + +public class RentCarTest { + + @DisplayName("Avante๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.") + @Test + void ์•„๋ฐ˜๋–ผ_์ƒ์„ฑ() { + final Car avante = RentCar.rent("avante", 200D); + assertThat(avante).isEqualTo(new Avante(200D)); + } + + @DisplayName("K5 ๋ฐ˜ํ™˜ํ•œ๋‹ค.") + @Test + void K5_์ƒ์„ฑ() { + final Car k5 = RentCar.rent("K5", 200D); + assertThat(k5).isEqualTo(new K5(200D)); + } + + @DisplayName("Sonata๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.") + @Test + void ์†Œ๋‚˜ํƒ€_์ƒ์„ฑ() { + final Car sonata = RentCar.rent("sonata", 200D); + assertThat(sonata).isEqualTo(new Sonata(200D)); + } + + @DisplayName("ํ•ด๋‹นํ•˜๋Š” ์ฐจ์ข…์ด ์—†์œผ๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.") + @Test + void ์ฐจ์ข…_์—†์Œ() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> RentCar.rent("none", 10D)); + } + + @DisplayName("์˜ˆ์•ฝ ํ•˜๋Š” ์—ฌํ–‰ ๊ฑฐ๋ฆฌ๊ฐ€ ์ตœ์†Œ ๊ฑฐ๋ฆฌ(1) ๋ณด๋‹ค ์ž‘์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.") + @Test + void ์ตœ์†Œ_๊ฑฐ๋ฆฌ๋ณด๋‹ค_์ž‘์Œ() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> RentCar.rent("avante", 0D)); + } + + @DisplayName("์ž๋™์ฐจ์˜ ์ฐจ์ข…์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š๋Š”๋‹ค.") + @ValueSource(strings = {"Sonata", "sOnAtA", "SONATA"}) + @ParameterizedTest + void ์†Œ๋‚˜ํƒ€_๋Œ€์†Œ๋ฌธ์ž_๊ตฌ๋ถ„์—†์ด_์ƒ์„ฑ(final String name) { + final Car sonata = RentCar.rent(name, 200D); + assertThat(sonata).isEqualTo(new Sonata(200D)); + } +} diff --git a/src/test/java/oilinjection/domain/RentCompanyTest.java b/src/test/java/oilinjection/domain/RentCompanyTest.java new file mode 100644 index 00000000..7a102708 --- /dev/null +++ b/src/test/java/oilinjection/domain/RentCompanyTest.java @@ -0,0 +1,64 @@ +package oilinjection.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import java.util.Arrays; +import java.util.List; +import oilinjection.domain.vo.RentInfo; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RentCompanyTest { + + private static final String NEWLINE = System.getProperty("line.separator"); + + @DisplayName("์ •์ƒ์ ์ธ ๋ ŒํŠธ์นด ์˜ˆ์•ฝ์„ ๋ฐ›๋Š”๋‹ค.") + @Test + void ์ •์ƒ์ ์ธ_๋ ŒํŠธ์นด_์˜ˆ์•ฝ() { + final List reservation = Arrays.asList( + new RentInfo("avante", 200D), + new RentInfo("K5", 100D)); + + final RentCompany rentCompany = new RentCompany(); + + assertDoesNotThrow(() -> rentCompany.acceptReservation(reservation)); + } + + @DisplayName("์†Œ์œ ํ•˜๊ณ  ์žˆ๋Š” ๋ ŒํŠธ์นด๋ณด๋‹ค ๋งŽ์€ ์ˆ˜๋ฅผ ์˜ˆ์•ฝํ•˜๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.") + @Test + void ์†Œ์œ ํ•œ_๋ ŒํŠธ์นด_์ˆ˜๋ณด๋‹ค_๋งŽ์€_์˜ˆ์•ฝ() { + final List reservation = Arrays.asList( + new RentInfo("avante", 200D), + new RentInfo("avante", 100D)); + + final RentCompany rentCompany = new RentCompany(); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> rentCompany.acceptReservation(reservation)); + } + + @DisplayName("์ฐจ๋Ÿ‰๋ณ„๋กœ ์ฃผ์ž…ํ•ด์•ผํ•  ์—ฐ๋ฃŒ๋Ÿ‰์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋ณด๊ณ ์„œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.") + @Test + void ๋ณด๊ณ ์„œ_์ƒ์„ฑ() { + final List reservation = Arrays.asList( + new RentInfo("Avante", 20D), + new RentInfo("Sonata", 300D), + new RentInfo("Sonata", 150D), + new RentInfo("K5", 130D), + new RentInfo("K5", 390D)); + + final RentCompany rentCompany = new RentCompany(); + rentCompany.acceptReservation(reservation); + String report = rentCompany.createReport(); + + assertThat(report).isEqualTo( + "Avante : 2๋ฆฌํ„ฐ" + NEWLINE + + "Sonata : 30๋ฆฌํ„ฐ" + NEWLINE + + "Sonata : 15๋ฆฌํ„ฐ" + NEWLINE + + "K5 : 10๋ฆฌํ„ฐ" + NEWLINE + + "K5 : 30๋ฆฌํ„ฐ" + NEWLINE + ); + } +} diff --git a/src/test/java/oilinjection/domain/car/AvanteTest.java b/src/test/java/oilinjection/domain/car/AvanteTest.java new file mode 100644 index 00000000..f5af99df --- /dev/null +++ b/src/test/java/oilinjection/domain/car/AvanteTest.java @@ -0,0 +1,16 @@ +package oilinjection.domain.car; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class AvanteTest { + + @DisplayName("์•„๋ฐ˜๋–ผ์˜ ์ฃผ์ž… ํ•ด์•ผํ•  ์—ฐ๋ฃŒ๋Ÿ‰์„ ๊ตฌํ•œ๋‹ค.") + @Test + void ์•„๋ฐ˜๋–ผ_ํ•„์š”ํ•œ_์—ฐ๋ฃŒ๋Ÿ‰() { + final Car avante = new Avante(300D); + assertThat(avante.getChargeQuantity()).isEqualTo(20D); + } +} diff --git a/src/test/java/oilinjection/domain/car/K5Test.java b/src/test/java/oilinjection/domain/car/K5Test.java new file mode 100644 index 00000000..427a01f6 --- /dev/null +++ b/src/test/java/oilinjection/domain/car/K5Test.java @@ -0,0 +1,16 @@ +package oilinjection.domain.car; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class K5Test { + + @DisplayName("K5์˜ ์ฃผ์ž…ํ•ด์•ผํ•  ์—ฐ๋ฃŒ๋Ÿ‰์„ ๊ตฌํ•œ๋‹ค.") + @Test + void K5_ํ•„์š”ํ•œ_์—ฐ๋ฃŒ๋Ÿ‰() { + final Car k5 = new K5(260D); + assertThat(k5.getChargeQuantity()).isEqualTo(20D); + } +} diff --git a/src/test/java/oilinjection/domain/car/SonataTest.java b/src/test/java/oilinjection/domain/car/SonataTest.java new file mode 100644 index 00000000..dffcd4a7 --- /dev/null +++ b/src/test/java/oilinjection/domain/car/SonataTest.java @@ -0,0 +1,16 @@ +package oilinjection.domain.car; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class SonataTest { + + @DisplayName("์†Œ๋‚˜ํƒ€์˜ ์ฃผ์ž…ํ•ด์•ผํ•  ์—ฐ๋ฃŒ๋Ÿ‰์„ ๊ตฌํ•œ๋‹ค.") + @Test + void ์†Œ๋‚˜ํƒ€_ํ•„์š”_์—ฐ๋ฃŒ๋Ÿ‰() { + final Car sonata = new Sonata(200D); + assertThat(sonata.getChargeQuantity()).isEqualTo(20D); + } +} \ No newline at end of file diff --git a/src/test/java/oilinjection/util/ParserTest.java b/src/test/java/oilinjection/util/ParserTest.java new file mode 100644 index 00000000..b1826534 --- /dev/null +++ b/src/test/java/oilinjection/util/ParserTest.java @@ -0,0 +1,63 @@ +package oilinjection.util; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.List; +import oilinjection.domain.vo.RentInfo; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +class ParserTest { + + @DisplayName("์˜ฌ๋ฐ”๋ฅธ ์ž…๋ ฅ์ด ๋“ค์–ด์˜ค๋ฉด, ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค.") + @Test + void ์ •์ƒ_์ž…๋ ฅ1() { + final String input = "car1:200"; + final RentInfo rent = new RentInfo("car1", 200); + assertThat(Parser.parse(input)).isEqualTo(Arrays.asList(rent)); + } + + @DisplayName("๋‘ ๊ฐœ ์ด์ƒ์˜ rent ์ •๋ณด๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ์ž…๋ ฅ ํ˜•์‹์œผ๋กœ ๋“ค์–ด์™€๋„ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค.") + @Test + void ์ •์ƒ_์ž…๋ ฅ2() { + final String input = "car1:200,car2:300"; + final List rents = Arrays.asList(new RentInfo("car1", 200), new RentInfo("car2", 300)); + assertThat(Parser.parse(input)).isEqualTo(rents); + } + + @DisplayName("์ž…๋ ฅ์ด ๋นˆ ๊ฐ’์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.") + @EmptySource + @ParameterizedTest + void ๋นˆ_์ž…๋ ฅ_์˜ˆ์™ธ_๋ฐœ์ƒ(final String input) { + Assertions.assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> Parser.parse(input)); + } + + @DisplayName("๋žœํŠธ ์ •๋ณด ๊ฐ’์ด ํ•˜๋‚˜๋กœ๋„ ๋นˆ ๊ฐ’์ด๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.") + @ValueSource(strings = {"car1:", ":200"}) + @ParameterizedTest + void ๋ ŒํŠธ_์ •๋ณด_์ค‘_ํ•˜๋‚˜์˜_๋นˆ_์ž…๋ ฅ_์˜ˆ์™ธ_๋ฐœ์ƒ(final String input) { + Assertions.assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> Parser.parse(input)); + } + + @DisplayName("๋žœํŠธ ์ •๋ณด ์ค‘ ๊ฑฐ๋ฆฌ ๊ฐ’์ด ์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.") + @Test + void ๊ฑฐ๋ฆฌ_๊ฐ’์ด_์ˆซ์ž๊ฐ€_์•„๋‹Œ_๊ฒฝ์šฐ_์˜ˆ์™ธ_๋ฐœ์ƒ() { + Assertions.assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> Parser.parse("car:aaa")); + } + + @DisplayName("์ž…๋ ฅ ํ˜•์‹์— ๋งž์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.") + @ValueSource(strings = {"car1 200", "car1,200", "card1200", "car::123"}) + @ParameterizedTest + void ์ž…๋ ฅ_ํ˜•์‹์—_๋งž์ง€_์•Š๋Š”_๊ฒฝ์šฐ_์˜ˆ์™ธ_๋ฐœ์ƒ(final String input) { + Assertions.assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> Parser.parse(input)); + } +}