feat: 인벤토리 캐시 및 JSON 인코더 추가

- 인벤토리 캐시 기능을 추가하여, RCON으로 인벤토리를 읽지 못할 경우 이전에 성공적으로 읽은 데이터를 활용
- Lua에서 JSON 인코딩을 위한 간단한 함수 추가, 일부 Factorio 버전에서 `game.table_to_json`이 없을 경우 대체
- `README.md`에 인벤토리 캐시 및 JSON 인코더 사용에 대한 설명 추가
- `scan_resources()`와 `mine_resource`의 반경을 확장하여 자원 탐색 실패를 줄임
This commit is contained in:
21in7
2026-03-25 23:03:08 +09:00
parent e98d08bb44
commit 8c90e80582
14 changed files with 573 additions and 123 deletions

View File

@@ -15,6 +15,56 @@ ZONE_SIZE = 64
P = 'local p = game.players[1] if not p then rcon.print("{}") return end '
# Factorio 버전에 따라 game.table_to_json 이 없을 수 있어,
# Lua 내부에서 간단한 JSON 인코더를 제공한다.
JSON_HELPER_LUA = r"""
local function json_escape(s)
s = tostring(s)
s = s:gsub("\\", "\\\\")
s = s:gsub('"', '\\"')
s = s:gsub('\b', '\\b')
s = s:gsub('\f', '\\f')
s = s:gsub('\n', '\\n')
s = s:gsub('\r', '\\r')
s = s:gsub('\t', '\\t')
return s
end
local function json_encode_value(v)
local t = type(v)
if t == "string" then
return '"' .. json_escape(v) .. '"'
elseif t == "number" then
return tostring(v)
elseif t == "boolean" then
return v and "true" or "false"
elseif t == "table" then
-- object encoding (배열은 별도 함수에서 처리)
local parts = {}
for k, val in pairs(v) do
local key = '"' .. json_escape(k) .. '"'
parts[#parts + 1] = key .. ":" .. json_encode_value(val)
end
return "{" .. table.concat(parts, ",") .. "}"
else
return "null"
end
end
local function json_encode_object(t)
return json_encode_value(t)
end
local function json_encode_array(arr)
local parts = {}
local n = #arr
for i = 1, n do
parts[#parts + 1] = json_encode_value(arr[i])
end
return "[" .. table.concat(parts, ",") .. "]"
end
"""
class ContextCompressor:
def __init__(self, rcon: FactorioRCON):
@@ -37,7 +87,7 @@ class ContextCompressor:
return self._format_level2(global_summary, zones, problems, drilldown, player)
def _get_global_summary(self) -> dict:
lua = P + """
lua = P + JSON_HELPER_LUA + """
local ok, err = pcall(function()
local surface = p.surface
local force = p.force
@@ -60,7 +110,7 @@ local ok, err = pcall(function()
for _, t in pairs(force.technologies) do
if t.researched then researched = researched + 1 end
end
rcon.print(game.table_to_json({
rcon.print(json_encode_object({
total_entities = total,
counts = result,
current_research = current_tech,
@@ -78,7 +128,7 @@ if not ok then rcon.print("{}") end
return {}
def _get_zone_summaries(self) -> list[dict]:
lua = P + """
lua = P + JSON_HELPER_LUA + """
local ok, err = pcall(function()
local surface = p.surface
local Z = 64
@@ -109,7 +159,7 @@ local ok, err = pcall(function()
for _, z in pairs(zones) do
if z.entities > 2 then result[#result+1] = z end
end
rcon.print(game.table_to_json(result))
rcon.print(json_encode_array(result))
end)
if not ok then rcon.print("[]") end
"""
@@ -120,7 +170,7 @@ if not ok then rcon.print("[]") end
return []
def _detect_problems(self) -> list[str]:
lua = P + """
lua = P + JSON_HELPER_LUA + """
local ok, err = pcall(function()
local surface = p.surface
local problems = {}
@@ -140,7 +190,7 @@ local ok, err = pcall(function()
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))
rcon.print(json_encode_array(problems))
end)
if not ok then rcon.print("[]") end
"""
@@ -164,7 +214,7 @@ if not ok then rcon.print("[]") end
return "\n".join(lines)
def _get_player_info(self) -> dict:
lua = P + """
lua = P + JSON_HELPER_LUA + """
local ok, err = pcall(function()
local inv = p.get_main_inventory()
if not inv then rcon.print("{}") return end
@@ -181,7 +231,7 @@ local ok, err = pcall(function()
end
end
end
rcon.print(game.table_to_json({
rcon.print(json_encode_object({
x = math.floor(p.position.x),
y = math.floor(p.position.y),
inventory = inv_summary