Files
txt3-wap/bbs/games.py
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

145 lines
5.7 KiB
Python

"""Games for the BBS. Shares `scores`, `sp_games` and `matches` with the web
site, so a telnet user and a WAP user can play each other in the same match."""
import json
import random
import time
from bbsdb import q, q1, ex, ago
QUESTIONS = [
("What does WAP stand for?",
["Wireless Application Protocol", "Web Access Point",
"Wide Area Paging", "Wireless Audio Player"], 0),
("WML is based on which language?", ["HTML", "XML", "SGML", "JSON"], 1),
("Which company made the Nokia 7110?",
["Ericsson", "Motorola", "Nokia", "Siemens"], 2),
("A WML file is made up of one or more...",
["Pages", "Cards", "Frames", "Slides"], 1),
("What year was WAP 1.1 published?", ["1996", "1999", "2002", "2005"], 1),
("GPRS stands for General Packet Radio...?",
["System", "Service", "Standard", "Stream"], 1),
]
def games_menu(t, con, me):
while True:
t.header("GAMES")
t.line(" [1] Guess the Number (single player)")
t.line(" [2] Quick Quiz (single player)")
t.line(" [3] Noughts & Crosses (multiplayer)")
t.line(" [4] Nim - 21 sticks (multiplayer)")
t.line(" [5] Mastermind (single player)")
t.line(" [6] Hangman (single player)")
t.line(" [7] High scores")
t.line(" [Q] Back to main")
c = t.ask("\ngames> ").lower()
if c == "1":
guess(t, con, me)
elif c == "2":
quiz(t, con, me)
elif c == "3":
ttt(t, con, me)
elif c == "4":
nim(t, con, me)
elif c == "5":
mastermind(t, con, me)
elif c == "6":
hangman(t, con, me)
elif c == "7":
scores(t, con)
elif c.startswith("q") or c == "":
return
def add_score(con, game, uid, score, detail=""):
ex(con, "INSERT INTO scores (game,user_id,score,detail,created_at)"
" VALUES (?,?,?,?,?)", (game, uid, score, detail, int(time.time())))
# ------------------------------------------------------------------ mastermind
def mastermind(t, con, me):
# Mastermind: 4 pegs, 6 colors, 10 tries
colors = ['1', '2', '3', '4', '5', '6'] # represent colors as digits
secret = [random.choice(colors) for _ in range(4)]
tries = 0
max_tries = 10
t.header("MASTERMIND")
t.line(" I have selected a sequence of 4 colors from 1-6.")
t.line(" You have 10 tries to guess the sequence.")
t.line(" Feedback: Black = correct color and position, White = correct color only.")
t.line(" Enter your guess as 4 digits (e.g. 1234) or Q to quit.")
while tries < max_tries:
s = t.ask("\n guess> ", maxlen=10)
if s.lower().startswith("q"):
t.line(" The code was %s." % ''.join(secret))
return
if len(s) != 4 or not all(c in colors for c in s):
t.line(" Enter exactly 4 digits from 1-6.")
continue
guess = list(s)
tries += 1
# Calculate black and white pegs
black = sum(1 for i in range(4) if guess[i] == secret[i])
# Count occurrences of each color in secret and guess for white pegs
secret_counts = {c: secret.count(c) for c in set(secret)}
guess_counts = {c: guess.count(c) for c in set(guess)}
white = sum(min(secret_counts.get(c, 0), guess_counts.get(c, 0)) for c in set(secret)) - black
if black == 4:
score = max(1, 100 - 10 * tries)
add_score(con, "mastermind", me["id"], score, "%d tries" % tries)
t.line(" Correct! %d in %d tries. Score %d." % (''.join(secret), tries, score))
return
else:
t.line(" Feedback: %d Black, %d White. (tries: %d)" % (black, white, tries))
t.line(" You ran out of tries. The code was %s." % ''.join(secret))
def hangman(t, con, me):
# Hangman: word guessing game, 6 lives
import string
WORDS = ["python", "bbs", "telnet", "wml", "matrix", "linux", "mastermind", "screens", "forum", "games"]
word = random.choice(WORDS)
word_letters = set(word)
alphabet = set(string.ascii_lowercase)
used_letters = set()
lives = 6
t.header("HANGMAN")
t.line("I am thinking of a word. It has %d letters." % len(word))
t.line("You have %d lives." % lives)
while len(word_letters) > 0 and lives > 0:
# What current word is (with dashes for unguessed letters)
word_list = [letter if letter in used_letters else "-" for letter in word]
t.line("Current word: " + " ".join(word_list))
t.line("Lives left: %d" % lives)
t.line("Used letters: " + " ".join(sorted(used_letters)))
# User input
t.line("Guess a letter:")
s = t.ask("> ").lower()
if len(s) != 1 or s not in alphabet:
t.line("Please enter a single letter from a-z.")
continue
# If user has already guessed this letter
if s in used_letters:
t.line("You have already used that letter. Please try again.")
continue
# Add letter to used
used_letters.add(s)
# Check if letter is in word
if s in word_letters:
word_letters.discard(s)
t.line("Good! %s is in the word." % s)
else:
lives -= 1
t.line("%s is not in the word." % s)
# Game over
if lives == 0:
t.line("You died! The word was %s." % word)
else:
t.line("Yay! You guessed the word %s!" % word)
score = max(1, 50 - (6 - lives) * 5)
add_score(con, "hangman", me["id"], score, "%d lives remaining" % lives)
def scores(t, con):
names = {"guess": "Guess the Number", "quiz": "Quick Quiz",
"ttt": "Noughts & Crosses", "nim": "Nim", "mastermind": "Mastermind", "hangman": "Hangman"}