From 4de2a3609964cf25e48c05817e64f7e033a0721f Mon Sep 17 00:00:00 2001 From: Johannes le Roux Date: Thu, 20 Feb 2025 22:53:28 +0200 Subject: [PATCH] added hangman --- config.template | 1 + mesh_bot.py | 30 +++++++++++++- modules/games/hangman.py | 89 ++++++++++++++++++++++++++++++++++++++++ modules/settings.py | 1 + modules/system.py | 9 +++- 5 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 modules/games/hangman.py diff --git a/config.template b/config.template index 4ecc69f..239daad 100644 --- a/config.template +++ b/config.template @@ -254,6 +254,7 @@ blackjack = True videopoker = True mastermind = True golfsim = True +hangman = True [messagingSettings] # delay in seconds for response to avoid message collision diff --git a/mesh_bot.py b/mesh_bot.py index a4ca77e..0948657 100755 --- a/mesh_bot.py +++ b/mesh_bot.py @@ -15,7 +15,7 @@ from modules.log import * from modules.system import * # list of commands to remove from the default list for DM only -restrictedCommands = ["blackjack", "videopoker", "dopewars", "lemonstand", "golfsim", "mastermind"] +restrictedCommands = ["blackjack", "videopoker", "dopewars", "lemonstand", "golfsim", "mastermind", "hangman"] restrictedResponse = "πŸ€–only available in a Direct MessageπŸ“΅" # "" for none # Global Variables @@ -57,6 +57,7 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n "games": lambda: gamesCmdList, "globalthermonuclearwar": lambda: handle_gTnW(), "golfsim": lambda: handleGolf(message, message_from_id, deviceID), + "hangman": lambda: handleHangman(message, message_from_id, deviceID), "hfcond": hf_band_conditions, "history": lambda: handle_history(message, message_from_id, deviceID, isDM), "joke": lambda: tell_joke(message_from_id), @@ -658,6 +659,32 @@ def handleGolf(message, nodeID, deviceID): time.sleep(responseDelay + 1) return msg +def handleHangman(message, nodeID, deviceID): + global hangmanTracker + index = 0 + for i in range(len(hangmanTracker)): + if hangmanTracker[i]['nodeID'] == nodeID: + hangmanTracker[i]["last_played"] = time.time() + index = i+1 + break + + if index and "end" in message.lower(): + hangman.end(nodeID) + hangmanTracker.pop(index-1) + return "bye." + + if not index: + hangmanTracker.append( + { + "nodeID": nodeID, + "last_played": time.time() + } + ) + msg = hangman.play(nodeID, message) + + time.sleep(responseDelay + 1) + return msg + def handle_riverFlow(message, message_from_id, deviceID): location = get_node_location(message_from_id, deviceID) userRiver = message.lower() @@ -982,6 +1009,7 @@ def checkPlayingGame(message_from_id, message_string, rxNode, channel_number): (jackTracker, "BlackJack", handleBlackJack) if 'jackTracker' in globals() else None, (mindTracker, "MasterMind", handleMmind) if 'mindTracker' in globals() else None, (golfTracker, "GolfSim", handleGolf) if 'golfTracker' in globals() else None, + (hangmanTracker, "Hangman", handleHangman) if 'hangmanTracker' in globals() else None, ] trackers = [tracker for tracker in trackers if tracker is not None] diff --git a/modules/games/hangman.py b/modules/games/hangman.py new file mode 100644 index 0000000..c47dac9 --- /dev/null +++ b/modules/games/hangman.py @@ -0,0 +1,89 @@ +# Written for Meshtastic mesh-bot by ZR1RF Johannes le Roux 2025 +import random +import time + + +class Hangman: + + def __init__(self): + self.game = {} + + def new_game(self, id): + games = won = 0 + ret = "" + if id in self.game: + games = self.game[id]["games"] + won = self.game[id]["won"] + ret += f"Total Games: {games}, Won: {won}\n" + + self.game[id] = { + "word": self.random_word(), + "guesses": [], + "games": games+1, + "won": won + } + ret += self.game_continue(id) + return ret + + def guess(self, id, input): + g = self.game[id] + if not input: + return + letter = input[0] + if letter.isalpha() and letter not in g["guesses"]: + g["guesses"].append(letter.lower()) + + def wrong_guesses(self, id): + g = self.game[id] + wrong = 0 + for letter in g["guesses"]: + if letter not in g["word"]: + wrong += 1 + return wrong + + def won(self, id): + g = self.game[id] + for letter in g["word"]: + if letter not in g["guesses"]: + return False + g["won"] += 1 + return True + + def mask(self, id): + g = self.game[id] + return " ".join([a if a in g["guesses"] else "_" for a in g["word"]]) + + def game_board(self, id): + g = self.game[id] + emotions = "πŸ˜€πŸ™‚πŸ˜πŸ˜‘πŸ˜•πŸ˜”πŸ’€" + wrong = self.wrong_guesses(id) + ret = emotions[wrong] + "\n" + ret += hangman.mask(id) + "\n" + if g["guesses"]: + ret += ",".join(g["guesses"]) + "\n" + return ret + + def game_continue(self, id): + return self.game_board(id) + "Guess a letter" + + def game_over(self, id): + return self.game_board(id) + "Game over" + + def play(self, id, input): + if id not in self.game: + return self.new_game(id) + self.guess(id, input) + wrong = self.wrong_guesses(id) + if wrong >= 6 or self.won(id): + return self.game_over(id) + "\n" + self.new_game(id) + return self.game_continue(id) + + def end(self, id): + del self.game[id] + + def random_word(self): + return random.choice("a|ability|able|about|above|accept|according|account|across|act|action|activity|actually|add|address|administration|admit|adult|affect|after|again|against|age|agency|agent|ago|agree|agreement|ahead|air|all|allow|almost|alone|along|already|also|although|always|American|among|amount|analysis|and|animal|another|answer|any|anyone|anything|appear|apply|approach|area|argue|arm|around|arrive|art|article|artist|as|ask|assume|at|attack|attention|attorney|audience|author|authority|available|avoid|away|baby|back|bad|bag|ball|bank|bar|base|be|beat|beautiful|because|become|bed|before|begin|behavior|behind|believe|benefit|best|better|between|beyond|big|bill|billion|bit|black|blood|blue|board|body|book|born|both|box|boy|break|bring|brother|budget|build|building|business|but|buy|by|call|camera|campaign|can|cancer|candidate|capital|car|card|care|career|carry|case|catch|cause|cell|center|central|century|certain|certainly|chair|challenge|chance|change|character|charge|check|child|choice|choose|church|citizen|city|civil|claim|class|clear|clearly|close|coach|cold|collection|college|color|come|commercial|common|community|company|compare|computer|concern|condition|conference|Congress|consider|consumer|contain|continue|control|cost|could|country|couple|course|court|cover|create|crime|cultural|culture|cup|current|customer|cut|dark|data|daughter|day|dead|deal|death|debate|decade|decide|decision|deep|defense|degree|Democrat|democratic|describe|design|despite|detail|determine|develop|development|die|difference|different|difficult|dinner|direction|director|discover|discuss|discussion|disease|do|doctor|dog|door|down|draw|dream|drive|drop|drug|during|each|early|east|easy|eat|economic|economy|edge|education|effect|effort|eight|either|election|else|employee|end|energy|enjoy|enough|enter|entire|environment|environmental|especially|establish|even|evening|event|ever|every|everybody|everyone|everything|evidence|exactly|example|executive|exist|expect|experience|expert|explain|eye|face|fact|factor|fail|fall|family|far|fast|father|fear|federal|feel|feeling|few|field|fight|figure|fill|film|final|finally|financial|find|fine|finger|finish|fire|firm|first|fish|five|floor|fly|focus|follow|food|foot|for|force|foreign|forget|form|former|forward|four|free|friend|from|front|full|fund|future|game|garden|gas|general|generation|get|girl|give|glass|go|goal|good|government|great|green|ground|group|grow|growth|guess|gun|guy|hair|half|hand|hang|happen|happy|hard|have|he|head|health|hear|heart|heat|heavy|help|her|here|herself|high|him|himself|his|history|hit|hold|home|hope|hospital|hot|hotel|hour|house|how|however|huge|human|hundred|husband|I|idea|identify|if|image|imagine|impact|important|improve|in|include|including|increase|indeed|indicate|individual|industry|information|inside|instead|institution|interest|interesting|international|interview|into|investment|involve|issue|it|item|its|itself|job|join|just|keep|key|kid|kill|kind|kitchen|know|knowledge|land|language|large|last|late|later|laugh|law|lawyer|lay|lead|leader|learn|least|leave|left|leg|legal|less|let|letter|level|lie|life|light|like|likely|line|list|listen|little|live|local|long|look|lose|loss|lot|love|low|machine|magazine|main|maintain|major|majority|make|man|manage|management|manager|many|market|marriage|material|matter|may|maybe|me|mean|measure|media|medical|meet|meeting|member|memory|mention|message|method|middle|might|military|million|mind|minute|miss|mission|model|modern|moment|money|month|more|morning|most|mother|mouth|move|movement|movie|Mr|Mrs|much|music|must|my|myself|name|nation|national|natural|nature|near|nearly|necessary|need|network|never|new|news|newspaper|next|nice|night|no|none|nor|north|not|note|nothing|notice|now|number|occur|of|off|offer|office|officer|official|often|oh|oil|ok|old|on|once|one|only|onto|open|operation|opportunity|option|or|order|organization|other|others|our|out|outside|over|own|owner|page|pain|painting|paper|parent|part|participant|particular|particularly|partner|party|pass|past|patient|pattern|pay|peace|people|per|perform|performance|perhaps|period|person|personal|phone|physical|pick|picture|piece|place|plan|plant|play|player|PM|point|police|policy|political|politics|poor|popular|population|position|positive|possible|power|practice|prepare|present|president|pressure|pretty|prevent|price|private|probably|problem|process|produce|product|production|professional|professor|program|project|property|protect|prove|provide|public|pull|purpose|push|put|quality|question|quickly|quite|race|radio|raise|range|rate|rather|reach|read|ready|real|reality|realize|really|reason|receive|recent|recently|recognize|record|red|reduce|reflect|region|relate|relationship|religious|remain|remember|remove|report|represent|Republican|require|research|resource|respond|response|responsibility|rest|result|return|reveal|rich|right|rise|risk|road|rock|role|room|rule|run|safe|same|save|say|scene|school|science|scientist|score|sea|season|seat|second|section|security|see|seek|seem|sell|send|senior|sense|series|serious|serve|service|set|seven|several|sex|sexual|shake|share|she|shoot|short|shot|should|shoulder|show|side|sign|significant|similar|simple|simply|since|sing|single|sister|sit|site|situation|six|size|skill|skin|small|smile|so|social|society|soldier|some|somebody|someone|something|sometimes|son|song|soon|sort|sound|source|south|southern|space|speak|special|specific|speech|spend|sport|spring|staff|stage|stand|standard|star|start|state|statement|station|stay|step|still|stock|stop|store|story|strategy|street|strong|structure|student|study|stuff|style|subject|success|successful|such|suddenly|suffer|suggest|summer|support|sure|surface|system|table|take|talk|task|tax|teach|teacher|team|technology|television|tell|ten|tend|term|test|than|thank|that|the|their|them|themselves|then|theory|there|these|they|thing|think|third|this|those|though|thought|thousand|threat|three|through|throughout|throw|thus|time|to|today|together|tonight|too|top|total|tough|toward|town|trade|traditional|training|travel|treat|treatment|tree|trial|trip|trouble|true|truth|try|turn|TV|two|type|under|understand|unit|until|up|upon|us|use|usually|value|various|very|victim|view|violence|visit|voice|vote|wait|walk|wall|want|war|watch|water|way|we|weapon|wear|week|weight|well|west|western|what|whatever|when|where|whether|which|while|white|who|whole|whom|whose|why|wide|wife|will|win|wind|window|wish|with|within|without|woman|wonder|word|work|worker|world|worry|would|write|writer|wrong|yard|yeah|year|yes|yet|you|young|your|yourself".split('|')) + + +hangmanTracker = [] +hangman = Hangman() diff --git a/modules/settings.py b/modules/settings.py index c132670..ad6b08c 100644 --- a/modules/settings.py +++ b/modules/settings.py @@ -331,6 +331,7 @@ try: videoPoker_enabled = config['games'].getboolean('videoPoker', True) mastermind_enabled = config['games'].getboolean('mastermind', True) golfSim_enabled = config['games'].getboolean('golfSim', True) + hangman_enabled = config['games'].getboolean('hangman', True) # messaging settings responseDelay = config['messagingSettings'].getfloat('responseDelay', 0.7) # default 0.7 diff --git a/modules/system.py b/modules/system.py index f9b4faa..3a0eb2e 100644 --- a/modules/system.py +++ b/modules/system.py @@ -151,7 +151,12 @@ if golfSim_enabled: from modules.games.golfsim import * # from the spudgunman/meshing-around repo trap_list = trap_list + ("golfsim",) games_enabled = True - + +if hangman_enabled: + from modules.games.hangman import * # from the spudgunman/meshing-around repo + trap_list = trap_list + ("hangman",) + games_enabled = True + # Games Configuration if games_enabled is True: help_message = help_message + ", games" @@ -172,6 +177,8 @@ if games_enabled is True: gamesCmdList += "masterMind, " if golfSim_enabled: gamesCmdList += "golfSim, " + if hangman_enabled: + gamesCmdList += "hangman, " gamesCmdList = gamesCmdList[:-2] # remove the last comma else: gamesCmdList = ""