C#开发迷宫控制台小游戏MazeDemo

迷宫小游戏,主要使用二维数组,栈Stack,自定义操作符operator,递归遍历等功能。

在VS2017中,新建控制台应用程序MazeDemo,选择.net framework 4.5。

一、新建一个枚举Direction,用于记录方向。Direction.cs代码如下:

using System;

namespace MazeDemo
{
    ///


    /// 移动方向:默认迷宫单元格为尚未遍历(None)时,可以按照顺时针方向,
    /// 以此可为:Up---Right---Down---Left。四个方向都遍历完成时,设置为All
    ///

    public enum Direction
    {
        ///
        /// 无方向,尚未遍历。默认值
        ///

        None = 0,
        ///
        /// 向上
        ///

        Up = 1,
        ///
        /// 向右
        ///

        Right = 2,
        ///
        /// 向下
        ///

        Down = 4,
        ///
        /// 向左
        ///

        Left = 8,
        ///
        /// 全部遍历完成。上、右、下、左依次全部遍历
        ///

        All = 16
    }
}
二、新建迷宫单元格类MazeGrid,该类主要有属性:RowIndex(行索引),ColumnIndex(列索引),IsWall(是否是障碍墙),IsVisited(是否已访问),Direction(方向),以及==与!=操作符。MazeGrid.cs代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MazeDemo
{
    ///


    /// 表示迷宫中的一个小格子
    ///

    public class MazeGrid
    {
        ///
        /// 以行索引、列索引、是否是障碍来初始化一个网格
        ///

        ///
        ///
        ///
        public MazeGrid(int rowIndex, int columnIndex, bool isWall)
        {
            this.RowIndex = rowIndex;
            this.ColumnIndex = columnIndex;
            this.IsWall = isWall;
            this.Direction = Direction.None;
        }

        ///


        /// 方向
        ///

        public Direction Direction { get; set; }

        ///


        /// 网格所在的行的索引
        ///

        public int RowIndex { get; set; }

        ///


        /// 网格所在的列的索引
        ///

        public int ColumnIndex { get; set; }

        ///


        /// 是否是障碍墙,如果是障碍,将无法通过
        ///

        public bool IsWall { get; set; }

        ///


        /// 是否已访问
        ///

        public bool IsVisited { get; set; }

        ///


        /// 比较两个网格是否【在同一个位置】
        ///

        ///
        ///
        ///
        public static bool operator ==(MazeGrid mg1, MazeGrid mg2)
        {
            return mg1.RowIndex == mg2.RowIndex && mg1.ColumnIndex == mg2.ColumnIndex;
        }

        ///


        /// 比较两个网格是否【不在同一个位置】
        ///

        ///
        ///
        ///
        public static bool operator !=(MazeGrid mg1, MazeGrid mg2)
        {
            return mg1.RowIndex != mg2.RowIndex || mg1.ColumnIndex != mg2.ColumnIndex;
        }

        ///


        /// 重写相等比较
        ///

        ///
        ///
        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }
            MazeGrid temp = obj as MazeGrid;
            return this == temp;
        }

        ///


        /// 打印该网格对象
        ///

        ///
        public override string ToString()
        {
            return $"{{Row={RowIndex},Column={ColumnIndex},Direction={Direction}}}";
        }
    }
}
三、新建关键迷宫逻辑类MazeUtil.cs。

比如 4行5列 网格如图示例,思路如下:按照上Up、右Right、下Down、左Left的顺序进行遍历,比如当前方向是Up,寻找后,将其置为Right继续遍历。当遍历完Left时,认为已完成,此时置为All。

C#开发迷宫控制台小游戏MazeDemo_第1张图片

源代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MazeDemo
{
    ///


    /// 迷宫可以认为是一个N*M的行列式,也可以认为是一个二维数组,每个元素都是一个单元格MazeGrid
    ///

    public class MazeUtil
    {
        ///
        /// 使用一个障碍墙的二维数组来初始化迷宫地图
        ///

        /// 数组元素的值为1时,代表障碍墙,否则是可通过的
        public MazeUtil(int[,] wallArray)
        {
            if (wallArray == null || wallArray.Length == 0)
            {
                throw new Exception("初始化数组不能为空");
            }
            Width = wallArray.GetLength(0);
            Height = wallArray.GetLength(1);
            //初始化地图
            MazeArray = new MazeGrid[Width, Height];
            for (int i = 0; i < Width; i++)
            {
                for (int j = 0; j < Height; j++)
                {
                    MazeArray[i, j] = new MazeGrid(i, j, wallArray[i, j] == 1);
                }
            }
            CurrentGrid = MazeArray[0, 0];
            TargetGrid = MazeArray[Width - 1, Height - 1];
        }
        ///
        /// 迷宫的宽度,也就是二维数组的行数
        ///

        public int Width { get; set; }
        ///
        /// 迷宫的高度,也就是二维数组的列数
        ///

        public int Height { get; set; }
        ///
        /// 迷宫行列式,由 Width*Height 个网格组成
        ///

        public MazeGrid[,] MazeArray { get; set; }

        ///


        /// 当前网格,起点默认为左上角,即 MazeGrid[0,0]
        ///

        public MazeGrid CurrentGrid { get; set; }

        ///


        /// 终点:目标网格,默认为右下角,即 MazeGrid[Width-1,Height-1]
        ///

        public MazeGrid TargetGrid { get; set; }

        ///


        /// 栈,用于存储移动的路径:找到一个 未访问的 并且 不是障碍,就Push()入栈
        ///

        public Stack stack = new Stack();

        ///


        /// 以此遍历当前网格的上、右、下、左四个方向。
        /// 如果遇到障碍 或者 已访问过,就尝试其他方向。否则就把 无障碍 并且 未访问的网格作为新的网格
        ///

        ///
        public void MoveNext(MazeGrid mazeGrid)
        {
            Direction direction = Direction.All;
            //按照上、右、下、左【顺时针】顺序以此遍历,当遍历完左Left后,则全部遍历完成,此时置方向为All
            switch (mazeGrid.Direction)
            {
                case Direction.None://当是None时,默认向上Up遍历
                    mazeGrid.Direction = Direction.Up;
                    direction = Direction.Up;
                    break;
                case Direction.Up://当是Up时,接着向右Right遍历
                    mazeGrid.Direction = Direction.Right;
                    direction = Direction.Right;
                    break;
                case Direction.Right://当是Right时,接着向下Down遍历
                    mazeGrid.Direction = Direction.Down;
                    direction = Direction.Down;
                    break;
                case Direction.Down://当是Down时,接着向左Left遍历
                    mazeGrid.Direction = Direction.Left;
                    direction = Direction.Left;
                    break;
                case Direction.Left://当是Left时,说明四个方向全部遍历完了,置为All
                    mazeGrid.Direction = Direction.All;
                    direction = Direction.All;
                    break;
            }

            //对上、右、下、左四个方向进行处理【None 和 All不做处理】
            switch (direction)
            {
                case Direction.Up:
                    if (mazeGrid.RowIndex - 1 >= 0)
                    {
                        MazeGrid upGrid = MazeArray[mazeGrid.RowIndex - 1, mazeGrid.ColumnIndex];
                        if (!upGrid.IsWall && !upGrid.IsVisited)
                        {
                            //如果不是障碍 并且 没有访问过
                            CurrentGrid = upGrid;
                        }
                        else
                        {
                            //尝试其他方向
                            MoveNext(CurrentGrid);
                        }
                    }
                    break;
                case Direction.Right:
                    if (mazeGrid.ColumnIndex + 1 < Height)
                    {
                        MazeGrid rightGrid = MazeArray[mazeGrid.RowIndex, mazeGrid.ColumnIndex + 1];
                        if (!rightGrid.IsWall && !rightGrid.IsVisited)
                        {
                            //如果不是障碍 并且 没有访问过
                            CurrentGrid = rightGrid;
                        }
                        else
                        {
                            //尝试其他方向
                            MoveNext(CurrentGrid);
                        }
                    }
                    break;
                case Direction.Down:
                    if (mazeGrid.RowIndex + 1 < Width)
                    {
                        MazeGrid downGrid = MazeArray[mazeGrid.RowIndex + 1, mazeGrid.ColumnIndex];
                        if (!downGrid.IsWall && !downGrid.IsVisited)
                        {
                            //如果不是障碍 并且 没有访问过
                            CurrentGrid = downGrid;
                        }
                        else
                        {
                            //尝试其他方向
                            MoveNext(CurrentGrid);
                        }
                    }
                    break;
                case Direction.Left:
                    if (mazeGrid.ColumnIndex - 1 >= 0)
                    {
                        MazeGrid leftGrid = MazeArray[mazeGrid.RowIndex, mazeGrid.ColumnIndex - 1];
                        if (!leftGrid.IsWall && !leftGrid.IsVisited)
                        {
                            //如果不是障碍 并且 没有访问过
                            CurrentGrid = leftGrid;
                        }
                        else
                        {
                            //尝试其他方向
                            MoveNext(CurrentGrid);
                        }
                    }
                    break;
            }
        }

        ///


        /// 查找路径
        ///

        public void FindPath()
        {
            //如果当前网格没有移动到目标网格。
            //这里如果遇到无法到达目标网格的障碍地图时,需要终止
            while (CurrentGrid != TargetGrid)
            {
                if (CurrentGrid.IsVisited)
                {
                    //如果当前网格全部访问完成,则出栈
                    if (CurrentGrid.Direction == Direction.All)
                    {
                        if (stack.Count > 0)
                        {
                            stack.Pop();//移除最后一次添加的
                        }
                        if (stack.Count > 0)
                        {
                            //获取倒数第二次添加的
                            CurrentGrid = stack.Peek();
                        }
                        else
                        {
                            Console.WriteLine("无路可走,请检查迷宫障碍设置...");
                            break;
                        }
                    }
                    else
                    {
                        //没有遍历完,继续遍历
                        MoveNext(CurrentGrid);
                    }
                }
                else
                {
                    //如果未访问,则设置为已访问,同时添加入栈
                    CurrentGrid.IsVisited = true;
                    stack.Push(MazeArray[CurrentGrid.RowIndex, CurrentGrid.ColumnIndex]);     
                }
            }
            //将目标网格添加到顶部
            if (stack.Count > 0)
            {
                stack.Push(TargetGrid);
            }
        }

        ///


        /// 打印路径
        ///

        public void PrintPath()
        {
            if (stack.Count == 0)
            {
                Console.WriteLine("无法到达目的网格,请检查迷宫地图设置...");
                return;
            }
            //因第一个插入的元素是入口,栈是先进后出,入口反而成为最后元素。这里进行反转
            IEnumerable grids = stack.Reverse();
            foreach (MazeGrid item in grids)
            {
                Console.WriteLine(item);
            }
        }
    }
}
 

四、在项目默认的Program.cs添加如下测试用例。

分死路 和 可连通两种情况。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MazeDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //测试无路可走
            int[,] NoWayArray = new int[5, 5] { { 0, 0, 1, 0, 0 }, { 0, 1, 0, 1, 0 }, { 0, 1, 0, 1, 0 }, { 0, 1, 0, 1, 0 }, { 0, 0, 0, 1, 0 } };
            MazeUtil mazeUtil = new MazeUtil(NoWayArray);
            mazeUtil.FindPath();
            mazeUtil.PrintPath();
            Console.WriteLine();

            //测试可到达
            int[,] wallArray = new int[5, 5] { { 0, 0, 1, 0, 0 }, { 0, 1, 0, 1, 0 }, { 0, 1, 0, 0, 0 }, { 0, 1, 0, 1, 0 }, { 0, 0, 0, 1, 0 } };
            mazeUtil = new MazeUtil(wallArray);
            mazeUtil.FindPath();
            mazeUtil.PrintPath();
            Console.ReadLine();
        }
    }
}
五、程序运行结果如下图:

C#开发迷宫控制台小游戏MazeDemo_第2张图片

 

你可能感兴趣的:(C#)