Repository: vaskoz/dailycodingproblem-go (go)

Issue:
Day 238
Good morning! Here's your coding interview problem for today.

This problem was asked by MIT.

Blackjack is a two player card game whose rules are as follows:

The player and then the dealer are each given two cards.
The player can then "hit", or ask for arbitrarily many additional cards, so long as their total does not exceed 21.
The dealer must then hit if their total is 16 or lower, otherwise pass.
Finally, the two compare totals, and the one with the greatest sum not exceeding 21 is the winner.
For this problem, cards values are counted as follows: each card between 2 and 10 counts as their face value, face cards count as 10, and aces count as 1.

Given perfect knowledge of the sequence of cards in the deck, implement a blackjack solver that maximizes the player's score (that is, wins minus losses).

Pull request:
Day238
Resolves #489 .

Patch:
diff --git a/README.md b/README.md
index 09b2c82..c87a6a6 100644
--- a/README.md
+++ b/README.md
@@ -246,3 +246,4 @@ problems from
 * [Day 235](https://github.com/vaskoz/dailycodingproblem-go/issues/483)
 * [Day 236](https://github.com/vaskoz/dailycodingproblem-go/issues/485)
 * [Day 237](https://github.com/vaskoz/dailycodingproblem-go/issues/487)
+* [Day 238](https://github.com/vaskoz/dailycodingproblem-go/issues/489)
diff --git a/day238/problem.go b/day238/problem.go
new file mode 100644
index 0000000..edaf7b7
--- /dev/null
+++ b/day238/problem.go
@@ -0,0 +1,85 @@
+package day238
+
+// Suit represents the 4 suits: Hearts, Spades, Diamonds and Clubs.
+type Suit string
+
+// Rank represents the face of the card.
+type Rank string
+
+// Card represents a playing card with a Suit and Rank.
+type Card struct {
+	s Suit
+	r Rank
+}
+
+var cardValues = map[Rank]int{
+	"ace":   1,
+	"two":   2,
+	"three": 3,
+	"four":  4,
+	"five":  5,
+	"six":   6,
+	"seven": 7,
+	"eight": 8,
+	"nine":  9,
+	"ten":   10,
+	"jack":  10,
+	"queen": 10,
+	"king":  10,
+}
+
+func (c *Card) value() int {
+	return cardValues[c.r]
+}
+
+// MostPlayerWins returns the most number of possible player wins.
+// Dealer gets the first 2 cards of each hand.
+// Player gets second 2 cards.
+// Player tries every game based on drawing cards up until bust.
+// Dealer draws until greater than or equal to 17 and stops.
+func MostPlayerWins(deck []Card) int {
+	if len(deck) < 4 { // not enough cards to deal
+		return 0
+	}
+	dealer := deck[0].value() + deck[1].value()
+	player := deck[2].value()
+	var most int
+	start := 3
+	cards := 1
+	for {
+		playerHand := player
+		dealerHand := dealer
+		next := start
+		for i := 0; i < cards; i++ {
+			if next == len(deck) {
+				return 0
+			}
+			playerHand += deck[next].value()
+			next++
+		}
+		if playerHand > 21 {
+			break
+		}
+		for dealerHand < 17 {
+			if next == len(deck) {
+				return 0
+			}
+			dealerHand += deck[next].value()
+			next++
+		}
+		if dealerHand > 21 || playerHand > dealerHand {
+			most = max(most, 1+MostPlayerWins(deck[next:]))
+		} else {
+			most = max(most, MostPlayerWins(deck[next:]))
+		}
+		cards++
+	}
+	return most
+}
+
+func max(a, b int) int {
+	if a > b {
+		return a
+	}
+	return b
+}


Test patch:
diff --git a/day238/problem_test.go b/day238/problem_test.go
new file mode 100644
index 0000000..7e10a61
--- /dev/null
+++ b/day238/problem_test.go
@@ -0,0 +1,52 @@
+package day238
+
+import (
+	"testing"
+)
+
+var testcases = []struct {
+	deck     []Card
+	mostWins int
+}{
+	{
+		[]Card{
+			{"clubs", "ace"}, {"spades", "six"}, {"hearts", "queen"}, {"clubs", "eight"}, {"spades", "five"}, {"hearts", "jack"}, {"hearts", "ace"}, {"clubs", "three"}, {"diamonds", "five"}, {"clubs", "two"}, {"diamonds", "queen"}, {"diamonds", "four"}, {"hearts", "five"}, {"hearts", "six"}, {"diamonds", "jack"}, {"hearts", "seven"}, {"clubs", "five"}, {"spades", "seven"}, {"clubs", "ten"}, {"diamonds", "three"}, {"spades", "eight"}, {"diamonds", "ten"}, {"diamonds", "two"}, {"spades", "nine"}, {"spades", "two"}, {"spades", "jack"}, {"diamonds", "eight"}, {"spades", "three"}, {"spades", "four"}, {"clubs", "four"}, {"clubs", "seven"}, {"diamonds", "nine"}, {"diamonds", "seven"}, {"diamonds", "king"}, {"hearts", "three"}, {"hearts", "eight"}, {"hearts", "two"}, {"clubs", "jack"}, {"spades", "ace"}, {"spades", "king"}, {"hearts", "ten"}, {"clubs", "king"}, {"clubs", "six"}, {"diamonds", "ace"}, {"hearts", "nine"}, {"spades", "ten"}, {"spades", "queen"}, {"hearts", "four"}, {"clubs", "nine"}, {"diamonds", "six"}, {"clubs", "queen"}, {"hearts", "king"},
+		},
+		8,
+	},
+	{
+		[]Card{
+			{"clubs", "ace"}, {"clubs", "two"}, {"clubs", "king"}, {"spades", "ten"}, {"hearts", "three"}, {"hearts", "six"}, {"clubs", "eight"}, {"spades", "nine"}, {"hearts", "ace"}, {"clubs", "six"}, {"clubs", "nine"}, {"hearts", "eight"}, {"spades", "three"}, {"clubs", "three"}, {"diamonds", "jack"}, {"hearts", "queen"}, {"diamonds", "nine"}, {"diamonds", "three"}, {"hearts", "four"}, {"hearts", "seven"}, {"spades", "four"}, {"hearts", "two"}, {"spades", "jack"}, {"spades", "ace"}, {"diamonds", "four"}, {"spades", "two"}, {"diamonds", "queen"}, {"hearts", "five"}, {"diamonds", "ace"}, {"spades", "five"}, {"clubs", "queen"}, {"hearts", "ten"}, {"diamonds", "king"}, {"clubs", "five"}, {"hearts", "king"}, {"diamonds", "eight"}, {"spades", "queen"}, {"diamonds", "five"}, {"clubs", "ten"}, {"spades", "six"}, {"diamonds", "two"}, {"spades", "eight"}, {"diamonds", "seven"}, {"clubs", "jack"}, {"hearts", "jack"}, {"spades", "seven"}, {"diamonds", "six"}, {"clubs", "four"}, {"spades", "king"}, {"diamonds", "ten"}, {"hearts", "nine"}, {"clubs", "seven"},
+		},
+		5,
+	},
+	{
+		[]Card{
+			{"clubs", "four"}, {"clubs", "six"}, {"diamonds", "five"}, {"hearts", "three"}, {"clubs", "eight"}, {"spades", "ten"}, {"diamonds", "eight"}, {"hearts", "six"}, {"clubs", "two"}, {"hearts", "queen"}, {"spades", "queen"}, {"spades", "six"}, {"spades", "ace"}, {"diamonds", "ten"}, {"spades", "nine"}, {"hearts", "king"}, {"clubs", "queen"}, {"hearts", "four"}, {"diamonds", "six"}, {"spades", "four"}, {"clubs", "ace"}, {"clubs", "jack"}, {"diamonds", "nine"}, {"diamonds", "king"}, {"hearts", "five"}, {"hearts", "jack"}, {"diamonds", "three"}, {"spades", "five"}, {"clubs", "five"}, {"spades", "seven"}, {"spades", "two"}, {"hearts", "ten"}, {"spades", "king"}, {"hearts", "eight"}, {"diamonds", "two"}, {"hearts", "ace"}, {"clubs", "three"}, {"hearts", "nine"}, {"hearts", "seven"}, {"hearts", "two"}, {"spades", "jack"}, {"spades", "three"}, {"diamonds", "jack"}, {"diamonds", "ace"}, {"spades", "eight"}, {"clubs", "seven"}, {"diamonds", "four"}, {"diamonds", "queen"}, {"clubs", "ten"}, {"diamonds", "seven"}, {"clubs", "king"}, {"clubs", "nine"},
+		},
+		6,
+	},
+	{
+		[]Card{
+			{"diamonds", "five"}, {"spades", "ten"}, {"spades", "king"}, {"clubs", "queen"}, {"hearts", "ten"}, {"spades", "jack"}, {"hearts", "four"}, {"spades", "two"}, {"clubs", "seven"}, {"hearts", "eight"}, {"spades", "four"}, {"hearts", "king"}, {"hearts", "six"}, {"hearts", "nine"}, {"spades", "six"}, {"clubs", "four"}, {"diamonds", "nine"}, {"spades", "queen"}, {"diamonds", "four"}, {"hearts", "five"}, {"clubs", "two"}, {"spades", "ace"}, {"spades", "three"}, {"spades", "nine"}, {"clubs", "six"}, {"diamonds", "king"}, {"clubs", "ten"}, {"clubs", "jack"}, {"hearts", "two"}, {"hearts", "three"}, {"spades", "five"}, {"diamonds", "ten"}, {"clubs", "nine"}, {"clubs", "three"}, {"diamonds", "queen"}, {"diamonds", "seven"}, {"hearts", "jack"}, {"hearts", "ace"}, {"hearts", "seven"}, {"clubs", "ace"}, {"diamonds", "jack"}, {"spades", "eight"}, {"diamonds", "ace"}, {"hearts", "queen"}, {"diamonds", "three"}, {"spades", "seven"}, {"diamonds", "two"}, {"clubs", "king"}, {"diamonds", "eight"}, {"clubs", "five"}, {"clubs", "eight"}, {"diamonds", "six"},
+		},
+		8,
+	},
+}
+
+func TestMostPlayerWins(t *testing.T) {
+	t.Parallel()
+	for _, tc := range testcases {
+		if most := MostPlayerWins(tc.deck); most != tc.mostWins {
+			t.Errorf("Expected %v, got %v", tc.mostWins, most)
+		}
+	}
+}
+
+func BenchmarkMostPlayerWins(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		for _, tc := range testcases {
+			MostPlayerWins(tc.deck)
+		}
+	}
+}