-
-
Save marzapower/60950151e112d9ca7c6d to your computer and use it in GitHub Desktop.
enum Suit { | |
case Clubs, Diamonds, Hearts, Spades | |
} | |
enum Rank { | |
case Jack, Queen, King, Ace | |
case Num(Int) | |
} | |
struct Card { | |
let suit: Suit | |
let rank: Rank | |
} | |
/* | |
* Expresses a generic rule for computing card values | |
*/ | |
struct Rule { | |
// Should return true if the rule is appliable | |
let applies : (currentCard:Card, previousCard:Card?) -> Bool | |
// Returns the computed value for the current card | |
let exec : (card:Card) -> Int | |
} | |
/* | |
* Keeps track of the total score of the hand and the last card | |
* that has been evaluated | |
*/ | |
struct Sum { | |
var total : Int | |
var lastCard : Card? | |
} | |
/* | |
* Extracts the integer value of the card's rank | |
*/ | |
func cardValue(card : Card?) -> Int { | |
if card?.rank { | |
switch(card!.rank) { | |
case .Ace : return 1 | |
case .King : return 13 | |
case .Queen : return 12 | |
case .Jack : return 11 | |
case .Num(let value) : return value | |
} | |
} else { | |
return 0 | |
} | |
} | |
/* | |
* This rule expresses rule n. 1 for computing card values | |
*/ | |
let aces = Rule( | |
applies: { currentCard, previousCard in | |
return cardValue(currentCard) == 1 && cardValue(previousCard?) == 5 && previousCard?.suit == Suit.Diamonds | |
}, exec: { card in return 100 }) | |
/* | |
* This rule expresses rule n. 2 for computing card values | |
*/ | |
let oddity = Rule( | |
applies: { currentCard, previousCard in | |
return cardValue(currentCard)%2==1 && previousCard?.suit == Suit.Hearts | |
}, exec: { card in return cardValue(card)*2 }) | |
let rules = [aces, oddity] // More rules could be added ... if necessary | |
/* | |
* Applies a set of rules to the current card using the previous one as the reference | |
*/ | |
func applyRules(rules: [Rule], currentCard: Card, previousCard: Card?) -> Int { | |
return maxElement(rules.map{ rule in rule.applies(currentCard: currentCard, previousCard: previousCard) ? rule.exec(card: currentCard) : 0 }) | |
} | |
/* | |
* Counts the hand applying a global set of rules to its sequence of cards | |
*/ | |
func countHand(cards : [Card]) -> Int { | |
return cards.reduce(Sum(total: 0, lastCard: nil)) { sum, currentCard in | |
Sum(total: sum.total + applyRules(rules, currentCard, sum.lastCard), lastCard: currentCard) | |
}.total | |
} | |
let result = countHand([ | |
Card(suit:Suit.Hearts, rank:Rank.Ace), | |
Card(suit:Suit.Hearts, rank:Rank.Num(10)), | |
Card(suit:Suit.Hearts, rank:Rank.Num(6)), | |
Card(suit:Suit.Diamonds, rank:Rank.Num(5)), | |
Card(suit:Suit.Clubs, rank:Rank.Ace), | |
Card(suit:Suit.Diamonds, rank:Rank.Jack) | |
]) //--> 110 |
P.S. I've updated the code to make it a little more readable. I could save some space compressing the functions, but making the whole code cryptic is not my target here ...
Nice job! I like that the solution is extensible :)
Is there an option to enable the color coding so the readers can read the code easier?
I like your solution very much but there are two little errors. In both of your rules you just assume that previousCard is not nil and force-unwrapped it using the '!' - Operator. It only works with the given example because the first Card is no Ace and an even Card.
@icanzlib: I've already set up the Gist to use the Swift language. Any missing code coloring is due to Github :)
@dkra89: thank you for the suggestion. I was using a slightly different version locally and I didn't notice the error. The updated version should be fine!
This is a simple but quickly-extensible solution to the Final Challenge for the Swift Ninja Challenge.
I decided to create a structure for quickly designing rules to compute the value of a hand, so it will be sufficient to increase the
rules
array with more rules to make the whole computation more complex.By default the two rules cannot overlap, but just in case they do (if you extend the rule set, obviously), the highest scoring one will be taken into consideration.