Beat Your Grandma at Cards - Chapter 2 - Modelling Cards
If we’re serious about beating a Michigan grandma, we need our software to have a firm grasp of Euchre concepts. Fortunately, modelling card games can be a fun exercise. The scope is usually small enough that a constrained and elegant set of classes and functions feels like it’s within reach.
In this article, we’ll establish these fundamental building blocks of Euchre, and we’ll lay the foundations of a visualizer for seeing the fruits of our AI labors.
Suits and Values
The core components of many playing card games are suits and values:
I mentioned in the previous article that Euchre only uses nine through ace, so there’s no reason to include the other values. There’s a few helpful features that I like to associate with suits and values, and dart has a nice feature called “extensions” that can be used for that purpose.
The first things I like to have are for printing nice strings for suits and values:
This way, I can write Suit.spades.shortName, and it will return ♠.
In the suit extension, I also like to include the notion of sister suits (suits of the same color), and opposite suits (suits of a different color). Sister suits are important to Euchre because the left bauer (the second best card in the game), is the jack of the sister suit of trump. Opposite suits are useful for other purposes like ordering cards (for, say, displaying somebody’s hand).
Cards and Decks
Now we can model an actual card. I’ll call it PlayingCard to avoid conflicts with material’s Card widget:
This is fairly straightforward, with the exception of the weird getSuit method. This is, unfortunately, a necessary piece of the Euchre puzzle, because the left bauer’s suit is always considered to be trump, even though it’s actual suit is the sister suit of trump. For example, if trump is ♥, that means the left bauer is J♦, but in all game logic, the J♦ needs to be treated like it’s a ♥, so we’ll use this method whenever we want to look at a card’s suit.
Next, let’s do a Deck:
Nothing fancy here, but it’ll do. We don’t actually need a “Deck” class or anything, as Dart’s List provides most functions you’d want.
Okay, now let’s finally put some stuff on a screen!
It seems reasonable to start with rendering cards. Let’s make a simple white card that displays the card value in the center:
Toss one of those into the main file that flutter generates when you create a new app like so:
And you get something that looks like this:
Let’s create a widget to render the back of a card as well. After all, you can’t see your opponents cards, right? This one’s pretty simple, it’s basically the same as the other card, but it has an inset rounded blue rectangle:
And finally, I’d like a widget to represent an empty space where a card might go. This will come in handy eventually:
So here’s a summary of all of our cards:
And lastly, for this chapter, I’d like the ability to render stacks of cards. This can be accomplished with flutter’s Stack widget. Stack allows you to overlay widgets on top of one another and precisely position them within the Stack’s bounds. We’ll provide our stack with the overlap amount (where 1.0 means the cards are exactly on top of each other), the list of card widgets, and the cardSize so that the stack can accurately size itself. Here’s what it looks like:
And if we put a few of these into our main, it looks like this:
Alright, we’ve got some simple cards rendering. In the next chapter, we’ll use these cards to build out a basic UI for visualizing Euchre games. Soon enough, we’ll be armed with the necessary infrastructure to tackle the AI. Watch out grandma!