import streamlit as st import numpy as np import random import json import math import time import pandas as pd import graphviz from scipy.sparse import csr_matrix from streamlit_autorefresh import st_autorefresh # ----------------------------------------------------------------------------- # Page Configuration & Auto-Refresh (every 5 seconds) # ----------------------------------------------------------------------------- st.set_page_config(page_title="NeuroDungeon: Cortical Column Game", layout="wide") st_autorefresh(interval=5000, limit=1000, key="game_refresh") # ----------------------------------------------------------------------------- # Shared Game State Initialization (simulate shared memory) # ----------------------------------------------------------------------------- if 'game_state' not in st.session_state: st.session_state.game_state = { 'hex_grid': [[{'type': 'empty', 'emoji': '', 'alive': False} for _ in range(12)] for _ in range(16)], 'heroes': {}, # {player_id: {x, y, hp, atk, def, xp, level, gear}} 'monsters': [], 'loot': [], 'exit': {'x': 15, 'y': 11}, 'overlord': {'x': 14, 'y': 10, 'hp': 50, 'atk': 5, 'alive': True}, 'players': {}, 'story': [], 'drawn_cards': 0, 'turn': 0 } # ----------------------------------------------------------------------------- # Card Deck for Narrative Events (Episodic Memory) # ----------------------------------------------------------------------------- if 'deck' not in st.session_state: def create_deck(): suits = ["Hearts", "Diamonds", "Clubs", "Spades"] ranks = list(range(1, 14)) deck = [(suit, rank) for suit in suits for rank in ranks] random.shuffle(deck) return deck st.session_state.deck = create_deck() # ----------------------------------------------------------------------------- # Emoji Paintbrush Mode State (shared via hidden DOM elements) # ----------------------------------------------------------------------------- if 'painting_mode' not in st.session_state: st.session_state.painting_mode = False if 'selected_emoji' not in st.session_state: st.session_state.selected_emoji = "" # ----------------------------------------------------------------------------- # Constants and UI Elements for Both Systems # ----------------------------------------------------------------------------- HEX_SIZE = 40 PLAYER_NAMES = ["SkyWalker", "ForestRanger", "CityBuilder", "MonsterTamer", "RailMaster"] PLANTS = ["๐ŸŒฑ", "๐ŸŒฒ", "๐ŸŒณ", "๐ŸŒด", "๐ŸŒต"] LOOT_EMOJIS = ["โš”๏ธ", "๐Ÿ›ก๏ธ", "๐Ÿ’ฐ", "๐Ÿ”‘"] TRAPS = ["โšก", "๐Ÿ•ณ๏ธ"] SUIT_PROPERTIES = {"Hearts": "heroic", "Diamonds": "treacherous", "Clubs": "fierce", "Spades": "enigmatic"} # A palette of ~60 emojis representing various world objects: emoji_palette = [ "๐Ÿ ", "๐Ÿข", "๐Ÿฌ", "๐Ÿญ", "๐Ÿฐ", "๐Ÿฏ", "๐ŸŸ๏ธ", "๐Ÿก", "๐Ÿš—", "๐Ÿš•", "๐Ÿš™", "๐ŸšŒ", "๐ŸšŽ", "๐Ÿš“", "๐Ÿš‘", "๐Ÿš’", "๐Ÿš", "๐Ÿšš", "๐Ÿš›", "๐Ÿšœ", "๐Ÿ›ต", "๐Ÿšฒ", "๐Ÿ›ด", "๐Ÿš€", "โœˆ๏ธ", "โ›ต", "๐Ÿšค", "๐Ÿ›ณ๏ธ", "๐Ÿš‚", "๐Ÿš†", "๐ŸšŠ", "๐Ÿš‰", "๐Ÿ‘จโ€๐Ÿ’ผ", "๐Ÿ‘ฉโ€๐Ÿ’ผ", "๐Ÿ‘จโ€๐Ÿ”ง", "๐Ÿ‘ฉโ€๐Ÿ”ง", "๐Ÿ‘จโ€โš•๏ธ", "๐Ÿ‘ฉโ€โš•๏ธ", "๐Ÿ‘ฎโ€โ™‚๏ธ", "๐Ÿ‘ฎโ€โ™€๏ธ", "๐Ÿ‘ทโ€โ™‚๏ธ", "๐Ÿ‘ทโ€โ™€๏ธ", "๐Ÿ’‚โ€โ™‚๏ธ", "๐Ÿ’‚โ€โ™€๏ธ", "๐Ÿถ", "๐Ÿฑ", "๐Ÿญ", "๐Ÿน", "๐Ÿฐ", "๐ŸฆŠ", "๐Ÿป", "๐Ÿผ", "๐Ÿจ", "๐Ÿฏ", "๐Ÿฆ", "๐Ÿฎ", "๐Ÿท", "๐Ÿธ", "๐Ÿต", "๐Ÿ”", "๐Ÿง", "๐Ÿฆ", "๐ŸŒณ", "๐ŸŒด", "๐ŸŒต", "๐ŸŽ", "๐ŸŒ", "๐Ÿ‰", "๐Ÿ‡", "๐Ÿ“", "๐Ÿ”", "๐Ÿ•" ] # ----------------------------------------------------------------------------- # Initialize Hero if Not Already Set # ----------------------------------------------------------------------------- if 'player_id' not in st.session_state: st.session_state.player_id = random.choice(PLAYER_NAMES) st.session_state.game_state['heroes'][st.session_state.player_id] = { 'x': 0, 'y': 0, 'hp': 20, 'atk': 3, 'def': 1, 'xp': 0, 'level': 1, 'gear': [] } player_id = st.sidebar.selectbox("Choose Hero", PLAYER_NAMES, index=PLAYER_NAMES.index(st.session_state.player_id)) st.session_state.player_id = player_id hero = st.session_state.game_state['heroes'].get(player_id, {'x': 0, 'y': 0, 'hp': 20, 'atk': 3, 'def': 1, 'xp': 0, 'level': 1, 'gear': []}) st.sidebar.subheader("๐Ÿ—ก๏ธ Hero Stats") st.sidebar.write(f"HP: {hero.get('hp', 20)} | ATK: {hero.get('atk', 3)} | DEF: {hero.get('def', 1)}") st.sidebar.write(f"Level: {hero.get('level', 1)} | XP: {hero.get('xp', 0)}") st.sidebar.write(f"Gear: {', '.join(hero.get('gear', [])) or 'None'}") st.sidebar.subheader("๐Ÿ† Scores") for p, s in st.session_state.game_state['players'].items(): st.sidebar.write(f"{p}: {s}") # ----------------------------------------------------------------------------- # Emoji Paintbrush Sidebar Palette # ----------------------------------------------------------------------------- st.sidebar.subheader("๐ŸŽจ Emoji Paintbrush") cols = st.sidebar.columns(10) # Display the palette in a grid (10 per row) for idx, emoji in enumerate(emoji_palette): col = cols[idx % 10] if col.button(emoji, key=f"emoji_{idx}"): st.session_state.painting_mode = True st.session_state.selected_emoji = emoji st.sidebar.info(f"Painting Mode: {emoji}") # Break out if you want one-click activation per emoji. # Every 10 emojis, refresh the row columns if (idx + 1) % 10 == 0 and idx + 1 < len(emoji_palette): cols = st.sidebar.columns(10) if st.sidebar.button("Cancel Paint Mode"): st.session_state.painting_mode = False st.session_state.selected_emoji = "" st.sidebar.info("Painting Mode Disabled") # ----------------------------------------------------------------------------- # Utility Functions for Game Dynamics & Neural Decay # ----------------------------------------------------------------------------- def apply_game_of_life(grid): new_grid = [[cell.copy() for cell in row] for row in grid] for i in range(len(grid)): for j in range(len(grid[0])): neighbors = count_neighbors(grid, i, j) if grid[i][j]['type'] in ['plant', 'trap'] and grid[i][j]['alive']: if neighbors < 2 or neighbors > 3: new_grid[i][j]['alive'] = False new_grid[i][j]['type'] = 'empty' new_grid[i][j]['emoji'] = '' elif grid[i][j]['type'] == 'empty' and neighbors == 3: new_grid[i][j]['type'] = 'plant' new_grid[i][j]['emoji'] = random.choice(PLANTS) new_grid[i][j]['alive'] = True return new_grid def count_neighbors(grid, x, y): count = 0 for di in [-1, 0, 1]: for dj in [-1, 0, 1]: if di == 0 and dj == 0: continue ni, nj = x + di, y + dj if 0 <= ni < len(grid) and 0 <= nj < len(grid[0]) and grid[ni][nj]['alive']: count += 1 return count def reset_game(): st.session_state.game_state = { 'hex_grid': [[{'type': 'empty', 'emoji': '', 'alive': False} for _ in range(12)] for _ in range(16)], 'heroes': {player_id: {'x': 0, 'y': 0, 'hp': 20, 'atk': 3, 'def': 1, 'xp': 0, 'level': 1, 'gear': []}}, 'monsters': [], 'loot': [], 'exit': {'x': 15, 'y': 11}, 'overlord': {'x': 14, 'y': 10, 'hp': 50, 'atk': 5, 'alive': True}, 'players': {}, 'story': [], 'drawn_cards': 0, 'turn': 0 } for i in range(16): for j in range(12): if random.random() < 0.1: st.session_state.game_state['hex_grid'][i][j] = {'type': 'wall', 'emoji': '๐Ÿงฑ', 'alive': False} # Place random walls in the dungeon initially for i in range(16): for j in range(12): if random.random() < 0.1: st.session_state.game_state['hex_grid'][i][j] = {'type': 'wall', 'emoji': '๐Ÿงฑ', 'alive': False} # ----------------------------------------------------------------------------- # p5.js Code for Rendering the Game (with Recenter & Painting Mode) # ----------------------------------------------------------------------------- p5js_code = f""" const HEX_SIZE = {HEX_SIZE}; const SQRT_3 = Math.sqrt(3); let hexGrid = []; let heroes = {{}}; let monsters = []; let loot = []; let exit = {{}}; let overlord = {{}}; let playerId; let story = []; function setup() {{ createCanvas(640, 480); updateFromState(); }} function updateFromState() {{ hexGrid = JSON.parse(document.getElementById('hex_grid').innerHTML); heroes = JSON.parse(document.getElementById('heroes').innerHTML); monsters = JSON.parse(document.getElementById('monsters').innerHTML); loot = JSON.parse(document.getElementById('loot').innerHTML); exit = JSON.parse(document.getElementById('exit').innerHTML); overlord = JSON.parse(document.getElementById('overlord').innerHTML); playerId = document.getElementById('player_id').innerHTML; story = JSON.parse(document.getElementById('story').innerHTML); }} function draw() {{ background(169, 169, 169); push(); // Recenter the board so the hero is at canvas center let hero = heroes[playerId]; if (hero) {{ let centerX = width / 2; let centerY = height / 2; let heroX = hero.x * HEX_SIZE * 1.5; let heroY = hero.y * HEX_SIZE * SQRT_3 + (hero.x % 2 ? HEX_SIZE * SQRT_3 / 2 : 0); let offsetX = centerX - heroX; let offsetY = centerY - heroY; translate(offsetX, offsetY); }} drawHexGrid(); drawLoot(); drawHeroes(); drawMonsters(); drawExit(); drawOverlord(); pop(); drawStory(); }} function drawHexGrid() {{ for (let i = 0; i < hexGrid.length; i++) {{ for (let j = 0; j < hexGrid[i].length; j++) {{ let x = i * HEX_SIZE * 1.5; let y = j * HEX_SIZE * SQRT_3 + (i % 2 ? HEX_SIZE * SQRT_3 / 2 : 0); fill(hexGrid[i][j].type === 'wall' ? '#555' : hexGrid[i][j].alive ? '#90EE90' : '#A9A9A9'); stroke(0); drawHex(x, y); if (hexGrid[i][j].emoji) {{ textSize(20); text(hexGrid[i][j].emoji, x - 10, y + 5); }} }} }} }} function drawHex(x, y) {{ beginShape(); for (let a = 0; a < 6; a++) {{ let angle = TWO_PI / 6 * a; vertex(x + HEX_SIZE * cos(angle), y + HEX_SIZE * sin(angle)); }} endShape(CLOSE); }} function drawHeroes() {{ Object.keys(heroes).forEach(h => {{ let hero = heroes[h]; let x = hero.x * HEX_SIZE * 1.5; let y = hero.y * HEX_SIZE * SQRT_3 + (hero.x % 2 ? HEX_SIZE * SQRT_3 / 2 : 0); fill(0, 255, 0); ellipse(x, y, HEX_SIZE * 0.8); textSize(14); text(h.slice(0,2), x - 10, y + 5); }}); }} function drawMonsters() {{ monsters.forEach(m => {{ let x = m.x * HEX_SIZE * 1.5; let y = m.y * HEX_SIZE * SQRT_3 + (m.x % 2 ? HEX_SIZE * SQRT_3 / 2 : 0); fill(255, 0, 0); ellipse(x, y, HEX_SIZE * 0.8); textSize(20); text(m.type === 'Godzilla' ? '๐Ÿฆ–' : '๐Ÿค–', x - 10, y + 5); }}); }} function drawLoot() {{ loot.forEach(l => {{ let x = l.x * HEX_SIZE * 1.5; let y = l.y * HEX_SIZE * SQRT_3 + (l.x % 2 ? HEX_SIZE * SQRT_3 / 2 : 0); fill(255, 215, 0); ellipse(x, y, HEX_SIZE * 0.6); textSize(20); text(l.type, x - 10, y + 5); }}); }} function drawExit() {{ let x = exit.x * HEX_SIZE * 1.5; let y = exit.y * HEX_SIZE * SQRT_3 + (exit.x % 2 ? HEX_SIZE * SQRT_3 / 2 : 0); fill(0, 191, 255); ellipse(x, y, HEX_SIZE * 0.8); textSize(20); text('๐Ÿšช', x - 10, y + 5); }} function drawOverlord() {{ if (overlord.alive) {{ let x = overlord.x * HEX_SIZE * 1.5; let y = overlord.y * HEX_SIZE * SQRT_3 + (overlord.x % 2 ? HEX_SIZE * SQRT_3 / 2 : 0); fill(139, 0, 139); ellipse(x, y, HEX_SIZE * 1.2); textSize(20); text('๐Ÿ‘‘', x - 10, y + 5); }} }} function drawStory() {{ fill(0); textSize(14); for (let i = 0; i < Math.min(story.length, 5); i++) {{ text(story[story.length - 1 - i], 10, 20 + i * 20); }} }} function mousePressed() {{ // Check if we're in painting mode (shared via hidden div) let paintingMode = document.getElementById('painting_mode').innerHTML; if (paintingMode === "true") {{ let i = Math.floor(mouseX / (HEX_SIZE * 1.5)); let j = Math.floor((mouseY - (i % 2 ? HEX_SIZE * SQRT_3 / 2 : 0)) / (HEX_SIZE * SQRT_3)); if (i >= 0 && i < hexGrid.length && j >= 0 && j < hexGrid[0].length) {{ let selectedEmoji = document.getElementById('selected_emoji').innerHTML; hexGrid[i][j].emoji = selectedEmoji; hexGrid[i][j].type = 'organism'; hexGrid[i][j].alive = true; updateState(); }} }} else {{ // Default behavior: move hero if adjacent cell is empty let i = Math.floor(mouseX / (HEX_SIZE * 1.5)); let j = Math.floor((mouseY - (i % 2 ? HEX_SIZE * SQRT_3 / 2 : 0)) / (HEX_SIZE * SQRT_3)); if (i >= 0 && i < hexGrid.length && j >= 0 && j < hexGrid[0].length) {{ let hero = heroes[playerId]; if (hexGrid[i][j].type === 'empty' && Math.abs(hero.x - i) <= 1 && Math.abs(hero.y - j) <= 1) {{ moveHero(i, j); }} }} }} }} function keyPressed() {{ let hero = heroes[playerId]; if (key === 'w' || key === 'W') moveHero(hero.x, hero.y - 1); if (key === 's' || key === 'S') moveHero(hero.x, hero.y + 1); if (key === 'a' || key === 'A') moveHero(hero.x - 1, hero.y); if (key === 'd' || key === 'D') moveHero(hero.x + 1, hero.y); if (key === 'q' || key === 'Q') moveHero(hero.x - 1, hero.y - 1); if (key === 'e' || key === 'E') moveHero(hero.x + 1, hero.y - 1); }} function moveHero(newX, newY) {{ if (newX < 0 || newX >= hexGrid.length || newY < 0 || newY >= hexGrid[0].length) return; let hero = heroes[playerId]; let cell = hexGrid[newX][newY]; if (cell.type === 'empty' || cell.type === 'plant') {{ hero.x = newX; hero.y = newY; handleInteractions(); updateState(); }} }} function handleInteractions() {{ let hero = heroes[playerId]; // Loot Interaction let lootIdx = loot.findIndex(l => l.x === hero.x && l.y === hero.y); if (lootIdx !== -1) {{ let item = loot[lootIdx].type; if (item === 'โš”๏ธ') hero.atk += 2; if (item === '๐Ÿ›ก๏ธ') hero.def += 2; if (item === '๐Ÿ’ฐ') hero.xp += 10; if (item === '๐Ÿ”‘') hero.gear.push('๐Ÿ”‘'); loot.splice(lootIdx, 1); addStory('Loot', item); }} // Trap Interaction if (hexGrid[hero.x][hero.y].type === 'trap') {{ hero.hp -= Math.floor(random(1, 5)); addStory('Trap', hexGrid[hero.x][hero.y].emoji); }} // Monster Combat let monsterIdx = monsters.findIndex(m => m.x === hero.x && m.y === hero.y); if (monsterIdx !== -1) {{ let monster = monsters[monsterIdx]; let damage = Math.max(0, hero.atk - 1); monster.hp -= damage; hero.hp -= Math.max(0, monster.atk - hero.def); if (monster.hp <= 0) {{ monsters.splice(monsterIdx, 1); hero.xp += 10; addStory('Monster', monster.type + ' slain'); }} }} // Overlord Combat if (overlord.alive && hero.x === overlord.x && hero.y === overlord.y) {{ let damage = Math.max(0, hero.atk - 2); overlord.hp -= damage; hero.hp -= Math.max(0, overlord.atk - hero.def); if (overlord.hp <= 0) {{ overlord.alive = false; hero.xp += 50; addStory('Monster', 'Overlord defeated'); }} }} // Exit Check if (hero.x === exit.x && hero.y === exit.y && hero.gear.includes('๐Ÿ”‘')) {{ alert('Victory! You escaped the Hex Dungeon!'); resetGame(); }} // Level Up if (hero.xp >= hero.level * 20) {{ hero.level += 1; hero.hp += 5; hero.atk += 1; hero.def += 1; addStory('Hero', 'leveled up'); }} }} function addStory(type, word) {{ let deck = JSON.parse(document.getElementById('deck').innerHTML); let cardIndex = {{"drawn": st.session_state.game_state['drawn_cards']}}.drawn % 52; let card = deck[cardIndex]; let suit = card[0]; let sentence = `A ${{SUIT_PROPERTIES[suit]}} event: ${{type.toLowerCase()}} ${{word}}`; story.push(sentence); // In a full implementation, update the drawn_cards counter server-side. }} function updateState() {{ // Send the updated state back to Streamlit. fetch('/update_state', {{ method: 'POST', headers: {{ 'Content-Type': 'application/json' }}, body: JSON.stringify({{ hex_grid: hexGrid, heroes: heroes, monsters: monsters, loot: loot, exit: exit, overlord: overlord, story: story, player_id: playerId }}) }}); }} """ # ----------------------------------------------------------------------------- # Multi-Tab UI: Cross Breeding Cortical Theory & Dungeon Crawl # ----------------------------------------------------------------------------- tabs = st.tabs(["Cortical Theory", "HexCitySaga", "Memory Integration", "Self Reward NPS", "Extra UI"]) # --- Tab 1: Cortical Column Theory Demonstration --- with tabs[0]: st.header("Cortical Column Theory: Self-Modifying Memory Systems") st.markdown(""" **Theory Overview:** Lifeโ€™s essential attribute is the capacity for self-modification. This demonstration illustrates: - **Episodic Memory (E):** Short-term, introspective attention (โ‰ˆ5โ€“10 seconds). - **Semantic Memory (K):** Accumulated knowledge formed by experiences. - **Neural Connectivity:** Modeled via sparse matrices and graph structures representing voting neurons. - **Free Energy & Bonding:** The drive that connects entitiesโ€”analogous to love and maximal energy states. """) st.subheader("Neural Connectivity Sparse Matrix") size = 10 density = 0.2 random_matrix = np.random.binomial(1, density, size=(size, size)) sparse_matrix = csr_matrix(random_matrix) st.write("Sparse Matrix Representation:") st.write(sparse_matrix) st.write("Dense Matrix Representation:") st.write(random_matrix) st.subheader("Concept Graph Visualization") graph_source = """ digraph G { "Cortical Column ๐Ÿง " -> "Episodic Memory (E) โฑ๏ธ" [label="short-term"]; "Cortical Column ๐Ÿง " -> "Semantic Memory (K) ๐Ÿ“š" [label="knowledge"]; "Episodic Memory (E) โฑ๏ธ" -> "Introspective Attention ๐Ÿ”" [label="focus"]; "Semantic Memory (K) ๐Ÿ“š" -> "Free Energy โšก" [label="agency"]; "Free Energy โšก" -> "Love โค๏ธ" [label="bond"]; "Love โค๏ธ" -> "Humanity ๐ŸŒ" [label="connection"]; } """ st.graphviz_chart(graph_source) # --- Tab 2: HexCitySaga: Dungeon Crawl Game --- with tabs[1]: st.header("HexCitySaga: Dungeon Crawl") st.write(f"Hero: **{player_id}**. Use WASD/QE to move or click on a hex (if in painting mode) to add a new organism.") # The following hidden divs provide shared state info to p5.js: hidden_divs = f""" """ game_html = f""" {hidden_divs} """ st.components.v1.html(game_html, height=500) # --- Tab 3: Memory Integration --- with tabs[2]: st.header("Memory Integration") st.write("Mapping game events as nodes in an episodic/semantic memory network:") if st.session_state.game_state['story']: nodes = [] edges = [] for idx, event in enumerate(st.session_state.game_state['story']): node_label = f"Event {idx+1}" safe_event = event.replace('"', '\'') nodes.append(f'"{node_label}" [label="{safe_event}"];') if idx > 0: edges.append(f'"Event {idx}" -> "Event {idx+1}";') graph_def = "digraph MemoryGraph {\n" + "\n".join(nodes + edges) + "\n}" st.graphviz_chart(graph_def) else: st.write("No memory events recorded yet. Engage in the dungeon to generate memories!") # --- Tab 4: Self Reward Learning NPS --- with tabs[3]: st.header("Self Reward Learning NPS Score") nps_score = st.slider("Rate Self Reward Learning (0-10):", 0, 10, 5) if nps_score <= 6: nps_comment = "Needs Improvement - Refine self-modification algorithms." elif nps_score <= 8: nps_comment = "Good, but can be better - Fine-tuning required." else: nps_comment = "Excellent! The system demonstrates robust self-reward learning." st.write(f"**NPS Score:** {nps_score} - {nps_comment}") # --- Tab 5: Extra UI Components --- with tabs[4]: st.header("Extra UI Components") with st.expander("More Details"): st.write("Additional interactive widgets and data visualizations can be added here.") col1, col2 = st.columns(2) with col1: st.write("**Column 1:** Neural Signal Simulation (Line Chart)") st.line_chart(np.random.randn(20, 1)) with col2: st.write("**Column 2:** Energy Consumption (Bar Chart)") st.bar_chart(np.random.randn(20, 1)) st.subheader("Data Table") df = pd.DataFrame({ "Component": ["Neuron", "Synapse", "Dendrite", "Axon"], "Status": ["Active", "Active", "Inactive", "Active"] }) st.dataframe(df) st.subheader("JSON Configuration") sample_json = { "Episodic": {"Duration": "5-10 sec", "Type": "Conscious"}, "Semantic": {"Label": "Cortical Column Theory"} } st.json(sample_json) # ----------------------------------------------------------------------------- # Sidebar: Reset Game Button # ----------------------------------------------------------------------------- if st.sidebar.button("Reset Game"): reset_game()