codenames / app.py
nathanael-fijalkow's picture
first commiiiiiiiiit
641f13e
import gradio as gr
INTRO_MD = """
# πŸ•΅οΈ Codenames LLM Challenge
Welcome! This is a coding challenge where you build a **guesser bot** for the board game
[Codenames](https://en.wikipedia.org/wiki/Codenames_(board_game)).
An LLM-powered **spymaster** will give you one-word clues. Your job is to write the
**guesser** that picks the right words on the board.
---
## How the game works
The board has **25 words** split into hidden roles:
| Role | Count | Meaning |
|------|-------|---------|
| πŸŸ₯ RED | 9 | Your team's words β€” guess these! |
| 🟦 BLUE | 8 | Opponent words β€” avoid these |
| πŸ’€ ASSASSIN | 8 | Instant loss if revealed |
Each round:
1. The **spymaster** gives a one-word clue + a number (e.g. `"ocean 3"`)
2. Your **guesser** can make up to `number + 1` guesses
3. A round ends when you reveal a BLUE word, reach the max guesses, or return `None`
4. The game ends when **all RED words are found** (win) or the **assassin is revealed** (loss)
Your score is based on the number of rounds needed β€” fewer is better.
---
## Two steps to participate
"""
STEP1_MD = """
## Step 1 β€” Clone the repository
Open a terminal and run:
```bash
git clone https://huggingface.co/spaces/LLM-course/codenames
cd codenames
```
Then set up the Python environment:
```bash
uv venv
source .venv/bin/activate # on Windows: .venv\\Scripts\\activate
uv pip install -r requirements.txt
```
*(Optional but recommended)* Pre-build the embedding cache so runs are faster:
```bash
python -m codenames.cli init-cache
```
"""
STEP2_MD = """
## Step 2 β€” Implement `my_guesser.py`
Open `my_guesser.py` and fill in the `guesser` function:
```python
# my_guesser.py
from codenames.embedder import EmbeddingIndex
from codenames.dictionary import load_dictionary
_dictionary = load_dictionary()
_index = EmbeddingIndex(_dictionary)
def guesser(clue: str, board_state: list[str]) -> str | None:
\"\"\"
Args:
clue: The spymaster's one-word clue (always from the 420-word dictionary)
board_state: List of unrevealed words currently on the board
Returns:
A word from board_state to guess, or None to stop guessing this round.
\"\"\"
# TODO: replace this with your smart logic!
return board_state[0]
```
### Tips
- Use **embedding similarity** between the clue and board words β€” `_index.vector(word)`
returns a numpy vector for any dictionary word.
- Return `None` when you're not confident: it's safer than hitting a BLUE or ASSASSIN word.
- The clue is always a word from the shared 420-word dictionary (case-insensitive).
### Test your guesser
```bash
python -m codenames.cli challenge my_guesser.py --seed 42 --output log.json
```
Options:
- `--seed` β€” fix the board for reproducibility
- `--max-rounds` β€” limit rounds (default 10)
- `--output` β€” save a detailed JSON log with clues, guesses and the final result
The JSON log is useful for inspecting mistakes and improving your strategy.
"""
RULES_MD = """
## Full rules summary
- Board: 25 words, 9 RED / 8 BLUE / 8 ASSASSIN (challenge / single-team mode)
- Clue + number are given each round by the LLM spymaster
- Guesser may guess up to `number + 1` times per round
- Revealing a BLUE word ends the round immediately
- Revealing the ASSASSIN ends the game immediately (loss)
- **Goal:** reveal all 9 RED words in as few rounds as possible
## Dictionary
All clue words and board words are drawn from a **fixed list of 420 Codenames words**
(see `codenames/data/codenames_dict.txt`). Your guesser receives words from this list
and must return a word from the current `board_state`.
"""
with gr.Blocks(title="Codenames LLM Challenge") as demo:
gr.Markdown(INTRO_MD)
with gr.Tabs():
with gr.TabItem("πŸ› οΈ Step 1 β€” Clone"):
gr.Markdown(STEP1_MD)
with gr.TabItem("πŸ’‘ Step 2 β€” Implement"):
gr.Markdown(STEP2_MD)
with gr.TabItem("πŸ“‹ Full Rules"):
gr.Markdown(RULES_MD)
if __name__ == "__main__":
demo.launch()