Hearths card game in Go

Published by Dev Kordeš on 09/18/2017

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 - deck and 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 Board and 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 Card and Deck with helpful methods for a standard hearths card and a stack of cards.


Card has 3 methods Value() str, Face() str, 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() error which shuffles the deck in first phase
  • P1DealAll() error which deals deck's cards to the 4 players and ends phase 1
  • P2Trade(from int, tradedCards []deck.Card) error which 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) error which 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.Deck is summed into Board.results [4]int

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.

This website uses  Google Analytics  cookies. Beware.