रस्ट, सी और किसी भी अन्य भाषा में स्टारक्राफ्ट के लिए बॉट

स्टारक्राफ्ट: ब्रूड वॉर । मेरे लिए कितना मायने रखता है। और आप में से कई के लिए। इतने कि मुझे शक हुआ कि क्या विकी को कोई लिंक देना है।


एक बार, हॉल्ट ने मुझे एक व्यक्तिगत ईमेल में दस्तक दी और जंग सीखने की पेशकश की। किसी भी सामान्य लोगों की तरह, हमने शुरुआत करने का फैसला किया नमस्ते दुनिया विंडोज के लिए एक डायनेमिक लाइब्रेरी लिखना जो स्टारक्राफ्ट गेम के एड्रेस स्पेस में लोड हो सकता है और यूनिट्स को मैनेज कर सकता है।


लेख समाधान खोजने की प्रक्रिया का वर्णन करेगा, प्रौद्योगिकियों, तकनीकों का उपयोग करके, जो आपको रस्ट भाषा और इसके पारिस्थितिकी तंत्र में नई चीजें सीखने या अपनी पसंदीदा भाषा में एक बॉट को लागू करने के लिए प्रेरित करने की अनुमति देगा, चाहे वह सी, सी ++, रूबी, अजगर, आदि हो।


यह लेख निश्चित रूप से दक्षिण कोरिया के गान के तहत पढ़ने लायक है:


Starcraft OST

BWAPI


यह खेल पहले से ही 20 साल पुराना है। और यह अभी भी लोकप्रिय है , चैंपियनशिप 2017 में भी संयुक्त राज्य अमेरिका में लोगों के पूरे हॉल को इकट्ठा करती है, जहां ग्रैंडमास्टर्स जैडॉन्ग बनाम बिसू की लड़ाई हुई थी। जीवित खिलाड़ियों के अलावा, सौललेस कारें भी लड़ाई में भाग लेती हैं! और यह BWAPI के लिए धन्यवाद संभव है। अधिक उपयोगी लिंक


एक दशक से अधिक समय से, इस खेल के आसपास बॉट डेवलपर्स का एक समुदाय रहा है। उत्साही लोग बॉट लिखते हैं और विभिन्न प्रतियोगिताओं में भाग लेते हैं। उनमें से कई एआई और मशीन लर्निंग का अध्ययन करते हैं। BWAPI का उपयोग विश्वविद्यालयों द्वारा अपने छात्रों को शिक्षित करने के लिए किया जाता है। यहां तक ​​कि एक चिकोटी चैनल भी है जो खेलों का प्रसारण करता है।


इसलिए, कुछ साल पहले प्रशंसकों की एक टीम ने स्टारक्राफ्ट इंटर्नल्स को उलट दिया और एक सी ++ एपीआई लिखा जो आपको बॉट लिखने, खेल प्रक्रिया में एकीकृत करने और दयनीय छोटे लोगों पर हावी होने की अनुमति देता है।


जैसा कि अक्सर पहले होता है एक घर बनाने के लिए, आपको अयस्क, फोर्ज टूल्स प्राप्त करने की आवश्यकता है ... एक बॉट लिखें, आपको एपीआई को लागू करने की आवश्यकता है। Rust क्या दे सकता है?


FFI


जंग से अन्य भाषाओं के साथ बातचीत बहुत सरल है। इसके लिए एफएफआई है। मुझे प्रलेखन से एक संक्षिप्त अंश प्रदान करते हैं।


मान लीजिए कि हमारे पास एक तड़क-भड़क वाली लाइब्रेरी है, जिसमें तड़क-भड़क वाली हेडर फाइल है, जिससे हम फंक्शन डिक्लेरेशन कॉपी करेंगे।


कार्गो का उपयोग करके एक प्रोजेक्ट बनाएं।


$ cargo new --bin snappy Created binary (application) `snappy` project $ cd snappy snappy$ tree . ├── Cargo.toml └── src └── main.rs 1 directory, 2 files 

कार्गो ने परियोजना के लिए एक मानक फ़ाइल संरचना बनाई।


Cargo.toml libc पर निर्भरता निर्दिष्ट करें:


 [dependencies] libc = "0.2" 

src/main.rs फ़ाइल इस तरह दिखाई देगी:


 extern crate libc; //   C ,     size_t use libc::size_t; #[link(name = "snappy")] //       extern { //    ,    //  C  : // size_t snappy_max_compressed_length(size_t source_length); fn snappy_max_compressed_length(source_length: size_t) -> size_t; } fn main() { let x = unsafe { snappy_max_compressed_length(100) }; println!("max compressed length of a 100 byte buffer: {}", x); } 

हम इकट्ठा करते हैं और चलाते हैं:


 snappy$ cargo build ... snappy$ cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.02s Running `target/debug/snappy` max compressed length of a 100 byte buffer: 148 

आप केवल cargo run कह सकते cargo run , जो लॉन्च से पहले cargo build बुलाता है। या प्रोजेक्ट बनाएं और बाइनरी को सीधे कॉल करें:


 snappy$ ./target/debug/snappy max compressed length of a 100 byte buffer: 148 

कोड इस शर्त पर संकलित करता है कि स्नैपी लाइब्रेरी स्थापित है (Ubuntu के लिए, libsnappy-dev पैकेज स्थापित होना चाहिए)।


 snappy$ ldd target/debug/snappy ... libsnappy.so.1 => /usr/lib/x86_64-linux-gnu/libsnappy.so.1 (0x00007f8de07cf000) 

जैसा कि आप देख सकते हैं, हमारा द्विआधारी libsnappy साझा पुस्तकालय से जुड़ा हुआ है। और snappy_max_compressed_length कॉल इस लाइब्रेरी से एक फ़ंक्शन कॉल है।


जंग-bindgen


यह अच्छा होगा यदि हम स्वचालित रूप से एफएफआई उत्पन्न कर सकते हैं। सौभाग्य से, रस्टोमोनोव के शस्त्रागार में ऐसी उपयोगिता है जिसे रस्ट-बिंडगेन कहा जाता है। वह FFI बाइंडिंग को C (और कुछ C ++) लाइब्रेरी में जेनरेट करने में सक्षम है।


स्थापना:


 $ cargo install bindgen 

जंग-बिंदेन का उपयोग करने से क्या दिखता है? हम सी / सी ++ हेडर फ़ाइलों को लेते हैं, उन पर बाइंडजेन उपयोगिता सेट करते हैं, और हम संरचना संरचनाओं और कार्यों की परिभाषाओं के साथ उत्पन्न जंग कोड प्राप्त करते हैं। यहां जानिए क्या है चुलबुली के लिए FFI जेनरेशन:


 $ bindgen /usr/include/snappy-ch | grep -C 1 snappy_max_compressed_length extern "C" { pub fn snappy_max_compressed_length(source_length: usize) -> usize; } 

यह पता चला कि बिंडगेन बीडब्ल्यूएपीआई हेडर के सामने से गुजरता है, जिससे गैर-उपयोग करने योग्य कोड शीट (वर्चुअल सदस्य कार्यों के कारण, सार्वजनिक एपीआई, आदि में स्ट्रिंग :: स्ट्रिंग) के टन पैदा होते हैं। बात यह है कि BWAPI C ++ में लिखा गया है। C ++ आमतौर पर C ++ प्रोजेक्ट से भी उपयोग करना मुश्किल है। एक बार संकलित पुस्तकालय एक ही लिंकर (समान संस्करणों) के साथ लिंक करने के लिए बेहतर है, हेडर फाइलें एक ही संकलक (समान संस्करण) के साथ पार्स करने के लिए बेहतर हैं। क्योंकि कई कारक हैं जो परिणाम को प्रभावित कर सकते हैं। उदाहरण के लिए, मैनबलिंग , जिसे जीएनयू जीसीसी अभी भी त्रुटियों के बिना लागू नहीं कर सकता है । ये कारक इतने महत्वपूर्ण हैं कि उन्हें gtest में भी दूर नहीं किया जा सकता है, और प्रलेखन ने संकेत दिया कि आपके लिए बेहतर होगा कि वे उसी कंपाइलर और उसी लिंकर के साथ प्रोजेक्ट के हिस्से के रूप में gtest का निर्माण करें।


BWAPI-सी


C लिंगुआ फ्रेंका प्रोग्रामिंग है। यदि जंग-बिंदगेन सी भाषा के लिए अच्छी तरह से काम करता है, तो सी के लिए बीडब्ल्यूएपीआई क्यों लागू नहीं करता है और फिर इसकी एपीआई का उपयोग करें? अच्छा विचार है!


हां, यह एक अच्छा विचार है जब तक कि आप BWAPI की हिम्मत में न देखें और उन कक्षाओं और विधियों की संख्या देखें जिन्हें आपको लागू करने की आवश्यकता है = (विशेष रूप से स्मृति, कोडांतरक, मेमोरी पैच और अन्य भयावह संरचनाओं के इन सभी लेआउट के लिए जिनके पास समय नहीं है। मौजूदा समाधान का अधिकतम उपयोग करना आवश्यक है।


लेकिन हमें किसी भी तरह मैन्बलिंग, C ++ कोड, इनहेरिटेंस, और वर्चुअल मेंबर फ़ंक्शंस से निपटना चाहिए।


सी ++ में, दो शक्तिशाली उपकरण हैं जो हम अपनी समस्या को हल करने के लिए उपयोग करेंगे, ये अपारदर्शी बिंदु और extern "C"


extern "C" {} C ++ कोड को C के रूप में स्वयं को छिपाने में सक्षम बनाता है। यह आपको बिना मेन्यूलेशन के शुद्ध फ़ंक्शन नाम उत्पन्न करने की अनुमति देता है।


अपारदर्शी संकेत हमें एक प्रकार को मिटाने और "कुछ प्रकार" के लिए एक पॉइंटर बनाने की क्षमता देते हैं जिसका कार्यान्वयन हम प्रदान नहीं करते हैं। चूंकि यह केवल कुछ प्रकार की घोषणा है, और इसके कार्यान्वयन के लिए नहीं, इस प्रकार का उपयोग मूल्य द्वारा करना असंभव है, इसका उपयोग केवल सूचक द्वारा किया जा सकता है।


मान लें कि हमारे पास ऐसे C ++ कोड हैं:


 namespace cpp { struct Foo { int bar; virtual int get_bar() { return this->bar; } }; } // namespace cpp 

हम इसे इस तरह सी हेडर में बदल सकते हैं:


 extern "C" { typedef struct Foo_ Foo; //    Foo //  cpp::Foo::get_bar int Foo_get_bar(Foo* self); } 

और सी ++ भाग, जो सी हेडर और सी ++ कार्यान्वयन के बीच की कड़ी होगी:


 int Foo_get_bar(Foo* self) { //      cpp::Foo    ::get_bar return reinterpret_cast<cpp::Foo*>(self)->get_bar(); } 

सभी वर्ग विधियों को इस तरह से संभाला नहीं गया था। BWAPI में कक्षाएं होती हैं, जिन पर आप इन संरचनाओं के क्षेत्र मूल्यों का उपयोग करके अपने आप को कार्यान्वित कर सकते हैं, उदाहरण के लिए typedef struct Position { int x; int y; } Position;typedef struct Position { int x; int y; } Position; typedef struct Position { int x; int y; } Position; और Position::get_distance जैसे तरीके Position::get_distance


ऐसे लोग थे जिन्हें एक विशेष तरीके से आजमाया जाना था। उदाहरण के लिए, AIModule वर्चुअल सदस्य कार्यों के एक विशिष्ट सेट के साथ C ++ क्लास का एक पॉइंटर होना चाहिए। हालांकि, यहां शीर्षक और कार्यान्वयन है


इसलिए, कई महीनों की मेहनत के बाद, 554 तरीके और एक दर्जन कक्षाएं, क्रॉस-प्लेटफॉर्म लाइब्रेरी BWAPI-C का जन्म हुआ, जो आपको सी में बॉट्स लिखने की अनुमति देता है। उपोत्पाद क्रॉस-संकलन और किसी भी अन्य भाषा में एपीआई को लागू करने की क्षमता थी जो एफएफआई और सीडीसीएल कॉलिंग हस्तक्षेप का समर्थन करता है।


यदि आप एक पुस्तकालय लिख रहे हैं, तो कृपया सी एपीआई लिखिए।


BWAPI-C की सबसे महत्वपूर्ण विशेषता अन्य भाषाओं के साथ एकीकृत करने की सबसे व्यापक क्षमता है। Python , Ruby , Rust , PHP , Java और कई अन्य लोग जानते हैं कि सी के साथ कैसे काम करना है, इसलिए आप उन पर एक बॉट भी लिख सकते हैं, अगर आप फ़ाइल के साथ थोड़ा काम करते हैं और अपने रैपर को लागू करते हैं।


C में बॉट लिखना


यह हिस्सा Starcraft मॉड्यूल के डिजाइन के सामान्य सिद्धांतों का वर्णन करता है।


बॉट्स 2 प्रकार के होते हैं: मॉड्यूल और क्लाइंट। हम मॉड्यूल लिखने के एक उदाहरण को देखेंगे।


एक मॉड्यूल एक डाउनलोड करने योग्य पुस्तकालय है; लोडिंग का सामान्य सिद्धांत यहां देखा जा सकता है । मॉड्यूल को 2 फ़ंक्शन निर्यात करना चाहिए: newAIModule और gameInit


gameInit साथ gameInit सब कुछ सरल है, इस फ़ंक्शन को वर्तमान गेम के लिए एक पॉइंटर पास करने के लिए कहा जाता है। यह सूचक बहुत महत्वपूर्ण है, क्योंकि BWAPI के विल्स में एक वैश्विक स्थिर चर रहता है जिसका उपयोग कोड के कुछ हिस्सों में किया जाता है। चलो gameInit का वर्णन gameInit :


 DLLEXPORT void gameInit(void* game) { BWAPIC_setGame(game); } 

newAIModule कुछ अधिक जटिल है। इसे एक C ++ क्लास के लिए एक पॉइंटर लौटना चाहिए, जिसमें onXXXXX के नाम के साथ एक वर्चुअल मेथड टेबल है, जिसे कुछ गेम इवेंट्स में बुलाया जाता है। मॉड्यूल की संरचना को परिभाषित करें:


 typedef struct ExampleAIModule { const AIModule_vtable* vtable_; const char* name; } ExampleAIModule; 

पहला क्षेत्र विधियों (जादू, सभी चीजों) की तालिका के लिए एक संकेतक होना चाहिए। तो, newAIModule फ़ंक्शन:


 DLLEXPORT void* newAIModule() { ExampleAIModule* const module = (ExampleAIModule*) malloc( sizeof(ExampleAIModule) ); module->name = "ExampleAIModule"; module->vtable_ = &module_vtable; return createAIModuleWrapper( (AIModule*) module ); } 

createAIModuleWrapper एक और जादू है जो C पॉइंटर को पॉइंटर में बदलकर C ++ क्लास को वर्चुअल कर देता है तरीकों सदस्य कार्य।


module_vtable टेबल पर एक स्थिर वैरिएबल है module_vtable , वैश्विक कार्यों के लिए तरीकों के मूल्यों को संकेत से भरा जाता है:


 static AIModule_vtable module_vtable = { onStart, onEnd, onFrame, onSendText, onReceiveText, onPlayerLeft, onNukeDetect, onUnitDiscover, onUnitEvade, onUnitShow, onUnitHide, onUnitCreate, onUnitDestroy, onUnitMorph, onUnitRenegade, onSaveGame, onUnitComplete }; void onEnd(AIModule* module, bool isWinner) { } void onFrame(AIModule* module) {} void onSendText(AIModule* module, const char* text) {} void onReceiveText(AIModule* module, Player* player, const char* text) {} void onPlayerLeft(AIModule* module, Player* player) {} void onNukeDetect(AIModule* module, Position target) {} void onUnitDiscover(AIModule* module, Unit* unit) {} void onUnitEvade(AIModule* module, Unit* unit) {} void onUnitShow(AIModule* module, Unit* unit) {} void onUnitHide(AIModule* module, Unit* unit) {} void onUnitCreate(AIModule* module, Unit* unit) {} void onUnitDestroy(AIModule* module, Unit* unit) {} void onUnitMorph(AIModule* module, Unit* unit) {} void onUnitRenegade(AIModule* module, Unit* unit) {} void onSaveGame(AIModule* module, const char* gameName) {} void onUnitComplete(AIModule* module, Unit* unit) {} 

फ़ंक्शंस और उनके हस्ताक्षरों के नाम से यह स्पष्ट है कि किन शर्तों के साथ और किस तर्क के साथ उन्हें बुलाया जाता है। उदाहरण के लिए, मैंने सिवाय सभी कार्यों को खाली कर दिया


 void onStart(AIModule* module) { ExampleAIModule* self = (ExampleAIModule*) module; Game* game = BWAPIC_getGame(); Game_sendText(game, "Hello from bwapi-c!"); Game_sendText(game, "My name is %s", self->name); } 

खेल शुरू होने पर इस फ़ंक्शन को कहा जाता है। वर्तमान मॉड्यूल के लिए एक संकेतक को एक तर्क के रूप में पारित किया जाता है। BWAPIC_getGame गेम को एक वैश्विक पॉइंटर लौटाता है जिसे हम BWAPIC_setGame कहकर सेट BWAPIC_setGame । इसलिए, हम क्रॉस-संकलन और मॉड्यूल ऑपरेशन का एक उदाहरण दिखाएंगे:


 bwapi-c/example$ tree . ├── BWAPIC.dll └── Dll.c 0 directories, 2 files bwapi-c/example$ i686-w64-mingw32-gcc -mabi=ms -shared -o Dll.dll Dll.c -I../include -L. -lBWAPIC bwapi-c/example$ cp Dll.dll ~/Starcraft/bwapi-data/ bwapi-c/example$ cd ~/Starcraft/bwapi-data/ Starcraft$ wine bwheadless.exe -e StarCraft.exe -l bwapi-data/BWAPI.dll --headful ... ... ... 

हम बटन दबाते हैं और खेल शुरू करते हैं। आप BWAPI वेबसाइट पर और BWAPI-C में लॉन्च के बारे में अधिक पढ़ सकते हैं।


मॉड्यूल का परिणाम:


छवि


मॉड्यूल का थोड़ा अधिक जटिल उदाहरण जो पुनरावृत्तियों , इकाई नियंत्रण, खनिज खोज और सांख्यिकी के साथ काम करता है, उसे bwapi-c / example / Dll.c में पाया जा सकता है।


bwapi-सिस


Rasta पारिस्थितिकी तंत्र में, यह एक निश्चित तरीके से संकुल को कॉल करने के लिए प्रथागत है जो मूल पुस्तकालयों से लिंक करता है। किसी भी फू-सीस पैकेज के दो महत्वपूर्ण कार्य हैं:


  • मूल लिबफू पुस्तकालय से लिंक
  • Libfoo लाइब्रेरी से फ़ंक्शन घोषणाएँ प्रदान करता है। लेकिन केवल घोषणाएँ, * -सीएस क्रेट में उच्च-स्तरीय अमूर्त प्रदान नहीं किए जाते हैं।

* -एसआईएस पैकेज को सफलतापूर्वक लिंक करने में सक्षम होने के लिए, वे इसमें स्रोत से एक देशी पुस्तकालय खोज और / या लाइब्रेरी असेंबली को एकीकृत करते हैं।


घोषणाओं को प्रदान करने के लिए * -एसआईएस पैकेज के लिए, किसी को या तो उन्हें हाथ से लिखना चाहिए या बाइंडजेन का उपयोग करके उत्पन्न करना चाहिए। फिर से बांधना। प्रयास नंबर दो =)


Bwapi-c के साथ बाइंडर बनाना अश्लील हो जाता है:


 bindgen BWAPI.h -o lib.rs \ --opaque-type ".+_" \ --blacklist-type "std.*|__.+|.+_$|Game_v(Send|Print|Draw).*|va_list|.+_t$" \ --no-layout-tests \ --no-derive-debug \ --raw-line "#![allow(improper_ctypes, non_snake_case)]" \ -- -I../submodules/bwapi-c/include sed -i -r -- 's/.+\s+(.+)_;/pub struct \1;/' lib.rs 

जहां BWAPI.h , BWAPI-C के सभी हेडर हेडर के समावेश के साथ फाइल है।


उदाहरण के लिए, पहले से ही ज्ञात कार्यों के लिए, bindgen ने निम्नलिखित घोषणाओं को उत्पन्न किया:


 extern "C" { /// BWAPIC_setGame must be called from gameInit to initialize BWAPI::BroodwarPtr pub fn BWAPIC_setGame(game: *mut Game); } extern "C" { pub fn BWAPIC_getGame() -> *mut Game; } 

2 रणनीतियां हैं: असेंबली के दौरान फ्लाई पर रिपॉजिटरी और जनरेट कोड में जनरेट कोड को स्टोर करना। इन दोनों दृष्टिकोणों के अपने फायदे और नुकसान हैं


आपका स्वागत है bwapi-sys , हमारे लक्ष्य की ओर एक और छोटा कदम।


याद है, मैंने क्रॉस-प्लेटफ़ॉर्म के बारे में बात की थी? Nlinker ने इस परियोजना में शामिल हो गए और एक मुश्किल रणनीति को लागू किया। यदि लक्ष्य विंडोज है, तो पहले से ही इकट्ठे BWAPIC को जीथब से डाउनलोड करें। और शेष लक्ष्यों के लिए, हम OpenBW के लिए स्रोतों से BWAPI-C एकत्र करते हैं (मैं आपको थोड़ी देर बाद बताऊंगा)।


bwapi-रु


अब जबकि हमारे पास बाइंडर्स हैं, हम उच्च-स्तरीय सार का वर्णन कर सकते हैं। हमारे पास काम करने के लिए 2 प्रकार हैं: शुद्ध मान और अपारदर्शी बिंदु।


शुद्ध मूल्यों के साथ, सब कुछ सरल है। उदाहरण के तौर पर रंग लें। हमें रस्ट कोड का उपयोग करने के लिए इसे सुविधाजनक बनाने की आवश्यकता है ताकि हम एक सुविधाजनक और प्राकृतिक तरीके से रंगों का उपयोग कर सकें:


 game.draw_line(CoordinateType::Screen, (10, 20), (30, 40), Color::Red); ^^^ 

तो सुविधाजनक उपयोग के लिए यह आवश्यक होगा कि C ++ से स्थिरांक के साथ रस्ट भाषा एन्यूमरेशन के लिए एक मुहावरेदार को परिभाषित करें और bwapi_sys में रूपांतरण विधियों को परिभाषित करें :: std का उपयोग करके रंग :: कन्वर्ट :: प्रकार से :


 // FFI version #[repr(C)] #[derive(Copy, Clone)] pub struct Color { pub color: ::std::os::raw::c_int, } // Idiomatic version #[derive(PartialEq, PartialOrd, Copy, Clone)] pub enum Color { Black = 0, Brown = 19, ... 

हालांकि सुविधा के लिए, आप टोकरा एनुम-आदिम-व्युत्पन्न का उपयोग कर सकते हैं


अपारदर्शी बिंदुओं के साथ, यह कठिन नहीं है। ऐसा करने के लिए, Newtype पैटर्न का उपयोग करें:


 pub struct Player(*mut sys::Player); 

यही है, प्लेयर एक निजी क्षेत्र के साथ एक निश्चित संरचना है - सी। से एक कच्चा अपारदर्शी सूचक और यहाँ आप प्लेयर का वर्णन कैसे कर सकते हैं :: रंग विधि:


 impl Player { //    Player::getColor  bwapi-sys //extern "C" { // pub fn Player_getColor(self_: *mut Player) -> Color; //} pub fn color(&self) -> Color { // bwapi_sys::Player_getColor -    BWAPI-C // self.0 -   let color = unsafe { bwapi_sys::Player_getColor(self.0) }; color.into() //  bwapi_sys::Color -> Color } } 

अब हम रस्ट में अपना पहला बॉट लिख सकते हैं!


रस्ट में बॉट लिखना


अवधारणा के प्रमाण के रूप में, बॉट एक प्रसिद्ध देश की तरह दिखेगा: इसकी सारी कार्यक्षमता श्रमिकों को काम पर रखने और खनिजों को इकट्ठा करने के लिए होगी।


उत्तर कोरिया


दक्षिण कोरिया


चलो आवश्यक gameInit और newAIModule साथ शुरू newAIModule :


 #[no_mangle] pub unsafe extern "C" fn gameInit(game: *mut void) { bwapi_sys::BWAPIC_setGame(game as *mut bwapi_sys::Game); } #[no_mangle] pub unsafe extern "C" fn newAIModule() -> *mut void { let module = ExampleAIModule { name: String::from("ExampleAIModule") }; let result = wrap_handler(Box::new(module)); result } 

#[no_mangle] C ++ में #[no_mangle] फ़ंक्शन extern "C" के समान कार्य करता है। wrap_handler अंदर, सभी प्रकार का जादू आभासी फ़ंक्शन तालिका के प्रतिस्थापन के साथ होता है और सी ++ वर्ग के रूप में प्रच्छन्न होता है।


मॉड्यूल संरचना का वर्णन C से भी अधिक सरल और सुंदर है:


 struct ExampleAIModule { name: String, } 

आँकड़े प्रस्तुत करने और आदेश देने के लिए कुछ तरीके जोड़ें:


 impl ExampleAIModule { fn draw_stat(&mut self) { let game = Game::get(); let message = format!("Frame {}", game.frame_count()); game.draw_text(CoordinateType::Screen, (10, 10), &message); } fn give_orders(&mut self) { let player = Game::get().self_player(); for unit in player.units() { match unit.get_type() { UnitType::Terran_SCV | UnitType::Zerg_Drone | UnitType::Protoss_Probe => { if !unit.is_idle() { continue; } if unit.is_carrying_gas() || unit.is_carrying_minerals() { unit.return_cargo(false); continue; } if let Some(mineral) = Game::get() .minerals() .min_by_key(|m| unit.distance_to(m)) { // WE REQUIRE MORE MINERALS unit.right_click(&mineral, false); } } UnitType::Terran_Command_Center => { unit.train(UnitType::Terran_SCV); } UnitType::Protoss_Nexus => { unit.train(UnitType::Protoss_Probe); } UnitType::Zerg_Hatchery | UnitType::Zerg_Lair | UnitType::Zerg_Hive => { unit.train(UnitType::Zerg_Drone); } _ => {} }; } } } 

उदाहरण के लिए एक वास्तविक मॉड्यूल बनने के लिए AIMODule प्रकार के लिए, आपको इसे onXXXX घटनाओं पर प्रतिक्रिया देने के लिए सिखाने की आवश्यकता है, जिसके लिए आपको EventHandler प्रकार को लागू करने की आवश्यकता है, जो C पर AIModule_vtable वर्चुअल टेबल का एक एनालॉग है:


 impl EventHandler for ExampleAIModule { fn on_start(&mut self) { Game::get().send_text(&format!("Hello from Rust! My name is {}", self.name)); } fn on_end(&mut self, _is_winner: bool) {} fn on_frame(&mut self) { self.draw_stat(); self.give_orders(); } fn on_send_text(&mut self, _text: &str) {} fn on_receive_text(&mut self, _player: &mut Player, _text: &str) {} fn on_player_left(&mut self, _player: &mut Player) {} fn on_nuke_detect(&mut self, _target: Position) {} fn on_unit_discover(&mut self, _unit: &mut Unit) {} fn on_unit_evade(&mut self, _unit: &mut Unit) {} fn on_unit_show(&mut self, _unit: &mut Unit) {} fn on_unit_hide(&mut self, _unit: &mut Unit) {} fn on_unit_create(&mut self, _unit: &mut Unit) {} fn on_unit_destroy(&mut self, _unit: &mut Unit) {} fn on_unit_morph(&mut self, _unit: &mut Unit) {} fn on_unit_renegade(&mut self, _unit: &mut Unit) {} fn on_save_game(&mut self, _game_name: &str) {} fn on_unit_complete(&mut self, _unit: &mut Unit) {} } 

मॉड्यूल बनाना और शुरू करना C के लिए जितना आसान है:


 bwapi-rs$ cargo build --example dll --target=i686-pc-windows-gnu bwapi-rs$ cp ./target/i686-pc-windows-gnu/debug/examples/dll.dll ~/Starcraft/bwapi-data/Dll.dll bwapi-rs$ cd ~/Starcraft/bwapi-data/ Starcraft$ wine bwheadless.exe -e StarCraft.exe -l bwapi-data/BWAPI.dll --headful ... ... ... 

और काम वीडियो:



क्रॉस-संकलन के बारे में थोड़ा


संक्षेप में, रस्ट सुंदर है! दो क्लिक में, आप विभिन्न प्लेटफ़ॉर्म के लिए कई टूलचिन डाल सकते हैं। विशेष रूप से, i686-pc-windows-gnu toolchain कमांड द्वारा सेट किया गया है:


 rustup target add i686-pc-windows-gnu 

आप .cargo/config प्रोजेक्ट की जड़ में कार्गो के लिए कोफ़िग भी निर्दिष्ट कर सकते हैं:


 [target.i686-pc-windows-gnu] linker = "i686-w64-mingw32-gcc" ar = "i686-w64-mingw32-ar" runner = "wine" ] [target.i686-pc-windows-gnu] linker = "i686-w64-mingw32-gcc" ar = "i686-w64-mingw32-ar" runner = "wine" 

और वह सब आपको विंडोज पर लिनक्स से अपनी जंग परियोजना को संकलित करने की आवश्यकता है।


OpenBW


ये लोग और भी आगे बढ़ गए। उन्होंने खेल एससी: बीडब्ल्यू का एक ओपन-सोर्स संस्करण लिखने का फैसला किया! और वे बहुत अच्छा करते हैं। उनका एक लक्ष्य एचडी चित्रों को लागू करना था, लेकिन एससी: रीमास्टर्ड उनके आगे हो गया = (फिलहाल, आप बॉट लिखने के लिए उनके एपीआई का उपयोग कर सकते हैं (हां, सी ++ में भी)। लेकिन सबसे आश्चर्यजनक विशेषता ब्राउज़र में सीधे रिप्ले देखने की क्षमता है


निष्कर्ष


कार्यान्वयन के दौरान एक अनसुलझी समस्या बनी रही: हम लिंक की विशिष्टता को नियंत्रित नहीं करते हैं, और साथ ही &mut और & के एक साथ अस्तित्व में होने से अपरिभाषित व्यवहार को बढ़ावा मिलेगा। मुसीबत। हॉल्ट ने मुहावरेदार बाइंडिंग को लागू करने की कोशिश की , लेकिन उनका फ्यूज थोड़ा फीका पड़ गया। साथ ही, इस समस्या को हल करने के लिए, आपको गुणात्मक रूप से C ++ API को फावड़ा करना होगा और सही ढंग से const क्वालिफायर सेट करना होगा।


मुझे वास्तव में इस परियोजना पर काम करने में बहुत मज़ा आया, मैंने and and रिप्ले देखा और वातावरण में गहराई से डूब गया। इस खेल ने एक विरासत को 믿어 믿어 믿어 믿어 믿어 छोड़ दिया। कोई गेम 수 없다 SC के साथ लोकप्रिय नहीं है: BW, और 대한민국 정치 대한민국 पर इसका प्रभाव अकल्पनीय था। कोरिया में प्रो-गेमर्स उतने ही लोकप्रिय हैं, जितने प्राइम टाइम में broadcast 배우 아마도 as कोरियन ड्रामा का प्रसारण। 한국, 한국 한국 한국 한국 한국 한국 한국 한국 한국 한국 한국 한국 한국।


लंबे समय तक जीवित StarCraft!


संदर्भ


Source: https://habr.com/ru/post/hi416743/


All Articles