순수 플레이 전환: AI 프롬프트에 이동거리/재료/건설거리 제약 반영

This commit is contained in:
2026-03-25 12:48:11 +09:00
parent 3d0f7c0517
commit 75195ece3e

View File

@@ -1,11 +1,12 @@
""" """
ai_planner.py — 완전 자율 에이전트 버전 ai_planner.py — 순수 AI 플레이 버전
핵심 변경사항: 핵심 변경사항 (치트 모드 대비):
- AI가 한 번에 여러 행동을 계획 (프로젝트 단위) - 이동에 실제 시간이 걸림 → 불필요한 장거리 이동 최소화
- 각 행동 결과(성공/실패/이유)를 AI에게 피드백 - 채굴은 자원 패치 근처에서만 가능 → 반드시 move 후 mine
- AI가 실패 원인을 분석하고 다른 방법으로 재시도 - 제작은 재료가 인벤토리에 있어야 함 → 재료 확보 순서 중요
- 삽입기, 전력선, 파이프, 조립기 레시피 등 전체 행동 지원 - 건설은 건설 거리 내에서만 가능 → 배치 전 move 필수
- AI가 이 제약을 이해하고 행동 순서를 계획해야 함
""" """
import json import json
import os import os
@@ -17,25 +18,36 @@ GLM_API_URL = "https://api.z.ai/api/coding/paas/v4/chat/completions"
GLM_MODEL = "GLM-4.7" GLM_MODEL = "GLM-4.7"
SYSTEM_PROMPT = """당신은 팩토리오 게임을 완전 자율적으로 플레이하는 AI 에이전트입니다. SYSTEM_PROMPT = """당신은 팩토리오 게임을 순수하게 플레이하는 AI 에이전트입니다.
치트나 텔레포트 없이, 실제 게임 메커닉만 사용합니다.
게임 상태와 이전 행동 결과를 분석해서 스스로 판단하고 계획을 세웁니다. 게임 상태와 이전 행동 결과를 분석해서 스스로 판단하고 계획을 세웁니다.
## 당신의 역할 ## 핵심 제약 사항 (반드시 준수!)
- 게임 상태를 보고 지금 가장 중요한 일이 무엇인지 스스로 판단 1. **이동은 실제 걷기** — 먼 거리는 시간이 오래 걸림. 불필요한 왕복 최소화
- 그 일을 완료하기 위한 행동 시퀀스 계획 (최대 8개) 2. **채굴은 자원 패치 근처에서만 가능** — 반드시 자원 위치로 move한 후 mine_resource
- 이전 행동이 실패했다면 왜 실패했는지 분석하고 다른 방법 시도 3. **제작은 재료가 있어야 함** — iron-plate 없이 iron-gear-wheel 못 만듦.
- 단기 행동과 장기 목표를 항상 연결해서 생각 재료 확인 후 craft_item. 재료 부족하면 먼저 채굴→제련
4. **건설은 건설 거리 내에서만 가능** — 배치할 좌표 근처로 move한 후 place_entity
5. **자원은 유한** — 직접 채굴해야 하고, 제련소에 넣어야 plate가 됨
## 효율적인 행동 패턴
- 같은 구역 작업을 몰아서 (이동 최소화)
- move → mine/place/insert 순서로 항상 위치 먼저 확보
- 채굴 → 제련 → 제작 → 건설 흐름 유지
- 한 번에 넉넉히 채굴 (왕복 줄이기)
## 팩토리오 자동화 핵심 지식 ## 팩토리오 자동화 핵심 지식
- 채굴기 출력 → inserter → 벨트 → inserter → 제련소/조립기 순서 - 채굴기(burner-mining-drill)는 광맥 위에 배치해야 작동
- 제련소/보일러/조립기는 연료 또는 전력 필요 - 제련소(stone-furnace)에 ore + 석탄 넣으면 plate 생산
- 채굴기 출력 → inserter → 벨트 → inserter → 제련소/조립기
- 제련소/보일러/채굴기는 석탄 연료 필요
- 전력: offshore-pump → pipe → boiler → steam-engine → small-electric-pole - 전력: offshore-pump → pipe → boiler → steam-engine → small-electric-pole
- 자동화 연구팩: iron-gear-wheel + iron-plate → assembling-machine으로 생산 - 자동화 연구팩: iron-gear-wheel + iron-plate → assembling-machine
- 건물 배치 전 반드시 인벤토리에 해당 아이템 존재 확인 - 건물 배치 전 반드시: 1) 인벤토리에 아이템 있는지 2) 가까이 있는지 확인
## 응답 형식 — JSON만 반환, 다른 텍스트 절대 금지 ## 응답 형식 — JSON만 반환, 다른 텍스트 절대 금지
{ {
"thinking": "현재 상태 분석 및 판단 과정 (자유롭게 서술)", "thinking": "현재 상태 분석. 인벤토리/위치/자원 확인 후 판단 (자유롭게 서술)",
"current_goal": "지금 달성하려는 목표", "current_goal": "지금 달성하려는 목표",
"actions": [ "actions": [
{"action": "행동유형", "params": {...}, "reason": "이 행동이 필요한 이유"}, {"action": "행동유형", "params": {...}, "reason": "이 행동이 필요한 이유"},
@@ -46,33 +58,46 @@ SYSTEM_PROMPT = """당신은 팩토리오 게임을 완전 자율적으로 플
## 전체 action 목록 ## 전체 action 목록
### 이동/채굴 ### 이동 (실제 걷기 — 거리에 비례해 시간 소요!)
- "move"{"x": int, "y": int} - "move"{"x": int, "y": int}
주의: 건설/채굴/삽입 전에 반드시 해당 위치 근처로 move
### 채굴 (자원 패치 근처에서만 작동)
- "mine_resource"{"ore": "iron-ore", "count": int} - "mine_resource"{"ore": "iron-ore", "count": int}
주의: 자원 패치 근처에 있어야 함. 없으면 move 먼저
채굴 가능: iron-ore, copper-ore, coal, stone
권장: count는 20~50 단위로 (작으면 비효율, 크면 오래 걸림)
### 제작 ### 제작 (인벤토리에 재료 필요!)
- "craft_item"{"item": str, "count": int} - "craft_item"{"item": str, "count": int}
가능한 item: stone-furnace, burner-mining-drill, transport-belt, 주의: 재료 부족하면 실패. 먼저 재료 확보할 것
burner-inserter, inserter, small-electric-pole, 레시피 예시:
medium-electric-pole, pipe, offshore-pump, boiler, stone-furnace: stone 5개
steam-engine, assembling-machine-1, iron-gear-wheel, lab burner-mining-drill: iron-gear-wheel 3 + iron-plate 3 + stone-furnace 1
transport-belt: iron-gear-wheel 1 + iron-plate 1
burner-inserter: iron-gear-wheel 1 + iron-plate 1
inserter: iron-gear-wheel 1 + iron-plate 1 + electronic-circuit 1
small-electric-pole: copper-cable 2 + wood 1
iron-gear-wheel: iron-plate 2
pipe: iron-plate 1
### 건물 배치 ### 건물 배치 (건설 거리 내에서만!)
- "place_entity" {"name": "burner-mining-drill", "x": int, "y": int, "direction": "north|south|east|west"} - "place_entity"{"name": str, "x": int, "y": int, "direction": "north|south|east|west"}
주의: 1) 인벤토리에 아이템 필요 2) 가까이 있어야 함 (약 10칸 내)
배치 가능: burner-mining-drill, electric-mining-drill, stone-furnace, 배치 가능: burner-mining-drill, electric-mining-drill, stone-furnace,
burner-inserter, inserter, fast-inserter, transport-belt, burner-inserter, inserter, transport-belt, small-electric-pole,
underground-belt, splitter, small-electric-pole, pipe, offshore-pump, boiler, steam-engine, assembling-machine-1, lab
medium-electric-pole, pipe, pipe-to-ground, offshore-pump,
boiler, steam-engine, assembling-machine-1, lab, chest
### 벨트 라인 (자동 경로) ### 벨트 라인 (걸어가며 한 칸씩 배치 — 시간 많이 걸림)
- "place_belt_line"{"from_x": int, "from_y": int, "to_x": int, "to_y": int} - "place_belt_line"{"from_x": int, "from_y": int, "to_x": int, "to_y": int}
주의: 벨트 수량 충분히 craft_item 먼저
### 연료/아이템 삽입 ### 연료/아이템 삽입 (건설 거리 내에서)
- "insert_to_entity"{"x": int, "y": int, "item": "coal", "count": int} - "insert_to_entity"{"x": int, "y": int, "item": "coal", "count": int}
주의: 가까이 있어야 하고, 플레이어 인벤토리에서 차감됨
### 조립기 레시피 설정 ### 조립기 레시피 설정
- "set_recipe" {"x": int, "y": int, "recipe": "automation-science-pack"} - "set_recipe"{"x": int, "y": int, "recipe": str}
### 연구 ### 연구
- "start_research"{"tech": "automation"} - "start_research"{"tech": "automation"}
@@ -92,8 +117,8 @@ class AIPlanner:
self.feedback_log: list[dict] = [] self.feedback_log: list[dict] = []
self.long_term_goal = ( self.long_term_goal = (
"완전 자동화 달성: " "완전 자동화 달성: "
"/구리 채굴제련 자동화 → 전력 구축 → automation 연구" "석탄 채굴 → 철 채굴+제련 자동화 → 구리 채굴+제련"
"빨간 과학팩 자동 생산 → lab에서 연구" "전력 구축 → automation 연구 → 빨간 과학팩 자동 생산"
) )
def decide(self, state_summary: str) -> list[dict]: def decide(self, state_summary: str) -> list[dict]:
@@ -110,6 +135,8 @@ class AIPlanner:
f"{feedback_text}" f"{feedback_text}"
f"### 장기 목표\n{self.long_term_goal}\n\n" f"### 장기 목표\n{self.long_term_goal}\n\n"
"현재 상태를 분석하고, 장기 목표를 향해 지금 해야 할 행동 시퀀스를 계획하세요.\n" "현재 상태를 분석하고, 장기 목표를 향해 지금 해야 할 행동 시퀀스를 계획하세요.\n"
"⚠️ 순수 플레이입니다. 건설/채굴/삽입 전에 반드시 move로 가까이 이동하세요.\n"
"⚠️ 제작은 재료가 있어야 합니다. 인벤토리를 확인하세요.\n"
"반드시 JSON만 반환하세요." "반드시 JSON만 반환하세요."
) )
@@ -160,7 +187,7 @@ class AIPlanner:
{"role": "user", "content": user_message}, {"role": "user", "content": user_message},
], ],
"temperature": 0.3, "temperature": 0.3,
"max_tokens": 1200, "max_tokens": 1500,
}).encode("utf-8") }).encode("utf-8")
req = urllib.request.Request( req = urllib.request.Request(