- Pokémon बैटल के नियम type matchups, moves, stats और abilities से जुड़े हुए एक rule engine के ज़्यादा करीब हैं, इसलिए इन्हें Prolog के relation·rule model में संक्षेप में व्यक्त किया जा सकता है
- Prolog
pokemon/1, type/2 जैसे predicate के रूप में facts रखता है, और बड़े अक्षरों वाले variables व unification के जरिए type·move conditions से मेल खाने वाले Pokémon ढूंढ लेता है
- Freeze-Dry सीखने वाले, Ice type होने वाले, और जिनका Special Attack 120 से बड़ा हो, ऐसे Pokémon को ढूंढने के लिए Prolog query, SQL के कई
EXISTS से छोटी होती है
- draft team को
alex/1, morry/1 जैसे predicate से व्यक्त किया जा सकता है, और priority move नियमों में exclusion conditions और Prankster effect को परत-दर-परत जोड़ा जा सकता है
- Techno's Prep Doc जैसी spreadsheets शक्तिशाली हैं, लेकिन Prolog database मनचाहे combinations वाली queries के लिए ज़्यादा लचीला है, और इसे prologdex व Scryer Prolog से लागू किया गया है
Pokémon बैटल के नियम logic programming के लिए उपयुक्त क्यों हैं
- Pokémon बैटल कई नियमों के जटिल मेल से बना एक rule engine जैसा है, और Prolog जैसी logic programming ऐसी relationships को संक्षेप में व्यक्त करने के लिए उपयुक्त है
- Pokémon प्रजाति-नाम वाले characters हैं, और Bulbasaur #1) से Pecharunt #1025) तक 1,000 से अधिक species हैं
- main series battles में 6 Pokémon वाली teams आपस में लड़ती हैं, और हर Pokémon आमतौर पर 4 moves में से एक चुनता है जो प्रतिद्वंद्वी को damage देती है; सामने वाली team के सभी Pokémon का HP 0 कर देने पर जीत मिलती है
- बैटल performance base stats, सीखी जा सकने वाली moves की सूची, abilities, और type पर निर्भर करती है, और combinations बहुत अधिक होने के कारण इन्हें software से track करना उपयोगी है
- type, moves और Pokémon दोनों पर लागू होता है, और अगर किसी move का type सामने वाले type पर मजबूत है तो 2x damage, और कमजोर है तो 1/2 damage देता है
- type modifiers एक-दूसरे पर stack होते हैं
- Scizor) Bug/Steel type है, और दोनों ही Fire के प्रति कमजोर हैं, इसलिए वह Fire moves से 4x damage लेता है
- Water/Ground type Swampert) पर Electric move इस्तेमाल करने पर Ground immunity की वजह से damage 0 हो जाता है
Prolog का बुनियादी मॉडल
- Prolog में relationships को predicate के रूप में घोषित किया जाता है
pokemon(bulbasaur).
pokemon(ivysaur).
pokemon(venusaur).
pokemon(charmander).
pokemon(charmeleon).
pokemon(charizard).
pokemon(squirtle).
pokemon(wartortle).
pokemon(blastoise).
pokemon/1 एक predicate है जिसका नाम pokemon है और जिसमें एक argument है; pokemon(squirtle). जैसी query यह जांचती है कि क्या उस statement को true बनाया जा सकता है
?- pokemon(squirtle).
true.
?- pokemon(alex).
false.
- Pokémon का type
type/2 जैसी two-argument relationship से व्यक्त किया जा सकता है, और जिन Pokémon के दो type होते हैं, उनके लिए उसी Pokémon पर type के दो facts रखे जाते हैं
type(bulbasaur, grass).
type(bulbasaur, poison).
type(charmander, fire).
type(charizard, fire).
type(charizard, flying).
type(squirtle, water).
- बड़े अक्षर से शुरू होने वाले नाम variables होते हैं, और Prolog variable वाली query को सभी संभव values के साथ unify करने की कोशिश करता है
?- type(squirtle, Type).
Type = water.
?- type(venusaur, Type).
Type = grass
; Type = poison.
type(Pokemon, grass). की तरह अगर पहला argument variable हो, तो पूरे Grass type Pokémon खोजे जा सकते हैं; वास्तविक डेटा में इसके 164 results आते हैं
- comma का मतलब है कि कई predicates सभी satisfy होने चाहिए, और एक ही variable name को query के भीतर एक ही value रखनी होती है
?- 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) की तरह stats और सीखी जा सकने वाली moves को भी relationships के रूप में query किया जा सकता है
?- 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 जैसी constraints मिलाने पर, 120 से अधिक Special Attack वाले, Freeze-Dry सीखने वाले, और Ice type 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 सिर और body से मिलकर बनता है; body true हो तो सिर भी unify हो जाता है
damaging_move(Move) :-
move_category(Move, physical)
; move_category(Move, special).
- यह rule Physical या Special moves को सीधे damaging moves के रूप में वर्गीकृत करता है
?- damaging_move(tackle).
true.
?- damaging_move(rest).
false.
SQL से तुलना की जाने वाली query अभिव्यक्ति
- अब तक के उदाहरण तर्क की दृष्टि से साधारण
and और or के संयोजन हैं, लेकिन Prolog में relation query, SQL की तुलना में अधिक छोटी और बदलने में आसान हो जाती है
- उसी data को SQL में बनाएं तो Pokémon, type और move को अलग-अलग table में रखा जा सकता है
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 type का, और 120 से अधिक Special Attack वाला 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 query आवश्यक relations को सीधे सूचीबद्ध करती है
?- pokemon_spa(Pokemon, SpA),
SpA #> 120,
learns(Pokemon, freezedry),
type(Pokemon, ice).
- शर्तें लगातार बढ़ती जाएं तो SQL query आसानी से जटिल हो सकती है, लेकिन Prolog query, variable के काम करने के तरीके की आदत पड़ जाने पर, पढ़ने और बदलने में आसान रूप बनाए रखती है
battle rules को परत-दर-परत जोड़ने का तरीका
- Pokémon battle में hit miss, stat बढ़ना-घटना, item effects, damage range, status conditions, weather·terrain·Trick Room जैसे field effects, abilities, और पहले से की गई stat allocation जैसे कई interaction rules होते हैं
- Pokémon के लिए software बनाते समय इस जटिलता को संभालते हुए model को manageable रूप में रखना पड़ता है
- Prolog की ताकत ad-hoc combinations को बयान करने वाले query model और consistent rule layering में है
- damage calculator से इस जटिलता को सीधे देखा जा सकता है
draft league और priority move query
- Pokémon draft में हर Pokémon की एक value तय होती है, और खिलाड़ी तय points के भीतर Pokémon चुनकर लगभग 8~11 Pokémon की team बनाता है
- असली battle 6v6 होती है, इसलिए प्रतिद्वंद्वी कौन-सी छह Pokémon ला सकता है यह सोचकर, उसके खिलाफ अपनी छह Pokémon चुनने की तैयारी अहम होती है
- खुद द्वारा draft किए गए Pokémon को
alex/1 जैसे predicate से सीधे व्यक्त किया जा सकता है
alex(meowscarada).
alex(weezinggalar).
alex(swampertmega).
alex(latios).
alex(volcarona).
alex(tornadus).
alex(politoed).
alex(archaludon).
alex(beartic).
alex(dusclops).
- इस team में Freeze-Dry सीखने वाले Pokémon को ढूंढने वाली query सरल है, लेकिन result नहीं आता
?- alex(Pokemon), learns(Pokemon, freezedry).
false.
- battle का क्रम मूल रूप से Speed तय करती है, लेकिन moves में priority होती है, और अधिक priority वाला move पहले चलता है
- ज़्यादातर moves की priority 0 होती है, लेकिन Accelerock जैसे priority 1 वाले moves, अधिक तेज़ Pokémon के priority 0 move से भी पहले चलते हैं
- किसी खास Pokémon के सीखे हुए positive priority moves को
learns/2, move_priority/2, और priority condition को जोड़कर खोजा जा सकता है
- साधारण query में Helping Hand, Ally Switch जैसे Double Battles में ज़्यादा मायने रखने वाले moves, या Bide जैसे व्यावहारिक रूप से कम मायने रखने वाले move भी शामिल हो जाते हैं
\+/1 तब true होता है जब goal fail हो, और dif/2 का मतलब है कि दो terms अलग हैं, इसलिए Double Battles के लिए बने moves और Bide को बाहर करने वाला rule जोड़ा जा सकता है
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 जैसे defensive moves को भी बाहर कर दिया जाए, तो आखिर में सिर्फ वही priority moves बचते हैं जो वास्तव में प्रतिद्वंद्वी को damage या negative effect दे सकते हैं
?- 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.
- वही rule अगर प्रतिद्वंद्वी team के predicate पर लागू करें, तो प्रतिद्वंद्वी के priority moves भी तुरंत पता चल जाते हैं
?- 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 की status moves की priority में अतिरिक्त +1 जुड़ता है, और इस प्रभाव को मौजूदा
learns_priority/3 नियम में भी जोड़ा जा सकता है
- टीम में Tornadus के पास Prankster एबिलिटी है
?- alex(Pokemon), pokemon_ability(Pokemon, prankster).
Pokemon = tornadus
; false.
- Prolog के
->/2 if/then सिंटैक्स का उपयोग करके, अगर Pokémon के पास Prankster हो और move category status हो, तो base priority में 1 जोड़ें, नहीं तो base priority को वैसे ही रखें
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.
- इस नियम के बाद वही query Tornadus की Agility, Defog, Nasty Plot, Rain Dance, Tailwind, Taunt, Toxic जैसी status moves को priority 1 के साथ शामिल करती है
- सिर्फ एक नियम बढ़ाकर एबिलिटी का असर भी दर्शाया जा सकता है, जिससे Prolog की layering का फ़ायदा साफ़ दिखता है
स्प्रेडशीट-आधारित टूल्स से तुलना
- Pokémon कम्युनिटी में opponent team की priority moves जैसी जानकारी खोजने के लिए पहले से resources मौजूद हैं, जिनमें “Techno’s Prep Doc” जैसी उन्नत Google Sheets प्रमुख हैं
- यह स्प्रेडशीट टीम डालने पर बहुत-सी matchup जानकारी बनाती है, और अलग-अलग formats का समर्थन, जल्दी स्कैन किए जा सकने वाले visual aids, और autocomplete देती है
- priority moves खोजने वाला formula
FILTER, VLOOKUP, INDIRECT का संयोजन करता है, और INDIRECT cell references लौटाता है
={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 शीट में सभी moves सूचीबद्ध हैं, और यह संरचना Prolog query के hardcoded संस्करण के काफ़ी करीब है
- Prolog database, ध्यान देने लायक moves की सूची hardcode करने की तुलना में ज़्यादा scalable है, और किसी भी move को query किया जा सकता है
- Justin की टीम के सदस्यों पर super effective होने वाली Tornadus की special moves खोजने जैसे compositional सवाल, जो मौजूदा टूल्स में नहीं हैं, उन्हें भी संक्षेप में व्यक्त किया जा सकता है
?- 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.
इम्प्लीमेंटेशन नोट्स और सीमाएँ
1 टिप्पणियां
Lobste.rs की राय
सोच रहा हूँ कि क्या कोई वास्तव में Prolog को प्रोडक्टिव तरीके से इस्तेमाल करता है। काम के लिए हो या निजी उपयोग के लिए, अब तक मैंने बस ऐसे खिलौना उदाहरण ही देखे हैं
सख़्ती से कहें तो मेरे पास एक से ज़्यादा चल रहे Prolog codebase भी हैं; एक internal analytics dashboard है, और पहले एक iOS app का backend server भी Prolog में लिखा था। उसी दौरान external service के बिना APNS notifications भेजने के लिए मैंने Prolog के लिए एक HTTP/2 client library भी बना ली
Prolog community में ऐसे लोग ज़रूर हैं जिन्हें इसे web server जैसी चीज़ों में इस्तेमाल करना पसंद है, लेकिन मेरे लिए यह ज़्यादा एक अलग skill tree खोलने जैसा है। उदाहरण के लिए, अब मैं बेहतर समझ पाता हूँ कि किस समस्या पर custom parser या DSL अच्छा बैठेगा
इस पोस्ट को लिखने के बाद मिली समझ के आधार पर मैंने IRS Fact Graph के tax logic engine के उपयोगी subset को फिर से implement किया। Prolog इस काम के लिए चौंकाने वाली तरह से उपयुक्त है, क्योंकि यह उन undocumented कोनों को सामने ले आता है जिन्हें imperative implementation में आसानी से नज़रअंदाज़ किया जा सकता है, और उन्हें हल करने के लिए मजबूर करता है
“execution” वाला हिस्सा अभी पूरा नहीं हुआ है, लेकिन parsing काफ़ी आगे बढ़ चुकी है, इतनी कि मैं एक ठीक-ठाक document draft लिख सका। एक बड़ा feature, date arithmetic, अभी नहीं है, और वह जुड़ने पर उसे अलग से लिखूँगा
DCG के साथ Prolog जटिल structured text parsing के लिए शानदार है। पहले जहाँ awk से काम न चले तो मैं Python या JS के बारे में सोचता था, अब जहाँ structure और discipline चाहिए वहाँ Prolog अच्छा फिट बैठता है। एक फ़ाइल में समाया हुआ जटिल codebase लिखने का इसका पुराना-सा एहसास भी अच्छा लगता है, और यह APL की तरह ज़रूरत से ज़्यादा संक्षिप्त भी नहीं है
उदाहरण खुद छोटा है, लेकिन Pokémon वाला केस ऐसा नहीं है। ज़्यादातर जो तुच्छ दिखने वाले उदाहरण हैं वे इसलिए संभव हुए क्योंकि बेहद बारीकी से implement किया गया code पहले से मौजूद है, जो हास्यास्पद रूप से जटिल battle mechanics को संभालता है। मेरी रुचि एक ऐसे Prolog rule engine में है जो मौजूदा tools के काम का कुछ हिस्सा करे, और मैं इस पर थोड़ा-थोड़ा प्रयोग भी कर रहा हूँ; इसका फ़ायदा यह है कि depth-first search के ज़रिए यह imperative code की तुलना में संभावनाओं को ज़्यादा आसानी से उजागर करता है और maintain करना भी आसान बनाता है
Scryer Prolog के docs भी DocLog नाम के Prolog program से generate होते हैं। मैंने इन programs द्वारा इस्तेमाल की जाने वाली कुछ libraries भी बनाई हैं। SWI Prolog भी अपनी website, Swish नाम के web IDE, और ClioPatria server जैसी चीज़ों में SWI का सीधे इस्तेमाल करता है
मैंने कभी SQLite को एक तरह की type-safe spreadsheet की तरह इस्तेमाल करके जानकारी व्यवस्थित की है। यह बिना किसी CRUD interface के भी संभव था
हालाँकि, यह हमेशा सबसे सुंदर दिखने वाली भाषा नहीं थी। मैंने कुछ समय Datalog को भी देखा, लेकिन ज़्यादातर implementations इस लेख की तरह आसानी से जानकारी लिखने के tool कम और बड़े programs में embed करने के लिए ज़्यादा लगीं
शायद Prolog ही वह tool हो जिसे वास्तव में इस्तेमाल करना चाहिए
Prolog, Datalog का superset है, इसलिए Datalog जो कुछ कर सकता है वह सब कर सकता है, और उससे भी अधिक। लेकिन वही “अधिक” कभी-कभी समस्या बन जाता है, क्योंकि अब यह एक Turing-complete language है, इसलिए यह ख़त्म न भी हो सकती है, reasoning थोड़ा कठिन हो सकता है, और इसे unsafe तरीकों से भी इस्तेमाल किया जा सकता है
Datalog पर लगी पाबंदियों की वजह से shortcut लिए जा सकते हैं, इसलिए कई बार performance भी बेहतर मिलती है
आख़िर में, Prolog में data और code एक ही चीज़ हैं, यानी homoiconicity है, इसलिए create/modify/delete operations असल में source code को बदलने जैसा हो जाता है। इसे dynamically भी किया जा सकता है, लेकिन यह उम्मीद नहीं करनी चाहिए कि इसका flow बहुत smooth होगा, और files तथा loaded program के बीच कुछ हद तक manual synchronization अब भी चाहिए होता है
मैं Prolog को और गहराई से सीखकर इसे instruction set queries में इस्तेमाल करना चाहता हूँ
लेकिन quantities और bit-level integers समेत logic को model करना काफ़ी कठिन है