This code is not done, but still fully functional.
But, before I continue I'd really appreciate some input on the code-structure as is.
AbstractHand.java
package com.tn.deck;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public abstract class AbstractHand<T extends Suitable & Rankable> {
protected List<T> hand = new ArrayList<>();
public void drawCard(T o) {
hand.add(o);
}
public void drawCards(T[] o) {
hand.addAll(Arrays.asList(o));
}
public abstract void status();
public abstract int calculateScore();
}
Deck.java
package com.tn.deck;
public interface Deck<T extends Suitable & Rankable> {
T dealCard();
T[] dealCards(int n);
void shuffle();
}
Rankable.java
package com.tn.deck;
public interface Rankable<T extends Comparable<T>> {
boolean isConsecutive(T other);
}
Suitable.java
package com.tn.deck;
public interface Suitable<T extends Comparable<T>> {
boolean isSameSuit(T other);
}
Card.java
package com.tn.blackjack;
import com.tn.deck.Rankable;
import com.tn.deck.Suitable;
public class Card implements Suitable<Card>, Rankable<Card>, Comparable<Card> {
private final Suit suit;
private final Rank rank;
Card(Suit suit, Rank rank) {
this.suit = suit;
this.rank = rank;
}
public Suit getSuit() {
return suit;
}
public Rank getRank() {
return rank;
}
public void print() {
System.out.printf("%s%s ", suit.getIcon(), rank.getName());
}
@Override
public boolean isConsecutive(Card other) {
return 1 + rank.ordinal() == other.rank.ordinal() ||
rank.ordinal() - 1 == other.rank.ordinal();
}
@Override
public boolean isSameSuit(Card other) {
return suit.equals(other.suit);
}
@Override
public int compareTo(Card other) {
if(rank.getValue() == other.getRank().getValue() &&
suit.getIcon().equals(other.getSuit().getIcon())) {
return 0;
} else if(rank.getValue() < other.getRank().getValue()) {
return -1;
} else {
return 1;
}
}
}
CardDeck.java
package com.tn.blackjack;
import com.tn.deck.Deck;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class CardDeck implements Deck<Card> {
private static final Random rand = new Random(System.nanoTime());
private List<Card> deck;
private List<Card> dealtCards;
CardDeck(Suit[] suits, Rank[] ranks) {
this.deck = initializeDeckWith(suits, ranks);
this.dealtCards = new ArrayList<>();
shuffle();
}
private List<Card> initializeDeckWith(Suit[] suits, Rank[] ranks) {
List<Card> newDeck = new ArrayList<>();
for(Suit suit : suits) {
for(Rank rank : ranks) {
newDeck.add(new Card(suit, rank));
}
}
return newDeck;
}
@Override
public Card dealCard() {
if(deck.size() < 1) {
throw new IllegalStateException("Deck is empty");
}
int index = rand.nextInt(deck.size());
Card card = deck.get(rand.nextInt(index));
deck.remove(index);
dealtCards.add(card);
return card;
}
@Override
public Card[] dealCards(int n) {
if(deck.size() < n) {
throw new IllegalStateException("Not enough cards left in deck");
}
Card[] cardsToDeal = new Card[n];
for(int i = 0; i < n; i++) {
int index = rand.nextInt(deck.size());
Card card = deck.get(index);
deck.remove(index);
cardsToDeal[i] = card;
dealtCards.add(card);
}
return cardsToDeal;
}
@Override
public void shuffle() {
Collections.shuffle(deck);
}
}
Dealer.java
package com.tn.blackjack;
import com.tn.deck.AbstractHand;
public class Dealer extends AbstractHand<Card> {
private CardDeck deck;
Dealer() {
this.deck = new CardDeck(Suit.getSuits(), Rank.getRanks());
}
public void startInitialDealingOfCards(Player[] players) {
for(Player player : players) {
Card[] initialCards = deck.dealCards(2);
player.drawCards(initialCards);
}
Card[] dealersInitialCards = deck.dealCards(2);
drawCards(dealersInitialCards);
}
@Override
public void status() {
System.out.printf("%nThe Dealer, has the following hand:%n");
hand.forEach(Card::print);
System.out.printf("( score of %d )", calculateScore());
}
@Override
public int calculateScore() {
return hand.stream().mapToInt(card -> card.getRank().getValue()).sum();
}
}
Player.java
package com.tn.blackjack;
import com.tn.deck.AbstractHand;
public class Player extends AbstractHand<Card> {
private int id;
Player(int id) {
this.id = id;
}
@Override
public void status() {
System.out.printf("%nPlayer %d, has the following hand:%n", id);
hand.forEach(Card::print);
System.out.printf("( score of %d )", calculateScore());
}
@Override
public int calculateScore() {
return hand.stream().mapToInt(card -> card.getRank().getValue()).sum();
}
}
Rank.java
package com.tn.blackjack;
public enum Rank {
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), ACE("A", 11);
private final String name;
private final int value;
Rank(String name, int value) {
this.name = name;
this.value = value;
}
public static Rank[] getRanks() {
return new Rank[] {
TWO, THREE, FOUR, FIVE,
SIX, SEVEN, EIGHT, NINE, TEN,
JACK, QUEEN, KING, ACE
};
}
public String getName() {
return name;
}
public int getValue() {
return value;
}
}
Suit.java
package com.tn.blackjack;
public enum Suit {
SPADE("\u2660"),
HEART("\u2665"),
DIAMOND("\u2666"),
CLUB("\u2663");
private final String icon;
Suit(String icon) {
this.icon = icon;
}
public static Suit[] getSuits() {
return new Suit[] { SPADE, HEART, DIAMOND, CLUB };
}
public String getIcon() {
return icon;
}
}
Game.java
package com.tn.blackjack;
import java.util.Arrays;
public class Game {
private Dealer dealer;
private Player[] players;
public Game() {
this.dealer = new Dealer();
this.players = new Player[] {
new Player(1),
new Player(2)
};
}
public void start() {
dealer.startInitialDealingOfCards(players);
dealer.status();
Arrays.stream(players).forEach(Player::status);
}
}