""" state_reader.py RCON을 통해 팩토리오 게임 상태를 읽어오는 모듈 """ import json from factorio_rcon import FactorioRCON class StateReader: def __init__(self, rcon: FactorioRCON): self.rcon = rcon def get_full_state(self) -> dict: """전체 게임 상태를 한 번에 수집""" return { "player": self.get_player_info(), "inventory": self.get_inventory(), "resources": self.scan_resources(), "buildings": self.get_buildings(), "tech": self.get_research_status(), } # ── 플레이어 ──────────────────────────────────────────────── def get_player_info(self) -> dict: lua = """ local p = game.player rcon.print(game.table_to_json({ x = math.floor(p.position.x), y = math.floor(p.position.y), health = p.character and p.character.health or 100 })) """ raw = self.rcon.lua(lua) return json.loads(raw) if raw else {} # ── 인벤토리 ──────────────────────────────────────────────── def get_inventory(self) -> dict: lua = """ local inv = game.player.get_main_inventory() local result = {} local contents = inv.get_contents() for name, count in pairs(contents) do result[name] = count end rcon.print(game.table_to_json(result)) """ raw = self.rcon.lua(lua) return json.loads(raw) if raw else {} # ── 자원 패치 스캔 ────────────────────────────────────────── def scan_resources(self, radius: int = 200) -> dict: """플레이어 주변 자원 패치 위치 탐색""" lua = f""" local p = game.player local surface = p.surface local center = p.position local resources = {{}} local ore_types = {{"iron-ore", "copper-ore", "coal", "stone", "uranium-ore"}} for _, ore in ipairs(ore_types) do local entities = surface.find_entities_filtered{{ area = {{ {{center.x - {radius}, center.y - {radius}}}, {{center.x + {radius}, center.y + {radius}}} }}, name = ore }} if #entities > 0 then -- 패치 중심점 계산 local sx, sy = 0, 0 for _, e in ipairs(entities) do sx = sx + e.position.x sy = sy + e.position.y end resources[ore] = {{ count = #entities, center_x = math.floor(sx / #entities), center_y = math.floor(sy / #entities) }} end end rcon.print(game.table_to_json(resources)) """ raw = self.rcon.lua(lua) return json.loads(raw) if raw else {} # ── 건물 목록 ─────────────────────────────────────────────── def get_buildings(self, radius: int = 300) -> list: lua = f""" local p = game.player local surface = p.surface local center = p.position local entity_names = {{ "burner-mining-drill", "electric-mining-drill", "stone-furnace", "steel-furnace", "electric-furnace", "transport-belt", "fast-transport-belt", "burner-inserter", "inserter", "small-electric-pole", "medium-electric-pole", "steam-engine", "boiler", "offshore-pump", "assembling-machine-1", "assembling-machine-2", "chest", "iron-chest", "steel-chest" }} local result = {{}} for _, name in ipairs(entity_names) do local entities = surface.find_entities_filtered{{ area = {{ {{center.x - {radius}, center.y - {radius}}}, {{center.x + {radius}, center.y + {radius}}} }}, name = name }} if #entities > 0 then result[name] = #entities end end rcon.print(game.table_to_json(result)) """ raw = self.rcon.lua(lua) return json.loads(raw) if raw else {} # ── 연구 현황 ─────────────────────────────────────────────── def get_research_status(self) -> dict: lua = """ local force = game.player.force local completed = {} local k = 0 for name, tech in pairs(force.technologies) do if tech.researched then k = k + 1 completed[k] = name end end rcon.print(game.table_to_json({ current = force.current_research and force.current_research.name or "none", completed_count = k })) """ raw = self.rcon.lua(lua) return json.loads(raw) if raw else {} def summarize_for_ai(self, state: dict) -> str: """AI 프롬프트용 상태 요약 텍스트 생성""" p = state.get("player", {}) inv = state.get("inventory", {}) res = state.get("resources", {}) bld = state.get("buildings", {}) lines = [ f"## 현재 게임 상태", f"### 플레이어", f"- 위치: ({p.get('x', '?')}, {p.get('y', '?')})", f"- 체력: {p.get('health', '?')}", "", "### 인벤토리 (주요 아이템)", ] key_items = [ "iron-ore", "copper-ore", "coal", "stone", "iron-plate", "copper-plate", "steel-plate", "burner-mining-drill", "electric-mining-drill", "stone-furnace", "transport-belt", "inserter", "burner-inserter", "small-electric-pole", ] for item in key_items: count = inv.get(item, 0) if count > 0: lines.append(f"- {item}: {count}개") lines += ["", "### 주변 자원 패치"] if res: for ore, info in res.items(): lines.append( f"- {ore}: {info['count']}타일 " f"(중심: {info['center_x']}, {info['center_y']})" ) else: lines.append("- 탐색된 자원 없음") lines += ["", "### 건설된 건물"] if bld: for name, count in bld.items(): lines.append(f"- {name}: {count}개") else: lines.append("- 아직 건물 없음") return "\n".join(lines)