The Farmer Was Replaced— 코딩 게임 완전 해부
Chapter 08 · 심화

제약 속에서 살아남는 법 — 파이썬 유령과의 화해

global 안 됨, tuple 안 됨, += 조심, lambda 조심, for 카운터 조심. 이 모든 제약을 피하는 패턴을 정리한다. 이 장은 f0.py 521줄의 모든 교훈이 응축된 생존 매뉴얼이다.

Survival Patterns Bug Catalog Idioms

§ 1Mutable Wrapper: global 대체

global 키워드가 안 되므로, 리스트로 값을 감싼다.

패턴: Mutable Wrapper
# global 없이 상태 공유
_SV = [None]           # visited 맵
_MZ_H = [[]]           # 드론 핸들
_phase = [""]           # 현재 단계
_spawn_i = [0]          # 스폰 인덱스
_FE = [None]            # 현재 엔티티

# 사용
_phase[0] = "plant"
_SV[0][x][y] = True
me = _spawn_i[0]
_spawn_i[0] = _spawn_i[0] + 1
직관 리스트는 mutable이다. _phase = [""]는 리스트 객체를 만들고, _phase[0] = "plant"는 리스트의 내용을 수정한다. 변수 자체를 재할당하는 게 아니라, 리스트 내용을 바꾸는 것이므로 global이 필요 없다.

§ 2인라인 수학: tuple 회피

튜플 언패킹이 안 되므로, 모든 좌표 계산을 인라인으로 한다:

인라인 방향 계산
nx = x
ny = y
if di == 0:
    ny = (y + 1) % WORLD_SIZE
elif di == 1:
    nx = (x + 1) % WORLD_SIZE
elif di == 2:
    ny = (y - 1) % WORLD_SIZE
else:
    nx = (x - 1) % WORLD_SIZE
주의 nx, ny = nb_pos(x, y, d) 같은 헬퍼 함수를 쓰고 싶겠지만, 튜플 반환→튜플 언패킹이 모두 금지되어 있다. 인라인 if/elif 체인이 유일한 방법이다.

§ 3방향 상수 규칙

can_move(0)은 크래시. 반드시 can_move(North)를 사용해야 한다.

하지만 정수 연산으로 방향을 관리해야 할 때가 있다. 해결: DIRS = [North, East, South, West] 배열을 만들고 DIRS[di]로 변환한다.

DIRS 배열 패턴
DIRS = [North, East, South, West]

# 정수로 계산
back = (di + 2) % 4      # 반대 방향

# 상수로 사용
can_move(DIRS[di])
move(DIRS[back])

§ 4for 루프 함정

for i in range(N): ...; i += 2는 i를 건너뛰지 않는다! Python의 for는 range 이터레이터에서 매 반복마다 i를 덮어쓴다.

해결: range(start, end, step)을 사용하거나, i * step 공식을 사용한다.

for 루프 함정 vs 올바른 패턴 STEP 01 / 4
space 재생 · ← → 단계 · R 리셋

§ 5버그 카탈로그

실제로 겪은 버그들. 모두 "조용히 실패"하는 것들이다.

버그원인해결
드론이 (0,0)에서 멈춤tuple 언패킹인라인 수학
함수가 실행 안 됨global 키워드[value] 래퍼
모든 드론이 같은 열공유 카운터 레이스클로저 팩토리
can_move() 크래시정수 방향DIRS[idx] 상수
for 루프 스킵 안 됨i += 무시됨range(step) 사용
리스트 인덱스 에러[None]*N 실패명시적 for+append
harvest 타이밍while 안에 harvestwhile 밖으로 이동
핵심 이 게임에서 디버깅은 print() 하나다. 에러 메시지가 없으므로, 의심되는 지점에 print()를 넣고 출력이 나오는지 확인하는 것이 유일한 방법이다. "조용한 실패"가 가장 위험한 적이다.