diff --git a/src/main/java/blackjack/BlackJackApplication.java b/src/main/java/blackjack/BlackJackApplication.java new file mode 100644 index 00000000..ad039529 --- /dev/null +++ b/src/main/java/blackjack/BlackJackApplication.java @@ -0,0 +1,33 @@ +package blackjack; + +import blackjack.domain.person.Dealer; +import blackjack.domain.person.Participants; +import blackjack.domain.person.Player; +import blackjack.view.InputView; + +import java.util.List; + +public class BlackJackApplication { + private static final InputView inputView = new InputView(); + + public static void main(String[] args) throws Exception { + // player name과 배팅 금액 설정 후 객체 생성 + List players = inputView.makePlayers(); + Participants participants = new Participants(players); + + // 모두에게 카드 2장 할당 + participants.initCards(); + + // player에게 카드 추가로 뽑을지 여부 선택하도록 함 + participants.getMoreCard(inputView); + + // 모두의 카드 패, 총점 출력 + System.out.println(participants.getResultCards()); + + // 승패 여부 가린 후 최종 수익 계산 + participants.match(); + + // 최종 수익 출력 + System.out.println(participants.getIncomes()); + } +} diff --git a/src/main/java/blackjack/README.md b/src/main/java/blackjack/README.md new file mode 100644 index 00000000..398bf858 --- /dev/null +++ b/src/main/java/blackjack/README.md @@ -0,0 +1,77 @@ +# 블랙잭 +## 게임 규칙 +- 딜러와 플레이어 중 카드의 합이 21 또는 21에 가장 가까운 숫자를 가지는 쪽이 배팅 금액을 획득 +- 딜러와 플레이어는 1:1로 경기 진행 +- 게임 시작 시 모두에게 2장의 카드를 지급 + - 플레이어 + - 2장의 카드 합이 21 미만일 경우 추가로 1장씩 뽑을 수 있음 + - 카드 추가로 뽑아 21 초과 시 배팅금액 모두 상실 + - 딜러 + - 2장의 카드 합이 16 이하이면 카드 1장 추가 + - 2장의 카드 합이 17 이상이면 동작 없음 +- 처음 두 장의 카드 합이 21일 경우 블랙잭이 되면 베팅 금액의 1.5 배를 딜러에게 받는다. +- 딜러와 플레이어가 모두 두 카드 합이 21인 경우 플레이어는 베팅한 금액을 돌려받는다. +- 딜러가 21 초과 시 남은 플레이어는 무조건 승리해 배팅 금액 획득 + +## 설계 +### CardFactory +상태 +- cardPool을 가진다. + +행동 +- cardPool을 생성하여 보관한다. +- cardPool에서 한 개의 card를 꺼내주고, 중복 방지를 위해 제거한다. + +### Card +상태 +- 모양과 숫자를 가진다. + +행동 +- K,Q,J라면 10을, 2~9 사이라면 해당 값을, A라면 1을 반환한다. + - +) A일 경우에 값을 11로 갖는 경우 체크는 Cards에서 대신한다. + +### Cards +상태 +- 카드 목록을 가진다. +- 새로운 카드를 가져올 수 있는 cardFactory를 가진다. +- 카드 전체의 값을 저장한다. + +행동 +- 카드 목록에 카드를 추가할 수 있다. +- 카드 전체의 값을 반환할 수 있다. + +### Person +상태 +- Cards 객체 보유하여 카드 목록과 카드의 총점 정보를 가진다. +- 게임의 승패여부를 가짐 + +행동 +- 카드 총점을 반환할 수 있다. +- 새 카드를 뽑을 수 있는지 판별한다. + +### Dealer +상태 +- 플레이어별 게임 결과에 따른 총 수익 금액을 가진다. + +행동 +- 16이하라면 새 카드를 뽑는다. +- 수익 금액을 증감한다. +- 21 초과 시 무조건 Lose + +### Player +상태 +- 배팅 금액을 가진다. +- 수익 금액을 가진다. + +행동 +- 딜러와 매칭하여 승패를 가린 후 Status 변경 +- Status에 따라 수익 금액을 증감한다. + +### Participants +상태 +- 참가자 목록(플레이어들 + 딜러)을 가진다. + +행동 +- 모든 플레이어와 딜러의 승패를 가린다. +- 딜러와 플레이어의 수익 금액을 최종 결정한다. + 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..73925053 --- /dev/null +++ b/src/main/java/blackjack/domain/card/Card.java @@ -0,0 +1,29 @@ +package blackjack.domain.card; + +public class Card { + private final String name; + private final Character number; + + public Card(String name, Character number) { + this.name = name; + this.number = number; + } + + public int getNumber() throws Exception { + if(number>= '2' && number <= '9') { + return number-'0'; + } + if(number == 'K' || number == 'Q' || number == 'J') { + return 10; + } + if(number == 'A') { + return 1; //if card's total value is less than 11, then add 10 additionally. + } + throw new Exception("can't return value of number"); + } + + @Override + public String toString() { + return number + name; + } +} diff --git a/src/main/java/blackjack/domain/card/CardFactory.java b/src/main/java/blackjack/domain/card/CardFactory.java new file mode 100644 index 00000000..862212b6 --- /dev/null +++ b/src/main/java/blackjack/domain/card/CardFactory.java @@ -0,0 +1,33 @@ +package blackjack.domain.card; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +public class CardFactory { + private final List cardPool; + + public CardFactory() { + this.cardPool = initCardPool(); + } + + protected List initCardPool() { + List cardList = new ArrayList<>(); + List figures = Arrays.asList("스페이드", "클로버", "하트", "다이아몬드"); + List numbers = Arrays.asList('2','3','4','5','6','7','8','9','K','Q','J','A'); + for (String figure:figures) { + for (Character number:numbers) { + cardList.add(new Card(figure, number)); + } + } + return cardList; + } + + public Card selectCard(){ + int index = new Random().nextInt(this.cardPool.size()); + Card card = cardPool.get(index); + cardPool.remove(index); + return card; + } +} 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..2b20485b --- /dev/null +++ b/src/main/java/blackjack/domain/card/Cards.java @@ -0,0 +1,36 @@ +package blackjack.domain.card; + +import java.util.ArrayList; +import java.util.List; + +public class Cards { + private final List cardList; + private int total; + + public Cards() { + this.cardList = new ArrayList<>(); + this.total = 0; + } + + public String getAllCards() { + return cardList.toString(); + } + + public void getMoreCard(CardFactory cardFactory) throws Exception { + Card card = cardFactory.selectCard(); + cardList.add(card); + Integer value = card.getNumber(); + if(total < 11 && value==1){ + total += 10; //handle if a number of card is 'A' + } + total += card.getNumber(); + } + + public int getTotal(){ + return total; + } + + public int getSize() { + return this.cardList.size(); + } +} diff --git a/src/main/java/blackjack/domain/person/Dealer.java b/src/main/java/blackjack/domain/person/Dealer.java new file mode 100644 index 00000000..ba40bcec --- /dev/null +++ b/src/main/java/blackjack/domain/person/Dealer.java @@ -0,0 +1,31 @@ +package blackjack.domain.person; + +public class Dealer extends Person{ + + public Dealer() { + super(); + } + + @Override + public boolean needMoreCard() { + return this.getTotal() <= 16; + } + + @Override + public String getAllCards() { + return "딜러 카드: "+cards.getAllCards(); + } + + @Override + public String getIncome() { + return "딜러: " + this.income +"\n"; + } + + public boolean exceedMAX() { + if(this.getTotal() > MAX_NUM){ + this.changeStatus(Status.LOSE); + return true; + } + return false; + } +} diff --git a/src/main/java/blackjack/domain/person/Participants.java b/src/main/java/blackjack/domain/person/Participants.java new file mode 100644 index 00000000..f50e18c5 --- /dev/null +++ b/src/main/java/blackjack/domain/person/Participants.java @@ -0,0 +1,77 @@ +package blackjack.domain.person; + +import blackjack.domain.card.CardFactory; +import blackjack.view.InputView; + +import java.util.List; + +public class Participants { + private final CardFactory cardFactory; + private final Dealer dealer; + private final List players; + + public Participants(List players) { + this.cardFactory = new CardFactory(); + this.dealer = new Dealer(); + this.players = players; + } + + public void initCards() throws Exception { + dealer.initCards(cardFactory); + for(Player player : players){ + player.initCards(cardFactory); + } + } + + public void getMoreCard(InputView inputView) throws Exception { + //player 카드 추가 할/말 + for(Player player : players){ + while(player.needMoreCard()) { + if(inputView.wantMoreCard(player)) { + player.getMoreCard(cardFactory); + System.out.println(player.getAllCards()); + continue; + } + break; + } + } + + // dealer 카드 한장 추가 할/말 + if(dealer.needMoreCard()){ + dealer.getMoreCard(cardFactory); + System.out.println("딜러는 16이하라 한장의 카드를 더 받았습니다."); + } + } + + public void match() { + // 딜러가 21 초과시 모든 플레이어 승리 + if (dealer.getTotal()>21){ + for(Player player : players) { + player.win(dealer); + dealer.changeStatus(Status.LOSE); + } + } + + for(Player player : players) { + player.match(dealer); + } + } + + public String getResultCards() { + String ret = ""; + ret += dealer.getAllCards() + " - 결과: " +dealer.getTotal() + "\n"; + for(Player player : players) { + ret += player.getAllCards() + " - 결과: " +player.getTotal() + "\n"; + } + return ret; + } + + public String getIncomes() { + String ret = ""; + ret += dealer.getIncome(); + for(Player player : players) { + ret += player.getIncome(); + } + return ret; + } +} diff --git a/src/main/java/blackjack/domain/person/Person.java b/src/main/java/blackjack/domain/person/Person.java new file mode 100644 index 00000000..aed1af78 --- /dev/null +++ b/src/main/java/blackjack/domain/person/Person.java @@ -0,0 +1,47 @@ +package blackjack.domain.person; + +import blackjack.domain.card.CardFactory; +import blackjack.domain.card.Cards; + +public abstract class Person { + protected static final int MAX_NUM = 21; + protected final Cards cards; + protected double income; + private Status status; + + public Person() { + this.cards = new Cards(); + this.income = 0; + this.status = Status.PROCESSING; + } + + protected int getTotal() { + return cards.getTotal(); + } + + public void changeStatus(Status status) { + this.status = status; + } + + public Status getStatus() { + return status; + } + public void initCards(CardFactory cardFactory) throws Exception { + getMoreCard(cardFactory); + getMoreCard(cardFactory); + } + + public void getMoreCard(CardFactory cardFactory) throws Exception { + cards.getMoreCard(cardFactory); + } + + public abstract boolean needMoreCard(); + + public abstract String getAllCards(); + + public abstract String getIncome(); + + public void addIncome(double income){ + this.income += income; + } +} diff --git a/src/main/java/blackjack/domain/person/Player.java b/src/main/java/blackjack/domain/person/Player.java new file mode 100644 index 00000000..f2052f2d --- /dev/null +++ b/src/main/java/blackjack/domain/person/Player.java @@ -0,0 +1,90 @@ +package blackjack.domain.person; + +public class Player extends Person{ + private final String name; + private final Integer betAmount; + + public String getName() { + return name; + } + + public Player(String name, Integer betAmount) { + super(); + this.name = name; + this.betAmount = betAmount; + } + + @Override + public boolean needMoreCard() { + return this.getTotal()<21; + } + + @Override + public String getAllCards(){ + return this.name+"카드: "+cards.getAllCards(); + } + + public void match(Dealer dealer){ + // player의 status가 PROCESSING이라면 승부 시작 + if (this.getStatus() == Status.PROCESSING) { + int thisPlayerScore = this.getTotal(); + int dealerScore = dealer.getTotal(); + + // player total > 21이면 + if(thisPlayerScore>21) { + // player lose, 배팅금액 lose + this.changeStatus(Status.LOSE); + this.addIncome(-this.betAmount); + dealer.addIncome(this.betAmount); + return; + } + + // player total == 21이고, card 개수 2개일 때 + if(thisPlayerScore==21 && this.cards.getSize()==2){ + // player win, 배팅금액 * 1.5배 get + this.changeStatus(Status.LOSE); + this.addIncome(this.betAmount*1.5); + dealer.addIncome(-this.betAmount*1.5); + return; + } + + // player total ==21 && dealer total ==21 + if(thisPlayerScore ==21 && dealerScore==21){ + // player draw, 배팅금액 get + this.changeStatus(Status.DRAW); + this.addIncome(this.betAmount); + dealer.addIncome(-this.betAmount); + return; + } + + // player total > dealer total + if(thisPlayerScore > dealerScore){ + // player win, 배팅금액 get + this.changeStatus(Status.WIN); + this.addIncome(this.betAmount); + dealer.addIncome(-this.betAmount); + return; + } + + // player total < dealer total + if(thisPlayerScore < dealerScore){ + // player lose, 배팅금액 lose + this.changeStatus(Status.LOSE); + this.addIncome(-this.betAmount); + dealer.addIncome(this.betAmount); + return; + } + } + } + + @Override + public String getIncome() { + return this.name + ": " + this.income +"\n"; + } + + public void win(Dealer dealer) { + this.changeStatus(Status.WIN); + this.addIncome(this.betAmount); + dealer.addIncome(-this.betAmount); + } +} diff --git a/src/main/java/blackjack/domain/person/Status.java b/src/main/java/blackjack/domain/person/Status.java new file mode 100644 index 00000000..3a880f8c --- /dev/null +++ b/src/main/java/blackjack/domain/person/Status.java @@ -0,0 +1,8 @@ +package blackjack.domain.person; + +public enum Status { + PROCESSING, + WIN, + LOSE, + DRAW +} diff --git a/src/main/java/blackjack/service/DistributeMoney.java b/src/main/java/blackjack/service/DistributeMoney.java new file mode 100644 index 00000000..9bf3f248 --- /dev/null +++ b/src/main/java/blackjack/service/DistributeMoney.java @@ -0,0 +1,5 @@ +package blackjack.service; + +public class DistributeMoney { + +} diff --git a/src/main/java/blackjack/view/InputView.java b/src/main/java/blackjack/view/InputView.java new file mode 100644 index 00000000..9c91f3f5 --- /dev/null +++ b/src/main/java/blackjack/view/InputView.java @@ -0,0 +1,50 @@ +package blackjack.view; + +import blackjack.domain.person.Player; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + +public class InputView { + private final Scanner scanner = new Scanner(System.in); + private static final String regex = ", "; + public List makePlayers(){ + System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)"); + List players = new ArrayList<>(); + String namesRaw = scanner.nextLine(); + List names = Arrays.asList(namesRaw.split(regex)); + List betAmounts = getBetAmounts(names); + + for(int i=0;i getBetAmounts(List names){ + List betAmounts = new ArrayList<>(); + for(String name: names){ + System.out.println(name + "의 배팅 금액은?"); + Integer betAmount = Integer.valueOf(scanner.nextLine()); + betAmounts.add(betAmount); + } + return betAmounts; + } + + public boolean wantMoreCard(Player player) { + System.out.println(player.getName() + "는 한장의 카드를 더 받겠습니까?(예는 y, 아니오는 n)"); + String s = scanner.nextLine(); + + if(!s.equals("y") && !s.equals("n")){ + throw new IllegalArgumentException("y 혹은 n을 입력해야 합니다."); + } + + if(s.equals("y")) { + System.out.println(s); + return true; + } + return false; + } +} diff --git a/src/main/java/nextstep/fp/Lambda.java b/src/main/java/nextstep/fp/Lambda.java index bd68fe1c..dce72e76 100644 --- a/src/main/java/nextstep/fp/Lambda.java +++ b/src/main/java/nextstep/fp/Lambda.java @@ -27,30 +27,23 @@ public void run() { } public static int sumAll(List numbers) { - int total = 0; - for (int number : numbers) { - total += number; - } - return total; + return numbers.stream().reduce(Integer::sum).get(); + } + + public interface Conditional { + boolean test(Integer number); } + public static int sumAll(List numbers, + Conditional c) { + // c.test(number)를 활용해 구현할 수 있다. + return numbers.stream().filter(c::test).reduce(Integer::sum).get(); + } public static int sumAllEven(List numbers) { - int total = 0; - for (int number : numbers) { - if (number % 2 == 0) { - total += number; - } - } - return total; + return sumAll(numbers, (integer)->{return integer%2==0;}); } public static int sumAllOverThree(List numbers) { - int total = 0; - for (int number : numbers) { - if (number > 3) { - total += number; - } - } - return total; + return sumAll(numbers, (integer)->{return integer>3;}); } } diff --git a/src/main/java/nextstep/fp/StreamStudy.java b/src/main/java/nextstep/fp/StreamStudy.java index b446983a..86b66d53 100644 --- a/src/main/java/nextstep/fp/StreamStudy.java +++ b/src/main/java/nextstep/fp/StreamStudy.java @@ -5,6 +5,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; @@ -28,6 +29,7 @@ public static void printLongestWordTop100() throws IOException { List words = Arrays.asList(contents.split("[\\P{L}]+")); // TODO 이 부분에 구현한다. + words.stream().filter(w -> w.length() > 12).sorted(Comparator.comparing(String::length).reversed()).distinct().limit(100).map(String::toLowerCase).forEach(System.out::println); } public static List doubleNumbers(List numbers) { @@ -39,6 +41,7 @@ public static long sumAll(List numbers) { } public static long sumOverThreeAndDouble(List numbers) { - return 0; + return doubleNumbers(numbers.stream().filter(x -> x>3).collect(Collectors.toList())) + .stream().reduce(Integer::sum).get(); } -} \ No newline at end of file +} diff --git a/src/main/java/nextstep/optional/Expression.java b/src/main/java/nextstep/optional/Expression.java index 1c98cd6a..2ca322a0 100644 --- a/src/main/java/nextstep/optional/Expression.java +++ b/src/main/java/nextstep/optional/Expression.java @@ -1,5 +1,8 @@ package nextstep.optional; +import java.util.Arrays; +import java.util.Optional; + enum Expression { PLUS("+"), MINUS("-"), TIMES("*"), DIVIDE("/"); @@ -14,12 +17,6 @@ private static boolean matchExpression(Expression e, String expression) { } static Expression of(String expression) { - for (Expression v : values()) { - if (matchExpression(v, expression)) { - return v; - } - } - - throw new IllegalArgumentException(String.format("%s는 사칙연산에 해당하지 않는 표현식입니다.", expression)); + return Arrays.stream(values()).filter(v -> matchExpression(v, expression)).findFirst().orElseThrow(() -> new IllegalArgumentException(String.format("%s는 사칙연산에 해당하지 않는 표현식입니다.", expression))); } } diff --git a/src/main/java/nextstep/optional/User.java b/src/main/java/nextstep/optional/User.java index 9614c2f4..e7fcc660 100644 --- a/src/main/java/nextstep/optional/User.java +++ b/src/main/java/nextstep/optional/User.java @@ -1,5 +1,7 @@ package nextstep.optional; +import java.util.Optional; + public class User { private String name; private Integer age; @@ -33,7 +35,7 @@ public static boolean ageIsInRange1(User user) { } public static boolean ageIsInRange2(User user) { - return false; + return Optional.ofNullable(user).map(x -> x.getAge()).filter(x -> x >=30 && x <= 45).isPresent(); } @Override diff --git a/src/main/java/nextstep/optional/Users.java b/src/main/java/nextstep/optional/Users.java index 6293040d..42183b7c 100644 --- a/src/main/java/nextstep/optional/Users.java +++ b/src/main/java/nextstep/optional/Users.java @@ -2,6 +2,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Optional; public class Users { static final User DEFAULT_USER = new User("codesquad", 100); @@ -13,11 +14,6 @@ public class Users { new User("honux", 45)); User getUser(String name) { - for (User user : users) { - if (user.matchName(name)) { - return user; - } - } - return DEFAULT_USER; + return users.stream().filter(user -> user.matchName(name)).findAny().orElse(DEFAULT_USER); } } 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..5f436b1a --- /dev/null +++ b/src/test/java/blackjack/domain/card/CardTest.java @@ -0,0 +1,45 @@ +package blackjack.domain.card; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CardTest { + CardFactory cardFactory; + @BeforeEach + public void setUp() { + cardFactory = new CardFactory(); + } + + @Test + public void testCardFactory(){ + List cardList = cardFactory.initCardPool(); + assertThat(cardList.size()).isEqualTo(12 * 4); + } + + @Test + void testCards() throws Exception { + Cards cards = new Cards(); + cards.getMoreCard(cardFactory); + cards.getMoreCard(cardFactory); + System.out.println(cards.getAllCards()); + } + + @Test + public void testCardNumber() throws Exception { + Card card = new Card("", 'Q'); + Card card2 = new Card("", '3'); + + assertThat(card.getNumber()).isEqualTo(10); + assertThat(card2.getNumber()).isEqualTo(3); + } + + @Test + public void testCardNumberIfAce() throws Exception { + Card card = new Card("", 'A'); + assertThat(card.getNumber()).isEqualTo(1); + } +} diff --git a/src/test/java/blackjack/domain/person/DealerTest.java b/src/test/java/blackjack/domain/person/DealerTest.java new file mode 100644 index 00000000..a6cc1691 --- /dev/null +++ b/src/test/java/blackjack/domain/person/DealerTest.java @@ -0,0 +1,31 @@ +package blackjack.domain.person; + +import blackjack.domain.card.CardFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DealerTest { + Dealer dealer; + CardFactory cardFactory; + + @BeforeEach + void setUp(){ + dealer = new Dealer(); + cardFactory = new CardFactory(); + } + + @Test + void initTest() throws Exception { + dealer.initCards(cardFactory); + assertThat(dealer.cards.getSize()).isEqualTo(2); + } + + @Test + void getMoreTest() throws Exception { + dealer.getMoreCard(cardFactory); + assertThat(dealer.cards.getSize()).isEqualTo(1); + } + +} diff --git a/src/test/java/nextstep/fp/CarTest.java b/src/test/java/nextstep/fp/CarTest.java index 1ab1106f..9ab4e3bd 100644 --- a/src/test/java/nextstep/fp/CarTest.java +++ b/src/test/java/nextstep/fp/CarTest.java @@ -8,24 +8,14 @@ public class CarTest { @Test public void 이동() { Car car = new Car("pobi", 0); - Car actual = car.move(new MoveStrategy() { - @Override - public boolean isMovable() { - return true; - } - }); + Car actual = car.move(() -> {return true;}); assertThat(actual).isEqualTo(new Car("pobi", 1)); } @Test public void 정지() { Car car = new Car("pobi", 0); - Car actual = car.move(new MoveStrategy() { - @Override - public boolean isMovable() { - return false; - } - }); + Car actual = car.move(() ->{return false;}); assertThat(actual).isEqualTo(new Car("pobi", 0)); } }