import streamlit as st import numpy as np import time from dataclasses import dataclass from typing import Dict, List, Optional @dataclass class Building: name: str cost: Dict[str, int] produces: Dict[str, float] # Changed to dict for multiple outputs color: str icon: str description: str class FactoryGame: def __init__(self): self.GRID_SIZE = 8 self.buildings = { "solar_panel": Building( name="Solar Panel", cost={"minerals": 10}, produces={"electricity": 1}, color="#FEF08A", icon="☀️", description="Produces 1 electricity unit" ), "mineral_extractor": Building( name="Mineral Extractor", cost={"electricity": 2}, produces={"minerals": 1}, color="#D1D5DB", icon="⛏️", description="Extracts minerals from the ground" ), "circuit_factory": Building( name="Circuit Factory", cost={"electricity": 5, "minerals": 15}, produces={"circuits": 1}, color="#BBF7D0", icon="🔧", description="Produces electronic circuits" ), "nuclear_plant": Building( name="Nuclear Plant", cost={"minerals": 50, "circuits": 20}, produces={"electricity": 10}, color="#A7F3D0", icon="⚛️", description="Produces 10 electricity units" ), "gpu_factory": Building( name="GPU Factory", cost={"circuits": 30, "minerals": 40}, produces={"tflops": 100, "electricity": -5}, # Negative for consumption color="#C7D2FE", icon="🎮", description="Produces 100 TFLOPS, consumes 5 electricity" ) } # Initialize session state if needed if "resources" not in st.session_state: st.session_state.resources = { "electricity": 50, "minerals": 50, "circuits": 0, "tflops": 0 } if "grid" not in st.session_state: st.session_state.grid = [[None for _ in range(self.GRID_SIZE)] for _ in range(self.GRID_SIZE)] if "last_update" not in st.session_state: st.session_state.last_update = time.time() if "selected_building" not in st.session_state: st.session_state.selected_building = None def update_resources(self): current_time = time.time() elapsed = current_time - st.session_state.last_update if elapsed >= 1.0: # Update every second new_resources = st.session_state.resources.copy() # First calculate total electricity production/consumption electricity_delta = 0 for row in range(self.GRID_SIZE): for col in range(self.GRID_SIZE): building_id = st.session_state.grid[row][col] if building_id: building = self.buildings[building_id] if "electricity" in building.produces: electricity_delta += building.produces["electricity"] # Only run other production if we have enough electricity if new_resources["electricity"] + electricity_delta >= 0: new_resources["electricity"] += electricity_delta for row in range(self.GRID_SIZE): for col in range(self.GRID_SIZE): building_id = st.session_state.grid[row][col] if building_id: building = self.buildings[building_id] for resource, amount in building.produces.items(): if resource != "electricity": # Already handled above new_resources[resource] += amount st.session_state.resources = new_resources st.session_state.last_update = current_time def can_afford(self, building_id: str) -> bool: building = self.buildings[building_id] for resource, cost in building.cost.items(): if st.session_state.resources.get(resource, 0) < cost: return False return True def place_building(self, row: int, col: int, building_id: str) -> bool: if not self.can_afford(building_id): return False if st.session_state.grid[row][col] is not None: return False # Deduct costs building = self.buildings[building_id] for resource, cost in building.cost.items(): st.session_state.resources[resource] -= cost # Place building st.session_state.grid[row][col] = building_id return True def select_building(building_id: str): st.session_state.selected_building = building_id def handle_cell_click(row: int, col: int, game: FactoryGame): if st.session_state.selected_building: game.place_building(row, col, st.session_state.selected_building) def main(): st.set_page_config(page_title="Factory Game", layout="wide") game = FactoryGame() # Title st.title("🏭 Factory Game", anchor=False) # Layout columns col1, col2 = st.columns([3, 1]) with col2: # Resources display st.subheader("Resources") col_a, col_b = st.columns(2) with col_a: st.metric("⚡ Power", f"{int(st.session_state.resources['electricity'])}") st.metric("🪨 Minerals", f"{int(st.session_state.resources['minerals'])}") with col_b: st.metric("🔌 Circuits", f"{int(st.session_state.resources['circuits'])}") st.metric("💻 TFLOPS", f"{int(st.session_state.resources['tflops'])}") # Building selection st.subheader("Buildings") for building_id, building in game.buildings.items(): if st.button( f"{building.icon} {building.name}\n" f"Cost: {', '.join(f'{cost} {res}' for res, cost in building.cost.items())}", help=building.description, key=f"building_{building_id}", type="secondary" if st.session_state.selected_building != building_id else "primary" ): select_building(building_id) st.caption(f"Selected: {game.buildings[st.session_state.selected_building].icon if st.session_state.selected_building else 'None'}") with col1: # Game grid with minimal spacing grid_container = st.container() with grid_container: for row in range(game.GRID_SIZE): cols = st.columns(game.GRID_SIZE) for col, column in enumerate(cols): building_id = st.session_state.grid[row][col] building = game.buildings.get(building_id) if building_id else None # Just show the emoji, no labels label = building.icon if building else "⬜" with column: if st.button( label, key=f"cell_{row}_{col}", help=building.name if building else "Empty space", use_container_width=True ): handle_cell_click(row, col, game) # Update resources periodically game.update_resources() # Force a rerun every second to update resources time.sleep(0.1) st.rerun() if __name__ == "__main__": main()