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 플레이 버전
|
||||
|
||||
핵심 수정:
|
||||
- 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]:
|
||||
|
||||
Reference in New Issue
Block a user