Skip to content

Instantly share code, notes, and snippets.

@larrasket
Created June 7, 2026 14:39
Show Gist options
  • Select an option

  • Save larrasket/266ebfebc1414fb3d2b647e819817ee0 to your computer and use it in GitHub Desktop.

Select an option

Save larrasket/266ebfebc1414fb3d2b647e819817ee0 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"math/rand"
"sort"
"strings"
"time"
)
var (
SUITS = []string{"♠", "♦", "♥", "♣"}
RANKS = []string{"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"}
RANK_VALUE = func() map[string]int {
m := make(map[string]int, len(RANKS))
for i, r := range RANKS {
m[r] = i
}
return m
}()
)
// Card represents a standard playing card
type Card struct {
Rank string
Suit string
}
// String returns the string representation of a card
func (c Card) String() string {
return fmt.Sprintf("%s%s", c.Rank, c.Suit)
}
type Shuffler interface {
Shuffle(n int, swap func(i, j int))
}
// Deck represents a mutable deck of cards
type Deck struct {
shuffler Shuffler
Cards []Card
}
// NewDeck creates a new deck of 52 cards
func NewDeck(shuffler Shuffler) *Deck {
deck := &Deck{Cards: make([]Card, 0, 52), shuffler: shuffler}
for _, rank := range RANKS {
for _, suit := range SUITS {
deck.Cards = append(deck.Cards, Card{Rank: rank, Suit: suit})
}
}
return deck
}
// String returns the string representation of the deck
func (d *Deck) String() string {
cardStrings := make([]string, len(d.Cards))
for i, card := range d.Cards {
cardStrings[i] = card.String()
}
return strings.Join(cardStrings, " ")
}
// Draw removes and returns the first card from the deck
func (d *Deck) Draw() Card {
if len(d.Cards) == 0 {
panic("Cannot draw from empty deck")
}
card := d.Cards[0]
d.Cards = d.Cards[1:]
return card
}
// Shuffle randomizes the order of cards in the deck
func (d *Deck) Shuffle() {
d.shuffler.Shuffle(len(d.Cards), func(i, j int) {
d.Cards[i], d.Cards[j] = d.Cards[j], d.Cards[i]
})
}
// Player represents a player in the game
type Player struct {
Name string
Hand []Card
}
// NewPlayer creates a new player with the given name
func NewPlayer(name string) *Player {
return &Player{
Name: name,
Hand: make([]Card, 0),
}
}
// String returns the string representation of the player
func (p *Player) String() string {
return p.Name
}
func (p *Player) SortedHand() []Card {
bySuit := make(map[string][]Card)
suitOrder := []string{}
for _, c := range p.Hand {
if _, ok := bySuit[c.Suit]; !ok {
suitOrder = append(suitOrder, c.Suit)
}
bySuit[c.Suit] = append(bySuit[c.Suit], c)
}
for _, suit := range suitOrder {
cards := bySuit[suit]
sort.Slice(cards, func(i, j int) bool {
return RANK_VALUE[cards[i].Rank] < RANK_VALUE[cards[j].Rank]
})
}
out := make([]Card, 0, len(p.Hand))
for _, suit := range suitOrder {
out = append(out, bySuit[suit]...)
}
return out
}
func Deal(deck *Deck, players []*Player) {
i := 0
for len(deck.Cards) > 0 {
player := players[i%len(players)]
player.Hand = append(player.Hand, deck.Draw())
fmt.Printf("Player (%s) drew a card\n", player.Name)
i++
}
}
func handString(cards []Card) string {
parts := make([]string, len(cards))
for i, c := range cards {
parts[i] = c.String()
}
return "[" + strings.Join(parts, ", ") + "]"
}
func playRound(players []*Player, starterIdx, roundNum int) (int, int) {
starter := players[starterIdx]
fmt.Printf("\nRound %d n", roundNum)
fmt.Printf("Player %s starts the round\n", starter.Name)
leadCard := starter.Hand[0]
starter.Hand = starter.Hand[1:]
fmt.Printf("Player %s played %s\n", starter.Name, leadCard)
type play struct {
idx int
card Card
}
plays := []play{{starterIdx, leadCard}}
leadSuit := leadCard.Suit
gameWinnerIdx := -1
if len(starter.Hand) == 0 {
gameWinnerIdx = starterIdx
}
for offset := 1; offset < len(players); offset++ {
pi := (starterIdx + offset) % len(players)
p := players[pi]
pick := -1
for i, c := range p.Hand {
if c.Suit == leadSuit {
pick = i
break
}
}
if pick == -1 {
pick = 0
}
card := p.Hand[pick]
p.Hand = append(p.Hand[:pick], p.Hand[pick+1:]...)
fmt.Printf("Player %s played %s\n", p.Name, card)
plays = append(plays, play{pi, card})
if gameWinnerIdx == -1 && len(p.Hand) == 0 {
gameWinnerIdx = pi
}
}
winnerIdx := starterIdx
winnerRank := -1
for _, pl := range plays {
if pl.card.Suit != leadSuit {
continue
}
if RANK_VALUE[pl.card.Rank] > winnerRank {
winnerRank = RANK_VALUE[pl.card.Rank]
winnerIdx = pl.idx
}
}
fmt.Printf("Winner of round %d: Player %s\n", roundNum, players[winnerIdx].Name)
return winnerIdx, gameWinnerIdx
}
func PlayGame(players []*Player, rng *rand.Rand) {
starterIdx := rng.Intn(len(players))
for roundNum := 1; ; roundNum++ {
var gameWinnerIdx int
starterIdx, gameWinnerIdx = playRound(players, starterIdx, roundNum)
if gameWinnerIdx != -1 {
fmt.Printf("\n🎉 Game Over! Player %s wins the game!\n", players[gameWinnerIdx].Name)
return
}
}
}
func main() {
players := []*Player{
NewPlayer("A"),
NewPlayer("B"),
NewPlayer("C"),
NewPlayer("D"),
}
shuffler := rand.New(rand.NewSource(time.Now().UnixNano()))
shuffler.Shuffle(10, func(i, j int) {})
deck := NewDeck(shuffler)
deck.Shuffle()
fmt.Println("Here's the shuffled deck:")
fmt.Println(deck)
fmt.Println()
Deal(deck, players)
fmt.Println("\nHands:")
for _, p := range players {
fmt.Printf("Player %s: %s\n", p.Name, handString(p.SortedHand()))
}
PlayGame(players, shuffler)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment