Skip to main content

32word

The game engine for 3-2-Word: Solve Wordle in three guesses.

PyPI version License: MIT Python 3.8+

What This Is

Most people play Wordle casually. Guess some vowels. Eliminate common letters. Hope for the best. Maybe they've heard that STAIR + LEMON + PUDGY is unbeatable.

We know better.

3-2-Word is a training app that elevates Wordle from a casual puzzle into something you can practice and master—like Scrabble or a spelling bee. This library (32word) is the engine underneath.

Smart players solved Wordle years ago. They proved optimal strategies mathematically. But most people didn't change how they played. This library changes that. It codifies the strategies that work and makes them accessible to everyone.

It's fun to challenge yourself. It's fun to be smart. This gives you the tools to win.

Installation

pip install 32word

Why This Exists

You can solve Wordle blindly, or you can learn the patterns that elite players discovered through exhaustive analysis. This library provides:

  1. Optimal two-guess strategies that consistently solve Wordle in three attempts
  2. Pre-computed strategy tables based on simulations across all 3,158 target words
  3. A clean API for building bots, websites, and training tools

The solver logic lives here. Everything else—web apps, Discord bots, Telegram bots—imports this library as a dependency.

Quick Start

from word32 import generate_clue, filter_targets, load_strategy, get_second_guess

# Load the default optimal strategy
strategy = load_strategy("v1.0")

# Get the best first guess
first_guess = strategy.first_guess()
print(f"First guess: {first_guess}")  # e.g., "STARE"

# Simulate Wordle feedback
target = "TRASH"
clue = generate_clue(first_guess, target)
print(f"Clue: {clue}")  # ('G', 'Y', 'B', 'B', 'G')

# Find remaining possibilities
from word32 import VALID_TARGETS
remaining = filter_targets(VALID_TARGETS, first_guess, clue)
print(f"{len(remaining)} words remain")

# Get optimal second guess
second_guess = get_second_guess(strategy, clue)
print(f"Second guess: {second_guess}")

Two guesses. Then solve. That's the strategy.

Phase 4.2/4.3 Features

The library now includes enhanced features for building interactive applications:

Response Schema (Phase 4.2)

Standardized JSON responses for cross-platform consistency:

from word32 import build_game_response, get_second_guess_recommendation

# Build a standardized response
response = build_game_response(
    guess="RAISE",
    clue=('G', 'Y', 'X', 'X', 'G'),
    remaining_targets=['AGILE', 'ALIAS', 'AMISS'],
    strategy_recommendation={
        'recommended_guess': 'CLOUD',
        'confidence': 0.95
    },
    game_state={
        'guess_number': 1,
        'guesses_so_far': ['RAISE'],
        'is_solved': False
    }
)

# Convert to JSON
print(response.to_json())

Custom First Guess Selection (Phase 4.3)

Choose from 32 optimal first guesses:

from word32 import get_available_first_guesses, select_first_guess, get_second_guess_recommendation

# Get all 32 available first guesses
options = get_available_first_guesses()
for opt in options[:5]:
    print(f"{opt['first_guess']} - Rank {opt['rank']}, Expected: {opt['expected_remaining']:.1f}")

# Select a custom first guess
selected = select_first_guess("RAISE")
if selected:
    print(f"Selected: {selected['first_guess']} (Rank {selected['rank']})")

# Get strategy recommendation for custom first guess
clue = ('G', 'Y', 'X', 'X', 'G')
recommendation = get_second_guess_recommendation("RAISE", clue)
print(f"Recommended second guess: {recommendation}")

Remaining Words Tracking

Track and display remaining possible words:

from word32 import build_game_response, filter_targets, VALID_TARGETS

remaining = filter_targets(VALID_TARGETS, "RAISE", ('G', 'Y', 'X', 'X', 'G'))
response = build_game_response(
    guess="RAISE",
    clue=('G', 'Y', 'X', 'X', 'G'),
    remaining_targets=remaining,
    sample_size=10  # Show up to 10 words
)

print(f"{response.remaining.count} words remain")
print(f"Sample: {response.remaining.sample}")

Minimal Memorization Strategies (Phase 4.8/4.9)

For mobile apps or players who want minimal memorization, use optimized 2d+8r strategies with tiny lookup tables (~650 bytes):

from word32 import load_strategy_by_components, list_strategies_by_depth

# Load TRICE - best 2d+8r performer (39.14% 2-guess win rate)
strategy = load_strategy_by_components("TRICE", 8)

# List all available 2d+8r strategies
strategies_8r = list_strategies_by_depth(8)
print(f"Available 8r strategies: {strategies_8r}")
# ['TRICE', 'DEALT', 'SIREN', 'ADULT', 'CRONE', 'NOISE', 'POSER', 'RINSE', 'RISEN', 'SITAR', 'SNORE']

# Get second guess from TRICE strategy
clue = ('G', 'Y', 'X', 'X', 'G')
second_guess = strategy.get_second_guess(clue)
print(f"Recommended second guess: {second_guess}")

Trade-offs:

  • ✅ Compact: ~650 bytes per strategy vs 425 KB for full lookup
  • ✅ Portable: Ideal for mobile/embedded apps
  • ⚠️ Coverage: Only 8 clue patterns per first guess (vs ~240 for full)
  • ⚠️ Win rate: 37-39% for 2-guess wins (vs 90%+ for full strategy)

Best for: 3-guess play, mobile constraints, or training progression.

Integration Guides:

Core API

Clue Generation

generate_clue(guess: str, target: str) -> tuple[str, str, str, str, str]

Returns a 5-tuple representing Wordle's feedback:

  • 'G': Green (correct letter, correct position)
  • 'Y': Yellow (correct letter, wrong position)
  • 'B': Black/Gray (letter not in target)

Example:

generate_clue("STARE", "TRASH")
# ('G', 'Y', 'B', 'B', 'G')
# S is green (position 0 matches)
# T is yellow (in word, wrong position)
# A, R are gray (not in target at those positions)
# E is green (position 4 matches)

Target Filtering

filter_targets(targets: list[str], guess: str, clue: tuple) -> list[str]

Returns all words in targets that would produce exactly clue if you guessed guess against them.

Example:

remaining = filter_targets(VALID_TARGETS, "STARE", ('G', 'Y', 'B', 'B', 'G'))
# Returns list of words compatible with that clue pattern

Strategy Loading

load_strategy(version: str = "v1.0") -> Strategy

Loads a pre-computed strategy table. The default v1.0 uses expected-remaining minimization over all 3,158 Wordle targets.

Strategy methods:

strategy = load_strategy("v1.0")

# Get recommended first guess
first = strategy.first_guess()

# Get recommended second guess based on clue
second = strategy.second_guess(clue)

# Get strategy metadata
meta = strategy.metadata()

Convenience Functions

get_second_guess(strategy: Strategy, first_clue: tuple) -> str

Shortcut for getting the optimal second guess.

is_valid_word(word: str) -> bool

Check if a word is in Wordle's valid guess list (14,855 words).

get_remaining_candidates(targets: list[str], guess: str, clue: tuple) -> int

Count how many targets remain after a guess.

Strategy Design

Each strategy is a lookup table built from tournament simulations. The process:

  1. First guess selection: Test every valid target word as a first guess against all 3,158 possible Wordle targets
  2. Clue simulation: For each first guess, generate all possible clues (up to 243 patterns, though most never occur)
  3. Second guess optimization: For each (first_guess, clue) pair, find the second guess that minimizes expected remaining candidates
  4. Tournament ranking: Score strategies by expected remaining words after two guesses across all targets

The default v1.0 strategy consistently leaves 1–3 candidates after two guesses, making the third guess nearly deterministic.

Strategy Metadata

strategy = load_strategy("v1.0")
meta = strategy.metadata()

print(meta)
# {
#   'version': 'v1.0',
#   'penalty_function': 'expected_remaining',
#   'depth': 2,
#   'symmetric': True,
#   'created': '2026-01-15',
#   'description': 'Optimal two-deep strategy minimizing expected remaining targets'
# }

Complete Example

from word32 import (
    generate_clue,
    filter_targets,
    load_strategy,
    get_second_guess,
    VALID_TARGETS
)

# Load strategy
strategy = load_strategy("v1.0")

# Simulate a game
target = "TRASH"  # The word Wordle picked
remaining = VALID_TARGETS.copy()

# First guess
guess1 = strategy.first_guess()
clue1 = generate_clue(guess1, target)
remaining = filter_targets(remaining, guess1, clue1)
print(f"After '{guess1}': {len(remaining)} words remain")

# Second guess
guess2 = get_second_guess(strategy, clue1)
clue2 = generate_clue(guess2, target)
remaining = filter_targets(remaining, guess2, clue2)
print(f"After '{guess2}': {len(remaining)} words remain")

# Third guess
if len(remaining) == 1:
    print(f"Solved in 3: {remaining[0]}")
elif len(remaining) <= 3:
    print(f"Choose from: {remaining}")
else:
    # Edge case handling
    print(f"Manual selection needed from {len(remaining)} candidates")

Most games end after two optimal guesses with 1–3 candidates remaining.

Architecture

This library is designed as a single source of truth for Wordle solver logic. All downstream applications (website, bots, mobile apps) import this package.

32word (PyPI library)
  ↓
  ├─ 3-2-Word website (React + Flask/FastAPI)
  ├─ Discord bot
  ├─ Telegram bot
  ├─ Signal bot
  └─ WhatsApp bot (future)

Design Principles

  • Stateless: All functions are deterministic and side-effect-free
  • Fast: Word lists cached in memory, strategy tables pre-computed
  • Tested: 95%+ coverage on core logic (clue generation, filtering)
  • Versioned: Strategy tables versioned independently from library code
  • Simple API: Six core functions cover all use cases

Development

Running Tests

pip install -e ".[dev]"
pytest tests/ -v --cov=word32

Package Structure

word32/
├── __init__.py           # Public API exports
├── core.py               # Clue generation, filtering
├── strategy.py           # Strategy loading and lookup
├── validation.py         # Word list validation
├── data/
│   ├── strategies/
│   │   └── v1.0.json     # Pre-computed optimal strategy
│   ├── targets.txt       # 3,158 Wordle targets
│   └── valid_guesses.txt # 14,855 valid guesses
└── tests/
    ├── test_clues.py
    ├── test_filtering.py
    ├── test_strategy.py
    └── test_integration.py

Creating New Strategies

Strategy tables are JSON files in word32/data/strategies/:

{
  "metadata": {
    "version": "v1.1-minimax",
    "penalty_function": "minimax",
    "depth": 2,
    "created": "2026-01-20"
  },
  "first_guess": "SALET",
  "clues": {
    "GGGGG": {"second_guess": "N/A", "remaining": 0},
    "GGGYB": {"second_guess": "BLAND", "remaining": 2},
    ...
  }
}

To add a new strategy:

  1. Run the tournament simulation to generate the table
  2. Save as v{X}.{Y}-{variant}.json
  3. Submit a PR with test coverage

Roadmap

Phase 1: Core Library ✅

  • Clue generation
  • Target filtering
  • Strategy loading
  • Pre-computed v1.0 strategy
  • PyPI package

Phase 2: Downstream Apps (In Progress)

  • Website at 3-2-word.com
  • Discord bot
  • Telegram bot
  • Signal bot
  • WhatsApp bot

Phase 4.2: Response Schema ✅

  • Standardized JSON response structure
  • Cross-platform consistency
  • Remaining words tracking
  • Strategy recommendations

Phase 4.3: First Guess Selection ✅

  • 32 optimal first guess options
  • Custom first guess selection
  • Strategy lookup for all first guesses
  • Metrics and ranking

Future Extensions

  • Three-deep strategies
  • Minimax variants
  • Anchor word customization
  • Quordle/Weaver support
  • Strategy comparison analytics

Why MIT Licensed?

This code should be free. The insights are already published—researchers solved Wordle years ago. I'm just packaging it so anyone can use it.

Build a bot. Build a training app. Build a better solver. The game is solved. Now go teach people how to win.

Data Completeness

The library requires several data files for full functionality. See Data Completeness Checklist for details.

Required files:

  • data/targets.txt - 2,309 target words
  • data/valid_guesses.txt - 12,950 valid guesses
  • data/phase2_naive_32.json - 32 first guess options
  • data/phase3_lookup.json - Strategy lookup for all patterns

The library will warn on import if data files are missing or incomplete.

Contributing

Contributions welcome! Please:

  1. Open an issue before starting major work
  2. Write tests for new features
  3. Follow the existing style (stateless functions, simple API)
  4. Update docs if you change the API

Integration Examples:

Areas for Contribution

  • New strategies: Minimax, three-deep, custom anchor words
  • Performance: Optimize filtering, caching
  • Variants: Quordle, Absurdle, other Wordle clones
  • Tests: Edge cases, integration tests
  • Docs: Tutorials, strategy explanations
  • 3-2-Word website: 3-2-word.com
  • Discord bot: Coming soon
  • Telegram bot: Coming soon

Acknowledgments

Built on the shoulders of researchers who solved Wordle mathematically. This library packages their insights for practical use.

Special thanks to the Wordle community for exploring optimal strategies and making the game more interesting.

License

MIT License. See LICENSE for details.


3-2-Word: The training app for people who want to master Wordle.
Built by Ben Mazzotta. Engine: 32word.

Get better at Wordle. Start here.

Metadata

Release files for 32word 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for 32word 0.2.1
File Size Uploaded Provenance
32word-0.2.1.tar.gz 178.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for 32word 0.2.1
File Interpreter ABI Platform
32word-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 340.0 kB

Release files / 32word-0.2.1.tar.gz

Download URL 32word-0.2.1.tar.gz
Size 178.8 kB
Tags Source
SHA-256 checksum
How to use checksums
a8cbf699ab0b28b693b100610e25dbc0583949cd5a118ebb227963a64ca304bb
BLAKE2b-256 checksum
How to use checksums
0ec28c26c1c6c855174f67705c40fd3f754dbef7d47ab0f1b440b41a7ee7f2ce
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.2

Release files / 32word-0.2.1-py3-none-any.whl

Download URL 32word-0.2.1-py3-none-any.whl
Size 161.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
745cf21e9a7e6fbce9ba1e5862166ef3c47418c523650cc8cc5d3e4dedd52693
BLAKE2b-256 checksum
How to use checksums
4d2afb45cb1e14c35fc2847229e9267ce5049cb2d6a9761e1edfad84869bc239
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.2

Security for 32word 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page