4 Commits

Author SHA1 Message Date
144ac314f7 Tell what game we are on 2024-11-02 23:04:23 +01:00
82d8926e0d Generate dictionary source 2024-11-02 22:51:27 +01:00
4f3e0e4c19 Implement guessing with hint answers 2024-11-02 22:44:45 +01:00
ca14d59400 Requirements to run on server 2024-11-02 21:49:59 +01:00
5 changed files with 194 additions and 5 deletions

View File

@@ -3,3 +3,11 @@
Omdat Nederlanders ook [alphaguess](https://alphaguess.com) willen spelen :) Omdat Nederlanders ook [alphaguess](https://alphaguess.com) willen spelen :)
My own implementation of an alphaguess like game with custom word lists, tailored to be used with a Dutch source. My own implementation of an alphaguess like game with custom word lists, tailored to be used with a Dutch source.
## Environment variables
```
set -x WORD_LIST /path/to/alfagok_wordlist.txt
set -x START_DATE 2024-11-02
```

3
requirements-server.in Normal file
View File

@@ -0,0 +1,3 @@
-r requirements.in
gunicorn

98
requirements-server.txt Normal file
View File

@@ -0,0 +1,98 @@
# This file was autogenerated by uv via the following command:
# uv pip compile requirements-server.in
annotated-types==0.7.0
# via pydantic
anyio==4.6.2.post1
# via
# httpx
# starlette
# watchfiles
certifi==2024.8.30
# via
# httpcore
# httpx
click==8.1.7
# via
# typer
# uvicorn
dnspython==2.7.0
# via email-validator
email-validator==2.2.0
# via fastapi
fastapi==0.115.4
# via -r requirements.in
fastapi-cli==0.0.5
# via fastapi
gunicorn==23.0.0
# via -r requirements-server.in
h11==0.14.0
# via
# httpcore
# uvicorn
httpcore==1.0.6
# via httpx
httptools==0.6.4
# via uvicorn
httpx==0.27.2
# via fastapi
idna==3.10
# via
# anyio
# email-validator
# httpx
jinja2==3.1.4
# via fastapi
markdown-it-py==3.0.0
# via rich
markupsafe==3.0.2
# via jinja2
mdurl==0.1.2
# via markdown-it-py
packaging==24.1
# via gunicorn
pydantic==2.9.2
# via
# fastapi
# pydantic-settings
pydantic-core==2.23.4
# via pydantic
pydantic-settings==2.6.1
# via -r requirements.in
pygments==2.18.0
# via rich
python-dotenv==1.0.1
# via
# pydantic-settings
# uvicorn
python-multipart==0.0.17
# via fastapi
pyyaml==6.0.2
# via uvicorn
rich==13.9.4
# via typer
shellingham==1.5.4
# via typer
sniffio==1.3.1
# via
# anyio
# httpx
starlette==0.41.2
# via fastapi
typer==0.12.5
# via fastapi-cli
typing-extensions==4.12.2
# via
# fastapi
# pydantic
# pydantic-core
# typer
uvicorn==0.32.0
# via
# fastapi
# fastapi-cli
uvloop==0.21.0
# via uvicorn
watchfiles==0.24.0
# via uvicorn
websockets==13.1
# via uvicorn

View File

@@ -1,23 +1,94 @@
"""Main alfagok API application."""
import logging
from datetime import date
from typing import Union from typing import Union
from fastapi import FastAPI from fastapi import FastAPI
from pydantic_settings import BaseSettings
from pydantic import FilePath from pydantic import FilePath
from pydantic_settings import BaseSettings
class Settings(BaseSettings): class Settings(BaseSettings):
"""Configuration needed for alfagok to find its word list, using environment variables."""
# Game words
word_list: FilePath word_list: FilePath
# All valid words
dictionary_list: FilePath
# Date of first game so we can calculate the game ID we're on
start_date: date
debug: bool = False
app = FastAPI() app = FastAPI()
settings = Settings() settings = Settings()
with open(settings.word_list, 'r', encoding='utf-8') as word_file:
# Load the game words
words = word_file.readlines()
with open(settings.dictionary_list, 'r', encoding='utf-8') as word_file:
# Load the list of valid words
dictionary = word_file.readlines()
logger = logging.getLogger('uvicorn.error')
logger.setLevel(logging.INFO)
if settings.debug:
logger.setLevel(logging.DEBUG)
def get_game_id():
"""Calculate the index for the game/word we are handling today."""
today = date.today()
# Calculate the amount of days since the start of the games so we know which word is used today
return (today - settings.start_date).days
def is_valid_dictionary_word(word: str) -> bool:
"""Verify if `word` is in the dictionary provided."""
return f'{word}\n' in dictionary
@app.get("/") @app.get("/")
def read_root(): def read_root():
"""Ohai."""
return {"Hello": "World"} return {"Hello": "World"}
@app.get("/items/{item_id}") @app.get('/api/game')
def read_item(item_id: int, q: Union[str, None] = None): def what_game():
return {"item_id": item_id, "q": q} """Handle incoming guess."""
return {'game': get_game_id()}
@app.get('/api/guess')
def handle_guess(word: Union[str, None] = None):
"""Handle incoming guess."""
current_game_id = get_game_id()
word_of_the_day = words[current_game_id].strip()
if not is_valid_dictionary_word(word):
logger.info('Guess: %s for game %d (%s), word not found in dictionary', word, current_game_id, word_of_the_day)
return {'error': 'Word not in dictionary'}
hint = 'it'
if word_of_the_day < word:
hint = 'before'
if word_of_the_day > word:
hint = 'after'
logger.info('Guess: %s for game %d (%s), goal is %s', word, current_game_id, word_of_the_day, hint)
# before, after, it
return {'game': current_game_id, 'hint': hint}
@app.get("/api/answer/{item_id}")
def read_item(item_id: int, guess: Union[str, None] = None):
"""Get the word item."""
word = words[item_id].strip()
return {"item_id": item_id, "guess": guess, 'word': word}

View File

@@ -9,18 +9,27 @@ with open('basiswoorden-gekeurd.txt', 'r', encoding='utf-8') as wordfile:
all_words = wordfile.readlines() all_words = wordfile.readlines()
print(f'original list contains {len(all_words)} words') print(f'original list contains {len(all_words)} words')
dictionary_list = []
result_list = [] result_list = []
for word in all_words: for word in all_words:
word = word.strip() word = word.strip()
if word.isalpha() and word.lower() == word:
# Word is valid for our dictionary
dictionary_list.append(f'{word}\n')
if word.isalpha() and word.lower() == word and len(word) > MIN_LENGTH and len(word) <= MAX_LENGTH: if word.isalpha() and word.lower() == word and len(word) > MIN_LENGTH and len(word) <= MAX_LENGTH:
# Word is 'fit' for our game
result_list.append(f'{word}\n') result_list.append(f'{word}\n')
# print(result_list) # print(result_list)
print(f'words filtered: {len(result_list)} with length > {MIN_LENGTH} and <= {MAX_LENGTH}') print(f'words filtered: {len(result_list)} with length > {MIN_LENGTH} and <= {MAX_LENGTH}')
print(f'words in dictionary: {len(dictionary_list)}')
with open('filtered.txt', 'w', encoding='utf-8') as f: with open('filtered.txt', 'w', encoding='utf-8') as f:
f.writelines(result_list) f.writelines(result_list)
with open('dictionary.txt', 'w', encoding='utf-8') as f:
f.writelines(dictionary_list)
selection_list = [] selection_list = []
# Randomly select words for each day # Randomly select words for each day