Files
txt3-wap/public_html/games/hangman.php
jamie prince 9473d853ce Add Mastermind and Hangman single-player games
- Mastermind: 4-color sequence, 10 tries, black/white peg feedback
- Hangman: 6 lives, progressive letter reveal, 10-word vocabulary
- Both available in BBS (telnet) and web (WML/XHTML) sides
- Shared sp_games persistence, scores table integration
- Updated games index and menu navigation
2026-09-10 02:09:09 +01:00

98 lines
4.2 KiB
PHP

<?php
// Single player: Hangman. Guess the word, 6 lives.
// State persisted in sp_games (game='hangman').
require_once __DIR__ . '/../lib/bootstrap.php';
$me = require_login();
const H_WORDS = ['python', 'bbs', 'telnet', 'wml', 'matrix', 'linux', 'mastermind', 'screens', 'forum', 'games'];
const H_MAX_LIVES = 6;
function h_load(int $uid): ?array {
$s = db()->prepare("SELECT * FROM sp_games WHERE user_id=? AND game='hangman'
AND status='active' ORDER BY id DESC LIMIT 1");
$s->execute([$uid]);
return $s->fetch() ?: null;
}
function h_new(int $uid): array {
$word = H_WORDS[array_rand(H_WORDS)];
$st = json_encode(['word' => $word, 'used' => [], 'lives' => H_MAX_LIVES]);
$now = time();
db()->prepare("INSERT INTO sp_games (game,user_id,state,created_at,updated_at)
VALUES ('hangman',?,?,?,?)")->execute([$uid, $st, $now, $now]);
return h_load($uid);
}
$msg = ''; $done = false;
$g = h_load((int)$me['id']);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
require_csrf();
$act = $_POST['act'] ?? '';
if ($act === 'new') {
if ($g) db()->prepare("UPDATE sp_games SET status='abandoned' WHERE id=?")->execute([$g['id']]);
$g = h_new((int)$me['id']);
$msg = 'New game! I have chosen a word. Guess letters one at a time.';
} else {
if (!$g) $g = h_new((int)$me['id']);
$st = json_decode($g['state'], true);
$letter = strtolower((string)($_POST['letter'] ?? ''));
if (strlen($letter) !== 1 || !ctype_alpha($letter)) {
$msg = 'Please enter a single letter a-z.';
} elseif (in_array($letter, $st['used'], true)) {
$msg = 'You have already used that letter.';
} else {
$st['used'][] = $letter;
if (in_array($letter, str_split($st['word']), true)) {
$msg = 'Good! ' . $letter . ' is in the word.';
} else {
$st['lives']--;
$msg = $letter . ' is not in the word.';
}
// Check win/loss
$word_letters = array_diff(str_split($st['word']), $st['used']);
if (empty($word_letters)) {
$score = max(1, 50 - (H_MAX_LIVES - $st['lives']) * 5);
db()->prepare("UPDATE sp_games SET status='won', state=?, updated_at=? WHERE id=?")
->execute([json_encode($st), time(), $g['id']]);
db()->prepare("INSERT INTO scores (game,user_id,score,detail,created_at)
VALUES ('hangman',?,?,?,?)")
->execute([$me['id'], $score, $st['lives'] . ' lives left', time()]);
$msg = "Yay! You guessed the word {$st['word']}! Score: $score";
$done = true; $g = null;
} elseif ($st['lives'] <= 0) {
db()->prepare("UPDATE sp_games SET status='lost', state=?, updated_at=? WHERE id=?")
->execute([json_encode($st), time(), $g['id']]);
$msg = "You died! The word was {$st['word']}.";
$done = true; $g = null;
} else {
db()->prepare("UPDATE sp_games SET state=?, updated_at=? WHERE id=?")
->execute([json_encode($st), time(), $g['id']]);
$g['state'] = json_encode($st);
}
}
}
}
page_start('Hangman', ['back' => '/games.php']);
if ($msg) p_para($msg);
if ($g) {
$st = json_decode($g['state'], true);
$display = '';
foreach (str_split($st['word']) as $ch) {
$display .= (in_array($ch, $st['used'], true) ? $ch : '-') . ' ';
}
p_para('Word: ' . trim($display));
p_para('Lives left: ' . $st['lives']);
if ($st['used']) p_para('Used: ' . implode(', ', $st['used']));
p_form('/games/hangman.php', [
['name' => 'letter', 'label' => 'Guess a letter', 'maxlength' => 1,
'format' => '*A'],
], 'Guess');
} else {
if (!$done) p_para('Guess the word one letter at a time. You have 6 lives.');
}
p_form('/games/hangman.php', [
['name' => 'act', 'type' => 'hidden', 'value' => 'new'],
], $g ? 'Restart' : 'New game');
p_links([['/games/scores.php', 'High scores', ['g' => 'hangman']], ['/games.php', 'Games']]);
page_end();