- 인벤토리 캐시 기능을 추가하여, RCON으로 인벤토리를 읽지 못할 경우 이전에 성공적으로 읽은 데이터를 활용 - Lua에서 JSON 인코딩을 위한 간단한 함수 추가, 일부 Factorio 버전에서 `game.table_to_json`이 없을 경우 대체 - `README.md`에 인벤토리 캐시 및 JSON 인코더 사용에 대한 설명 추가 - `scan_resources()`와 `mine_resource`의 반경을 확장하여 자원 탐색 실패를 줄임
7.0 KiB
7.0 KiB
팩토리오 AI 에이전트 (순수 플레이)
AI가 팩토리오를 치트 없이 자율적으로 플레이하는 에이전트입니다. 실제 걷기, 실제 채굴, 실제 제작, 건설 거리 제한 등 모든 게임 메커닉을 준수합니다.
순수 플레이 모드란?
| 항목 | 치트 모드 | 순수 모드 (현재) |
|---|---|---|
| 이동 | 텔레포트 | 실제 걷기 (walking_state) |
| 채굴 | 인벤토리 직접 삽입 | 자원 패치에서 실제 채굴 (mining_state) |
| 제작 | 무조건 지급 | 재료 소모 실제 제작 (begin_crafting) |
| 건설 | 어디서든 create_entity | 건설 거리 내에서 build_from_cursor |
| 삽입 | 무한 아이템 | 플레이어 인벤토리에서 차감 |
파일 구조
factorio_ai/
├── main.py ← 메인 루프 (여기서 실행)
├── factorio_rcon.py ← RCON 연결 (게임과 통신)
├── state_reader.py ← 게임 상태 읽기 (자원, 인벤토리, 건물)
├── context_compressor.py ← 중반 이후 상태 압축
├── ai_planner.py ← AI 행동 계획 (순수 플레이 제약 반영)
├── action_executor.py ← 실제 게임 조작 (순수 메커닉)
└── agent_log.jsonl ← 행동 로그 (자동 생성)
설치
# 외부 라이브러리 불필요! Python 표준 라이브러리만 사용
팩토리오 RCON 설정
방법 1: 싱글플레이
- 팩토리오 실행
- 게임 시작 후 콘솔 열기 (` 키 또는 ~)
- 아래 입력:
/rcon-port 25575 /rcon-password factorio_ai
방법 2: 서버 모드 (권장)
# Windows
factorio.exe --start-server saves/mysave.zip --rcon-port 25575 --rcon-password factorio_ai
# Linux/Mac
./factorio --start-server saves/mysave.zip --rcon-port 25575 --rcon-password factorio_ai
실행
# Z.ai API 키 설정
export ZAI_API_KEY="your-key-here"
# 기본 실행
python main.py
# 커스텀 서버
FACTORIO_HOST=192.168.1.10 FACTORIO_PORT=25575 FACTORIO_PASSWORD=mypass python main.py
# Z.ai API 키 설정
$env:ZAI_API_KEY="your-key-here"
# 기본 실행
python .\main.py
# 커스텀 서버 (한 줄 버전)
$env:FACTORIO_HOST="192.168.1.10"; $env:FACTORIO_PORT="25575"; $env:FACTORIO_PASSWORD="mypass"; python .\main.py
# (권장) 스크립트 실행
.\run_factorio_ai.ps1 "your-key-here" "mypass"
동작 원리
1. RCON → 팩토리오에서 현재 상태 읽기
(플레이어 위치, 인벤토리, 자원 패치, 건물 목록)
2. 상태 → AI API로 전송
"다음에 뭘 해야 하나요?" (순수 플레이 제약 포함)
3. AI → JSON 행동 시퀀스 반환
[move → mine_resource → craft_item → place_entity ...]
4. RCON → 팩토리오에서 실제 게임 메커닉으로 실행
- 캐릭터가 실제로 걸어감
- 실제로 곡괭이질해서 채굴
- 재료를 소모해서 제작
- 건설 거리 내에서만 배치
AI 목표 변경
planner.set_goal(
"전력 인프라 구축: offshore-pump → boiler → steam-engine → 전선 연결"
)
주의사항
- 순수 플레이이므로 걷기, 채굴, 제작에 실제 시간이 소요됩니다
- AI가 "move 먼저 → 작업" 패턴을 학습하도록 프롬프트가 설계되어 있습니다
agent_log.jsonl에 모든 행동과 타임스탬프가 기록됩니다ai_planner.py는 GLM 응답이 잘리거나(중괄호/대괄호 불일치) 마크다운이 섞여도 JSON 파싱을 복구하도록{} / []균형 추적과 보정 로직을 사용합니다.ai_planner.py의_call_glm()에서 GLM 지연 원인 분석을 위한 타이밍 로그가 출력됩니다.total: 요청 시작~콘텐츠 반환까지 전체 소요http_read: HTTP 응답 본문 수신까지 소요json_parse: 응답 JSON 파싱 시간
- 파이썬 에이전트 재실행 시 인벤토리 “기억”이 필요하면
inventory_memory.json캐시를 사용합니다.RCON에서 인벤토리를 읽지 못하거나(비정상 출력/파싱 실패 등) 직전에 성공적으로 읽은 인벤토리를 대신 사용합니다.- 성공적으로 인벤토리가 읽히면 해당 캐시를 갱신합니다(코드는
inv.get_contents()가nil/비테이블을 반환해도 안전하게{}로 취급). - 또한 일부 환경에서
p.get_main_inventory()가 없으면defines.inventory.character_main으로 한 번 더 읽습니다. - 캐시 파일이 아직 없으면(초기 실행) fallback도
{}가 됩니다.
- (중요) 일부 Factorio 버전에서는
game.table_to_json이 없을 수 있어서, Lua 내부에 간단 JSON 인코더를 넣어 인벤토리/상태를 안정적으로 가져옵니다. - 디버깅용으로
INV_DEBUG=1을 켜면,main inventory외에cursor_stack/armor/trash총합도 함께 출력합니다(어디에 아이템이 있는지 확인용).- 또한
main inventory가 비어 캐시 fallback이 동작하면, 로드된 캐시의items/total도 함께 출력합니다.
- 또한
mine_resource에서 실패한 채굴 타일 제외(exclude)는 Lua와 Python 양쪽에서 정수 타일 좌표(tx, ty) 키로 통일해, 제외한 좌표가 반복 선택되지 않도록 합니다.- 또한 채굴 시작(
mining_state) 좌표는 정수 타일이 아니라, Lua가 찾은 실제 자원 엔티티의e.position(정확 실수 좌표)을 사용해 “플레이어가 타일 위에 있는데도 즉시 채굴 감지 실패”를 줄입니다. mine_resource는 move 후p.position근처(반경 1.2)에서 실제로 해당 광물 엔티티가 있는지 재확인하고, 없으면 채굴을 시도하지 않고 다음 후보로 넘어갑니다.scan_resources()는 패치 평균 중심(center_x/y)만이 아니라 플레이어 기준 가장 가까운 실제 엔티티 좌표(anchor_x/y)도 함께 반환하며,summarize_for_ai()에서는 앵커를 기준으로 거리/추천을 계산합니다.mine_resource의 초기 “후보 엔티티 탐색” 반경은 패치 중심 오차를 흡수하기 위해 250으로 확장해, 이동 직후 바로 실패하는 케이스를 줄입니다.explore는wanted_ores가 있으면 해당 자원이 발견될 때까지 멈추지 않고 계속 이동해,iron-ore처럼 주변에 흔한 자원만 계속 발견되어 진행이 막히는 문제를 줄입니다.- (Cursor) Windows에서
sessionStart훅 실행 중 앱 선택창이 뜨는 경우:- 프로젝트 훅은
E:/develop/factorio-ai-agent/.cursor/hooks.json및E:/develop/factorio-ai-agent/.cursor/session-start-hook.ps1를 PowerShell로 실행하도록 구성되어 있습니다. - Superpowers 플러그인의
./hooks/session-start가 bash로 실행되도록hooks-cursor.json을 수정했습니다(필요 시 Cursor 재시작 후View -> Output -> Hooks에서 확인).
- 프로젝트 훅은
- 인벤토리 판독은
inv.get_contents()를 우선 사용해, 일부 환경에서#inv/ 인덱스 접근이 비어있음으로 오인되는 문제를 줄입니다.