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()