diff --git a/context_compressor.py b/context_compressor.py index 9a8b0db..7996440 100644 --- a/context_compressor.py +++ b/context_compressor.py @@ -2,9 +2,10 @@ context_compressor.py — 계층적 컨텍스트 압축 핵심 변경: -- game.player → game.players[1] (RCON 호환) -- pcall로 엔티티 검색 감싸기 (Factorio 2.0 이름 변경 대응) -- type 기반 검색으로 버전 호환성 확보 +- f-string 제거 또는 최소화 (Lua 중괄호 충돌 방지) +- position+radius 방식 사용 +- pcall + try/except 완전 감싸기 +- get_contents() 대신 인덱스 접근 (Factorio 2.0 호환) """ import json import math @@ -12,9 +13,7 @@ from factorio_rcon import FactorioRCON ZONE_SIZE = 64 -P = """local p = game.players[1] -if not p then rcon.print("{}") return end -""" +P = 'local p = game.players[1] if not p then rcon.print("{}") return end ' class ContextCompressor: @@ -37,200 +36,117 @@ class ContextCompressor: drilldown = self._drilldown_problem_zones(zones, problems) return self._format_level2(global_summary, zones, problems, drilldown, player) - # ── 글로벌 지표 (pcall로 안전하게) ────────────────────────────── - def _get_global_summary(self) -> dict: lua = P + """ -local surface = p.surface -local force = p.force -local center = p.position - --- type 기반으로 검색 (Factorio 2.0 엔티티 이름 변경에 안전) -local type_list = { - "mining-drill", "furnace", "assembling-machine", - "transport-belt", "inserter", "electric-pole", - "generator", "boiler", "offshore-pump", "pipe", - "lab", "rocket-silo", "radar", - "ammo-turret", "electric-turret", "wall", "gate", - "oil-refinery", "chemical-plant", "solar-panel", "accumulator", - "container" -} - -local counts = {} -local total = 0 -for _, t in ipairs(type_list) do - local ok, found = pcall(function() - return surface.find_entities_filtered{ - area={{center.x-500,center.y-500},{center.x+500,center.y+500}}, - type=t - } - end) - if ok and found then - for _, e in ipairs(found) do - counts[e.name] = (counts[e.name] or 0) + 1 - total = total + 1 +local ok, err = pcall(function() + local surface = p.surface + local force = p.force + local result = {} + local total = 0 + local all = surface.find_entities_filtered{position = p.position, radius = 500, force = "player"} + for _, e in ipairs(all) do + if e.name ~= "character" then + result[e.name] = (result[e.name] or 0) + 1 + total = total + 1 + end end - end -end - --- 연구 진행 -local current_tech = "none" -local tech_progress = 0 -if force.current_research then - current_tech = force.current_research.name - tech_progress = math.floor(force.research_progress * 100) -end - -local researched = 0 -for _, t in pairs(force.technologies) do - if t.researched then researched = researched + 1 end -end - -rcon.print(game.table_to_json({ - total_entities = total, - counts = counts, - current_research = current_tech, - research_progress = tech_progress, - researched_count = researched, - evolution = math.floor(force.evolution_factor * 100) -})) + local current_tech = "none" + local tech_progress = 0 + if force.current_research then + current_tech = force.current_research.name + tech_progress = math.floor(force.research_progress * 100) + end + local researched = 0 + for _, t in pairs(force.technologies) do + if t.researched then researched = researched + 1 end + end + rcon.print(game.table_to_json({ + total_entities = total, + counts = result, + current_research = current_tech, + research_progress = tech_progress, + researched_count = researched, + evolution = math.floor(force.evolution_factor * 100) + })) +end) +if not ok then rcon.print("{}") end """ - raw = self.rcon.lua(lua) try: - return json.loads(raw) if raw else {} + raw = self.rcon.lua(lua) + return json.loads(raw) if raw and raw.startswith("{") else {} except Exception: return {} - # ── 구역 요약 ─────────────────────────────────────────────────── - def _get_zone_summaries(self) -> list[dict]: - lua = P + f""" -local surface = p.surface -local center = p.position -local Z = {ZONE_SIZE} -local radius = 512 -local zones = {{}} - -local function zone_key(x, y) - return math.floor(x/Z) .. "," .. math.floor(y/Z) -end - -local all_entities = surface.find_entities_filtered{{ - area={{{{center.x-radius, center.y-radius}}, - {{center.x+radius, center.y+radius}}}}, - force = "player" -}} - -for _, e in ipairs(all_entities) do - local key = zone_key(e.position.x, e.position.y) - if not zones[key] then - zones[key] = {{ - key = key, - x = math.floor(e.position.x/Z)*Z, - y = math.floor(e.position.y/Z)*Z, - entities = 0, - miners = 0, furnaces = 0, assemblers = 0, - belts = 0, inserters = 0, poles = 0, - turrets = 0, labs = 0, pumps = 0 - }} - end - local z = zones[key] - z.entities = z.entities + 1 - local n = e.name - local t = e.type - if t == "mining-drill" then z.miners = z.miners + 1 - elseif t == "furnace" then z.furnaces = z.furnaces + 1 - elseif t == "assembling-machine" then z.assemblers = z.assemblers + 1 - elseif t == "transport-belt" then z.belts = z.belts + 1 - elseif t == "inserter" then z.inserters = z.inserters + 1 - elseif t == "electric-pole" then z.poles = z.poles + 1 - elseif t == "ammo-turret" or t == "electric-turret" then z.turrets = z.turrets + 1 - elseif n == "lab" then z.labs = z.labs + 1 - elseif t == "offshore-pump" then z.pumps = z.pumps + 1 - end -end - -local result = {{}} -for _, z in pairs(zones) do - if z.entities > 2 then - result[#result+1] = z - end -end -rcon.print(game.table_to_json(result)) + lua = P + """ +local ok, err = pcall(function() + local surface = p.surface + local Z = 64 + local zones = {} + local all = surface.find_entities_filtered{position = p.position, radius = 512, force = "player"} + for _, e in ipairs(all) do + local kx = math.floor(e.position.x / Z) + local ky = math.floor(e.position.y / Z) + local key = kx .. "," .. ky + if not zones[key] then + zones[key] = {key=key, x=kx*Z, y=ky*Z, entities=0, miners=0, furnaces=0, assemblers=0, belts=0, inserters=0, poles=0, turrets=0, labs=0, pumps=0} + end + local z = zones[key] + z.entities = z.entities + 1 + local t = e.type + if t == "mining-drill" then z.miners = z.miners + 1 + elseif t == "furnace" then z.furnaces = z.furnaces + 1 + elseif t == "assembling-machine" then z.assemblers = z.assemblers + 1 + elseif t == "transport-belt" then z.belts = z.belts + 1 + elseif t == "inserter" then z.inserters = z.inserters + 1 + elseif t == "electric-pole" then z.poles = z.poles + 1 + elseif t == "ammo-turret" or t == "electric-turret" then z.turrets = z.turrets + 1 + elseif e.name == "lab" then z.labs = z.labs + 1 + elseif t == "offshore-pump" then z.pumps = z.pumps + 1 + end + end + local result = {} + for _, z in pairs(zones) do + if z.entities > 2 then result[#result+1] = z end + end + rcon.print(game.table_to_json(result)) +end) +if not ok then rcon.print("[]") end """ - raw = self.rcon.lua(lua) try: - return json.loads(raw) if raw else [] + raw = self.rcon.lua(lua) + return json.loads(raw) if raw and raw.startswith("[") else [] except Exception: return [] - # ── 문제 감지 ─────────────────────────────────────────────────── - def _detect_problems(self) -> list[str]: lua = P + """ -local surface = p.surface -local force = p.force -local center = p.position -local problems = {} - --- 1. 연료 없는 버너 건물 -local burner_entities = surface.find_entities_filtered{ - area={{center.x-300,center.y-300},{center.x+300,center.y+300}}, - type={"mining-drill","furnace"} -} -local no_fuel = 0 -for _, e in ipairs(burner_entities) do - if e.burner then - local fi = e.burner.inventory - if fi and fi.is_empty() then - no_fuel = no_fuel + 1 +local ok, err = pcall(function() + local surface = p.surface + local problems = {} + local burners = surface.find_entities_filtered{position = p.position, radius = 300, type = {"mining-drill", "furnace"}} + local no_fuel = 0 + for _, e in ipairs(burners) do + if e.burner then + local fi = e.burner.inventory + if fi and fi.is_empty() then no_fuel = no_fuel + 1 end + end end - end -end -if no_fuel > 0 then - problems[#problems+1] = "연료 부족: " .. no_fuel .. "개 건물이 연료 없음" -end - --- 2. 꽉 찬 삽입기 (병목) -local inserters = surface.find_entities_filtered{ - area={{center.x-300,center.y-300},{center.x+300,center.y+300}}, - type="inserter" -} -local stuck = 0 -for _, ins in ipairs(inserters) do - if ins.held_stack and ins.held_stack.valid_for_read then - stuck = stuck + 1 - end -end -if stuck > 5 then - problems[#problems+1] = "벨트 병목: 삽입기 " .. stuck .. "개가 아이템 들고 멈춤" -end - --- 3. 자원 고갈 임박 -local drills = surface.find_entities_filtered{ - area={{center.x-300,center.y-300},{center.x+300,center.y+300}}, - type="mining-drill" -} -local depleting = 0 -for _, d in ipairs(drills) do - local ore = surface.find_entities_filtered{ - area={{d.position.x-3, d.position.y-3}, - {d.position.x+3, d.position.y+3}}, - type="resource" - } - if #ore > 0 and ore[1].amount < 5000 then - depleting = depleting + 1 - end -end -if depleting > 0 then - problems[#problems+1] = "자원 고갈 임박: " .. depleting .. "개 채굴기 구역 5000 미만" -end - -rcon.print(game.table_to_json(problems)) + if no_fuel > 0 then problems[#problems+1] = "연료 부족: " .. no_fuel .. "개" end + local drills = surface.find_entities_filtered{position = p.position, radius = 300, type = "mining-drill"} + local depleting = 0 + for _, d in ipairs(drills) do + local ore = surface.find_entities_filtered{position = d.position, radius = 3, type = "resource"} + if #ore > 0 and ore[1].amount < 5000 then depleting = depleting + 1 end + end + if depleting > 0 then problems[#problems+1] = "자원 고갈 임박: " .. depleting .. "개" end + rcon.print(game.table_to_json(problems)) +end) +if not ok then rcon.print("[]") end """ - raw = self.rcon.lua(lua) try: - return json.loads(raw) if raw else [] + raw = self.rcon.lua(lua) + return json.loads(raw) if raw and raw.startswith("[") else [] except Exception: return [] @@ -243,31 +159,33 @@ rcon.print(game.table_to_json(problems)) lines.append( f" 구역({z['x']},{z['y']}): " f"채굴기{z.get('miners',0)} 제련소{z.get('furnaces',0)} " - f"조립기{z.get('assemblers',0)} 벨트{z.get('belts',0)} " - f"삽입기{z.get('inserters',0)} 전선주{z.get('poles',0)}" + f"조립기{z.get('assemblers',0)} 벨트{z.get('belts',0)}" ) return "\n".join(lines) def _get_player_info(self) -> dict: lua = P + """ -local inv = p.get_main_inventory() -if not inv then rcon.print("{}") return end -local contents = inv.get_contents() - -local inv_summary = {} -for name, count in pairs(contents) do - if count > 0 then inv_summary[name] = count end -end - -rcon.print(game.table_to_json({ - x = math.floor(p.position.x), - y = math.floor(p.position.y), - inventory = inv_summary -})) +local ok, err = pcall(function() + local inv = p.get_main_inventory() + if not inv then rcon.print("{}") return end + local inv_summary = {} + for i = 1, #inv do + local stack = inv[i] + if stack.valid_for_read then + inv_summary[stack.name] = (inv_summary[stack.name] or 0) + stack.count + end + end + rcon.print(game.table_to_json({ + x = math.floor(p.position.x), + y = math.floor(p.position.y), + inventory = inv_summary + })) +end) +if not ok then rcon.print("{}") end """ - raw = self.rcon.lua(lua) try: - return json.loads(raw) if raw else {} + raw = self.rcon.lua(lua) + return json.loads(raw) if raw and raw.startswith("{") else {} except Exception: return {} @@ -315,17 +233,10 @@ rcon.print(game.table_to_json({ return lines def _guess_zone_role(self, z: dict) -> str: - miners = z.get("miners", 0) - furnaces = z.get("furnaces", 0) - assemblers= z.get("assemblers", 0) - turrets = z.get("turrets", 0) - labs = z.get("labs", 0) - pumps = z.get("pumps", 0) - - if turrets > 3: return "방어구역" - if labs > 0: return "연구실" - if pumps > 0: return "정유/전력" - if assemblers > miners: return "조립라인" - if furnaces > miners: return "제련구역" - if miners > 0: return "채굴구역" + if z.get("turrets", 0) > 3: return "방어구역" + if z.get("labs", 0) > 0: return "연구실" + if z.get("pumps", 0) > 0: return "정유/전력" + if z.get("assemblers", 0) > z.get("miners", 0): return "조립라인" + if z.get("furnaces", 0) > z.get("miners", 0): return "제련구역" + if z.get("miners", 0) > 0: return "채굴구역" return "기타"