3 Commits

Author SHA1 Message Date
b2bf55935a Translation 2024-11-03 21:34:48 +01:00
cdd0085f26 Using Alpine.js to make the game page dynamic 2024-11-03 21:28:50 +01:00
ff7ad42e32 Serve static HTML game page 2024-11-03 20:14:03 +01:00
7 changed files with 158 additions and 7 deletions

View File

@@ -4,10 +4,13 @@ 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.
Using Alpine.js on the frontend and FastAPI as backend.
## Environment variables ## Environment variables
``` ```
set -x WORD_LIST /path/to/alfagok_wordlist.txt set -x WORD_LIST /path/to/alfagok_wordlist.txt
set -x START_DATE 2024-11-02 set -x START_DATE 2024-11-02
set -x DICTIONARY_LIST /path/to/dictionary.txt
``` ```

View File

@@ -1,11 +1,13 @@
"""Main alfagok API application.""" """Main alfagok API application."""
import logging import logging
from datetime import date from datetime import date
from typing import Union from typing import Union
from fastapi import FastAPI from fastapi import FastAPI, Request
from pydantic import FilePath from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from pydantic import DirectoryPath, FilePath
from pydantic_settings import BaseSettings from pydantic_settings import BaseSettings
@@ -19,11 +21,17 @@ class Settings(BaseSettings):
# Date of first game so we can calculate the game ID we're on # Date of first game so we can calculate the game ID we're on
start_date: date start_date: date
static_dir: DirectoryPath = 'static'
template_dir: DirectoryPath = 'templates'
debug: bool = False debug: bool = False
app = FastAPI() app = FastAPI()
settings = Settings() settings = Settings()
app.mount('/static', StaticFiles(directory=settings.static_dir), name='static')
templates = Jinja2Templates(directory=settings.template_dir)
with open(settings.word_list, 'r', encoding='utf-8') as word_file: with open(settings.word_list, 'r', encoding='utf-8') as word_file:
# Load the game words # Load the game words
@@ -53,10 +61,11 @@ def is_valid_dictionary_word(word: str) -> bool:
return f'{word}\n' in dictionary return f'{word}\n' in dictionary
@app.get("/") @app.get("/", response_class=HTMLResponse)
def read_root(): async def index(request: Request):
"""Ohai.""" """Generate the main HTML page of the game."""
return {"Hello": "World"} language = 'nl'
return templates.TemplateResponse(request=request, name='index.html', context={'language': language})
@app.get('/api/game') @app.get('/api/game')

View File

@@ -0,0 +1,10 @@
var clip = new Clipboard('.copy');
clip.on("success", function(e) {
document.getElementById('copyresults').innerHTML = '<p style="font-size:var(--small);opacity:50%">Copied! Share your results with friends.</p>';
e.clearSelection();
});
clip.on("error", function() {
document.getElementById('copyresults').innerHTML = '<p style="font-size:var(--small);opacity:50%">Error. Please copy manually...</p>';
});

View File

@@ -0,0 +1,23 @@
/* Original from alphaguess.com */
function go() {
window.timerID = window.setInterval(timer, 0);
}
function timer(){
var nextgame = document.getElementById('nextgame');
var now = new Date();
var midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate()+1, 0, 0, 0);
var diff = Math.floor((midnight - now)/1000);
var hoursRemain = Math.floor(diff/(60*60));
var minutesRemain = Math.floor((diff-hoursRemain*60*60)/60);
var secondsRemain = Math.floor(diff%60);
nextgame.innerHTML = '<span class="nextgame">'+addZero(hoursRemain)+':'+addZero(minutesRemain)+':'+addZero(secondsRemain)+' over</span>';
}
function addZero(num){
if(num <=9) return '0'+num;
else return num;
}
go();

View File

@@ -0,0 +1,4 @@
body {
background-color: #333;
color: #FFF;
}

View File

View File

@@ -0,0 +1,102 @@
<!DOCTYPE html>
<html lang="{{ language }}">
<head>
<title>alfagok</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/static/game.css">
</head>
<body>
<div
id="container"
x-data="{
search: '',
items: ['foo', 'bar', 'baz'],
winTime: null,
startTime: null,
nrGuesses: 0,
guessesBefore: ['blah'],
guessesAfter: [],
guessValue: '',
gameID: 0,
get filteredItems() {
return this.items.filter(
i => i.startsWith(this.search)
)
}
}"
>
<a href="/" x-cloak class="title">alfagok</a> <span class="puzzleno">puzzle #[[ gameID ]] • <span id="nextgame"></span></span>
<div x-cloak x-if="guessesBefore.length || guessesAfter.length"><br></div>
<div x-cloak class="instructions" x-else>
<p>Guess the word of the day. Each guess reveals where the word sits alphabetically.</p>
</div>
<center>
<ul>
<template x-for="item in guessesBefore" :key="item">
<li x-text="item"></li>
</template>
</ul>
<input type="text" x-model="guessValue">
<p x-cloak>Je huidige gok is: <span x-text="guessValue"></span></p>
<input type="button" value="Doe een gok">
<ul>
<template x-for="item in guessesAfter" :key="item">
<li x-text="item"></li>
</template>
</ul>
<div>
<div v-if="winTime" class="win">
<h3><b>Je hebt hem! 🎉</b></h3>
<p>Het woord van vandaag was <b x-text="guessValue"></b>.</p>
<div id="stats">
<div id="results">
<p><b x-data="{ message: '🧩 Puzzle #' + gameID }" x-text="message"></b></p>
<p x-data="{ message: '🤔 '+ nrGuesses + ' gokken' }" x-text="message"></p>
<p x-data="{ message: '⏱️ ' + getFormattedTime(winTime - startTime) '" x-text="message"></p>
<p>🔗 <span style="color:var(--blue)">alfagok.diginaut.net</span></p>
</div>
</div>
<div id="copyresults"></div>
<button class="copy" data-clipboard-target="#results">
Tik om te kopi&euml;ren te delen ❤️
</button>
</div>
</center>
<input x-model="search" placeholder="Search...">
<ul>
<template x-for="item in filteredItems" :key="item">
<li x-text="item"></li>
</template>
</ul>
</div>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.12/clipboard.min.js" rel=preload></script>
<script src="/static/copy.js"></script>
<script src="/static/countdown.js"></script>
<script src="/static/game.js"></script>
</body>
</html>