I have inspired from the recent blackjack question here and decide to code for simple blackjack console application Please review and let me know What can improve on the code?
Rules for the application:
- There's a single dealer and a player
- Initially both get two cards
- Number
2
to9
cards have their numerical value that is counted as their score, Face cards have value of10
, Ace's value changes based on the current score - Then Player asks dealer to
hit
, as long as Player's score is less than17
- When Player reaches
17
or more then they no longer go forhit
- Dealer starts their turn, they go for hit as long as Dealer's score is less than
17
- After both are done with their turns, Outcome would be decided
- Every action has system out, output will be similar to this
Task :GamePlay.main()
Dealer got 4♣ score: 4 status: HIT
Dealer got K♦︎ score: 14 status: HIT
Player got A♦︎ score: 11 status: HIT
Player got J♥ score: 21 status: BLACKJACK
Dealer got 10♠ score: 24 status: BUST
Dealer Bust, Player Wins
class Card
public class Card {
private Suit suit;
private Type type;
public Card(Suit suit, Type type) {
this.suit = suit;
this.type = type;
}
public Suit getSuit() {
return suit;
}
public Type getType() {
return type;
}
@Override
public String toString() {
return this.getType().toString() + this.getSuit().toString();
}
}
enum Suit
public enum Suit {
SPADE('♠'), DIAMOND('♦︎'), CLUB('♣'), HEART('♥');
private final char symbol;
Suit(char symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return String.valueOf(this.symbol);
}
}
enum Type
Type enum constructor takes two parameters, their default value and symbol to represent on output, also the enum has getDefaultValue
method
Ace
has overriden the method. since it has it's own implementation
import static blackjack.GamePlay.ACE_MAX_VALUE;
public enum Type {
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"),
ACE(ACE_MAX_VALUE, "A") {
@Override
public int getDefaultValue(int totalValue) {
if (totalValue + ACE_MAX_VALUE > 21) {
return 1;
}
return ACE_MAX_VALUE;
}
},
JACK(10, "J"), QUEEN(10, "Q"), KING(10, "K");
private int defaultValue;
private String symbol;
Type(int defaultValue, String symbol) {
this.defaultValue = defaultValue;
this.symbol = symbol;
}
public int getDefaultValue(int totalValue) {
return this.defaultValue;
}
@Override
public String toString() {
return this.symbol;
}
}
class Deck
Deck has list of all 52 Card
s and loads them into the list on constructor, it has getACard
method which removes a card from random index and return a Card
object for dealer to draw
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Deck {
private List<Card> cards;
public Deck() {
this.cards = new ArrayList<>();
loadCards();
}
private void loadCards() {
for (Suit suit : Suit.values()) {
for (Type type : Type.values()) {
this.cards.add(new Card(suit, type));
}
}
}
public Card getACard() {
int randomIndex = new Random().nextInt(this.cards.size());
return this.cards.remove(randomIndex);
}
}
class Dealer
Dealer
has the Deck
, on constructor it draws two cards and adds to their list of cards
and updates their score and status.
It has hit
method which draws a Card
from the Deck
and returns. based on the value of the card
their score and status is updated.
play
method does the orchestration also prints the state of the Dealer
canHit
method checks the status of the Dealer returns true
if they're in HIT
status
import java.util.ArrayList;
import java.util.List;
import static blackjack.GamePlay.*;
public class Dealer {
private Deck deck;
private List<Card> cards;
private int score;
private Status status;
public Dealer(Deck deck) {
this.cards = new ArrayList<>();
this.deck = deck;
for (int i = 0; i < INITIAL_CARDS_COUNT; i++) {
play();
}
}
public Card hit() {
return this.deck.getACard();
}
public void play() {
Card card = hit();
this.score += card.getType().getDefaultValue(this.score);
this.cards.add(card);
this.status = Status.getStatus(this.score);
System.out.printf("Dealer got %s \t score: %d \t status: %s \n", card, this.score,this.status.toString());
}
public boolean canHit() {
return this.status.equals(Status.HIT);
}
public Status getStatus() {
return status;
}
public int getScore() {
return score;
}
}
class Player
Player
has the Dealer
, on constructor it draws two cards and adds to their list of cards and updates their score and status.
It has hit
method which draws a Card
from the Dealer
and returns. based on the value of the card
their score and status is updated.
play
method does the orchestration also prints the state of the Player
canHit
method checks the status of the Dealer returns true
if they're in HIT
status
import java.util.ArrayList;
import java.util.List;
import static blackjack.GamePlay.INITIAL_CARDS_COUNT;
import static blackjack.Status.HIT;
public class Player {
private List<Card> cards;
private Dealer dealer;
private int score;
private Status status;
public Player(Dealer dealer) {
this.cards = new ArrayList<>();
this.dealer = dealer;
for (int i = 0; i < INITIAL_CARDS_COUNT; i++) {
play();
}
}
public void play() {
Card card = this.dealer.hit();
this.score += card.getType().getDefaultValue(this.score);
this.cards.add(card);
this.status = Status.getStatus(this.score);
System.out.printf("Player got %s \t score: %d \t status: %s \n", card, this.score,this.status.toString());
}
public boolean canHit() {
return this.status.equals(HIT);
}
public Status getStatus() {
return status;
}
public int getScore() {
return score;
}
}
enum Status
Status enum has constructor takes two params min
and max
, this decides which Status
any score fall in
getStatus
method takes score and return Status
based on the score
public enum Status {
HIT(0, 16), STAND(17, 20), BLACKJACK(21, 21), BUST(22, 100);
private int min;
private int max;
Status(int min, int max) {
this.min = min;
this.max = max;
}
public static Status getStatus(int score) {
Status[] statuses = Status.values();
for (Status status : statuses) {
if (status.min <= score && status.max >= score) {
return status;
}
}
return null;
}
}
class GamePlay
This is orchestrator for the application creates All objects and decides the outcome of the game
public class GamePlay {
public static final int INITIAL_CARDS_COUNT = 2;
public static final int ACE_MAX_VALUE = 11;
public static String getOutcome(Player player, Dealer dealer) {
if (player.getStatus().equals(Status.BUST)) {
return "Player Bust, Dealer Wins";
} else if (player.getScore() == dealer.getScore()) {
return "Tie";
} else if (dealer.getStatus().equals(Status.BUST)) {
return "Dealer Bust, Player Wins";
} else if (player.getScore() > dealer.getScore()) {
return "Player Wins";
}
return "Dealer Wins";
}
public static void main(String[] args) {
Deck deck = new Deck();
Dealer dealer = new Dealer(deck);
Player player = new Player(dealer);
while (player.canHit()) {
player.play();
}
while (dealer.canHit()) {
dealer.play();
}
System.out.println(getOutcome(player, dealer));
}
}