There was a task on Stack Overflow which quite obviously was too broad (it didn't have a solution provided or anything!) which was the following:
Give an object oriented design for a game that has the following requirements:
- It is a game of cards.
- Cards can have four symbols - Hearts, Diamonds, Clubs, Leaves
- Cards have numbers from A to 10. These are the values of the cards.
- There is an operation associated with each symbol. They are Hearts - Sum, Diamonds - Subtract, Clubs - Multiply, Leaves - Divide.
- Each Player would be dealt 3 cards.
The score of the player would be calculated by carrying out the particular operation on the card.
E.g. if a player has 1 of hearts, 2 of Diamonds, 4 of hearts, the score would be calculated as +1 -2 +4, which is equal to 3.
Precedence of the operators would be given as input.
Give a design for the same.
Technically, I've just realized as I was writing this that I'm not providing the precedence of the operators as input and just evaluating them in order of the cards, but please excuse me for that.
Otherwise, I've implemented a possible solution to this given task:
public interface Operator {
double evaluate(double carrier, double newValue);
}
public enum Operations implements Operator {
ADDITION {
@Override
public double evaluate(double carrier, double newValue) {
return carrier + newValue;
}
},
SUBTRACTION {
@Override
public double evaluate(double carrier, double newValue) {
return carrier - newValue;
}
},
MULTIPLICATION {
@Override
public double evaluate(double carrier, double newValue) {
return carrier * newValue;
}
},
DIVISION {
@Override
public double evaluate(double carrier, double newValue) {
return carrier / newValue;
}
}
}
public enum CardTypes {
HEART(Operations.ADDITION),
DIAMOND(Operations.SUBTRACTION),
CLUB(Operations.MULTIPLICATION),
LEAVES(Operations.DIVISION);
private Operator operation;
private CardTypes(Operator operation) {
this.operation = operation;
}
public Operator getOperation() {
return operation;
}
}
public enum CardValues {
_A(1.0),
_2(2.0),
_3(3.0),
_4(4.0),
_5(5.0),
_6(6.0),
_7(7.0),
_8(8.0),
_9(9.0),
_10(10.0);
private double value;
private CardValues(double value) {
this.value = value;
}
public double getValue() {
return value;
}
}
public class Card {
private CardValues cardValue;
private CardTypes cardType;
public Card() {
}
public Card(CardValues cardValue, CardTypes cardType) {
this.cardType = cardType;
this.cardValue = cardValue;
}
public double evaluateCard(double currentScore) {
Operator operator = cardType.getOperation();
double value = cardValue.getValue();
return operator.evaluate(currentScore, value);
}
}
public class Deck {
public static class EmptyDeckException extends Exception {
public EmptyDeckException() {
super("There are no cards left in the deck.");
}
}
private List<Card> cards;
public Deck() {
this.cards = new ArrayList<>();
for(CardTypes cardTypes : CardTypes.values()) {
for(CardValues cardValues : CardValues.values()) {
Card card = new Card(cardValues, cardTypes);
cards.add(card);
}
}
}
public Card getCardFromDeck(Random random) throws EmptyDeckException {
int currentDeckSize = cards.size();
if(currentDeckSize == 0) {
throw new EmptyDeckException();
} else {
return cards.remove(random.nextInt(currentDeckSize));
}
}
}
public class Player {
private List<Card> cards;
public Player() {
this.cards = new ArrayList<>();
}
public void addCard(Card card) {
this.cards.add(card);
}
public void removeCard(Card card) {
this.cards.remove(card);
}
public double evaluateCards() {
double score = 0;
for(Card card : cards) {
score = card.evaluateCard(score);
}
return score;
}
}
public class Game {
private List<Player> players;
private Deck deck;
private static final int CARD_COUNT = 3;
private Random random;
public Game() {
this(new ArrayList<>());
}
public Game(List<Player> players) {
this.players = players;
this.deck = new Deck();
this.random = new Random();
}
public void addPlayer(Player player) {
this.players.add(player);
}
public void play() {
try {
for (Player player : players) {
for(int i = 0; i < CARD_COUNT; i++) {
player.addCard(deck.getCardFromDeck(random));
}
}
} catch(Deck.EmptyDeckException e) {
System.out.println(e.getMessage());
}
double[] results = new double[players.size()];
for(int i = 0, n = players.size(); i < n; i++) {
Player player = players.get(i);
double result = player.evaluateCards();
results[i] = result;
System.out.println("Player " + (i+1) + " had a score of " + result);
}
System.out.println("");
double max = results[0];
int playerIndex = 0;
for(int i = 1; i < results.length; i++) {
if(max < results[i]) {
max = results[i];
playerIndex = i;
}
}
int playerId = playerIndex + 1;
System.out.println("Player " + playerId + " won with a score of " + max);
}
}
public class Main {
public void execute() {
Game game = new Game();
game.addPlayer(new Player());
game.addPlayer(new Player());
game.addPlayer(new Player());
game.addPlayer(new Player());
game.play();
}
public static void main(String[] args) {
Main main = new Main();
main.execute();
}
}
And the result:
Player 1 had a score of 0.0 Player 2 had a score of 6.0 Player 3 had a score of 2.0 Player 4 had a score of -2.0 Player 2 won with a score of 6.0
I'd like to ask if there are any glaring flaws and ways to improve the code.
(I used double
because there's division as a possible operator, and I figured it'd make no sense to have int
and integer division in the evaluation of a card game.)
*3 /2 /4
? \$\endgroup\$double score = 0; ... score = card.evaluateCard(score);
, I think that just becomes0
... This challenge will be more fun if players get to decide their card order to maximize their points, just saying :D. \$\endgroup\$