Coding a Euchre Game, Part 7: Total Logic
Since I’ve been concentrating on specific VB functionality, you may have noticed that the one topic I haven’t really drilled into yet is game logic, and yet it’s central to what a game is all about. Games have certainly gotten more sophisticated over the years, and yet that sophistication is largely a result of graphical and audio advances. The actual logic of games itself hasn’t changed nearly as much – I still have to exercise pretty much the same control over my NWN2 party as I did way back in Pools of Darkness, for example, lest I get blown away by friendly fire.
For card games, the logic is well-defined and ages-old… sort of. Card games can have a lot of local variations in the rules, and Euchre is no exception. I grew up playing a particular set of rules in Michigan (24 cards, plus “Stick the Dealer” if all agreed), but my wife’s family in West Virginia plays with three less cards (getting rid of all 9’s except the nine of hearts) and always advances the deal if trump isn’t called in two bidding rounds. Obviously, if I wanted my game to be successful for both families, I was going to have to accommodate all of these options. Further research into Euchre showed me that there are all sorts of variations of Euchre, not only in the U.S. but around the world. I settled for the two choices I was familiar with (24 vs. 21 cards, “Stick the Dealer” vs. new hand), and added a couple that sounded interesting to me (“SuperEuchre,” which simply changes the number of points awarded if the defending team takes all of the trumps, and “Quiet Dealer,” which forces a dealer’s partner to play alone if he/she chooses the trump suit) – the latter options would be easy to implement by minor modifications to the logic.
For Euchre, there are two interesting areas of logic: bidding and playing, both as concern the AI players. (I could leverage that logic to “tutor” the human player as well, and maybe I’ll do so in a future version.) As you might guess, in either case the AI needs to understand the value of a given hand or card – I’ll refer to this as the “score,” though it has nothing to do with the actual scoring players receive when winning. The ordering of the cards for scoring is quite straightforward, but the relative distances between them can vary. Let me explain…
In Euchre, the ordering of cards (from most powerful to least powerful) goes as follows: the Jack of Trumps (Right Bower), the Jack of the other same-color suit (Left Bower), the Ace of Trumps, the King of Trumps, the Queen of Trumps, the Ten of Trumps, and (if it exists) the Nine of Trumps. These are followed by the normal ordering (A, K, Q, J, 10, 9) of any other non-trump suit. So, I could start out with an enum defining this ordering as follows:
Public Enum Values
NineNoTrump = 1
TenNoTrump = 2
JackNoTrump = 3
QueenNoTrump = 4
KingNoTrump = 5
AceNoTrump = 6
NineTrump = 7
TenTrump = 8
QueenTrump = 9
KingTrump = 10
AceTrump = 11
LeftBower = 12
RightBower = 13
End Enum
And that would seem to be all right. However, let’s consider the case where Bob is holding three non-trump aces, the queen trump, and the 10 trump(A A A QT 10T) and Alice is holding both bowers and the nine of trumps, in addition to a couple of non-trump nines (RT LT 9T 9 9). Bob “hand value” is 35, and Alice’s is 34. Bob would seem to have a better hand, right? Wrong! The goal for the person who declares trump is to win three of the five “tricks” (hands). Both of Bob’s trumps would be neutralized by Alice’s superior trump cards, and Alice would still have one trump left to neutralize any of the aces in Bob’s hand. The only missing trumps (AT and KT), even if not owned by Alice’s partner, would also be covered by Alice’s bowers, making it highly certain that Alice would win her needed tricks. (Alice, of course, doesn’t know Bob’s cards, and so might still be reluctant to make a bid in case he’s got all of the rest of the trump.) In fact, players of Euchre are lucky if they can take a trick without using a trump or a non-trump ace, since the person who declares trump is likely to be deficient in non-trump cards, so cards other than those should be significantly lower in score.
The AI players, of course, are forbidden from knowing what each others’ cards are (i.e., no peeking!), so I couldn’t very well implement logic that would involve figuring out if high trumps would neutralize specific cards, etc. So, after some thinking about the probabilities involved, I revised the enum be more reflective of those probabilities. This is where testing is important – the game has to “feel right” when played. The user shouldn’t be thinking “why in the world did the AI choose trump with that hand?” Based on probability, testplay, and a strong familiarity with the game, I ended up with the following enum:
Public Enum Values
NineNoTrump = 1
TenNoTrump = 2
J ackNoTrump = 3
QueenNoTrump = 4
KingNoTrump = 5
AceNoTrump = 10
NineTrump = 12
TenTrump = 15
QueenTrump = 20
KingTrump = 25
AceTrump = 30
LeftBower = 31
RightBower = 35
NoValue = -1
End Enum
and in this case, Bob would have a score of 65 and Alice would have a score of 80, which is much more reflective of the reality of the situation.
Now, we’ll need code to actually leverage this. Each AI will need to figure out what their hand score would be for a given trump suit. First, we need to deal with the incongruous case of the Jack of the same-color suit (which I’ll refer to as the Bower suit, even though that’s not technically accurate) being treated as trump suit:
Public Shared Function GetBowerSuit(ByVal Trump As Suits) As Suits
Select Case Trump
Case Suits.Hearts
Return Suits.Diamonds
Case Suits.Diamonds
Return Suits.Hearts
Case Suits.Clubs
Return Suits.Spades
Case Suits.Spades
Return Suits.Clubs
End Select
End Function
and then get the value of a hand (per-card) based on the given potential trump suit:
Public Function GetValue(ByVal Trump As Suits) As Values
If Suit = Trump Then
Select Case Rank
Case Ranks.Nine
Return Values.NineTrump
Case Ranks.Ten
Return Values.TenTrump
Case Ranks.Queen
Return Values.QueenTrump
Case Ranks.King
Return Values.KingTrump
Case Ranks.Ace
Return Values.AceTrump
Case Ranks.Jack
Return Values.RightBower
End Select
ElseIf Suit = GetBowerSuit(Trump) AndAlso Rank = Ranks.Jack Then
Return Values.LeftBower
Else
Select Case Rank
Case Ranks.Nine
Return Values.NineNoTrump
Case Ranks.Ten
Return Values.TenNoTrump
Case Ranks.Jack
0 comments
Be the first to start the discussion.