Skip to content

gaohey/wordle_pythonanywhere

Repository files navigation

Wordle Solver Bot & API

An optimized Wordle solver with a Telegram bot interface and REST API, deployable on PythonAnywhere. Uses a precomputed pattern matrix with min-max ranking to deliver hints in milliseconds.


What It Does

  • Telegram Bot (flask_app.py): Play Wordle or get solving hints via Telegram commands (/wordle, /solver, /hint, /rollback).
  • REST API (/get-hint/<guessAndScore>): Returns the optimal next guess given a history of guesses and scores — designed for a Chrome extension frontend.
  • Solver Engine: Uses min-max optimization (minimize the worst-case remaining group size) to find the best guess at each step. The full 13,033 × 13,033 pattern matrix is precomputed once and loaded at server startup for O(1) pattern lookups.

Solver Architecture

wordle_fast.py — Core Engine

The low-level scoring engine. Converts words to integer matrices and scores one guess against ALL answers in a single vectorized NumPy operation. Handles:

  • Pattern encoding (base-3 integers via PATTERN_WEIGHTS = [81, 27, 9, 3, 1])
  • Index-based answer space filtering (boolean masks on integer arrays, no DataFrame copies)
  • Word list loading and validation

wordle_solver.py — Min-Max Solver (latest)

Built on wordle_fast.py's core engine. Key features:

  • Precomputed pattern matrix: 13,033 × 13,033 stored as 2 compressed .npz chunks (~37 MB each), loaded into RAM at startup and shared across all user sessions
  • Min-max ranking: Picks the guess that minimizes the largest remaining candidate group, directly bounding worst-case performance to ≤6 moves for 98.24% of words
  • Optimal opener: Precomputes the best first guess (SERAI) at startup using min-max over the full dictionary
  • O(1) pattern lookups: matrix[guess_idx] instead of computing scores on the fly

Result: 230 failures out of 13,033 words (1.76%), all on obscure/non-corpus words.


File Structure

wordle_api/
├── wordle_fast.py              # Core scoring engine (vectorized NumPy)
├── wordle_solver.py            # Min-max solver with precomputed matrix
├── flask_app.py                # Flask app + Telegram bot (uses wordle_solver)
├── build_pattern_matrix.py     # One-time script to build .npz chunks
├── pattern_matrix_chunk0.npz   # Precomputed matrix — rows 0–6516
├── pattern_matrix_chunk1.npz   # Precomputed matrix — rows 6517–13032
├── pattern_matrix_meta.npz     # Metadata (word list, shapes, version)
├── validated__words.csv        # 13,033 five-letter words
├── test_all_words_solve.py     # Full solver test (all words as answers)
└── README.md                   # This file

API Endpoints

GET /get-hint/<guessAndScore>

Returns the single best next guess.

GET /get-hint/serai-00000

Response:

{
  "hint": "ponty",
  "possible_choices": 629,
  "guess_and_score": {"serai": "00000"}
}

GET /get-hint-multi/<guessAndScore>

Returns the top-5 guesses plus progress tracking.

GET /get-hint-multi/serai-12011-strew-11220

Response:

{
  "hint": "tares",
  "possible_choices": 1,
  "guess_and_score": {"serai": "12011", "strew": "11220"},
  "top_hints": ["tares", ...],
  "progress_tracker": [32, 1]
}

Format: <guess><score> pairs separated by -. Score is a 5-digit string of 0 (grey), 1 (yellow), 2 (green).


Telegram Bot Commands

Command Description
/start Show available commands
/wordle Start a new Wordle game (bot picks a secret word)
/solver Start solver mode (you provide guess + score from your own game)
/hint Get the optimal next guess
/rollback Undo the last guess (solver mode only)

How to Deploy on PythonAnywhere

Prerequisites

  • A PythonAnywhere account (free tier works, paid has more RAM)
  • Python 3.10+ environment

Step 1: Upload files

Via PythonAnywhere's Files tab or git clone:

wordle_api/
├── wordle_fast.py
├── wordle_solver.py
├── flask_app.py
├── build_pattern_matrix.py
├── validated__words.csv
├── pattern_matrix_chunk0.npz  (if already built)
├── pattern_matrix_chunk1.npz  (if already built)
└── pattern_matrix_meta.npz    (if already built)

Step 2: Install dependencies

Open a Bash console on PythonAnywhere:

pip install flask numpy pandas telepot urllib3

Step 3: Build the pattern matrix (one-time)

If you didn't upload the .npz files:

python build_pattern_matrix.py

This takes ~60–90 seconds and produces the 3 .npz files (~75 MB total).

Step 4: Configure the web app

  1. Go to the Web tab and create a new web app
  2. Choose Manual configurationPython 3.10+
  3. Set the source code directory to the path containing your files
  4. Set the WSGI configuration file to point to flask_app.py's app object

Example WSGI file (/var/www/yourusername_pythonanywhere_com_wsgi.py):

import sys
sys.path.insert(0, '/home/yourusername/wordle_api')
from flask_app import app as application

Step 5: Set up the Telegram webhook (optional)

In flask_app.py, update the webhook URL:

bot.setWebhook("https://yourusername.pythonanywhere.com/{}".format(secret), max_connections=15)

Step 6: Reload

Click the Reload button on the Web tab. The first reload takes ~10 seconds (matrix loads from disk). If the matrix hasn't been built yet, the first reload takes ~90 seconds.

Memory Notes

  • The pattern matrix uses ~170 MB of RAM after loading
  • PythonAnywhere free tier: 512 MB limit — this fits comfortably
  • Each additional user session adds negligible memory (~a few KB for answer_indices)

Performance

Scenario Time
Matrix load at startup ~0.7 seconds
First hint (full 13k ranking) ~0.2 seconds
Subsequent hints (~100 candidates) ~50 ms
Full game solve (6 turns) ~0.2 seconds total
New user session creation instant (shared matrix)

Solver accuracy (tested against all 13,033 words):

  • 98.24% solved in ≤6 moves (230 failures, all on obscure/rare words)
  • Average solve: ~4.2 moves
  • Optimal opener: SERAI (worst-case bucket: 714)

Technical Details

Score Encoding

Wordle feedback for a 5-letter guess is encoded as a base-3 integer:

  • 0 — letter not in the word (grey)
  • 1 — letter in the word but wrong position (yellow)
  • 2 — letter in correct position (green)
PATTERN_WEIGHTS = [81, 27, 9, 3, 1]
# "21012" → 2×81 + 1×27 + 0×9 + 1×3 + 2×1 = 194

Min-Max Ranking

The solver ranks guesses by three criteria in order:

  1. Smallest worst-case bucket — minimizes the maximum remaining group after the guess
  2. Possible answer preference — biases toward guesses that could be the actual answer
  3. Entropy — tiebreaker using expected information gain

This contrasts with pure entropy minimization, which optimizes the average case but doesn't bound the worst case. Min-max directly reduces the number of 7-move failures.

About

wordle solver API on pythonanywhere

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages