<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-2845358561009098821</atom:id><lastBuildDate>Fri, 01 Nov 2024 10:42:22 +0000</lastBuildDate><category>SEngine</category><category>Искусственный интеллект</category><category>AntiChat Tetra</category><category>My programs</category><category>Графика</category><category>DirectX</category><category>Mobile games</category><category>Компьютеры</category><title>Scripter&#39;s Blog | AI programming</title><description>Artificial intelligence, C++, Delphi, DirectX</description><link>http://scr1pter.blogspot.com/</link><managingEditor>noreply@blogger.com (Scripter)</managingEditor><generator>Blogger</generator><openSearch:totalResults>41</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-7652928939035636729</guid><pubDate>Sat, 25 Jun 2016 10:42:00 +0000</pubDate><atom:updated>2016-06-25T13:42:23.902+03:00</atom:updated><title>Life is Feudal: Forest Village</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;b&gt;&lt;iframe frameborder=&quot;0&quot; height=&quot;137&quot; scrolling=&quot;no&quot; src=&quot;https://steamcommunity.com/sharedfiles/widget/709170194&quot; width=&quot;336&quot;&gt;&lt;/iframe&gt;&lt;/b&gt;&lt;br /&gt;&lt;/div&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
&lt;b&gt;Life is Feudal: Forest Village&lt;/b&gt; is a feature-rich, town building simulator strategy game with engaging survival aspects. Lead your people: a small group of refugees who were forced to start again on an unknown island.&lt;br /&gt;
&lt;br /&gt;
Terraform land and build houses, pastures, orchards, farms, windmills and many other buildings. Forage in the forest, hunt for prey, grow crops and domestic animals for food. Stock up with enough firewood, charcoal and warm clothes to survive an upcoming harsh winter. Lack of certain vitamins in villagers’ rations may lead to disease and can even totally wipe out your village!&lt;br /&gt;
&lt;br /&gt;
You can rule your village from a birdseye view or possess one of your villagers to perform tasks in a first person view to speed things up or just to wander around.&lt;br /&gt;
&lt;br /&gt;
Here some of the most exciting features:&lt;br /&gt;
&lt;b&gt;Advanced farming system.&lt;/b&gt; Different crops and fruit trees require different amount of moisture, man labour and time to ripen ready for harvest. Your pastures and hens require the attention of hunters in order to protect them from forest predators.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Real time weather and seasons system.&lt;/b&gt; Drought will force your farmers to water your crops and rainy days will force them to dig trenches. Winter will require firewood and warm clothes for your villagers.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Diseases, food rations and vitamins.&lt;/b&gt; Diseases will spread faster in cold weather, especially if your villagers lack warm clothes or their ration is too monotonous and lacks certain vitamins. Try to stock all types of food possible (meat, fish, bread, vegetables and fruits).&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Switch between birdseye and first person views.&lt;/b&gt; You can play your game in a birdseye view and issue strategic orders or you can posses a villager and play from a first person perspective performing everyday tasks along with your villagers.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Dynamic ecosystem.&lt;/b&gt; Villagers that are constantly walking in the surrounding woods will scare wildlife deeper into forest. Excessive farming or woodcutting can lead to flora and fauna extinction thus lowering the flow of meat, hides, mushrooms, berries and medical herbs to your stocks.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Terraforming and pavement.&lt;/b&gt; Terraform land around your settlement to enable construction of larger buildings. Pave roads to speed up the movement of your villagers and thus improve the economy of your settlement.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Morale and increasing population&lt;/b&gt; Keep your villagers happy and their families will grow, granted their needs are being met. :) Living in overcrowded hostels, always in need and witnessing recent deaths of neighboring villagers will reduce their morale and their productivity.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Disasters.&lt;/b&gt; Lightning will hit your tall buildings that are standing on hilltops. Tornados can leave a devastating trail of destruction across your settlement. Earthquakes can destroy your houses and even topple trees. Are your sure that you’re ready to face those challenges?&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Mod friendly.&lt;/b&gt; Our game is designed to be modder friendly. Game modders will have access to AI, animations, task and resource management, navigation, sounds and many other game systems. Those systems and assets can be completely changed through game scripts written in LUA.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
</description><link>http://scr1pter.blogspot.com/2016/06/life-is-feudal-forest-village.html</link><author>noreply@blogger.com (Scripter)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-6773682287135479784</guid><pubDate>Sun, 03 Aug 2014 00:21:00 +0000</pubDate><atom:updated>2014-08-03T04:23:09.617+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Mobile games</category><title>Balloon Shooter</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;b&gt;Balloon Shooter&lt;/b&gt; - это новая игра в жанре тайм-киллера.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Особенности проекта:&lt;/b&gt;&lt;br /&gt;
- уничтожайте шары в двух режимах игры&lt;br /&gt;
- собирайте разнообразные бонусы&lt;br /&gt;
- смена дня и ночи&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Google Play:&lt;/b&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.Scripter.BalloonShooter&quot;&gt;скачать&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Устанавливайте новые рекорды!&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://2.bp.blogspot.com/UpOckurrkvjZp3TktWan1lnptWteG_NIdb3Zjl75Q0HKOvEKcqa3VvwzVJKELqEPsDg=h900-rw&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/UpOckurrkvjZp3TktWan1lnptWteG_NIdb3Zjl75Q0HKOvEKcqa3VvwzVJKELqEPsDg=h900-rw&quot; height=&quot;225&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://1.bp.blogspot.com/DGfM5XWKtBQtzmVfTOG9eCa7TfYbsOG4o-KDOgrvs5AFjzzzpQODwJgdsJfgnbk7iA=h900-rw&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://1.bp.blogspot.com/DGfM5XWKtBQtzmVfTOG9eCa7TfYbsOG4o-KDOgrvs5AFjzzzpQODwJgdsJfgnbk7iA=h900-rw&quot; height=&quot;225&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://4.bp.blogspot.com/-H-V1KW7pODi1_uAaBP2wZEIuJOtTKYaRM1O2hXgjT9So7NO6O5WKQUj9Kt1VvZI4vk=h900-rw&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://4.bp.blogspot.com/-H-V1KW7pODi1_uAaBP2wZEIuJOtTKYaRM1O2hXgjT9So7NO6O5WKQUj9Kt1VvZI4vk=h900-rw&quot; height=&quot;225&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://2.bp.blogspot.com/IdiTjGTeKEZDZTdbChFLIyXn5K5xI3taEP8pFdqSOv1-LsyeddpBTcvOCEb97PcaCN2c=h900-rw&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/IdiTjGTeKEZDZTdbChFLIyXn5K5xI3taEP8pFdqSOv1-LsyeddpBTcvOCEb97PcaCN2c=h900-rw&quot; height=&quot;225&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;</description><link>http://scr1pter.blogspot.com/2014/08/balloon-shooter.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/UpOckurrkvjZp3TktWan1lnptWteG_NIdb3Zjl75Q0HKOvEKcqa3VvwzVJKELqEPsDg=s72-h900-c-rw" height="72" width="72"/><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-6026240197933002163</guid><pubDate>Sun, 18 Mar 2012 16:23:00 +0000</pubDate><atom:updated>2012-03-20T14:25:26.335+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Искусственный интеллект</category><title>Finite-state machine</title><description>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiN7lMo67u01kFvkCOxQwhbjCixnASyWrsh0RnIjNPPYVZCEXHfDMViJzqVJfZnkY9wuK9sGuRofvmo7hg2tQPEBeTrggBll7IFLGASYK-_I9Y7mYlGp4D_pDr-Qu2oK23df8gO_h-kQcY/s311/image002.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 193px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiN7lMo67u01kFvkCOxQwhbjCixnASyWrsh0RnIjNPPYVZCEXHfDMViJzqVJfZnkY9wuK9sGuRofvmo7hg2tQPEBeTrggBll7IFLGASYK-_I9Y7mYlGp4D_pDr-Qu2oK23df8gO_h-kQcY/s311/image002.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Конечные автоматы&lt;/span&gt; (&lt;span style=&quot;font-weight:bold;&quot;&gt;FSM&lt;/span&gt;, &lt;span style=&quot;font-weight:bold;&quot;&gt;Finite-state machine&lt;/span&gt;) - системы основанные на правилах, которые можно применять к строго ограниченному набору состояний (ситуаций). Переход из одного состояния в другое определяется структурой конечного автомата. &lt;span style=&quot;font-weight:bold;&quot;&gt;FSM&lt;/span&gt; относится к &lt;span style=&quot;font-weight:bold;&quot;&gt;рефлексивным детерминированным методам&lt;/span&gt;, а значит можно полностью предсказать реакцию поведения. И как становится ясно из определения, состояния в конечных автоматах это системы основанные на правилах (&lt;span style=&quot;font-weight:bold;&quot;&gt;RBS&lt;/span&gt;, которые мы рассмотрели в прошлой статье).&lt;br /&gt;Давайте попробуем реализовать этот &lt;span style=&quot;font-weight:bold;&quot;&gt;рефлексивный метод&lt;/span&gt; на практике. Для этого мы будем использовать все то, что проходили в прошлых уроках. Когда мы реализовывали Rule Based Systems, у нас &quot;NPС&#39;ы&quot; убегали от курсора, в этой статье мы эту задачу усложним, реализуем салки. У нас будет: &lt;div&gt;&lt;ul&gt;&lt;li&gt;определенное количество неигровых персонажей, которые будут убегать от догоняющего;&lt;/li&gt;&lt;li&gt;кто то будет являться догоняющим, но только один; &lt;/li&gt;&lt;li&gt;догоняющий может осалить убегающего и они поменяются ролями.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Немного &quot;обмусолим&quot; все в теории. Так как NPC может быть и догоняющим и убегающим, то они являются одним и тем же персонажем, просто с разным поведением. Можно было бы сделать, всего два состояния:&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;догоняющий &amp;lt;-&amp;gt; убегающий&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;но я посчитал удобнее сделать:&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;состояние инициализации догоняющего;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;состояние инициализации убегающего;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;состояние поиска ближайшего NPC и его преследование с попыткой осалить;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;состояние проверки, есть ли &quot;враг&quot; рядом, если есть убегать;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;состояние ожидания (чтобы у убегающих была фора, перед новым догоняющим).&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;center&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;Работу программы, которую мы создадим, можно посмотреть тут&lt;/span&gt;:&lt;br /&gt;&lt;iframe width=&quot;420&quot; height=&quot;315&quot; src=&quot;http://www.youtube.com/embed/zsGgpMxMdhE&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Приступим! Для начала дополним класс из прошлого урока под именем &quot;&lt;span style=&quot;font-weight:bold;&quot;&gt;CNpc&lt;/span&gt;&quot;. Мы объявим там новую переменную, которая будет обозначать догоняющий ли данный персонаж или нет.&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;class CNpc: public CPathUser&lt;br /&gt;{&lt;br /&gt;private:&lt;br /&gt;   ...&lt;br /&gt;   bool m_bCatchState;&lt;br /&gt;public:&lt;br /&gt;   void SetCatchState(bool bCatch)&lt;br /&gt;   {&lt;br /&gt;       m_bCatchState = bCatch;&lt;br /&gt;   }&lt;br /&gt;   bool IsCatcher()&lt;br /&gt;   {&lt;br /&gt;      return m_bCatchState;&lt;br /&gt;   }&lt;br /&gt;...&lt;br /&gt;};&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;Так же в классе &quot;&lt;span style=&quot;font-weight:bold;&quot;&gt;CNpc&lt;/span&gt;&quot; изменим функцию RunFromEnemy, которая отвечает за то, как персонаж будет убегать от &quot;врага&quot;. В прошлой версии NPC подходил к краю карты и останавливался, а нас сейчас это никак не устраивает, исправим это:&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;void CNpc::RunFromEnemy(vector2f EnemyPos)&lt;br /&gt;{ &lt;br /&gt;   vector2f Diff = normalize(GetPos() - EnemyPos);&lt;br /&gt;   if (GetPos() == EnemyPos)&lt;br /&gt;      Diff = vector2f(1.0f, 0.0f);&lt;br /&gt;&lt;br /&gt;   vector2f TravelPos = Diff*CHUNK_SIZE*2 + GetPos();&lt;br /&gt;   bool bGoodWay = SetWay(TravelPos);&lt;br /&gt;&lt;br /&gt;   // угол в радианах, около 11 градусов&lt;br /&gt;   float fAngle = 0.2f;&lt;br /&gt;&lt;br /&gt;   // если путь не установлен&lt;br /&gt;   while (!bGoodWay)&lt;br /&gt;   {&lt;br /&gt;      // поворачиваем вектор направления для движения&lt;br /&gt;      vector2f sDir = rotate_vector_2D(Diff, fAngle);&lt;br /&gt;      TravelPos = sDir*CHUNK_SIZE*2 + GetPos();&lt;br /&gt;&lt;br /&gt;      bGoodWay = SetWay(TravelPos);&lt;br /&gt;&lt;br /&gt;      // если путь опять не установлен&lt;br /&gt;      if (!bGoodWay)&lt;br /&gt;      {&lt;br /&gt;         // поворачиваем в другую сторону&lt;br /&gt;         sDir = rotate_vector_2D(Diff, -fAngle);&lt;br /&gt;         TravelPos = sDir*CHUNK_SIZE*2 + GetPos();&lt;br /&gt;         bGoodWay = SetWay(TravelPos);&lt;br /&gt;      }&lt;br /&gt;      // прибавляем ещё 11 градусов к углу&lt;br /&gt;      fAngle+=0.2f;&lt;br /&gt;      // если был сделан &quot;круг&quot;, а путь так и не утсановлен&lt;br /&gt;      // выходим&lt;br /&gt;      if (fAngle &amp;gt; 6.283f)&lt;br /&gt;         return;&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Теперь нам понадобится менеджер, который будет создавать, обновлять, очищать неигровых персонажей, а так же заниматься поиском ближайшего NPC, установкой догоняющего и другими общими их делами. Так как такой менеджер нам нужен один, то для реализации нам подойдет &lt;span style=&quot;font-weight:bold;&quot;&gt;singleton&lt;/span&gt; (шаблон одиночка, который гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа). Так как я пользуюсь библиотекой &lt;b&gt;Boost&lt;/b&gt;, то шаблон одиночку я возьму из него.&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;class ManagerNPC : public boost::serialization::singleton&amp;lt;ManagerNPC&gt;&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;   // функция добавления нового NPC&lt;br /&gt;   void AddNPC(CNpc* pNPC);&lt;br /&gt;   // возвращает кол-во NPSов&lt;br /&gt;   unsigned int GetCountNPC();&lt;br /&gt;   // возвращает NPC по номеру в массиве&lt;br /&gt;   CNpc* GetNPC(unsigned int ID);&lt;br /&gt;   // функция обновления всех NPSов&lt;br /&gt;   void Update(int iDeltaMilliSeconds);&lt;br /&gt;   // функция инициализации&lt;br /&gt;   void Init();&lt;br /&gt;   // функция освобождения&lt;br /&gt;   void Clear();&lt;br /&gt;   // поиск ближайшего NPC относительно заданного&lt;br /&gt;   CNpc* FindNearestNPC(CNpc* pNPC);&lt;br /&gt;   // возвращение догоняющего&lt;br /&gt;   CNpc* GetCatcher();&lt;br /&gt;   // может ли один NPC осалить другого&lt;br /&gt;   bool IsCatch(CNpc* pFrom, CNpc* pTo);&lt;br /&gt;   // установка нового догоняющего&lt;br /&gt;   void SetCatcher(CNpc* pNPC);&lt;br /&gt;protected:&lt;br /&gt;private:&lt;br /&gt;   // указатель на догоняющего&lt;br /&gt;   CNpc* m_pCatcher;&lt;br /&gt;   // массив NPCов&lt;br /&gt;   std::vector&amp;lt;CNpc*&gt; m_aNpc;&lt;br /&gt;};&lt;/pre&gt;&lt;br /&gt;Рассмотрим функции класса подробнее:&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;// функция добавления нового NPC&lt;br /&gt;void ManagerNPC::AddNPC(CNpc* pNPC)&lt;br /&gt;{&lt;br /&gt;   m_aNpc.push_back(pNPC);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// возвращает NPC по номеру в массиве&lt;br /&gt;unsigned int ManagerNPC::GetCountNPC()&lt;br /&gt;{&lt;br /&gt;   return m_aNpc.size();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// возвращает NPC по номеру в массиве&lt;br /&gt;CNpc* ManagerNPC::GetNPC(unsigned int ID)&lt;br /&gt;{&lt;br /&gt;   if (ID &amp;lt; GetCountNPC())       &lt;br /&gt;      return m_aNpc[ID];    &lt;br /&gt;   return NULL; &lt;br /&gt;}  &lt;br /&gt;&lt;br /&gt;// функция обновления всех NPSов &lt;br /&gt;void ManagerNPC::Update(int iDeltaMilliSeconds) &lt;br /&gt;{    &lt;br /&gt;   for (unsigned int i = 0; i &amp;lt; GetCountNPC(); i++)    &lt;br /&gt;   {       &lt;br /&gt;      GetNPC(i)-&amp;gt;Update(iDeltaMilliSeconds);&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// функция инициализации&lt;br /&gt;void ManagerNPC::Init()&lt;br /&gt;{&lt;br /&gt;   m_pCatcher = NULL;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// функция освобождения&lt;br /&gt;void ManagerNPC::Clear()&lt;br /&gt;{&lt;br /&gt;   for (unsigned int i = 0; i &amp;lt; m_aNpc.size(); i++)    &lt;br /&gt;   {       &lt;br /&gt;      if (m_aNpc[i])       &lt;br /&gt;      {          &lt;br /&gt;         delete m_aNpc[i];          &lt;br /&gt;         m_aNpc[i] = NULL;       &lt;br /&gt;      }    &lt;br /&gt;   }    &lt;br /&gt;   m_aNpc.clear(); &lt;br /&gt;}  &lt;br /&gt;&lt;br /&gt;// поиск ближайшего NPC относительно заданного &lt;br /&gt;CNpc* ManagerNPC::FindNearestNPC(CNpc* pNPC) &lt;br /&gt;{    &lt;br /&gt;   // если у менеджера вообще нет NPCов    &lt;br /&gt;   if (GetCountNPC() == 0)       &lt;br /&gt;      return NULL;     &lt;br /&gt;&lt;br /&gt;   // позиция NPC, относительно которой надо найти другого ближайшего    &lt;br /&gt;   vector2f Pos = pNPC-&amp;gt;GetPos();&lt;br /&gt;   // указатель на ближайшего NPC&lt;br /&gt;   CNpc* pNearestNPC = NULL;&lt;br /&gt;   // расстояние до него&lt;br /&gt;   float fMinLen = 0;&lt;br /&gt;&lt;br /&gt;   // найдем самого первого NPC, который не является заданным&lt;br /&gt;   for (unsigned int i = 0; i &amp;lt; GetCountNPC(); i++)    &lt;br /&gt;   {       &lt;br /&gt;      if (GetNPC(i) != pNPC)       &lt;br /&gt;      {          &lt;br /&gt;         // предположим, что она самый ближайший          &lt;br /&gt;         pNearestNPC = GetNPC(i);          &lt;br /&gt;         fMinLen = length(pNearestNPC-&amp;gt;GetPos() - Pos);&lt;br /&gt;         break;&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   // пройдем по всем NPCам&lt;br /&gt;   for (unsigned int i = 0; i &amp;lt; GetCountNPC(); i++)    &lt;br /&gt;   {       &lt;br /&gt;      // если это не заданный       &lt;br /&gt;      if (GetNPC(i) != pNPC)       &lt;br /&gt;      {          &lt;br /&gt;         // вычислим вектор между ними          &lt;br /&gt;         vector2f Diff = GetNPC(i)-&amp;gt;GetPos() - Pos;&lt;br /&gt;         // вычислим расстоянием между ними&lt;br /&gt;         float fLen = length(Diff);&lt;br /&gt;         // если оно меньше, прошлого&lt;br /&gt;         if (fLen &amp;lt; fMinLen)          &lt;br /&gt;         {             &lt;br /&gt;            // то на данный момент он ближайший             &lt;br /&gt;            pNearestNPC = GetNPC(i);             &lt;br /&gt;            fMinLen = fLen;          &lt;br /&gt;         }       &lt;br /&gt;      }    &lt;br /&gt;   }    &lt;br /&gt;   return pNearestNPC; &lt;br /&gt;}  &lt;br /&gt;&lt;br /&gt;// возвращение догоняющего &lt;br /&gt;CNpc* ManagerNPC::GetCatcher() &lt;br /&gt;{    &lt;br /&gt;   // так как у нас NPCы не удаляются на протяжении работы программы,    &lt;br /&gt;   // то можемпросто хранить указатель на догоняющего,    &lt;br /&gt;   // иначе бы искали в массиве NPCa с флагом догоняющего    &lt;br /&gt;   return m_pCatcher; &lt;br /&gt;}  &lt;br /&gt;&lt;br /&gt;// может ли один NPC осалить другого &lt;br /&gt;bool ManagerNPC::IsCatch(CNpc* pFrom, CNpc* pTo) &lt;br /&gt;{    &lt;br /&gt;   // вычислим расстояние между ними и сверим его с расстоянием,     &lt;br /&gt;   // при котором можно осалить    &lt;br /&gt;   // #define CATCH_RADIUS CHUNK_SIZE*1.1f    &lt;br /&gt;   if (length(pFrom-&amp;gt;GetPos()-pTo-&amp;gt;GetPos()) &amp;gt; CATCH_RADIUS)&lt;br /&gt;      return false;&lt;br /&gt;   return true;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// установка нового догоняющего&lt;br /&gt;void ManagerNPC::SetCatcher(CNpc* pNPC)&lt;br /&gt;{&lt;br /&gt;   // если догоняющий уже есть&lt;br /&gt;   if (m_pCatcher)&lt;br /&gt;      // меняем ему флаг&lt;br /&gt;      m_pCatcher-&amp;gt;SetCatchState(false);&lt;br /&gt;   // устанавливаем флаг новому догоняющему&lt;br /&gt;   pNPC-&amp;gt;SetCatchState(true);&lt;br /&gt;   // запоминаем нового догоняющего&lt;br /&gt;   m_pCatcher = pNPC;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Вызов функций моего класса одиночки выглядит не очень приятно:&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;ManagerNPC::get_mutable_instance().Init();&lt;br /&gt;// поэтому я заменю её с помощью define&lt;br /&gt;#define MANAGER_NPC ManagerNPC::get_mutable_instance()&lt;br /&gt;// а теперь это выглядит так&lt;br /&gt;MANAGER_NPC.Init();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Так, у нас есть класс NPC и класс менеджера неигровых персонажей. Так как вся логика ИИ у нас будет в скриптах (как и в прошлом уроке), то расшарим наши функции для LUA.&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;CNpc* FindNearestNPC(CNpc* pNPC)&lt;br /&gt;{&lt;br /&gt;   return MANAGER_NPC.FindNearestNPC(pNPC);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;CNpc* GetCatcher()&lt;br /&gt;{&lt;br /&gt;   return MANAGER_NPC.GetCatcher();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;bool IsCatch(CNpc* pFrom, CNpc* pTo)&lt;br /&gt;{&lt;br /&gt;   return MANAGER_NPC.IsCatch(pFrom, pTo);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void SetCatcher(CNpc* pNPC)&lt;br /&gt;{&lt;br /&gt;   return MANAGER_NPC.SetCatcher(pNPC);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void LuaShareNPC(lua_State* pLua)&lt;br /&gt;{&lt;br /&gt;   using namespace luabind;&lt;br /&gt;   module(pLua)&lt;br /&gt;   [&lt;br /&gt;      class_&amp;lt;CNpc&gt;(&quot;Npc&quot;)&lt;br /&gt;      .def(&quot;RunFromEnemy&quot;, &amp;amp;CNpc::RunFromEnemy )&lt;br /&gt;      .def(&quot;SetScriptName&quot;, &amp;amp;CNpc::SetScriptName )&lt;br /&gt;      .def(&quot;SetPauseTime&quot;, &amp;amp;CNpc::SetPauseTime )&lt;br /&gt;      .def(&quot;IsSeeEnemy&quot;, &amp;amp;CNpc::IsSeeEnemy )&lt;br /&gt;      .def(&quot;SetWay&quot;, &amp;amp;CNpc::SetWay )&lt;br /&gt;      .def(&quot;SetSpeed&quot;, &amp;amp;CNpc::SetSpeed )&lt;br /&gt;      .def(&quot;GetPos&quot;, &amp;amp;CNpc::GetPos ),&lt;br /&gt;&lt;br /&gt;      class_&amp;lt;vector2f&gt;(&quot;vec2&quot;),&lt;br /&gt;&lt;br /&gt;      def(&quot;FindNearestNPC&quot;, &amp;amp;FindNearestNPC),&lt;br /&gt;      def(&quot;GetCatcher&quot;, &amp;amp;GetCatcher),&lt;br /&gt;      def(&quot;IsCatch&quot;, &amp;amp;IsCatch),&lt;br /&gt;      def(&quot;SetCatcher&quot;, &amp;amp;SetCatcher)&lt;br /&gt;   ];&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Все! Теперь у нас есть все, чтобы приступить к реализации игровой логики для FSM.&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;-- состояние инициализации убегающего&lt;br /&gt;function Escapee_Init(self)&lt;br /&gt;   -- устанавливаем паузу между апдейтами&lt;br /&gt;   self:SetPauseTime(300)&lt;br /&gt;   -- устанавливаем скорость&lt;br /&gt;   self:SetSpeed(0.1)&lt;br /&gt;   -- устанавливаем имя скрипта&lt;br /&gt;   self:SetScriptName(&quot;Escapee_Go&quot;)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;-- состояние инициализации догоняющего&lt;br /&gt;function Catcher_Init(self)&lt;br /&gt;   -- устанавливаем паузу между апдейтами&lt;br /&gt;   self:SetPauseTime(3000)&lt;br /&gt;   -- устанавливаем скорость&lt;br /&gt;   self:SetSpeed(0.12)&lt;br /&gt;   -- устанавливаем имя скрипта&lt;br /&gt;   self:SetScriptName(&quot;Catcher_Wait&quot;)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;-- состояние проверки, есть ли &quot;враг&quot; рядом, если есть убегать&lt;br /&gt;function Escapee_Go(self)&lt;br /&gt;   -- получаем догоняющего&lt;br /&gt;   catcher = GetCatcher()&lt;br /&gt;   -- если он есть&lt;br /&gt;   if catcher ~= nil then&lt;br /&gt;      -- если мы его видим&lt;br /&gt;      if self:IsSeeEnemy(catcher:GetPos()) then&lt;br /&gt;         -- убегаем от него&lt;br /&gt;         self:RunFromEnemy(catcher:GetPos());&lt;br /&gt;      end&lt;br /&gt;   end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;-- состояние поиска ближайшего NPC и его преследование с попыткой осалить&lt;br /&gt;function Catcher_Go(self)&lt;br /&gt;   -- ищем ближайшего&lt;br /&gt;   npc = FindNearestNPC(self)&lt;br /&gt;   -- если он есть&lt;br /&gt;   if npc ~= nil then&lt;br /&gt;      -- двигаемся к нему&lt;br /&gt;      self:SetWay(npc:GetPos())&lt;br /&gt;      -- если осалили&lt;br /&gt;      if IsCatch(self, npc) then&lt;br /&gt;         -- ставим себе новый скрипт&lt;br /&gt;         self:SetScriptName(&quot;Escapee_Init&quot;)&lt;br /&gt;         -- ставим ему новый скрипт&lt;br /&gt;         npc:SetScriptName(&quot;Catcher_Init&quot;)&lt;br /&gt;         -- указываем нового догоняющего&lt;br /&gt;         SetCatcher(npc)&lt;br /&gt;      end&lt;br /&gt;   end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;-- состояние ожидания (чтобы у убегающих была фора, перед новым догоняющим)&lt;br /&gt;function Catcher_Wait(self)&lt;br /&gt;   -- устанавливаем паузу между апдейтами&lt;br /&gt;   self:SetPauseTime(100)&lt;br /&gt;   -- устанавливаем имя скрипта&lt;br /&gt;   self:SetScriptName(&quot;Catcher_Go&quot;)&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;Нам осталось использовать все то, что мы написали выше и увидеть результат.&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;&lt;br /&gt;// НАЧАЛО ПРОГРАММЫ &lt;br /&gt;// расшариваем функции для LUA&lt;br /&gt;LuaShareNPC(myLua);&lt;br /&gt;// инициализация менеджера NPC&lt;br /&gt;MANAGER_NPC.Init();&lt;br /&gt;&lt;br /&gt;// создаем убегающих&lt;br /&gt;for (int x = 0; x &amp;lt; 20; x++) &lt;br /&gt;{    &lt;br /&gt;   CNpc* pNewNPC = new CNpc();    &lt;br /&gt;   int randomX = (int)rand()%(WIDTH);    &lt;br /&gt;   int randomY = (int)rand()%(HEIGHT);&lt;br /&gt;   pNewNPC-&amp;gt;SetPos(vector2f(float(randomX),float(randomY)));&lt;br /&gt;   pNewNPC-&amp;gt;SetScriptName(&quot;Escapee_Init&quot;);&lt;br /&gt;   MANAGER_NPC.AddNPC(pNewNPC);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// среди них случайным образом одного делаем догоняющим&lt;br /&gt;int RandomID = (int)rand()%(MANAGER_NPC.GetCountNPC()-1);&lt;br /&gt;CNpc* RandomNPC = MANAGER_NPC.GetNPC(RandomID);&lt;br /&gt;MANAGER_NPC.SetCatcher(RandomNPC);&lt;br /&gt;RandomNPC-&amp;gt;SetScriptName(&quot;Catcher_Init&quot;);&lt;br /&gt;...&lt;br /&gt;// ЦИКЛ&lt;br /&gt;// апдейтим &lt;br /&gt;MANAGER_NPC.Update(int(dwDT));&lt;br /&gt;...&lt;br /&gt;// КОНЕЦ ПРОГРАММЫ &lt;br /&gt;// освобождение менеджера NPC&lt;br /&gt;MANAGER_NPC.Clear();&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Конечные автоматы (finite state machine) - технология, которая несмотря на свою простоту (а мы в этом убедились) оказывается очень мощной и применяется во многих играх.&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;a href=&quot;http://scr1pter.blogspot.com/2011/06/rule-based-systems.html&quot;&gt;&amp;lt;&amp;lt; Назад&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/p/blog-page.html&quot;&gt;| Оглавление |&lt;/a&gt;   &lt;a href=&quot;&quot;&gt;Вперед &amp;gt;&amp;gt;&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;</description><link>http://scr1pter.blogspot.com/2012/03/finite-state-machine.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiN7lMo67u01kFvkCOxQwhbjCixnASyWrsh0RnIjNPPYVZCEXHfDMViJzqVJfZnkY9wuK9sGuRofvmo7hg2tQPEBeTrggBll7IFLGASYK-_I9Y7mYlGp4D_pDr-Qu2oK23df8gO_h-kQcY/s72-c/image002.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-2952483278676339920</guid><pubDate>Tue, 07 Jun 2011 17:28:00 +0000</pubDate><atom:updated>2012-03-20T13:50:30.916+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Искусственный интеллект</category><title>Rule Based Systems</title><description>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgG1dWXboBFR0XykGXw11i8XpR4IoMN_HOB8SPYtRAXdKzh1DKbjBcXmJnwFcZeoT0VJZ_O7wuD4AWOWyQOjTZZAGjvrvcUSXXw_6vwDVQQt61hjEwSSp82JnPboaz8TLsDeArQdwluXis/s1600/30193_Rules.gif&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 193px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgG1dWXboBFR0XykGXw11i8XpR4IoMN_HOB8SPYtRAXdKzh1DKbjBcXmJnwFcZeoT0VJZ_O7wuD4AWOWyQOjTZZAGjvrvcUSXXw_6vwDVQQt61hjEwSSp82JnPboaz8TLsDeArQdwluXis/s200/30193_Rules.gif&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5615535416552302002&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Rule Based Systems&lt;/span&gt; (&lt;b&gt;cистема, основанная на правилах&lt;/b&gt;) - это система контроля не игровых персонажей (Non-Player Character — NPC), состоящая из ситуаций и действий (ЕСЛИ - ТО). RBS относится к &lt;b&gt;рефлексивным детерминированным методам&lt;/b&gt;, а значит можно полностью предсказать реакцию поведения. Это, пожалуй, самый простой способ реализации &lt;b&gt;ИИ в играх&lt;/b&gt;. Так как он прост, то и задача, с которой он должен справляться, тоже должна быть проста.&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;При реализации &lt;b&gt;AI&lt;/b&gt; в этой статье (и в последующих тоже), я буду использовать скрипты на LUA. Очень часто приходится корректировать/менять/улучшать &lt;b&gt;поведение NPC&lt;/b&gt;, поэтому использование скриптового языка LUA тут очень кстати.&lt;/div&gt;&lt;br /&gt;Давайте сформулируем очень простую задачу:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Нашими NPC будут квадратики.&lt;/li&gt;&lt;li&gt;Врагом NPC будет курсор.&lt;/li&gt;&lt;li&gt;Как только враг будет в поле зрения, NPC будет убегать от него, пока не упрется в преграду.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;center&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;Работу программы, которую мы создадим, можно посмотреть тут:&lt;/span&gt;&lt;/center&gt;&lt;br /&gt;&lt;center&gt;&lt;iframe width=&quot;425&quot; height=&quot;349&quot; src=&quot;http://www.youtube.com/embed/8swIvYHTtKU&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Перейдем к реализации. Так как мы будем использовать LUA для скриптинга, то давайте с ним ознакомимся. Его исходники можно скачать &lt;a href=&quot;www.lua.org&quot;&gt;тут&lt;/a&gt;, так же нам потребуется &lt;a href=&quot;http://www.rasterbar.com/products/luabind.html&quot;&gt;LuaBind&lt;/a&gt;. Я не буду рассказывать, как их собирать и подключать, это можно найти на других сайтах, поэтому перейдем сразу к делу.&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;&lt;br /&gt;   // создаем состояние lua&lt;br /&gt;   lua_State* pLua = lua_open();&lt;br /&gt;   // инициализация luabind&lt;br /&gt;   luabind::open(pLua);&lt;br /&gt;   // функция, которая открывает все стандартные библиотеки&lt;br /&gt;   luaL_openlibs(pLua);&lt;br /&gt;   // если ошибка при создании&lt;br /&gt;   if (NULL == pLua)  &lt;br /&gt;   {  &lt;br /&gt;      printf(&quot;Error Initializing lua\n&quot;);  &lt;br /&gt;      return;  &lt;br /&gt;   } &lt;br /&gt;   // загружает указанный файл&lt;br /&gt;   luaL_dofile(pLua, &quot;ai.lua&quot;);&lt;br /&gt;   // эта функция расшаривает функции и классы С++,&lt;br /&gt;   // которые мы будем использовать в скрипте&lt;br /&gt;   LuaShareNPC(pLua);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Функцию &quot;LuaShareNPC&quot;, которую мы использовали выше, мы рассмотрим позже. Так же не забудьте в конце программы, использовать функцию, которая закроет состояние lua:&lt;/div&gt;&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;lua_close(pLua);&lt;/pre&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Создадим новый класс &quot;&lt;span style=&quot;font-weight:bold;&quot;&gt;CNpc&lt;/span&gt;&quot;, который мы унаследуем от &quot;&lt;span style=&quot;font-weight:bold;&quot;&gt;CPathUser&lt;/span&gt;&quot; (этот класс отвечает за перемещение, смотрите его реализацию в предыдущих статьях).&lt;/div&gt;&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;class CNpc: public CPathUser&lt;br /&gt;{&lt;br /&gt;private:&lt;br /&gt;   // необходимое время для паузы&lt;br /&gt;   int m_iPouseNeed;&lt;br /&gt;   // сколько времени уже прошло с последнего апдейта&lt;br /&gt;   int m_iPouseNow;&lt;br /&gt;   // имя скрипта&lt;br /&gt;   std::string m_sScriptName;&lt;br /&gt;public:&lt;br /&gt;   CNpc(void); &lt;br /&gt;   ~CNpc(void);&lt;br /&gt;   // установка имени скрипта&lt;br /&gt;   void SetScriptName(std::string sScriptName);&lt;br /&gt;   // установка времени для паузы между обновлением AI&lt;br /&gt;   void SetPauseTime(int iMilliSecond);&lt;br /&gt;   // видим ли врага&lt;br /&gt;   bool IsSeeEnemy(vector2f Target);&lt;br /&gt;   // рассчитываем путь, чтобы убежать от врага&lt;br /&gt;   void RunFromEnemy(vector2f EnemyPos);&lt;br /&gt;   // функция обновления&lt;br /&gt;   void Update(int iDeltaMilliSeconds);&lt;br /&gt;};&lt;/pre&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Конструктор, деструктор, установка времени для паузы и имени скрипта.&lt;/div&gt;&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;CNpc::CNpc()&lt;br /&gt;{&lt;br /&gt;   m_iPouseNow = 0;&lt;br /&gt;   m_iPouseNeed = 1000;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;CNpc::~CNpc()&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void CNpc::SetPauseTime(int iMilliSecond)&lt;br /&gt;{&lt;br /&gt;   m_iPouseNeed = iMilliSecond;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void CNpc::SetScriptName(std::string sScriptName)&lt;br /&gt;{&lt;br /&gt;   m_sScriptName = sScriptName;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Следующая функция возвращает true, если враг виден. VIEW_RADIUS - это радиус взора нашего NPC, устанавливаем значение этого параметра по своему желанию (у меня он равен 200).&lt;/div&gt;&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;bool CNpc::IsSeeEnemy(vector2f EnemyPos) &lt;br /&gt;{ &lt;br /&gt;   vector2f Diff = GetPos() - EnemyPos;&lt;br /&gt;   float Len = length(Diff);  &lt;br /&gt;   if (Len &lt; VIEW_RADIUS)&lt;br /&gt;      return true;&lt;br /&gt;   return false;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Функция задания нового пути, следуя по которому, мы будем убегать от врага.&lt;/div&gt;&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;void CNpc::RunFromEnemy(vector2f EnemyPos) &lt;br /&gt;{ &lt;br /&gt;   vector2f Diff = normalize(GetPos() - EnemyPos);&lt;br /&gt;   SetWay(Diff*CHUNK_SIZE*2 + GetPos());&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Функция обновления нашего NPC. Обратите внимание, что скрипт срабатывает не каждый кадр, а в зависимости от параметра m_iPouseNeed, значение которому мы устанавливаем опять же по желанию (у меня это 300 миллисекунд). Таким способом мы уменьшаем нагрузку на наш компьютер и создаем иллюзию того, что NPC &quot;думает&quot; перед принятием решения. При такой реализации нам придется функцию передвижения использовать не в скрипте (иначе он бы у нас ходил раз в 300 миллисекунд).&lt;/div&gt;&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;void CNpc::Update(int iDeltaMilliSeconds)&lt;br /&gt;{&lt;br /&gt;   m_iPouseNow += iDeltaMilliSeconds;&lt;br /&gt;   if (m_iPouseNow &gt; m_iPouseNeed)&lt;br /&gt;   {&lt;br /&gt;      if (myLua)&lt;br /&gt;         // вызываем функцию из скрипта&lt;br /&gt;         luabind::call_function&lt; void &gt;(myLua, m_sScriptName.c_str(), this, CurPos);&lt;br /&gt;         m_iPouseNow = 0;&lt;br /&gt;   }&lt;br /&gt;   // передвигаемся&lt;br /&gt;   Move(iDeltaMilliSeconds);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Давайте рассмотрим функцию, которая расшаривает классы и функции для их использования в скриптах. Так как задача у нас простая, то нам потребуется всего две функции класса &quot;CNpc&quot;, и сам класс &quot;vector2f&quot;.&lt;/div&gt;&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;&lt;br /&gt;void LuaShareNPC(lua_State* pLua)&lt;br /&gt;{&lt;br /&gt;   using namespace luabind;&lt;br /&gt;   module(pLua)&lt;br /&gt;   [&lt;br /&gt;      class_&lt; CNpc &gt;(&quot;Npc&quot;)&lt;br /&gt;      .def(&quot;RunFromEnemy&quot;, &amp;CNpc::RunFromEnemy )&lt;br /&gt;      .def(&quot;IsSeeEnemy&quot;, &amp;CNpc::IsSeeEnemy ),&lt;br /&gt;&lt;br /&gt;      class_&lt; vector2f &gt;(&quot;vec2&quot;)&lt;br /&gt;   ];&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;И наконец, наш скрипт. Он очень прост: если мы видим врага (ситуация), находим новый путь (действие), чтобы от него удалиться, в противном случае ничего не делаем.&lt;/div&gt;&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;&lt;br /&gt;function Update(npc, enemy)&lt;br /&gt;   if npc:IsSeeEnemy(enemy) then&lt;br /&gt;      npc:RunFromEnemy(enemy)&lt;br /&gt;   end&lt;br /&gt;end&lt;/pre&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Мы убедились, что это достаточно примитивный и простой способ для &lt;span style=&quot;font-weight:bold;&quot;&gt;организации простого ИИ&lt;/span&gt; в играх. При большом объеме поставленных задач для ИИ, этот способ крайне неэффективен. В нашем примере всего одно условие и одно действие, как бы одна частичка (по поставленным изначально задачам, нам больше и не надо). При объединении этих частиц получается - &lt;span style=&quot;font-weight:bold;&quot;&gt;автоматизированная система, основанная на правилах&lt;/span&gt; (проверках). Как только одно из условий этой цепи возвращает истину, выполняется его действие и выход из системы.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;a href=&quot;http://scr1pter.blogspot.com/2011/06/blog-post.html&quot;&gt;&amp;lt;&amp;lt; Назад&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/p/blog-page.html&quot;&gt;| Оглавление |&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/2012/03/finite-state-machine.html&quot;&gt;Вперед &amp;gt;&amp;gt;&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;</description><link>http://scr1pter.blogspot.com/2011/06/rule-based-systems.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgG1dWXboBFR0XykGXw11i8XpR4IoMN_HOB8SPYtRAXdKzh1DKbjBcXmJnwFcZeoT0VJZ_O7wuD4AWOWyQOjTZZAGjvrvcUSXXw_6vwDVQQt61hjEwSSp82JnPboaz8TLsDeArQdwluXis/s72-c/30193_Rules.gif" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-5234717916788556627</guid><pubDate>Wed, 01 Jun 2011 20:55:00 +0000</pubDate><atom:updated>2011-06-08T00:57:50.561+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Искусственный интеллект</category><title>Очередь на поиск пути</title><description>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiII7QmET3MJ4kSq296HIwqtPcF6DOKArsMA-aVFloBtJ-VMxxS51uQm5sFP1j8U4ZGXdcnxxR-Qy_sHxGkFV1cbfS5Nx_3T5TbRopptULUL2ENSqwuMkd4Qq-_VQCw3eMN7wGOk1gwwwc/s1600/running-marathon.gif&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 160px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiII7QmET3MJ4kSq296HIwqtPcF6DOKArsMA-aVFloBtJ-VMxxS51uQm5sFP1j8U4ZGXdcnxxR-Qy_sHxGkFV1cbfS5Nx_3T5TbRopptULUL2ENSqwuMkd4Qq-_VQCw3eMN7wGOk1gwwwc/s200/running-marathon.gif&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5613357947615114626&quot; /&gt;&lt;/a&gt;&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: justify;&quot;&gt;&lt;b&gt;Очередь на поиск путей&lt;/b&gt; позволит избежать случая, когда за один кадр ищется путь для большого количества объектов, что может вызвать лаг. Эта статья будет теоретической по двум причинам. Во-первых, реализация этого метода очень проста. А во-вторых, &lt;b&gt;алгоритм нахождения пути&lt;/b&gt;, который мы разобрали, дает задержку в одну секунду при одновременном поиске 10000 путей за раз. Даже если у нас будет десять тысяч солдат в игре, то вряд ли мы ощутим такой лаг, так как они будут &lt;b&gt;искать путь&lt;/b&gt; в разное время. Но все же давайте рассмотрим такой случай.&lt;/p&gt;&lt;p class=&quot;MsoNormal&quot; style=&quot;text-align: justify;&quot;&gt;&lt;/p&gt;&lt;p class=&quot;MsoNormal&quot;&gt;Чтобы избежать такой задержки, мы создадим &lt;b&gt;очередь на поиск путей&lt;/b&gt; в виде массива. Каждый объект, которому нужно будет получить новый путь, будет записываться в эту очередь. Теперь нам надо каждый кадр брать часть запросов (или все запросы, если их мало) и находить для них путь. Но сколько запросов брать для обработки? Я бы не советовал указывать какое-то конкретное число и не стал бы распределять это количество в зависимости от &lt;span lang=&quot;EN-US&quot; style=&quot;mso-ansi-language:EN-US&quot;&gt;FPS&lt;/span&gt;. «Тогда как?» - спросите вы (а если не спросите, то можете двигаться к следующей статье). А все просто, отведем, скажем, время в 100-200 миллисекунд на эту процедуру, после каждого &lt;b&gt;найденного пути&lt;/b&gt; проверяем общее потраченное время за этот кадр, и если оно больше нашего заранее определенного, то продолжаем работу программы, а другие «счастливчики» будут ждать следующий кадр.&lt;/p&gt;&lt;p class=&quot;MsoNormal&quot;&gt;По своему опыту скажу честно, такого мне делать не приходилось, но если вам это все-таки понадобится, то не забудьте удалять из очереди запросы тех объектов, которые, скажем, погибли.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;a href=&quot;http://scr1pter.blogspot.com/2011/05/blog-post_31.html&quot;&gt;&amp;lt;&amp;lt; Назад&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/p/blog-page.html&quot;&gt;| Оглавление |&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/2011/06/rule-based-systems.html&quot;&gt;Вперед &amp;gt;&amp;gt;&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;</description><link>http://scr1pter.blogspot.com/2011/06/blog-post.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiII7QmET3MJ4kSq296HIwqtPcF6DOKArsMA-aVFloBtJ-VMxxS51uQm5sFP1j8U4ZGXdcnxxR-Qy_sHxGkFV1cbfS5Nx_3T5TbRopptULUL2ENSqwuMkd4Qq-_VQCw3eMN7wGOk1gwwwc/s72-c/running-marathon.gif" height="72" width="72"/><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-8542383798165521352</guid><pubDate>Tue, 31 May 2011 16:32:00 +0000</pubDate><atom:updated>2012-03-20T13:29:16.931+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Искусственный интеллект</category><title>Передвижение по точкам</title><description>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMbM9_yvkIDQIzzdBBvrAfvp2DeHpcfDg6keCa7-8bL9B9DIZC8iiKHl7-Dnk4RwHdJHwJgOjzaHbjKtHUQGl4GJc-ay9JLvuHFSw5ZeRnEGQSd78BSz8WPx9WBskeSGGOJYaLvFqX6YE/s1600/running1.gif&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 160px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMbM9_yvkIDQIzzdBBvrAfvp2DeHpcfDg6keCa7-8bL9B9DIZC8iiKHl7-Dnk4RwHdJHwJgOjzaHbjKtHUQGl4GJc-ay9JLvuHFSw5ZeRnEGQSd78BSz8WPx9WBskeSGGOJYaLvFqX6YE/s200/running1.gif&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5612919003653316674&quot; /&gt;&lt;/a&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Если у нас есть &lt;b&gt;путь из одной точки в другую&lt;/b&gt;, который мы нашли с помощью &lt;b&gt;алгоритма А-звездочка&lt;/b&gt; (смотрите в предыдущих статьях), то почему бы нам не реализовать &lt;b&gt;перемещение&lt;/b&gt; по этому пути?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Чтобы &lt;b&gt;найти путь&lt;/b&gt;, который прошел наш объект, нам надо умножить потраченное время на скорость объекта. Скорость объекта мы будем задавать сами, а время будем вычислять разницей времени между кадрами.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Получив пройденный путь, мы его будем вычитать из нашего общего пути. Таким образом, в конце наш путь будет состоять из одной точки, конечной, куда мы и должны были добраться. Плюсы этого метода:&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;ul&gt;&lt;li&gt;первая точка будет являться положением объекта в пространстве;&lt;/li&gt;&lt;li&gt;пройденные точки будут удаляться&lt;/li&gt;&lt;/ul&gt;Для удобства я удалил ранее созданную структуру &quot;&lt;b&gt;MapPoint&lt;/b&gt;&quot; и заменил её на vector2f. Чтобы использовать vector2f, потребуется подключить &lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: arial, helvetica, sans-serif; line-height: 18px; &quot;&gt;&lt;a href=&quot;http://cmldev.net/&quot;&gt;Configurable Math Library&lt;/a&gt;. Как видно из названия это двухмерный вектор, где каждый элемент имеет тип float.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: arial, helvetica, sans-serif; line-height: 18px; &quot;&gt;&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;Работу программы, которую мы создадим, можно посмотреть тут&lt;/span&gt;:&lt;br /&gt;&lt;iframe width=&quot;425&quot; height=&quot;349&quot; src=&quot;http://www.youtube.com/embed/7Y7J-1nzITI&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;Давайте взглянем на наш класс, который сможет передвигаться.&lt;pre class=&quot;brush: cpp&quot;&gt;class CPathUser&lt;br /&gt;{&lt;br /&gt;private:&lt;br /&gt;  // скорость&lt;br /&gt;  float m_Speed;&lt;br /&gt;  // вектор наших точек пути&lt;br /&gt;  std::vector&lt; vector2f &gt; m_Way;&lt;br /&gt;public:&lt;br /&gt;  CPathUser(void);&lt;br /&gt;  ~CPathUser(void);&lt;br /&gt;  // установка скорости&lt;br /&gt;  void SetSpeed(float Speed);&lt;br /&gt;  // прошли ли мы заданный путь&lt;br /&gt;  bool IsWayFinished();&lt;br /&gt;  // перемещение&lt;br /&gt;  void Move(int iDeltaMilliSeconds);&lt;br /&gt;  // возвращение позиции&lt;br /&gt;  vector2f GetPos();&lt;br /&gt;  // установка позиции&lt;br /&gt;  void SetPos(vector2f Pos);&lt;br /&gt;  // установка пути&lt;br /&gt;  bool SetWay(vector2f EndPos);&lt;br /&gt;};&lt;/pre&gt;Предлагаю взглянуть на код примитивный функций, который я не буду комментировать - тут и так все ясно.&lt;pre class=&quot;brush: cpp&quot;&gt;CPathUser::CPathUser()&lt;br /&gt;{&lt;br /&gt;   m_Speed = 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;CPathUser::~CPathUser()&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void CPathUser::SetSpeed(float Speed)&lt;br /&gt;{&lt;br /&gt;   m_Speed = Speed;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;bool CPathUser::IsWayFinished() &lt;br /&gt;{ &lt;br /&gt;   if (m_Way.size() &gt; 1)&lt;br /&gt;      return false;&lt;br /&gt;   return true;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void CPathUser::SetPos(vector2f Pos)&lt;br /&gt;{&lt;br /&gt;   m_Way.clear();&lt;br /&gt;   m_Way.push_back(Pos);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;vector2f CPathUser::GetPos()&lt;br /&gt;{&lt;br /&gt;   return m_Way[0];&lt;br /&gt;}&lt;/pre&gt;Рассмотрим детально нашу &lt;span style=&quot;font-weight:bold;&quot;&gt;функцию передвижения&lt;/span&gt;.&lt;pre class=&quot;brush: cpp&quot;&gt;void CPathUser::Move(int iDeltaMilliSeconds)&lt;br /&gt;{&lt;br /&gt;   // если мы уже прошли путь выходим&lt;br /&gt;   if (IsWayFinished())&lt;br /&gt;      return;&lt;br /&gt;   // находим пройденный путь&lt;br /&gt;   float WayLen = iDeltaMilliSeconds * m_Speed;&lt;br /&gt;   // пока у нас есть точки, между которыми можно перемещаться&lt;br /&gt;   while(m_Way.size() &gt; 1)&lt;br /&gt;   {&lt;br /&gt;      // находим вектор между двумя точками&lt;br /&gt;      vector2f vDir = m_Way[1] - m_Way[0];&lt;br /&gt;      // находим длину полученного вектора&lt;br /&gt;      float fLen = vDir.length();&lt;br /&gt;      // находим разницу длины между точками &lt;br /&gt;      // и длины пути, который мы успели пройти&lt;br /&gt;      float fDelta = fLen - WayLen;&lt;br /&gt;      // если разница отрицательная&lt;br /&gt;      if (fDelta &lt;= 0)&lt;br /&gt;      {&lt;br /&gt;         // отнимаем от пройденной длины, длину между точками&lt;br /&gt;         WayLen = abs(fDelta);&lt;br /&gt;         // удаляем пройденную точку&lt;br /&gt;         m_Way.erase(m_Way.begin());&lt;br /&gt;      }&lt;br /&gt;      // если положительная&lt;br /&gt;      else&lt;br /&gt;      { &lt;br /&gt;         // так как мы где то между точками&lt;br /&gt;         // найдем наше местоположение&lt;br /&gt;         vector2f NewPos = m_Way[0] + vDir.normalize()*WayLen;&lt;br /&gt;         // сохраним его в первую точку пути&lt;br /&gt;         m_Way[0] = NewPos;&lt;br /&gt;         return;&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Функция задания нового маршрута. Мы будем сохранять положение, в котором находимся, и потом его опять добавлять, так как при расчете нового пути, наша первая точка изменится на центр ближайшего квадрата. Поэтому нам ещё придется менять первую точку пути (которая потом окажется второй, при вставке сохраненной точки), чтобы бы мы правильно передвинулись в нужный центр квадрата.&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;bool CPathUser::SetWay(vector2f EndPos) &lt;br /&gt;{ &lt;br /&gt;   // так как мы сейчас можем находиться между двумя &lt;br /&gt;   // точками, сохраним наше местоположение&lt;br /&gt;   vector2f PosNow = m_Way[0];&lt;br /&gt;   // рассчитываем новый путь&lt;br /&gt;   bool ValidWay = Pathfinding.GetWay(m_Way[0], EndPos, &amp;m_Way);&lt;br /&gt;   // если путь не найден&lt;br /&gt;   if (!ValidWay)&lt;br /&gt;   {&lt;br /&gt;      // добавляем точку, в которой мы сейчас&lt;br /&gt;      // находимся, так как путь очищен&lt;br /&gt;      m_Way.push_back(PosNow);&lt;br /&gt;      return false;&lt;br /&gt;   }&lt;br /&gt;   // находим направление движения&lt;br /&gt;   vector2f Diff = m_Way[1] - m_Way[0];&lt;br /&gt;   if (Diff[0])&lt;br /&gt;      Diff[0] = Diff[0]/abs(Diff[0]);&lt;br /&gt;   if (Diff[1])&lt;br /&gt;      Diff[1] = Diff[1]/abs(Diff[1]);&lt;br /&gt;&lt;br /&gt;   // правим первую точку пути для правильного движения&lt;br /&gt;   m_Way[0]= m_Way[0]+Diff*20;&lt;br /&gt; &lt;br /&gt;   // вставляем сохраненную точку, где мы сейчас есть&lt;br /&gt;   m_Way.insert(m_Way.begin(), PosNow);&lt;br /&gt;   return true;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;a href=&quot;http://scr1pter.blogspot.com/2011/05/blog-post_24.html&quot;&gt;&amp;lt;&amp;lt; Назад&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/p/blog-page.html&quot;&gt;| Оглавление |&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/2011/06/blog-post.html&quot;&gt;Вперед &amp;gt;&amp;gt;&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;</description><link>http://scr1pter.blogspot.com/2011/05/blog-post_31.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMbM9_yvkIDQIzzdBBvrAfvp2DeHpcfDg6keCa7-8bL9B9DIZC8iiKHl7-Dnk4RwHdJHwJgOjzaHbjKtHUQGl4GJc-ay9JLvuHFSw5ZeRnEGQSd78BSz8WPx9WBskeSGGOJYaLvFqX6YE/s72-c/running1.gif" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-7853198860208936871</guid><pubDate>Tue, 24 May 2011 14:01:00 +0000</pubDate><atom:updated>2011-05-31T21:28:36.693+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Искусственный интеллект</category><title>Оптимизация найденного пути</title><description>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWwDvpsiQl72P-5_UlE-XfRR8rmszLtkdAg4fHj0mL7UH93k2lPf5R91bYrhtc5GYT4WIs2XxFatiOT_dsmofTsJYlvrxN3Vqs5iQprDpe4viaLjBMEciNmqNkuD20MZtjakp8NybB0pw/s1600/way.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 167px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWwDvpsiQl72P-5_UlE-XfRR8rmszLtkdAg4fHj0mL7UH93k2lPf5R91bYrhtc5GYT4WIs2XxFatiOT_dsmofTsJYlvrxN3Vqs5iQprDpe4viaLjBMEciNmqNkuD20MZtjakp8NybB0pw/s200/way.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5610285393009200322&quot; /&gt;&lt;/a&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;В чем заключается &lt;span style=&quot;font-weight:bold;&quot;&gt;оптимизация найденного пути&lt;/span&gt;? Все очень просто: мы должны убрать точки лежащие на одной прямой. Но у меня идея получше: в момент &lt;span style=&quot;font-weight:bold;&quot;&gt;построения пути&lt;/span&gt; не будем добавлять лишние точки.&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Для этого нам потребуется отредактировать нашу функцию &quot;CPathfinding::GetWay&quot;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Как мы узнаем, что точки лежат на одной прямой? Так как у нас все &lt;span style=&quot;font-weight:bold;&quot;&gt;клетки карты&lt;/span&gt; хранятся в одномерном массиве и имеют свой уникальный номер, то просто давайте найдем разницу между номерами добавляемых в карту клеток. И если при следующем добавлении разница совпадает, то эту клетку мы добавлять не будем.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Итого, вот так выглядит наш кусок кода, который в конце ищет &lt;span style=&quot;font-weight:bold;&quot;&gt;точки пути&lt;/span&gt; и добавляет его:&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;&lt;br /&gt;while (ID != iMasterID) &lt;br /&gt;{&lt;br /&gt;   ID = iMasterID;&lt;br /&gt;   iMasterID = GetMasterID(ID);&lt;br /&gt;   PointOfWay.x = ID % WayMap-&gt;GetGridWidth()* WayMap-&gt;GetChunkSize();&lt;br /&gt;   PointOfWay.z = ID / WayMap-&gt;GetGridWidth()* WayMap-&gt;GetChunkSize();&lt;br /&gt;&lt;br /&gt;   int iDelta = abs(ID-iMasterID);&lt;br /&gt;   if (iDelta != iSaveDelta) &lt;br /&gt;   {&lt;br /&gt;      Way-&gt;insert(Way-&gt;begin(), PointOfWay);&lt;br /&gt;      iSaveDelta = iDelta;&lt;br /&gt;   }   &lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Вот и все, теперь наш &lt;span style=&quot;font-weight:bold;&quot;&gt;путь оптимизирован&lt;/span&gt; и не будет содержать лишних ненужных точек.&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;a href=&quot;http://scr1pter.blogspot.com/2011/05/star_23.html&quot;&gt;&amp;lt;&amp;lt; Назад&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/p/blog-page.html&quot;&gt;| Оглавление |&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/2011/05/blog-post_31.html&quot;&gt;Вперед &amp;gt;&amp;gt;&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;</description><link>http://scr1pter.blogspot.com/2011/05/blog-post_24.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWwDvpsiQl72P-5_UlE-XfRR8rmszLtkdAg4fHj0mL7UH93k2lPf5R91bYrhtc5GYT4WIs2XxFatiOT_dsmofTsJYlvrxN3Vqs5iQprDpe4viaLjBMEciNmqNkuD20MZtjakp8NybB0pw/s72-c/way.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-8835559211012207674</guid><pubDate>Mon, 23 May 2011 16:07:00 +0000</pubDate><atom:updated>2011-06-05T16:15:39.887+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Искусственный интеллект</category><title>Стоимость передвижения в A star</title><description>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilUMFhdFoFw6zy60HarCz0uSiJhuiTmf8UyxH5B7INxoGHXq6LTDxlAAIhacGxH9L8o4oz4wToGBi79OScBlGg0bW-5abnteFA94hFFdEkvM0iIfHLOvBekuDxU22mT-BG3HgPKuGck2Y/s1600/Razvilka.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 145px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilUMFhdFoFw6zy60HarCz0uSiJhuiTmf8UyxH5B7INxoGHXq6LTDxlAAIhacGxH9L8o4oz4wToGBi79OScBlGg0bW-5abnteFA94hFFdEkvM0iIfHLOvBekuDxU22mT-BG3HgPKuGck2Y/s200/Razvilka.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5609948741951466594&quot; /&gt;&lt;/a&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;В прошлых примерах мы рассматривали только два &lt;b&gt;типа проходимости&lt;/b&gt;, где можно пройти и где нельзя. Давайте исправим ситуацию и рассмотрим реализацию, когда каждая клетка имеет разную &lt;b&gt;степень проходимости&lt;/b&gt;, или как ещё говорят имеют &lt;b&gt;разный вес&lt;/b&gt;. Мы просто раздадим разным клеткам разный вес в зависимости от рельефа. Чем темнее у нас будет клетка, тем больше времени она будет отнимать у &quot;путника&quot;, который будет по ней идти.&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Как же это реализовать? А все очень и очень просто! В структуре &lt;span style=&quot;font-weight:bold;&quot;&gt;CMapChunk &lt;/span&gt;&lt;span&gt;(смотрите прошлые статьи)&lt;/span&gt; мы введем дополнительный параметр &lt;span style=&quot;font-weight:bold;&quot;&gt;RankPassability&lt;/span&gt;, который будет хранить вес проходимости.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Я, например, заполнил так, что в середине карты у меня получилась синусоида, как будто тропа между холмами :)&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXo96CKUOeR1N2mxHN3k8WYlfHZhINJ2tUxdWS_hHwoPzgS9jryqmzpCD51nke623dQSaEntQpCugN1upTJePYHoCChFAV7rgeLDD2T6OGZH_SsD4oviultg-iJyve6HvgReEhzWOZ8lg/s1600/step1.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXo96CKUOeR1N2mxHN3k8WYlfHZhINJ2tUxdWS_hHwoPzgS9jryqmzpCD51nke623dQSaEntQpCugN1upTJePYHoCChFAV7rgeLDD2T6OGZH_SsD4oviultg-iJyve6HvgReEhzWOZ8lg/s320/step1.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5610038418974486258&quot; /&gt;&lt;/a&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Сейчас вы наверно подумали, опять будет куча расчетов и нудятины, а вот и ничего подобного!&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Как один из вариантов: мы просто в оценке пути при расчете F (&lt;span class=&quot;Apple-style-span&quot; style=&quot;color: rgb(51, 51, 51); font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 13px; line-height: 18px; &quot;&gt;&lt;span style=&quot;font-weight: bold; &quot;&gt;F это &lt;/span&gt;сумма G и H&lt;/span&gt;) умножаем его результат на вес клетки:&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;Chunk.F = (Chunk.H + Chunk.G)&lt;b&gt; * WayMap-&amp;gt;GetRankPass(Chunk.ID);&lt;/b&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Если вам эта строка ни о чем не говорит, и вы не знаете что такое F, H и G, то вы не читали мои прошлые статьи. А зря :)&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;ДА! И это все :) Вот результат:&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ851pEOKzLcQPROtR3xn12OR_ZtQfmQw58gPBXo-YFZPTalMLVrwA8xal-4_L_bEEyEt-bE81321MbZDpQHgtvXNVGIlEv_3CNoiQ4RhZvtePi7rOuadSxECHd9-gBCkrBJNe2L9pkuc/s1600/step2.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ851pEOKzLcQPROtR3xn12OR_ZtQfmQw58gPBXo-YFZPTalMLVrwA8xal-4_L_bEEyEt-bE81321MbZDpQHgtvXNVGIlEv_3CNoiQ4RhZvtePi7rOuadSxECHd9-gBCkrBJNe2L9pkuc/s320/step2.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5610040109043260930&quot; /&gt;&lt;/a&gt;&lt;div&gt;Но внезапно на нашем пути появилась китайская стена :)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ7lAGvIqQXZLNebn7Lnr-tOZxSr-Qh96BqNh7P6RUG892qvJLS8W6SgjhOyvMyGArd1v-rJK45RJ1Q0b4PYVONJk2UqiypI28tavp_ncOJgMHpA2YXKkocgPA4Z7QCzvQBIRNiXoijJE/s1600/step3.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ7lAGvIqQXZLNebn7Lnr-tOZxSr-Qh96BqNh7P6RUG892qvJLS8W6SgjhOyvMyGArd1v-rJK45RJ1Q0b4PYVONJk2UqiypI28tavp_ncOJgMHpA2YXKkocgPA4Z7QCzvQBIRNiXoijJE/s320/step3.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5610040348026310354&quot; /&gt;&lt;/a&gt;&lt;div&gt;Ну и последний пример:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEji5g3Zp6cREdf1Zr3_QvOQ2gFu1pXTk6L1DI4CTvT9RFrFNxrPMlPixELVeVYHmfCPqjMJBgzkKXpsPkCCV-z-aqh9FnT2NGoWdRITkoOK4CC0VWFGJY9tHIKjBV7hN9APvNweZgcR8Z0/s1600/step4.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEji5g3Zp6cREdf1Zr3_QvOQ2gFu1pXTk6L1DI4CTvT9RFrFNxrPMlPixELVeVYHmfCPqjMJBgzkKXpsPkCCV-z-aqh9FnT2NGoWdRITkoOK4CC0VWFGJY9tHIKjBV7hN9APvNweZgcR8Z0/s320/step4.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5610040636400036706&quot; /&gt;&lt;/a&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Таким образом можно задать болота, реки, рельеф и другие типы местности на карте.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;a href=&quot;http://scr1pter.blogspot.com/2011/05/star.html&quot;&gt;&amp;lt;&amp;lt; Назад&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/p/blog-page.html&quot;&gt;| Оглавление |&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/2011/05/blog-post_24.html&quot;&gt;Вперед &amp;gt;&amp;gt;&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;</description><link>http://scr1pter.blogspot.com/2011/05/star_23.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilUMFhdFoFw6zy60HarCz0uSiJhuiTmf8UyxH5B7INxoGHXq6LTDxlAAIhacGxH9L8o4oz4wToGBi79OScBlGg0bW-5abnteFA94hFFdEkvM0iIfHLOvBekuDxU22mT-BG3HgPKuGck2Y/s72-c/Razvilka.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-4777266275576993057</guid><pubDate>Mon, 23 May 2011 09:53:00 +0000</pubDate><atom:updated>2011-06-05T16:04:18.337+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Искусственный интеллект</category><title>A star в картинках</title><description>&lt;div style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;В прошлой статье мы рассмотрели &lt;b&gt;алгоритм поиска пути A star&lt;/b&gt;, как на теории, так и на практике. Но было бы не плохо ещё разобраться наглядно не используя ни формулы ни код.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Представим, что у нас есть карта &lt;b&gt;6&lt;/b&gt; на &lt;b&gt;5&lt;/b&gt; клеток, итого &lt;b&gt;30&lt;/b&gt;, и нам надо попасть из клетки с номером &lt;b&gt;6&lt;/b&gt; в &lt;b&gt;22&lt;/b&gt;.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;b&gt;Зеленым&lt;/b&gt;&lt;/span&gt; квадратиком, я буду обозначать, что эта клетка находится в открытом списке.&lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;b&gt;Синим&lt;/b&gt;&lt;/span&gt; - в закрытом.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;center&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpgdK9XGIX3kUCyTF3Q-GZWcRtZ4BKy5FHmk3EG27y_wASxaEKTXXvUg8JEtu-vCruFhh6A2cByOXgW4YMOeFz8-wNuho2ZSmlLPhxzLTNWAU0zj3hhz9fJC7VEOzN_rXkJw1-mQIo-6Q/s1600/step1.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;float:center; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 267px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpgdK9XGIX3kUCyTF3Q-GZWcRtZ4BKy5FHmk3EG27y_wASxaEKTXXvUg8JEtu-vCruFhh6A2cByOXgW4YMOeFz8-wNuho2ZSmlLPhxzLTNWAU0zj3hhz9fJC7VEOzN_rXkJw1-mQIo-6Q/s320/step1.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5609849611528063538&quot; /&gt;&lt;/a&gt;&lt;/center&gt;&lt;center style=&quot;text-align: justify;&quot;&gt;Я выбрал именно это расположение стартовой, конечной точек и преград, потому что мне показался этот случай интересным. Как мы видим мы сразу добавили нашу стартовую клетку в открытый список.  Напомню, открытый список служит для хранения клеток, которые нужно проверить, закрытый  хранит уже проверенные ячейки.&lt;/center&gt;&lt;center style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/center&gt;&lt;center style=&quot;text-align: justify;&quot;&gt;Давайте взглянем на то, как я буду записывать данные ячейки:&lt;/center&gt;&lt;center style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/center&gt;&lt;center style=&quot;text-align: left;&quot;&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfSa-vTqVID6tDzDLCnrgNEDwzmoKszbmvyy1yURevONTk8QfjPRKWLlq-bKJXTaqBvw8W8hOlNEmLHjnr1g5rd8ZbyAJONNQnI0d2Z8SbE1WaW7J7IL4S2FNMiSwVORd1Cv7voRM6cu4/s320/Legenda.jpg&quot; style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 64px; height: 63px;&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5609852430696557186&quot; /&gt;&lt;/center&gt;&lt;center style=&quot;text-align: left;&quot;&gt;Напомню обозначения из прошлого урока:&lt;/center&gt;&lt;center style=&quot;text-align: left;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;color: rgb(51, 51, 51); font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 13px; line-height: 18px; &quot;&gt;&lt;ul style=&quot;padding-top: 0px; padding-right: 2.5em; padding-bottom: 0px; padding-left: 2.5em; margin-top: 0.5em; margin-right: 0px; margin-bottom: 0.5em; margin-left: 0px; line-height: 1.4; list-style-type: disc; list-style-position: initial; list-style-image: initial; &quot;&gt;&lt;li style=&quot;padding-top: 0.25em; padding-right: 0px; padding-bottom: 0.25em; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0.25em; margin-left: 0px; text-indent: 0px; color: rgb(51, 51, 51); border-top-width: initial; border-top-style: none; border-top-color: initial; border-bottom-width: 1px; border-bottom-style: none; border-bottom-color: transparent; border-right-style: none; border-left-style: none; border-width: initial; border-color: initial; &quot;&gt;&lt;span style=&quot;font-weight: bold; &quot;&gt;ID&lt;/span&gt; - номер ячейки в сетке;&lt;br /&gt;&lt;/li&gt;&lt;li style=&quot;padding-top: 0.25em; padding-right: 0px; padding-bottom: 0.25em; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0.25em; margin-left: 0px; text-indent: 0px; color: rgb(51, 51, 51); border-top-width: 0px; border-top-style: none; border-top-color: rgb(51, 51, 51); border-bottom-width: 1px; border-bottom-style: none; border-bottom-color: transparent; border-right-style: none; border-left-style: none; border-width: initial; border-color: initial; &quot;&gt;&lt;span style=&quot;font-weight: bold; &quot;&gt;mID&lt;/span&gt; - её родитель;&lt;br /&gt;&lt;/li&gt;&lt;li style=&quot;text-align: justify;padding-top: 0.25em; padding-right: 0px; padding-bottom: 0.25em; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0.25em; margin-left: 0px; text-indent: 0px; color: rgb(51, 51, 51); border-top-style: none; border-bottom-style: none; border-right-style: none; border-left-style: none; border-width: initial; border-color: initial; &quot;&gt;&lt;span style=&quot;font-weight: bold; &quot;&gt;G&lt;/span&gt; - стоимость передвижения из стартовой клетки к данной. В моем примере каждое передвижение по горизонтали и вертикале будет стоить 10 единиц, а по диагонали 14. Так вот G это сумма единиц пройденного расстояния от начала до конкретно рассматриваемой точки;&lt;br /&gt;&lt;/li&gt;&lt;li style=&quot;text-align: justify;padding-top: 0.25em; padding-right: 0px; padding-bottom: 0.25em; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0.25em; margin-left: 0px; text-indent: 0px; color: rgb(51, 51, 51); border-top-style: none; border-bottom-style: none; border-right-style: none; border-left-style: none; border-width: initial; border-color: initial; &quot;&gt;&lt;span style=&quot;font-weight: bold; &quot;&gt;H&lt;/span&gt; - стоимость передвижения от данной клетки до конечной по &lt;span style=&quot;font-weight: bold; &quot;&gt;методу Манхеттена&lt;/span&gt; (&lt;span style=&quot;font-weight: bold; &quot;&gt;Manhattan method&lt;/span&gt;). Этот метод состоит в том что мы рассчитываем расстояние только по горизонтали и вертикали, игнорируя препятствия и диагональные перемещения.&lt;br /&gt;&lt;/li&gt;&lt;li style=&quot;padding-top: 0.25em; padding-right: 0px; padding-bottom: 0.25em; padding-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0.25em; margin-left: 0px; text-indent: 0px; color: rgb(51, 51, 51); border-top-width: 0px; border-top-style: none; border-top-color: rgb(51, 51, 51); border-bottom-width: initial; border-bottom-style: none; border-bottom-color: initial; border-right-style: none; border-left-style: none; border-width: initial; border-color: initial; &quot;&gt;&lt;span style=&quot;font-weight: bold; &quot;&gt;F&lt;/span&gt; - сумма G и H;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Так как в открытом списке у нас лишь одна клетка &lt;b&gt;6&lt;/b&gt;, то мы берем её для анализа.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/span&gt;&lt;/center&gt;&lt;/div&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU93Emi3uLh1SvdZFGy0lTnF6unX2jFzuOrX6KqWBxG8rac_hjOjCBYV8-JG0FH-45Mv7lpc3qEHCxfYtBh6nqh3q-x_dsfAhxVj5zpAISCg-ddv3jAVa9pxsnfNlMMGCItnZwU_TD3sw/s1600/step2.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 267px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU93Emi3uLh1SvdZFGy0lTnF6unX2jFzuOrX6KqWBxG8rac_hjOjCBYV8-JG0FH-45Mv7lpc3qEHCxfYtBh6nqh3q-x_dsfAhxVj5zpAISCg-ddv3jAVa9pxsnfNlMMGCItnZwU_TD3sw/s320/step2.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5609855002009477634&quot; /&gt;&lt;/a&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Мы добавили её проходимых соседей в открытый список, оценили их путь и нашу клетку отправили в закрытый список. Теперь нам надо выбрать клетку с наименьшим значением F (цифра верхнего левого уголка) для анализа. Так как мы начинали искать соседей слева направо, то первая клетка с наименьшим F у нас будет &lt;b&gt;12&lt;/b&gt;.&lt;/div&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlbwksxagoZWyCBpQaK0Ky1llPzcdBh8y7OqUeVUFIow0E8uUm7oEjg0hMhLjQl-i8i0d1z6eDTAdp73nngnkVfqD2dOpLto5ANQ3MCAsveG9i3EJxqKjtfSlAchFa9YMICNddhxDH_VI/s1600/step3.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 267px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlbwksxagoZWyCBpQaK0Ky1llPzcdBh8y7OqUeVUFIow0E8uUm7oEjg0hMhLjQl-i8i0d1z6eDTAdp73nngnkVfqD2dOpLto5ANQ3MCAsveG9i3EJxqKjtfSlAchFa9YMICNddhxDH_VI/s320/step3.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5609857934014437426&quot; /&gt;&lt;/a&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Опять у нас спорный момент между клетками &lt;b&gt;7&lt;/b&gt; и &lt;b&gt;18&lt;/b&gt;, но &lt;b&gt;7&lt;/b&gt; была добавлена раньше. Значит, ищем её соседей.&lt;/div&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4_3LclXPJlsk_3LVd4dJb0AeCtLcyDmPNjPKJLZlT9BdXnS4tUrxZZssST4iykYBr5cTjPx7yL_KceLjf1Tk0bujnU0lRjaxRYyEv__5gFOx2MWpKDQnS6LigUgsEeqIPxN-3HukiOW0/s1600/step4.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 267px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4_3LclXPJlsk_3LVd4dJb0AeCtLcyDmPNjPKJLZlT9BdXnS4tUrxZZssST4iykYBr5cTjPx7yL_KceLjf1Tk0bujnU0lRjaxRYyEv__5gFOx2MWpKDQnS6LigUgsEeqIPxN-3HukiOW0/s320/step4.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5609858451872520322&quot; /&gt;&lt;/a&gt;&lt;div&gt;Клетка под номером &lt;b&gt;18&lt;/b&gt; была добавлена раньше.&lt;/div&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1c9O1sLqCnKz4XZLm9YXQSqkJeRoweNCBUVg5_iX63mUbl-H4AKT0VTu7RX70882lwQdK_zyqGW5svKxAg11zXQJKcYTtNu5QPYvv1MI6wfrunZkGhEcQQFVldcTwuuHd9jUDECoId0M/s1600/step5.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; cursor: pointer; width: 320px; height: 267px; text-align: center; &quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1c9O1sLqCnKz4XZLm9YXQSqkJeRoweNCBUVg5_iX63mUbl-H4AKT0VTu7RX70882lwQdK_zyqGW5svKxAg11zXQJKcYTtNu5QPYvv1MI6wfrunZkGhEcQQFVldcTwuuHd9jUDECoId0M/s320/step5.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5609858884267514354&quot; /&gt;&lt;/a&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Уже видно два пути, которых наш алгоритм пытается сравнить и выявить наименьший. Хоть нам и ясно какой из них будет верным, продолжим наше наглядное исследование. Ищем соседей для клетки &lt;b&gt;8&lt;/b&gt;.&lt;/div&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEnnqEa5LQJuZRCpRRse6KpTPV8wDTYBlZpGbZBi_kXHp5XIqu4T1I-Csap2UfGhMCF8qU_V38TvNo2gTID2R3fdvEB-gE_GZ0JOe_Tzm2-Are3YUkDg2USRdGQF3UkUwZmxL7SLpvNIY/s1600/step6.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 267px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEnnqEa5LQJuZRCpRRse6KpTPV8wDTYBlZpGbZBi_kXHp5XIqu4T1I-Csap2UfGhMCF8qU_V38TvNo2gTID2R3fdvEB-gE_GZ0JOe_Tzm2-Are3YUkDg2USRdGQF3UkUwZmxL7SLpvNIY/s320/step6.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5609859599575082242&quot; /&gt;&lt;/a&gt;&lt;div&gt;Следующая клетка, соседей которой мы будем оценивать, это клетка &lt;b&gt;19. &lt;/b&gt;И она будет последним анализируемым звеном в не самом коротком пути (но я вам этого не говорил).&lt;/div&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQIPDFMlSpRL14knI527EMWigZOfKccYMyGUz24340r9swgcLH4AYS3t3oStwdKFUvkuMbR90QrmdT66DPlKW2JHcqX1SmDaFxuupHGqduAY9w92zlYrTIfvUJw6GIReEcsoczx5oMvsU/s1600/step7.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 267px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQIPDFMlSpRL14knI527EMWigZOfKccYMyGUz24340r9swgcLH4AYS3t3oStwdKFUvkuMbR90QrmdT66DPlKW2JHcqX1SmDaFxuupHGqduAY9w92zlYrTIfvUJw6GIReEcsoczx5oMvsU/s320/step7.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5609860243889024322&quot; /&gt;&lt;/a&gt;&lt;div&gt;В порядке очереди выбираем клетку &lt;b&gt;9&lt;/b&gt;.&lt;/div&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2Gv8gZHw-pvlESGlmx_CebYoNceWdCFXinlnGTPv89eIPVt1R8kGyoVUuJdekMJBdcURyspFtcdIIk43fHVk1SRKlaAdKtPKXGlmE854-NWPicO4WIciJrr8nBTY2L8NLzOabWgiEdeI/s1600/step8.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 267px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2Gv8gZHw-pvlESGlmx_CebYoNceWdCFXinlnGTPv89eIPVt1R8kGyoVUuJdekMJBdcURyspFtcdIIk43fHVk1SRKlaAdKtPKXGlmE854-NWPicO4WIciJrr8nBTY2L8NLzOabWgiEdeI/s320/step8.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5609860663152786178&quot; /&gt;&lt;/a&gt;&lt;div&gt;На данном этапе, у нас появилась одна единственная клетка с наименьшим F, и она под номером &lt;b&gt;16&lt;/b&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimHi1d7XRAd248RuXzc4Tgjs4UeG4lE6r0mbAbbkGui-t-_Q9ST__zUeY2ZZTIMo-r7N-VNOrNDmyWpPUum7RHW7qmjzonUGqF2CgGRP5GCNUUEy5qDOl-9AbVZ-oszEJmZvNPxdvy2RE/s1600/step9.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; cursor: pointer; width: 320px; height: 267px; text-align: center; &quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimHi1d7XRAd248RuXzc4Tgjs4UeG4lE6r0mbAbbkGui-t-_Q9ST__zUeY2ZZTIMo-r7N-VNOrNDmyWpPUum7RHW7qmjzonUGqF2CgGRP5GCNUUEy5qDOl-9AbVZ-oszEJmZvNPxdvy2RE/s320/step9.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5609861107475300002&quot; /&gt;&lt;/a&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Добавляя соседей для клетки &lt;b&gt;16&lt;/b&gt;, мы так же добавили нашу конечную точку, а значит, пора останавливать нашу рутинную процедуру :) Теперь нам надо найти по клеткам, находящимся в закрытом списке наш путь. Так как мы храним их номера родителей (master ID, правая верхняя цифра в квадратике), то двигаясь с конца по родительским клеткам дойдем до стартовой, это и будет наш путь.&lt;/div&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKY0j535MIRxNjd8t9_t4LywQmyX0hCO6yLAp8X-AGopUMU8zeaWCcA4b9HlcYOOQIu73L5yS2ySVvN4y_h44Ly93rUHvUYa6zLuGpY4fT7GM9pQfNEkZNULCinDlAwEyD-Wbt2kOLwfc/s1600/step10.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 267px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKY0j535MIRxNjd8t9_t4LywQmyX0hCO6yLAp8X-AGopUMU8zeaWCcA4b9HlcYOOQIu73L5yS2ySVvN4y_h44Ly93rUHvUYa6zLuGpY4fT7GM9pQfNEkZNULCinDlAwEyD-Wbt2kOLwfc/s320/step10.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5609862011806430354&quot; /&gt;&lt;/a&gt;&lt;div&gt;Ну и вот так выглядит наш путь найденный &lt;b&gt;алгоритмом  A&lt;/b&gt;*.&lt;/div&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEho6dwhDGWiV-WyAoe92LNMuOvkWADXfuf5e4b38W0XolIOpdR69jjg_oD8fnN2M66STxVfTmMNudQ9pKfkJu580TwCT-poIYqxvWmKfRDBY7ZMvL_Z4SSzHrZ_gv2Z7FwAKN9nZW4QQK8/s1600/step11.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; cursor: pointer; width: 320px; height: 267px; text-align: center; &quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEho6dwhDGWiV-WyAoe92LNMuOvkWADXfuf5e4b38W0XolIOpdR69jjg_oD8fnN2M66STxVfTmMNudQ9pKfkJu580TwCT-poIYqxvWmKfRDBY7ZMvL_Z4SSzHrZ_gv2Z7FwAKN9nZW4QQK8/s320/step11.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5609862814786847858&quot; /&gt;&lt;/a&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Думаю теперь, когда мы &quot;разжевали&quot; алгоритм поиска пути A star (на теории, на картинках, на практике), недопонимания и вопросов не осталось ни у кого. Теперь мы легко можем найти кратчайший путь из одной точки в другу.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;a href=&quot;http://scr1pter.blogspot.com/2011/05/blog-post_21.html&quot;&gt;&amp;lt;&amp;lt; Назад&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/p/blog-page.html&quot;&gt;| Оглавление |&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/2011/05/star_23.html&quot;&gt;Вперед &amp;gt;&amp;gt;&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;</description><link>http://scr1pter.blogspot.com/2011/05/star.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpgdK9XGIX3kUCyTF3Q-GZWcRtZ4BKy5FHmk3EG27y_wASxaEKTXXvUg8JEtu-vCruFhh6A2cByOXgW4YMOeFz8-wNuho2ZSmlLPhxzLTNWAU0zj3hhz9fJC7VEOzN_rXkJw1-mQIo-6Q/s72-c/step1.jpg" height="72" width="72"/><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-4131066233583615944</guid><pubDate>Sat, 21 May 2011 21:38:00 +0000</pubDate><atom:updated>2011-06-08T01:10:21.183+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Искусственный интеллект</category><title>Алгоритм поиска пути A*</title><description>&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2mDU6dMFb7Om-eIIGqroTSlKCCk-Vx3YnVO3raFR0NXz6aPcKjJ-OGu-8d6oFhnvp2P6qKvmDH8d_NujEaYR7QvgWYCnRGF2heQaUS3hyjajZv1MbNlJFRjsyP04DejUY5xxu63_HlDw/s1600/Astar.jpg&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 150px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2mDU6dMFb7Om-eIIGqroTSlKCCk-Vx3YnVO3raFR0NXz6aPcKjJ-OGu-8d6oFhnvp2P6qKvmDH8d_NujEaYR7QvgWYCnRGF2heQaUS3hyjajZv1MbNlJFRjsyP04DejUY5xxu63_HlDw/s200/Astar.jpg&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5609288082572238578&quot; /&gt;&lt;/a&gt; &lt;div style=&quot;text-align: justify;&quot;&gt;В этой статье мы рассмотрим &lt;span style=&quot;font-weight:bold;&quot;&gt;поиск пути&lt;/span&gt;. Говорят, что &lt;span style=&quot;font-weight:bold;&quot;&gt;алгоритм A*&lt;/span&gt; (или &lt;span style=&quot;font-weight:bold;&quot;&gt;А звездочка&lt;/span&gt;, или &lt;span style=&quot;font-weight:bold;&quot;&gt;A star&lt;/span&gt;) сложен для новичков, ну что ж, пусть так говорят :) &lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Давайте же на теории пройдемся по этапам &lt;span style=&quot;font-weight:bold;&quot;&gt;поиска пути по алгоритму A*&lt;/span&gt;, а потом закрепим это на практике.&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;У нас будет два списка, которые будут хранить номера клеток. В первом будут клетки, которые нужно проверить, во втором - клетки, которые уже проверены. Первый список обычно называют &quot;открытый&quot;, второй - &quot;закрытый&quot;.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;ul&gt;&lt;li&gt;Мы добавляем стартовую точку в наш открытый список.&lt;br /&gt;&lt;li&gt;Находим точку с наименьшим оцененным путем в открытом списке (как будем оценивать, написано ниже) и ищем для неё проходимых соседей. Найденные новые точки мы оцениваем путь и добавляем их в открытый список. А исследуемую точку мы удаляем из открытого списка и добавляем в закрытый, чтобы больше её не проверять.&lt;br /&gt;&lt;li&gt;Повторяем предыдущий пункт до тех пор, пока не найдем конечную точку (а не найти мы её не сможем из-за &quot;островного&quot; разделения закрытых друг от друга зон, которое мы рассмотрели в прошлой статье).&lt;br /&gt;&lt;li&gt;По закрытому списку строим наш путь.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;Работу программы, которую мы создадим, можно посмотреть тут&lt;/span&gt;:&lt;/center&gt;&lt;br /&gt;&lt;center&gt;&lt;iframe width=&quot;425&quot; height=&quot;349&quot; src=&quot;http://www.youtube.com/embed/P5Qu0xnGouU&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;/ul&gt;&lt;/div&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhBaxk7b0Uzd6p2sM-SmkmvK_yvyjOWmlXrHHaDrvdiO7o0ZLKeTpR5QXSXWPBnkycYud47GFMQKf7QFUffZwvb_oJmIJZpFqlvjBnSm52ediWDhp0sqbobogBrpZIJzsCrbkixxjqS8M/s1600/way.jpg&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 248px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhBaxk7b0Uzd6p2sM-SmkmvK_yvyjOWmlXrHHaDrvdiO7o0ZLKeTpR5QXSXWPBnkycYud47GFMQKf7QFUffZwvb_oJmIJZpFqlvjBnSm52ediWDhp0sqbobogBrpZIJzsCrbkixxjqS8M/s320/way.jpg&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5614703151828791218&quot; /&gt;&lt;/a&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Для начала немного подправим класс &quot;&lt;span style=&quot;font-weight:bold;&quot;&gt;CWayMap&lt;/span&gt;&quot;, который мы рассмотрели в прошлом уроке.&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;class CWayMap&lt;br /&gt;{&lt;br /&gt;private:&lt;br /&gt;   ...&lt;br /&gt;public:&lt;br /&gt;   ...&lt;br /&gt;   // эту функцию мы просто перенесли из private в public&lt;br /&gt;   bool CheckDiagonals(int iNeighborX, int iNeighborZ, int iMasterX, int iMasterZ);&lt;br /&gt;   // функция, возвращает true если из одной точки можно пройти в другую&lt;br /&gt;   bool IsCanWay(int iStartID, int iEndID);&lt;br /&gt;   // возвращаем ширину сетки&lt;br /&gt;   int GetGridWidth() {return m_iGridWidth;}&lt;br /&gt;   // возвращаем размер ячейки сетки&lt;br /&gt;   int GetChunkSize() {return m_iChunkSize;}&lt;br /&gt;};&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Тут вроде ничего серьезного кроме &quot;&lt;span style=&quot;font-weight:bold;&quot;&gt;IsCanWay&lt;/span&gt;&quot;, но комментарии в коде дают нам понять, что можно смело двигать дальше, не тратя времени на ерунду.&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;bool CWayMap::IsCanWay(int iStartID, int iEndID)&lt;br /&gt;{&lt;br /&gt;   // не равны ли начальная и конечная точки пути&lt;br /&gt;   if (iStartID == iEndID)&lt;br /&gt;      return false;&lt;br /&gt;   // проверяем, сможем туда попасть или нет, за счет разделения зон&lt;br /&gt;   if (m_Grid[iStartID].Passability != m_Grid[iEndID].Passability)&lt;br /&gt;      return false;&lt;br /&gt;   // проверив одну из точек, мы сразу отсекаем случаи:&lt;br /&gt;   // конечная точка - препятствие&lt;br /&gt;   // начальная точка - препятствие&lt;br /&gt;   if (!m_Grid[iEndID].Passability)&lt;br /&gt;      return false;&lt;br /&gt;   return true;&lt;br /&gt;}&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Я ещё ввел структуру &quot;&lt;span style=&quot;font-weight:bold;&quot;&gt;MapPoint&lt;/span&gt;&quot;, она будет хранить позиции ячеек сетки в экранных координатах, но можно обойтись и без неё.&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;struct MapPoint&lt;br /&gt;{&lt;br /&gt;   int x;&lt;br /&gt;   int z;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Так же у нас появится ещё одна структура &quot;&lt;span style=&quot;font-weight:bold;&quot;&gt;WayChunk&lt;/span&gt;&quot;. Она пригодится для оценки пути определенной клетки. Давайте взглянем на неё:&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;struct WayChunk&lt;br /&gt;{&lt;br /&gt;   int ID; &lt;br /&gt;   int MasterID;&lt;br /&gt;   int G;&lt;br /&gt;   int H;&lt;br /&gt;   int F;&lt;br /&gt;};&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;ID&lt;/span&gt; - номер ячейки в сетке;&lt;br /&gt;&lt;li&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;MasterID&lt;/span&gt; - её родитель;&lt;br /&gt;&lt;li&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;G&lt;/span&gt; - стоимость передвижения из стартовой клетки к данной. В моем примере каждое передвижение по горизонтали и вертикале будет стоить 10 единиц, а по диагонали 14. Так вот, G -это сумма единиц пройденного расстояния от начала до конкретно рассматриваемой точки;&lt;br /&gt;&lt;li&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;H&lt;/span&gt; - стоимость передвижения от данной клетки до конечной по &lt;span style=&quot;font-weight:bold;&quot;&gt;методу Манхеттена&lt;/span&gt; (&lt;span style=&quot;font-weight:bold;&quot;&gt;Manhattan method&lt;/span&gt;). Этот метод состоит в том, что мы рассчитываем расстояние только по горизонтали и вертикали, игнорируя препятствия и диагональные перемещения.&lt;br /&gt;&lt;li&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;F&lt;/span&gt; - сумма G и H;&lt;/ul&gt;Теперь становится ясно, что каждый раз, мы будем искать точку с наименьшем значением F.&lt;/div&gt;&lt;br /&gt;Давайте же взглянем на объявление нашего класса:&lt;pre class=&quot;brush: cpp&quot;&gt;class CPathfinding&lt;br /&gt;{&lt;br /&gt;private:&lt;br /&gt;   // стоимость передвижения по горизонтали и вертикали&lt;br /&gt;   #define WeightLine 10&lt;br /&gt;   // стоимость передвижения по диагонали&lt;br /&gt;   #define WeightDiag 14&lt;br /&gt;   // возможные результаты&lt;br /&gt;   enum &lt;br /&gt;   { &lt;br /&gt;      SEARTH_COMPLETED = -1, &lt;br /&gt;      NOT_FIND_END = -2,&lt;br /&gt;      FIND_END = -3 &lt;br /&gt;   };&lt;br /&gt;    // наша карта&lt;br /&gt;   CWayMap* WayMap;&lt;br /&gt;   // Список, который надо проверить&lt;br /&gt;   std::vector &lt; WayChunk &gt; m_aOpenList;&lt;br /&gt;   // Проверенный список&lt;br /&gt;   std::vector &lt; WayChunk &gt; m_aCloseList;&lt;br /&gt;&lt;br /&gt;   // точка, до куда мы должны найти путь&lt;br /&gt;   int iEndX;&lt;br /&gt;   int iEndZ;&lt;br /&gt;&lt;br /&gt;   // возвращает клетку с наименьшим F&lt;br /&gt;   int GetMinWayChunk();&lt;br /&gt;   // проверяем клетку, ищем её соседей&lt;br /&gt;   bool DoSearchStep(int iChunkNum);&lt;br /&gt;   // поиск в открытом списке&lt;br /&gt;   int FindInOpenList(int iChunkID);&lt;br /&gt;   // поиск в закрытом списке&lt;br /&gt;   int FindInCloseList(int iChunkID);&lt;br /&gt;   // возвращаем мастер клетку по ID&lt;br /&gt;   int GetMasterID(int iChunkID);&lt;br /&gt;public:&lt;br /&gt;   CPathfinding(int iWidth, int iHeight, int iSize); &lt;br /&gt;   ~CPathfinding(void);&lt;br /&gt;   void DrawGrid(HDC dc);&lt;br /&gt;   // наша функция, которой мы будем пользоваться&lt;br /&gt;   bool GetWay(MapPoint From, MapPoint To, std::vector&lt; MapPoint &gt;* Way); &lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Не стоит пугаться, здесь большинство функций работают с нашими списками, и ничего сложного в них нет. Я даже не буду их комментировать, просто пробежимся по ним, но только после конструктора и деструктора :)&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;CPathfinding::CPathfinding(int iWidth, int iHeight, int iSize)&lt;br /&gt;{&lt;br /&gt;   WayMap = NULL;&lt;br /&gt;   // создаем нашу карту&lt;br /&gt;   WayMap = new CWayMap(iWidth, iHeight, iSize);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;CPathfinding::~CPathfinding(void)&lt;br /&gt;{&lt;br /&gt;   SafeDelete(WayMap);&lt;br /&gt;}&lt;br /&gt;// ищем в закрытом списке клетку по ID &lt;br /&gt;// и возвращаем его мастера&lt;br /&gt;int CPathfinding::GetMasterID(int iChunkID)&lt;br /&gt;{&lt;br /&gt;   for (unsigned int i=0; i &lt; m_aCloseList.size(); i++)&lt;br /&gt;   {&lt;br /&gt;      if (m_aCloseList[i].ID == iChunkID) &lt;br /&gt;         return m_aCloseList[i].MasterID;&lt;br /&gt;   } &lt;br /&gt;   return -1;&lt;br /&gt;}&lt;br /&gt;// есть ли в закрытом списке клетка с указанным ID &lt;br /&gt;int CPathfinding::FindInCloseList(int iChunkID)&lt;br /&gt;{&lt;br /&gt;   for (unsigned int i=0; i &lt; m_aCloseList.size(); i++)&lt;br /&gt;   {&lt;br /&gt;      if (iChunkID == m_aCloseList[i].ID) &lt;br /&gt;         return i;&lt;br /&gt;   }&lt;br /&gt;   return -1;&lt;br /&gt;}&lt;br /&gt;// есть ли в открытом списке клетка с указанным ID &lt;br /&gt;int CPathfinding::FindInOpenList(int iChunkID)&lt;br /&gt;{&lt;br /&gt;   for (unsigned int i=0; i &lt; m_aOpenList.size(); i++)&lt;br /&gt;   {&lt;br /&gt;      if (iChunkID == m_aOpenList[i].ID) &lt;br /&gt;         return i;&lt;br /&gt;   }&lt;br /&gt;   return -1;&lt;br /&gt;}&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Функция &quot;&lt;span style=&quot;font-weight:bold;&quot;&gt;GetMinWayChunk&lt;/span&gt;&quot; ищет в открытом списке клетку с наименьшим F для её дальнейшего анализа (возвращает порядковый номер открытого списка!).&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;int CPathfinding::GetMinWayChunk()&lt;br /&gt;{&lt;br /&gt;   // если открытый список пуст, то мы не нашли путь&lt;br /&gt;   if (!m_aOpenList.size()) &lt;br /&gt;      return NOT_FIND_END;&lt;br /&gt;&lt;br /&gt;   // ищем в открытом списке клетки с наименьшим F&lt;br /&gt;   int iMinWay = m_aOpenList[0].F;&lt;br /&gt;   int ID = 0;&lt;br /&gt;   for (unsigned int i=0; i &lt; m_aOpenList.size(); i++)&lt;br /&gt;   {&lt;br /&gt;      if (m_aOpenList[i].F &lt; iMinWay) &lt;br /&gt;      {&lt;br /&gt;         iMinWay = m_aOpenList[i].F;&lt;br /&gt;         ID = i;&lt;br /&gt;      }&lt;br /&gt;   } &lt;br /&gt;   // если H этой клетки не 0, то это не конец пути&lt;br /&gt;   if (m_aOpenList[ID].H != 0)&lt;br /&gt;      return ID;&lt;br /&gt;   else&lt;br /&gt;   {&lt;br /&gt;      // иначе добавляем конечную точку и сообщаем об этом&lt;br /&gt;      m_aCloseList.push_back(m_aOpenList[ID]);&lt;br /&gt;      return FIND_END;&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&quot;&lt;span style=&quot;font-weight:bold;&quot;&gt;DoSearchStep&lt;/span&gt;&quot; получает номер элемента открытого списка, из которого определяет ID клетки, для которой будем искать проходимых соседей. Каждого &quot;хорошего&quot; мы добавляем в открытый список, оценивая его путь. В конце анализирующую клетку, с которой работали, удаляем из открытого списка и добавляем в закрытый.&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;bool CPathfinding::DoSearchStep(int iChunkNum)&lt;br /&gt;{&lt;br /&gt;   // Родительский ID, для которого ищем соседей&lt;br /&gt;   int iMasterID = m_aOpenList[iChunkNum].ID;&lt;br /&gt;&lt;br /&gt;   // вычисляем по номеру ячейки ширину и высоту в сетке&lt;br /&gt;   int iMasterX = iMasterID % WayMap-&gt;GetGridWidth();&lt;br /&gt;   int iMasterZ = iMasterID / WayMap-&gt;GetGridWidth();&lt;br /&gt;   // проходимся по всем соседям&lt;br /&gt;   for (int i = -1; i &lt;= 1; i++)&lt;br /&gt;   {&lt;br /&gt;      for (int j = -1; j &lt;= 1; j++)&lt;br /&gt;      {&lt;br /&gt;         // если это и есть ячейка, соседей которых мы ищем, то пропускаем&lt;br /&gt;         if (!j &amp;&amp; !i)&lt;br /&gt;            continue;&lt;br /&gt;&lt;br /&gt;         int iNeighborX = iMasterX+i;&lt;br /&gt;         int iNeighborZ = iMasterZ+j;&lt;br /&gt;         // проверка на диапазон и проходимость клетки&lt;br /&gt;         if (WayMap-&gt;CheckPassability(iNeighborX, iNeighborZ))&lt;br /&gt;         {&lt;br /&gt;            // если он является диагональным, то можно ли&lt;br /&gt;            // пройти в него не срезав углы&lt;br /&gt;            if (WayMap-&gt;CheckDiagonals(iNeighborX, iNeighborZ, iMasterX, iMasterZ))&lt;br /&gt;            {&lt;br /&gt;               // по ширине и высоте находим номер соседа в сетке&lt;br /&gt;               int iNeighborID = WayMap-&gt;GetIDByPos(iNeighborX, iNeighborZ);&lt;br /&gt;               &lt;br /&gt;               // если клетка проверялась, пропускаем&lt;br /&gt;               if (FindInCloseList(iNeighborID) &gt; -1)&lt;br /&gt;                  continue;&lt;br /&gt;  &lt;br /&gt;               // начинаем оценивать путь этой точки&lt;br /&gt;               WayChunk Chunk;  &lt;br /&gt;               // устанавливаем ID клетки&lt;br /&gt;               Chunk.ID = iNeighborID;&lt;br /&gt;               // устанавливаем родителя клетки&lt;br /&gt;               Chunk.MasterID = iMasterID;&lt;br /&gt;               // стоимость передвижения Манхеттена&lt;br /&gt;               Chunk.H = (abs(iNeighborX - iEndX) + abs(iNeighborZ - iEndZ)) * 10;&lt;br /&gt;&lt;br /&gt;               // рассчитываем длину пути от начальной точки до нынешней&lt;br /&gt;               int iDiffX = abs(iMasterX - iNeighborX);&lt;br /&gt;               int iDiffZ = abs(iMasterZ - iNeighborZ);&lt;br /&gt;               int iDiag = min(iDiffX, iDiffZ);  &lt;br /&gt;               int iDirect = max(iDiffX, iDiffZ) - min(iDiffX, iDiffZ);&lt;br /&gt;               Chunk.G = iDiag*WeightDiag + iDirect*WeightLine + m_aOpenList[iChunkNum].G;&lt;br /&gt;               // вычисляем наш F&lt;br /&gt;               Chunk.F = Chunk.H + Chunk.G; &lt;br /&gt;&lt;br /&gt;               // есть ли наша клетка в открытом списке&lt;br /&gt;               int iNumChunk = FindInOpenList(Chunk.ID);&lt;br /&gt;               if (iNumChunk == -1)&lt;br /&gt;                  // если нет, то добавлем&lt;br /&gt;                  m_aOpenList.push_back(Chunk);&lt;br /&gt;               else&lt;br /&gt;               {  &lt;br /&gt;                  // если есть, то не короче ли наши новые расчеты&lt;br /&gt;                  if (m_aOpenList[iNumChunk].G &gt; Chunk.G)&lt;br /&gt;                     m_aOpenList[iNumChunk] = Chunk;&lt;br /&gt;               }&lt;br /&gt;            }&lt;br /&gt;         }&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;   &lt;br /&gt;   // после того как с нашей точкой мы поработали, пора её перенести в другой список&lt;br /&gt;   m_aCloseList.push_back(m_aOpenList[iChunkNum]);&lt;br /&gt;   m_aOpenList.erase(m_aOpenList.begin() + iChunkNum);&lt;br /&gt;   return true;&lt;br /&gt;}&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Ну и последняя функция, которая собирает все части пазла. В неё мы передаем начальную точку, конечную и указатель на вектор, который мы наполним точками найденного пути. Мы будем анализировать каждый раз клетку с наименьшим F, пока не добавим конечную точку в открытый список. Далее мы будем следовать с конца через мастер точки, добавляя их в путь, до тех пор, пока не дойдем до стартовой точки. Это и будет наш путь.&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;bool CPathfinding::GetWay(MapPoint From, MapPoint To, std::vector&lt; MapPoint &gt;* Way)&lt;br /&gt;{&lt;br /&gt;   // очищаем вектор, который мы передали в функцию, вдруг он не пустой&lt;br /&gt;   Way-&gt;clear();&lt;br /&gt;&lt;br /&gt;   // переводим стартовую точку в координаты сетки&lt;br /&gt;   int iStartX = From.x/WayMap-&gt;GetChunkSize();&lt;br /&gt;   int iStartZ = From.z/WayMap-&gt;GetChunkSize();&lt;br /&gt;   // проверяем, есть ли такая точка на карте&lt;br /&gt;   if (!WayMap-&gt;CheckRange(iStartX, iStartZ))&lt;br /&gt;      return false;&lt;br /&gt;&lt;br /&gt;   // переводим конечную точку в координаты сетки&lt;br /&gt;   iEndX = To.x/WayMap-&gt;GetChunkSize();&lt;br /&gt;   iEndZ = To.z/WayMap-&gt;GetChunkSize();&lt;br /&gt;   // проверяем, есть ли такая точка на карте&lt;br /&gt;   if (!WayMap-&gt;CheckRange(iEndX, iEndZ))&lt;br /&gt;      return false;&lt;br /&gt;&lt;br /&gt;   // находим их ID&lt;br /&gt;   int iIDFromChunk = WayMap-&gt;GetIDByPos(iStartX, iStartZ);&lt;br /&gt;   int iIDToChunk = WayMap-&gt;GetIDByPos(iEndX, iEndZ);&lt;br /&gt;   // если туда добраться нельзя, возвращаем false&lt;br /&gt;   if (!WayMap-&gt;IsCanWay(iIDFromChunk, iIDToChunk))&lt;br /&gt;      return false;&lt;br /&gt;   // добавляем в открытый список стартовую точку&lt;br /&gt;   WayChunk Chunk;&lt;br /&gt;   Chunk.ID = iIDFromChunk;&lt;br /&gt;   Chunk.MasterID = iIDFromChunk;&lt;br /&gt;   Chunk.G = 0;&lt;br /&gt;   Chunk.H = (abs(iStartX - iEndX)+abs(iStartZ - iEndZ))*WeightLine;&lt;br /&gt;   Chunk.F = Chunk.G + Chunk.H;&lt;br /&gt;   m_aOpenList.push_back(Chunk);&lt;br /&gt;    &lt;br /&gt;   // находим номер элемента, у которого минимальный F&lt;br /&gt;   int iNumInOpenList = GetMinWayChunk();&lt;br /&gt;   // до тех пор пока поиск не завершится, делаем анализ клетки с минимальным F&lt;br /&gt;   // а после опять ищем новую клетку с минимальным F&lt;br /&gt;   for (; iNumInOpenLis &gt; SEARTH_COMPLETED; iNumInOpenLis = GetMinWayChunk())&lt;br /&gt;   {&lt;br /&gt;      DoSearchStep(iNumInOpenLis);&lt;br /&gt;   }&lt;br /&gt;   &lt;br /&gt;   bool bDone = false;&lt;br /&gt;   // если конечная точка найдена&lt;br /&gt;   if (iNumInOpenLis == FIND_END)&lt;br /&gt;   {  &lt;br /&gt;      // добавляем конечную точку в наш вектор пути&lt;br /&gt;      MapPoint PointOfWay;&lt;br /&gt;      PointOfWay.x = iEndX * WayMap-&gt;GetChunkSize();&lt;br /&gt;      PointOfWay.z = iEndZ * WayMap-&gt;GetChunkSize();&lt;br /&gt;      int ID = -1;&lt;br /&gt;      int iMasterID = WayMap-&gt;GetIDByPos(iEndX, iEndZ); &lt;br /&gt;      // до тех пор пока мы в обратном порядке на найдем стартовую точку&lt;br /&gt;      // через мастер точки движемся с конца в начало, добавляя путь в вектор&lt;br /&gt;      while (ID != iMasterID) &lt;br /&gt;      {  &lt;br /&gt;         ID = iMasterID;&lt;br /&gt;         iMasterID = GetMasterID(ID);&lt;br /&gt;         PointOfWay.x = ID % WayMap-&gt;GetGridWidth()* WayMap-&gt;GetChunkSize();&lt;br /&gt;         PointOfWay.z = ID / WayMap-&gt;GetGridWidth()* WayMap-&gt;GetChunkSize();&lt;br /&gt;         Way-&gt;push_back(PointOfWay);&lt;br /&gt;      }&lt;br /&gt;      bDone = true;&lt;br /&gt;   }&lt;br /&gt;   // очищаем наши открытый и закрытый списки&lt;br /&gt;   m_aCloseList.clear();&lt;br /&gt;   m_aOpenList.clear();&lt;br /&gt;   return bDone;&lt;br /&gt;}&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;center&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;Путевая точка, находящаяся на пересечении. Этот вариант реализован в коде данной статьи.&lt;/span&gt;&lt;/center&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSsESg9yF4R8_ZFbrfURQX1OG0QPMU8iWeg2oDPcac0OyaAMDZOyWmja-q7cokoAVeuYicKYE8Mk5wF4n9hIzo5I5vaZQddKr4pILSEswPkVQ-ej4W_ZczkEJPawIqswLi10v83aki8WI/s1600/pos2.jpg&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 128px; height: 128px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSsESg9yF4R8_ZFbrfURQX1OG0QPMU8iWeg2oDPcac0OyaAMDZOyWmja-q7cokoAVeuYicKYE8Mk5wF4n9hIzo5I5vaZQddKr4pILSEswPkVQ-ej4W_ZczkEJPawIqswLi10v83aki8WI/s320/pos2.jpg&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5615525714363610914&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;center&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;Путевая точка, находящаяся в центре квадрата. Чтобы реализовать этот вариант, вам надо к каждой путевой точке добавить половину размера клетки карты по x и z. Именно этот вариант я буду использовать в дальнейшем.&lt;/span&gt;&lt;/center&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgqBSxe3EwpWeZzhXMBo9WJ_6Oz-7p1uCi-w0ogfH-ulzEKtpvmCDG3cDNaQt56uPZiXhqVi9wqJwnEcSaeykmSl_Icgf5AHDG6ZfC34klWxzVYEVhAevVPemSU1YD7nfuzmrdWJQmQ00/s1600/pos1.jpg&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 128px; height: 128px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgqBSxe3EwpWeZzhXMBo9WJ_6Oz-7p1uCi-w0ogfH-ulzEKtpvmCDG3cDNaQt56uPZiXhqVi9wqJwnEcSaeykmSl_Icgf5AHDG6ZfC34klWxzVYEVhAevVPemSU1YD7nfuzmrdWJQmQ00/s320/pos1.jpg&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5615525716854378402&quot; /&gt;&lt;/a&gt;&lt;br /&gt;Вот мы и заполнили вектор точками найденного пути. В общем все просто! А ещё говорят, что &lt;span style=&quot;font-weight:bold;&quot;&gt;алгоритм поиска пути A*&lt;/span&gt; сложен &lt;span style=&quot;font-weight:bold;&quot;&gt;для новичков&lt;/span&gt;. Стоит обратить внимание на то, что мы в наш путь добавляем все путевые точки подряд, что совсем не правильно. В дальнейшем мы рассмотрим способ добавления путевых точек, пропуская при этом лишние. А следующая статья будет посвящена опять таки алгоритму A star, но только уже в виде картинок, для полного понимания и закрепления наших знаний.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;a href=&quot;http://scr1pter.blogspot.com/2011/05/blog-post_13.html&quot;&gt;&lt;&lt; Назад&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/p/blog-page.html&quot;&gt;| Оглавление |&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/2011/05/star.html&quot;&gt;Вперед &gt;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;</description><link>http://scr1pter.blogspot.com/2011/05/blog-post_21.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2mDU6dMFb7Om-eIIGqroTSlKCCk-Vx3YnVO3raFR0NXz6aPcKjJ-OGu-8d6oFhnvp2P6qKvmDH8d_NujEaYR7QvgWYCnRGF2heQaUS3hyjajZv1MbNlJFRjsyP04DejUY5xxu63_HlDw/s72-c/Astar.jpg" height="72" width="72"/><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-6533255696668897831</guid><pubDate>Fri, 13 May 2011 17:39:00 +0000</pubDate><atom:updated>2011-06-07T16:12:10.557+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Искусственный интеллект</category><title>Пространство поиска пути</title><description>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjY9_mZuDMuez1ONbCqrhTZ480fCPvLNpPRtcnrXrtE7SBDxFEaCHsE-u_-X8cOKIzl5n7aPTsPZQu5gXvB9SDWF6dwnqsP2eDhQf4JGujCekuVbXSyommzu3KZ75Xy7RqLk99WnIaPSLo/s1600/%25D0%25B4%25D0%25BB%25D1%258F+%25D1%2581%25D1%2582%25D0%25B0%25D1%2582%25D1%258C%25D0%25B8.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 150px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjY9_mZuDMuez1ONbCqrhTZ480fCPvLNpPRtcnrXrtE7SBDxFEaCHsE-u_-X8cOKIzl5n7aPTsPZQu5gXvB9SDWF6dwnqsP2eDhQf4JGujCekuVbXSyommzu3KZ75Xy7RqLk99WnIaPSLo/s200/%25D0%25B4%25D0%25BB%25D1%258F+%25D1%2581%25D1%2582%25D0%25B0%25D1%2582%25D1%258C%25D0%25B8.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5606257905102395282&quot; /&gt;&lt;/a&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;b&gt;Пространство поиска пути&lt;/b&gt; - это территория разделенная на части, где будет искаться путь из одной точки в другую.&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Существует множество способов разделить пространство поиска пути. Это &lt;strong&gt;клетки&lt;/strong&gt;, &lt;strong&gt;шестиугольники&lt;/strong&gt;, &lt;strong&gt;путевые точки&lt;/strong&gt; (&lt;strong&gt;waypoints&lt;/strong&gt;) и &lt;strong&gt;полигонная сетка&lt;/strong&gt; (&lt;strong&gt;navigation mesh&lt;/strong&gt;). Для простоты я буду использовать &lt;strong&gt;клетки&lt;/strong&gt;.&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Мы создадим &lt;b&gt;пространство поиска пути &lt;/b&gt; в виде прямоугольника и разделим его на квадратики (такой квадрат называют ещё - chunk), но это задача легкая, поэтому усложним её. Так как в дальнейшем мы будем рассматривать &lt;strong&gt;алгоритм поиска пути&lt;/strong&gt; &lt;strong&gt;A* &lt;/strong&gt;(&lt;strong&gt;A star&lt;/strong&gt;), хотелось бы отметить главный из его недостатков. Он состоит в том, что для &lt;strong&gt;нахождения пути&lt;/strong&gt; к недоступному участку, алгоритм будет искать путь на всей карте, потратив много времени. Этого можно избежать, если разделить карту на закрытые участки (недоступные друг для друга). Об этом мы и поговорим.&lt;/div&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiA8gbQGtv3DazBf5zI6JUte7YxHhQlDyyMmZvskh7KJ31XdRldbnQTjT7phyl80-JyIdNvY0RLJ2iTx4rLYHpZEnfWb75X5Ha1MQvQqaiNcPWCeOCNpA9FdUR7SJnb8svFNajrfDFsIBc/s1600/zones.jpg&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 246px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiA8gbQGtv3DazBf5zI6JUte7YxHhQlDyyMmZvskh7KJ31XdRldbnQTjT7phyl80-JyIdNvY0RLJ2iTx4rLYHpZEnfWb75X5Ha1MQvQqaiNcPWCeOCNpA9FdUR7SJnb8svFNajrfDFsIBc/s320/zones.jpg&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5614693204601960674&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Итого наша задача не только создать &quot;карту&quot; пути, но и сделать так, чтобы она делилась на зоны (&quot;острова&quot;).&lt;/div&gt;&lt;br /&gt;&lt;center&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;Работу программы, которую мы создадим, можно посмотреть тут:&lt;/span&gt;&lt;/center&gt;&lt;center&gt;&lt;iframe width=&quot;425&quot; height=&quot;349&quot; src=&quot;http://www.youtube.com/embed/JRLqfhngAjw&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Как вы заметили по видео, если был изменен цвет в какой то области, то там происходила &lt;strong&gt;проверка либо на существование отделенных друг от друга зон&lt;/strong&gt;, либо на их &lt;span style=&quot;font-weight:bold;&quot;&gt;слияние&lt;/span&gt;.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Давайте пройдемся по этапам нашего алгоритма:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Указываем номер области, для которой нужно сделать проверку.&lt;br /&gt;&lt;li&gt;Ищем элемент карты, совпадающий с указанным номером, и добавляем его в массив для обработки. Этот квадрат будет являться частью новой зоны.&lt;br /&gt;&lt;li&gt;Берем первый элемент из массива клеток, которые нам нужно проверить, и ищем для него проходимых соседей, которые будут добавляться в тот же массив. После чего элементу, который мы проанализировали, мы присваиваем новый номер зоны и удаляем его. Этот этап мы повторяем до тех пор, пока наш массив не станет пустым.&lt;br /&gt;&lt;li&gt;Повторяем пункт №2 до тех пор, пока совпадений по заданному номеру области не будет обнаружено.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Пора переходить к практике. Создадим класс поиска пути с именем «&lt;strong&gt;CWayMap&lt;/strong&gt;», который потом будет использоваться и для алгоритма &lt;strong&gt;А* &lt;/strong&gt;(&lt;strong&gt;A star&lt;/strong&gt;).&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;class CWayMap&lt;br /&gt;{&lt;br /&gt;private:&lt;br /&gt;   // структура, которая будет являться каждой ячейкой сетки&lt;br /&gt;   struct CMapChunk&lt;br /&gt;   {&lt;br /&gt;      // проходимость&lt;br /&gt;      int Passability;&lt;br /&gt;   };&lt;br /&gt;   // тип переменной для проходимости, означающий что нужна проверка&lt;br /&gt;   #define NO_INIT -1&lt;br /&gt;   // освобождение памяти&lt;br /&gt;   #define SafeDelete(b) if (b) { delete b; b = NULL;}&lt;br /&gt;&lt;br /&gt;   // cетка, которая состоит из наших клеток&lt;br /&gt;   CMapChunk * m_Grid;&lt;br /&gt;   // ширина сетки&lt;br /&gt;   int m_iGridWidth;&lt;br /&gt;   // высота сетки&lt;br /&gt;   int m_iGridHeight;&lt;br /&gt;   // размер чанка (клетки)&lt;br /&gt;   int m_iChunkSize;&lt;br /&gt;   // счетчик зон&lt;br /&gt;   int m_iCountZone;&lt;br /&gt;&lt;br /&gt;   // разделение клеток с определенным типом проходимости на зоны&lt;br /&gt;   void SplitZones(int iPassabilityType);&lt;br /&gt;   // создание зоны&lt;br /&gt;   void CreateZone(int iChunkID, int NewZone);&lt;br /&gt;   // проверки на воможность перемещения по диагонали&lt;br /&gt;   bool CheckDiagonals(int iNeighborX, int iNeighborZ, int iMasterX, int iMasterZ);&lt;br /&gt;public:&lt;br /&gt;   CWayMap(int iWidth, int iHeight, int iSize);&lt;br /&gt;   ~CWayMap(void);&lt;br /&gt;   // проверка существует ли клетка с указанной шириной и высотой в сетке&lt;br /&gt;   bool CheckRange(int iChunkX, int iChunkZ);&lt;br /&gt;   // смена значения проходимости клетки&lt;br /&gt;   void ChangePassability(int x, int z);&lt;br /&gt;   // возвращаем ID клетки по его ширине и высоте&lt;br /&gt;   int GetIDByPos(int iChunkX, int iChunkZ);&lt;br /&gt;   // проверка на диапазон и проходимость клетки&lt;br /&gt;   bool CheckPassability(int iChunkX, int iChunkZ);&lt;br /&gt;};&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;strong&gt;Passability&lt;/strong&gt; у нас будет отвечать за проходимость каждой ячейки сетки. Так же &lt;strong&gt;ячейку&lt;/strong&gt; сетки я буду называть – &lt;strong&gt;клетка&lt;/strong&gt; и &lt;strong&gt;чанк&lt;/strong&gt;. Таким образом, сравнив значение &lt;strong&gt;Passability&lt;/strong&gt; стартовой и конечной точки пути, можно будет сразу узнать – &lt;strong&gt;можно туда попасть или нет&lt;/strong&gt;. А если оно равно нулю, то эта зона не проходима, таким образом можно легко это будет проверить:&lt;/div&gt;&lt;pre&gt;&lt;strong&gt;&lt;i&gt;Если&lt;/i&gt;&lt;/strong&gt; (Passability) &lt;strong&gt;&lt;i&gt;То&lt;/i&gt;&lt;/strong&gt; можем_пройти;&lt;/pre&gt;Начнем с конструктора:&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;CWayMap::CWayMap(int iWidth, int iHeight, int iSize)&lt;br /&gt;{&lt;br /&gt;   // запоминаем размер ячейки&lt;br /&gt;   m_iChunkSize = iSize;&lt;br /&gt;   // вычисляем кол-во ячеек в ширину&lt;br /&gt;   m_iGridWidth = iWidth/iSize;&lt;br /&gt;   // вычисляем кол-во ячеек в высоту&lt;br /&gt;   m_iGridHeight = iHeight/iSize;&lt;br /&gt;&lt;br /&gt;   m_Grid = NULL;&lt;br /&gt;   // создаем нашу сетку с нужным кол-ом ячеек&lt;br /&gt;   m_Grid = new CMapChunk[m_iGridWidth*m_iGridHeight];&lt;br /&gt;   // проходимся по всем ячейкам и выставляем им одну зону&lt;br /&gt;   for (int x = 0; x &amp;lt; m_iGridWidth*m_iGridHeight; x++)&lt;br /&gt;   {&lt;br /&gt;      m_Grid[x].Passability = NO_INIT;&lt;br /&gt;   }&lt;br /&gt;   // у нас пока 0 зон, указываем это&lt;br /&gt;   int m_iCountZone = 0;&lt;br /&gt;   // делим нашу сетку на зоны, если они есть&lt;br /&gt;   SplitZones(NO_INIT);&lt;br /&gt;}&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Тут все просто… мы передаем в функцию размеры нашей &lt;strong&gt;игровой карты&lt;/strong&gt; и &lt;strong&gt;размер ячейки&lt;/strong&gt;, на которые будет разбита эта карта. &lt;strong&gt;Разбиваем карту&lt;/strong&gt;, сохраняем нужные нам параметры и присваиваем каждой созданной ячейке значение &lt;strong&gt;NO_INIT&lt;/strong&gt;. Указываем, что у нас на данный момент нет зон, и используем функцию «&lt;strong&gt;SplitZones&lt;/strong&gt;», которая проверяет клетки с типом проходимости &lt;strong&gt;NO_INIT&lt;/strong&gt; и разделяет их на зоны, если они есть, но об этом позже.&lt;/div&gt;&lt;br /&gt;В деструкторе освобождаем память, которую выделяли для нашей сетки.&lt;br /&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;CWayMap::~CWayMap(void)&lt;br /&gt;{&lt;br /&gt;  // освобождение памяти&lt;br /&gt;   SafeDelete(m_Grid);&lt;br /&gt;}&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Функция «&lt;strong&gt;CheckRange&lt;/strong&gt;» проверяет, существует ли указанная ячейка в диапазоне нашей сетки, это понадобится при поиске соседей у клетки. Ведь на границе карты у нашей клетки может отсутствовать несколько соседей.&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;bool CWayMap::CheckRange(int iChunkX, int iChunkZ)&lt;br /&gt;{&lt;br /&gt;   // если ширина ячейки меньше 0 или больше ширины сетки, возвращаем false,&lt;br /&gt;   // с высотой так же.&lt;br /&gt;   if ( iChunkX &amp;lt; 0 || iChunkZ &amp;lt; 0 || iChunkX &amp;gt; m_iGridWidth-1 || iChunkZ &amp;gt; m_iGridHeight-1)&lt;br /&gt;      return false;&lt;br /&gt;   return true;&lt;br /&gt;}&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Функция «&lt;strong&gt;GetIDByPos&lt;/strong&gt;» возвращает номер клетки по высоте и ширине сетки.&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;int CWayMap::GetIDByPos(int iChunkX, int iChunkZ)&lt;br /&gt;{&lt;br /&gt;   return iChunkZ*m_iGridWidth + iChunkX;&lt;br /&gt;}&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;«&lt;strong&gt;CheckDiagonals&lt;/strong&gt;» проверяет, является ли найденный сосед (клетка находящаяся рядом с исследуемой)  «мастер» клетки диагональным, и если да, то можно ли туда &lt;strong&gt;переместиться, не срезав углы недоступных клеток&lt;/strong&gt;. В моем примере я решил не допускать срезы углов (рис.1).&lt;/div&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjRCHuwbxQ6sqZlcmeWlHmPbNgGfr5AjUtqTemMrlaF4qp4E_3aKHUN3m-HvNdtTDvMnEFd9UOuaageS-NV2yE5l29V0VvY8KX3UNI33mqjCOTaty6wtBgEFuv7kEmSPqcsT1Siv17KFs/s1600/%25D0%25A1%25D1%2580%25D0%25B5%25D0%25B7%25D0%25BA%25D0%25B0+%25D1%2583%25D0%25B3%25D0%25BB%25D0%25BE%25D0%25B2.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 190px; height: 193px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjRCHuwbxQ6sqZlcmeWlHmPbNgGfr5AjUtqTemMrlaF4qp4E_3aKHUN3m-HvNdtTDvMnEFd9UOuaageS-NV2yE5l29V0VvY8KX3UNI33mqjCOTaty6wtBgEFuv7kEmSPqcsT1Siv17KFs/s200/%25D0%25A1%25D1%2580%25D0%25B5%25D0%25B7%25D0%25BA%25D0%25B0+%25D1%2583%25D0%25B3%25D0%25BB%25D0%25BE%25D0%25B2.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5606263524464652194&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;center&gt;&lt;em&gt;Рис.1 неразрешенный срез угла недоступной ячейки, красная точка – мастер точка, зеленая точка – её диагональный сосед.&lt;/em&gt;&lt;/center&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;В эту функцию мы передаем «координаты» соседа и его хозяина.&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;bool CWayMap::CheckDiagonals(int iNeighborX, int iNeighborZ, int iMasterX, int iMasterZ)&lt;br /&gt;{&lt;br /&gt;   // вычисяем разницу в ширине между соседем и «хозяином»&lt;br /&gt;   int iDiffX = iNeighborX - iMasterX;&lt;br /&gt;   // вычисяем разницу в высоте между соседем и «хозяином»&lt;br /&gt;   int iDiffZ = iNeighborZ - iMasterZ;&lt;br /&gt;   // если сосед является диагональным&lt;br /&gt;   if (iDiffX &amp;amp;&amp;amp; iDiffZ)&lt;br /&gt;   {&lt;br /&gt;      // находим первого соседа для соседа соприкасающимся с «хозяином»&lt;br /&gt;      int iContact1X = iMasterX + iDiffX;&lt;br /&gt;      int iContact1Y = iMasterZ;&lt;br /&gt;      // если найденный сосед не проходимый, возвращаем false&lt;br /&gt;      if (!m_Grid[GetIDByPos(iContact1X, iContact1Y)].Passability)&lt;br /&gt;         return false;&lt;br /&gt;      // находим второго соседа для соседа соприкасающимся с «хозяином»&lt;br /&gt;      int iContact2X = iMasterX;&lt;br /&gt;      int iContact2Y = iMasterZ + iDiffZ;&lt;br /&gt;      // если найденный сосед не проходимый, возвращаем false&lt;br /&gt;      if (!m_Grid[GetIDByPos(iContact2X, iContact2Y)].Passability)&lt;br /&gt;         return false;&lt;br /&gt;   }&lt;br /&gt;   // в противном случае сосед «мастера» проходимый&lt;br /&gt;   return true;&lt;br /&gt;}&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;«&lt;b&gt;CheckPassability&lt;/b&gt;» проверяет, существует ли такой чанк в диапазоне нашей сетки и если существует, то является ли он проходимым.&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;bool CWayMap::CheckPassability(int iChunkX, int iChunkZ)&lt;br /&gt;{&lt;br /&gt;   // если сосед существует в нашем диапазоне&lt;br /&gt;   if (CheckRange(iChunkX, iChunkZ))&lt;br /&gt;   {&lt;br /&gt;      // если этот сосед ещё не проверялся и является проходимым&lt;br /&gt;      if (m_Grid[GetIDByPos(iChunkX, iChunkZ)].Passability)      &lt;br /&gt;         return true;    &lt;br /&gt;   }&lt;br /&gt;   return false;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;«&lt;b&gt;CreateZone&lt;/b&gt;» это функция непосредственного &lt;strong&gt;создания зоны&lt;/strong&gt;. В неё мы передаем номер клетки, с которой начнется исследование по определению закрытой зоны, а так же новый номер зоны, который мы будем присваивать.&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;void CWayMap::CreateZone(int iIDChunk, int iNewZone)&lt;br /&gt;{&lt;br /&gt;   // вектор номеров ячеек, для которых надо найти всех соседей&lt;br /&gt;   std::vector  aListForCheck;&lt;br /&gt;   // добавляем туда номер ячейки из параметра функции&lt;br /&gt;   aListForCheck.push_back(iIDChunk);&lt;br /&gt;&lt;br /&gt;   // до тех пор пока наш вектор не пуст&lt;br /&gt;   while(aListForCheck.size() != 0)&lt;br /&gt;   {&lt;br /&gt;      // вычисляем по номеру ячейки его ширину и высоту в сетке&lt;br /&gt;      int iChunkX = aListForCheck[0] % m_iGridWidth;&lt;br /&gt;      int iChunkZ = aListForCheck[0] / m_iGridWidth;&lt;br /&gt;      // проходимся по всем соседям&lt;br /&gt;      for (int i = -1; i &amp;lt;= 1; i++)&lt;br /&gt;      {&lt;br /&gt;         for (int j = -1; j &amp;lt;= 1; j++)&lt;br /&gt;         {&lt;br /&gt;            int iNeighborX = iChunkX+i;&lt;br /&gt;            int iNeighborZ = iChunkZ+j;&lt;br /&gt;            // проверка на диапазон и проходимость клетки&lt;br /&gt;            if (CheckPassability(iNeighborX, iNeighborZ))&lt;br /&gt;            {&lt;br /&gt;               // по его ширине и высоте находим номер в сетке&lt;br /&gt;               int iNeighborID = GetIDByPos(iNeighborX, iNeighborZ);&lt;br /&gt;               if (m_Grid[iNeighborID].Passability != iNewZone)      &lt;br /&gt;               {&lt;br /&gt;                  // если он является диагональным, то можно ли&lt;br /&gt;                  // пройти в него не срезав углы&lt;br /&gt;                  if (CheckDiagonals(iNeighborX, iNeighborZ, iChunkX, iChunkZ))&lt;br /&gt;                  {&lt;br /&gt;                     // устанавливаем ему новую зону&lt;br /&gt;                     m_Grid[iNeighborID].Passability = iNewZone;&lt;br /&gt;                     // добавляем его в вектор, для поиска его соседей&lt;br /&gt;                     aListForCheck.push_back(iNeighborID);&lt;br /&gt;                  }&lt;br /&gt;               }&lt;br /&gt;            }&lt;br /&gt;         }&lt;br /&gt;      }&lt;br /&gt;   // удаляем из вектора проверенную ячейку&lt;br /&gt;   aListForCheck.erase(aListForCheck.begin());&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Суть такова: мы добавляем номер ячейки в вектор, в котором каждый элемент проверяется на возможных &lt;strong&gt;проходимых соседей&lt;/strong&gt;, которые тоже потом добавляются в вектор. И так до тех пор, пока количество элементов в векторе не будет равно нулю. А это значит, что функция будет искать всех соседей и метить их до тех пор, пока мы не найдем &lt;strong&gt;закрытую зону&lt;/strong&gt;.&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Ну и вот наша главная функция «&lt;span style=&quot;font-weight:bold;&quot;&gt;SplitZones&lt;/span&gt;», которая использовалась в конструкторе:&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;void CWayMap::SplitZones(int iPassabilityType)&lt;br /&gt;{&lt;br /&gt;   // пробегаемся по всем ячейкам сетки&lt;br /&gt;   for (int x = 0; x &amp;lt; m_iGridWidth*m_iGridHeight; x++)&lt;br /&gt;   {&lt;br /&gt;      // если это та зона, которую нужно проверить&lt;br /&gt;      if (m_Grid[x].Passability == iPassabilityType)&lt;br /&gt;      {&lt;br /&gt;         // увеличиваем наши зоны&lt;br /&gt;         if (++m_iCountZone &gt; INT_MAX -1)&lt;br /&gt;            // ошибка, счетчик переполнится и будет баг&lt;br /&gt;            showerror(&quot;ошибка, счетчик переполнится и будет баг&quot;);&lt;br /&gt;         // создаем новую зону&lt;br /&gt;         CreateZone(x, m_iCountZone);&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Наверно уже понятно как она работает. Мы указываем номер зоны, который надо проверить (в конструкторе это &lt;strong&gt;NO_INIT&lt;/strong&gt;), при первом поиске элемента с нужной зоной, функция «&lt;strong&gt;CreateZone&lt;/strong&gt;» находит всех её соседей... потом соседей соседей… и так далее, присваивая им новое значение зоны… Но если мы потом опять в цикле встретим указанный номер зоны для проверки, то &lt;strong&gt;эта зона отделена от первой&lt;/strong&gt; и проделываем для неё тоже самое. Ну и так далее пока не найдем все &lt;strong&gt;отделенные друг от друга зоны&lt;/strong&gt;.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;Как же быть, если произошли &lt;strong&gt;изменения на карте&lt;/strong&gt;? Я сделал так:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;если ячейка изменилась на непроходимую, то проверяю, сколько у неё таких же непроходимых соседей, если больше 1 (не включая её саму), то делаю проверку на &lt;strong&gt;разделение той зоны&lt;/strong&gt;, в которой появилась новая «стенка».&lt;/li&gt;&lt;br /&gt;&lt;li&gt;если же «стенка» наоборот убралась, я устанавливаю новую зону всем её уникальным по зонам соседям, и делаю &lt;strong&gt;проверку этой новой общей зоны&lt;/strong&gt;.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/div&gt;&lt;pre class=&quot;brush: cpp&quot;&gt;void CWayMap::SetPassability(int x, int z)&lt;br /&gt;{&lt;br /&gt;   // x и z это оконные координаты клика мыши, по ним находим ширину и высоту&lt;br /&gt;   // в сетке ячейки на которую тыкнули&lt;br /&gt;   int iChunkX = int(x)/m_iChunkSize;&lt;br /&gt;   int iChunkZ = int(z)/m_iChunkSize;&lt;br /&gt;   // по ширине и высоте ячейки находим её номер в сетке&lt;br /&gt;   int iChunkID = GetIDByPos(iChunkX, iChunkZ);&lt;br /&gt;&lt;br /&gt;   // запоминаем значение проходимости, так как мы его изменим&lt;br /&gt;   int iChangePassability = m_Grid[iChunkID].Passability;&lt;br /&gt;   // если мы убираем препятствие&lt;br /&gt;   if (!iChangePassability)&lt;br /&gt;   {&lt;br /&gt;      // указываем что нужна проверка&lt;br /&gt;      iChangePassability = NO_INIT;&lt;br /&gt;      m_Grid[iChunkID].Passability = NO_INIT;&lt;br /&gt;   }&lt;br /&gt;   else&lt;br /&gt;      // меняем на непроходимую&lt;br /&gt;      m_Grid[iChunkID].Passability = 0;&lt;br /&gt;&lt;br /&gt;   // вектор зон, которые возможно соединятся&lt;br /&gt;   std::vector&lt; int &gt; aZoneForSplit;&lt;br /&gt;&lt;br /&gt;   // кол-во найденных непроходимых соседей&lt;br /&gt;   int iCountBlock = 0;&lt;br /&gt;   // пробегаемся по соседям&lt;br /&gt;   for (int i = -1; i &lt;= 1; i++)&lt;br /&gt;   {&lt;br /&gt;      for (int j = -1; j &lt;= 1; j++)&lt;br /&gt;      {&lt;br /&gt;         // если это и есть ячейка, соседей которых мы ищем, то пропускаем&lt;br /&gt;         if (!j &amp;&amp; !i)&lt;br /&gt;            continue;&lt;br /&gt;&lt;br /&gt;         int iNeighborX = iChunkX+i;&lt;br /&gt;         int iNeighborZ = iChunkZ+j;&lt;br /&gt;         // по ширине и высоте находим номер в сетке&lt;br /&gt;         int iNeighborID = GetIDByPos(iNeighborX, iNeighborZ);&lt;br /&gt;         // проверка на диапазон и проходимость клетки&lt;br /&gt;         if (CheckPassability(iNeighborX, iNeighborZ))&lt;br /&gt;         {&lt;br /&gt;            // если по клику мыши мы убрали «стену»&lt;br /&gt;            if (iChangePassability == NO_INIT)&lt;br /&gt;            {&lt;br /&gt;               // уникальна ли зона&lt;br /&gt;               bool bNewSplitZone = true;&lt;br /&gt;               for (unsigned int i = 0; i &lt; aZoneForSplit.size(); i++ )&lt;br /&gt;               {&lt;br /&gt;                  // если зона уже есть в списке, то она не уникальна&lt;br /&gt;                  if (aZoneForSplit[i] == m_Grid[iNeighborID].Passability)&lt;br /&gt;                     bNewSplitZone = false;&lt;br /&gt;               }&lt;br /&gt;               // если зона уникальная, то добавляем&lt;br /&gt;               if (bNewSplitZone)&lt;br /&gt;                  aZoneForSplit.push_back(m_Grid[iNeighborID].Passability);&lt;br /&gt;    &lt;br /&gt;               // меняем цвет зоны, где убрали стенку, на цвет соседа &lt;br /&gt;               m_Grid[iChunkID].Passability = m_Grid[iNeighborID].Passability;&lt;br /&gt;            }&lt;br /&gt;         }&lt;br /&gt;         // иначе увеличиваем счетчик «плохих» соседей&lt;br /&gt;         else&lt;br /&gt;            iCountBlock++; &lt;br /&gt;&lt;br /&gt;         // если найденных непроходимых соседей больше 1&lt;br /&gt;         if (iCountBlock &gt; 1)&lt;br /&gt;         {&lt;br /&gt;            // если по клику мыши мы убрали «стену»&lt;br /&gt;            if (iChangePassability == NO_INIT)&lt;br /&gt;            {&lt;br /&gt;               // пробегаемся по всем ячейкам и из всех зон в aZoneForSpli&lt;br /&gt;               // делаем одну общую&lt;br /&gt;               for (int x=0; x &lt; (m_iGridWidth * m_iGridHeight); x++)&lt;br /&gt;               {&lt;br /&gt;                  for (unsigned int i = 0; i &lt; aZoneForSplit.size(); i++ )&lt;br /&gt;                  {&lt;br /&gt;                     if (aZoneForSplit[i] == m_Grid[x].Passability)&lt;br /&gt;                     {&lt;br /&gt;                        m_Grid[x].Passability = NO_INIT;&lt;br /&gt;                        break;&lt;br /&gt;                     }&lt;br /&gt;                  }&lt;br /&gt;               }&lt;br /&gt;            }&lt;br /&gt;            // перепроверяем и делим зону, если это требуется&lt;br /&gt;            SplitZones(iChangePassability);&lt;br /&gt;            return;&lt;br /&gt;         }&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Вот и все!&lt;/span&gt; Мы создали и разделили пространство поиска пути на клетки, по которым сможем искать путь. Так же реализовали &quot;островную&quot; систему, которая автоматически разделяет и делает слияние непроходимых зон на нашей карте. И теперь мы можем сразу определить до поиска пути: можно ли добраться из одной клетки в другую или нет? &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;a href=&quot;http://scr1pter.blogspot.com/2011/05/blog-post.html&quot;&gt;&lt;&lt; Назад&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/p/blog-page.html&quot;&gt;| Оглавление |&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/2011/05/blog-post_21.html&quot;&gt;Вперед &gt;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;</description><link>http://scr1pter.blogspot.com/2011/05/blog-post_13.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjY9_mZuDMuez1ONbCqrhTZ480fCPvLNpPRtcnrXrtE7SBDxFEaCHsE-u_-X8cOKIzl5n7aPTsPZQu5gXvB9SDWF6dwnqsP2eDhQf4JGujCekuVbXSyommzu3KZ75Xy7RqLk99WnIaPSLo/s72-c/%25D0%25B4%25D0%25BB%25D1%258F+%25D1%2581%25D1%2582%25D0%25B0%25D1%2582%25D1%258C%25D0%25B8.jpg" height="72" width="72"/><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-5769210952781899111</guid><pubDate>Wed, 11 May 2011 20:04:00 +0000</pubDate><atom:updated>2011-05-22T15:10:08.021+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Искусственный интеллект</category><title>Искусственный интеллект</title><description>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGsuAnOPZBiuS3hqoVpVeuwFEVUC1usA1iltkek9n_5nOE5mivdzJae25fBg6B0V8IQDhCEICZnLAvkcL4gVqGJ-5bJjjWVE_OaEoeh6KwQmPF78GY7Z8vJcPYsRbN5liu8OUNrVxsO1A/s1600/brain.jpg&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px; height: 152px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGsuAnOPZBiuS3hqoVpVeuwFEVUC1usA1iltkek9n_5nOE5mivdzJae25fBg6B0V8IQDhCEICZnLAvkcL4gVqGJ-5bJjjWVE_OaEoeh6KwQmPF78GY7Z8vJcPYsRbN5liu8OUNrVxsO1A/s200/brain.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5605557569987338546&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Рассмотрим в этой статье искусственный интеллект. Статья будет разбита на разделы, поэтому каждый сможет быстро найти то, что его интересует.&lt;/div&gt;&lt;b&gt;1. Искусственный интеллект и его понятие.&lt;/b&gt;&lt;div&gt;&lt;b&gt;2. Главные проблемы.&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;3. Задачи.&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;4. Технологии создания.&lt;/b&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;1. Искусственный интеллект и его понятие.&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Искусственный интеллект (&lt;b&gt;ИИ&lt;/b&gt;, &lt;b&gt;Artificial intelligence&lt;/b&gt;, &lt;b&gt;AI&lt;/b&gt;) — набор программных методик, которые используются в компьютерных играх для создания иллюзии интеллекта в поведении персонажей, управляемых компьютером. Его применяют для контролирования неигровых персонажей (&lt;b&gt;Non-player character&lt;/b&gt;, &lt;b&gt;NPC&lt;/b&gt;).&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Искусственный интеллект является не просто частью игры, а одним из самых главных её составляющих. Кто то может представить себе игру без  врагов? С кем нам сражаться и воевать, если их не будет?&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Даже тетрис становится интересней, если компьютер будет играть параллельно с нами в каком-нибудь уголку окна, ведь появляется дух соперничества. Но не только вражеские интеллектуалы доставляют радость в играх. К большому сожалению, не во всех играх разработчики на сторону игрока ставят дружественных объектов «с мозгами». Постоянно бегать/летать/скакать одному очень угнетает, а когда кто-то рядом уже не так страшно, да и намного интересней.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;2. Главные проблемы&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Многие наверно скажут, что главная проблема, это то, что искусственный интеллект никогда не заменит человека. А ведь в компьютерных играх этого совсем и не нужно. Кто захочет перестреливаться с вражеским юнитом в какой-нибудь FSP (First-person shooter) десятки минут? Для этого вполне подойдет игра по сети.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;По мне, две главные проблемы это – «сверхглупые» и «сверхумные» враги. В первом случае игрок довольно быстро заскучает, во втором обидится, а это приведет к тому, что игра будет закрыта. А кому нравится проигрывать? Целью искусственного интеллекта является вовсе не обыгрывать игрока.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Супер пупер врага наверно легче написать, чем глупого, так как у них нет погрешности при стрельбе, он знает куда надо стрелять (чтобы сразу попасть в голову), он имеет данных больше чем игрок, поэтому его от них надо ограничить. Можно сделать проверку, которая скажет виден ли враг для нашего NPC или нет, можно сделать ему разброс при стрельбе, способов подпортить его читерские способности навалом.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;3. Задачи&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Теперь, когда мы знаем понятие искусственный интеллект и где он применяется, хотелось бы узнать его задачи. Пустышкам, наделенных &quot;&lt;b&gt;электронным мозгом&lt;/b&gt;&quot;, обычно приходится решать в ходе игры такие же задачи, что и игрокам. А значит, мы сами ставим им задачи и учим их самостоятельно с ними справляться. Примерами таких задач являются следующие категории:&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;ul&gt;&lt;li style=&quot;text-align: justify;&quot;&gt;Движение (нахождение пути, патрулирование, перемещение, преследование, уклонение и др.);&lt;/li&gt;&lt;li style=&quot;text-align: justify;&quot;&gt;Атака (стрельба, перезарядка, бросок гранаты, установка мины и др.);&lt;/li&gt;&lt;li style=&quot;text-align: justify;&quot;&gt;Взаимодействие с предметами (включить/выключить свет, пнуть предмет, попавшийся на пути, подобрать боеприпасы и др.);&lt;/li&gt;&lt;li style=&quot;text-align: justify;&quot;&gt;Связь с другими NPC (переговоры по рации, включение сигнализации, запуск сигнальной ракеты и др.)&lt;/li&gt;&lt;/ul&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Полно других очень и очень даже интересных задач.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;4. Технологии создания ИИ в играх&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Теперь ознакомимся с некоторыми самыми популярными технологиями создания игрового ИИ.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;Система, основанная на правилах (Rule Based Systems).&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Она заключается в составлении узкой области поведения при каких либо условиях. Таким образом, частичка этой системы состоит из условия и действия, а при объединении этих частиц получается - автоматизированная система, основанная на правилах (проверках).  Как только одно из условий этой цепи возвращает истину, выполняется его действие и выход из системы.&lt;/div&gt;&lt;br /&gt;Пример вражеского патрулирующего NPC:&lt;br /&gt;&lt;br /&gt;Если наши жизни &amp;lt; 20%, то убегаем.&lt;br /&gt;Если враг в зоне нашей атаки, то атакуем.&lt;br /&gt;Если мы видим врага, бежим к нему.&lt;br /&gt;Если наши силы = 100%, патрулируем.&lt;br /&gt;Стоим.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Как видно из примера, последнее действие не имеет условия. Достаточно примитивный и простой способ для организации ИИ в простых играх. При большом объеме поставленных задач для ИИ, этот способ крайне неэффективен.&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;Конечные автоматы (Finite State Machine).&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Эта система, описывающая поведение объекта, используют заранее подготовленные состояния, выбирая нужное.  Для удобства рассмотрим пример выше, у нас получатся следующие состояния: стоять, патрулировать, преследовать, атаковать, убегать. Мы находимся в том или ином состоянии до тех пор, пока не произойдет некое событие.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Например: в состоянии патрулирования, если мы увидели врага, мы переходим в состояние «атаковать», а в случае если устали, то в состояние «стоять».&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Конечные автоматы бывают детерминированными и недетерминированными. Детерминированные это те конечные автоматы, переход состояний которых определен строго. Недетерминированные же имеют некую случайность. Скажем, мы можем и вовсе не входить в состояние убегать, а воевать до победного конца или смерти. А так же мы можем «случайно» не увидеть врага, которого бы увидели в детерминированном конечном автомате.&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;Нейронные сети (Neural Network).&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Нейронные сети эмулируют нервные клетки живого организма. Эта технология состоит из двух этапов: обучение и использование. Сам по себе искусственный интеллект сложно отлаживать, а если он основан на технологиях нейронных сетей, то вообще пиши пропало. Сама система немного сложна и я с ней ещё не сталкивался, но посчитал нужным, хотя бы просто сказать о ней пару слов.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;a href=&quot;http://scr1pter.blogspot.com/p/blog-page.html&quot;&gt;| Оглавление |&lt;/a&gt;   &lt;a href=&quot;http://scr1pter.blogspot.com/2011/05/blog-post_13.html&quot;&gt;Вперед &gt;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;</description><link>http://scr1pter.blogspot.com/2011/05/blog-post.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGsuAnOPZBiuS3hqoVpVeuwFEVUC1usA1iltkek9n_5nOE5mivdzJae25fBg6B0V8IQDhCEICZnLAvkcL4gVqGJ-5bJjjWVE_OaEoeh6KwQmPF78GY7Z8vJcPYsRbN5liu8OUNrVxsO1A/s72-c/brain.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-4850392368807027026</guid><pubDate>Thu, 28 Apr 2011 10:17:00 +0000</pubDate><atom:updated>2011-04-28T14:44:16.129+04:00</atom:updated><title>Интересные цитаты</title><description>Ошибка - не грех. Неспособность к обучению на основе ошибок - вот что такое грех.&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt; &lt;span style=&quot;font-weight:bold;&quot;&gt;Макконнелл.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Если вы не недостаточно любопытны для того, чтобы не отставать от изменений, вы рискуете разделить участь динозавров. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Макконнелл.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Требования подобны воде. Опираться на них легче, если они заморожены. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Аноним.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Если вы не можете объяснить что-то шестилетнему ребенку, значит, вы этого сами не понимаете. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Энштейн.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Мы пытаемся решить проблему, максимаьно ускоряя процесс проектирования, чтобы в конце работы над системой у нас осталось достаточно времени на нахождение ошибок, допущенных из-за слишком быстрого проектирования. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;b&gt;Мейерс.&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;Я никогда не встречал человека, желающего читать 17000 страниц документации, е если бы встретил, то убил его бы его, чтобы он не портил генофонд. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Костелло.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Если для понимания того, что происходит, нужно увидеть реализацию, это не абстракция.&lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt; &lt;span style=&quot;font-weight:bold;&quot;&gt;Плоджер.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Неработающая программа обычно приносит меньше вреда, чем работающая плохо. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Энди Хант и Дейв Томас.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Если бы строители возводили здания так, как программисты пишут программы, первый же дятел уничтожил бы цивилизацию. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Вайберг.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Программы - не люди, а ошибки не микробы: программа не может нахвататься ошибок, общаясь с другими дефектными программами. Ошибки всегда допускают программисты.&lt;/div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Миллз.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Отлаживать код вдвое сложнее, чем писать. Поэтому, если при написании программы вы используете весь свой интеллект, вы по определению недостаточно умны, чтобы её отладить. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Карниган.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Никогда не отлаживайте программу стоя. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Вайнберг.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Все удачные программы изменяются. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Брукс.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Не бывает кода, настолько громоздского, изощренного или сложного, чтобы его нельзя было ухудшить при сопровождении. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Вайнберг.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Крупномасштабный рефакторинг - путь к катастрофе. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Бек.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Необдуманная оптимизация - корень всего зла. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Кнут.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Любой дурак может написать код, понятный компьютеру. Хорошие программисты пишут код, понятный людям. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Фаулер.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Пишите код, исходя из того, что все программсты, которые будут сопровождать вашу программу, - склонные к насилию психопаты, знающие, где вы живете. &lt;/div&gt;&lt;div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: bold; &quot;&gt;Аноним.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Любой дурак способен отстаивать свои ошибка - большинство дураков именно так и делают.&lt;/div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Карнеги.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: right;&quot;&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Цитаты взяты из книги &quot;Совершенный код&quot;  С.Макконнелл.&lt;/span&gt;&lt;/div&gt;</description><link>http://scr1pter.blogspot.com/2011/04/blog-post.html</link><author>noreply@blogger.com (Scripter)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-3014267737368437895</guid><pubDate>Wed, 20 Apr 2011 13:24:00 +0000</pubDate><atom:updated>2011-05-12T00:02:17.487+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">My programs</category><title>Nick Parser v0.1</title><description>&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJfdw75HvGh5Bxhvm5uHSAwheM6efSHp8RrHTxW-0Mz9dCvtuOH6tnvJp8sU08Vgp2ImI0Uhu5ONgRXWeDdsKic3OqjxtNbj33QG470YoWJRZtCvWD305LRvmo80_6AZkTpBw6023bImiZ/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a href=&quot;http://zalil.ru/30899005&quot;&gt;Тыкнуть для скачивания&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Nick Parser v0.1&lt;/span&gt; - универсальный парсер любой информации с форумов.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Шаг 1.&lt;/span&gt;&lt;br /&gt;Указываем низкий уровень форума.&lt;br /&gt;Пример:&lt;br /&gt;&lt;pre class=&quot;brush: delphi&quot;&gt;forum.[site].ru/viewtopic.php?t=&lt;/pre&gt;&lt;br /&gt;Жмем &quot;check&quot;.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Шаг 2&lt;/span&gt;.&lt;br /&gt;Указываем начало и конец html кода, за который сможем зацепиться при парсинге.&lt;br /&gt;Пример:&lt;br /&gt;Рассмотрим неудобный вариант, где рядом с ником хранится разный id :&lt;br /&gt;&lt;pre class=&quot;brush: delphi&quot;&gt;&amp;lt;a name=&quot;205366&quot;&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;b&amp;gt;Admin&amp;lt;/a&amp;gt;&lt;/pre&gt;&lt;br /&gt;Поле1:&lt;br /&gt;&lt;pre class=&quot;brush: delphi&quot;&gt;&amp;lt;a name=&lt;/pre&gt;&lt;br /&gt;Поле2:&lt;br /&gt;&lt;pre class=&quot;brush: delphi&quot;&gt;&amp;lt;/a&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Шаг 3&lt;/span&gt;.&lt;br /&gt;Запускаем, нажав на &quot;start&quot;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Шаг 4.&lt;/span&gt;&lt;br /&gt;Как ясно из названий кнопок: поставить на паузу, снять с паузы, завершить.&lt;br /&gt;&lt;br /&gt;[!] Программа создаст папку с именем сайта в директории .exe файла приложения. В ней вы найдете сохраненную информацию.&lt;br /&gt;[!] После 500 сохраненных ников, программа прекратит работу и так же нет возможности сохранения шага.&lt;br /&gt;[!] Программа написана в ознакомительных целях, автор не несет ответственности.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;___________________________________________&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Предположим я получил следующую информацию:&lt;/span&gt;&lt;br /&gt;&lt;pre class=&quot;brush: delphi&quot;&gt;&quot;78626&quot;&amp;gt;&amp;lt;b&amp;gt;Гость&lt;br /&gt;&quot;2&quot;&amp;gt;&amp;lt;b&amp;gt;kaj&lt;br /&gt;&quot;78626&quot;&amp;gt;&amp;lt;b&amp;gt;;Гость&lt;br /&gt;&quot;3&quot;&amp;gt;&amp;lt;b&amp;gt;Кот&lt;br /&gt;&quot;82667&quot;&amp;gt;&amp;lt;b&amp;gt;BestDen&lt;br /&gt;&quot;2&quot;&amp;gt;&amp;lt;b&amp;gt;kaj&lt;br /&gt;&quot;78626&quot;&amp;gt;&amp;lt;b&amp;gt;Гость&lt;br /&gt;&quot;3&quot;&amp;gt;&amp;lt;b&amp;gt;Кот&lt;br /&gt;&quot;82667&quot;&amp;gt;&amp;lt;b&amp;gt;BestDen&lt;br /&gt;&quot;4&quot;&amp;gt;&amp;lt;b&amp;gt;Venomch&lt;br /&gt;&quot;5&quot;&amp;gt;&amp;lt;b&amp;gt;Кот&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Тут нам понадобится &lt;a href=&quot;http://scr1pter.blogspot.com/2011/04/generatorpro.html&quot;&gt;Generator Pro&lt;/a&gt;&lt;br /&gt;Используем &quot;Spacekiller&quot; и с помощью &quot;Замена&quot; заменяем одинаковые символы во всех строчках на &quot;;&quot;&lt;br /&gt;&lt;pre class=&quot;brush: delphi&quot;&gt;&quot;78626&quot;&amp;gt;;Гость&lt;br /&gt;&quot;2&quot;&amp;gt;;kaj&lt;br /&gt;&quot;78626&quot;&amp;gt;;Гость&lt;br /&gt;&quot;3&quot;&amp;gt;;Кот&lt;br /&gt;&quot;82667&quot;&amp;gt;;BestDen&lt;br /&gt;&quot;2&quot;&amp;gt;;kaj&lt;br /&gt;&quot;78626&quot;&amp;gt;;Гость&lt;br /&gt;&quot;3&quot;&amp;gt;;Кот&lt;br /&gt;&quot;82667&quot;&amp;gt;;BestDen&lt;br /&gt;&quot;4&quot;&amp;gt;;Venomch&lt;br /&gt;&quot;5&quot;&amp;gt;;Кот&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;Устанавливаем символ &quot;;&quot; на разделитель и жмем &quot;Y;X&quot;, следом &quot;X&quot;, а в конце используем &quot;Clonekiller&quot;.&lt;br /&gt;В итоге у нас выходит:&lt;br /&gt;&lt;pre class=&quot;brush: delphi&quot;&gt;Гость&lt;br /&gt;kaj&lt;br /&gt;Кот&lt;br /&gt;BestDen&lt;br /&gt;Venomch&lt;br /&gt;BestDen&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;Готово!&lt;/center&gt;</description><link>http://scr1pter.blogspot.com/2011/04/nick-parser-v01.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJfdw75HvGh5Bxhvm5uHSAwheM6efSHp8RrHTxW-0Mz9dCvtuOH6tnvJp8sU08Vgp2ImI0Uhu5ONgRXWeDdsKic3OqjxtNbj33QG470YoWJRZtCvWD305LRvmo80_6AZkTpBw6023bImiZ/s72-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-6399890223792543185</guid><pubDate>Sun, 17 Apr 2011 21:31:00 +0000</pubDate><atom:updated>2011-05-11T20:08:48.914+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">My programs</category><title>Docs translate v0.1</title><description>&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmZBmOrxRV4g7XXYStMh1o3S_9fHtoMes6E3ggiBTvWGopKUkUnLY5QikfOcVVdol7eaBZFcTyAiO1Sx1O_oVdwJ0spRT3RK68fBWJBGcrWOamaAu756ij2agvqNnTUM862RTQ1qLzvL0n/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;1. Указываете директорию.&lt;br /&gt;2. Выбираете с какого на какой язык хотите перевести.&lt;br /&gt;3. Запускаете.&lt;br /&gt;&lt;br /&gt;Программа переведет все .txt файлы в указанной директории и сохранит их в отдельную папку.&lt;br /&gt;&lt;br /&gt;Перевод осуществляется с помощью Google Translate.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://zalil.ru/30882263&quot;&gt;Тыкнуть для скачивания&lt;/a&gt;</description><link>http://scr1pter.blogspot.com/2011/04/docs-translate-v01.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmZBmOrxRV4g7XXYStMh1o3S_9fHtoMes6E3ggiBTvWGopKUkUnLY5QikfOcVVdol7eaBZFcTyAiO1Sx1O_oVdwJ0spRT3RK68fBWJBGcrWOamaAu756ij2agvqNnTUM862RTQ1qLzvL0n/s72-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-4260912595849748761</guid><pubDate>Sun, 17 Apr 2011 21:21:00 +0000</pubDate><atom:updated>2011-05-11T20:08:59.302+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">My programs</category><title>GeneratorPro</title><description>&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYNHU_KWPyE-AMqTw2qa62H81aAEw2S1mRJ4Ju-3c3KkVCyNGHrkVX2WKH3VmG4HdBrnBNGX0_GD5pH_jLY36QYX3x2p3urC5-itN7EpE_uWaCSqTiflFavHGPRQfC35TlhEG3f3hLEFmC/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitXJSk16VEz-i5OdE1k74OFlElMMBInN9SAbzpTyn2nkj-AlQhNcjeVjIbPNiX3BIGFzofkw3HdeCDQHcpBvThuxGThDGBQetBQ2-G61e0lUsVwZxrViNiq0udV-5_W50M5aIr2u-f8jsC/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;Возможности:&lt;br /&gt;Генерирование по диапазону с одним или несколькими паролями.&lt;br /&gt;Генерирование из файла с одним или несколькими паролями.&lt;br /&gt;&lt;br /&gt;Имеются такие функции:&lt;br /&gt;- Cортировка&lt;br /&gt;- Clonekiller (удаление повторов)&lt;br /&gt;- Spacekiller (удаление пробелов)&lt;br /&gt;- Удаление Y из X;Y при любом разделителе&lt;br /&gt;- Обмен местами X;Y на Y;X&lt;br /&gt;- Быстрая замена&lt;br /&gt;- Anti mass changer (замена разделителя на случайный знак)&lt;br /&gt;&lt;br /&gt;! Работа как в окне программы, так и с .txt файлом&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://zalil.ru/30882211&quot;&gt;Тыкнуть для скачивания&lt;/a&gt;</description><link>http://scr1pter.blogspot.com/2011/04/generatorpro.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYNHU_KWPyE-AMqTw2qa62H81aAEw2S1mRJ4Ju-3c3KkVCyNGHrkVX2WKH3VmG4HdBrnBNGX0_GD5pH_jLY36QYX3x2p3urC5-itN7EpE_uWaCSqTiflFavHGPRQfC35TlhEG3f3hLEFmC/s72-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-5492516479451530125</guid><pubDate>Sun, 17 Oct 2010 20:22:00 +0000</pubDate><atom:updated>2011-05-11T20:06:24.883+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SEngine</category><title>Spot light</title><description>Сделал spot light, он же конусный источник света (к примеру фонарик).&lt;br /&gt;&lt;br /&gt;dot light + Ambient light&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimVqaZmt9sjdm5o8gH5mZdL9-Y6zYMFwATu75U990jq9lBKLnyjUiWx7F5uwJezXOVdV-PqxiMRXeyigXBjjissPnzLKJC_3YJSzhbA_XrGM5Jjz-6sgLuHNz2RPd4ZPEVcQuOa2BJlbnZ/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;Возможность менять дальность:&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCXULAWOOYXOH-LXLwf34R2lkzSueYh4aj_lIxIS3JZrMp6O7QXnB7OEs2HH8KXO94agIfKxo7qYIsNcivgxEAo3_2d9OcNRZRNIwhL7QrVqnSNSDAdmEw4hsGsPnZcEC-8yeAKxnsPVLb/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;Возможность менять точность:&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTAe6ZGfKh_bJJv7BB3MrbRMdvenpzny6Gt3OCNoijBgbaRy0yqaziNxlVOyoVN82BwX0R3A5qPpqaOBObxe2t0vc8OJ6H5W8kcUT3k2HFS3fvTOPI-4IGKzHEXiJdh9DgYYl4Mt4gTmH8/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;Возможность менять диаметр:&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKklnKklTd0rIx9J8RsDVfdb8kEkAtrHhP5sIEGzzHbyCnllcYE2vju23wgoN0SZbQrckVNkGuE-Oiq01WzVrcmNwFzSpzcymmVVIStUlDGlpchfCDCKq7udE_8K8ilDVozxjnpsOPWfOJ/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;Возможность менять яркость:&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7S1HwlV1n-jsKImO8d7k1QwJawcSJAoPNUrV0epR8iXysrRN6zcACwQWHZyYxjbLipBSt-jbOWiGfa065jeY9ZX55KEBtMHkHUL4EEsS7afMBxxyuPRCpePDmiijVA_phCORxYR2IprB8/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;Возможность задавать цвет:&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlll2dO7w8kIu_r6sa69VxKhzlzPllJ01N9HF3a8ZU34zwCqKpEmc8_s02F8chB9DVT16gCbH3UytI5nhLirvMdt824JHu7x1pHoLPjhAB_TQ1tGeNRYIv9ndyDPWpWHWAZ7ZtN-5Blj7f/&quot; /&gt;</description><link>http://scr1pter.blogspot.com/2010/10/spot-light.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimVqaZmt9sjdm5o8gH5mZdL9-Y6zYMFwATu75U990jq9lBKLnyjUiWx7F5uwJezXOVdV-PqxiMRXeyigXBjjissPnzLKJC_3YJSzhbA_XrGM5Jjz-6sgLuHNz2RPd4ZPEVcQuOa2BJlbnZ/s72-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-586525988290124363</guid><pubDate>Sat, 16 Oct 2010 22:47:00 +0000</pubDate><atom:updated>2011-05-11T20:07:53.397+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SEngine</category><title>Dot light</title><description>Сделал dot light, он же точечный источник света (к примеру лампочка).&lt;br /&gt;&lt;br /&gt;dot light + Ambient light&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkPXYDhH59Wfd2IuWGAaFzK0TgB0Pn3ZNDGeZaMt5vOFVpYif_7PGBNGGdMrs6lTVHLkgV5BNN6RcsYcViy-kVELNIOl6oMRW3Fm8zHKNtbrXrR2CMxah-Tay_AhedmT6BddVd1WNkc-JO/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;Возможность менять дальность:&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiB8L6KPuZUvRuC6SUiPE4uAdRqialMii0jbG-hRGIxjXIWr5y1VF_ujGcD-Vorc_SkShMRb4QR9TA_nvbyqOaRIJHo27nrr1Cu0BPUAzkWJJ5NrgJHQk9xdVRgVcnsvHt26sZB30YCvKK/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;Возможность менять яркость:&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnpw1RKt1hMfhRHxC2lxvCBg7kK_o5sELdmhs7sim8AvUO08x1N42MEhybD66AvFvDue3wCna3dYRrMMkrVFHJ3tAbj97OlU2CRP0Zs9uRanHkex1sz866e070F6Jf26XYd2U-DjX8nzf3/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;Возможность задавать цвет:&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOMkYDYgEIhkVXxD54waK1EFKi8rAQSRvhGmWJY7hoQ6aGTJYhIn4nyzGLE8VuxD31hYGzHGUexJb8MH-OhF6RE7rkp4o_jvsrelx5983QUdrWx25iLYQnJsfXTkYZ7PBg79jusdGt5rOO/&quot; /&gt;</description><link>http://scr1pter.blogspot.com/2010/10/dot-light.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkPXYDhH59Wfd2IuWGAaFzK0TgB0Pn3ZNDGeZaMt5vOFVpYif_7PGBNGGdMrs6lTVHLkgV5BNN6RcsYcViy-kVELNIOl6oMRW3Fm8zHKNtbrXrR2CMxah-Tay_AhedmT6BddVd1WNkc-JO/s72-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-6570508977252131816</guid><pubDate>Sat, 16 Oct 2010 20:50:00 +0000</pubDate><atom:updated>2011-05-11T20:05:51.854+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SEngine</category><title>Directional light</title><description>Сделал directional light, он же направленный источник света (к примеру солнце).&lt;br /&gt;&lt;br /&gt;directional light + Ambient light&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdlKFnwAdVH_7W_53X0fiiv4d7djO34QLiWGX0_QvzPV-NPRTKXCORD6O9BFgkRmSz8zHp0StuoiUpELCBYhzbp0_CwCnpgnyKR5vGtW1z324Ns4RSesv4XlUZugnMBdQf2WtygXAV7gxK/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;Возможность менять яркость:&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0fG8abYljD5BXoNKHW_X4jSTBctOBJV9ZUb8iOE8Jnc56l8YXc-k9EYbHnRkaMr0QgmxvsKEfgvUiwH5HErOF9rCyl0orBtGothiFXT1BE6ask4KlZL5wW1kmp1nkWPJbneg8KUr44gPP/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;Возможность задавать цвет:&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpCCOyYskFbvROW3RIpO84Zi6Dk1dhLg5vXvPXPG38NKhgHDDOLEnxYrNazaGIP5BJ50ObY4I8PLw6q59iYx1vMnoKMak_vUc2VsYwUvKCb8hxDNkLnr41pdr0NMXUWLZs1Mz7UdzIv35E/&quot; /&gt;</description><link>http://scr1pter.blogspot.com/2010/10/directional-light.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdlKFnwAdVH_7W_53X0fiiv4d7djO34QLiWGX0_QvzPV-NPRTKXCORD6O9BFgkRmSz8zHp0StuoiUpELCBYhzbp0_CwCnpgnyKR5vGtW1z324Ns4RSesv4XlUZugnMBdQf2WtygXAV7gxK/s72-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-3957592876769025431</guid><pubDate>Thu, 14 Oct 2010 16:05:00 +0000</pubDate><atom:updated>2011-05-11T20:05:32.836+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SEngine</category><title>Квады</title><description>Поразмыслил о том какие могут быть квады, а точнее по &quot;деформации&quot;.&lt;br /&gt;&lt;br /&gt;1) ширина и высота в координатах&lt;br /&gt;2) ширина и высота в процентах&lt;br /&gt;3) ширина в процентах, высота в координатах&lt;br /&gt;4) высота в координатах, ширина в процентах&lt;br /&gt;5) сохранение пропорции&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Пункт 1.&lt;/span&gt;&lt;br /&gt;Высота и ширина квада статичны, и не изменяются, скажем подходит для GUI, HUD&#39;ов и тд.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;До деформации:&lt;/span&gt;&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhkT-hyrLBBKsgp4EJz5HdzBp-O7GEySQpei2jOs7BxmN8Yr1FnWPAZRx0NjKJgm3HnKm1wE7T9xA7QzfQkiyUXkkqQa6UT3MILgr9QxJhE848jiM-UPYBwHR3_aRp-RuIqYKKH8Y-sJ_a/&quot; /&gt;&lt;br /&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;После деформации:&lt;/span&gt;&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6eT3cSjesdzd5b_xQ-5K5c6n7uAYvC41SVR1x6N8OHg3TMwsxX-zFjsf48BOuY7BORiJMF5eo1gs6eFbucvBXgUI_PNV_uVl98_dV3h29Efdo82RokjiQwaLktGctX9jbU8k4lj1JcMSd/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Пункт 2.&lt;/span&gt;&lt;br /&gt;Ширина и высота зависят от размеров окна, и всегда вычисляются в процентах.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;До деформации:&lt;/span&gt;&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaC8R6kHqB1WSh_06BeaGcfpQPQvctmX2Aqvi1pUNPX-kbTp3eAxzi4yctvcQwr1srvOX9MMkFhyVHFhmB9Y-GsysAsUwu2IhnImVWPfZ_oJg9HGUUm8TEatP9XscDlriMtxpeKDCcEngf/&quot; /&gt;&lt;br /&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;После деформации:&lt;/span&gt;&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcWGdwTiQgxCOXWBXQDuWN48JcaNSl74P7W_GcqgyC5Z8_yiNwyZLjnDTbpdZ13MIx6iNwvkknt8HR_CUdq_p667_H7iKbOiQpQlCGgdUlAWzdFFj8CYhYXvlecFq0y63I_JSqwLUeln5m/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Пункт 3.&lt;/span&gt;&lt;br /&gt;Ширина п2, высота п1.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;До деформации:&lt;/span&gt;&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkSGUvrfKqAoyaFdWbPO35foVhMDAtta1kvBN1eiin21fFkDLhjtNvf70CJTL98ssgjHyx3o0zycUN5fSxit5vOziBnsfMtO0XJmbLOpvWI5qxRb1L1nd-qAnqoTrFy_EwREznY285cgA1/&quot; /&gt;&lt;br /&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;После деформации:&lt;/span&gt;&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLLeF1e_9Z3Gkuq7bcrwSb8VlNfP3sVypy_HSfA73ecwka8bHBqA-bF4RWXUG849fNZR_k-lVpQLys0PjEIkhMYvWbvOAj0rEY39XcHi_NVLWdeZUmPVnOzH06dmPceAgKcN14x3HmTal1/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Пункт 4.&lt;/span&gt;&lt;br /&gt;Ширина п1, высота п2.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;До деформации:&lt;/span&gt;&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiA4oi5Unba5LF-X3IV30ucvgeeBikpqnZjLRX8r_7A4dJ-791HAVogskdBLJcEM-VIPRQD0KJRkW8E-4Bxt3NCQnIMFb8w8vn9TDYdlZBZDDJoUTxquYIlsKziGe2vtjUGm9uA0zCUkHX5/&quot; /&gt;&lt;br /&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;После деформации:&lt;/span&gt;&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgps6dSlbeAqWETROBs2SaB2TiNoHzP5bkp6IRBJ5-FHZzeLEQDdXPF9CsnPz1m7-xEOYwTpMCuC6esBsC8G9IxEDa2ltAYU4ePAEoSgN7kjh-t0AGcc3Qnp_fpk98Jo3s7YTHStohgSU0w/&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;Пункт 5.&lt;/span&gt;&lt;br /&gt;Когда ширина и высота сохраняют, некую пропорцию (например квад для вывода видео).&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;До деформации:&lt;/span&gt;&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilGS6i7OklS_HvOLB0wrX8yxfysiyxWhV27ZN2jMgjLoNgUg6wiOuYTt4B3YEU-rzMYTufHlyhwH_wIDch60AWXkWu_IBM9NFD7f89W7-zAvsZdEKsSkPKAqT39X6G_Bn60k9QiI5gnPgh/&quot; /&gt;&lt;br /&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;После деформации:&lt;/span&gt;&lt;br /&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilGS6i7OklS_HvOLB0wrX8yxfysiyxWhV27ZN2jMgjLoNgUg6wiOuYTt4B3YEU-rzMYTufHlyhwH_wIDch60AWXkWu_IBM9NFD7f89W7-zAvsZdEKsSkPKAqT39X6G_Bn60k9QiI5gnPgh/&quot; /&gt;</description><link>http://scr1pter.blogspot.com/2010/10/blog-post.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhkT-hyrLBBKsgp4EJz5HdzBp-O7GEySQpei2jOs7BxmN8Yr1FnWPAZRx0NjKJgm3HnKm1wE7T9xA7QzfQkiyUXkkqQa6UT3MILgr9QxJhE848jiM-UPYBwHR3_aRp-RuIqYKKH8Y-sJ_a/s72-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-5080443005068373157</guid><pubDate>Thu, 07 Oct 2010 21:29:00 +0000</pubDate><atom:updated>2011-05-11T23:40:23.849+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SEngine</category><title>Квады - начало</title><description>Ух сделал квады, размеры которых составляют процент от окна.&lt;br /&gt;&lt;br /&gt;Возможность зависимости ширины от высоты и наоборот отсутствует, пока что, так как пока необходима.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG_m37WKII5rtEZQs-wce933tGfVP10E-sjItYcNisA5P8Vl1E2-4OjREtWC0jExPjw8xnR7l66karIuCsi5D3u8ldERaEBqIVNRTrfxoZNIqTeZA_DtG-PlBb7YMsU25NE2va_BIqI7rL/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;А так же сделан рендер видео в текстуру, в прошлом у меня рендер шел в окно, а там были свои неудобные нюансы :)&lt;br /&gt;&lt;br /&gt;Вот я проигрываю видео в текстуре (на прошлом скрине она черная)&lt;br /&gt;&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQsc-NnXPcLAwUUBh85itiBmk9i7brgog9rHb9rjrgX3my9F7O4V4ZwBCHqmz18FKn1EEKfvJCYIwlsYlo7rczhHP0s5w1glzHkTEw_YHs8sCbiIOhK4JCsYXweZshMcleTjqy6UrhcNHG/&quot; /&gt;&lt;/center&gt;</description><link>http://scr1pter.blogspot.com/2010/10/sengine.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG_m37WKII5rtEZQs-wce933tGfVP10E-sjItYcNisA5P8Vl1E2-4OjREtWC0jExPjw8xnR7l66karIuCsi5D3u8ldERaEBqIVNRTrfxoZNIqTeZA_DtG-PlBb7YMsU25NE2va_BIqI7rL/s72-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-4365092227286952606</guid><pubDate>Thu, 12 Aug 2010 23:42:00 +0000</pubDate><atom:updated>2010-08-23T23:11:19.659+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SEngine</category><title>Intro</title><description>Создал заставку для движка&lt;br /&gt;&lt;br /&gt;&lt;object width=&quot;425&quot; height=&quot;344&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/CKb7sPW6Nj8&amp;hl=ru&amp;fs=1&quot;&gt;&lt;/param&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;&gt;&lt;/param&gt;&lt;param name=&quot;allowscriptaccess&quot; value=&quot;always&quot;&gt;&lt;/param&gt;&lt;embed src=&quot;http://www.youtube.com/v/CKb7sPW6Nj8&amp;hl=ru&amp;fs=1&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;344&quot;&gt;&lt;/embed&gt;&lt;/object&gt;</description><link>http://scr1pter.blogspot.com/2010/08/intro.html</link><author>noreply@blogger.com (Scripter)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-6262053156650682653</guid><pubDate>Tue, 10 Aug 2010 00:12:00 +0000</pubDate><atom:updated>2011-05-11T23:38:59.092+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SEngine</category><title>Конец Delphi</title><description>Вот и настало время переводить мой главный проект. Ещё раз немного полюбуемся скринами :)&lt;br /&gt;&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjav6_EKHtjOVNi83QgiBG5ZODSZ2sCuGFy3sXzvPwQ9NOw4h8fMrIdAZ4wWaMp-LCfKI2SzgGSEBPhvX4BmNtrbr1bUxk95EbSmMLbQkm-t0sjwWFiDNglEVE8pwCaH42algCeaktNYBfC/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiooTYL-Q7wJ8EIXYlh0bpCTRz1I4e_r3OzLr0lWGNJzBTN2NrdWjB0UbnbDBUy9DzunRjLBQyfCYkaZBHD1lACNhSnv-b0g__cXsHg8gtHmP0SR6ek0EFxvI7NBqgY_goI-2pxqVFGP3bA/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD2S8ukmX3PEoOwAoDIQLU1NSlBWmQOCkrWABhug12qFY7y9HqvJGzhDoDGPv37NdXU9cJipIfRRq_S26Hn1XsDXN_bJObxe2yKG35UPQ14i-4S6Bg-uH9VBZwtw70DyQb0MUHatHL19WL/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7K67KoSB4tGzkeXW2hEnCjLYqArq76FtLjhxG8TVjfDlNFBYlU6SQrSXVGXRHyqxbWVf8mP_HLG19OGSDcuY-g6gP5hZNdFoA-v444vW4DraKr946jU5EYs7JVgprdaH0rs4pwgTtVnu6/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD4GSgKLmeRiZ4TsevM_v6hSjuXSV1SMFqVWH4gkJi1V09vbFyXDPICswohBCsfcaR0hfD7X5pfavkJDo26rsBzGACpWkyvHB_DVQ_lFY3x8e06ObMViSN9cfrELUuW5QcBxYFr059adcE/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaW4KbVlGYTY_vtrp-Wp_gWAJP-nLncLcX3W_FW2U48ejATHvzu5RmjWDTQ355NylrfZ64Mfwrpe0sNz6-j7nSDT1hyt0oFrE_Q1tUukeNhStz1muOHNVTPIK3tXL0zrSEs9gmP6O-7j3a/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVQ8o8-4eVteO5_NbHUMn3nTnU5LyRgXNNvwe7nikHG04MYEQ782HreAFM110MC1gWp7JmgZdORDLCJfbiVAMo_KcU2p7HAIzJlL1UMjKpsQnhZkcuhysOxpG0hnqHgHa_zsrzIDHMfI9b/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;И так что у меня есть на данный момент:&lt;br /&gt;* Создание девайса со всевозможными настройками&lt;br /&gt;* Возобновление потерянного девайса&lt;br /&gt;* Подсчет ФПС&lt;br /&gt;* Вывод ошибок / логирование&lt;br /&gt;* Менеджер ресурсов (пока что создает только текстуры, вершинные и пиксельные шейдеры, декларации)&lt;br /&gt;* Сохранение скриншота всей области или отдельной части по имени или по дате&lt;br /&gt;* Смена режима полноэкранный / оконный&lt;br /&gt;* Стандартный текст (закомментирован ибо уг, нужно писать свой)&lt;br /&gt;* Настройка курсора (свободный в рабочей области или невидимый в центре)&lt;br /&gt;* Чтение из файла / архива&lt;br /&gt;* Вычисление md5 файла или файла в памяти&lt;br /&gt;* Настройка приложения в Settings.ini&lt;br /&gt;* Рендер в отдельном модуле, для удобства&lt;br /&gt;* Камера от 3его и от первого лица&lt;br /&gt;* Инверсия мыши для камеры&lt;br /&gt;&lt;br /&gt;Хоть и не лучшие возможности и не самый правильный движок, зато делается так, как позволяют мои умственные способности, а главное делается с любовью :)</description><link>http://scr1pter.blogspot.com/2010/08/sengine.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjav6_EKHtjOVNi83QgiBG5ZODSZ2sCuGFy3sXzvPwQ9NOw4h8fMrIdAZ4wWaMp-LCfKI2SzgGSEBPhvX4BmNtrbr1bUxk95EbSmMLbQkm-t0sjwWFiDNglEVE8pwCaH42algCeaktNYBfC/s72-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-2706445173492110167</guid><pubDate>Mon, 26 Jul 2010 14:44:00 +0000</pubDate><atom:updated>2011-05-11T20:04:49.430+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AntiChat Tetra</category><title>AntiChat Tetra v1.1</title><description>&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIlPJucFXgZru2jKMSfqaXLLf4OsA56VYu-jMaAf-QgQHdoEo__zvk8O0cFcfIPv1gGPi_x9RuNFRbMXGZNMK0dt68YKgIcCSHdF9KrAcdHUjeGkFcIaoTUiImoBwugHJWTocqpBq5EjX9/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;Готово обновление:&lt;br /&gt;&lt;br /&gt;[!] Исправлен баг из-за которого была синяя полоска на скриншоте;&lt;br /&gt;[+] Теперь есть подсветка (прозрачная), которая указывает, куда падают кубики (на выбор желтая/красная);&lt;br /&gt;[+] Теперь каждая фигура имеет различный цвет;&lt;br /&gt;[+] Скорость мыши теперь можно изменять;&lt;br /&gt;[+] Сделана настойка для включения инверсии мыши;&lt;br /&gt;[+] Сделано табло, на котором отображается следующая фигура;&lt;br /&gt;[+] Для каждой из 4 зон камеры, теперь своё управление;&lt;br /&gt;[+] Готов MatrixMod, возможность останавливать время, при котором можно вращат ьи двигать фигурки (изначально 30 сек + за каждое сжигание 30 сек)&lt;br /&gt;[+] Появилась возможность отключать АА для старых компьютеров;&lt;br /&gt;*AntiAliasing — технология, использующаяся в обработке изображений с целью делать границы кривых линий более гладкими, убирая «зубцы», возникающие на краях объектов.&lt;br /&gt;&lt;br /&gt;Скачать можно тут:&lt;br /&gt;&lt;a href=&quot;http://rapidshare.com/files/409183238/Update.zip&quot;&gt;http://rapidshare.com/files/409183238/Update.zip&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://slil.ru/29502128&quot;&gt;http://slil.ru/29502128&lt;/a&gt;</description><link>http://scr1pter.blogspot.com/2010/07/antichat-tetra-v11.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIlPJucFXgZru2jKMSfqaXLLf4OsA56VYu-jMaAf-QgQHdoEo__zvk8O0cFcfIPv1gGPi_x9RuNFRbMXGZNMK0dt68YKgIcCSHdF9KrAcdHUjeGkFcIaoTUiImoBwugHJWTocqpBq5EjX9/s72-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-2845358561009098821.post-5190004347684510217</guid><pubDate>Fri, 23 Jul 2010 02:40:00 +0000</pubDate><atom:updated>2011-05-11T20:04:38.514+04:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AntiChat Tetra</category><title>AntiChat Tetra v1.0</title><description>&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAShiguRr3q2x152PPBg8GM2nhiXF0QEBd2HUlc_YbkIQrWJZEuJ3h2iPuRk8qECZVCA8MyaTcWRTtfH6Z0VuCXBNuALyLq-1P9tlWap1Ee8ANM41hglR9lKPFdbxf7mdj50xJtso07yG0/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2X9u6MoNk59b6tvGlAOkxq4Xjd8WhPx2FEQVPIYMRNI82_8D4G4LJzpnwCWNmpdUmzz3SHvPMyXPH2IZkNPywooE-GJ6FKyfAjBxbK0iIu2up4r4PIY97Dy3HqBRW1ejXCi-BzvN8_PcD/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;center&gt;&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5uui1zXn10kWo_vnKz65jKtsVOWunQmjLmNQkzf5E5fsvu6bfR7lG5izGCWv6Ve_-iQBDl-rgaNyahsv-uVEDVPaXkKXpjxaukheWR-3Su0upcmLUGphwqsUTBH_9OqZ_KYwFVlpHZRv4/&quot; /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;Скачать можно тут:&lt;br /&gt;&lt;a href=&quot;http://slil.ru/29492110&quot;&gt;http://slil.ru/29492110&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://rapidshare.com/files/408516216/AntiChat_Tetra.zip&quot;&gt;http://rapidshare.com/files/408516216/AntiChat_Tetra.zip&lt;/a&gt;</description><link>http://scr1pter.blogspot.com/2010/07/antichat-tetra-v10.html</link><author>noreply@blogger.com (Scripter)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAShiguRr3q2x152PPBg8GM2nhiXF0QEBd2HUlc_YbkIQrWJZEuJ3h2iPuRk8qECZVCA8MyaTcWRTtfH6Z0VuCXBNuALyLq-1P9tlWap1Ee8ANM41hglR9lKPFdbxf7mdj50xJtso07yG0/s72-c" height="72" width="72"/><thr:total>0</thr:total></item></channel></rss>