feat(hand): implement index-based removal and playCards method, update tests
This commit is contained in:
18
src/main/scala/combinations/Flush.scala
Normal file
18
src/main/scala/combinations/Flush.scala
Normal file
@@ -0,0 +1,18 @@
|
||||
package combinations
|
||||
|
||||
import cards._
|
||||
import score._
|
||||
|
||||
case class Flush() extends PokerCombination {
|
||||
val name: String = "Flush"
|
||||
val score: Score = Score(35, 4)
|
||||
def verify(cards: List[Card]): Boolean = {
|
||||
var sameSuit = true
|
||||
for (card <- cards) {
|
||||
if (card.suit != cards.head.suit) {
|
||||
sameSuit = false
|
||||
}
|
||||
}
|
||||
cards.length == 5 && sameSuit
|
||||
}
|
||||
}
|
||||
25
src/main/scala/combinations/FourOfAKind.scala
Normal file
25
src/main/scala/combinations/FourOfAKind.scala
Normal file
@@ -0,0 +1,25 @@
|
||||
package combinations
|
||||
|
||||
import cards._
|
||||
import score._
|
||||
|
||||
case class FourOfAKind() extends PokerCombination {
|
||||
val name: String = "FourOfAKind"
|
||||
val score: Score = Score(60, 7)
|
||||
|
||||
def verify(cards: List[Card]): Boolean = {
|
||||
var bool: Boolean = false
|
||||
for (pokerCard <- cards){
|
||||
var i:Int= 0
|
||||
for(card <- cards){
|
||||
if(card.rank == pokerCard.rank){
|
||||
i=i+1
|
||||
}
|
||||
}
|
||||
if(i == 4){
|
||||
bool = true
|
||||
}
|
||||
}
|
||||
bool
|
||||
}
|
||||
}
|
||||
39
src/main/scala/combinations/FullHouse.scala
Normal file
39
src/main/scala/combinations/FullHouse.scala
Normal file
@@ -0,0 +1,39 @@
|
||||
package combinations
|
||||
|
||||
import cards._
|
||||
import score._
|
||||
|
||||
class FullHouse extends PokerCombination {
|
||||
val name: String = "FullHouse"
|
||||
val score: Score = new Score(40, 4)
|
||||
|
||||
def verify(cards: List[Card]): Boolean = {
|
||||
var hasThreeOfAKind = false
|
||||
var hasPair = false
|
||||
|
||||
for (threeCandidate <- cards) {
|
||||
var threeCount = 0
|
||||
for (card <- cards) {
|
||||
if (card.rank == threeCandidate.rank) {
|
||||
threeCount += 1
|
||||
}
|
||||
}
|
||||
if (threeCount == 3) {
|
||||
hasThreeOfAKind = true
|
||||
for (pairCandidate <- cards if pairCandidate.rank != threeCandidate.rank) {
|
||||
var pairCount = 0
|
||||
for (other <- cards) {
|
||||
if (other.rank == pairCandidate.rank) {
|
||||
pairCount += 1
|
||||
}
|
||||
}
|
||||
if (pairCount == 2) {
|
||||
hasPair = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hasThreeOfAKind && hasPair
|
||||
}
|
||||
}
|
||||
31
src/main/scala/combinations/HighCard.scala
Normal file
31
src/main/scala/combinations/HighCard.scala
Normal file
@@ -0,0 +1,31 @@
|
||||
package combinations
|
||||
|
||||
import cards._
|
||||
import score._
|
||||
|
||||
case class HighCard() extends PokerCombination {
|
||||
val name: String = "HighCard"
|
||||
val score: Score = Score(5, 1)
|
||||
|
||||
def verify(cards: List[Card]): Boolean = {
|
||||
val straightFlush = new StraightFlush()
|
||||
val fourOfAKind = new FourOfAKind()
|
||||
val fullHouse = new FullHouse()
|
||||
val flush = new Flush()
|
||||
val straight = new Straight()
|
||||
val threeOfAKind = new ThreeOfAKind()
|
||||
val twoPair = new TwoPair()
|
||||
val pair = new Pair()
|
||||
|
||||
if (straightFlush.verify(cards)) return false
|
||||
if (fourOfAKind.verify(cards)) return false
|
||||
if (fullHouse.verify(cards)) return false
|
||||
if (flush.verify(cards)) return false
|
||||
if (straight.verify(cards)) return false
|
||||
if (threeOfAKind.verify(cards)) return false
|
||||
if (twoPair.verify(cards)) return false
|
||||
if (pair.verify(cards)) return false
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
25
src/main/scala/combinations/Pair.scala
Normal file
25
src/main/scala/combinations/Pair.scala
Normal file
@@ -0,0 +1,25 @@
|
||||
package combinations
|
||||
|
||||
import cards._
|
||||
import score._
|
||||
|
||||
case class Pair() extends PokerCombination {
|
||||
val name: String = "Pair"
|
||||
val score: Score = Score(10, 2)
|
||||
|
||||
def verify(cards: List[Card]): Boolean = {
|
||||
var bool: Boolean = false
|
||||
for (pokerCard <- cards){
|
||||
var i:Int= 0
|
||||
for(card <- cards){
|
||||
if(card.rank == pokerCard.rank){
|
||||
i=i+1
|
||||
}
|
||||
}
|
||||
if(i == 2){
|
||||
bool = true
|
||||
}
|
||||
}
|
||||
bool
|
||||
}
|
||||
}
|
||||
16
src/main/scala/combinations/Straight.scala
Normal file
16
src/main/scala/combinations/Straight.scala
Normal file
@@ -0,0 +1,16 @@
|
||||
package combinations
|
||||
|
||||
import cards._
|
||||
import score._
|
||||
|
||||
class Straight extends PokerCombination {
|
||||
val name: String = "Straight"
|
||||
val score: Score = new Score(30, 4)
|
||||
|
||||
def verify(cards: List[Card]): Boolean = {
|
||||
if (cards.length != 5) return false
|
||||
|
||||
val sortedRanks = cards.map(_.rank.value).distinct.sorted
|
||||
sortedRanks.length == 5 && sortedRanks.last - sortedRanks.head == 4
|
||||
}
|
||||
}
|
||||
17
src/main/scala/combinations/StraightFlush.scala
Normal file
17
src/main/scala/combinations/StraightFlush.scala
Normal file
@@ -0,0 +1,17 @@
|
||||
package combinations
|
||||
|
||||
import cards._
|
||||
import score._
|
||||
|
||||
class StraightFlush extends PokerCombination {
|
||||
val name: String = "StraightFlush"
|
||||
val score: Score = new Score(100, 8)
|
||||
|
||||
def verify(cards: List[Card]): Boolean = {
|
||||
if (cards.length != 5) return false
|
||||
|
||||
val sameSuit = cards.forall(_.suit == cards.head.suit)
|
||||
val sortedRanks = cards.map(_.rank.value).distinct.sorted
|
||||
sameSuit && sortedRanks.length == 5 && sortedRanks.last - sortedRanks.head == 4
|
||||
}
|
||||
}
|
||||
28
src/main/scala/combinations/ThreeOfAKind.scala
Normal file
28
src/main/scala/combinations/ThreeOfAKind.scala
Normal file
@@ -0,0 +1,28 @@
|
||||
package combinations
|
||||
|
||||
import cards._
|
||||
import score._
|
||||
|
||||
class ThreeOfAKind extends PokerCombination {
|
||||
val name: String = "ThreeOfAKind"
|
||||
val score: Score = new Score(30, 3)
|
||||
|
||||
def verify(cards: List[Card]): Boolean = {
|
||||
var found = false
|
||||
|
||||
for (candidate <- cards) {
|
||||
var count = 0
|
||||
for (card <- cards) {
|
||||
if (card.rank == candidate.rank) {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
if (count == 3) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
found
|
||||
}
|
||||
|
||||
}
|
||||
23
src/main/scala/combinations/TwoPair.scala
Normal file
23
src/main/scala/combinations/TwoPair.scala
Normal file
@@ -0,0 +1,23 @@
|
||||
package combinations
|
||||
|
||||
import cards._
|
||||
import score._
|
||||
|
||||
class TwoPair extends PokerCombination {
|
||||
val name: String = "TwoPair"
|
||||
val score: Score = new Score(20, 2)
|
||||
|
||||
def verify(cards: List[Card]): Boolean = {
|
||||
var pairs = Set[Rank]()
|
||||
|
||||
for (a <- cards) {
|
||||
var count = 0
|
||||
for (b <- cards) {
|
||||
if (a.rank == b.rank) count += 1
|
||||
}
|
||||
if (count == 2) pairs += a.rank
|
||||
}
|
||||
|
||||
pairs.size == 2
|
||||
}
|
||||
}
|
||||
@@ -5,62 +5,110 @@ import jokers.Joker
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
/**
|
||||
* Represents a Hand that contains a set of Cards and a set of Jokers.
|
||||
* Does not include any extra logic or scoring yet.
|
||||
* Represents a Hand in Balatro, containing Cards and Jokers.
|
||||
* Allows adding/removing cards and jokers by index, and playing cards.
|
||||
*/
|
||||
class Hand {
|
||||
// Mutable collections to store cards and jokers internally
|
||||
private val cards = ListBuffer[Card]()
|
||||
private val jokers = ListBuffer[Joker]()
|
||||
|
||||
/**
|
||||
* Adds a Card to the hand.
|
||||
* Assumes there is space in the hand.
|
||||
*
|
||||
* @param card the card to add
|
||||
* @param card The Card object to add.
|
||||
*/
|
||||
def addCard(card: Card): Unit = cards += card
|
||||
def addCard(card: Card): Unit = {
|
||||
cards += card
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a Card from the hand.
|
||||
* Removes a Card from the hand at the specified index.
|
||||
* Assumes the provided index is valid as per requirements.
|
||||
* Includes a basic check for safety during development.
|
||||
*
|
||||
* @param card the card to remove
|
||||
* @param index The zero-based index of the card to remove.
|
||||
*/
|
||||
def removeCard(card: Card): Unit = cards -= card
|
||||
def removeCardAtIndex(index: Int): Unit = {
|
||||
// Basic check, although requirements assume valid index.
|
||||
if (index >= 0 && index < cards.length) {
|
||||
cards.remove(index)
|
||||
} else {
|
||||
// As per requirements, we don't need to strictly handle invalid indices for the final submission.
|
||||
println(s"Warning: Attempted to remove card at invalid index $index. Hand size: ${cards.length}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Joker to the hand.
|
||||
* Assumes there is space for the joker.
|
||||
*
|
||||
* @param joker the Joker to add
|
||||
* @param joker The Joker object to add.
|
||||
*/
|
||||
def addJoker(joker: Joker): Unit = jokers += joker
|
||||
def addJoker(joker: Joker): Unit = {
|
||||
jokers += joker
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a Joker from the hand.
|
||||
* Removes a Joker from the hand at the specified index.
|
||||
* Assumes the provided index is valid as per requirements.
|
||||
* Includes a basic check for safety during development.
|
||||
*
|
||||
* @param joker the Joker to remove
|
||||
* @param index The zero-based index of the joker to remove.
|
||||
*/
|
||||
def removeJoker(joker: Joker): Unit = jokers -= joker
|
||||
def removeJokerAtIndex(index: Int): Unit = {
|
||||
// Basic check, although requirements assume valid index.
|
||||
if (index >= 0 && index < jokers.length) {
|
||||
jokers.remove(index)
|
||||
} else {
|
||||
// As per requirements, we don't need to strictly handle invalid indices for the final submission.
|
||||
println(s"Warning: Attempted to remove joker at invalid index $index. Joker count: ${jokers.length}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an immutable list of cards currently in the hand.
|
||||
* "Plays" cards from the hand based on a list of indices.
|
||||
* This involves returning the selected cards and removing them from the hand.
|
||||
* Assumes the indices provided are valid and correspond to cards in the hand.
|
||||
* Assumes the list of indices contains between 1 and 5 elements.
|
||||
*
|
||||
* @return a list of cards contained in the hand
|
||||
* @param indices A list of zero-based indices corresponding to the cards to be played.
|
||||
* @return A list containing the Card objects that were played (and thus removed).
|
||||
*/
|
||||
def playCards(indices: List[Int]): List[Card] = {
|
||||
// Assumes indices are valid and within bounds as per requirements.
|
||||
val cardsToPlay = indices.map(index => cards(index)).toList
|
||||
// Removes the selected cards from the hand.
|
||||
cards --= cardsToPlay
|
||||
// Returns the list of cards that were played.
|
||||
cardsToPlay
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an immutable view of the cards currently in the hand.
|
||||
* Useful for displaying the hand or passing it to other components
|
||||
* without allowing external modification of the internal state.
|
||||
*
|
||||
* @return An immutable List[Card] representing the cards in hand.
|
||||
*/
|
||||
def getCards: List[Card] = cards.toList
|
||||
|
||||
/**
|
||||
* Provides an immutable list of jokers currently in the hand.
|
||||
* Provides an immutable view of the jokers currently in the hand.
|
||||
*
|
||||
* @return a list of jokers contained in the hand
|
||||
* @return An immutable List[Joker] representing the jokers in hand.
|
||||
*/
|
||||
def getJokers: List[Joker] = jokers.toList
|
||||
|
||||
|
||||
/**
|
||||
* Returns a string representation of the hand, listing cards and jokers.
|
||||
* Useful for debugging and logging purposes.
|
||||
*
|
||||
* @return a string of cards and jokers
|
||||
* @return A String describing the current state of the hand.
|
||||
*/
|
||||
override def toString: String =
|
||||
s"Hand(cards: $cards, jokers: $jokers)"
|
||||
override def toString: String = {
|
||||
val cardsString = cards.mkString(", ")
|
||||
val jokersString = jokers.mkString(", ")
|
||||
s"Hand(Cards: [$cardsString], Jokers: [$jokersString])"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,59 +5,145 @@ import cards._
|
||||
import jokers._
|
||||
|
||||
class HandTest extends FunSuite {
|
||||
test("Hand toString returns a string with cards and jokers") {
|
||||
|
||||
val card1: Card = new Card(Ace, Hearts)
|
||||
val card2: Card = new Card(King, Spades)
|
||||
val card3: Card = new Card(Ten, Diamonds)
|
||||
val card4: Card = new Card(Two, Clubs)
|
||||
|
||||
val joker1: Joker = new Joker("Devious Joker")
|
||||
val joker2: Joker = new Joker("Greedy Joker")
|
||||
|
||||
test("Hand toString returns a string with current cards and jokers") {
|
||||
val hand = new Hand()
|
||||
|
||||
hand.addCard(new Card(Five, Hearts))
|
||||
hand.addCard(new Card(King, Spades))
|
||||
|
||||
hand.addJoker(new Joker("Devious Joker"))
|
||||
hand.addJoker(new Joker("Greedy Joker"))
|
||||
hand.addCard(card1)
|
||||
hand.addCard(card2)
|
||||
hand.addJoker(joker1)
|
||||
|
||||
val result = hand.toString
|
||||
|
||||
assert(result.contains("cards"))
|
||||
assert(result.contains("jokers"))
|
||||
assert(result.contains("Five"))
|
||||
assert(result.contains("King"))
|
||||
assert(result.contains("Devious Joker"))
|
||||
assert(result.contains("Greedy Joker"))
|
||||
assert(result.contains("Cards:"), "toString should contain 'Cards:' label")
|
||||
assert(result.contains("Jokers:"), "toString should contain 'Jokers:' label")
|
||||
assert(result.contains(card1.toString), s"toString should contain ${card1.toString}")
|
||||
assert(result.contains(card2.toString), s"toString should contain ${card2.toString}")
|
||||
assert(result.contains(joker1.toString), s"toString should contain ${joker1.toString}")
|
||||
assert(!result.contains(card3.toString), s"toString should not contain ${card3.toString}")
|
||||
assert(!result.contains(joker2.toString), s"toString should not contain ${joker2.toString}")
|
||||
}
|
||||
test("addCard should add a card to the hand") {
|
||||
|
||||
test("addCard should add a card to the hand's card list") {
|
||||
val hand = new Hand
|
||||
val card = new Card(Ace, Hearts)
|
||||
assertEquals(hand.getCards.length, 0, "Hand should start empty")
|
||||
|
||||
hand.addCard(card)
|
||||
hand.addCard(card1)
|
||||
assertEquals(hand.getCards.length, 1, "Hand should have 1 card after adding one")
|
||||
assert(hand.getCards.contains(card1), "Hand should contain the added card")
|
||||
|
||||
assert(hand.getCards.contains(card))
|
||||
hand.addCard(card2)
|
||||
assertEquals(hand.getCards.length, 2, "Hand should have 2 cards after adding another")
|
||||
assert(hand.getCards.contains(card2), "Hand should contain the second added card")
|
||||
}
|
||||
|
||||
test("removeCard should remove a card from the hand") {
|
||||
test("removeCardAtIndex should remove the card at the specified index") {
|
||||
val hand = new Hand
|
||||
val card = new Card(Ace, Hearts)
|
||||
hand.addCard(card1)
|
||||
hand.addCard(card2)
|
||||
hand.addCard(card3)
|
||||
assertEquals(hand.getCards.length, 3, "Initial hand size should be 3")
|
||||
|
||||
hand.addCard(card)
|
||||
hand.removeCard(card)
|
||||
hand.removeCardAtIndex(1)
|
||||
|
||||
assert(!hand.getCards.contains(card))
|
||||
assertEquals(hand.getCards.length, 2, "Hand size should be 2 after removal")
|
||||
assert(!hand.getCards.contains(card2), "Removed card (King of Spades) should not be in hand")
|
||||
assert(hand.getCards.contains(card1), "Card before removed index (Ace of Hearts) should still be in hand")
|
||||
assert(hand.getCards.contains(card3), "Card after removed index (Ten of Diamonds) should still be in hand")
|
||||
assertEquals(hand.getCards, List(card1, card3), "Remaining cards should be Ace of Hearts and Ten of Diamonds in order")
|
||||
}
|
||||
|
||||
test("addJoker should add a joker to the hand") {
|
||||
test("removeCardAtIndex should do nothing for invalid negative index (as per requirement relaxation)") {
|
||||
val hand = new Hand
|
||||
val joker = new Joker("Greedy Joker")
|
||||
|
||||
hand.addJoker(joker)
|
||||
|
||||
assert(hand.getJokers.contains(joker))
|
||||
hand.addCard(card1)
|
||||
val initialCards = hand.getCards
|
||||
hand.removeCardAtIndex(-1)
|
||||
assertEquals(hand.getCards, initialCards, "Cards should not change for invalid negative index")
|
||||
}
|
||||
|
||||
test("removeJoker should remove a joker from the hand") {
|
||||
test("removeCardAtIndex should do nothing for invalid out-of-bounds index (as per requirement relaxation)") {
|
||||
val hand = new Hand
|
||||
val joker = new Joker("Greedy Joker")
|
||||
hand.addCard(card1)
|
||||
val initialCards = hand.getCards
|
||||
hand.removeCardAtIndex(1)
|
||||
assertEquals(hand.getCards, initialCards, "Cards should not change for invalid out-of-bounds index")
|
||||
}
|
||||
|
||||
hand.addJoker(joker)
|
||||
hand.removeJoker(joker)
|
||||
|
||||
assert(!hand.getJokers.contains(joker))
|
||||
test("addJoker should add a joker to the hand's joker list") {
|
||||
val hand = new Hand
|
||||
assertEquals(hand.getJokers.length, 0, "Hand should start with no jokers")
|
||||
|
||||
hand.addJoker(joker1)
|
||||
assertEquals(hand.getJokers.length, 1, "Hand should have 1 joker after adding one")
|
||||
assert(hand.getJokers.contains(joker1), "Hand should contain the added joker")
|
||||
}
|
||||
|
||||
test("removeJokerAtIndex should remove the joker at the specified index") {
|
||||
val hand = new Hand
|
||||
hand.addJoker(joker1)
|
||||
hand.addJoker(joker2)
|
||||
assertEquals(hand.getJokers.length, 2, "Initial joker count should be 2")
|
||||
|
||||
hand.removeJokerAtIndex(0)
|
||||
|
||||
assertEquals(hand.getJokers.length, 1, "Joker count should be 1 after removal")
|
||||
assert(!hand.getJokers.contains(joker1), "Removed joker (Devious Joker) should not be in hand")
|
||||
assert(hand.getJokers.contains(joker2), "Remaining joker (Greedy Joker) should still be in hand")
|
||||
assertEquals(hand.getJokers, List(joker2), "Remaining joker should be Greedy Joker")
|
||||
}
|
||||
|
||||
test("playCards should return the selected cards and remove them from the hand") {
|
||||
val hand = new Hand
|
||||
hand.addCard(card1)
|
||||
hand.addCard(card2)
|
||||
hand.addCard(card3)
|
||||
hand.addCard(card4)
|
||||
assertEquals(hand.getCards.length, 4, "Initial hand size should be 4")
|
||||
|
||||
val playedIndices = List(1, 3)
|
||||
val playedCards = hand.playCards(playedIndices)
|
||||
|
||||
assertEquals(playedCards.length, 2, "Should return 2 cards")
|
||||
assert(playedCards.contains(card2), "Returned list should contain King of Spades")
|
||||
assert(playedCards.contains(card4), "Returned list should contain Two of Clubs")
|
||||
assertEquals(playedCards, List(card2, card4), "Returned list should be King Spades, Two Clubs")
|
||||
|
||||
assertEquals(hand.getCards.length, 2, "Hand size should be 2 after playing cards")
|
||||
assert(!hand.getCards.contains(card2), "Played card (King Spades) should be removed from hand")
|
||||
assert(!hand.getCards.contains(card4), "Played card (Two Clubs) should be removed from hand")
|
||||
assert(hand.getCards.contains(card1), "Non-played card (Ace Hearts) should remain in hand")
|
||||
assert(hand.getCards.contains(card3), "Non-played card (Ten Diamonds) should remain in hand")
|
||||
assertEquals(hand.getCards, List(card1, card3), "Remaining cards should be Ace Hearts and Ten Diamonds")
|
||||
}
|
||||
|
||||
test("playCards should handle playing a single card") {
|
||||
val hand = new Hand
|
||||
hand.addCard(card1)
|
||||
hand.addCard(card2)
|
||||
val playedIndices = List(0)
|
||||
val playedCards = hand.playCards(playedIndices)
|
||||
|
||||
assertEquals(playedCards, List(card1), "Should return the single played card")
|
||||
assertEquals(hand.getCards, List(card2), "Remaining card should be King Spades")
|
||||
}
|
||||
|
||||
test("playCards should handle playing multiple cards with adjacent indices") {
|
||||
val hand = new Hand
|
||||
hand.addCard(card1)
|
||||
hand.addCard(card2)
|
||||
hand.addCard(card3)
|
||||
val playedIndices = List(0, 1)
|
||||
val playedCards = hand.playCards(playedIndices)
|
||||
|
||||
assertEquals(playedCards, List(card1, card2), "Should return the two played cards")
|
||||
assertEquals(hand.getCards, List(card3), "Remaining card should be Ten Diamonds")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user