贪吃蛇AI-思路二

另一种实现思路

首先,将点阵转化成一个大环路,如下图:

为了美观,我们将它画成圆形,图中橙色为蛇的头,两次键头指向了食物,由于蛇头一般有三个方向(其中一个必是蛇的身体)可以移动,因此,它可以在(一定条件下)不按这个圆圈走,跳着走,便会体现一定的智能性,那么是在什么样的条件下呢?

蛇头和蛇尾之间的所有的点(其中一些并没有被蛇的身体占据),归类为蛇头不可跳点。只要不是不可跳点,那么蛇都可以尽可能的抄近路跳着走来体现智能性。

使用方法Snake(int x,int y)控制长和宽,必须为偶数

Thread.Sleep(100)指制跑的速度;己注释,跑得老快,算法本身很快但打印跟不上。

 

 

 

 

 

 

 


using System;
using System.Collections;
using System.Threading;
using System.Collections.Generic;

//特殊情况 头走尾,尾不删,不置false
//遇食物,尾不动
namespace snakeai
{

    class Program
    {
        static void Main(string[] args)
        {
            Console.CursorVisible = false;
            Snake a = new Snake(16, 16);
            a.eatFood();
        }
    }
    class Snake {
        public struct Point {
            public int x;
            public int y;
            public Point(int xx, int yy) {
                x = xx; y = yy;
            }
            public static bool operator ==(Point a, Point b) {
                return a.x == b.x && a.y == b.y;
            }
            public static bool operator !=(Point a, Point b)
            {
                return !(a == b);
            }

        }

        LinkedList<Point> body;
        Hashtable hash;
        Point[] pos;
        Point food;
        Random rd;
        int xlen;
        int ylen;
        bool[,] board;
        public Point generateNode() {
            Point p;
            int len = xlen * ylen - body.Count;
            if(len==0) return new Point(-1, -1);
            int t = rd.Next(0, len-1);
            int c = 0;
            for (int i = 0; i < xlen; i++) { 
                for(int j = 0; j < ylen; j++)
                {
                    if (board[i, j] == false) {
                        
                        if (c == t) return new Point(i, j);
                        c++;
                    }
                }
            }
            Console.WriteLine("Error");
            return new Point(-1, -1);
        }
        public Snake(int x ,int y) {
            body = new LinkedList<Point>();
            hash = new Hashtable();
            rd = new Random();
            board = new bool[x, y];
            for (int i = 0; i < x; i++) for (int j = 0; j < y; j++) board[i, j] = false;
            xlen = x;
            ylen = y;
            int cnt = 0;
            pos = new Point[x * y];
            for (int i = 0; i < x; i++) {
                if (i % 2 == 1)
                {
                    for (int j = ylen - 1; j > 0; j--)
                    {
                        if (hash.Contains(new Point(i, j)))
                            hash.Remove(new Point(i, j));
                        hash.Add(new Point(i, j), cnt);
                        pos[cnt++] = new Point(i, j);
                    }
                }
                else
                {
                    for (int j = 1; j < ylen; j++)
                    {
                        if (hash.Contains(new Point(i, j)))
                            hash.Remove(new Point(i, j));
                        hash.Add(new Point(i, j), cnt);
                        pos[cnt++] = new Point(i, j);
                    }
                }
            }
            for (int i = xlen - 1; i >= 0; i--) {
                if (hash.Contains(new Point(i, 0)))
                    hash.Remove(new Point(i, 0));
                hash.Add(new Point(i, 0), cnt);
                pos[cnt++] = new Point(i, 0);
            }
            food = generateNode();
            GenerateBody(food);
            var s = generateNode();
            GenerateBody(s);
            body.AddFirst(s);
        }
        public Point generateNewHead() {
            var head = body.First.Value;
            var p = (int)hash[head];
            return pos[(p + 1)%(xlen*ylen)];
        }
        public void move(Point newHead) {
            //Thread.Sleep(100);
            body.AddFirst(newHead);
            board[newHead.x, newHead.y] = true;
            GenerateBody(newHead);
            if (newHead == food)
            {
                food = generateNode();
                if(food.x!=-1)
                    GenerateBody(food);
            }
            else
            {
                var last = body.Last.Value;
                body.RemoveLast();
                
                if (last != newHead)
                {
                    board[last.x, last.y] = false;
                    EraseTail(last);
                }
            }
        }
        
        public void eatFood() {
            while (body.Count < xlen * ylen)
            {
                int dx = (food.x > body.First.Value.x) ? 1 : -1;
                int dy = (food.y > body.First.Value.y) ? 1 : -1;
                if (food.x == body.First.Value.x) dx = 0;
                if (food.y == body.First.Value.y) dy = 0;
                var head = body.First.Value;
                Point[] tmp = new Point[4];
                tmp[0].x = head.x+1;tmp[0].y = head.y;
                tmp[1].x = head.x-1;tmp[1].y = head.y;
                tmp[2].x = head.x;tmp[2].y = head.y+1;
                tmp[3].x = head.x;tmp[3].y = head.y-1;
                Point init = generateNewHead();
                int len = Distance(init, food);
                for (int i = 0; i < 4; i++) {
                    if (LegalPoint(tmp[i])&&board[tmp[i].x, tmp[i].y] == false&&inCircle(tmp[i])==false) {
                        if (Distance(init, food) > Distance(tmp[i], food)) 
                            init = tmp[i];
                    }
                }
                move(init);
            }
            while (true) ;
        }
        public bool LegalPoint(Point p)
        {
            return p.x >= 0 && p.x < xlen && p.y >= 0 && p.y < ylen;
        }
        public int Distance(Point a, Point b) {
            int alen = (int)hash[a];
            int blen = (int)hash[b];
            if (alen > blen)
                return (xlen*ylen-(alen-blen));
            else
                return blen - alen;
            
        }
        public bool inCircle (Point p){
            int start = (int)hash[body.First.Value];
            int end = (int)hash[body.Last.Value];
            int v =(int) hash[p];
            if (start >= end){
                return v <= start && v >= end;
            }
            else {
                return !(v <= start && v >= end);
            }
        }
        public static void GenerateBody(Point p)
        {
            Console.SetCursorPosition(p.x, p.y);
            Console.Write("*");
        }
        public static void EraseTail(Point p)
        {
            Console.SetCursorPosition(p.x, p.y);
            Console.Write(" ");
        }
    }

}

 

Snake-AI,这是一个用 C/C 语言编写的贪吃蛇游戏的人工智能。AI 的目的是让蛇尽可能的吃更多的食物,直到吃满整个地图。想参与这个项目,请查看todos。Demo使用方法编译与运行:$ make $ make run为了解详细使用方法, 请查看主函数main()算法介绍函数Snake.decideNext(): 计算蛇S1的下一个移动方向D计算从蛇S1的头部到达食物的最短路径P1。派一条与蛇S1完全一样的虚拟蛇S2沿路径P1吃掉食物。计算从蛇S2的头部到其尾部的最长路径P2。如果路径P2存在,将移动方向D设置为路径P1的第一个方向,否则进行步骤4。计算从蛇S1的头部到达其尾部的最长路径P3。如果P3存在,将移动方向D设置为路径P3的第一个方向,否则进行步骤5。将移动方向D设置为离食物最远的方向。函数Map.findMinPath(): 计算两个位置间的最短路径算法建立在BFS的基础上。为了使路径尽可能直,每次遍历邻接点时,在当前移动方向上的位置会被优先遍历。效果展示:(绿色区域为搜索算法扫描到的区域,红色区域为最后计算出的最短路径,每个位置上的数字表示了从起始位置开始到该位置的最短距离)函数Map.findMaxPath(): 计算两个位置间的最长路径算法建立在DFS与贪心算法的基础上。每次遍历邻接点时,离目标位置最远(使用曼哈顿距离估计)的位置将会被优先遍历到。另外,为了使路径尽可能直,如果两个位置到目标位置的距离相等,在当前移动方向上的位置将被优先遍历到。这个问题是一个NP完全问题,此算法得出的结果路径只是一个近似最长路径。效果展示:(绿色区域为搜索算法扫描到的区域,红色区域为最后计算出的最长路径,每个位置上的数字表示了从该位置开始到目标位置的估计距离) 标签:Snake
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值