游戏开发中的经典算法集锦之二

Dijkstra最短路径(一点到各顶点最短路径)

{本程序解决6个顶点之间的最短路径问题,各顶点间关系的数据文件在sj.txt中}
{如果顶点I到顶点J不能直达就设置距离为30000}
program dijkstra;
type
   jihe=set of 0..5;
var
   a:array[0..5,0..5] of integer;
   dist:array[0..5] of integer;
   i,j,k,m,n:integer;
   fv:text;
   s:jihe;
begin
   s:=[0];
   assign(fv,'sj.txt');
   reset(fv);
   for i:=0 to 5 do  {从文件中读数据,其中a[i,j]代表从顶点i到顶点j的直达距离,如果不通用30000代替}
     begin
        for j:=0 to 5 do read(fv,a[i,j]);
        readln(fv)
     end;
   for i:=1 to 5 do  {设置DIST数组的初始值,即为顶点0到各顶点的直达距离(算法的第一步)}
      dist[i]:=a[0,i];
   for i:=1 to 5 do
   begin
        m:=0;
        dist[m]:=30000;    {设置DIST[M]的目的是为下面的一步做准备,即在DIST数组中一个最小的值}

        for j:=1 to 5 do    {算法的第二步,找最小的DIST值}
        if (not (j in s)) and (dist[m]>dist[j]) 
         then m:=j ;    {用M来记录到底是哪个顶点}
        s:=s+[m];    {把顶点加入S中}

        for k:=1 to 5 do     {算法的第三步,修改后面的DIST值}
           if (not (k in s)) and  (dist[k]>dist[m]+a[m,k])
             then
               dist[k]:=dist[m]+a[m,k]
   end;
   writeln('原各顶点间的路径关系是:(30000代表不通)');
   for i:=0 to 5 do
      begin
        for j:=0 to 5 do  write(a[i,j]:6);
        writeln
      end;
   writeln; writeln;

八皇后问题

{问题描述:在国际象棋8X8的棋盘里摆放8个皇后,使每个皇后都能生存而不互相冲突,即同一行、同一列同对角线(包括主对角线和辅对角线)都只能有一个皇后}

program eightqueen;  {本程序可以搜索出所有的解}
var
  a,b:array[1..8] of integer;
  c:array[-7..7] of integer;
  d:array[2..16] of integer;
  i,k:integer;  {K变量用来存放答案的个数}
  fv:text;

  procedure print;
  var
    i:integer;
  begin
    for i:=1 to 8 do
       writeln(fv,'第',i:2, '行放在第', a[i]:2,'列');  {结果输出到文件里}
       k:=k+1;  {每输出一个答案计数加1}
       writeln(fv)
  end;

  procedure try(i:integer);
  var
    j:integer;
  begin
    for j:=1 to 8 do
      if (b[j]=0) and (c[i-j]=0) and (d[i+j]=0) then
         begin
           a[i]:=j; 
           b[j]:=1; {宣布占领列、主副对角线}
           c[i-j]:=1;
           d[i+j]:=1;
           if i<8 then try(i+1) else print;
           b[j]:=0;  {释放占领列、主副对角线}
           c[i-j]:=0;
           d[i+j]:=0
         end
  end;

begin
  for i:=1 to 8 do a[i]:=0;
  for i:=-7 to 7 do c[i]:=0;
  for i:=2 to 16 do d[i]:=0;
  k:=0;
  assign(fv,'jieguo.txt');  {指定文件与文件变量相联系}
  rewrite(fv);  {以写的方式打开文件}
  try(1);
  close(fv);  {一定要记得关闭文件,不然数据有可能丢失}
  writeln('共有 ',k:3,' 种摆法')
end.

 快速排序算法

program kuaisu(input,output);
const n=10;
var
   s:array[1..10] of integer;
   k,l,m:integer;

procedure qsort(lx,rx:integer);
var
   I,j,t:integer;
Begin
   I:lx;j:rx;t:s[I];
   Repeat
      While (s[j]>t) and (j>I) do
         Begin
            k:=k+1;
            j:=j-1
         end;
   if I<j then
begin
   s[I]:=s[j];I:=I+1;l:=l+1;
   while (s[I]<t) and (I<j) do
       begin
          k:=k+1;
          I:=I+1
      End;
   If I<j then
begin
         S[j]:=s[I];j:=j-1;l:=l+1;
      End;
End;
Until I=j;
S[I]:=t;I:=I+1;j:=j-1;l:=l+1;
If lx<j then qsort(lx,j);
If I<rx then qsort(I,rx)
End;{过程qsort结束}

Begin
Writeln('input 10 integer num:');
For m:=1 to n do read(s[m]);
K:=0;l:=0;
Qsort(l,n);
Writeln('排序后结果是:');
For m:=1 to n do write(s[m]:4)
 End.
地图四色问题

{问题描述:任何一张地图只要用四种颜色进行填涂,就可以保证相邻省份不同色}

program tt;
const num=20;
var a:array [1..num,1..num] of 0..1;
    s:array [1..num] of 0..4; {用1-4分别代表RBWY四种颜色;0代表末填进任何颜色}
    k1,k2,n:integer;
function pd(i,j:integer):boolean;{判断可行性:第I个省填上第J种颜色}
var k:integer;
begin
     for k:=1 to i-1 do   {一直从第一个省开始进行比较一直到I省减一的那个省,目的是对已经着色的省份来进行比较,因为>I的省还没                           有着色,比较没有意义,着色的顺序是先第一、二、三……I个省}
         if (a[i,k]=1) and (j=s[k]) then {省I和省J相邻且将填进的颜色和已有的颜色相同}
            begin
               pd:=false; {即不能进行着色}
               exit;   {退出当前函数}
            end;
     pd:=true;  {可以进行着色}
end;

procedure print;{打印结果}
var k:integer;
begin
      for k:=1 to n do{将数字转为RBWY串}
          case s[k] of
            1:write('R':4);
            2:write('B':4);
            3:write('W':4);
            4:write('Y':4);
          end;
      writeln;
end;

procedure try(i:integer);
var j:integer;
begin
     for j:=1 to 4 do
         if pd(i,j) then begin
                              s[i]:=j;
                              if i=n then print
                                 else try(i+1);  {对下一个省进行着色}
                              s[i]:=0;  {不能进行着色,将当前状态设置0,即不进行着色}
                          end;
end;

BEGIN
     write('please input city number: '); readln(n);
     writeln('please input the relation of the cities:');
     for k1:=1 to n do
     begin
          for k2:=1 to n do read(a[k1,k2]);  {A[K1,K2]=1表示省K1、K2相邻,为0就不相邻}
          readln;
     end;
     for k1:=1 to n do s[k1]:=0;  {把所有的颜色设置为0,即还没有进行着色}
     try(1);
END.


穿越迷宫
 

{本程序假设迷宫是一个4 X 4的矩阵,入口在A[1,1],出口在A[4,4]}
{矩阵数据放在文件shuju3.txt 中}
program mikong;
var
  a,b,c:array[1..4,1..4] of integer;  {数组A用来存放迷宫路径,约定元素值为0表示通,1表示不通}
                                      {数组B用来存放方向增量} 
                                      {数组C用来记录结果,当第I步移到某一元素时,该元素就等于I}
  i,j,k,m,n:integer;
  fv:text;
  q:boolean;  {用来标记迷宫是否有出路}

  procedure print;
  var
     m,n:integer;
  begin
    q:=true;  {如果打印步骤,表示肯定有出路}
    writeln;
    writeln;
    writeln('穿越迷宫的步骤是:');
    for m:=1 to 4 do
      begin
              for n:=1 to 4 do
          write(c[m,n]:4);
        writeln;
      end
  end;

  procedure try(x,y,i:integer);
  var
    u,v,k:integer;
  begin
    for k:=1 to 4 do   {可以有4个方向选择}
      begin
        u:=x+b[k,1];     {当前坐标加上方向增量}
        v:=y+b[k,2];
        if (u>=1) and (u<=4) and (v>=1) and (v<=4) then  {判断是否越界}
          if (a[u,v]=0) and (c[u,v]=0) then    {判断是否为0,为0就表示通,为1就表示不通}
            begin
              if (x=2) and (y=3) then writeln('aaaaaaaaaaaa');
              c[u,v]:=i;  {数组 C打上记号,表示此元素是第I步到达}
              if (u<>4) or (v<>4) then  {判断是否到出口}
                  try(u,v,i+1)  {没有就继续走}
                else   print;
              c[u,v]:=0   {下一路所有方向都不通,清除本次所做的标记}
            end
      end
  end;

 

begin
  assign(fv,'shuju3.txt');
  reset(fv);
  for i:=1 to 4 do
    begin
      for j:=1 to 4 do
        read(fv,a[i,j]);
        readln(fv)
      end;
  b[1,1]:=-1;  b[1,2]:=0;
  b[2,1]:=0;   b[2,2]:=1;
  b[3,1]:=1;   b[3,2]:=0;
  b[4,1]:=0;   b[4,2]:=-1;
  close(fv);
  for i:=1 to 4 do    {首先标记数组C所有元素全部为0}
    for j:=1 to 4 do c[i,j]:=0;
  c[1,1]:=1;
  for i:=1 to 4 do  {显示迷宫具体线路,0表示通,1表示不通}
    begin
      for j:=1 to 4 do
        write(a[i,j]:4);
        writeln
      end;
  q:=false;  {假设迷宫没有出路}
  try(1,1,2);
 if q=false then writeln( '此迷宫没有出路')
end.

算法一:A*寻路初探 From GameDev.net 译者序:很久以前就知道了A*算法,但是从未认真读过相关的文章,也没有看过代码,只是脑子里有个模糊的概念。这次决定从头开始,研究一下这个被人推崇备至的简单方法,作为学习人工智能的开始。 这 篇文章非常知名,国内应该有不少人翻译过它,我没有查找,觉得翻译本身也是对自身英文水平的锻炼。经过努力,终于完成了文档,也明白的A*算法的原理。毫 无疑问,作者用形象的描述,简洁诙谐的语言由浅入深的讲述了这一神奇的算法,相信每个读过的人都会对此有所认识(如果没有,那就是偶的翻译太差了-- b)。 原文链接:http://www.gamedev.net/reference/articles/article2003.asp 以下是翻译的正文。(由于本人使用ultraedit编辑,所以没有对原文的各种链接加以处理(除了图表),也是为了避免未经许可链接的嫌疑,有兴趣的读者可以参考原文。 会者不难,A*(念作A星)算法对初学者来说的确有些难度。 这篇文章并不试图对这个话题作权威的陈述。取而代之的是,它只是描述算法的原理,使你可以在进一步的阅读理解其他相关的资料。 最后,这篇文章没有程序细节。你尽可以用任意的计算机程序语言实现它。如你所愿,我在文章的末尾包含了一个指向例子程序的链接。 压缩包包括C++和Blitz Basic两个语言的版本,如果你只是想看看它的运行效果,里面还包含了可执行文件。 我们正在提高自己。让我们从头开始。。。 序:搜索区域 假设有人想从A点移动到一墙之隔的B点,如下图,绿色的是起点A,红色是终点B,蓝色方块是间的墙。 [图1] 你 首先注意到,搜索区域被我们划分成了方形网格。像这样,简化搜索区域,是寻路的第一步。这一方法把搜索区域简化成了一个二维数组。数组的每一个元素是网格 的一个方块,方块被标记为可通过的和不可通过的。路径被描述为从A到B我们经过的方块的集合。一旦路径被找到,我们的人就从一个方格的心走向另一个,直 到到达目的地。 这些点被称为“节点”。当你阅读其他的寻路资料时,你将经常会看到人们讨论节点。为什么不把他们描述为方格呢?因为有可 能你的路径被分割成其他不是方格的结构。他们完全可以是矩形,六角形,或者其他任意形状。节点能够被放置在形状的任意位置-可以在心,或者沿着边界,或 其他什么地方。我们使用这种系统,无论如何,因为它是最简单的。 开始搜索 正如我们处理上图网格的方法,一旦搜索区域被转化为容易处理的节点,下一步就是去引导一次找到最短路径的搜索。在A*寻路算法,我们通过从点A开始,检查相邻方格的方式,向外扩展直到找到目标。 我们做如下操作开始搜索: 1,从点A开始,并且把它作为待处理点存入一个“开启列表”。开启列表就像一张购物清单。尽管现在列表里只有一个元素,但以后就会多起来。你的路径可能会通过它包含的方格,也可能不会。基本上,这是一个待检查方格的列表。 2,寻找起点周围所有可到达或者可通过的方格,跳过有墙,水,或其他无法通过地形的方格。也把他们加入开启列表。为所有这些方格保存点A作为“父方格”。当我们想描述路径的时候,父方格的资料是十分重要的。后面会解释它的具体用途。 3,从开启列表删除点A,把它加入到一个“关闭列表”,列表保存所有不需要再次检查的方格。 在这一点,你应该形成如图的结构。在图,暗绿色方格是你起始方格的心。它被用浅蓝色描边,以表示它被加入到关闭列表了。所有的相邻格现在都在开启列表,它们被用浅绿色描边。每个方格都有一个灰色指针反指他们的父方格,也就是开始的方格。 [图2] 接着,我们选择开启列表的临近方格,大致重复前面的过程,如下。但是,哪个方格是我们要选择的呢?是那个F值最低的。 路径评分 选择路径经过哪个方格的关键是下面这个等式: F = G + H 这里: * G = 从起点A,沿着产生的路径,移动到网格上指定方格的移动耗费。 * H = 从网格上那个方格移动到终点B的预估移动耗费。这经常被称为启发式的,可能会让你有点迷惑。这样叫的原因是因为它只是个猜测。我们没办法事先知道路径的长 度,因为路上可能存在各种障碍(墙,水,等等)。虽然本文只提供了一种计算H的方法,但是你可以在网上找到很多其他的方法。 我们的路径是通过反复遍历开启列表并且选择具有最低F值的方格来生成的。文章将对这个过程做更详细的描述。首先,我们更深入的看看如何计算这个方程。 正 如上面所说,G表示沿路径从起点到当前点的移动耗费。在这个例子里,我们令水平或者垂直移动的耗费为10,对角线方向耗费为14。我们取这些值是因为沿对 角线的距离是沿水平或垂直移动耗费的的根号2(别怕),或者约1.414倍。为了简化,我们用10和14近似。比例基本正确,同时我们避免了求根运算和小 数。这不是只因为我们怕麻烦或者不喜欢数学。使用这样的整数对计算机来说也更快捷。你不就就会发现,如果你不使用这些简化方法,寻路会变得很慢。 既然我们在计算沿特定路径通往某个方格的G值,求值的方法就是取它父节点的G值,然后依照它相对父节点是对角线方向或者直角方向(非对角线),分别增加14和10。例子这个方法的需求会变得更多,因为我们从起点方格以外获取了不止一个方格。 H 值可以用不同的方法估算。我们这里使用的方法被称为曼哈顿方法,它计算从当前格到目的格之间水平和垂直的方格的数量总和,忽略对角线方向。然后把结果乘以 10。这被成为曼哈顿方法是因为它看起来像计算城市从一个地方到另外一个地方的街区数,在那里你不能沿对角线方向穿过街区。很重要的一点,我们忽略了一 切障碍物。这是对剩余距离的一个估算,而非实际值,这也是这一方法被称为启发式的原因。想知道更多?你可以在这里找到方程和额外的注解。 F的值是G和H的和。第一步搜索的结果可以在下面的图表看到。F,G和H的评分被写在每个方格里。正如在紧挨起始格右侧的方格所表示的,F被打印在左上角,G在左下角,H则在右下角。 [图3] 现在我们来看看这些方格。写字母的方格里,G = 10。这是因为它只在水平方向偏离起始格一个格距。紧邻起始格的上方,下方和左边的方格的G值都等于10。对角线方向的G值是14。 H 值通过求解到红色目标格的曼哈顿距离得到,其只在水平和垂直方向移动,并且忽略间的墙。用这种方法,起点右侧紧邻的方格离红色方格有3格距离,H值就 是30。这块方格上方的方格有4格距离(记住,只能在水平和垂直方向移动),H值是40。你大致应该知道如何计算其他方格的H值了~。 每个格子的F值,还是简单的由G和H相加得到 继续搜索 为了继续搜索,我们简单的从开启列表选择F值最低的方格。然后,对选的方格做如下处理: 4,把它从开启列表删除,然后添加到关闭列表。 5,检查所有相邻格子。跳过那些已经在关闭列表的或者不可通过的(有墙,水的地形,或者其他无法通过的地形),把他们添加进开启列表,如果他们还不在里面的话。把选的方格作为新的方格的父节点。 6,如果某个相邻格已经在开启列表里了,检查现在的这条路径是否更好。换句话说,检查如果我们用新的路径到达它的话,G值是否会更低一些。如果不是,那就什么都不做。 另一方面,如果新的G值更低,那就把相邻方格的父节点改为目前选的方格(在上面的图表,把箭头的方向改为指向这个方格)。最后,重新计算F和G的值。如果这看起来不够清晰,你可以看下面的图示。 好了,让我们看看它是怎么运作的。我们最初的9格方格,在起点被切换到关闭列表后,还剩8格留在开启列表。这里面,F值最低的那个是起始格右侧紧邻的格子,它的F值是40。因此我们选择这一格作为下一个要处理的方格。在紧随的图,它被用蓝色突出显示。 [图4] 首先,我们把它从开启列表取出,放入关闭列表(这就是他被蓝色突出显示的原因)。然后我们检查相邻的格子。哦,右侧的格子是墙,所以我们略过。左侧的格子是起始格。它在关闭列表里,所以我们也跳过它。 其 他4格已经在开启列表里了,于是我们检查G值来判定,如果通过这一格到达那里,路径是否更好。我们来看选格子下面的方格。它的G值是14。如果我们从当 前格移动到那里,G值就会等于20(到达当前格的G值是10,移动到上面的格子将使得G值增加10)。因为G值20大于14,所以这不是更好的路径。如果 你看图,就能理解。与其通过先水平移动一格,再垂直移动一格,还不如直接沿对角线方向移动一格来得简单。 当我们对已经存在于开启列表的4个临近格重复这一过程的时候,我们发现没有一条路径可以通过使用当前格子得到改善,所以我们不做任何改变。既然我们已经检查过了所有邻近格,那么就可以移动到下一格了。 于 是我们检索开启列表,现在里面只有7格了,我们仍然选择其F值最低的。有趣的是,这次,有两个格子的数值都是54。我们如何选择?这并不麻烦。从速度上 考虑,选择最后添加进列表的格子会更快捷。这种导致了寻路过程,在靠近目标的时候,优先使用新找到的格子的偏好。但这无关紧要。(对相同数值的不同对 待,导致不同版本的A*算法找到等长的不同路径。) 那我们就选择起始格右下方的格子,如图。 [图5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值