added rough hangman game

This commit is contained in:
TussockyJoker committed 2026-03-17 15:53:09 +00:00
1 parent 42c47b3f18
commit b1731c4aab
4 files changed
+153 -1

No files matched your search

+11
View File
@@ -0,0 +1,11 @@
from flask import render_template, Blueprint
from database.db_commands import get_all_species
page = Blueprint('game', __name__)
@page.route('/game')
def game():
species = get_all_species()
species_list = [s['species_english'] for s in species]
return render_template('game/game.jinja', species_list=species_list)
+78
View File
@@ -0,0 +1,78 @@
const bodyParts = ['p-head','p-body','p-larm','p-rarm','p-lleg','p-rleg'];
let word, guessed, lives, over;
function initGame() {
word = SPECIES_LIST[Math.floor(Math.random() * SPECIES_LIST.length)].toUpperCase().replace(/\s+/g, ' ').trim();
guessed = [];
lives = 6;
over = false;
bodyParts.forEach(id => document.getElementById(id).setAttribute('display', 'none'));
document.getElementById('hm-msg').textContent = '';
document.getElementById('hm-msg').className = 'hm-msg';
document.getElementById('hm-restart-wrap').style.display = 'none';
renderWord();
renderKeyboard();
renderLives();
renderGuessed();
}
function renderWord() {
const el = document.getElementById('hm-word');
el.innerHTML = word.split('').map(l => {
const show = l === ' ' || guessed.includes(l) || over;
return `<div class="hm-letter"><span>${show ? l : ''}</span>${l !== ' ' ? '<hr>' : ''}</div>`;
}).join('');
}
function renderLives() {
document.getElementById('hm-lives').textContent = `Lives: ${lives}`;
}
function renderGuessed() {
const wrong = guessed.filter(l => !word.includes(l));
document.getElementById('hm-guessed').textContent = wrong.length ? `Wrong: ${wrong.join(', ')}` : '';
}
function renderKeyboard() {
const kb = document.getElementById('hm-keyboard');
kb.innerHTML = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map(l => {
const used = guessed.includes(l);
const isRight = used && word.includes(l);
const isWrong = used && !word.includes(l);
return `<button class="hm-key${isRight ? ' right' : isWrong ? ' wrong' : ''}" ${used || over ? 'disabled' : ''} data-letter="${l}">${l}</button>`;
}).join('');
kb.querySelectorAll('.hm-key:not(:disabled)').forEach(btn => {
btn.addEventListener('click', () => guess(btn.dataset.letter));
});
}
function guess(letter) {
if (over || guessed.includes(letter)) return;
guessed.push(letter);
if (!word.includes(letter)) {
lives--;
document.getElementById(bodyParts[6 - lives - 1]).setAttribute('display', 'inline');
}
const won = word.split('').filter(l => l !== ' ').every(l => guessed.includes(l));
const lost = lives === 0;
if (won || lost) {
over = true;
const msg = document.getElementById('hm-msg');
msg.textContent = won ? `You won! The species was: ${word}` : `Game over! The species was: ${word}`;
msg.className = 'hm-msg ' + (won ? 'win' : 'lose');
document.getElementById('hm-restart-wrap').style.display = 'block';
}
renderWord();
renderKeyboard();
renderLives();
renderGuessed();
}
document.addEventListener('keydown', e => {
const l = e.key.toUpperCase();
if (/^[A-Z]$/.test(l) && !over) guess(l);
});
document.getElementById('hm-restart').addEventListener('click', initGame);
initGame();
-1
View File
@@ -4,7 +4,6 @@
{% endblock %}
{% block content %}
{% from 'button.jinja' import button %}
<!-- Banner section -->
<section style="padding: 100px 20px;">
<h1>About Komodo Hub</h1>
<p>
+64
View File
@@ -0,0 +1,64 @@
{% extends "base.jinja" %}
{% block title %}Hangman{% endblock %}
{% block head %}
<style>
#hangman-app { padding: 1.5rem 0; max-width: 520px; margin: 0 auto; }
.hm-title { font-size: 22px; font-weight: 500; margin: 0 0 0.25rem; }
.hm-subtitle { font-size: 14px; color: #6b7280; margin: 0 0 1.5rem; }
.hm-canvas-wrap { background: #f9fafb; border-radius: 12px; padding: 1rem; margin-bottom: 1.25rem; display: flex; justify-content: center; }
.hm-word { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-bottom: 1.25rem; }
.hm-letter { width: 32px; text-align: center; }
.hm-letter span { display: block; font-size: 22px; font-weight: 500; min-height: 28px; }
.hm-letter hr { border: none; border-top: 2px solid #111827; margin: 4px 0 0; }
.hm-guessed { font-size: 13px; color: #6b7280; text-align: center; margin-bottom: 1.25rem; min-height: 18px; }
.hm-keyboard { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; margin-bottom: 1.25rem; }
.hm-key { width: 36px; height: 36px; border-radius: 8px; border: 1px solid #d1d5db; background: #fff; font-size: 13px; font-weight: 500; cursor: pointer; transition: background 0.1s; }
.hm-key:hover:not(:disabled) { background: #f3f4f6; }
.hm-key:disabled { opacity: 0.3; cursor: not-allowed; }
.hm-key.wrong { background: #fee2e2; color: #991b1b; border-color: #fca5a5; }
.hm-key.right { background: #dcfce7; color: #166534; border-color: #86efac; }
.hm-msg { text-align: center; font-size: 15px; font-weight: 500; margin-bottom: 1rem; min-height: 22px; }
.hm-msg.win { color: #166534; }
.hm-msg.lose { color: #991b1b; }
.hm-lives { text-align: center; font-size: 13px; color: #6b7280; margin-bottom: 0.75rem; }
</style>
{% endblock %}
{% block content %}
{% from 'button.jinja' import button %}
<div id="hangman-app">
<p class="hm-title">Hangman</p>
<p class="hm-subtitle">Guess the species name, one letter at a time.</p>
<div class="hm-canvas-wrap">
<svg id="gallows" width="160" height="160" viewBox="0 0 160 160">
<line x1="20" y1="155" x2="140" y2="155" stroke="#9ca3af" stroke-width="2"/>
<line x1="50" y1="155" x2="50" y2="10" stroke="#9ca3af" stroke-width="2"/>
<line x1="50" y1="10" x2="110" y2="10" stroke="#9ca3af" stroke-width="2"/>
<line x1="110" y1="10" x2="110" y2="30" stroke="#9ca3af" stroke-width="2"/>
<circle id="p-head" cx="110" cy="42" r="12" stroke="#111827" stroke-width="2" fill="none" display="none"/>
<line id="p-body" x1="110" y1="54" x2="110" y2="100" stroke="#111827" stroke-width="2" display="none"/>
<line id="p-larm" x1="110" y1="65" x2="88" y2="85" stroke="#111827" stroke-width="2" display="none"/>
<line id="p-rarm" x1="110" y1="65" x2="132" y2="85" stroke="#111827" stroke-width="2" display="none"/>
<line id="p-lleg" x1="110" y1="100" x2="88" y2="125" stroke="#111827" stroke-width="2" display="none"/>
<line id="p-rleg" x1="110" y1="100" x2="132" y2="125" stroke="#111827" stroke-width="2" display="none"/>
</svg>
</div>
<div id="hm-word" class="hm-word"></div>
<div id="hm-lives" class="hm-lives">Lives: 6</div>
<div id="hm-guessed" class="hm-guessed"></div>
<div id="hm-msg" class="hm-msg"></div>
<div id="hm-keyboard" class="hm-keyboard"></div>
<div id="hm-restart-wrap" style="display:none; text-align:center;">
{{ button(url='/game', button_text='Play Again!', extra_classes='homepage-btn', id='hm-restart') }}
</div>
</div>
<script>
const SPECIES_LIST = {{ species_list | tojson }};
</script>
<script src="{{ url_for('static', filename='game.js') }}"></script>
{% endblock %}