Yes I have checked the very similarly titled question but the answer given is not as helpful to me as I am new to Scala and am having trouble understanding it.
I'm writing some functions that check a list of cards and return a score based on the result of the list. Technically, it checks a list of groups of cards, however I am simplifying the code for the purposes of this question.
Now, I want these functions to be extensible to different types of scoring. For instance, if all the cards are Hearts, then we may give them 1 point. However, in another ruleset, it may give 3 points.
I have a points wrapper that translates to a final score. This means that a type of point may translate to a different final score than another type of point. The purpose here is to allow you to customise the scoring and play the card game in a slightly different way.
You will see in the sample code below, but I end up getting a lot of repetition in my method declarations, namely having to write [T <: HandPoints[T]]
over and over again.
All of the def
methods have been written in an object
, so I cannot add the type parameter to the class.
I imagine there's probably a neat solution to extract these methods outside of the class, but I want the methods that check the cards to not be repeated, so it makes a lot of sense to me to have them declared statically in an object
Here is the HandPoints trait:
trait HandPoints[T] {
def toHandScore: HandScore
def zero: T
def add(that: T): T
}
case class RegularPoint(points: Int) extends HandPoints[RegularPoint] {
override def toHandScore: HandScore = HandScore(points)
override def zero: RegularPoint = RegularPoint(0)
override def add(that: RegularPoint): RegularPoint = RegularPoint(points + that.points)
}
case class DoublingPoints(points: Int) extends HandPoints[DoublingPoints] {
override def toHandScore: HandScore = HandScore(points*2)
override def zero: DoublingPoints = DoublingPoints(0)
override def add(that: DoublingPoints): DoublingPoints = DoublingPoints(points + that.points)
}
case class HandScore(score: Int) {
}
Here are the functions I wrote to assess the cards
trait Card {
def getValue: Int
def getSuit: String
}
def scored[T <: HandPoints[T]](score: T)(boolean: Boolean): T = {
if (boolean) score else score.zero
}
def isAllEvens[T <: HandPoints[T]](score: T)(cards: List[Card]): T = {
scored(score) {
cards.forall(_.getValue % 2 == 0)
}
}
def isAllReds[T <: HandPoints[T]](score: T)(cards: List[Card]): T = {
scored(score) {
cards.forall(List("HEARTS", "DIAMONDS").contains(_))
}
}
def isAllNoDuplicates[T <: HandPoints[T]](score: T)(cards: List[Card]): T = {
scored(score) {
cards.distinct == cards
}
}
val regularGameCriteria: List[List[Card] => RegularPoint] = List(
isAllEvens(RegularPoint(1)),
isAllReds(RegularPoint(3)),
isAllNoDuplicates(RegularPoint(5))
)
val beginnerGameCriteria: List[List[Card] => RegularPoint] = List(
isAllEvens(RegularPoint(1)),
isAllReds(RegularPoint(1)),
isAllNoDuplicates(RegularPoint(1))
)
val superGameCriteria: List[List[Card] => DoublingPoints] = List(
isAllEvens(DoublingPoints(1)),
isAllReds(DoublingPoints(3)),
isAllNoDuplicates(DoublingPoints(5))
)
def countScore[T <: HandPoints[T]](scoreList: List[List[Card] => T])(melds: List[Card]): T = {
scoreList.map(f => f(melds)).reduce((a, b) => a.add(b))
}
def regularGameScore(cards: List[Card]): RegularPoint = {
countScore(regularGameCriteria)(cards)
}
def beginnerGameScore(cards: List[Card]): RegularPoint = {
countScore(beginnerGameCriteria)(cards)
}
def superGameScore(cards: List[Card]): DoublingPoints = {
countScore(superGameCriteria)(cards)
}