自动寻路贪吃蛇C#

实现原理


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 snake = new Snake(24, 24);
            snake.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);
            }
        };
        public Point food;
        const int Down = 1;
        const int Up = 2;
        const int Right = 3;
        const int Left = 4;
        public LinkedList<Point> body;//存蛇的身体
        public int xlen;//x方向长度,偶数
        public int ylen;//y方向长度,偶数
        int[,] dir;
        bool[,] hasBody;//
        Random rd;
        public void debug()
        {
            Console.SetCursorPosition(0, 24);
            var t = front();
            for (int i = 0; i < xlen; i++)
            {
                for (int j = 0; j < ylen; j++)
                {
                    if (t == new Point(i, j))
                    {
                        Console.Write(" ");
                        continue;
                    }
                    switch (dir[i, j])
                    {
                        case Left:
                            Console.Write("L"); break;
                        case Right:
                            Console.Write("R"); break;
                        case Down:
                            Console.Write("D"); break;
                        case Up:
                            Console.Write("U"); break;
                    }
                }
                Console.WriteLine("");
            }
        }
        public Snake(int x, int y)
        {
            xlen = x;
            ylen = y;
            dir = new int[x, y];
            body = new LinkedList<Point>();
            for (int i = 0; i < x; i += 2)
            {
                for (int j = 0; j < y; j += 2)
                {
                    dir[i, j] = Right;
                    dir[i, j + 1] = Down;
                    dir[i + 1, j + 1] = Left;
                    dir[i + 1, j] = Up;
                }
            }
            hasBody = new bool[x, y];
            for (int i = 0; i < x; i++)
                for (int j = 0; j < y; j++)
                    hasBody[i, j] = false;
            rd = new Random();
            Point p = generateNode();
            push(p);
        }
        public void move()
        {
            var newHead = generateNewHead();
            push(newHead);
           Thread.Sleep(10);
            if (newHead == food)
            {
                food = generateNode();
                GenerateBody(food);
            }
            else
                pop();

        }
        public void eatFood()
        {
            food = generateNode();
            GenerateBody(food);
            int[] tmpdir = new int[2];
            while (body.Count < xlen * ylen)
            {
                var head = body.First.Value;
                var foodlt = getLT(food);
                var headlt = getLT(head);
                if(foodlt.x>headlt.x)
                for (int i = headlt.x; i < foodlt.x; i+=2)
                    ConnectDown(new Point(i, headlt.y));
                if (foodlt.x < headlt.x)
                    for (int i = headlt.x; i > foodlt.x; i -= 2)
                        ConnectUp(new Point(i, headlt.y));
                if (foodlt.y > headlt.y)
                    for (int i = headlt.y; i < foodlt.y; i += 2)
                        ConnectRight(new Point(foodlt.x, i));
                if (foodlt.y < headlt.y)
                    for (int i = headlt.y; i > foodlt.y; i -= 2)
                        ConnectLeft(new Point(foodlt.x, i));
                move();
                move();
                for(int i = 0; i < xlen; i+=2)
                {
                    for(int j = 0; j < ylen; j+=2)
                    {

                        var p = new Point(i, j);
                        if (inCircle(p) && !HasBody(p)) {
                            
                            var up = p;up.x -= 2;
                            if (LegalPoint(up) && inCircle(up)) DropDown(up);
                            var left = p;left.y -= 2;
                            if (LegalPoint(left) && inCircle(left)) DropRight(left);
                            var down = p; down.x += 2;
                            
                            if (LegalPoint(down) && inCircle(down)) DropUp(down);
                            var right = p; right.y += 2;
                            
                            if (LegalPoint(right) && inCircle(right)) DropLeft(right);
                        }
                        
                    }
                }
            }
        }

        public bool LegalPoint(Point p) {
            return p.x >= 0 && p.x < xlen && p.y >= 0 && p.y < ylen;
        }
        public Point generateNewHead()
        {
            var p = body.First.Value;

            switch (dir[p.x, p.y])
            {
                case Up:
                    p.x -= 1; break;
                case Down:
                    p.x += 1; break;
                case Right:
                    p.y += 1; break;
                case Left:
                    p.y -= 1; break;
                default: break;
            }
            return p;
        }

        public bool inCircle(Point p)
        {
            p = getLT(p);
            return !(dir[p.x, p.y] == Right && dir[p.x, p.y + 1] == Down
                     && dir[p.x + 1, p.y + 1] == Left && dir[p.x + 1, p.y] == Up);
        }
        public Point generateNode()
        {
            Point p;
            do
            {
                p.x = rd.Next(0, xlen - 1);
                p.y = rd.Next(0, ylen - 1);
            } while (hasBody[p.x, p.y]);
            return p;
        }
        public Point getLT(Point p)
        {
            p.x -= p.x % 2;
            p.y -= p.y % 2;
            return p;
        }
        public void ConnectLeft(Point p)
        {
            p = getLT(p);
            var t = p;
            t.y -= 2;
            if (inCircle(t)) return;
            dir[p.x, p.y - 1] = Right;
            dir[p.x + 1, p.y] = Left;
        }
        public void ConnectRight(Point p)
        {
            p = getLT(p);
            var t = p;
            t.y += 2;
            if (inCircle(t)) return;
            dir[p.x, p.y + 1] = Right;
            dir[p.x + 1, p.y + 2] = Left;
        }
        public void ConnectUp(Point p)
        {
            p = getLT(p);
            var t = p;
            t.x -= 2;
            if (inCircle(t)) return;
            dir[p.x, p.y] = Up;
            dir[p.x - 1, p.y + 1] = Down;
        }
        public void ConnectDown(Point p)
        {
            p = getLT(p);
            var t = p;
            t.x += 2;
            if (inCircle(t)) return;
            dir[p.x + 2, p.y] = Up;
            dir[p.x + 1, p.y + 1] = Down;
        }
        public void DropLeft(Point p)
        {
            p = getLT(p);
            dir[p.x, p.y - 1] = Down;
            dir[p.x + 1, p.y] = Up;
        }
        public void DropRight(Point p)
        {
            p = getLT(p);
            dir[p.x, p.y + 1] = Down;
            dir[p.x + 1, p.y + 2] = Up;
        }
        public void DropUp(Point p)
        {
            p = getLT(p);
            dir[p.x, p.y] = Right;
            dir[p.x - 1, p.y + 1] = Left;
        }
        public void DropDown(Point p)
        {
            p = getLT(p);
            dir[p.x + 2, p.y] = Right;
            dir[p.x + 1, p.y + 1] = Left;
        }

        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(" ");
        }
        public void push(Point p)
        {
            
            body.AddFirst(p);
            hasBody[p.x, p.y] = true;
            GenerateBody(p);
        }
        public void pop()
        {
            var p = body.Last.Value;
            body.RemoveLast();
            if (p != body.First.Value)
            {
                hasBody[p.x, p.y] = false;
                EraseTail(p);
            }
        }

        public void Drop(Point d) {
            Point n = body.Last.Value;
            if (n.x < d.x) { DropDown(n); return; }
            if (n.x > d.x) { DropUp(n); return; }
            if (n.y < d.y) { DropRight(n); return; }
            if (n.y > d.y) { DropLeft(n); return; }
        }
        public Point front()
        {
            return body.First.Value;
        }
        public bool HasBody(Point p)
        {
            p = getLT(p);
            for (int i = 0; i <= 1; i++)
                for (int j = 0; j <= 1; j++)
                    if (hasBody[p.x + i, p.y + j])
                        return true;
            return false;

        }
    }
}

 

具有自动寻路功能的推箱子算法 使用A 寻路算法 效果并非最优 但是希望可以给大家一些启发 有问题或建议欢迎站内联系 或电邮chenc9410@gmail com 希望对大家学习人工智能或者C#有帮助 附:算法简介 忽略了小人的移动状态 以箱子的位置作为状态变量 在大多数情况下 通过仅仅移动小人可以达到的地图(状态)我们认为是相同的 当然 有时小人的状态不同但是箱子的位置相同时 也可能属于两种不同的状态 因此我规定了有效连通域的概念:在不移动箱子的情况下小人可以达到的位置都属于这个有效连通域 我们通过宽度优先搜索来处理一个原始的地图 来得到一个标记了有效连通域的地图(这两个地图绑定在一起) 我的方法本质上属于A 方法 评价函数的h n 指的是“不在位”的箱子个数(这个和“八数码”问题类似) 当然 由于有6种对应关系 我们需要计算然后取出最小的值作为h n 我们通过检查不在位的箱子个数是否为0来判断是否移动成功 如果成功我们通过记录在每一个状态中的它的前一个状态来获得正确的搜索路线 然后可以对每两个相邻的状态分别作为宽度优先搜索的起点和终点来获得小人的具体路线 ">具有自动寻路功能的推箱子算法 使用A 寻路算法 效果并非最优 但是希望可以给大家一些启发 有问题或建议欢迎站内联系 或电邮chenc9410@gmail com 希望对大家学习人工智能或者C#有帮助 附:算法简介 忽略了小人的移动 [更多]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值