<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>mike &#187; Алгоритмы</title>
	<atom:link href="http://mikhail.krivyy.com/category/zametki/algoritmyi/feed/" rel="self" type="application/rss+xml" />
	<link>http://mikhail.krivyy.com</link>
	<description>мнение автора может не совпадать с его точкой зрения ©</description>
	<lastBuildDate>Fri, 10 Feb 2012 08:08:41 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
<mikes>en</mikes>		<item>
		<title>Построение кратчайшего маршрута</title>
		<link>http://mikhail.krivyy.com/2004/10/10/postroenie-kratchayshego-marshruta/</link>
		<comments>http://mikhail.krivyy.com/2004/10/10/postroenie-kratchayshego-marshruta/#comments</comments>
		<pubDate>Sun, 10 Oct 2004 16:09:57 +0000</pubDate>
		<dc:creator>mike</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>

		<guid isPermaLink="false">http://mike.nov.ru/2004/10/10/postroenie-kratchayshego-marshruta/</guid>
		<description><![CDATA[Попросила меня тут одна знакомая сделать одну лабораторную по нахождению кратчайшего пути. Вспомнилось мне, что давным-давно, еще на старом добром Спектруме, читал я по этому поводу статейку Славы Медноногова. Статейка нашлась, оказывается, было это в ZX-Formаt #6. Сразу извинюсь за кривоту кода и реализации. Хотелось сделать в точности так, как описывалось в статье, но и [...]]]></description>
			<content:encoded><![CDATA[<p>Попросила меня тут одна знакомая сделать одну лабораторную по нахождению кратчайшего пути. Вспомнилось мне, что давным-давно, еще на старом добром Спектруме, читал я по этому поводу статейку Славы Медноногова. Статейка <a href="http://groups.google.com/groups?as_q=%D0%B2%D0%BE%D0%BB%D0%BD%D0%BE%D0%B2%D0%BE%D0%B9+%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC&#038;num=50&#038;as_scoring=r&#038;hl=en&#038;ie=UTF-8&#038;oe=UTF-8&#038;newwindow=1&#038;btnG=Google+Search&#038;as_epq=&#038;as_oq=&#038;as_eq=&#038;as_ugroup=&#038;as_usubject=&#038;as_uauthors=Mednonogov&#038;as_umsgid=&#038;lr=&#038;as_drrb=q&#038;as_qdr=&#038;as_mind=12&#038;as_minm=5&#038;as_miny=1981&#038;as_maxd=17&#038;as_maxm=12&#038;as_maxy=2002&#038;safe=images" target="_blank">нашлась</a>, оказывается, было это в ZX-Formаt #6.
</p>
<p>Сразу извинюсь за кривоту кода и реализации. Хотелось сделать в точности так, как описывалось в <a href=#text>статье</a>, но и задачу решить надо было.</p>
<p><span id="more-50"></span></p>
<p>Да и паскаля я года четыре в глаза не видел &#8212; всюду лезли фигурные скобки :)). Вот что из этого вышло: </p>
<ol class="codelist">
<li class="tab0 odd"><code>Program test;</code></li>
<li class="even">&nbsp;</li>
<li class="tab0 odd"><code>(*</code></li>
<li class="tab5 even"><code>П р а в и л а   п р о г р а м м и с т с к о й   и г р ы   &quot;К Л А Д&quot;:</code></li>
<li class="tab4 odd"><code>Имеется игровое поле  N x N .  Оно представлено массивом  Pole : array [ 1..N,1..N ] of integer.</code></li>
<li class="tab1 even"><code>Если элемент массива Pole[i,k] равен нулю, то клетка игрового поля на пересечении  i-той строки</code></li>
<li class="tab1 odd"><code>и  k-того столбца пустая.  По пустым полям можно ходить.  Если  Pole[i,k]=1, то клетка игрового</code></li>
<li class="tab1 even"><code>поля в  i-той строке и  k-том столбце занята каменной стеной.  Такие клетки нельзя пересекать,</code></li>
<li class="tab1 odd"><code>даже слегка задевать.  В одной из клеток  Pole[i,k]=5.  В этой клетке находится клад.</code></li>
<li class="tab4 even"><code>Задача игрока -- дойти до клетки, в которой находится клад.   Каждый участник игры пишет</code></li>
<li class="tab1 odd"><code>процедуру, содержащую алгоритм одного своего хода.  В начале хода игрок располагает такой</code></li>
<li class="tab1 even"><code>информацией:</code></li>
<li class="tab15 odd"><code>X, Y -- номер столбца и номер строки, где сейчас он находится;</code></li>
<li class="tab15 even"><code>Pole -- весь массив тоже доступен для просмотра.</code></li>
<li class="tab4 odd"><code>Процедура игрока должна определять свой ход, задав две величины dX и dY:</code></li>
<li class="tab15 even"><code>dX -- направление смещения по горизонтали:</code></li>
<li class="tab33 odd"><code>dX = -1 -- есть смещение на запад;</code></li>
<li class="tab33 even"><code>dX =  1 -- есть смещение на восток;</code></li>
<li class="tab33 odd"><code>dX =  0 -- нет смещения по горизонтали.</code></li>
<li class="tab15 even"><code>dY -- направление смещения по вертикали:</code></li>
<li class="tab33 odd"><code>dY = -1 -- есть смещение на север;</code></li>
<li class="tab33 even"><code>dY =  1 -- есть смещение на юг;</code></li>
<li class="tab33 odd"><code>dY =  0 -- нет смещения по вертикали.</code></li>
<li class="tab4 even"><code>Таким образом, совершая ход, игрок может передвинуться на одну клетку в одном из восьми</code></li>
<li class="tab1 odd"><code>направлений (как шахматный король) или остаться на месте.  Если заявленный игроком ход</code></li>
<li class="tab1 even"><code>невозможен, то перемещение не происходит.  Ход невозможен, если:</code></li>
<li class="tab15 odd"><code>в результате хода игрок встал бы на занятую стеной клетку;</code></li>
<li class="tab15 even"><code>или при движении по диагонали игрок задел бы угол клетки, занятой стеной.</code></li>
<li class="tab4 odd"><code>Оценка зависит от времени (числа ходов), затраченного на то, чтобы найти клад.</code></li>
<li class="tab0 even"><code>*)</code></li>
<li class="tab0 odd"><code>uses crt;</code></li>
<li class="even">&nbsp;</li>
<li class="tab0 odd"><code>const N=20; (* Размер поля *)</code></li>
<li class="tab0 even"><code>const kX=4; (* Координата X клада *)</code></li>
<li class="tab0 odd"><code>const kY=6; (* Координата Y клада *)</code></li>
<li class="tab0 even"><code>const Nk=100; (* Максимальное количество итераций *)</code></li>
<li class="odd">&nbsp;</li>
<li class="tab0 even"><code>var</code></li>
<li class="tab0 odd"><code>Pole  : array [ 1..N,1..N ] of integer;</code></li>
<li class="tab0 even"><code>X,Y   : integer;</code></li>
<li class="tab0 odd"><code>dX,dY : integer;</code></li>
<li class="tab0 even"><code>step  : integer; (* Количество шагов *)</code></li>
<li class="tab0 odd"><code>first : integer; (* Если не 1, то процедура OneStep запускается первый раз*)</code></li>
<li class="even">&nbsp;</li>
<li class="tab0 odd"><code>wayX  : array [ 1..Nk ] of integer; (* Координаты X маршрута *)</code></li>
<li class="tab0 even"><code>wayY  : array [ 1..Nk ] of integer; (* Координаты Y маршрута *)</code></li>
<li class="tab0 odd"><code>pos   : integer; (* Номер шага *)</code></li>
<li class="even">&nbsp;</li>
<li class="odd">&nbsp;</li>
<li class="tab0 even"><code>(* Задает поле *)</code></li>
<li class="tab0 odd"><code>procedure SetField();</code></li>
<li class="tab0 even"><code>begin</code></li>
<li class="tab2 odd"><code>(* Стены *)</code></li>
<li class="tab2 even"><code>Pole[5,5]:=1;Pole[5,6]:=1;Pole[5,7]:=1;</code></li>
<li class="tab2 odd"><code>Pole[5,8]:=1;Pole[5,9]:=1;Pole[5,10]:=1;</code></li>
<li class="tab2 even"><code>Pole[4,5]:=1;Pole[3,5]:=1;Pole[2,5]:=1;</code></li>
<li class="tab2 odd"><code>Pole[2,6]:=1;Pole[2,7]:=1;Pole[2,8]:=1;</code></li>
<li class="tab2 even"><code>Pole[2,9]:=1;Pole[2,10]:=1;Pole[2,11]:=1;</code></li>
<li class="tab2 odd"><code>Pole[2,12]:=1;Pole[2,13]:=1;Pole[2,14]:=1;</code></li>
<li class="even">&nbsp;</li>
<li class="tab2 odd"><code>Pole[15,6]:=1;Pole[15,7]:=1;Pole[15,8]:=1;</code></li>
<li class="tab2 even"><code>Pole[15,9]:=1;Pole[15,10]:=1;Pole[14,5]:=1;</code></li>
<li class="tab2 odd"><code>Pole[13,5]:=1;Pole[12,5]:=1;Pole[12,6]:=1;</code></li>
<li class="tab2 even"><code>Pole[12,7]:=1;Pole[12,8]:=1;Pole[12,9]:=1;</code></li>
<li class="tab2 odd"><code>Pole[12,10]:=1;Pole[12,11]:=1;Pole[12,12]:=1;</code></li>
<li class="tab2 even"><code>Pole[12,13]:=1;Pole[12,14]:=1;Pole[12,15]:=1;</code></li>
<li class="tab2 odd"><code>Pole[12,16]:=1;Pole[13,16]:=1;Pole[14,16]:=1;</code></li>
<li class="tab2 even"><code>Pole[15,16]:=1;Pole[16,16]:=1;Pole[16,16]:=1;</code></li>
<li class="odd">&nbsp;</li>
<li class="tab2 even"><code>Pole[9,2]:=1;Pole[9,3]:=1;Pole[9,4]:=1;</code></li>
<li class="tab2 odd"><code>Pole[9,5]:=1;Pole[9,6]:=1;Pole[9,7]:=1;</code></li>
<li class="tab2 even"><code>Pole[9,8]:=1;Pole[9,9]:=1;Pole[9,10]:=1;</code></li>
<li class="tab2 odd"><code>Pole[9,11]:=1;Pole[9,12]:=1;Pole[9,13]:=1;</code></li>
<li class="tab2 even"><code>Pole[9,14]:=1;Pole[9,15]:=1;Pole[9,16]:=1;</code></li>
<li class="tab2 odd"><code>Pole[9,17]:=1;Pole[9,18]:=1;Pole[9,19]:=1;</code></li>
<li class="tab2 even"><code>Pole[9,20]:=1;Pole[8,2]:=1;Pole[15,5]:=1;</code></li>
<li class="tab2 odd"><code>Pole[7,2]:=1;Pole[6,2]:=1;Pole[5,2]:=1;</code></li>
<li class="tab2 even"><code>Pole[4,2]:=1;Pole[3,2]:=1;Pole[2,2]:=1;</code></li>
<li class="odd">&nbsp;</li>
<li class="tab2 even"><code>(* Клад *)</code></li>
<li class="tab2 odd"><code>Pole[kX,kY]:=5;</code></li>
<li class="even">&nbsp;</li>
<li class="tab2 odd"><code>(* Игрок *)</code></li>
<li class="tab2 even"><code>X:=13;</code></li>
<li class="tab2 odd"><code>Y:=6;</code></li>
<li class="tab0 even"><code>end;</code></li>
<li class="odd">&nbsp;</li>
<li class="tab0 even"><code>(* Вывод поля на экран *)</code></li>
<li class="tab0 odd"><code>procedure DrawField();</code></li>
<li class="tab0 even"><code>var i,j : integer;</code></li>
<li class="tab0 odd"><code>begin</code></li>
<li class="tab2 even"><code>for i:=1 to N do</code></li>
<li class="tab4 odd"><code>for j:=1 to N do</code></li>
<li class="tab6 even"><code>begin</code></li>
<li class="tab8 odd"><code>gotoxy(i*2,j);</code></li>
<li class="tab8 even"><code>case Pole[i][j] of</code></li>
<li class="tab10 odd"><code>0: write('__');</code></li>
<li class="tab10 even"><code>1: write('SS');</code></li>
<li class="tab10 odd"><code>5: write('');</code></li>
<li class="tab8 even"><code>end;</code></li>
<li class="tab6 odd"><code>end;</code></li>
<li class="tab2 even"><code>(* Вывод игрока *)</code></li>
<li class="tab2 odd"><code>gotoxy(X*2,Y);</code></li>
<li class="tab2 even"><code>write('');</code></li>
<li class="tab0 odd"><code>end;</code></li>
<li class="even">&nbsp;</li>
<li class="tab0 odd"><code>(* Один шаг *)</code></li>
<li class="tab0 even"><code>procedure OneStep();</code></li>
<li class="tab0 odd"><code>var</code></li>
<li class="tab2 even"><code>R       : array [ 1..N,1..N ] of integer; (* Рабочее поле *)</code></li>
<li class="tab2 odd"><code>i,j     : integer;</code></li>
<li class="tab2 even"><code>Ni      : integer; (* Номер итерации *)</code></li>
<li class="tab2 odd"><code>found   : boolean;</code></li>
<li class="tab2 even"><code>min     : integer;</code></li>
<li class="tab2 odd"><code>mDx,mDy : integer; (* перемещения в волновом алгоритме *)</code></li>
<li class="tab2 even"><code>mstep   : integer;</code></li>
<li class="tab0 odd"><code>begin</code></li>
<li class="tab2 even"><code>if first&lt;&gt;1 then</code></li>
<li class="tab4 odd"><code>begin (* Начало реализации волнового алгоритма *)</code></li>
<li class="tab6 even"><code>first:=1;</code></li>
<li class="tab6 odd"><code>for i:=1 to N do</code></li>
<li class="tab8 even"><code>for j:=1 to N do</code></li>
<li class="tab10 odd"><code>case Pole[i][j] of</code></li>
<li class="tab12 even"><code>0: R[i][j]:=254;</code></li>
<li class="tab12 odd"><code>1: R[i][j]:=255;</code></li>
<li class="tab12 even"><code>5: R[i][j]:=0;</code></li>
<li class="tab10 odd"><code>end;</code></li>
<li class="tab6 even"><code>R[X][Y]:=253;</code></li>
<li class="tab6 odd"><code>Ni:=0;</code></li>
<li class="tab6 even"><code>found:=false;</code></li>
<li class="tab6 odd"><code>(* Шагаем *)</code></li>
<li class="tab6 even"><code>while (Ni&lt;Nk) do</code></li>
<li class="tab8 odd"><code>begin</code></li>
<li class="even">&nbsp;</li>
<li class="tab10 odd"><code>(* Построчно просмaтривaем рaбочий мaссив *)</code></li>
<li class="tab10 even"><code>for i:=1 to N do</code></li>
<li class="tab12 odd"><code>for j:=1 to N do</code></li>
<li class="tab14 even"><code>if not found then</code></li>
<li class="tab16 odd"><code>begin</code></li>
<li class="tab18 even"><code>if R[i][j]=Ni then</code></li>
<li class="tab20 odd"><code>begin</code></li>
<li class="tab22 even"><code>(* Идем влево вверх *)</code></li>
<li class="tab22 odd"><code>if (R[i-1][j-1]=254) AND (R[i][j-1]=254) AND (R[i-1][j]=254) AND (i-1&gt;0) AND (j-1&gt;0) then R[i-1][j-1]:=Ni+1;</code></li>
<li class="tab22 even"><code>(* Идем влево вниз *)</code></li>
<li class="tab22 odd"><code>if (R[i-1][j+1]=254) AND (R[i][j+1]=254) AND (R[i-1][j]=254) AND (i-1&gt;0) AND (j+1&lt;=N) then R[i-1][j+1]:=Ni+1;</code></li>
<li class="tab22 even"><code>(* Идем вправо вверх *)</code></li>
<li class="tab22 odd"><code>if (R[i+1][j-1]=254) AND (R[i][j-1]=254) AND (R[i+1][j]=254) AND (i+1&lt;=N) AND (j-1&gt;0) then R[i+1][j-1]:=Ni+1;</code></li>
<li class="tab22 even"><code>(* Идем вправо вниз *)</code></li>
<li class="tab22 odd"><code>if (R[i+1][j+1]=254) AND (R[i][j+1]=254) AND (R[i+1][j]=254) AND (i+1&lt;=N) AND (j+1&lt;=N) then R[i+1][j+1]:=Ni+1;</code></li>
<li class="tab22 even"><code>(* Идем влево *)</code></li>
<li class="tab22 odd"><code>if (R[i-1][j]=254) AND (i-1&gt;0) then R[i-1][j]:=Ni+1;</code></li>
<li class="tab22 even"><code>(* Идем вправо *)</code></li>
<li class="tab22 odd"><code>if (R[i+1][j]=254) AND (i+1&lt;=N) then R[i+1][j]:=Ni+1;</code></li>
<li class="tab22 even"><code>(* Идем вверх *)</code></li>
<li class="tab22 odd"><code>if (R[i][j-1]=254) AND (j-1&gt;0) then R[i][j-1]:=Ni+1;</code></li>
<li class="tab22 even"><code>(* Идем вниз *)</code></li>
<li class="tab22 odd"><code>if (R[i][j+1]=254) AND (j+1&lt;=N) then R[i][j+1]:=Ni+1;</code></li>
<li class="even">&nbsp;</li>
<li class="tab22 odd"><code>(* Проверка  достижения конечной точки *)</code></li>
<li class="tab22 even"><code>if R[i-1][j-1]=253 then found:=true;</code></li>
<li class="tab22 odd"><code>if R[i-1][j+1]=253 then found:=true;</code></li>
<li class="tab22 even"><code>if R[i+1][j-1]=253 then found:=true;</code></li>
<li class="tab22 odd"><code>if R[i+1][j+1]=253 then found:=true;</code></li>
<li class="tab22 even"><code>if R[i-1][j]=253 then found:=true;</code></li>
<li class="tab22 odd"><code>if R[i+1][j]=253 then found:=true;</code></li>
<li class="tab22 even"><code>if R[i][j-1]=253 then found:=true;</code></li>
<li class="tab22 odd"><code>if R[i][j+1]=253 then found:=true;</code></li>
<li class="tab20 even"><code>end;</code></li>
<li class="tab16 odd"><code>end;</code></li>
<li class="tab8 even"><code>Ni:=Ni+1;</code></li>
<li class="tab8 odd"><code>end;</code></li>
<li class="even">&nbsp;</li>
<li class="tab7 odd"><code>(* Если маршрута не найдено, то ругаемся *)</code></li>
<li class="tab7 even"><code>if not found then</code></li>
<li class="tab9 odd"><code>begin</code></li>
<li class="tab11 even"><code>WriteLn('Маршрута не найдено.');</code></li>
<li class="tab11 odd"><code>halt(1);</code></li>
<li class="tab9 even"><code>end;</code></li>
<li class="odd">&nbsp;</li>
<li class="tab7 even"><code>(* Теперь строим путь *)</code></li>
<li class="tab7 odd"><code>i:=X;</code></li>
<li class="tab7 even"><code>j:=Y;</code></li>
<li class="tab7 odd"><code>mstep:=0;</code></li>
<li class="tab7 even"><code>min:=255;</code></li>
<li class="tab7 odd"><code>while (R[i][j]&lt;&gt;0) do</code></li>
<li class="tab9 even"><code>begin</code></li>
<li class="tab11 odd"><code>if (min&gt;R[i][j+1]) AND (j+1&lt;=N) then begin min:=R[i][j+1];mdx:=0;mdy:=1;end;</code></li>
<li class="tab11 even"><code>if (min&gt;R[i][j-1]) AND (j-1&gt;0) then begin min:=R[i][j-1];mdx:=0;mdy:=-1;end;</code></li>
<li class="odd">&nbsp;</li>
<li class="tab11 even"><code>if (min&gt;R[i+1][j+1]) AND (i+1&lt;=N) AND (j+1&lt;=N) then begin min:=R[i+1][j+1];mdx:=1;mdy:=1;end;</code></li>
<li class="tab11 odd"><code>if (min&gt;R[i+1][j+0]) AND (i+1&lt;=N) then begin min:=R[i+1][j+0];mdx:=1;mdy:=0;end;</code></li>
<li class="tab11 even"><code>if (min&gt;R[i+1][j-1]) AND (i+1&lt;=N) AND (j-1&gt;0) then begin min:=R[i+1][j-1];mdx:=1;mdy:=-1;end;</code></li>
<li class="odd">&nbsp;</li>
<li class="tab11 even"><code>if (min&gt;R[i-1][j+1]) AND (i-1&gt;0) AND (j+1&lt;=N) then begin min:=R[i-1][j+1];mdx:=-1;mdy:=1;end;</code></li>
<li class="tab11 odd"><code>if (min&gt;R[i-1][j+0]) AND (i-1&gt;0) then begin min:=R[i-1][j+0];mdx:=-1;mdy:=0;end;</code></li>
<li class="tab11 even"><code>if (min&gt;R[i-1][j-1]) AND (i-1&gt;0) AND (j-1&gt;0) then begin min:=R[i-1][j-1];mdx:=-1;mdy:=-1;end;</code></li>
<li class="odd">&nbsp;</li>
<li class="tab11 even"><code>i:=i+mdx;</code></li>
<li class="tab11 odd"><code>j:=j+mdy;</code></li>
<li class="tab11 even"><code>mstep:=mstep+1;</code></li>
<li class="tab11 odd"><code>wayX[mstep]:=mdx;</code></li>
<li class="tab11 even"><code>wayY[mstep]:=mdy;</code></li>
<li class="tab9 odd"><code>end;</code></li>
<li class="tab4 even"><code>end; (* Конец реализации волнового алгоритма *)</code></li>
<li class="tab2 odd"><code>pos:=pos+1;</code></li>
<li class="tab2 even"><code>dX:=wayX[pos];</code></li>
<li class="tab2 odd"><code>dY:=wayY[pos];</code></li>
<li class="tab0 even"><code>end;</code></li>
<li class="odd">&nbsp;</li>
<li class="tab0 even"><code>begin</code></li>
<li class="tab2 odd"><code>pos:=0;</code></li>
<li class="tab2 even"><code>first:=0;</code></li>
<li class="tab2 odd"><code>ClrScr;</code></li>
<li class="tab2 even"><code>SetField;</code></li>
<li class="tab2 odd"><code>DrawField;</code></li>
<li class="even">&nbsp;</li>
<li class="tab2 odd"><code>(* Делаем шаги пока не нашли клад *)</code></li>
<li class="tab2 even"><code>step:=0;</code></li>
<li class="tab2 odd"><code>while ((X&lt;&gt;kX) or (Y&lt;&gt;kY)) and (step&lt;100)  do</code></li>
<li class="tab4 even"><code>begin</code></li>
<li class="tab6 odd"><code>DrawField;</code></li>
<li class="tab6 even"><code>delay(200);</code></li>
<li class="tab6 odd"><code>OneStep;</code></li>
<li class="tab6 even"><code>X:=X+dX;</code></li>
<li class="tab6 odd"><code>Y:=Y+dY;</code></li>
<li class="tab6 even"><code>gotoxy(1,22);</code></li>
<li class="tab6 odd"><code>writeln('игрок: ',X,':',Y,' клад: ',kX,':',kY,'    ');</code></li>
<li class="tab6 even"><code>step:=step+1;</code></li>
<li class="tab4 odd"><code>end;</code></li>
<li class="even">&nbsp;</li>
<li class="tab2 odd"><code>WriteLn('Клад найден за ',step,' шагов');</code></li>
<li class="even">&nbsp;</li>
<li class="tab0 odd"><code>end.</code></li>
</ol>
<p> <P><strong>Download this code:</strong> <a href="/wp-content/uploads/2006/09/way.pas">way.pas</a></li>
<p><a name=text> </a></p>
<h2>Построение кратчайшего маршрута</h2>
<p>Задача нахождения самого короткого пути между некими точками а и В на игровом поле с произвольно расположенными препятствиями характерна, в первую очередь,для популярных сегодня тактических и стратегических игр. Как подзадача,она может возникать практически в любых играх &#8212; RPG,квестах,логических (типичный пример &#8212; &#171;Color Lines&#187;,кстати,слепить очередную версию такой игрушки после этой статьи &#8212; раз плюнуть).Почему надо искать самый короткий маршрут? В некоторых играх, например &#171;HЛО-2&#8243;,&#187;Lаser Squаd&#187;,от длины маршрута зависит количество потраченных единиц времени &#8212;  чем оптимальней будет найден путь, тем быстрее воин доберётся до цели. а, например, в &#171;Color Lines&#187; длина маршрута не оговорена правилами, имеет значение лишь сам факт возможности или невозможности перемещения шара. Hо и в этой игре будет приятнее смотреться, если шарик сразу направится куда надо,а не будет загадочно дефилировать по всей игровой доске.
</p>
<p>Решение этой задачи пришло к нам из такой далёкой,казалось бы, от игр области как электроника.а именно &#8212; разводка печатных плат (все знают,что это такое?).
</p>
<p>Существует большое количество трассировщиков (программ для разводки платы), основанных на не меньшем количестве различных методов, занимающихся соединением двух контактов единым проводником.Hо мы рассмотрим только один из них, самый простой (а значит,самый надёжный и самый популярный) &#8212; волновой трассировщик.
</p>
<p>Поставим перед волновым трассировщиком задачу в терминах разрабатываемой нами игры:
</p>
<p>Имеется игровое поле Р(MxN),где M и N, соответственно, размер поля по вертикали и горизонтали. Попросту,это массив размерностью MxN (DIM P(M,N). Каждая клетка игрового поля (элемент массива) может обладать большим количеством свойств по вашему усмотрению, но для нас важно только одно &#8212;  её проходимость (или непроходимость). Каким образом она определяется &#8212; это уж ваше дело. Дальше: имеется некоторая стартовая точка, где находится герой вашей игры, и конечная точка, куда ему необходимо попасть. Условимся пока,что ходить он может только по четырём направлениям (чего требует от нас классический волновой метод) &#8212; вправо,влево,вперёд, назад. Hеобходимо переместить героя от места старта к финишу за наименьшее количество ходов,если такое перемещение вообще возможно.
</p>
<p>алгоритм нахождения кратчайшего маршрута между двумя точками для такой задачи достаточно прост:</p>
<ol>
<li>Сначала необходимо создать рабочий<br />
массив R(MxN),равный по размеру массиву<br />
игрового поля P(MxN).</p>
</li>
<li>Каждому элементу рабочего массива<br />
R(i,j) присваивается некоторое значение<br />
в зависимости от свойств элемента игрового<br />
поля P(i,j) по следующим правилам:</p>
<ol>
<li>Если поле P(i,j) непроходимо, то<br />
R(i,j):=255;
</li>
<li>Если поле P(i,j) проходимо, то<br />
R(i,j):=254;
</li>
<li>Если поле P(i,j) является целевой<br />
(финишной) позицией, то R(i,j):=0;
</li>
<li>Если поле P(i,j) является стартовой<br />
позицией, то R(i,j):=253.
</li>
</ol>
<p>Этап &#171;Распространения волны&#187;. Вводим переменную Ni &#8212; счётчик итераций (повторений) и присваиваем ей начальное значение 0.
</li>
<li>Вводим константу Nк,которую устанавливаем равной максимально возможному числу итераций.
</li>
<li>Построчно просматриваем рабочий массив R (т.е.организуем два вложенных цикла: по индексу массива i от 1 до М, по индексу массива j от 1 до N).
</li>
<li>Если R(i,j) равен Ni,то просматриваются соседние элементы R(i+1,j), R(i-1,j), R(i,j+1), R(i,j-1) по следующему правилу (в качестве примера рассмотрим R(i+1,j):
<ol>
<li>Если R(i+1,j)=253, то переходим к пункту 10;
</li>
<li>Если R(i+1,j)=254, выполняется присваивание R(i+1,j):=Ni+1;
</li>
<li>Во всех остальных случаях R(i+1,j) остаётся без изменений.
</li>
</ol>
<p>аналогично поступаем с элементами R(i-1,j), R(i,j+1),R(i,j-1).
</li>
<li>По завершению построчного просмотра всего массива увеличиваем Ni на 1.
</li>
<li>Если Ni>Nк,то поиск маршрута признаётся неудачным. Выход из программы.
</li>
<li>Переходим к пункту 5.
</li>
<li>Этап построения маршрута перемещения. Присваиваем переменным Х и Y значения координат стартовой позиции.
</li>
<li>В окрестности позиции R(Х,Y) ищем элемент с наименьшим значением (т.е.для этого просматриваем R(Х+1,Y), R(Х-1,Y), R(Х,Y+1), R(Х,Y-1). Координаты этого элемента заносим в переменные X1 и Y1.
</li>
<li>Совершаем перемещение объекта (кто там у вас будет &#8212; робот, акванавт, Винни-Пух) по игровому полю из позиции [X,Y] в позицию [X1,Y1]. (По желанию, вы можете предварительно заносить координаты X1,Y1 в некоторый массив, и, только закончив построение всего маршрута,заняться перемещением героя на экране).
</li>
<li>Если R(X1,Y1)=0,то переходим к пункту 15.
</li>
<li>Выполняем присваивание X:=X1,Y:=Y1. Переходим к пункту 11.
</li>
<li>Всё !!!
</li>
</ol>
<p>Для тех,кто всё сразу понял,рекомендую дальше не читать. Для нормальных людей повторю всё ещё раз с комментариями и пояснениями:
</p>
<p>1.Сначала необходимо создать рабочий массив R(MxN),равный по размеру массиву игрового поля P(MxN).</p>
<p>Пока всё просто.Hа Бейсике &#8212; просто команда DIM R(M,N).  Hа ассемблере &#8212; что-нибудь типа &#171;_R DEFS _M*_N&#187;. Если игровое поле большое,имеет смысл выделить некоторое окно, куда попадают начальная и конечная точки (например,в &#171;HЛО-2.Дьяволы бездны&#187; при размере поля 64х64 работа ведётся лишь с частью поля 32х32), что бы ограничить число вычислений.
</p>
<p>2.Каждому элементу рабочего массива R(i,j) присваивается некоторое значение в зависимости от свойств элемента игрового поля P(i,j) по следующим правилам:
</p>
<p>а) Если поле P(i,j) непроходимо, то R(i,j):=255;
</p>
<p>б) Если поле P(i,j) проходимо, то R(i,j):=254;
</p>
<p>в) Если поле P(i,j) является целевой (финишной) позицией, то R(i,j):=0;
</p>
<p>г) Если поле P(i,j) является стартовой позицией, то R(i,j):=253.</p>
<p>Тоже без сложностей. Проходите по массиву игрового поля Р,определяете проходима/непроходима текущая клетка,в соответствии с этим записываете в ячейку массива R число 254/255. По завершении в позиции старт/стоп заносите 253/0. Существует несколько способов задания свойств элемента игрового поля. Два конкретных примера: в &#171;HЛО1/HЛО2&#8243; организован байтовый массив свойств спрайтов ландшафта, каждому биту соответствует своё свойство, за проходимость отвечает,например, 7-ой бит. В &#171;Чёрном Вороне&#187; сделано проще &#8212; спрайты с номерами от 0 до 31 &#8212; проходимы, старше &#8212; нет.
</p>
<p>3.Этап &#171;Распространения волны&#187;. Вводим переменную Ni &#8212; счётчик итераций (повторений) и присваиваем ей начальное значение 0.
</p>
<p>Этап назван так потому, что заполнение рабочего массива действительно напоминает волну. Обратите внимание, что распространение волны начинается с конечной точки.
</p>
<p>Вводим константу Nк, которую устанавливаем равной максимально возможному числу итераций.
</p>
<p>Это очень тонкий момент. Предположим, что между началом и концом лежит непреодолимое препятствие, тогда поиск пути зайдёт в тупик и программа зациклится. Чтобы этого не произошло, и вводится такая переменная. Её величина подбирается экспериментально. Hапример, в той же &#171;HЛО-2&#8243; даже акванавт-генерал,имея 108 единиц времени и кучу энергии, не сможет за ход переместится более, чем на 27 клеток. Однако я всё же сделал Nк=64. В любом случае, при нашем способе решения задачи Nк не может превышать 253 (догадались,почему?).
</p>
<p>5.Построчно просматриваем рабочий массив R (т.е.организуем два вложенных цикла: по индексу массива i от 1 до М, по индексу массива j от 1 до N)
</p>
<p>Думаю, понятно, как сделать это на Бейсике. Hа ассемблере я не стал бы делать два цикла, а сделал бы один, помня о том, что строки массива в памяти хранятся друг за другом и образуют непрерывную цепочку байтов.
</p>
<p>Более того, если вы обладаете неким запасом свободной памяти, неплохо на каждой предыдущей итерации запоминать координаты точек, входящих в последнюю волну. Тогда пункты 5-6 сведутся к просмотру только этих точек, что существенно поднимет быстродействие!
</p>
<p>6. Если R(i,j) равен Ni,то просматриваются соседние элементы R(i+1,j), (R(i-1,j), R(i,j+1), R(i,j-1) по следующему правилу (в качестве примера рассмотрим R(i+1,j):
</p>
<p>а) если R(i+1,j)=253, то переходим к пункту 10;
</p>
<p>б) если R(i+1,j)=254, выполняется присваивание R(i+1,j):=Ni+1;
</p>
<p>в) в остальных случаях R(i+1,j) остаётся без изменений.
</p>
<p>аналогично поступаем с элементами R(i-1,j),R(i,j+1),R(i,j-1).
</p>
<p>Hесколько моментов для программирующих на ассемблере. Т.к. мы ищем совпадение элементов массива только с одним числом (Ni), то для достижения наибольшей скорости поиска рекомендуется использовать команду CPIR. Второе замечание: при фиксированных размерах игрового поля адреса соседних элементов можно не вычислять по сложным формулам, а задать числовыми смещениями (например,при поле 32х32 смещения четырёх соседних клеток равны -32,-1,+1,+32). Третье замечание,важное для всех: много времени при вычислениях может отнимать учёт краевых эффектов (имеются в виду элементы, расположенные на границах массива). Действительно,если, например, i=1 (или 0 в Си), то обращение к R(i-1,j) не имеет смысла и может привести к порче данных и зависанию компьютера. Я рекомендую ещё в пункте 1 создать рабочий массив размером не M на N, а (M+2)x(N+2) и всем граничным элементам дать значение 255 (непроходим). Памяти тратится немного больше, зато программировать легче, да и расчёты будут идти быстрее. Так я и делал в &#171;HЛО-2&#8243;.
</p>
<p>7. По завершению построчного просмотра всего массива увеличиваем Ni на 1.
</p>
<p>8.Если Ni>Nк, то поиск маршрута признаётся неудачным.Выход из программы.
</p>
<p>Я вас немного обманул. Математически точно условия неудачного поиска звучат так: &#171;Если на текущем шаге не было найдено ни одного элемента R(i,j), равного Ni, то маршрута, соединяющего две точки, не существует&#187;. Вы можете воспользоваться этим правилом, если любите абсолютную точность (в этом случае параметр Nк вообще не нужен), но мне кажется,лучше сделать одну проверку в конце, чем сотню на этапе поиска.
</p>
<p>Да, чуть не забыл,алгоритм распространения волны может прекрасно использоваться для заливки небольших фигур произвольной формы. Так что,если вы хотите создать свою собственную аrt Studio, и в голову ничего не лезет &#8212; можете использовать этот метод (для этого выбрасываем пункты 10-15 и слегка модифицируем алгоритм. Как? Придумайте сами).
</p>
<p>9. Переходим к пункту 5.
</p>
<p>10. Этап построения маршрута перемещения. Присваиваем переменным Х и Y значения координат стартовой позиции.
</p>
<p>11. В окрестности позиции R(Х,Y) ищем элемент с наименьшим значением (т.е.для этого просматриваем R(Х+1,Y), R(Х-1,Y), R(Х,Y+1), R(Х,Y-1). Координаты этого элемента заносим в переменные X1 и Y1.</p>
<p>Способ просмотра окрестных элементов аналогичен тому, как это делалось в пункте 6. Если ваш герой умеет ходить по диагонали, то можете включить в поиск ещё и четыре соседних диагональных элемента, которые надо просмотреть в _первую_ очередь. Так же, но чуть сложнее, сделано в &#171;HЛО-2&#8243; (при рассмотрении диагональных участков перемещения по правилам, принятым для большинства стратегий, не должно быть помех движению справа или слева).
</p>
<p>Внимание! Такой способ учёта диагональных перемещений даёт примерно 95% вероятности нахождения действительно самого короткого маршрута. Hа мой взгляд, этого вполне достаточно. Если же вам вдруг необходим самый короткий путь с гарантией на 100%, то уже в пункте 6 вы должны принимать во внимание диагональные элементы с учётом наложенных вашей игрой ограничений. Скорость распространения волны при этом сильно падает.
</p>
<p>12.Совершаем перемещение объекта по игровому полю из позиции [X,Y] в позицию [X1,Y1]. По желанию,вы можете предварительно заносить координаты X1,Y1 в некоторый массив, и, только закончив построение всего маршрута, заняться перемещением героя на экране.
</p>
<p>Заносить координаты маршрута в такой промежуточный список имеет смысл, если у вас одновременно перемещается несколько героев, а память выделена только под один рабочий массив R. Или же, если место под R выделяется в некоей общей области, которую другие подпрограммы могут использовать под свои нужды.Кстати, можно запоминать не сами координаты, на что в нашем примере уйдёт 2 байта, а коды направлений перемещения, на что достаточно и одного.
</p>
<p>13.Если R(X1,Y1)=0, то переходим к пункту 15.
</p>
<p>Hу вот мы и дошли до ручки,т.е.до конечной точки.
</p>
<p>14.Выполняем присваивание X:=X1, Y:=Y1. Переходим к пункту 11.
</p>
<p>15. Всё !!!</p>
<p>Hе правда ли,просто? Во избежании неясностей, в этом номере журнала приводится простенький пример на Бейсике. Посмотрев его, вы, как минимум, сможете повторить &#171;Color Lines&#187;.</p>
<h2>Достоинства и недостатки метода.</h2>
</p>
<p>Достоинства &#8212; простота, надёжность, 100% самый короткий путь (для классического метода). Hедостатки &#8212; большой объём требуемой памяти и не самая высокая скорость нахождения пути. В &#171;HЛО-2&#8243;, при перечисленных выше условиях, нахождение пути может достигать по времени до 1/10 секунды. Это, конечно, приемлимо для пошаговых стратегий и логических игрушек, но с трудом подойдёт для динамических игр. а про попытку реализации на Бейсике я вообще молчу (разве в качестве примера).</p>
<h2>Вариации метода.</h2>
</p>
<p>Двойная волна &#8212; распространение волны начинается как от конечной,так и от начальной точки, а маршрут составляется из двух участков &#8212; от точки встречи волн до старта и до финиша. Теоретически, может повысить скорость поиска в 3-4 раза. Hо вот как на практике?
</p>
<p>В случае острой нехватки памяти, например, если вы задумали не игру,а самый настоящий трассировщик плат на Спектруме, может применяться усечённое кодирование волны. Т.е. первая волна имеет номер 1, вторая &#8212; 2, третья &#8212; снова 2, четвёртая &#8212; 1, и так далее.Hа кодировку одного элемента потребуется два бита (числа 0/3 будут описывать проходимое/непроходимое поле). При поиске маршрута ищем соседние ячейки памяти в том же порядке (&#8230; 1 1 2 2 1 1 2 2 1 1 2 2&#8230;). Hи о каких диагональных перемещениях не может быть и речи.
</p>
<p>Кроме волнового, существует сравнительно большое количество методов для поиска маршрутов. Где-то требуется наибольшая скорость расчётов в ущерб качеству, где-то &#8212;  наименьшее число поворотов, где-то &#8212; необходимо, чтобы маршрут обязательно прошёл через некоторые ключевые точки (неважно, в каком порядке). Hовые методы трассировки позволяют искать маршруты, в которых путь может проходить под любыми углами (не только кратными 90-а и 45-и градусам). Прогресс не стоит на месте. </p>
<div аlign=right>(c) Copper Feet &#8217;97<br />(c) zx-formаt #6 &#8217;97</div>
]]></content:encoded>
			<wfw:commentRss>http://mikhail.krivyy.com/2004/10/10/postroenie-kratchayshego-marshruta/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>XCode в виде модуля С/C++</title>
		<link>http://mikhail.krivyy.com/2001/09/30/xcode-c-module/</link>
		<comments>http://mikhail.krivyy.com/2001/09/30/xcode-c-module/#comments</comments>
		<pubDate>Sun, 30 Sep 2001 05:36:48 +0000</pubDate>
		<dc:creator>mike</dc:creator>
				<category><![CDATA[Алгоритмы]]></category>

		<guid isPermaLink="false">http://mike.nov.ru/2004/09/30/xcode-c-module/</guid>
		<description><![CDATA[XCode &#8212; это программка для конвертации русских текстов с автоматическим определением кодировки исходного файла. Понадобилось тут мне перекодировать файл, каждая строка которого была написана в своей кодировке. Накапал xcode в портах FreeBSD, оформил его в виде отдельного модуля, и написал простенькую программку: Скачать: my_xcode.tgz]]></description>
			<content:encoded><![CDATA[<p>XCode &#8212; это программка для конвертации русских текстов с автоматическим определением кодировки исходного файла.</p>
<p>Понадобилось тут мне перекодировать файл, каждая строка которого была написана в своей кодировке.</p>
<p>Накапал <a href="http://www.freebsd.org/cgi/ports.cgi?query=xcode&#038;stype=all" target="_blank">xcode</a> в портах FreeBSD, оформил его в виде отдельного модуля, и написал простенькую программку:</p>
<p>Скачать: <a id="p30" href="http://mikhail.krivyy.com/wp-content/uploads/2006/09/my_xcode.tgz">my_xcode.tgz</a></p>
]]></content:encoded>
			<wfw:commentRss>http://mikhail.krivyy.com/2001/09/30/xcode-c-module/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

