Hearths card game in Go
I stripped down my old idea for an online multiplayer hearths cards game to just a simple playable hearths card game "package". And actually finished it this time.
Project on github
I separated the project into 2 parts -
hearths. Deck subpackage is just an independent package for managing a stack of cards. With methods a card deck or a player's hand might need. While hearths subpackage part contains
Player objects specifically for a game of hearths and rules for interactions between them.
I used TDD. Last time I tried doing this I wasn't doing any tests and printing all the time (had a Print method on everything). Even after getting something right, if I wanted to change the implementation or some design decisions I spent a lot of time testing that other functionality still works as intended (and debugging why they don't). So this time around I wrote some simple tests. They don't cover all scenarios, but they generally let me know when a new implementation affected something else.
Package - Deck
First I wrote the deck subpackage. It has 2 objects
Deck with helpful methods for a standard hearths card and a stack of cards.
Card has 3 methods
Comparable() int and
Valid() bool. Value() and Face() return a one-letter string, Comparable() returns default implementation for comparing 2 cards and Valid() just checks if the card is valid.
A new card is created with
NewCard("Qc") for a Queen of Clubs.
Card source on github .
Deck has a range of useful methods for drawing, inserting, finding and removing cards. It has a method for sorting by card's Comparable() and sorting by a custom function.
It is created either with
NewDeck(Card) or with
NewDefaultDeck() which creates a deck with default 52 cards, one of each.
Deck source on github .
Package - Hearths
Hearths package has a simple
Player object and a
Board with all the game information.
Player is just a holder for 2 stacks of card
Hand() deck.Deck and
Garbage() deck.Deck. Hand are cards in player's hand and garbage are cards that bring minus points.
A new player is created with
NewPlayer() Player which returns a player with no cards in hand or garbage.
Player source on github .
Board holds all the information on game's state: 4 players, a drawing deck, a playing pile, information on turn and phase and some more.
It implements getters for relevant information and
NextPlayerI() int, which returns index of player who's move it is.
But more importantly it implements:
P1ShuffleDeck() errorwhich shuffles the deck in first phase
P1DealAll() errorwhich deals deck's cards to the 4 players and ends phase 1
P2Trade(from int, tradedCards deck.Card) errorwhich takes index of player and 3 cards, to trade to the next player in phase 2. Ends phase 2 after 4 trades.
P3PutOnPile(from int, card deck.Card) errorwhich takes index of player and a card he's putting on the main playing pile. When this has been done 52 times, the game is ended and each players
Garbage deck.Deckis summed into
All Board methods are either fully completed, if it's possible on current board state, or they return an error and nothing changes.
Board source on github .
For various reasons, including stupid, but more thorough, testing I've also written very simple and dumb methods for automatic playing of the game. The methods sort the cards in hand and basically bruteforce their way into playing the correct card.
PlayPhase2player(b *Board, player int) error plays player's turn on phase 2 and
PlayPhase3player(b *Board, player int) error play's phase 3 for a specific player.
Depending on how I'm going to use this package, I might improve the self-playing methods and add more advanced in the future.
Autoplayer source on github .
Where to now
I might actually try to create a simple sockets based SPA game against the golang autoplayers some time in the future. But considering I'm pretty busy working on some other project with 2 friends, I probably won't come around to it soon.
Also I'd like to change the
Deck object to depend on some kind of
Card abstraction rather than an implementation you can't change. I'll get around to it...
It was a nice experience with completely test-based programming and I got to use some of the algorithms I've been practicing on LeetCode lately.