pokerstove

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 17, 2026 License: BSD-3-Clause Imports: 6 Imported by: 0

README

pokerstove-go

Native Go PokerStove core for poker applications. This package is a Go port with C++ PokerStove parity tests; it is not a runtime wrapper around the C++ library or command-line tools.

Install

Requires Go 1.22 or newer.

go get github.com/kevinmcmahon/pokerstove-go
import pokerstove "github.com/kevinmcmahon/pokerstove-go"

No separate package publishing step is required for Go modules. Once this repository is public, users can add it with go get; pkg.go.dev will index the module from the public repository.

Tagged releases use Go module semantic versions such as v0.1.0. Until the first tag exists, go get resolves to a pseudo-version from the default branch. For reproducible builds, depend on a tagged version once releases begin. Future v2 and later releases require the Go module path to include the major version suffix, such as /v2.

Quick Start

hand, err := pokerstove.ParseCardSet("AcAs")
if err != nil {
	return err
}
other, err := pokerstove.ParseCardSet("KhQh")
if err != nil {
	return err
}
board, err := pokerstove.ParseCardSet("2c3d4h5s9c")
if err != nil {
	return err
}

evaluator, err := pokerstove.NewEvaluator("h")
if err != nil {
	return err
}
metadata := evaluator.Metadata() // Hold'em: 2-card hands, 5-card boards.

holdem, err := pokerstove.EvaluateHoldem(hand, board)
if err != nil {
	return err
}
evaluation, err := evaluator.Evaluate(hand, board)
if err != nil {
	return err
}
comparison, err := evaluator.Compare(hand, other, board)
if err != nil {
	return err
}
_ = metadata
_ = holdem
_ = evaluation
_ = comparison

result, err := pokerstove.EvaluateShowdown(pokerstove.ShowdownRequest{
	Game:  "h",
	Board: "2c3d4h5s9c",
	Hands: []string{"AcAs", "KhQh"},
})
if err != nil {
	return err
}
_ = result

ParseCardSet accepts two-character card tokens with optional whitespace. NewEvaluator accepts PokerStove game identifiers and common aliases such as h, o, o8, and omaha/8.

Guides

Current Scope

The package supports direct evaluation and comparison for the copied C++ parity rows across Hold'em, Omaha variants, Omaha/8 variants, Stud/8, Stud high/low no qualifier, lowball, three-card poker, razz, draw, and badugi ordering cases. Showdown oracle coverage is documented separately in the compatibility matrix. Showdown evaluation completes partial hands and boards, applies weighted card distributions, handles split pots, and preserves PokerStove's zero-share behavior when no legal disjoint completion exists.

License

pokerstove-go is licensed under the BSD 3-Clause License. See LICENSE.

This project is a native Go port with behavior validated against PokerStove. PokerStove is copyright Andrew C. Prock and is used under its BSD-style license. See THIRD_PARTY_NOTICES.md.

Documentation

Overview

Package pokerstove provides native Go card parsing, hand evaluation, and showdown equity enumeration for PokerStove-style poker games.

The package does not wrap or call the C++ PokerStove library at runtime. Instead, copied golden vectors and optional oracle-backed tests keep the Go implementation aligned with PokerStove compatibility behavior.

Cards are represented compactly as bits in a CardSet. CardSet is a value type: methods such as Insert and Remove return a new set and never mutate the receiver. Public helpers parse PokerStove card text such as "AhKd", construct evaluators for supported game codes, compare complete or partial hands, and enumerate showdown equities.

LowEvaluation.Code is opaque ordering data, and CompareHandEvaluations is meaningful for HandEvaluation values produced by this package's evaluators. Showdown enumeration follows lower-level PokerStove parity behavior, including successful zero-share results when legal inputs have no disjoint completion. Applications can layer stricter user-facing validation on top.

Example (LowballComparison)
package main

import (
	"fmt"

	pokerstove "github.com/kevinmcmahon/pokerstove-go"
)

func mustCardSet(text string) pokerstove.CardSet {
	cards, err := pokerstove.ParseCardSet(text)
	if err != nil {
		panic(err)
	}
	return cards
}

func main() {
	evaluator, err := pokerstove.NewEvaluator("k")
	if err != nil {
		panic(err)
	}

	comparison, err := evaluator.Compare(
		mustCardSet("2c3d4h5s7c"),
		mustCardSet("2d3h4s5c6d"),
		0,
	)
	if err != nil {
		panic(err)
	}

	fmt.Println(comparison > 0)

}
Output:
true
Example (NoDisjointZeroShare)
package main

import (
	"fmt"

	pokerstove "github.com/kevinmcmahon/pokerstove-go"
)

func main() {
	result, err := pokerstove.EvaluateShowdown(pokerstove.ShowdownRequest{
		Game:  "h",
		Hands: []string{"AcAs", "AcKd"},
	})
	if err != nil {
		panic(err)
	}

	fmt.Printf(
		"%.0f %.0f %.0f\n",
		result.Players[0].WinShares,
		result.Players[1].WinShares,
		result.TotalShares,
	)

}
Output:
0 0 0
Example (OmahaEightSplitPot)
package main

import (
	"fmt"

	pokerstove "github.com/kevinmcmahon/pokerstove-go"
)

func main() {
	result, err := pokerstove.EvaluateShowdown(pokerstove.ShowdownRequest{
		Game:  "o8",
		Board: "2c5c6cJhTd",
		Hands: []string{"3d4hKhQd", "AcKcQhTs"},
	})
	if err != nil {
		panic(err)
	}

	fmt.Printf("%.1f %.1f\n", result.Players[0].WinShares, result.Players[1].WinShares)

}
Output:
0.5 0.5
Example (PartialHandsAndBoards)
package main

import (
	"fmt"

	pokerstove "github.com/kevinmcmahon/pokerstove-go"
)

func main() {
	result, err := pokerstove.EvaluateShowdown(pokerstove.ShowdownRequest{
		Game:  "h",
		Board: "2c3d4h5s",
		Hands: []string{"Ac", "KhQh"},
	})
	if err != nil {
		panic(err)
	}

	fmt.Printf("%.0f\n", result.TotalShares)
	fmt.Println(result.Players[0].WinShares > 0)
	fmt.Println(result.Players[1].WinShares > 0)

}
Output:
1980
true
false
Example (StudEight)
package main

import (
	"fmt"

	pokerstove "github.com/kevinmcmahon/pokerstove-go"
)

func main() {
	result, err := pokerstove.EvaluateShowdown(pokerstove.ShowdownRequest{
		Game:  "e",
		Hands: []string{"Ac2d3h4c5s7d8c", "KcKdQhQsJc9d8h"},
	})
	if err != nil {
		panic(err)
	}

	fmt.Printf("%.0f %.0f\n", result.Players[0].WinShares, result.Players[1].WinShares)

}
Output:
1 0
Example (WeightedRanges)
package main

import (
	"fmt"

	pokerstove "github.com/kevinmcmahon/pokerstove-go"
)

func main() {
	result, err := pokerstove.EvaluateShowdown(pokerstove.ShowdownRequest{
		Game:  "h",
		Board: "2c3d4h5s9c",
		Hands: []string{"AcAs=0.25,KcKs=0.75", "QhQs=0.5,JhJs=0.5"},
	})
	if err != nil {
		panic(err)
	}

	fmt.Printf("%.1f %.1f\n", result.Players[0].WinShares, result.Players[1].WinShares)
	fmt.Printf("%.1f\n", result.TotalShares)

}
Output:
1.0 0.0
1.0

Index

Examples

Constants

View Source
const (
	// NoPair is a high-card hand.
	NoPair = 0
	// OnePair is one pair.
	OnePair = 1
	// ThreeFlush is a three-card poker flush.
	ThreeFlush = 2
	// ThreeStraight is a three-card poker straight.
	ThreeStraight = 3
	// TwoPair is two pair.
	TwoPair = 4
	// ThreeOfAKind is three of a kind.
	ThreeOfAKind = 5
	// ThreeStraightFlush is a three-card poker straight flush.
	ThreeStraightFlush = 6
	// Straight is a five-card straight.
	Straight = 7
	// Flush is a five-card flush.
	Flush = 8
	// FullHouse is a full house.
	FullHouse = 9
	// FourOfAKind is four of a kind.
	FourOfAKind = 10
	// StraightFlush is a five-card straight flush.
	StraightFlush = 11
)

Variables

This section is empty.

Functions

func CompareEvaluations

func CompareEvaluations(a, b Evaluation) int

CompareEvaluations compares two high-hand evaluations.

It returns 1 when a is stronger, -1 when b is stronger, and 0 when they tie.

func CompareHandEvaluations

func CompareHandEvaluations(a HandEvaluation, b HandEvaluation) int

CompareHandEvaluations compares two hand evaluations.

It returns 1 when a is stronger, -1 when b is stronger, and 0 when they tie. The comparison is meaningful for HandEvaluation values returned by Evaluator.Evaluate or package helpers; manually constructed values do not contain the private PokerStove-compatible ordering code.

Types

type Card

type Card uint8

Card identifies one card in a standard 52-card deck.

func NewCard

func NewCard(rank Rank, suit Suit) Card

NewCard returns the card with the supplied rank and suit.

func ParseCard

func ParseCard(text string) (Card, error)

ParseCard parses a two-character PokerStove card token.

func (Card) Rank

func (c Card) Rank() Rank

Rank returns the card's rank.

func (Card) String

func (c Card) String() string

String formats the card as PokerStove text, such as "Ah" or "Tc".

func (Card) Suit

func (c Card) Suit() Suit

Suit returns the card's suit.

type CardSet

type CardSet uint64

CardSet is a bit set of cards.

CardSet is a value type. Methods such as Insert and Remove return a new set instead of mutating the receiver.

func FullDeck

func FullDeck() CardSet

FullDeck returns a CardSet containing all 52 standard deck cards.

func ParseCardSet

func ParseCardSet(text string) (CardSet, error)

ParseCardSet parses whitespace-separated or contiguous PokerStove card text.

It rejects malformed cards and duplicate cards. The returned String form is canonical PokerStove card order, not necessarily the input order.

Example
package main

import (
	"fmt"

	pokerstove "github.com/kevinmcmahon/pokerstove-go"
)

func main() {
	cards, err := pokerstove.ParseCardSet("Ac Kd\nQh")
	if err != nil {
		panic(err)
	}

	fmt.Println(cards)
	fmt.Println(cards.Size())

}
Output:
AcKdQh
3

func (CardSet) Cards

func (cs CardSet) Cards() []Card

Cards returns the cards in ascending deck order.

func (CardSet) Contains

func (cs CardSet) Contains(card Card) bool

Contains reports whether cs contains card.

func (CardSet) ContainsSet

func (cs CardSet) ContainsSet(other CardSet) bool

ContainsSet reports whether cs contains every card in other.

func (CardSet) Disjoint

func (cs CardSet) Disjoint(other CardSet) bool

Disjoint reports whether cs and other share no cards.

func (CardSet) Insert

func (cs CardSet) Insert(card Card) CardSet

Insert returns a set containing all cards in cs plus card.

func (CardSet) Remove

func (cs CardSet) Remove(other CardSet) CardSet

Remove returns a set with all cards in other removed from cs.

func (CardSet) Size

func (cs CardSet) Size() int

Size returns the number of cards in cs.

func (CardSet) String

func (cs CardSet) String() string

String formats the set as contiguous PokerStove card text.

type CardTextError

type CardTextError struct {
	// Message is the human-readable parse error.
	Message string
}

CardTextError describes invalid PokerStove card text.

func (CardTextError) Error

func (e CardTextError) Error() string

Error returns the card text parse error message.

type Evaluation

type Evaluation struct {
	// Type is one of the exported hand category constants.
	Type int
	// Major is the primary rank for made hands such as pairs or straights.
	Major Rank
	// Minor is the secondary rank for hands such as two pair or full houses.
	Minor Rank
	// Kickers are remaining ranks used to break ties, highest first.
	Kickers []Rank
}

Evaluation describes the high-hand category and tie breakers.

func EvaluateHoldem

func EvaluateHoldem(hand CardSet, board CardSet) (Evaluation, error)

EvaluateHoldem evaluates a hold'em hand and board as a high hand.

The hand may contain at most two cards and the board may contain at most five. Partial inputs are evaluated as the best high hand available from the cards provided; use EvaluateShowdown when missing cards should be enumerated.

Example
package main

import (
	"fmt"

	pokerstove "github.com/kevinmcmahon/pokerstove-go"
)

func mustCardSet(text string) pokerstove.CardSet {
	cards, err := pokerstove.ParseCardSet(text)
	if err != nil {
		panic(err)
	}
	return cards
}

func main() {
	evaluation, err := pokerstove.EvaluateHoldem(
		mustCardSet("AcKs"),
		mustCardSet("AhKd2c3d9s"),
	)
	if err != nil {
		panic(err)
	}

	fmt.Println(evaluation.Type == pokerstove.TwoPair)
	fmt.Println(evaluation.Major, evaluation.Minor)

}
Output:
true
A K

type Evaluator

type Evaluator struct {
	// contains filtered or unexported fields
}

Evaluator evaluates hands for one game.

func NewEvaluator

func NewEvaluator(text string) (*Evaluator, error)

NewEvaluator parses text as a game code and returns an evaluator for it.

Example
package main

import (
	"fmt"

	pokerstove "github.com/kevinmcmahon/pokerstove-go"
)

func main() {
	evaluator, err := pokerstove.NewEvaluator("omaha/8")
	if err != nil {
		panic(err)
	}

	metadata := evaluator.Metadata()
	fmt.Println(metadata.Name)
	fmt.Println(metadata.HandSize, metadata.BoardSize, metadata.EvaluationSize)

}
Output:
Omaha high/low
4 5 2

func (*Evaluator) Compare

func (e *Evaluator) Compare(left CardSet, right CardSet, board CardSet) (int, error)

Compare evaluates and compares two hands according to the evaluator's game.

Example
package main

import (
	"fmt"

	pokerstove "github.com/kevinmcmahon/pokerstove-go"
)

func mustCardSet(text string) pokerstove.CardSet {
	cards, err := pokerstove.ParseCardSet(text)
	if err != nil {
		panic(err)
	}
	return cards
}

func main() {
	evaluator, err := pokerstove.NewEvaluator("h")
	if err != nil {
		panic(err)
	}

	comparison, err := evaluator.Compare(
		mustCardSet("AcAs"),
		mustCardSet("KhQh"),
		mustCardSet("2c3d4h5s9c"),
	)
	if err != nil {
		panic(err)
	}

	fmt.Println(comparison > 0)

}
Output:
true

func (*Evaluator) Evaluate

func (e *Evaluator) Evaluate(hand CardSet, board CardSet) (HandEvaluation, error)

Evaluate evaluates a hand and board according to the evaluator's game.

Validation follows the selected game. Hold'em accepts partial hands and boards up to their maximum sizes; Omaha variants require complete inputs because evaluation must choose exact counts from hand and board.

Example
package main

import (
	"fmt"

	pokerstove "github.com/kevinmcmahon/pokerstove-go"
)

func mustCardSet(text string) pokerstove.CardSet {
	cards, err := pokerstove.ParseCardSet(text)
	if err != nil {
		panic(err)
	}
	return cards
}

func main() {
	evaluator, err := pokerstove.NewEvaluator("o8")
	if err != nil {
		panic(err)
	}

	evaluation, err := evaluator.Evaluate(
		mustCardSet("4c3cKhQd"),
		mustCardSet("AcKc6cJs2d"),
	)
	if err != nil {
		panic(err)
	}

	fmt.Println(evaluation.High.Type == pokerstove.Flush)
	fmt.Println(evaluation.HighLow())

}
Output:
true
true

func (*Evaluator) Game

func (e *Evaluator) Game() Game

Game returns the evaluator's game code.

func (*Evaluator) Metadata

func (e *Evaluator) Metadata() GameMetadata

Metadata returns metadata for the evaluator's game.

type Game

type Game string

Game is a PokerStove game code.

const (
	// Holdem is Texas hold'em high.
	Holdem Game = "h"
	// OmahaHigh is four-card Omaha high.
	OmahaHigh Game = "o"
	// OmahaEight is four-card Omaha high/low with an eight-or-better low qualifier.
	OmahaEight Game = "o8"
	// OmahaFive is the C++ PokerStove five-card Omaha high game code.
	OmahaFive Game = "o5"
	// OmahaSix is the C++ PokerStove six-card Omaha high game code.
	OmahaSix Game = "o6"
	// OmahaFiveEight is the C++ PokerStove five-card Omaha high/low game code.
	OmahaFiveEight Game = "o5/8"
	// OmahaSixEight is the C++ PokerStove six-card Omaha high/low game code.
	OmahaSixEight Game = "o6/8"
	// KansasCityLowball is deuce-to-seven lowball.
	KansasCityLowball Game = "k"
	// AceToFiveLowball is ace-to-five lowball.
	AceToFiveLowball Game = "l"
	// ThreeCardPoker is three-card poker high.
	ThreeCardPoker Game = "3"
	// Razz is seven-card ace-to-five lowball.
	Razz Game = "r"
	// Stud is seven-card stud high.
	Stud Game = "s"
	// StudHighLowNoQualifier is seven-card stud high/low with no low qualifier.
	StudHighLowNoQualifier Game = "q"
	// DrawHigh is five-card draw high.
	DrawHigh Game = "d"
	// TripleDrawDeuceToSeven is triple draw deuce-to-seven lowball.
	TripleDrawDeuceToSeven Game = "t"
	// TripleDrawA5 is triple draw ace-to-five lowball.
	TripleDrawA5 Game = "T"
	// StudEight is seven-card stud high/low with an eight-or-better low qualifier.
	StudEight Game = "e"
	// Badugi is four-card badugi.
	Badugi Game = "b"
)

func ParseGame

func ParseGame(text string) (Game, error)

ParseGame parses a PokerStove game code or supported game name.

Most game names are normalized by their leading lowercase character. The uppercase "T" code is intentionally distinct and selects TripleDrawA5; lowercase "t" selects TripleDrawDeuceToSeven.

func (Game) Metadata

func (g Game) Metadata() GameMetadata

Metadata returns metadata for the game.

func (Game) String

func (g Game) String() string

String returns the PokerStove game code.

type GameMetadata

type GameMetadata struct {
	// Game is the game code described by this metadata.
	Game Game
	// Name is the human-readable game name.
	Name string
	// HandSize is the number of cards accepted in one hand option according to
	// the current PokerStove-compatible metadata row.
	HandSize int
	// BoardSize is the number of board cards used by the game.
	BoardSize int
	// EvaluationSize is the number of evaluation pots, such as high and low.
	EvaluationSize int
	// UsesSuits reports whether suits affect hand strength.
	UsesSuits bool
}

GameMetadata describes the hand and board shape for a game.

type HandDistribution

type HandDistribution []HandOption

HandDistribution is a list of weighted hand options.

func ParseHandDistribution

func ParseHandDistribution(text string) (HandDistribution, error)

ParseHandDistribution parses comma-separated weighted hand options.

Options use "cards" or "cards=weight" syntax. The special string "." represents a random completion placeholder and must be the entire distribution text.

type HandEvaluation

type HandEvaluation struct {
	// High is the high-hand evaluation. Lowball-only games may leave High at
	// its zero value and use internal ordering data for comparison.
	High Evaluation
	// Low is non-nil when the hand has a qualifying low evaluation.
	Low *LowEvaluation
	// contains filtered or unexported fields
}

HandEvaluation combines the high hand with an optional qualifying low hand.

func (HandEvaluation) HighLow

func (e HandEvaluation) HighLow() bool

HighLow reports whether the hand has a qualifying low evaluation.

type HandOption

type HandOption struct {
	// Cards is the fixed or partial hand for this option.
	Cards CardSet
	// Weight is the relative frequency assigned to this option.
	Weight float64
}

HandOption is one weighted hand option in a distribution.

type LowEvaluation

type LowEvaluation struct {
	// Code is the ordering code for a qualifying low hand.
	Code int
}

LowEvaluation is the low side of a split-pot hand evaluation.

Code is opaque PokerStove-compatible ordering data. Use Evaluator.Compare, CompareHandEvaluations, or showdown results to compare lows instead of presenting or interpreting the integer directly.

type PlayerEquity

type PlayerEquity struct {
	// Label is the original hand distribution text for this player.
	Label string
	// IsRandomHand reports whether this player was generated automatically.
	IsRandomHand bool
	// WinShares is the weighted number of outright pot shares won.
	WinShares float64
	// TieShares is the weighted number of split pot shares won.
	TieShares float64
	// Equity is this player's normalized share of TotalShares.
	Equity float64
}

PlayerEquity is one player's share of the enumerated showdown.

type Rank

type Rank uint8

Rank identifies a card rank from Two through Ace.

const (
	// Two is the deuce rank.
	Two Rank = iota
	// Three is the trey rank.
	Three
	// Four is the four rank.
	Four
	// Five is the five rank.
	Five
	// Six is the six rank.
	Six
	// Seven is the seven rank.
	Seven
	// Eight is the eight rank.
	Eight
	// Nine is the nine rank.
	Nine
	// Ten is the ten rank.
	Ten
	// Jack is the jack rank.
	Jack
	// Queen is the queen rank.
	Queen
	// King is the king rank.
	King
	// Ace is the ace rank.
	Ace
)

func (Rank) String

func (r Rank) String() string

String formats the rank as PokerStove rank text.

type ShowdownRequest

type ShowdownRequest struct {
	// Game is the PokerStove game code. An empty value defaults to hold'em.
	Game string
	// Board is PokerStove card text for known board cards. "-" is accepted as an
	// empty board.
	Board string
	// Hands contains one hand distribution string per player.
	Hands []string
	// AddRandomHandForSingleHand adds a random hold'em opponent for one-hand requests.
	AddRandomHandForSingleHand bool
}

ShowdownRequest describes a showdown equity calculation.

type ShowdownResult

type ShowdownResult struct {
	// Players contains equity results in request hand order.
	Players []PlayerEquity
	// TotalShares is the weighted total used to normalize player equity. It can be
	// zero when no legal disjoint completion exists.
	TotalShares float64
}

ShowdownResult is the result of a showdown equity calculation.

func EvaluateShowdown

func EvaluateShowdown(req ShowdownRequest) (ShowdownResult, error)

EvaluateShowdown enumerates a showdown request and returns player equities.

It accepts weighted ranges, dot/random hand placeholders, partial hands, and partial boards. Missing cards are enumerated from the live deck. If no disjoint completion exists, the function succeeds with zero shares to match lower-level PokerStove parity behavior.

Example
package main

import (
	"fmt"

	pokerstove "github.com/kevinmcmahon/pokerstove-go"
)

func main() {
	result, err := pokerstove.EvaluateShowdown(pokerstove.ShowdownRequest{
		Game:  "h",
		Board: "2c3d4h5s9c",
		Hands: []string{"AcAs", "KhQh"},
	})
	if err != nil {
		panic(err)
	}

	fmt.Printf("%.0f %.0f\n", result.Players[0].WinShares, result.Players[1].WinShares)
	fmt.Printf("%.2f %.2f\n", result.Players[0].Equity, result.Players[1].Equity)

}
Output:
1 0
1.00 0.00

type Suit

type Suit uint8

Suit identifies a card suit.

const (
	// Clubs is the club suit.
	Clubs Suit = iota
	// Diamonds is the diamond suit.
	Diamonds
	// Hearts is the heart suit.
	Hearts
	// Spades is the spade suit.
	Spades
)

func (Suit) String

func (s Suit) String() string

String formats the suit as PokerStove suit text.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL