# A*算法【拼图游戏】

```using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace 拼图
{

/// <summary>
/// 空格移动的方向
/// </summary>
enum Direction
{
None,
Up,
Left,
Right,
Down
}

/// <summary>
/// 解答
/// </summary>
{
/// <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;

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];
}

/// <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))
else
{
same++;
if (depth < boardtable[newcode].Depth)
{
boardtable.Remove(newcode);
}
}
}
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;
}
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))
InitComp(start, end, maxDepth);
if (startBoard == endBoard)
long oldtime = System.DateTime.Now.Ticks;
int eval = 0;
eval = BestFirstSearch();
time = (System.DateTime.Now.Ticks - oldtime) / 10000000.0f;
if (eval == WinnerCode)
}

}
}```

A*算法【拼图游戏】

(0)
(0)