- गेम के मुख्य मैकेनिज़्म को function, class, state variable की जगह logical relations और predicates के रूप में डिज़ाइन करने की एक पद्धति पर चर्चा की गई है
- Prolog, LISP की तुलना में अधिक अपरिचित इसलिए लगा क्योंकि यह syntax से अधिक गणितीय संबंधों के आधार पर data structures और algorithms को संभालता है
- यदि relational database की rows, digital circuits के input/output, और game objects को सभी n-ary relations के रूप में देखा जाए, तो Prolog का declarative model और data-driven design आपस में जुड़ते हैं
- graphics, audio, physics, और simple I/O तक सब कुछ Prolog में लागू करना कठिन है, लेकिन gameplay mechanics में इसकी संभावनाएँ हैं
- भाग 1 में world और actor के आधार पर tags और actors के बीच संबंधों को Prolog predicates से व्यक्त किया गया है, और समय के साथ बदलाव तथा game loop को अगले भाग के लिए छोड़ा गया है
गेम मैकेनिज़्म में Prolog लागू करने की समस्या-चेतना
- LISP और Prolog, C#, Java जैसी सामान्य object-oriented भाषाओं से अलग programming paradigms में आते हैं
- LISP functional programming और meta-linguistic patterns में मज़बूत है, जबकि Prolog को logic programming का प्रतिनिधि language माना जाता है
- LISP तक पहुँचना C# की lambda expressions, higher-order functions जैसी अवधारणाओं और electrical engineering तथा signal processing की पृष्ठभूमि के कारण अपेक्षा से आसान था
- इसके विपरीत, Prolog का syntax सरल दिखने पर भी data structures और algorithms को गणितीय संबंधों के रूप में संभालने का तरीका एक बड़ी कठिनाई था
- Prolog का दृष्टिकोण कई engineering models से जुड़ता है
- relational database की हर row को Prolog syntax में n-ary predicate के रूप में देखा जा सकता है
- digital circuit components के input/output व्यवहार को input और output ports को मिलाकर बने n-ary relation के रूप में लागू किया जा सकता है
- hardware के टुकड़े या memory के भीतर मौजूद pure data जैसे objects को भी logic programming में relations के रूप में परिभाषित किया जा सकता है
- जैसे functional programming में objects को functions की तरह देखा जा सकता है, वैसे ही logic programming में objects को relations की तरह देखा जा सकता है
गेम के मुख्य मैकेनिज़्म और declarative design
- logic programming का उपयोग video game के core gameplay mechanics जैसे जटिल systems को डिज़ाइन और लागू करने में किया जा सकता है
- पूरे गेम को Prolog में लागू करना, यदि वह केवल प्रयोग का उद्देश्य न हो, तो सामान्य game developers के लिए बहुत बड़ी चुनौती है
- गेम के सभी components Prolog के लिए उपयुक्त नहीं होते
- simple I/O modules
- graphics modules
- audio modules
- physics modules
- इसका लागू होने वाला क्षेत्र पूरे गेम की बजाय core mechanics के अधिक निकट है
- यदि gameplay system को declarative statements के समूह के रूप में बनाया जाए, तो वह imperative systems की तुलना में अधिक मज़बूत और modular हो सकता है, और race condition जैसे भ्रमित करने वाले edge cases से अधिक मुक्त रह सकता है
- इस methodology में system को केवल logical relations और predicates से डिज़ाइन करना होता है
- कोई function नहीं
- कोई struct नहीं
- कोई class नहीं
- कोई interface नहीं
- कोई state variable नहीं
World और Actor मॉडल
- Prolog-आधारित game programming का मूल विचार यह है कि electrical circuits के resistor, transistor, capacitor, inductor की तरह relations को सबसे आदिम building block के रूप में इस्तेमाल किया जाए
- गेम को मोटे तौर पर world और actor में बाँटा जाता है
- world वह दृश्य है जहाँ सब कुछ होता है
- actor वह object है जो world के भीतर मौजूद है
- actor से आशय player, enemy, obstacle, item जैसी नाम और गुणों वाली अलग-अलग entities से है
- actor एक-दूसरे के साथ या स्वयं के साथ interact कर सकते हैं, और इस प्रक्रिया में कई तरह की events उत्पन्न होती हैं
- gameplay को इन events की शृंखला के रूप में देखा जा सकता है
- इस मॉडल में world कई actors को शामिल करता है, और हर actor की अपनी state और behavior होती है
Tags को unary predicates के रूप में व्यक्त करना
- हर actor की पहचान एक unique name से होती है
- उदाहरण में
actor1, actor2 पहले और दूसरे actor को दर्शाते हैं
- tag वह keyword है जो actor पर लगाया जाता है और यह बताता है कि actor क्या है
bread(actor1). और bread(actor2). का अर्थ है कि actor1 और actor2 दोनों bread हैं
bread(actor1).
bread(actor2).
- Prolog में
bread एक unary relation है, और bread(actor1) तथा bread(actor2) उस relation के अलग-अलग instances हैं
- एक actor के पास कई tags हो सकते हैं
- यदि सभी bread को
flammable और decomposable होना चाहिए, तो हर actor पर manually tags लगाना झंझटभरा और error-prone हो सकता है
- Horn clause का उपयोग करने पर, जब
bread(X) सत्य हो, तो flammable(X) और decomposable(X) अपने-आप स्थापित हो जाते हैं
flammable(X) :- bread(X).
decomposable(X) :- bread(X).
- ये Horn clauses गेम के configuration data की तरह काम करते हैं
- यह technical design document या spreadsheet में character type, skill type, mission type की properties define करने जैसा है
- संरचना ऐसी है कि
bread नाम के type-specifier में flammable, decomposable गुण शामिल हैं
- Unity के उदाहरण से देखें तो यह
Bread prefab में Flammable, Decomposable components जोड़ने जैसा है
- object-oriented environment में यह उस स्थिति जैसा है जहाँ
Bread class, IFlammable, IDecomposable interfaces को implement करती है
- इस संदर्भ में Prolog की Horn clauses data type definition की भूमिका निभाती हैं
अलग-अलग actors पर लगने वाले custom tags
- पहले से परिभाषित tags के अलावा, किसी खास actor पर custom tag लगाया जा सकता है
- उदाहरण में एक wizard ने दूसरी bread
actor2 पर जादू किया है, इसलिए enchanted tag की ज़रूरत है
enchanted(actor2).
flammable और decomposable, सभी bread की विशेषताएँ हैं
enchanted केवल उस विशेष bread की विशेषता है जिस पर जादू किया गया है
Actors के बीच संबंधों को binary predicates के रूप में व्यक्त करना
- gameplay system को केवल अलग-अलग actors की विशेषताएँ ही नहीं, बल्कि actors के बीच के संबंध भी व्यक्त करने होते हैं
- संबंधों के उदाहरण इस प्रकार हैं
- ecosystem में predator, prey का पीछा करता है और prey, predator से भागता है
- dating simulator में पुरुष, महिला से flirt करने की कोशिश करता है और महिला मना कर देती है
- The Sims जैसे social simulator में लोग एक-दूसरे के दोस्त, दुश्मन, या उनके बीच कहीं होते हैं
- chess में bishop तिरछी दिशा में rook को पकड़ता है, और rook सीधी दिशा में bishop को पकड़ता है
- Prolog में जैसे unary predicates किसी एक actor की विशेषता दिखाते हैं, वैसे ही binary predicates दो actors के बीच संबंध दिखा सकते हैं
- Horn clause का उपयोग करने पर आवश्यक शर्तों के समूह से संबंधों का dynamic inference किया जा सकता है
canEat और canSpoil उदाहरण
- यदि
actor3 एक human है और human bread खा सकता है, तो “यदि X human है और Y bread है, तो X, Y को खा सकता है” यह संबंध परिभाषित किया जा सकता है
human(actor3).
canEat(X, Y) :- human(X), bread(Y).
- यहाँ
canEat(X, Y) X और Y के बीच स्थापित होने वाला एक relation है
- यदि
actor4 fungus है और bread decomposable है, तो fungus किसी decomposable actor को spoil कर सकता है
fungus(actor4).
canSpoil(X, Y) :- fungus(X), decomposable(Y).
canSpoil(X, Y) का अर्थ है “यदि X fungus है और Y decomposable है, तो X, Y को spoil कर सकता है”
decomposable(Y) को पहले दिए गए bread(Y) से Horn clause के माध्यम से infer किया जा सकता है
अभी बाकी समस्या: समय के साथ बदलाव और game loop
- अब तक के उदाहरण actors की विशेषताओं और संबंधों को व्यक्त करते हैं, लेकिन ये सभी static हैं
- गेम कोई स्थिर दृश्य नहीं है; समय बीतने के साथ actors को चलना और interact करना चाहिए
- केवल Prolog की declarative प्रकृति से समय के साथ गुणों में होने वाले बदलाव की समस्या को हल करना आसान नहीं लगता
- Prolog में game loop की अवधारणा कैसे बनाई जाए, यह Part 2 में जारी है
1 टिप्पणियां
Hacker News की राय
आम तौर पर जिन लेखों में सिर्फ Part 1 होता है और फिर वे रुक जाते हैं, उन्हें मैं कम आंकता हूं, लेकिन इस लेख के असल में आगे के पार्ट्स लगातार आ रहे हैं
https://thingspool.net/morsels/page-11.html (part 2)
https://thingspool.net/morsels/page-12.html (part 3)
https://thingspool.net/morsels/page-13.html (part 4)
https://thingspool.net/morsels/page-14.html (part 5)
और यह आगे भी जारी है
लगता है इन तकनीकों को Rust गेम प्रोग्रामिंग में लाया जा सकता है
Breath of the Wild के गेम डिज़ाइन में chemistry engine नाम का एक concept है। आम तौर पर game engine में physics engine होता है, जो यह calculate करता है कि motion के लिहाज़ से objects कैसे interact करते हैं; chemistry engine यह संभालता है कि materials एक alchemical अर्थ में कैसे interact करते हैं
यह rules-based engine की तरह अलग-अलग substances की interactions calculate करता है, जिससे ऐसे हैरान करने वाले interactions पैदा होते हैं जैसे arrow लकड़ी का होने की वजह से उसमें आग लगाई जा सकती है
YouTube लिंक: https://www.youtube.com/watch?v=QyMsF31NdNc&t=2354s
गेम के rules-based elements आम तौर पर अपेक्षाकृत सरल होते हैं और उन्हें general-purpose होने की ज़रूरत नहीं होती, इसलिए लगता है कि उन्हें हाथ से code किया जाता है। Baba is You के creator से जब पूछा गया कि क्या उन्होंने Datalog engine implement किया था, तो उन्होंने भी कहा था कि नहीं; Breath of the Wild भी शायद ऐसा ही होगा
फिर भी, मैं अक्सर सोचता हूं कि ऐसे chemistry engine को तेज़ experimentation के लिए Prolog या Datalog जैसी logic languages में implement करना सबसे अच्छा नहीं होगा क्या। SQL भी queries की flexibility बनाए रखने के लिए इस्तेमाल किया गया और आखिरकार वैसे ही deploy हो गया; पहले शायद SQL queries धीमी होने की शिकायतें बहुत होती होंगी, लेकिन उसकी flexibility इतनी उपयोगी थी कि उसे तेज़ बनाने में बहुत समय लगाया गया। अब लगभग कोई नहीं सोचता कि “मैं इस query से तेज़ imperative code खुद लिख सकता हूं”
अब तक खेले गए games में यह काफी दिलचस्प लगा
https://store.steampowered.com/app/881100/Noita/
https://www.youtube.com/watch?v=prXuyMCgbTc
शायद MUD भी ऐसा ही होगा
graphical games जिन चीज़ों को innovative तरीके से करने की कोशिश कर रहे हैं, वे 40 साल पहले text games, खासकर interactive fiction, में पहले ही हो चुकी थीं। यह Infocom के शुरुआती titles से भी ज़्यादा 90s के Curses, Jigsaw, Anchorhead, Devours, Spider and Web जैसे बेहतरीन amateur games के renaissance में दिखा; NetHack/Slash'EM जैसे roguelikes और CDDA: Bright Nights में भी, और fantasy ही नहीं बल्कि cyberpunk या SF settings में अजीब materials और interactions扱ने वाले online MUDs में भी
Warren Spector जैसे बेहतरीन game creators ने भी free software games, indie games और geeky games खेलकर नए gameplay ideas लिए होंगे
original Deus Ex में NetHack का reference सिर्फ easter egg नहीं था; मेरे हिसाब से वह शायद NetHack से inspire हुए emergent gameplay को tribute था
Deus Ex की ताकत graphics या worldbuilding नहीं, बल्कि यह थी कि game के अंदर आप क्या कर सकते हैं; और इसने FPS की linear structure को तोड़ा, फिर भी System Shock 1 और 2 की तुलना में इसे ज़्यादा approachable बनाया। System Shock side में difficulty tuning और gameplay polish में कुछ failures थे। Arx Fatalis (अब Libertatis) और Ultima Underworld भी ऐसे ही हैं
सरल rules से सच में बहुत दिलचस्प complexity पैदा होती है
general-purpose logic/relational language ने जो solver algorithms implement किए हैं सिर्फ वही संभव नहीं होते; और जितनी language general-purpose होगी, game engine को जिन problems को efficiently solve करना है उनके लिए वह उतनी inefficient हो सकती है। उल्टा, अगर logic language में ऐसी features को natively extend किया जाए, तो simplification assumptions टूट सकते हैं या evaluation order बदलना पड़ सकता है, जिससे language को simple और efficient तरीके से implement करना मुश्किल हो सकता है
उदाहरण के लिए, मैं logical relations और linear constraints को साथ इस्तेमाल करना चाहता था। जैसे
A <= (B-10.0) .OR. B <= (A-10.0)—इस तरह width 10.0 वाले दो game objects के center positions पर constraint लगाकर उनकी boundaries overlap न हों, लेकिन किस तरफ कौन आगे है इसकी परवाह न करनी पड़े। ऐसे statements से scenes या complex game objects को qualitatively compose किया जा सकता है, और exact coordinates linear constraint solver चुन सकता हैसमस्या यह है कि
f(A) <= g(B)जैसे linear constraint को solve करने पर उससे जुड़ी सभी variables प्रभावित हो सकती हैं। A या B के C, D, E, F के साथ भी constraints हो सकते हैं, इसलिए logical expressionprop(X) .OR. prop(Y)के left या right side को चुनते ही execute कर दिया जाए, तो पहले से तय किए गए दूसरे propositions invalid हो सकते हैंइसलिए linear variables के
<=को Boolean result देने वाले test के रूप में interpret करने के बजाय, उसे global linear constraint store मेंf(A) <= g(B)जोड़ने वाली command के रूप में interpret करना और बाद में पूरे linear variable matrix पर solver चलाना बेहतर हैतब design questions उठते हैं: language को कैसे पता चले कि constraints जोड़ना कब खत्म हुआ और solve करने का समय कब है; जिन logical clauses में वास्तव में value test करनी हो, constraint नहीं लगाना हो, उनका क्या किया जाए। लेकिन core point यह है कि game engine context में linear constraint solving और logic programming दोनों में लंबी computations कब चलें और कितनी देर चलें, इस पर ज़्यादा control चाहिए
यानी logic language के नज़रिए से extension feature को अच्छी तरह काम करने के लिए design कर भी दिया जाए, तो outer system के नज़रिए से problem बचती है: logic program के किसी हिस्से को solve करने की search time अचानक बहुत बढ़ सकती है, और programmer के लिए हमेशा predict करना मुश्किल होता है कि ऐसा कब और कहां होगा। इस अर्थ में hand-written imperative code का तेज़ होना भी ज़रूरी नहीं; धीमा हो, लेकिन predictability हो तो काफी है
असल में सब कुछ खुद लिखना भी ज़रूरी नहीं। मुख्य बात यह है कि rules engine ऐसा solver है जिसे imperative game code बाहर से drive और preempt कर सकता है, या पूरा game solver यानी relational language के अंदर चलता है। अगर पूरा game relational हो तो क्या किया जा सकता है, यह कल्पना करना आकर्षक है; लेकिन पूरे game पर वह technique इस्तेमाल करने के लिए एक ऐसे जादुई relational solver की ज़रूरत होगी जो असल में मौजूद नहीं है
Prolog गेम प्रोग्रामिंग का परिचय होते हुए भी सीधे action game की तरफ जाना दिलचस्प है। यह real-time, timeline, 3D, ECS और event पहलुओं को कवर करता है
आम तौर पर Prolog game development की शुरुआती किताबें adventure games से शुरू होती हैं, खासकर classic text adventures से। वजह यह है कि maze और inventory puzzles, Prolog के facts और rules, DSL से सीधे मेल खाते हैं
card games या board games में भी rules को Prolog में बहुत आसानी से व्यक्त किया जा सकता है, और robotics, logistics, finance, industry आदि में इस्तेमाल होने वाले Prolog planners की तरह basic combinatorial general-purpose game opponent को लगभग आसानी से extend किया जा सकता है
AI क्लास में मैंने सबसे पहले Prolog सीखा था, और assignment के तौर पर सभी को adventure/colossal cave style game लिखना था। Prolog इस काम के लिए अच्छा फिट था, और क्लास में बने simple games की विविधता हैरान करने वाली थी
मुझे लगा कि काश दूसरे students के बनाए सारे games इकट्ठा करके रखे होते। कुछ हफ्तों तक इसे करने के बाद हम CLIPS और Lisp जैसे दूसरे topics पर चले गए
अपने assignment में मैंने Bureaucratic Maze [1] बनाया था, और यह भी Prolog में काफी intuitive था
[1]. http://logicmazes.com/bureau/index.htm
लेकिन Inform6 में game design पर applied object-oriented approach और ZMachine की तरह game के भीतर objects के relationships को सीधे handle करने के तरीके की वजह से difficulty बहुत घटकर लगभग trivial हो जाती है
http://logicmazes.com/alice.html
alice.html:353 Uncaught TypeError: Cannot read properties of undefined (reading 'play')at playSound (alice.html:353:30)at finalize2 (alice.html:347:1)at :1:112 posts सरसरी तौर पर देखीं, और Prolog इस्तेमाल करने के परिचय के तौर पर ये अच्छी लगती हैं, लेकिन इसे अभी game programming से जुड़ा कहना थोड़ा मुश्किल लगता है
आगे चलकर यह develop हो सकता है, लेकिन lecture 12 तक ज्यादातर content Prolog में कुछ object-oriented concepts को model करने की कोशिश के बारे में था
पता नहीं मैंने कुछ miss किया है या नहीं, लेकिन game की prerequisite जैसा लगने वाला user interaction अभी cover नहीं किया गया था। message passing पर छोटा-सा हिस्सा था, शायद उसी को intended माना गया हो
इसी तरह के context में Prolog की जगह CLIPS इस्तेमाल करने वाली logic programming game programming किताब पढ़ना मजेदार लगा
Adventures in Rule-Based Programming: A CLIPS Tutorial https://a.co/d/7wVOcZp
https://www.clipsrules.net/
programming books अक्सर Kindle पर खराब होती हैं, लेकिन Fire tablet पर देखने पर शायद ठीक हो
game state को facts और relations के set के रूप में देखने का नजरिया (Prolog) और ECS के core “यह database है”[1] के बीच दिलचस्प overlap है
personally मैं Datascript को game state database के तौर पर experiment कर रहा हूं, लेकिन अभी यह तय करने के लिए शुरुआती stage है कि यह सफल होगा या नहीं। इस tutorial के ideas को उस perspective से 1:1 map होते देखना अच्छा है
अभी पक्का नहीं है कि मैंने जो लिखा है उससे भी ज्यादा concise rule definition संभव है या नहीं। Stanford paper में rules
(<= (column ?n ?x) ...)की तरह लिखे जा सकते हैं, लेकिन Datascript में data move करने का surrounding work कहीं ज्यादा हो जाता हैयह Datascript/Datomic Datalog की limitation की वजह से है या मेरी understanding की कमी की वजह से, पता नहीं
आप experiment को कैसे approach कर रहे हैं, यह जानने में curiosity है। अगर share करने लायक work या मेरे current approach पर advice हो, तो मुझे बहुत interest होगा
[1]: https://www.cs.uic.edu/~hinrichs/papers/love2006general.pdf
बहुत logic वाले games, जैसे city simulation में state machine problem को handle करने के तरीके के तौर पर यह fresh लगता है। Prolog को इस तरह इस्तेमाल करने के बारे में मैंने कभी नहीं सोचा था
causalता और relativity पर चर्चा में वाकई शानदार चीज़ें की जा सकती हैं, ऐसा लगता है। उदाहरण के लिए, NPC के साथ random encounter जनरेट करते समय, उस NPC की event history बनाई जा सकती है जिसमें खिलाड़ी द्वारा अतीत में शुरू की गई causal chain शामिल हो
सब कुछ relation है, और NPC के अभी मौजूद होने का तथ्य, साथ ही observe किए गए सभी world states और actions की history मौजूद है, इसलिए पहले से simulate किए बिना भी पीछे की ओर trace करके NPC को पूरी तरह consistent past दिया जा सकता है
मैंने कभी सोचा नहीं था कि आम actor-world/entity-trait model Prolog के relational तरीके से इतना अच्छी तरह मेल खाता है
हालांकि predictable और efficient execution time भी महत्वपूर्ण है। Prolog आम तौर पर query को satisfy करने के लिए terms को match करते हुए brute-force search करने की प्रवृत्ति रखता है। मैंने अभी पूरी series नहीं पढ़ी है, लेकिन जानना चाहता हूँ कि क्या यह इस समस्या को address करती है
मेरा पहला अनुमान है कि solutions के बारे में ज्ञात constraints को किसी तरह के “type declaration” की तरह लिखने का रूप होगा