I was ready to do some more coding but I couldn’t think of a suitable project. Another game would have been ideal, to go alongside my other coding projects (cribbage, gin rummy, chess, backgammon, boxes).
I came up with the idea of coding the card game, sevens. It’s a simple game but I didn’t really think it would be much fun playing it against the computer. So I decided to code it as an exercise in testing various playing strategies rather than a game to be played. I was hoping to come up with four strategies for a four-player game and to see how they worked out with the computer playing all four hands. Unfortunately, the game is so simple I could only think of two strategies, a dumb one where the card choice is random, and an intelligent one. I could mix it up by getting the computer to play a mix of strategies, for example 1 dumb and 3 intelligent, or 2 dumb + 2 intelligent. So that’s what I did.
What did I discover?
Each trial was played over 100,000 games (taking 6 seconds on my laptop!)
- If 4 dummies played against each other, the dummies each won 25% of games
- If 4 smarties played against each other, the smarties each won 25% of games
- If 1 dummy played against 3 smarties, the dummy won 13% of games, the smarties each won 29% of games
- If 2 dummies played against 2 smarties, the dummies each won 13% of games, the smarties each won 37% of games
- If 3 dummies played against 1 smarty, the dummies each won 17% of games, the smarty won 49% of games
Conclusion?
Although sevens is a simple card game and there’s the luck of the draw influencing who wins, it’s clear that one’s chances of winning can be significantly improved by playing the cards well. So it’s not entirely a game of chance!
The code
There aren’t that many lines of code but as usual I had issues with the logic and with finding bugs. An interesting bug that took me a long time to find, was as a result of me using the wrong format for a piece of code. See below.
I had written the following piece of code:
The wrong code
Select Case card
Case 1 - 6
direction = +1
Case 7
Return vbTrue
Case Else
direction = -1
End Select
The objective was to take different actions depending on whether the variable ‘card’ had a value between 1 and 6, or a value of 7, or any other value (actually between 8 and 13).
The bug was that ‘Case 1 – 6’ does not capture the range of values 1 to 6, but instead captures only the value -5 (ie 1-6). The result was that values between 1 and 6 were handled by the wrong piece of code, the Case Else code. This was a hard to spot bug! The correct code is on the right.
The correct code
Select Case card
Case 1 TO 6
direction = +1
Case 7
Return vbTrue
Case Else
direction = -1
End Select
The code for the two strategies
Private Function PlayACard_strategy_1(player As Integer) As Boolean
'
' Strategy-1: Choose a RANDOM playable card for the current player to play and play it.
' Return true if card played otherwise false if no card able to be played
'
Dim t As Integer
nPlayableCards = 0
Call GetPlayableCards(player)
If Testing Then Call PrintCards(player)
If Testing Then Call PrintPlayableCards()
If nPlayableCards = 0 Then
Return vbFalse
End If
t = Math.Min(CInt((nPlayableCards * Rnd()) + 1), nPlayableCards)
PlayerCards(PlayableCardsCard(t), PlayableCardsSuit(t), player) = vbFalse ' Change status to card played
If Testing Then Debug.Print("Player " & Str(player) & " randomly plays " & Str(PlayableCardsCard(t)))
Return vbTrue
End Function
Private Function PlayACard_strategy_2(player As Integer) As Boolean
'
' Strategy-2: Choose the BEST card for the current player to play and play it.
' Return true if card played otherwise false if no card able to be played
'
Dim i, card, suit As Integer
nPlayableCards = 0
Call GetPlayableCards(player)
If Testing Then Call PrintCards(player)
If Testing Then Call PrintPlayableCards()
If nPlayableCards = 0 Then
Return vbFalse
End If
For card = 1 To 7 ' Look for aces and kings, and if none then twos and queens, etc
For suit = 1 To nSuits
If PlayerCards(card, suit, player) Then ' If player has an ace (then up to 7) in this suit
For i = 1 To nPlayableCards
If PlayableCardsSuit(i) = suit And PlayableCardsCard(i) <= 7 Then ' if player has a playable card (7 or below)
PlayerCards(PlayableCardsCard(i), suit, player) = vbFalse ' Yes. Mark card as played
If Testing Then Debug.Print("Player " & Str(player) & " plays " & Str(PlayableCardsCard(i)))
Return vbTrue
End If
Next
End If
If PlayerCards(nCardsSuit + 1 - card, suit, player) Then ' If player has a king (then down to 7) in this suit
For i = 1 To nPlayableCards
If PlayableCardsSuit(i) = suit And PlayableCardsCard(i) >= 7 Then ' if player has a playable card (7 or above)
PlayerCards(PlayableCardsCard(i), suit, player) = vbFalse ' Yes. Mark card as played
If Testing Then Debug.Print("Player " & Str(player) & " plays " & Str(PlayableCardsCard(i)))
Return vbTrue
End If
Next
End If
Next
Next
MsgBox("We shouldn't ever get here!")
Return vbTrue
End Function

Have your say! (No need for any ID!)