Skip to content

About

Lookup-table based Teen Patti (3-card poker) hand evaluator for Java, Go and C++, with full Joker (wild card) support and O(1) rank and type queries.

Topics

Resources

Stars

19 stars

Watchers

1 watching

Forks

Latest commit

 

History

20 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TeenPatti Algorithm

中文文档

A high-performance lookup-table algorithm for the Indian card game Teen Patti, with full support for Joker (wild card). Given any 3-card hand, the library instantly returns the hand's rank, type, and best possible combination. Available for Java, Go and C++, with identical behavior.

Derived from texas_algorithm.


Project Structure

teenpatti_algorithm/
├── java/     # Java implementation (published to Maven Central)
├── go/       # Go implementation (go:embed based, byte-identical lookup table)
├── cpp/      # C++17 implementation (CMake, data embedded via .incbin)
└── .github/  # CI: Java CI (maven.yml), Go CI (go.yml), C++ CI (cpp.yml), publishing (publish.yml)

All three implementations share the same teenpatti_data.txt lookup table and pass the same set of unit tests; the Go and C++ generation pipelines reproduce the table byte-for-byte.


Table of Contents


Game Rules Overview

Teen Patti is played with a standard 52-card deck plus 3 Jokers (55 cards total). Each player is dealt 3 cards. The goal is to have the best 3-card hand. Jokers are wild — they substitute for any card to form the best possible combination.


Quick Start

Maven dependency

<dependency>
    <groupId>com.github.esrrhs</groupId>
    <artifactId>teenpatti_algorithm</artifactId>
    <version>1.0.3</version>
</dependency>

Basic usage

// 1. Load the lookup table once at startup
TeenPattiAlgorithmUtil.load();

// 2. Get the hand type (returns an integer constant, see Hand Rankings)
int type = TeenPattiAlgorithmUtil.getWinType("黑A,方A,鬼");
// type == 6  → Three of a Kind (Joker acts as a third Ace)

// 3. Get the rank position (higher = stronger hand)
int position = TeenPattiAlgorithmUtil.getWinPosition("黑2,黑3,黑4");

// 4. Compare two hands  (positive = first hand wins, negative = second wins, 0 = tie)
int result = TeenPattiAlgorithmUtil.compare("黑A,方A,鬼", "黑A,鬼,方3");

// 5. Get the best resolved hand (Joker expanded to its optimal card)
int maxKey = TeenPattiAlgorithmUtil.getMax("黑A,方A,鬼");
String maxStr = TeenPattiAlgorithmUtil.keyToStr(maxKey);  // e.g. "方A黑A梅A" (three Aces)

Go module

go get github.com/esrrhs/teenpatti_algorithm/go

Basic usage (Go)

package main

import (
	"fmt"

	teenpatti "github.com/esrrhs/teenpatti_algorithm/go"
)

func main() {
	// 1. Load the embedded lookup table once at startup
	if err := teenpatti.Load(); err != nil {
		panic(err)
	}

	// 2. Get the hand type (returns a CardType constant, see Hand Rankings)
	typ := teenpatti.GetWinType("黑A,方A,鬼")
	// typ == 6  → Three of a Kind (Joker acts as a third Ace)

	// 3. Get the rank position (higher = stronger hand)
	position := teenpatti.GetWinPosition("黑2,黑3,黑4")

	// 4. Compare two hands (positive = first hand wins, negative = second wins, 0 = tie)
	result := teenpatti.Compare("黑A,方A,鬼", "黑A,鬼,方3")

	// 5. Get the best resolved hand (Joker expanded to its optimal card)
	maxKey := teenpatti.GetMax("黑A,方A,鬼")
	maxStr := teenpatti.KeyToStr(maxKey)

	fmt.Println(typ, position, result, maxKey, maxStr)
}

C++ (CMake)

cmake -S cpp -B cpp/build -DCMAKE_BUILD_TYPE=Release
cmake --build cpp/build

Basic usage (C++)

#include <teenpatti/teenpatti.hpp>

// 1. Load the embedded lookup table once at startup
if (!teenpatti::load()) {
  return 1;
}

// 2. Get the hand type (returns a CardType constant, see Hand Rankings)
int type = teenpatti::get_win_type("黑A,方A,鬼");
// type == 6  → Three of a Kind (Joker acts as a third Ace)

// 3. Get the rank position (higher = stronger hand)
int position = teenpatti::get_win_position("黑2,黑3,黑4");

// 4. Compare two hands (positive = first hand wins, negative = second wins, 0 = tie)
int result = teenpatti::compare("黑A,方A,鬼", "黑A,鬼,方3");

// 5. Get the best resolved hand (Joker expanded to its optimal card)
int maxKey = teenpatti::get_max("黑A,方A,鬼");
std::string maxStr = teenpatti::key_to_str(maxKey);  // e.g. "方A黑A梅A" (three Aces)

API Reference

All public methods are on TeenPattiAlgorithmUtil.

Method Parameters Return Description
load() — void Load lookup table from teenpatti_data.txt (call once at startup)
loadNormal(InputStream) input stream void Load lookup table from a custom stream
getWinType(String) comma-separated cards int Hand type constant (see Hand Rankings)
getWinPosition(String) comma-separated cards int Global rank (higher = stronger)
getMax(String) comma-separated cards int Encoded key of the best resolved hand
compare(String, String) two hands int Positive/zero/negative comparison result
keyToStr(int) encoded key String Human-readable card string

KeyData fields

getKeyData() returns a KeyData object with three fields:

Field Getter Description
position getPosition() / getPostion() Global rank index among all possible hands
type getType() Hand type (1–6, see Hand Rankings)
max getMax() Encoded key of best resolved hand

Go API

All functions live in package teenpatti (github.com/esrrhs/teenpatti_algorithm/go).

Function Parameters Return Description
Load() — error Load the embedded teenpatti_data.txt (call once at startup)
LoadFromFile(path) file path error Load the lookup table from a custom file
GetWinType(string) comma-separated cards int Hand type constant (see Hand Rankings)
GetWinPosition(string) comma-separated cards int Global rank (higher = stronger)
GetMax(string) comma-separated cards int Encoded key of the best resolved hand
Compare(a, b string) two hands int Positive/zero/negative comparison result
KeyToStr(int) encoded key string Human-readable card string

The ...ByCards variants (GetWinTypeByCards, GetWinPositionByCards, GetMaxByCards, CompareByCards) accept parsed []byte card slices instead of strings; GetKeyDataByCards / GetKeyDataByKey return a *KeyData with Position, Type and Max fields. CompareCards / MaxCards / GetCardTypeUnordered operate on []Poke directly, mirroring TeenPattiCardUtil.

C++ API

All functions live in namespace teenpatti (header <teenpatti/teenpatti.hpp>, library target teenpatti).

Function Return Description
load() bool Load the embedded teenpatti_data.txt (call once at startup)
load_from_file(path) / load_from_stream(in) bool Load the lookup table from a custom file/stream
get_win_type(cards) int Hand type constant (see Hand Rankings)
get_win_position(cards) int Global rank (higher = stronger)
get_max(cards) int Encoded key of the best resolved hand
compare(a, b) int Positive/zero/negative comparison result
key_to_str(key) std::string Human-readable card string

The cards parameter is overloaded: std::string ("黑A,方A,鬼"), std::vector<std::uint8_t> (packed card bytes) or int (encoded key). get_key_data(cards) returns a std::optional<KeyData> with position, type and max; compare_cards / max_cards / get_card_type_unordered operate on std::vector<Poke> directly, mirroring TeenPattiCardUtil.


Card Notation

Cards are written as <suit><value> in Chinese notation, separated by commas.

Suits

Notation Suit English
方 ♦ Diamonds
梅 ♣ Clubs
红 ♥ Hearts
黑 ♠ Spades

Values

2 3 4 5 6 7 8 9 10 J Q K A

Joker

Write 鬼 for a wild Joker card. There are 3 Jokers in the deck.

Examples

Input string Meaning
"黑A,方A,鬼" Ace of Spades, Ace of Diamonds, Joker
"黑2,黑3,黑4" 2♠ 3♠ 4♠
"红K,梅Q,方J" K♥ Q♣ J♦

Hand Rankings

Ranked from lowest to highest:

Rank Type constant Name Description
1 TEENPATTI_CARD_TYPE_GAOPAI = 1 High Card No combination; highest card wins
2 TEENPATTI_CARD_TYPE_DUIZI = 2 Pair Two cards of the same value
3 TEENPATTI_CARD_TYPE_TONGHUA = 3 Flush All three cards of the same suit
4 TEENPATTI_CARD_TYPE_SHUNZI = 4 Straight Three consecutive values (A-2-3 also valid)
5 TEENPATTI_CARD_TYPE_TONGHUASHUN = 5 Straight Flush Consecutive values, all same suit
6 TEENPATTI_CARD_TYPE_SANTIAO = 6 Three of a Kind All three cards of the same value

Note: Three of a Kind ranks higher than Straight Flush in Teen Patti, unlike Texas Hold'em.

Tie-breaking rules:

  • Three of a Kind / Pair: compare by the repeated card's value, then the kicker.
  • All others: compare highest card first, then second, then third.

Algorithm Design

Overview

The library uses a pre-computed lookup table. At query time, a 3-card hand is encoded into a single integer key; the key is looked up in a ConcurrentHashMap to retrieve the hand's rank, type, and best expansion in O(1).

Step 1 — Card encoding

Each card is packed into one byte: the upper 4 bits hold the suit (0–3), the lower 4 bits hold the value (2–14). A Joker uses a reserved (color=5, value=8) sentinel.

byte = (suit << 4) | value

A 3-card hand is encoded into a single int by concatenating the three byte values in decimal:

key = card1_byte * 10000 + card2_byte * 100 + card3_byte

Cards are sorted before encoding so the same set always produces the same key regardless of order.

Step 2 — Enumerate all combinations

The deck contains 55 cards (52 regular + 3 Jokers). All C(55, 3) = 26,235 unique combinations are enumerated using a recursive combination generator. Duplicate-key hands (e.g. a hand where Joker resolves to an identical state) are deduplicated.

Step 3 — Multi-threaded quicksort

All combination keys are sorted by hand strength using a parallel quicksort (Sorter.java / sorter.go / teenpatti.cpp). The thread pool size equals the number of available CPU cores. When the number of active threads exceeds 2 × CPU_CORES, sub-partitions fall back to in-thread recursion to avoid thread explosion.

The comparison function (GenUtil.compare) resolves Jokers to their best possible substitution before comparing, so the sort order reflects the true game outcome.

Step 4 — Output the lookup table

After sorting, each entry is written to teenpatti_data.txt with:

<key> <rank> <rank_index> <total> <best_hand_str> <hand_type> <best_hand_key> <best_hand_readable>

The rank index is incremented only when two adjacent hands are not equal in strength, producing a dense sequential ranking.

Query path (runtime)

input string  →  parse cards  →  sort bytes  →  encode key  →  HashMap.get(key)  →  KeyData{position, type, max}

Generating the Lookup Table

Run TeenPattiAlgorithmUtil.main() (or GenUtil.genKey() + GenUtil.outputData()) to regenerate teenpatti_data.txt. This is only needed if you modify the deck or ranking rules.

# Java
cd java && mvn exec:java -Dexec.mainClass="com.github.esrrhs.teenpatti_algorithm.TeenPattiAlgorithmUtil"

# Go (writes teenpatti_data.txt into the working directory)
cd go && go run ./cmd/teenpatti_gen

# C++ (writes teenpatti_data.txt into the working directory)
cmake --build cpp/build --target teenpatti_gen && cpp/build/teenpatti_gen

The generation process prints progress with estimated time remaining and throughput (entries/sec). The Go and C++ implementations reproduce the Java-generated table byte-for-byte.


Running the Demo

TestUtil.main() loads the table and prints results for two sample hands:

TeenPattiAlgorithmUtil.load();

String cards  = "黑A,方A,鬼";   // A♠ A♦ Joker  → Three Aces
String cards1 = "黑A,鬼,方3";   // A♠ Joker 3♦  → Pair of Aces

System.out.println(TeenPattiAlgorithmUtil.getWinPosition(cards));   // rank
System.out.println(TeenPattiAlgorithmUtil.getWinType(cards));       // 6 = Three of a Kind
System.out.println(TeenPattiAlgorithmUtil.keyToStr(
        TeenPattiAlgorithmUtil.getMax(cards)));                      // best resolved hand

System.out.println(TeenPattiAlgorithmUtil.compare(cards, cards1));  // > 0: cards wins

The same demo in Go:

teenpatti.Load()

cards := "黑A,方A,鬼"  // A♠ A♦ Joker  → Three Aces
cards1 := "黑A,鬼,方3" // A♠ Joker 3♦ → Pair of Aces

fmt.Println(teenpatti.GetWinPosition(cards))             // rank
fmt.Println(teenpatti.GetWinType(cards))                 // 6 = Three of a Kind
fmt.Println(teenpatti.KeyToStr(teenpatti.GetMax(cards))) // best resolved hand
fmt.Println(teenpatti.Compare(cards, cards1))            // > 0: cards wins

And in C++:

teenpatti::load();

std::string cards = "黑A,方A,鬼";   // A♠ A♦ Joker  → Three Aces
std::string cards1 = "黑A,鬼,方3";  // A♠ Joker 3♦ → Pair of Aces

std::cout << teenpatti::get_win_position(cards) << "\n";              // rank
std::cout << teenpatti::get_win_type(cards) << "\n";                  // 6 = Three of a Kind
std::cout << teenpatti::key_to_str(teenpatti::get_max(cards)) << "\n"; // best resolved hand
std::cout << teenpatti::compare(cards, cards1) << "\n";               // > 0: cards wins

Related Projects


License

This project is licensed under the MIT License.

About

Lookup-table based Teen Patti (3-card poker) hand evaluator for Java, Go and C++, with full Joker (wild card) support and O(1) rank and type queries.

Topics

Resources

Stars

19 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages