From bc7bb4d1e62e322a8198ae53823005568bfb5e1a Mon Sep 17 00:00:00 2001 From: gihyeon Date: Wed, 25 Mar 2026 20:59:03 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20mine=5Fresource=20=EC=8B=A4=ED=8C=A8?= =?UTF-8?q?=ED=95=9C=20=ED=83=80=EC=9D=BC=20=EC=A2=8C=ED=91=9C=20=EA=B8=B0?= =?UTF-8?q?=EC=96=B5=20+=20Lua=EC=97=90=EC=84=9C=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 근본 원인: 매번 같은 타일(388,2)을 찾아서 무한 반복 수정: 1. Python에서 failed_positions set() 유지 2. Lua에 exclude 테이블 전달 → 실패한 좌표 건너뛰기 3. 접근 불가 타일은 즉시 제외 목록에 추가 4. 고갈된 타일도 제외 5. 최대 15개 다른 타일 순서대로 시도 6. ALL_EXCLUDED 시 명확한 메시지 반환 --- action_executor.py | 85 +++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/action_executor.py b/action_executor.py index 085e43c..b723636 100644 --- a/action_executor.py +++ b/action_executor.py @@ -2,9 +2,8 @@ action_executor.py — 순수 AI 플레이 버전 핵심 수정: -- mine_resource: 광석 타일로 먼저 WALK → 도착 후 mining_state -- 손 채굴 거리 2.7타일 이내에서만 채굴 가능 -- 가장 가까운 광석으로 걸어가서 채굴 → 고갈 시 다음 타일로 이동 +- mine_resource: 실패한 타일 좌표를 기억하고 Lua에 전달해서 제외 +- 매 라운드마다 다른 타일을 찾도록 보장 """ import time from factorio_rcon import FactorioRCON @@ -80,7 +79,7 @@ if ok then rcon.print(data) else rcon.print("ERROR") end last_pos = pos_str if stuck_count >= 3: self.rcon.lua(P + "p.walking_state = {walking = false, direction = defines.direction.north}") - return False, f"장애물에 막힘 위치({pos_str}) — 다른 방향 시도" + return False, f"장애물에 막힘 ({pos_str}) — 다른 방향 시도" self.rcon.lua(P + "p.walking_state = {walking = false, direction = defines.direction.north}") return False, f"{direction} 방향 자원 미발견 — 다른 방향 시도" @@ -126,56 +125,80 @@ rcon.print("WALK:" .. string.format("%.1f", dist)) self.rcon.lua(P + "p.walking_state = {walking = false, direction = defines.direction.north}") return False, f"({x},{y}) 시간초과 (거리: {last_dist:.0f})" - # ── 채굴 (광석으로 걸어간 뒤 채굴) ────────────────────────────── + # ── 채굴 (실패 타일 제외 + 걸어간 뒤 채굴) ────────────────────── def mine_resource(self, ore: str = "iron-ore", count: int = 10) -> tuple[bool, str]: - """1. 가까운 광석 찾기 → 2. 그 위치로 걸어가기 → 3. 채굴. - 한 타일 고갈 시 다음 가까운 타일로 자동 이동.""" + """1. 가까운 광석 찾기 (실패한 좌표 제외) + 2. 그 위치로 걸어가기 + 3. 채굴 시도 + 4. 실패 시 해당 좌표를 제외 목록에 추가 → 반복""" before_count = self._get_item_count(ore) target_count = before_count + count total_mined = 0 + failed_positions = set() # 실패한 좌표 기억 + + for round_num in range(15): # 최대 15번 시도 + # 1. Lua에서 가장 가까운 광석 찾기 (실패한 좌표 제외) + # 제외 좌표를 Lua 테이블로 전달 + exclude_lua = "local exclude = {}\n" + for i, (fx, fy) in enumerate(failed_positions): + exclude_lua += f'exclude["{fx:.0f},{fy:.0f}"] = true\n' - for round_num in range(10): # 최대 10번 이동+채굴 시도 - # 1. 가장 가까운 광석 위치 찾기 find_result = self.rcon.lua(P + f""" +{exclude_lua} local res = p.surface.find_entities_filtered{{position = p.position, radius = 80, name = "{ore}"}} if #res == 0 then rcon.print("NOT_FOUND") return end --- 거리순 정렬해서 가장 가까운 1개 -local closest = res[1] -local min_dist = 999999 +-- 거리순 정렬 +local pos = p.position +table.sort(res, function(a, b) + local da = (a.position.x - pos.x)^2 + (a.position.y - pos.y)^2 + local db = (b.position.x - pos.x)^2 + (b.position.y - pos.y)^2 + return da < db +end) +-- 제외 목록에 없는 가장 가까운 광석 찾기 for _, e in ipairs(res) do - local d = (e.position.x - p.position.x)^2 + (e.position.y - p.position.y)^2 - if d < min_dist then min_dist = d closest = e end + local key = string.format("%.0f,%.0f", e.position.x, e.position.y) + if not exclude[key] then + rcon.print(string.format("%.1f,%.1f", e.position.x, e.position.y)) + return + end end -rcon.print(string.format("%.1f,%.1f", closest.position.x, closest.position.y)) +rcon.print("ALL_EXCLUDED") """) - if not find_result or find_result in ("NO_PLAYER", "NO_CHARACTER", "NOT_FOUND"): + if not find_result or find_result in ("NO_PLAYER", "NO_CHARACTER"): + break + if find_result == "NOT_FOUND": if total_mined > 0: return True, f"{ore} {total_mined}개 채굴 (주변 광석 소진)" return False, f"반경 80 내 {ore} 없음 — explore로 다른 광맥 찾기" + if find_result == "ALL_EXCLUDED": + if total_mined > 0: + return True, f"{ore} {total_mined}개 채굴 (접근 가능 타일 모두 시도)" + return False, f"{ore} 근처 타일 {len(failed_positions)}개 모두 접근 불가 — 다른 위치로 이동 필요" try: parts = find_result.split(",") ox, oy = float(parts[0]), float(parts[1]) except: - return False, f"광석 좌표 파싱 실패: {find_result}" + return False, f"좌표 파싱 실패: {find_result}" # 2. 광석 위치로 걸어가기 - print(f" [채굴] 광석({ox:.0f},{oy:.0f})으로 이동 중...") + print(f" [채굴] 광석({ox:.0f},{oy:.0f})으로 이동... (시도 {round_num+1}, 제외: {len(failed_positions)}개)") ok, msg = self.move(int(ox), int(oy)) if not ok: - print(f" [채굴] 이동 실패: {msg} — 다음 시도") - # 이동 실패해도 현재 위치에서 채굴 시도 - pass + print(f" [채굴] 이동 실패: {msg}") + failed_positions.add((ox, oy)) + continue - # 3. 현재 위치에서 반경 3 이내 광석 채굴 (손 채굴 거리) + # 3. 현재 위치에서 채굴 시도 stall_count = 0 last_item = self._get_item_count(ore) + mined_this_tile = False for tick in range(300): # 최대 30초 self.rcon.lua(P + f"p.mining_state = {{mining = true, position = {{{ox}, {oy}}}}}") time.sleep(0.1) - if tick % 8 == 7: # 매 0.8초마다 체크 + if tick % 8 == 7: current = self._get_item_count(ore) if current >= target_count: self.rcon.lua(P + "p.mining_state = {mining = false}") @@ -185,24 +208,32 @@ rcon.print(string.format("%.1f,%.1f", closest.position.x, closest.position.y)) if current > last_item: stall_count = 0 last_item = current + mined_this_tile = True else: stall_count += 1 - # 2번 연속 진행 없음 → 이 타일 안 닿음, 다음으로 if stall_count >= 2: - print(f" [채굴] 타일({ox:.0f},{oy:.0f}) 채굴 불가/고갈 — 다음 타일 이동") break self.rcon.lua(P + "p.mining_state = {mining = false}") total_mined = self._get_item_count(ore) - before_count + if not mined_this_tile: + # 이 타일에서 한 개도 못 캤음 → 접근 불가 + failed_positions.add((ox, oy)) + print(f" [채굴] ({ox:.0f},{oy:.0f}) 접근 불가 → 제외 목록 추가 (총 {len(failed_positions)}개)") + else: + # 채굴 됐지만 타일 고갈 + print(f" [채굴] ({ox:.0f},{oy:.0f}) 고갈/중단. 현재 {total_mined}개 채굴됨") + failed_positions.add((ox, oy)) # 고갈된 것도 제외 + if total_mined >= count: return True, f"{ore} {total_mined}개 채굴 완료" - # 10라운드 후 + # 모든 라운드 후 if total_mined > 0: return True, f"{ore} {total_mined}개 채굴 (목표 {count}개 중 일부)" - return False, f"{ore} 채굴 실패 — 접근 가능한 광석 없음" + return False, f"{ore} 채굴 실패 — {len(failed_positions)}개 타일 접근 불가" # ── 제작/배치/삽입/레시피/연구/대기 ────────────────────────────── def craft_item(self, item: str, count: int = 1) -> tuple[bool, str]: