diff --git a/README.md b/README.md new file mode 100644 index 0000000..fcc2ea0 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Crazy Card Game + +Assignment documentation is here: https://courses.grainger.illinois.edu/cs126/fa2019/assignments/crazy-cardgame/ + +Good luck! diff --git a/crazy-cardgame.iml b/crazy-cardgame.iml new file mode 100644 index 0000000..7350b04 --- /dev/null +++ b/crazy-cardgame.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..4feed12 --- /dev/null +++ b/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + uiuc126 + crazy-cardgame + 1.0-SNAPSHOT + + + + com.google.code.gson + gson + 2.8.5 + + + + commons-io + commons-io + 2.6 + + + junit + junit + 4.12 + test + + + + com.github.stefanbirkner + system-rules + 1.19.0 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + + \ No newline at end of file diff --git a/src/main/java/cardgame/Card.java b/src/main/java/cardgame/Card.java new file mode 100644 index 0000000..35d8888 --- /dev/null +++ b/src/main/java/cardgame/Card.java @@ -0,0 +1,112 @@ +package cardgame; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; + +/* + * ======================================== + * You should not need to modify this file. + * ======================================== + */ + +/** + * Represents a standard playing card from a 52 card deck. + */ +public class Card { + + public enum Suit {DIAMONDS, HEARTS, SPADES, CLUBS} + + public enum Rank { + ACE, + TWO, + THREE, + FOUR, + FIVE, + SIX, + SEVEN, + EIGHT, + NINE, + TEN, + JACK, + QUEEN, + KING + } + + private static final int FACE_CARD_VALUE = 10; + private static final int EIGHT_CARD_VALUE = 50; + + private static final int DECK_SIZE = Suit.values().length * Rank.values().length; + + private Suit suit; + private Rank rank; + + public Card(Suit suit, Rank rank) { + this.suit = suit; + this.rank = rank; + } + + public Suit getSuit() { + return suit; + } + + public Rank getRank() { + return rank; + } + + /** + * Returns the Crazy8s point value for this card. + * + * @return An integer representing this card's point value + */ + public int getPointValue() { + if (rank.ordinal() >= Rank.JACK.ordinal()) { + return FACE_CARD_VALUE; + } + + if (rank == Rank.EIGHT) { + return EIGHT_CARD_VALUE; + } + + // Otherwise, return numeric value of card + return rank.ordinal() + 1; + } + + /** + * Creates a new, unshuffled deck of standard playing cards. + * + * @return A list representing an unshuffled deck of cards + */ + public static List getDeck() { + List cardDeck = new ArrayList<>(DECK_SIZE); + + for (Suit suit : EnumSet.allOf(Suit.class)) { + for (Rank rank : EnumSet.allOf(Rank.class)) { + cardDeck.add(new Card(suit, rank)); + } + } + + return cardDeck; + } + + // Convenience methods; you might or might not need these. + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Card card = (Card) o; + return suit == card.suit && + rank == card.rank; + } + + @Override + public int hashCode() { + return Objects.hash(suit, rank); + } +} diff --git a/src/main/java/cardgame/Play.java b/src/main/java/cardgame/Play.java new file mode 100644 index 0000000..a2bce05 --- /dev/null +++ b/src/main/java/cardgame/Play.java @@ -0,0 +1,109 @@ +package cardgame; + +import java.util.*; + +import cardgame.Card.Rank; + +public class Play { + public static void main(String[] args) { + Play o = new Play(); + Player1 dharshan = new Player1(); + Player2 sherin = new Player2(); + List deck = new ArrayList<>(); + deck = Card.getDeck(); + Collections.shuffle(deck); + deck = o.start(deck, dharshan, sherin); + o.play(deck, dharshan, sherin); + } + + List start(List deck, Player1 dharshan, Player2 sherin) { + int i; + List player1 = new ArrayList<>(); + List player2 = new ArrayList<>(); + for (i = 0; i < 14; i++) { + if (i % 2 == 0) { + player2.add(deck.get(0)); + } else { + player1.add(deck.get(0)); + } + deck.remove(0); + } + System.out.println(); + System.out.println("Card of Player1: "); + for (i = 0; i < player1.size(); i++) { + System.out.println(player1.get(i).getRank() + " " + player1.get(i).getSuit() + " "); + } + System.out.println("Card of Player2: "); + for (i = 0; i < player2.size(); i++) { + System.out.println(player2.get(i).getRank() + " " + player2.get(i).getSuit() + " "); + } + System.out.println(); + dharshan.receiveInitialCards(player1); + sherin.receiveInitialCards(player2); + return deck; + } + + void play(List deck, Player1 dharshan, Player2 sherin) { + Play o = new Play(); + int score1 = 0, i, score2 = 0; + Card topCard; + topCard = deck.get(0); + deck.remove(0); + System.out.println("Top Card : " + topCard.getRank() + " " + topCard.getSuit()); + Card.Suit decCard = null; + while (score1 < 200 && score2 < 200) { + for (i = 0; i < 3; i++) { + if (sherin.shouldDrawCard(topCard, decCard)) { + if (deck.size() != 0) { + sherin.receiveCard(deck.get(0)); + deck.remove(0); + } + } else { + topCard = sherin.playCard(); + System.out.println("Top Card : " + topCard.getRank() + " " + topCard.getSuit()); + if (topCard.getRank() == Rank.EIGHT && sherin.myCards.size() != 0) { + decCard = sherin.declareSuit(); + } + break; + } + } + for (i = 0; i < 3; i++) { + if (dharshan.shouldDrawCard(topCard, decCard)) { + if (deck.size() != 0) { + dharshan.receiveCard(deck.get(0)); + deck.remove(0); + } + } else { + topCard = dharshan.playCard(); + System.out.println("Top Card : " + topCard.getRank() + " " + topCard.getSuit()); + if (topCard.getRank() == Rank.EIGHT && sherin.myCards.size() != 0) { + decCard = dharshan.declareSuit(); + } + break; + } + } + if (dharshan.myCards.size() == 0 || deck.size() == 0) { + score2 += sherin.getScore(); + System.out.println("Score of Player 1 : " + score2); + } + if (sherin.myCards.size() == 0 || deck.size() == 0) { + score1 += dharshan.getScore(); + System.out.println("Score of Player 2 : " + score1); + } + if (deck.size() == 0 && score1 < 200 && score2 < 200) { + deck = Card.getDeck(); + Collections.shuffle(deck); + deck = o.start(deck, dharshan, sherin); + } + } + o.results(score1, score2); + } + + void results(int s1, int s2) { + if (s1 >= 200) { + System.out.println("Winnner is sherin"); + } else { + System.out.println("Winnner is Dharshan"); + } + } +} \ No newline at end of file diff --git a/src/main/java/cardgame/Player1.java b/src/main/java/cardgame/Player1.java new file mode 100644 index 0000000..10e4c16 --- /dev/null +++ b/src/main/java/cardgame/Player1.java @@ -0,0 +1,107 @@ +package cardgame; + +import java.util.List; + +public class Player1 implements PlayerStrategy { + int playerId; + List opponentIds; + List myCards; + Card topPileCard; + Card.Suit changedSuit; + + public void init(int playerId, List opponentIds) { + this.playerId = playerId; + this.opponentIds = opponentIds; + } + + public void receiveInitialCards(List cards) { + this.myCards = cards; + } + + public boolean shouldDrawCard(Card topPileCard, Card.Suit changedSuit) { + this.topPileCard = topPileCard; + this.changedSuit = changedSuit; + if (changedSuit == null) { + for (int i = 0; i < myCards.size(); i++) { + if (myCards.get(i).getSuit().equals(topPileCard.getSuit()) + || myCards.get(i).getRank().equals(topPileCard.getRank())) { + return false; + } + } + } else { + for (int i = 0; i < myCards.size(); i++) { + if (myCards.get(i).getSuit().equals(changedSuit)) { + return false; + } + } + } + return true; + } + + public void receiveCard(Card drawnCard) { + System.out.println("Dharshan recieved :" + drawnCard.getRank() + " " + drawnCard.getSuit()); + myCards.add(drawnCard); + } + + public Card playCard() { + Card outCard = null; + if (changedSuit == null) { + for (int i = 0; i < myCards.size(); i++) { + if (myCards.get(i).getSuit().equals(topPileCard.getSuit()) + || myCards.get(i).getRank().equals(topPileCard.getRank())) { + System.out.println("Dharshan played: " + myCards.get(i).getRank() + " " + myCards.get(i).getSuit()); + outCard = myCards.get(i); + myCards.remove(i); + break; + } + } + } else { + for (int i = 0; i < myCards.size(); i++) { + if (myCards.get(i).getSuit().equals(changedSuit)) { + System.out.println("Dharshan played: " + myCards.get(i).getRank() + " " + myCards.get(i).getSuit()); + outCard = myCards.get(i); + myCards.remove(i); + break; + } + } + } + return outCard; + + } + + public Card.Suit declareSuit() { + Card Dsiut = myCards.get(0); + int max = 0, count = 0; + for (int i = 0; i < myCards.size(); i++) { + count = 0; + for (int j = 0; j < myCards.size(); j++) { + if (myCards.get(i) == myCards.get(j)) + count++; + } + if (count > max) { + max = count; + Dsiut = myCards.get(i); + } + } + System.out.println("Delcare suit: " + Dsiut.getSuit()); + return Dsiut.getSuit(); + + } + + public void processOpponentActions(List opponentActions) { + + } + + public void reset() { + + } + + @Override + public int getScore() { + int point = 0; + for (int i = 0; i < myCards.size(); i++) { + point += myCards.get(i).getPointValue(); + } + return point; + } +} diff --git a/src/main/java/cardgame/Player2.java b/src/main/java/cardgame/Player2.java new file mode 100644 index 0000000..8d15f93 --- /dev/null +++ b/src/main/java/cardgame/Player2.java @@ -0,0 +1,109 @@ +package cardgame; + +import java.util.List; + +public class Player2 implements PlayerStrategy { + int playerId; + List opponentIds; + List myCards; + Card topPileCard; + Card.Suit changedSuit; + + public void init(int playerId, List opponentIds) { + this.playerId = playerId; + this.opponentIds = opponentIds; + } + + public void receiveInitialCards(List cards) { + this.myCards = cards; + + } + + public boolean shouldDrawCard(Card topPileCard, Card.Suit changedSuit) { + this.topPileCard = topPileCard; + this.changedSuit = changedSuit; + if (changedSuit == null) { + for (int i = 0; i < myCards.size(); i++) { + if (myCards.get(i).getSuit().equals(topPileCard.getSuit()) + || myCards.get(i).getRank().equals(topPileCard.getRank())) { + return false; + } + } + } else { + for (int i = 0; i < myCards.size(); i++) { + if (myCards.get(i).getSuit().equals(changedSuit)) { + return false; + } + } + } + return true; + + } + + public void receiveCard(Card drawnCard) { + System.out.println("Sherin recieved: " + drawnCard.getRank() + " " + drawnCard.getSuit()); + myCards.add(drawnCard); + } + + public Card playCard() { + Card outCard = null; + if (changedSuit == null) { + for (int i = 0; i < myCards.size(); i++) { + if (myCards.get(i).getSuit().equals(topPileCard.getSuit()) + || myCards.get(i).getRank().equals(topPileCard.getRank())) { + System.out.println("Shrein played: " + myCards.get(i).getRank() + " " + myCards.get(i).getSuit()); + outCard = myCards.get(i); + myCards.remove(i); + break; + } + } + } else { + for (int i = 0; i < myCards.size(); i++) { + if (myCards.get(i).getSuit().equals(changedSuit)) { + System.out.println("Sherin played: " + myCards.get(i).getRank() + " " + myCards.get(i).getSuit()); + outCard = myCards.get(i); + myCards.remove(i); + break; + } + } + } + return outCard; + + } + + public Card.Suit declareSuit() { + Card Dsiut = myCards.get(0); + int max = 52, count = 0; + for (int i = 0; i < myCards.size(); i++) { + count = 0; + for (int j = 0; j < myCards.size(); j++) { + if (myCards.get(i) == myCards.get(j)) + count++; + } + if (count < max) { + max = count; + Dsiut = myCards.get(i); + } + } + System.out.println("Delcare Suit: " + Dsiut.getSuit()); + return Dsiut.getSuit(); + + } + + public void processOpponentActions(List opponentActions) { + + } + + public void reset() { + + } + + @Override + public int getScore() { + int point = 0; + for (int i = 0; i < myCards.size(); i++) { + point += myCards.get(i).getPointValue(); + } + return point; + } +} \ No newline at end of file diff --git a/src/main/java/cardgame/PlayerStrategy.java b/src/main/java/cardgame/PlayerStrategy.java new file mode 100644 index 0000000..75f209d --- /dev/null +++ b/src/main/java/cardgame/PlayerStrategy.java @@ -0,0 +1,109 @@ +package cardgame; + +import java.util.List; + +/* + * ======================= + * DO NOT MODIFY THIS FILE + * ======================= + */ + +/** + * A contract for how a Crazy8's player will interact with a Crazy8's game + * engine. + *

+ * A game engine would call these methods to interact with implementors of this + * interface in order + * to orchestrate a game of Crazy8's. + */ +public interface PlayerStrategy { + + /** + * Gives the player their assigned id, as well as a list of the opponents' + * assigned ids. + *

+ * This method will be called by the game engine once at the very beginning + * (before any games + * are started), to allow the player to set up any initial state. + * + * @param playerId The id for this player, assigned by the game engine + * @param opponentIds A list of ids for this player's opponents + */ + void init(int playerId, List opponentIds); + + /** + * Called once at the beginning of o game to deal the player their initial + * cards. + * + * @param cards The initial list of cards dealt to this player + */ + void receiveInitialCards(List cards); + + /** + * Called to ask whether the player wants to draw this turn. Gives this player + * the top card of + * the discard pile at the beginning of their turn, as well as an optional suit + * for the pile in + * case a "8" was played, and the suit was changed. + *

+ * By having this return true, the game engine will then call receiveCard() for + * this player. + * Otherwise, playCard() will be called. + * + * @param topPileCard The card currently at the top of the pile + * @param changedSuit The suit that the pile was changed to as the result of an + * "8" being + * played. Will be null if no "8" was played. + * @return whether or not the player wants to draw + */ + boolean shouldDrawCard(Card topPileCard, Card.Suit changedSuit); + + /** + * Called when this player has chosen to draw a card from the deck. + * + * @param drawnCard The card that this player has drawn + */ + void receiveCard(Card drawnCard); + + /** + * Called when this player is ready to play a card (will not be called if this + * player drew on + * their turn). + *

+ * This will end this player's turn. + * + * @return The card this player wishes to put on top of the pile + */ + Card playCard(); + + /** + * Called if this player decided to play a "8" card to ask the player what suit + * they would like + * to declare. + *

+ * This player should then return the Card.Suit enum that it wishes to set for + * the discard + * pile. + */ + Card.Suit declareSuit(); + + /** + * Called at the very beginning of this player's turn to give it context of what + * its opponents + * chose to do on each of their turns. + * + * @param opponentActions A list of what the opponents did on each of their + * turns + */ + void processOpponentActions(List opponentActions); + + /** + * Returns the score of each player + */ + int getScore(); + + /** + * Called before a game begins, to allow for resetting any state between games. + */ + void reset(); +} diff --git a/src/main/java/cardgame/PlayerTurn.java b/src/main/java/cardgame/PlayerTurn.java new file mode 100644 index 0000000..dfe440a --- /dev/null +++ b/src/main/java/cardgame/PlayerTurn.java @@ -0,0 +1,59 @@ +package cardgame; + +/* + * ======================== + * DO NOT MODIFY THIS FILE. + * ======================== + */ + +import java.util.Objects; + +/** + * Represents an player's action on their turn: either they drew a card or they played a card. + */ +public class PlayerTurn { + + /** + * The ID of the player that this action corresponds to + */ + public int playerId; + + /** + * If the player drew a card on their turn + */ + public boolean drewACard; + + /** + * The card the player played on their turn, or null if the player didn't play a card. + */ + public Card playedCard; + + /** + * When a player plays an "8", they can declare what suit the next player must play to. + *

+ * If the player played an "8", this is the suit that they declared. Otherwise, this is null. + */ + public Card.Suit declaredSuit; + + // Convenience methods; you might or might not need these. + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PlayerTurn that = (PlayerTurn) o; + return playerId == that.playerId && + drewACard == that.drewACard && + Objects.equals(playedCard, that.playedCard) && + declaredSuit == that.declaredSuit; + } + + @Override + public int hashCode() { + return Objects.hash(playerId, drewACard, playedCard, declaredSuit); + } +} diff --git a/src/test/java/cardgame/CardTest.java b/src/test/java/cardgame/CardTest.java new file mode 100644 index 0000000..db435c6 --- /dev/null +++ b/src/test/java/cardgame/CardTest.java @@ -0,0 +1,119 @@ +package cardgame; + +import static cardgame.Card.Rank; +import static cardgame.Card.Suit; +import static cardgame.Card.getDeck; +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.Before; +import org.junit.Test; + +/* + * ======================================================= + * DO NOT PUT YOUR TESTS IN THIS FILE! + * + * Write your own test classes for any classes you create. + * ======================================================= + */ + +public class CardTest { + + private List deck; + + @Before + public void setUp() { + deck = getDeck(); + } + + @Test + public void faceCardsWorthTenPoints() { + List faceCards = Arrays.asList( + new Card(Suit.DIAMONDS, Rank.JACK), + new Card(Suit.HEARTS, Rank.QUEEN), + new Card(Suit.SPADES, Rank.KING) + ); + + for (Card card : faceCards) { + assertEquals(10, card.getPointValue()); + } + } + + @Test + public void eightsWorthFiftyPoints() { + List eights = Arrays.asList( + new Card(Suit.DIAMONDS, Rank.EIGHT), + new Card(Suit.HEARTS, Rank.EIGHT), + new Card(Suit.SPADES, Rank.EIGHT), + new Card(Suit.CLUBS, Rank.EIGHT) + ); + + for (Card card : eights) { + assertEquals(50, card.getPointValue()); + } + } + + @Test + public void acesWorthOnePoint() { + List aces = Arrays.asList( + new Card(Suit.DIAMONDS, Rank.ACE), + new Card(Suit.HEARTS, Rank.ACE), + new Card(Suit.SPADES, Rank.ACE), + new Card(Suit.CLUBS, Rank.ACE) + ); + + for (Card card : aces) { + assertEquals(1, card.getPointValue()); + } + } + + @Test + public void numericCardsWorthNumericPoints() { + assertEquals(2, new Card(Suit.DIAMONDS, Rank.TWO).getPointValue()); + assertEquals(3, new Card(Suit.HEARTS, Rank.THREE).getPointValue()); + assertEquals(4, new Card(Suit.SPADES, Rank.FOUR).getPointValue()); + assertEquals(5, new Card(Suit.CLUBS, Rank.FIVE).getPointValue()); + assertEquals(6, new Card(Suit.DIAMONDS, Rank.SIX).getPointValue()); + assertEquals(7, new Card(Suit.HEARTS, Rank.SEVEN).getPointValue()); + assertEquals(9, new Card(Suit.SPADES, Rank.NINE).getPointValue()); + assertEquals(10, new Card(Suit.CLUBS, Rank.TEN).getPointValue()); + } + + @Test + public void deckHasFiftyTwoCards() { + assertEquals(52, deck.size()); + } + + @Test + public void deckHasThirteenOfEachSuit() { + int numDiamonds = 0; + int numHearts = 0; + int numSpades = 0; + int numClubs = 0; + + for (Card card : deck) { + switch (card.getSuit()) { + case DIAMONDS: + numDiamonds++; + break; + case HEARTS: + numHearts++; + break; + case SPADES: + numSpades++; + break; + case CLUBS: + numClubs++; + break; + default: + break; + } + } + + assertEquals(13, numDiamonds); + assertEquals(13, numHearts); + assertEquals(13, numSpades); + assertEquals(13, numClubs); + } +} diff --git a/target/classes/cardgame/Card$Rank.class b/target/classes/cardgame/Card$Rank.class new file mode 100644 index 0000000..3d4d4a0 Binary files /dev/null and b/target/classes/cardgame/Card$Rank.class differ diff --git a/target/classes/cardgame/Card$Suit.class b/target/classes/cardgame/Card$Suit.class new file mode 100644 index 0000000..e6ec179 Binary files /dev/null and b/target/classes/cardgame/Card$Suit.class differ diff --git a/target/classes/cardgame/Card.class b/target/classes/cardgame/Card.class new file mode 100644 index 0000000..055ba41 Binary files /dev/null and b/target/classes/cardgame/Card.class differ diff --git a/target/classes/cardgame/Play.class b/target/classes/cardgame/Play.class new file mode 100644 index 0000000..568fcff Binary files /dev/null and b/target/classes/cardgame/Play.class differ diff --git a/target/classes/cardgame/Player1.class b/target/classes/cardgame/Player1.class new file mode 100644 index 0000000..2d15674 Binary files /dev/null and b/target/classes/cardgame/Player1.class differ diff --git a/target/classes/cardgame/Player2.class b/target/classes/cardgame/Player2.class new file mode 100644 index 0000000..3e6c1a3 Binary files /dev/null and b/target/classes/cardgame/Player2.class differ diff --git a/target/classes/cardgame/PlayerStrategy.class b/target/classes/cardgame/PlayerStrategy.class new file mode 100644 index 0000000..335d767 Binary files /dev/null and b/target/classes/cardgame/PlayerStrategy.class differ diff --git a/target/classes/cardgame/PlayerTurn.class b/target/classes/cardgame/PlayerTurn.class new file mode 100644 index 0000000..5079cdc Binary files /dev/null and b/target/classes/cardgame/PlayerTurn.class differ diff --git a/target/test-classes/cardgame/CardTest.class b/target/test-classes/cardgame/CardTest.class new file mode 100644 index 0000000..9da4ee6 Binary files /dev/null and b/target/test-classes/cardgame/CardTest.class differ