Last active
January 7, 2026 22:17
-
-
Save peteristhegreat/63c2527b222208e2527ca5fd5671c358 to your computer and use it in GitHub Desktop.
10 Room Maze
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Quick dungeon | |
| import random | |
| import itertools | |
| import math | |
| DIR_VEC = { | |
| "n": (0, 1, 0), | |
| "s": (0, -1, 0), | |
| "e": (1, 0, 0), | |
| "w": (-1, 0, 0), | |
| "ne": (1, 1, 0), | |
| "nw": (-1, 1, 0), | |
| "se": (1, -1, 0), | |
| "sw": (-1,-1, 0), | |
| "u": (0, 0, 1), | |
| "d": (0, 0,-1), | |
| } | |
| def dot(a,b): return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] | |
| def norm(v): return math.sqrt(dot(v,v)) | |
| def cos_sim(a,b): | |
| na, nb = norm(a), norm(b) | |
| return dot(a,b) / (na*nb) | |
| def best_return_dir(b_free, dir_from_a): | |
| target = tuple(-x for x in DIR_VEC[dir_from_a]) | |
| best = None | |
| best_score = -2.0 | |
| for d in b_free: | |
| score = cos_sim(DIR_VEC[d], target) | |
| if score > best_score: | |
| best_score = score | |
| best = d | |
| return best, best_score | |
| def add_pair(a, b, threshold): | |
| a_used = nodes[a]["exits"] | |
| b_used = nodes[b]["exits"] | |
| a_free = list(directions - a_used.keys()) | |
| b_free = list(directions - b_used.keys()) | |
| if not a_free or not b_free: | |
| return False # ran out of direction slots | |
| random.shuffle(a_free) | |
| # try each possible outgoing dir from a, pick the best return dir in b | |
| best_choice = None # (dir_a, dir_b, score) | |
| for dir_a in a_free: | |
| dir_b, score = best_return_dir(b_free, dir_a) | |
| if dir_b is None: | |
| continue | |
| if score >= threshold: | |
| # good enough; take first fit to keep it fast/varied | |
| nodes[a]["exits"][dir_a] = b | |
| nodes[b]["exits"][dir_b] = a | |
| return True | |
| # keep best in case we relax later (optional) | |
| if best_choice is None or score > best_choice[2]: | |
| best_choice = (dir_a, dir_b, score) | |
| return False | |
| def solve_unsolved(unsolved_pairs): | |
| # passes: strict, near, rough, random | |
| passes = [ | |
| (0.999999, "strict"), | |
| (0.707106, "near"), | |
| (0.0, "rough"), | |
| (-2.0, "random"), | |
| ] | |
| remaining = unsolved_pairs[:] | |
| for thr, name in passes: | |
| if not remaining: | |
| break | |
| next_remaining = [] | |
| for a,b in remaining: | |
| ok = add_pair(a,b,thr) | |
| if not ok: | |
| next_remaining.append((a,b)) | |
| remaining = next_remaining | |
| return remaining | |
| directions = {'n','s','e','w','ne','nw','se','sw','u','d'} | |
| inverse_dir = { | |
| 'n':'s', | |
| 's':'n', | |
| 'e':'w', | |
| 'w':'e', | |
| 'ne':'sw', | |
| 'nw':'se', | |
| 'se':'nw', | |
| 'sw':'ne', | |
| 'u':'d', | |
| 'd':'u' | |
| } | |
| secret_code = str(random.randint(1000, 9999)) | |
| start = 0 | |
| end = 11 | |
| # nodes = {i:{"exits":{e:0 for e in directions}} for i in range(12)} | |
| nodes = {i:{"exits":{}, "desc": f"In room {i}."} for i in range(0,12)} | |
| def rd_set(keys): | |
| unused_directions = list(directions.difference(keys)) | |
| random.shuffle(unused_directions) | |
| return unused_directions | |
| def room_set(values): | |
| unreachable_rooms = list(set(range(0,12)).difference(values)) | |
| return unreachable_rooms | |
| # def rd(keys): | |
| # return random.randint(0,len(keys)) | |
| nodes[0]["desc"] = "Maze Start (0)" | |
| pairs = list(itertools.combinations(list(range(1,11)), 2)) | |
| random.shuffle(pairs) | |
| pairs.insert(0, (0,1)) | |
| pairs.insert(1, (10,11)) | |
| unsolved_pairs = [] | |
| for a,b in pairs: | |
| # try strict first | |
| ok = add_pair(a,b,0.999999) | |
| if not ok: | |
| unsolved_pairs.append((a,b)) | |
| still_unsolved = solve_unsolved(unsolved_pairs) | |
| if still_unsolved: | |
| print("Could not solve (ran out of direction slots):", still_unsolved) | |
| # for a,b in pairs: | |
| # keys = nodes[a]["exits"].keys() | |
| # dir = None | |
| # opposite = None | |
| # # while not dir and not opposite and opposite not in nodes[b]["exits"]: | |
| # tries = 0 | |
| # max_tries = 10 | |
| # while not dir and not opposite or (inverse_dir[dir] not in nodes[b]["exits"] and tries < max_tries): | |
| # dir = rd_set(keys)[0] | |
| # opposite = inverse_dir[dir] | |
| # tries += 1 | |
| # if inverse_dir[dir] in nodes[b]["exits"]: | |
| # # can't use this one directly, save for later | |
| # unsolved_pairs.append((a,b)) | |
| # else: | |
| # nodes[a]["exits"] |= { dir: b } | |
| # nodes[b]["exits"] |= { opposite: a } | |
| for room in range(2, 10): | |
| keys = nodes[room]["exits"].keys() | |
| loop_back = rd_set(keys)[0] | |
| nodes[room]["exits"][loop_back] = room | |
| for room in range(0,12): | |
| keys = nodes[room]["exits"].keys() | |
| other_exits = rd_set(keys) | |
| values = nodes[room]["exits"].values() | |
| unreachable_rooms = room_set(values) | |
| print(room,"has other exits", other_exits) | |
| print(room,"can't reach", unreachable_rooms) | |
| print(nodes) | |
| # nodes[1]["exits"] = { e: door for e, door in zip(rd_set({}),range(10) )} | |
| # for room in range(2,11): | |
| # keys = nodes[room]["exits"].keys() | |
| # nodes[room]["exits"] = {e: door for e, door in zip(rd_set(keys), range(room, 11))} | |
| # room = 10 | |
| # keys = nodes[room]["exits"].keys() | |
| # nodes[room]["exits"] = {e: door for e, door in zip(rd_set(keys), [11])} | |
| # nodes[11]["desc"] = "Maze Exit (11)" | |
| # nodes = { | |
| # "start":{ | |
| # "desc":"In start", | |
| # "exits":{ | |
| # "n": "finish", | |
| # "s": "a1", | |
| # } | |
| # }, | |
| # "finish":{ | |
| # "desc":"You see a keypad", | |
| # "exits":{ | |
| # secret_code: "win", | |
| # "s":"start" | |
| # } | |
| # }, | |
| # "a1":{ | |
| # "desc":"South of maze", | |
| # "exits":{ | |
| # "n": "start", | |
| # "e": "b1" | |
| # } | |
| # }, | |
| # "b1":{ | |
| # "desc": f"Side room, and you see scrawled on the wall '{secret_code}'", | |
| # "exits":{ | |
| # "s":"a1" | |
| # } | |
| # } | |
| # } | |
| # Canonical direction order (NOT a set) | |
| DIRECTION_ORDER = ("n","s","e","w","ne","nw","se","sw","u","d") | |
| DIR_INDEX = {d:i for i,d in enumerate(DIRECTION_ORDER)} | |
| def init_visit_state(nodes): | |
| for r, node in nodes.items(): | |
| node.setdefault("visited", False) | |
| node.setdefault("traversed", {}) # dir -> bool | |
| def ordered_dirs(iterable_dirs): | |
| return sorted(iterable_dirs, key=lambda d: DIR_INDEX.get(d, 10**9)) | |
| # Add once when building nodes | |
| for rid in nodes: | |
| nodes[rid].setdefault("nick", str(rid)) # room nickname for map display | |
| nodes[rid].setdefault("visited", False) | |
| nodes[rid].setdefault("traversed", {}) # dir -> bool | |
| def _fmt_dir_for_room(room, d, *, map_level): | |
| """ | |
| Returns (shown_dir, shown_dest_or_blank) | |
| """ | |
| trav = nodes[room].get("traversed", {}) | |
| exits = nodes[room]["exits"] | |
| shown = d.upper() if (map_level >= 1 and trav.get(d, False)) else d | |
| # destination naming rules: | |
| # map_level 0/1: none | |
| # map_level 2: only if traversed | |
| # map_level >=3: always if exit exists | |
| show_dest = (map_level >= 3) or (map_level >= 2 and trav.get(d, False)) | |
| if show_dest and d in exits: | |
| dest = exits[d] | |
| dest_nick = nodes[dest].get("nick", str(dest)) | |
| return shown, dest_nick | |
| return shown, "" | |
| def _cell(room, d, *, map_level, width=14): | |
| """ | |
| Build a single cell like: | |
| "NE" or "NE Kitchen" (padded to width) | |
| If the exit doesn't exist, returns blanks. | |
| """ | |
| exits = nodes[room]["exits"] | |
| if d not in exits: | |
| return " " * width | |
| shown, dest = _fmt_dir_for_room(room, d, map_level=map_level) | |
| text = f"{shown} {dest}".rstrip() | |
| return f"{text:<{width}}" | |
| def _center(text, width): | |
| return f"{text:^{width}}" | |
| def question_flat(msg, answers, *, room=None, map_level=0): | |
| """ | |
| map_level: | |
| 0 = no map decorations | |
| 1 = capitalize traversed exits | |
| 2 = capitalize traversed exits + show destination nick (only if traversed) | |
| 3 = show destination nick always (optional upgrade) | |
| """ | |
| resp = "" | |
| visible_answers = ordered_dirs(list(answers)) | |
| if secret_code in visible_answers: | |
| visible_answers.remove(secret_code) | |
| if map_level and room is not None: | |
| trav = nodes[room].get("traversed", {}) | |
| exits = nodes[room]["exits"] | |
| def fmt_dir(d): | |
| shown = d.upper() if (map_level >= 1 and trav.get(d, False)) else d | |
| if (map_level >= 2 and trav.get(d, False)) or (map_level >= 3): | |
| dest = exits[d] | |
| dest_nick = nodes[dest].get("nick", str(dest)) | |
| return f"{shown} {dest_nick}" | |
| return shown | |
| visible_answers_disp = [fmt_dir(d) for d in visible_answers] | |
| else: | |
| visible_answers_disp = visible_answers | |
| visible_answers_str = "|".join(visible_answers_disp) | |
| valid = set(visible_answers) | {d.upper() for d in visible_answers} | |
| while resp not in valid: | |
| resp = input(f"{msg} [{visible_answers_str}] ").strip() | |
| return resp.lower() | |
| def question_dpad(msg, answers, *, room=None, map_level=0, colw=16): | |
| """ | |
| Same semantics as question_flat, but shows a DPAD view plus a compact input list. | |
| """ | |
| resp = "" | |
| visible_answers = ordered_dirs(list(answers)) | |
| if secret_code in visible_answers: | |
| visible_answers.remove(secret_code) | |
| if room is not None and map_level > 0: | |
| cur = nodes[room].get("nick", str(room)) | |
| mid = _center(cur, colw) | |
| # 3-column layout (nw/n/ne), (w/cur/e), (sw/s/se) | |
| line1 = f"{'':<{colw}}{_center('U', colw)}{'':<{colw}}" | |
| line2 = f"{'':<{colw}}{_cell(room,'u',map_level=map_level,width=colw)}{'':<{colw}}" | |
| line3 = f"{_center('NW', colw)}{_center('N', colw)}{_center('NE', colw)}" | |
| line4 = f"{_cell(room,'nw',map_level=map_level,width=colw)}{_cell(room,'n',map_level=map_level,width=colw)}{_cell(room,'ne',map_level=map_level,width=colw)}" | |
| line5 = f"{_center('W', colw)}{mid}{_center('E', colw)}" | |
| line6 = f"{_cell(room,'w',map_level=map_level,width=colw)}{mid}{_cell(room,'e',map_level=map_level,width=colw)}" | |
| line7 = f"{_center('SW', colw)}{_center('S', colw)}{_center('SE', colw)}" | |
| line8 = f"{_cell(room,'sw',map_level=map_level,width=colw)}{_cell(room,'s',map_level=map_level,width=colw)}{_cell(room,'se',map_level=map_level,width=colw)}" | |
| line9 = f"{'':<{colw}}{_center('D', colw)}{'':<{colw}}" | |
| line10 = f"{'':<{colw}}{_cell(room,'d',map_level=map_level,width=colw)}{'':<{colw}}" | |
| print(msg) | |
| # print(line1); | |
| print(line2) | |
| # print(line3); | |
| print(line4) | |
| # print(line5); | |
| print(line6) | |
| # print(line7); | |
| print(line8) | |
| # print(line9); | |
| print(line10) | |
| # keep a compact prompt too (user types direction only) | |
| # show decorated tokens (dir or DIR nick) in the bracket list | |
| if room is not None and map_level > 0: | |
| disp = [] | |
| for d in visible_answers: | |
| shown, dest = _fmt_dir_for_room(room, d, map_level=map_level) | |
| token = f"{shown} {dest}".rstrip() | |
| disp.append(token) | |
| visible_answers_str = "|".join(disp) | |
| else: | |
| visible_answers_str = "|".join(visible_answers) | |
| valid = set(visible_answers) | {d.upper() for d in visible_answers} | |
| while resp not in valid: | |
| # resp = input(f"[{visible_answers_str}] ").strip() | |
| resp = input("? ").strip() | |
| return resp.lower() | |
| def mark_traversal(a, dir_a, b): | |
| """Mark the exit dir_a from room a as traversed and also mark the corresponding exit in b.""" | |
| nodes[a].setdefault("traversed", {})[dir_a] = True | |
| # find reverse direction by looking up which exit in b returns to a | |
| for dir_b, dest in nodes[b]["exits"].items(): | |
| if dest == a: | |
| nodes[b].setdefault("traversed", {})[dir_b] = True | |
| break | |
| def run(*, map_level=0): | |
| room = 0 | |
| nodes[room]["visited"] = True | |
| while True: | |
| node = nodes[room] | |
| exits = node["exits"] | |
| # direction = question(node["desc"], exits.keys(), room=room, map_level=map_level) | |
| # direction = question_flat(node["desc"], exits.keys(), room=room, map_level=map_level) | |
| direction = question_dpad(node["desc"], exits.keys(), room=room, map_level=map_level) | |
| next_room = exits[direction] | |
| mark_traversal(room, direction, next_room) | |
| room = next_room | |
| nodes[room]["visited"] = True | |
| if room == 11: | |
| print(nodes[room]["desc"]) | |
| print("You exited the dungeon!") | |
| break | |
| def to_mermaid(nodes, *, direction="LR", include_desc=False): | |
| """ | |
| Mermaid flowchart output for https://mermaid.live | |
| nodes: {id: {"exits": {dir: neighbor_id}, "desc": str}} | |
| """ | |
| lines = [f"flowchart {direction}"] | |
| if include_desc: | |
| for nid, data in nodes.items(): | |
| label = data.get("nick", f"room {nid}").replace('"', "'") | |
| lines.append(f' {nid}["{label}"]') | |
| seen = set() # undirected dedupe: (min,max,dir_from_min,dir_from_max) is overkill; keep simple pair dedupe | |
| for a, data in nodes.items(): | |
| for d, b in data.get("exits", {}).items(): | |
| # If your graph is bidirectional, suppress the reverse edge duplicate | |
| key = tuple(sorted((a, b)) + [d]) # keeps one of them; still shows label | |
| if (b, a) in seen: | |
| continue | |
| seen.add((a, b)) | |
| lines.append(f' {a} -- "{d}" --> {b}') | |
| return "\n".join(lines) | |
| DIR_TO_PORT = { | |
| "n": "n", "ne": "ne", "e": "e", "se": "se", | |
| "s": "s", "sw": "sw", "w": "w", "nw": "nw", | |
| "u": "c", "d": "c", | |
| } | |
| def to_dot_with_ports(nodes, inverse_dir, *, graph_name="maze", rankdir="LR", | |
| directed=True, include_desc=True, dedupe_bidirectional=True): | |
| g = "digraph" if directed else "graph" | |
| edgeop = "->" if directed else "--" | |
| lines = [f"{g} {graph_name} {{", f" rankdir={rankdir};", ' node [shape=box];'] | |
| if include_desc: | |
| for nid, data in nodes.items(): | |
| label = data.get("nick", f"room {nid}").replace('"', r'\"') | |
| lines.append(f' {nid} [label="{label}"];') | |
| seen = set() | |
| for a, data in nodes.items(): | |
| for d, b in data.get("exits", {}).items(): | |
| if dedupe_bidirectional and (b, a) in seen: | |
| continue | |
| seen.add((a, b)) | |
| tail = DIR_TO_PORT.get(d, "c") | |
| head = DIR_TO_PORT.get(inverse_dir.get(d, d), "c") | |
| # node:compass syntax mounts the edge on that side/corner | |
| # label shows your direction name | |
| lines.append(f' {a}:{tail} {edgeop} {b}:{head} [label="{d}"];') | |
| lines.append("}") | |
| return "\n".join(lines) | |
| # --- Themes (each has exactly 10 names for rooms 1..10) --- | |
| # --- Airport --- | |
| AIRPORT = [ | |
| "Terminal","Gate","Concourse","Security", | |
| "Baggage Claim","Ticketing","Customs", | |
| "Lounge","Restrooms","Control Tower" | |
| ] | |
| # --- US Cities --- | |
| US_CITIES = [ | |
| "New York","Los Angeles","Chicago","Houston", | |
| "Phoenix","Philadelphia","San Antonio", | |
| "San Diego","Dallas","San Jose" | |
| ] | |
| # --- Zoo --- | |
| ZOO = [ | |
| "Entrance","Savannah","Reptile House","Aviary", | |
| "Big Cats","Primate House","Aquarium", | |
| "Insect House","Petting Zoo","Food Plaza" | |
| ] | |
| # --- Aquarium / Sea World --- | |
| AQUARIUM = [ | |
| "Coral Reef","Open Ocean","Shark Tunnel","Kelp Forest", | |
| "Touch Pool","Penguin Habitat","Jellyfish Gallery", | |
| "Seahorse Bay","Dolphin Lagoon","Research Lab" | |
| ] | |
| # --- Jurassic Park --- | |
| JURASSIC_PARK = [ | |
| "Visitor Center","Raptor Paddock","Tyrannosaur Enclosure", | |
| "Control Room","Genetics Lab","Maintenance Shed", | |
| "Power Station","Helipad","Safari Trail","Dock" | |
| ] | |
| # --- Home Depot Aisles --- | |
| HOME_DEPOT = [ | |
| "Lumber","Plumbing","Electrical","Paint", | |
| "Hardware","Flooring","Garden", | |
| "Appliances","Lighting","Tool Rental" | |
| ] | |
| # --- Department Store Aisles --- | |
| DEPARTMENT_STORE = [ | |
| "Entrance","Men's Wear","Women's Wear","Shoes", | |
| "Accessories","Cosmetics","Housewares", | |
| "Electronics","Kids","Checkout" | |
| ] | |
| # --- Disney Land / Disney World --- | |
| DISNEY = [ | |
| "Main Street","Adventureland","Frontierland", | |
| "Fantasyland","Tomorrowland","Liberty Square", | |
| "New Orleans Square","Toontown","Galaxy's Edge","Castle" | |
| ] | |
| # --- Lego Land --- | |
| LEGOLAND = [ | |
| "Miniland","Brick Factory","Pirate Shores", | |
| "Adventure Zone","Kingdoms","Ninjago World", | |
| "Imagination Zone","Technic Coaster","Water Park","Entrance Plaza" | |
| ] | |
| # --- Wonders of the World --- | |
| WONDERS = [ | |
| "Great Pyramid","Hanging Gardens","Statue of Zeus", | |
| "Temple of Artemis","Mausoleum","Colossus", | |
| "Lighthouse","Machu Picchu","Taj Mahal","Petra" | |
| ] | |
| # --- Master Planned Community --- | |
| MASTER_COMMUNITY = [ | |
| "Main Gate","Town Center","Clubhouse","Pool", | |
| "Park","Elementary School","Shopping Plaza", | |
| "Walking Trail","Community Lake","Sales Office" | |
| ] | |
| # --- Major Department Stores / Franchises --- | |
| DEPARTMENT_CHAINS = [ | |
| "Macy's","Target","Walmart","Costco", | |
| "Nordstrom","Kohl's","IKEA", | |
| "Best Buy","HomeGoods","TJ Maxx" | |
| ] | |
| # --- Major Fast Food Chains --- | |
| FAST_FOOD = [ | |
| "McDonald's","Burger King","Wendy's","Taco Bell", | |
| "KFC","Subway","Chick-fil-A", | |
| "In-N-Out","Popeyes","Arby's" | |
| ] | |
| # --- Major Restaurant Chains --- | |
| RESTAURANT_CHAINS = [ | |
| "Olive Garden","Applebee's","Chili's","IHOP", | |
| "Denny's","Red Lobster","Cheesecake Factory", | |
| "Outback","Buffalo Wild Wings","Cracker Barrel" | |
| ] | |
| # --- Holiday Themed Town --- | |
| HOLIDAY_TOWN = [ | |
| "Town Square","Candy Cane Lane","Toy Workshop", | |
| "Clock Tower","Graveyard Hill","Pumpkin Patch", | |
| "Town Hall","Snow Plaza","Holiday Market","North Gate" | |
| ] | |
| # --- Locations in The Simpsons --- | |
| SIMPSONS = [ | |
| "Simpson House","Kwik-E-Mart","Springfield Elementary", | |
| "Moe's Tavern","Nuclear Plant","Town Hall", | |
| "Android's Dungeon","Krusty Burger","City Jail","Evergreen Terrace" | |
| ] | |
| # --- Locations in StrongBadia --- | |
| STRONGBADIA = [ | |
| "Strong House","The Stick","Bubs' Concession", | |
| "The Field","The Pit","StrongBadia Border", | |
| "Marzipan's","Coach Z's","King of Town","Computer Room" | |
| ] | |
| # --- Sections of a Library --- | |
| LIBRARY_SECTIONS = [ | |
| "Entrance","Reference","Stacks","Reading Room", | |
| "Periodicals","Archives","Children's", | |
| "Media Center","Study Carrels","Circulation Desk" | |
| ] | |
| # --- Bureaucratic Building Departments --- | |
| BUREAUCRACY = [ | |
| "Lobby","Information Desk","Permits Office", | |
| "Records","Finance","Human Resources", | |
| "Legal","Compliance","Director's Office","Break Room" | |
| ] | |
| # --- School Rooms / Departments --- | |
| SCHOOL = [ | |
| "Front Office","Classroom","Science Lab","Library", | |
| "Gym","Cafeteria","Auditorium", | |
| "Nurse's Office","Counseling","Playground" | |
| ] | |
| # --- College / University Campus --- | |
| COLLEGE = [ | |
| "Quad","Lecture Hall","Library","Student Union", | |
| "Dormitory","Science Building","Administration", | |
| "Gymnasium","Cafeteria","Observatory" | |
| ] | |
| # --- Food Court Layout --- | |
| FOOD_COURT = [ | |
| "Entrance","Pizza Counter","Burger Grill","Asian Kitchen", | |
| "Mexican Grill","Salad Bar","Dessert Stand", | |
| "Coffee Kiosk","Seating Area","Trash Station" | |
| ] | |
| THEMES = { | |
| "clue": [ | |
| "Hall","Lounge","Dining","Kitchen","Ballroom", | |
| "Conservatory","Billiard","Library","Study","Cellar" | |
| ], | |
| "mansion": [ | |
| "Hall","Parlor","Dining","Kitchen","Ballroom", | |
| "Garden","Gallery","Library","Study","Vault" | |
| ], | |
| "dungeon": [ | |
| "Entry","Armory","Library","Chapel","Vault", | |
| "Forge","Garden","Hall","Cellar","Sanctum" | |
| ], | |
| "Airport": AIRPORT, | |
| "US Cities": US_CITIES, | |
| "Zoo": ZOO, | |
| "Aquarium / Sea World": AQUARIUM, | |
| "Jurassic Park": JURASSIC_PARK, | |
| "Home Depot": HOME_DEPOT, | |
| "Departments": DEPARTMENT_STORE, | |
| "Disney": DISNEY, | |
| "Lego Land": LEGOLAND, | |
| "Wonders of the World": WONDERS, | |
| "Community": MASTER_COMMUNITY, | |
| "Department Stores": DEPARTMENT_CHAINS, | |
| "Fast Food": FAST_FOOD, | |
| "Restaurant": RESTAURANT_CHAINS, | |
| "Holidays": HOLIDAY_TOWN, | |
| "Simpsons": SIMPSONS, | |
| "StrongBad": STRONGBADIA, | |
| "Library": LIBRARY_SECTIONS, | |
| "Bureaucracy": BUREAUCRACY, | |
| "School": SCHOOL, | |
| "College Campus": COLLEGE, | |
| "Food Court": FOOD_COURT, | |
| } | |
| def assign_room_theme(nodes, *, start_id=0, end_id=11): | |
| """ | |
| Randomly choose a theme and assign nodes[r]["nick"] + nodes[r]["desc"]. | |
| Rooms 1..10 get themed names; 0 and 11 get fixed names. | |
| Returns the chosen theme name. | |
| """ | |
| theme_name = random.choice(list(THEMES.keys())) | |
| names = THEMES[theme_name].copy() | |
| random.shuffle(names) | |
| # fixed endpoints | |
| nodes[start_id]["nick"] = "Start" | |
| nodes[start_id]["desc"] = "Maze Start" | |
| nodes[end_id]["nick"] = "Exit" | |
| nodes[end_id]["desc"] = "Maze Exit" | |
| # assign 10 themed names to rooms 1..10 | |
| for rid, room_name in zip(range(1, 11), names): | |
| nodes[rid]["nick"] = room_name | |
| nodes[rid]["desc"] = f"You are in the {room_name}." | |
| return theme_name | |
| if __name__ == "__main__": | |
| # print("\n"*3) | |
| # print(to_mermaid(nodes, direction="LR", include_desc=True)) | |
| print("\n"*3) | |
| # print(to_dot(nodes, include_desc=True, directed=True)) | |
| print(to_dot_with_ports(nodes, inverse_dir, directed=True, include_desc=True)) | |
| print("\n"*3) | |
| theme = assign_room_theme(nodes) | |
| print(f"Theme: {theme}") | |
| try: | |
| run(map_level=3) | |
| except KeyboardInterrupt: | |
| print("\nThanks for playing") | |
| raise SystemExit(0) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment