fix: mine_resource 실패한 타일 좌표 기억 + Lua에서 제외
근본 원인: 매번 같은 타일(388,2)을 찾아서 무한 반복 수정: 1. Python에서 failed_positions set() 유지 2. Lua에 exclude 테이블 전달 → 실패한 좌표 건너뛰기 3. 접근 불가 타일은 즉시 제외 목록에 추가 4. 고갈된 타일도 제외 5. 최대 15개 다른 타일 순서대로 시도 6. ALL_EXCLUDED 시 명확한 메시지 반환
This commit is contained in:
@@ -2,9 +2,8 @@
|
|||||||
action_executor.py — 순수 AI 플레이 버전
|
action_executor.py — 순수 AI 플레이 버전
|
||||||
|
|
||||||
핵심 수정:
|
핵심 수정:
|
||||||
- mine_resource: 광석 타일로 먼저 WALK → 도착 후 mining_state
|
- mine_resource: 실패한 타일 좌표를 기억하고 Lua에 전달해서 제외
|
||||||
- 손 채굴 거리 2.7타일 이내에서만 채굴 가능
|
- 매 라운드마다 다른 타일을 찾도록 보장
|
||||||
- 가장 가까운 광석으로 걸어가서 채굴 → 고갈 시 다음 타일로 이동
|
|
||||||
"""
|
"""
|
||||||
import time
|
import time
|
||||||
from factorio_rcon import FactorioRCON
|
from factorio_rcon import FactorioRCON
|
||||||
@@ -80,7 +79,7 @@ if ok then rcon.print(data) else rcon.print("ERROR") end
|
|||||||
last_pos = pos_str
|
last_pos = pos_str
|
||||||
if stuck_count >= 3:
|
if stuck_count >= 3:
|
||||||
self.rcon.lua(P + "p.walking_state = {walking = false, direction = defines.direction.north}")
|
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}")
|
self.rcon.lua(P + "p.walking_state = {walking = false, direction = defines.direction.north}")
|
||||||
return False, f"{direction} 방향 자원 미발견 — 다른 방향 시도"
|
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}")
|
self.rcon.lua(P + "p.walking_state = {walking = false, direction = defines.direction.north}")
|
||||||
return False, f"({x},{y}) 시간초과 (거리: {last_dist:.0f})"
|
return False, f"({x},{y}) 시간초과 (거리: {last_dist:.0f})"
|
||||||
|
|
||||||
# ── 채굴 (광석으로 걸어간 뒤 채굴) ──────────────────────────────
|
# ── 채굴 (실패 타일 제외 + 걸어간 뒤 채굴) ──────────────────────
|
||||||
def mine_resource(self, ore: str = "iron-ore", count: int = 10) -> tuple[bool, str]:
|
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)
|
before_count = self._get_item_count(ore)
|
||||||
target_count = before_count + count
|
target_count = before_count + count
|
||||||
total_mined = 0
|
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"""
|
find_result = self.rcon.lua(P + f"""
|
||||||
|
{exclude_lua}
|
||||||
local res = p.surface.find_entities_filtered{{position = p.position, radius = 80, name = "{ore}"}}
|
local res = p.surface.find_entities_filtered{{position = p.position, radius = 80, name = "{ore}"}}
|
||||||
if #res == 0 then rcon.print("NOT_FOUND") return end
|
if #res == 0 then rcon.print("NOT_FOUND") return end
|
||||||
-- 거리순 정렬해서 가장 가까운 1개
|
-- 거리순 정렬
|
||||||
local closest = res[1]
|
local pos = p.position
|
||||||
local min_dist = 999999
|
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
|
for _, e in ipairs(res) do
|
||||||
local d = (e.position.x - p.position.x)^2 + (e.position.y - p.position.y)^2
|
local key = string.format("%.0f,%.0f", e.position.x, e.position.y)
|
||||||
if d < min_dist then min_dist = d closest = e end
|
if not exclude[key] then
|
||||||
|
rcon.print(string.format("%.1f,%.1f", e.position.x, e.position.y))
|
||||||
|
return
|
||||||
|
end
|
||||||
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:
|
if total_mined > 0:
|
||||||
return True, f"{ore} {total_mined}개 채굴 (주변 광석 소진)"
|
return True, f"{ore} {total_mined}개 채굴 (주변 광석 소진)"
|
||||||
return False, f"반경 80 내 {ore} 없음 — explore로 다른 광맥 찾기"
|
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:
|
try:
|
||||||
parts = find_result.split(",")
|
parts = find_result.split(",")
|
||||||
ox, oy = float(parts[0]), float(parts[1])
|
ox, oy = float(parts[0]), float(parts[1])
|
||||||
except:
|
except:
|
||||||
return False, f"광석 좌표 파싱 실패: {find_result}"
|
return False, f"좌표 파싱 실패: {find_result}"
|
||||||
|
|
||||||
# 2. 광석 위치로 걸어가기
|
# 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))
|
ok, msg = self.move(int(ox), int(oy))
|
||||||
if not ok:
|
if not ok:
|
||||||
print(f" [채굴] 이동 실패: {msg} — 다음 시도")
|
print(f" [채굴] 이동 실패: {msg}")
|
||||||
# 이동 실패해도 현재 위치에서 채굴 시도
|
failed_positions.add((ox, oy))
|
||||||
pass
|
continue
|
||||||
|
|
||||||
# 3. 현재 위치에서 반경 3 이내 광석 채굴 (손 채굴 거리)
|
# 3. 현재 위치에서 채굴 시도
|
||||||
stall_count = 0
|
stall_count = 0
|
||||||
last_item = self._get_item_count(ore)
|
last_item = self._get_item_count(ore)
|
||||||
|
mined_this_tile = False
|
||||||
|
|
||||||
for tick in range(300): # 최대 30초
|
for tick in range(300): # 최대 30초
|
||||||
self.rcon.lua(P + f"p.mining_state = {{mining = true, position = {{{ox}, {oy}}}}}")
|
self.rcon.lua(P + f"p.mining_state = {{mining = true, position = {{{ox}, {oy}}}}}")
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
if tick % 8 == 7: # 매 0.8초마다 체크
|
if tick % 8 == 7:
|
||||||
current = self._get_item_count(ore)
|
current = self._get_item_count(ore)
|
||||||
if current >= target_count:
|
if current >= target_count:
|
||||||
self.rcon.lua(P + "p.mining_state = {mining = false}")
|
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:
|
if current > last_item:
|
||||||
stall_count = 0
|
stall_count = 0
|
||||||
last_item = current
|
last_item = current
|
||||||
|
mined_this_tile = True
|
||||||
else:
|
else:
|
||||||
stall_count += 1
|
stall_count += 1
|
||||||
|
|
||||||
# 2번 연속 진행 없음 → 이 타일 안 닿음, 다음으로
|
|
||||||
if stall_count >= 2:
|
if stall_count >= 2:
|
||||||
print(f" [채굴] 타일({ox:.0f},{oy:.0f}) 채굴 불가/고갈 — 다음 타일 이동")
|
|
||||||
break
|
break
|
||||||
|
|
||||||
self.rcon.lua(P + "p.mining_state = {mining = false}")
|
self.rcon.lua(P + "p.mining_state = {mining = false}")
|
||||||
total_mined = self._get_item_count(ore) - before_count
|
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:
|
if total_mined >= count:
|
||||||
return True, f"{ore} {total_mined}개 채굴 완료"
|
return True, f"{ore} {total_mined}개 채굴 완료"
|
||||||
|
|
||||||
# 10라운드 후
|
# 모든 라운드 후
|
||||||
if total_mined > 0:
|
if total_mined > 0:
|
||||||
return True, f"{ore} {total_mined}개 채굴 (목표 {count}개 중 일부)"
|
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]:
|
def craft_item(self, item: str, count: int = 1) -> tuple[bool, str]:
|
||||||
|
|||||||
Reference in New Issue
Block a user