Pokémon으로 설명하는 Prolog 기초

1 hour ago 2
  • Pokémon 전투 규칙은 타입 상성, 기술, 능력치, 특성이 얽힌 규칙 엔진에 가까워 Prolog의 관계·규칙 모델로 간결하게 표현할 수 있음
  • Prolog는 pokemon/1, type/2 같은 술어로 사실을 두고, 대문자 변수와 통합으로 타입·기술 조건에 맞는 Pokémon을 찾아냄
  • Freeze-Dry를 배우고 Ice 타입이며 Special Attack이 120보다 큰 Pokémon 찾기는 SQL의 여러 EXISTS보다 Prolog 질의가 짧음
  • 드래프트 팀은 alex/1, morry/1 같은 술어로 표현하고, 우선도 기술 규칙에는 제외 조건과 Prankster 효과를 층층이 더할 수 있음
  • Techno's Prep Doc 같은 스프레드시트는 강력하지만, Prolog 데이터베이스는 임의 조합 질의에 더 유연하며 prologdex와 Scryer Prolog로 구현됨

Pokémon 전투 규칙이 논리 프로그래밍에 맞는 이유

  • Pokémon 전투는 여러 규칙이 복잡하게 맞물리는 규칙 엔진에 가깝고, Prolog 같은 논리 프로그래밍은 이런 관계를 간결하게 표현하기 좋음
  • Pokémon은 종 이름을 가진 캐릭터이며, Bulbasaur #1)부터 Pecharunt #1025)까지 1,000종이 넘음
  • 메인 시리즈 전투는 6마리로 구성된 팀끼리 싸우며, 각 Pokémon은 보통 상대에게 피해를 주는 4개의 기술 중 하나를 선택하고 상대 팀의 HP를 모두 0으로 만들면 승리함
  • 전투 성능은 기본 능력치, 배울 수 있는 기술 목록, 특성, 타입에 따라 달라지며, 조합 수가 많아 소프트웨어로 추적할 가치가 커짐
  • 타입은 기술과 Pokémon 모두에 붙으며, 어떤 기술 타입이 상대 타입에 강하면 2배 피해, 약하면 1/2 피해를 줌
  • 타입 보정은 누적됨
    • Scizor)는 Bug/Steel 타입이고 둘 다 Fire에 약해 Fire 기술에 4배 피해를 받음
    • Water/Ground 타입 Swampert)에게 Electric 기술을 쓰면 Ground의 면역 때문에 피해가 0이 됨

Prolog 기본 모델

  • Prolog에서는 술어(predicate) 로 관계를 선언함
pokemon(bulbasaur). pokemon(ivysaur). pokemon(venusaur). pokemon(charmander). pokemon(charmeleon). pokemon(charizard). pokemon(squirtle). pokemon(wartortle). pokemon(blastoise).
  • pokemon/1은 이름이 pokemon이고 인수가 하나인 술어이며, pokemon(squirtle). 같은 질의는 해당 문장을 참으로 만들 수 있는지 확인함
?- pokemon(squirtle). true. ?- pokemon(alex). false.
  • Pokémon 타입은 type/2처럼 두 인수의 관계로 표현할 수 있고, 두 타입을 가진 Pokémon은 같은 Pokémon에 대해 type 사실을 두 개 둠
type(bulbasaur, grass). type(bulbasaur, poison). type(charmander, fire). type(charizard, fire). type(charizard, flying). type(squirtle, water).
  • 대문자로 시작하는 이름은 변수이며, Prolog는 변수가 들어간 질의를 가능한 모든 값과 통합(unify)하려고 시도함
?- type(squirtle, Type). Type = water. ?- type(venusaur, Type). Type = grass ; Type = poison.
  • type(Pokemon, grass).처럼 첫 번째 인수를 변수로 두면 Grass 타입 Pokémon 전체를 찾을 수 있고, 실제 데이터에서는 164개 결과가 나옴
  • 쉼표는 여러 술어를 모두 만족해야 한다는 뜻이며, 같은 변수 이름은 질의 안에서 같은 값을 가져야 함
?- type(Pokemon, water), type(Pokemon, ice). Pokemon = dewgong ; Pokemon = cloyster ; Pokemon = lapras ; Pokemon = laprasgmax ; Pokemon = spheal ; Pokemon = sealeo ; Pokemon = walrein ; Pokemon = arctovish ; Pokemon = ironbundle ; false.
  • Iron Bundle)처럼 능력치와 배울 수 있는 기술도 관계로 질의할 수 있음
?- pokemon_spa(ironbundle, SpA). SpA = 124. ?- learns(ironbundle, Move), move_category(Move, special). Move = aircutter ; Move = blizzard ; Move = chillingwater ; Move = freezedry ; Move = hydropump ; Move = hyperbeam ; Move = icebeam ; Move = icywind ; Move = powdersnow ; Move = swift ; Move = terablast ; Move = waterpulse ; Move = whirlpool.
  • SpA #> 120 같은 제약을 섞으면, Special Attack이 120보다 크고 Freeze-Dry를 배우며 Ice 타입인 Pokémon을 바로 찾을 수 있음
?- pokemon_spa(Pokemon, SpA), SpA #> 120, learns(Pokemon, freezedry), type(Pokemon, ice). Pokemon = glaceon, SpA = 130 ; Pokemon = kyurem, SpA = 130 ; Pokemon = kyuremwhite, SpA = 170 ; Pokemon = ironbundle, SpA = 124 ; false.
  • Prolog의 규칙(rule) 은 머리와 본문으로 구성되며, 본문이 참이면 머리도 통합됨
damaging_move(Move) :- move_category(Move, physical) ; move_category(Move, special).
  • 이 규칙은 Physical 또는 Special 기술을 직접 피해 기술로 분류함
?- damaging_move(tackle). true. ?- damaging_move(rest). false.

SQL과 비교되는 질의 표현

  • 지금까지의 예시는 논리적으로는 단순한 and와 or 조합이지만, Prolog에서는 관계 질의가 SQL보다 짧고 수정하기 쉬운 형태가 됨
  • 같은 데이터를 SQL로 구성하면 Pokémon, 타입, 기술을 별도 테이블로 둘 수 있음
CREATE TABLE pokemon (pokemon_name TEXT, special_attack INTEGER); CREATE TABLE pokemon_types(pokemon_name TEXT, type TEXT); CREATE TABLE pokemon_moves(pokemon_name TEXT, move TEXT, category TEXT);
  • Freeze-Dry를 배우고 Ice 타입이며 Special Attack이 120보다 큰 Pokémon을 SQL로 찾으려면 EXISTS를 여러 번 써야 함
SELECT DISTINCT pokmeon, special_attack FROM pokemon as p WHERE p.special_attack > 120 AND EXISTS ( SELECT 1 FROM pokemon_moves as pm WHERE p.pokemon_name = pm.pokemon_name AND move = 'freezedry' ) AND EXISTS ( SELECT 1 FROM pokemon_types as pt WHERE p.pokemon_name = pt.pokemon_name AND type = 'ice' );
  • 동일한 Prolog 질의는 필요한 관계를 그대로 나열함
?- pokemon_spa(Pokemon, SpA), SpA #> 120, learns(Pokemon, freezedry), type(Pokemon, ice).
  • 조건이 계속 추가되면 SQL 질의는 복잡해지기 쉽지만, Prolog 질의는 변수 동작에 익숙해지면 읽고 고치기 쉬운 형태를 유지함

전투 규칙을 층층이 쌓는 방식

  • Pokémon 전투에는 명중 실패, 능력치 상승·하락, 아이템 효과, 피해량 범위, 상태 이상, 날씨·지형·Trick Room 같은 필드 효과, 특성, 사전 능력치 배분 등 많은 상호작용 규칙이 있음
  • Pokémon용 소프트웨어를 만들 때는 이 복잡성을 다루면서 모델을 감당 가능한 형태로 유지해야 함
  • Prolog는 즉석 조합을 묘사하는 질의 모델과 일관된 규칙 레이어링에 강점이 있음
  • damage calculator로 이런 복잡성을 직접 확인할 수 있음

드래프트 리그와 우선도 기술 질의

  • Pokémon 드래프트에서는 Pokémon마다 가치가 정해지고, 플레이어가 정해진 포인트 안에서 Pokémon을 뽑아 8~11마리 정도의 팀을 구성함
  • 실제 전투는 6v6이므로, 상대가 가져올 수 있는 여섯 마리 조합을 대비하고 그에 맞설 여섯 마리를 고르는 준비가 중요함
  • 자신이 뽑은 Pokémon은 alex/1 같은 술어로 바로 표현할 수 있음
alex(meowscarada). alex(weezinggalar). alex(swampertmega). alex(latios). alex(volcarona). alex(tornadus). alex(politoed). alex(archaludon). alex(beartic). alex(dusclops).
  • 이 팀에서 Freeze-Dry를 배우는 Pokémon을 찾는 질의는 간단하지만, 결과는 없음
?- alex(Pokemon), learns(Pokemon, freezedry). false.
  • 전투 순서는 기본적으로 Speed가 결정하지만, 기술에는 우선도(priority) 가 있고 더 높은 우선도의 기술이 먼저 나감
  • 대부분의 기술 우선도는 0이지만, Accelerock처럼 우선도 1인 기술은 더 빠른 Pokémon의 우선도 0 기술보다 먼저 나감
  • 특정 Pokémon이 배우는 우선도 양수 기술은 learns/2, move_priority/2, 우선도 조건을 결합해 찾을 수 있음
  • 단순 질의는 Helping Hand, Ally Switch처럼 Double Battles에서 의미가 큰 기술이나 Bide처럼 실전 의미가 낮은 기술까지 포함함
  • \+/1은 목표가 실패할 때 참이고, dif/2는 두 항이 다르다는 뜻이므로, Double Battles용 기술과 Bide를 제외하는 규칙을 추가할 수 있음
learns_priority(Mon, Move, Priority) :- learns(Mon, Move), \+ doubles_move(Move), dif(Move, bide), move_priority(Move, Priority), Priority #> 0.
  • Protect, Detect, Endure, Magic Coat 같은 보호성 기술도 제외하면, 실제로 상대에게 피해나 부정적 효과를 줄 수 있는 우선도 기술만 남음
?- alex(Pokemon), learns_priority(Pokemon, Move, Priority). Pokemon = meowscarada, Move = quickattack, Priority = 1 ; Pokemon = meowscarada, Move = suckerpunch, Priority = 1 ; Pokemon = beartic, Move = aquajet, Priority = 1 ; Pokemon = dusclops, Move = shadowsneak, Priority = 1 ; Pokemon = dusclops, Move = snatch, Priority = 4 ; Pokemon = dusclops, Move = suckerpunch, Priority = 1 ; false.
  • 같은 규칙을 상대 팀 술어에 적용하면, 상대가 가진 우선도 기술도 바로 찾을 수 있음
?- morry(Pokemon), learns_priority(Pokemon, Move, Priority). Pokemon = mawilemega, Move = snatch, Priority = 4 ; Pokemon = mawilemega, Move = suckerpunch, Priority = 1 ; Pokemon = walkingwake, Move = aquajet, Priority = 1 ; Pokemon = ursaluna, Move = babydolleyes, Priority = 1 ; Pokemon = lokix, Move = feint, Priority = 2 ; Pokemon = lokix, Move = firstimpression, Priority = 2 ; Pokemon = lokix, Move = suckerpunch, Priority = 1 ; Pokemon = alakazam, Move = snatch, Priority = 4 ; Pokemon = skarmory, Move = feint, Priority = 2 ; Pokemon = froslass, Move = iceshard, Priority = 1 ; Pokemon = froslass, Move = snatch, Priority = 4 ; Pokemon = froslass, Move = suckerpunch, Priority = 1 ; Pokemon = dipplin, Move = suckerpunch, Priority = 1.

Prankster 특성 확장

  • Prankster 특성을 가진 Pokémon은 상태 기술의 우선도가 추가로 +1 되며, 이 효과도 기존 learns_priority/3 규칙에 더할 수 있음
  • 팀 안에서는 Tornadus가 Prankster 특성을 가짐
?- alex(Pokemon), pokemon_ability(Pokemon, prankster). Pokemon = tornadus ; false.
  • Prolog의 ->/2 if/then 구문을 사용해, Pokémon이 Prankster이고 기술 분류가 status이면 기본 우선도에 1을 더하고 아니면 기본 우선도를 그대로 쓰게 만들 수 있음
learns_priority(Mon, Move, Priority) :- learns(Mon, Move), \+ doubles_move(Move), \+ protection_move(Move), Move \= bide, move_priority(Move, BasePriority), ( pokemon_ability(Mon, prankster), move_category(Move, status) -> Priority #= BasePriority + 1 ; Priority #= BasePriority ), Priority #> 0.
  • 이 규칙 이후 같은 질의는 Tornadus의 Agility, Defog, Nasty Plot, Rain Dance, Tailwind, Taunt, Toxic 같은 상태 기술을 우선도 1로 포함함
  • 규칙 하나를 확장해 특성 효과까지 반영할 수 있어 Prolog의 레이어링 장점이 드러남

스프레드시트 기반 도구와의 대비

  • Pokémon 커뮤니티에는 상대 팀의 우선도 기술 같은 정보를 찾는 리소스가 이미 있으며, 대표적으로 “Techno’s Prep Doc” 같은 고급 Google Sheets가 쓰임
  • 이 스프레드시트는 팀을 넣으면 많은 매치업 정보를 생성하고, 다양한 포맷 지원, 훑어보기 쉬운 시각 자료, 자동완성을 제공함
  • 우선도 기술을 찾는 수식은 FILTER, VLOOKUP, INDIRECT를 조합하며, INDIRECT는 셀 참조를 반환
={IFERROR(ARRAYFORMULA(VLOOKUP(FILTER(INDIRECT(Matchup!$S$3&"!$AV$4:$AV"),INDIRECT(Matchup!$S$3&"!$AT$4:$AT")="X"),{Backend!$L$2:$L,Backend!$F$2:$F},2,FALSE))),IFERROR(FILTER(INDIRECT(Matchup!$S$3&"!$AW$4:$AW"),INDIRECT(Matchup!$S$3&"!$AT$4:$AT")="X"))}
  • Backend 시트에는 모든 기술이 나열되어 있고, 이 구조는 Prolog 질의를 하드코딩한 버전에 가까움
  • Prolog 데이터베이스는 주목할 만한 기술 목록을 하드코딩하는 방식보다 확장성이 높고, 어떤 기술이든 조회할 수 있음
  • Tornadus가 배우는 Special 기술 중 Justin의 팀 멤버에게 효과가 굉장한 기술을 찾는 것처럼, 기존 도구에 없는 조합형 질문도 짧게 표현할 수 있음
?- justin(Target), learns(tornadus, Move), super_effective_move(Move, Target), move_category(Move, special). Target = charizardmegay, Move = chillingwater ; Target = terapagosterastal, Move = focusblast ; Target = alomomola, Move = grassknot ; Target = scizor, Move = heatwave ; Target = scizor, Move = incinerate ; Target = runerigus, Move = chillingwater ; Target = runerigus, Move = darkpulse ; Target = runerigus, Move = grassknot ; Target = runerigus, Move = icywind ; Target = screamtail, Move = sludgebomb ; Target = screamtail, Move = sludgewave ; Target = trapinch, Move = chillingwater ; Target = trapinch, Move = grassknot ; Target = trapinch, Move = icywind ; false.

구현 메모와 한계

Read Entire Article