A*算法拼图游戏
Posted fuao2000
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了A*算法拼图游戏相关的知识,希望对你有一定的参考价值。
数据结构
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 拼图 { /// <summary> /// 空格移动的方向 /// </summary> enum Direction { None, Up, Left, Right, Down } /// <summary> /// 解答 /// </summary> enum Answer { /// <summary> /// 不存在解答 /// </summary> NotExist, /// <summary> /// 存在解答 /// </summary> Exist, /// <summary> /// 在当前指定的搜索深度内不存在解答(需要扩大深度) /// </summary> NotExistInDepth } /// <summary> /// 局面状态信息 /// </summary> class StateMsg { private int depth; private Direction dir; public int Depth { get { return depth; } } public Direction Dir { get { return dir; } } public StateMsg(int depth, Direction dir) { this.depth = depth; this.dir = dir; } } /// <summary> /// 局面状态 /// </summary> class State : StateMsg { private long code; /// <summary> /// 棋盘的编码 /// </summary> public long Code { get { return code; } set { code = value; } } public State(long code, int depth, Direction dir) : base(depth, dir) { this.code = code; } } }
Astar类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 拼图 { class AStar { /// <summary> /// ten[i]代表10的i次方 /// </summary> private static readonly long[] tens = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; /// <summary> /// 不是合理的编码 /// </summary> private const int OutOfCode = -1; /// <summary> /// 标志是否找到目标状态的编码 /// </summary> public const int WinnerCode = 1; private Direction[][] dirs; private int startBoard; private static int endBoard; private static int[] endBoardArray; private int MaxDepth; private SortedList<long, StateMsg> openList; private Dictionary<long, StateMsg> boardtable; private const long maxNodes = 9223372036854775800; //至多搜索的结点数=最大局面状态数量:(9!)=362880; private long nodes; private int same; private float time; private Direction[] result; /// <summary> /// 已经访问的结点数量 /// </summary> public long Nodes { get { return nodes; } } /// <summary> /// 重复访问相同结点的数量 /// </summary> public int Same { get { return same; } } public float Time { get { return time; } } public static int N; /// <summary> /// 最终结果 /// </summary> public Direction[] Result { get { return result; } } public AStar(int n) { N = n; dirs = new Direction[n*n][]; if(n==3) { dirs[0] = new Direction[] { Direction.Right, Direction.Down }; dirs[1] = new Direction[] { Direction.Left, Direction.Right, Direction.Down }; dirs[2] = new Direction[] { Direction.Left, Direction.Down }; dirs[3] = new Direction[] { Direction.Up, Direction.Right, Direction.Down }; dirs[4] = new Direction[] { Direction.Up, Direction.Left, Direction.Right, Direction.Down }; dirs[5] = new Direction[] { Direction.Up, Direction.Left, Direction.Down }; dirs[6] = new Direction[] { Direction.Up, Direction.Right }; dirs[7] = new Direction[] { Direction.Left, Direction.Right, Direction.Up }; dirs[8] = new Direction[] { Direction.Up, Direction.Left }; } else { dirs[0] = new Direction[] { Direction.Right, Direction.Down }; dirs[1] = new Direction[] { Direction.Left, Direction.Right, Direction.Down }; dirs[2] = new Direction[] { Direction.Left, Direction.Right, Direction.Down }; dirs[3] = new Direction[] { Direction.Left, Direction.Right, Direction.Down }; dirs[4] = new Direction[] { Direction.Left, Direction.Down }; dirs[5] = new Direction[] { Direction.Up, Direction.Right, Direction.Down }; dirs[6] = new Direction[] { Direction.Up, Direction.Left, Direction.Right, Direction.Down }; dirs[7] = new Direction[] { Direction.Up, Direction.Left, Direction.Right, Direction.Down }; dirs[8] = new Direction[] { Direction.Up, Direction.Left, Direction.Right, Direction.Down }; dirs[9] = new Direction[] { Direction.Up, Direction.Left, Direction.Down }; dirs[10] = new Direction[] { Direction.Up, Direction.Right, Direction.Down }; dirs[11] = new Direction[] { Direction.Up, Direction.Left, Direction.Right, Direction.Down }; dirs[12] = new Direction[] { Direction.Up, Direction.Left, Direction.Right, Direction.Down }; dirs[13] = new Direction[] { Direction.Up, Direction.Left, Direction.Right, Direction.Down }; dirs[14] = new Direction[] { Direction.Up, Direction.Left, Direction.Down }; dirs[15] = new Direction[] { Direction.Up, Direction.Right, Direction.Down }; dirs[16] = new Direction[] { Direction.Up, Direction.Left, Direction.Right, Direction.Down }; dirs[17] = new Direction[] { Direction.Up, Direction.Left, Direction.Right, Direction.Down }; dirs[18] = new Direction[] { Direction.Up, Direction.Left, Direction.Right, Direction.Down }; dirs[19] = new Direction[] { Direction.Up, Direction.Left, Direction.Down }; dirs[20] = new Direction[] { Direction.Up, Direction.Right }; dirs[21] = new Direction[] { Direction.Left, Direction.Right, Direction.Up }; dirs[22] = new Direction[] { Direction.Left, Direction.Right, Direction.Up }; dirs[23] = new Direction[] { Direction.Left, Direction.Right, Direction.Up }; dirs[24] = new Direction[] { Direction.Up, Direction.Left }; } } /// <summary> /// 求与目标位置不同的个数(不计空格,因此返回值0~8) /// </summary> /// <param name="curboard"></param> /// <returns></returns> public static int Different(int curboard) { int t_start = curboard; int emp_start = curboard % 10; int ev = 0; //写2个for是为了减少9个if for (int i = N*N; i > emp_start; i--) { t_start /= 10; if (t_start % 10 != endBoardArray[i]) ev++; } for (int i = emp_start - 1; i >= 1; i--) { t_start /= 10; if (t_start % 10 != endBoardArray[i]) ev++; } return ev; } public static int getBoard(long code) { return (int)(code % tens[9]); } private static int getEval(long code) { return (int)(code / tens[9]); } private static int getEmpIndex(long code) { return (int)(code % 10); } private static long combinCode(int board, int eval) { long codehead = eval * tens[9]; return codehead + board; } /// <summary> /// 改变局面(移动空格) /// </summary> /// <param name="code"></param> /// <param name="dir"></param> public static long change(long code, Direction dir) { int newboard; int eval; int num; int t0; long t1; long t2; switch (dir) { case Direction.Left: newboard = getBoard(code) - 1; if (newboard == endBoard) return WinnerCode; eval = Different(newboard); return combinCode(newboard, eval); case Direction.Right: newboard = getBoard(code) + 1; if (newboard == endBoard) return WinnerCode; eval = Different(newboard); return combinCode(newboard, eval); case Direction.Up: num = getBoard(code); t0 = N*N - num % 10 + 1; t1 = num / tens[t0]; t2 = t1 % 1000; t1 = t1 - t2 + (t2 % 100) * 10 + t2 / 100; t1 *= tens[t0]; newboard = (int)(t1 + ((num % tens[t0]) - N)); if (newboard == endBoard) return WinnerCode; eval = Different(newboard); return combinCode(newboard, eval); case Direction.Down: num = getBoard(code); t0 = N*N - num % 10 + 1 - N;//跟Up不同的地方 t1 = num / tens[t0]; t2 = t1 % 1000; t1 = t1 - t2 + (t2 % 10) * 100 + t2 / 10;//跟Up不同的地方 t1 *= tens[t0]; newboard = (int)(t1 + ((num % tens[t0]) + N));//跟Up不同的地方 if (newboard == endBoard) return WinnerCode; eval = Different(newboard); return combinCode(newboard, eval); } return OutOfCode; } /// <summary> /// 恢复上一步的局面 /// </summary> /// <param name="code"></param> /// <param name="dir"></param> public long unchange(long code, Direction dir) { return change(code, (Direction)(5 - dir)); } /// <summary> /// 当找到目标时,从哈希表里找原来的路径 /// </summary> /// <param name="endCode"></param> /// <param name="depth"></param> private void setResult(long endCode, Direction curDir, Direction lastDir, int depth) { long lastCode = endCode; result = new Direction[depth]; result[depth - 1] = curDir; for (int i = depth - 2; i >= 0; i--) { if (boardtable.ContainsKey(lastCode)) { result[i] = boardtable[lastCode].Dir; lastCode = unchange(lastCode, result[i]); } else return; } } //本算法的核心部分 #region 带Open表和HashTable的最好优先搜索(每次扩展Open表后都对Open表排序) /// <summary> /// 扩展Open表 /// </summary> /// <param name="board"></param> /// <param name="depth"></param> private bool extentOpenList(long code, Direction lastDir, int depth) { int empIndex = (int)(code % 10 - 1); int len_moves = dirs[empIndex].Length; long newcode; Direction dir = 5 - lastDir; for (int i = 0; i < len_moves; i++) if (dirs[empIndex][i] != dir) { newcode = change(code, dirs[empIndex][i]); //跟目标匹配,结束 if (newcode == WinnerCode) { setResult(code, dirs[empIndex][i], lastDir, depth); return true; } if (newcode <= tens[8]) throw new Exception("棋盘码修改错误! "); if (newcode != OutOfCode) { if (!openList.ContainsKey(newcode)) { if (!boardtable.ContainsKey(newcode)) openList.Add(newcode, new StateMsg(depth, dirs[empIndex][i])); else { same++; if (depth < boardtable[newcode].Depth) { boardtable.Remove(newcode); openList.Add(newcode, new StateMsg(depth, dirs[empIndex][i])); } } } else { same++; if (depth < openList[newcode].Depth) openList[newcode] = new StateMsg(depth, dirs[empIndex][i]); } } } return false; } /// <summary> /// 带Open表和HashTable的最好优先搜索(每次扩展Open表后都对Open表排序) /// </summary> /// <returns></returns> private int BestFirstSearch() { int depth = 1; Direction[] moves; int board; long newCode = combinCode(this.startBoard, 0); int empIndex = getEmpIndex(newCode); moves = dirs[empIndex - 1]; StateMsg data; if (extentOpenList(newCode, Direction.None, depth)) return WinnerCode; while (openList.Count > 0) { lock (this) { nodes++; if (nodes >= maxNodes) return -1; newCode = openList.Keys[0]; board = getBoard(newCode); data = openList[newCode]; if (data.Depth != 0) { depth = data.Depth; if (board == endBoard) { return WinnerCode; } boardtable.Add(newCode, new StateMsg(depth, data.Dir)); openList.RemoveAt(0); if (depth < MaxDepth) if (extentOpenList(newCode, data.Dir, depth + 1)) return WinnerCode; } } } return -1; } #endregion /// <summary> /// 把一维数组的局面编码成一个整数的表示形式 /// </summary> /// <param name="genBoard"></param> /// <returns></returns> public int ToIntBoard(int[] genBoard) { int board = 0; int emp = 0; for (int i = 0; i < genBoard.Length; i++) { if (genBoard[i] != 0) board = 10 * board + genBoard[i]; else emp = i + 1; } return 10 * board + emp; } /// <summary> /// 判断是否可以从初始状态到达目标状态(计算两个状态的逆序列,奇偶性相同的返回true) /// </summary> /// <param name="start"></param> /// <param name="end"></param> /// <returns></returns> private bool ExistAns(int[] start, int[] end) { int sequence_start = 0, sequence_end = 0; for (int i = 0; i < start.Length; i++) { if (start[i] != 0) for (int j = i + 1; j < start.Length; j++) { if (start[j] != 0 && start[j] < start[i]) sequence_start++; } if (end[i] != 0) for (int j = i + 1; j < start.Length; j++) { if (end[j] != 0 && end[j] < end[i]) sequence_end++; } } return (sequence_start + sequence_end) % 2 == 0; } /// <summary> /// 初始化求解 /// </summary> /// <param name="start"></param> /// <param name="end"></param> /// <param name="maxDepth"></param> private void InitComp(int[] start, int[] end, int maxDepth) { nodes = 0; same = 0; MaxDepth = maxDepth; result = null; boardtable = new Dictionary<long, StateMsg>(); openList = new SortedList<long, StateMsg>(); //openStack = new Stack<State>(); //openQueue = new Queue<State>(); this.startBoard = ToIntBoard(start); endBoard = ToIntBoard(end); int t_end = endBoard; int emp_end = endBoard % 10; endBoardArray = new int[N*N+1]; endBoardArray[0] = emp_end; endBoardArray[emp_end] = 0; for (int i = N*N; i > emp_end; i--) { t_end /= 10; endBoardArray[i] = t_end % 10; } for (int i = emp_end - 1; i >= 1; i--) { t_end /= 10; endBoardArray[i] = t_end % 10; } } /// <summary> /// 求解8数码问题 /// </summary> /// <param name="start"></param> /// <param name="end"></param> /// <param name="maxDepth"></param> /// <returns></returns> public Answer Compute(int[] start, int[] end, int maxDepth) { if (!ExistAns(start, end)) return Answer.NotExist; InitComp(start, end, maxDepth); if (startBoard == endBoard) return Answer.Exist; long oldtime = System.DateTime.Now.Ticks; int eval = 0; eval = BestFirstSearch(); time = (System.DateTime.Now.Ticks - oldtime) / 10000000.0f; if (eval == WinnerCode) return Answer.Exist; return Answer.NotExistInDepth; } } }
以上是关于A*算法拼图游戏的主要内容,如果未能解决你的问题,请参考以下文章