# AI 스킴

> 스킴 하나가 곧 완전한 AI 하나입니다. Farsight에서 클릭 한 번으로 바꾸고, 진행 중인 게임도 즉시 새 AI가 넘겨받을 수 있습니다. zip으로 내보내 공유하고, 다른 사람의 스킴을 가져와 테스트하며, 스킴마다 전적이 자동으로 집계됩니다.

출처: https://war3ai.com/ko/docs/schemes/

**스킴** 하나 = 완전한 AI 하나: 폴더 하나 + 매니페스트 `scheme.json` + 코드. 게임 인스턴스마다 스킴을 하나씩 고르며, Farsight에서 클릭 한 번으로 바꿀 수 있고 **진행 중인 게임도 즉시 새 스킴이 넘겨받습니다**.

다른 사람이 공유한 스킴을 가져오면 **별도의 영역**에 들어가므로 여러분의 스킴과 서로 영향을 주지 않습니다. 고치고 싶다면 "내 스킴으로 복사"하세요.

```text
schemes/
  mine/<id>/          내 스킴: 직접 쓴 것, 또는 다른 스킴에서 복사해 고친 것(마음대로 수정, 다음 게임부터 적용)
  installed/<id>/     설치됨: 다른 사람이 공유한 zip을 여기에 풂(처음 실행하기 전에 신뢰 확인 필요)
brains/xwar3/         내장: 레퍼런스 브레인(완전한 AI)
brains/examples/      내장: 교육용 예제 4개 hello / rush / macro / micro, 동료 예제 buddy, 게임플레이 모드 2개(영웅 로그라이크, 무한 디펜스)
```

스킴이 꼭 여러분 대신 싸우는 AI일 필요는 없습니다. `kind: mod`인 스킴은 하나의 **게임 규칙**입니다. 여러분이 직접 플레이하고 스킴은 과제를 냅니다. [게임플레이 모드](https://war3ai.com/ko/docs/mods/)를 참고하세요.

## Farsight에서 사용하기

"AI 스킴" 페이지(왼쪽 사이드바 "시스템 → AI 스킴"):

| 작업 | 하는 일 |
|---|---|
| 스킴 가져오기(zip) | `installed/`에 설치. 같은 id가 이미 설치되어 있으면 교체할지 물음(교체하면 신뢰를 다시 확인해야 함) |
| 인스턴스에 적용… | 인스턴스 선택 + "즉시 적용"(현재 AI를 멈추고 새 스킴이 이 게임을 넘겨받음) 또는 "다음 테스트 시작부터 적용" |
| 내 스킴으로 복사 | `mine/`에 복사본을 만들고, 작성자는 "나", 버전은 0.1.0으로 기록하며, 어느 스킴의 어느 버전에서 복사했는지도 기록 |
| zip 내보내기 | `<id>-<버전>.zip`으로 묶음. 다른 사람에게 보내면 그것이 곧 공유 |
| 폴더 열기 | 탐색기에서 스킴 폴더를 열어 코드를 직접 수정 |
| 신뢰 | 다른 사람의 스킴은 처음 실행하기 전에 반드시 눌러야 함(아래 "신뢰와 보안" 참고) |
| 최근 전적 | 이 스킴의 게임별 승패, 경기 시간, 종료 사유 |
| 삭제 | "내 스킴"과 "설치됨"만 삭제 가능. 인스턴스가 사용 중이면 삭제 불가 |

인스턴스 카드에도 "AI 스킴" 줄이 추가되었습니다. 드롭다운에서 스킴을 고르고 → "전환(즉시 적용)". 인스턴스가 실행 중이 아닐 때는 버튼 이름이 "선택"이며, 다음에 "테스트 시작"을 누르면 그 스킴으로 AI를 시작합니다.

## 매니페스트 scheme.json

```json
{
  "format": 1,
  "id": "fast-rush",
  "name": "3분 러시",
  "version": "1.2.0",
  "author": "홍길동",
  "description": "이 AI가 어떤 전략을 쓰는지 한 문장으로 설명",
  "entry": "rush_bot.py",
  "class": "RushBot",
  "fair": true,
  "hz": 5,
  "races": ["human", "orc"],
  "license": "MIT"
}
```

| 필드 | 필수 | 설명 |
|---|---|---|
| `id` | ✔ | 영문 소문자, 숫자, `-`, `_`. 2 ~ 41자 |
| `entry` | ✔ | 스킴 폴더 안의 `.py` 파일 하나(절대 경로 불가, `..` 불가) |
| `kind` | | 기본값 `bot`(`openwar3.Bot`의 하위 클래스, 여러분 대신 싸움). `mod` = [게임플레이 모드](https://war3ai.com/ko/docs/mods/)(`openwar3.Mod`의 하위 클래스. 항상 공정 모드를 쓰지 않으며, 대전 규칙으로 승패를 판정하지 않음) |
| `class` | | 진입 파일 안의 Bot(또는 Mod) 하위 클래스 이름. 생략하면 진입 파일의 마지막 `openwar3.Bot` 하위 클래스를 사용 |
| `fair` | | 기본값 `true`: 시야 안의 것만 보며, 아레나와 같은 규칙. `false` = 맵 전체가 보이며, 이때만 [JASS 채널](https://war3ai.com/ko/docs/jass/)을 쓸 수 있음(동료에게 필요) |
| `judge` | | 기본값 `true`: 대전 규칙으로 승패 판정. RPG / 동료 스킴은 `false` |
| `hz` | | `on_tick`을 초당 몇 번 호출할지. 기본값 5 |
| `format` | | 매니페스트 형식 버전. 현재 1. 이 컴퓨터의 OpenWar3보다 새 버전이면 거부하고 업데이트를 안내 |
| 기타 | | `name`, `version`, `author`, `description`, `races`, `license`, `homepage`, `forked_from`은 표시용 |

스킴 폴더는 Python 모듈 검색 경로에 추가되므로, 진입 파일에서 같은 폴더의 다른 파일을 `import`할 수 있습니다. 서드파티 패키지(numpy, torch……)는 자동으로 설치되지 않습니다 — 무엇이 필요한지 `description`에 분명히 적으세요.

**가장 작은 스킴은 파일 두 개면 충분합니다**:

```python
# my_bot.py
from openwar3 import Bot

class MyBot(Bot):
    def on_tick(self, g):
        for w in g.idle_workers():
            mine = g.nearest(g.gold_mines(), w)
            if mine:
                g.gather(w, mine)
```

```json
{"id": "my-first", "name": "나의 첫 AI", "entry": "my_bot.py"}
```

`schemes/mine/my-first/`에 넣고 Farsight를 새로 고치면 바로 보입니다. 더 간편한 출발점: "내장"에서 예제를 하나 골라 "내 스킴으로 복사"를 누르세요.

## 실행 방식과 전적

스킴은 **스킴 러너**가 실행합니다(Farsight의 "테스트 시작 / 전환"이 띄우는 것이 바로 이것입니다).

```bash
python tools/run_scheme.py --inst 20 --scheme builtin/micro --hours 6
```

- 인스턴스마다 상주하는 감독 프로세스가 하나 있고, **게임마다 자식 프로세스를 하나 띄워** 스킴을 실행합니다. 스킴 코드가 죽어도 감독 프로세스는 영향을 받지 않으며, "내 스킴"의 코드를 고치면 다음 게임부터 자동으로 새 코드를 씁니다.
- 게임이 끝날 때마다 전적을 한 줄 기록합니다: 스킴, 버전, 작성자, 승패, 사유, 게임 시간, 오류 횟수. Farsight의 승률은 여기서 집계합니다.

승패 판정 방법:

| 상황 | 기록 |
|---|---|
| 상대 건물이 전부 없어짐 | 승 |
| 아군 건물이 전부 없어짐(병력이 살아 있어도 마찬가지 — 대전에서는 이렇게 패배를 판정함) | 패 |
| 아군 유닛이 전부 없어짐 | 패 |
| Farsight에서 수동으로 종료 / 중지 | 미정 |
| 전환 시점에 이 게임이 이미 60 게임 초 이상 진행됨(중간에 넘겨받음) | 따로 집계하며 **승률에 넣지 않음** |
| 게임 시계가 오랫동안 멈춤 | 미정 |

승패가 가려지면 러너가 결과 화면을 닫고 "다음 게임 설정"에 따라 다음 게임을 시작하며, 스킴이 이어서 넘겨받습니다 — 밤새 켜 두고 전적을 쌓을 수 있습니다. 일시정지는 종료가 아닙니다. 일시정지 중에도 Bot은 평소처럼 돌아가고, 게임 시계만 멈춥니다.

## 신뢰와 보안

**스킴은 코드이며, 실행되면 여러분 본인과 같은 권한을 가집니다**(파일을 읽고 쓰고, 네트워크에 접속할 수 있음). 그래서:

- `installed/`의 스킴은 기본적으로 **신뢰되지 않으며**, 여러분이 "신뢰"를 누를 때까지 Farsight와 러너 모두 실행을 거부합니다.
- 같은 id의 스킴을 교체 설치하면 **신뢰가 초기화됩니다**(새 버전 = 새 코드).
- 가져올 때 검사합니다: zip은 50 MB 이하, 파일 2000개 이하. 절대 경로와 `..`는 허용하지 않습니다(스킴 폴더 밖에 쓰는 것을 방지). 매니페스트가 올바르지 않거나 진입 파일이 없으면 바로 거부합니다.

> **주의**
>
> 신뢰하기 전에 "폴더 열기"로 코드를 한 번 읽어 보세요. 믿을 수 있는 사람에게서만 스킴을 받으세요.

## API(스크립트용)

| API | 설명 |
|---|---|
| `GET /api/schemes` | 스킴 목록 + 전적 + 인스턴스별로 선택된 스킴과 실행 중인 스킴 |
| `GET /api/schemes/results?ref=` | 한 스킴의 최근 30게임 |
| `POST /api/schemes/import` | zip 가져오기 |
| `GET /api/schemes/export?ref=` | zip 다운로드 |
| `POST /api/schemes/fork` | 내 스킴으로 복사 |
| `POST /api/schemes/trust` | 신뢰 |
| `DELETE /api/schemes?ref=` | 삭제(인스턴스가 사용 중이면 거부) |
| `POST /api/instances/{n}/scheme` | 인스턴스의 스킴 변경: 이 게임을 즉시 넘겨받거나, 다음 테스트 시작부터 적용 |

Python에서는 라이브러리를 바로 쓰면 됩니다: `from openwar3 import schemes`(`list_schemes`, `install_zip`, `export_zip`, `fork`, `trust`, `stats`……).

## 앞으로: 스킴 웹사이트

내보낸 zip이 곧 공유 단위이므로, 웹사이트는 그 바깥에 한 겹만 더하면 됩니다. Farsight에서 클릭 한 번으로 업로드하고, 웹사이트에서 받은 스킴은 "스킴 가져오기"와 완전히 같은 검사를 거치며 마찬가지로 신뢰를 확인해야 합니다. 전적 보고를 선택할 수 있고, 웹사이트는 버전별로 승률을 집계합니다. Farsight의 "스킴 웹사이트에 공유" 버튼은 이미 자리를 마련해 두었습니다. 진행 상황은 [로드맵](https://war3ai.com/ko/roadmap/)을 보세요.
