8 puzzle with A* algorithm

The main idea is from:

http://www.cs.princeton.edu/courses/archive/spr10/cos226/assignments/8puzzle.html

A* is used to find the best solution. The detail of A* could be found:

http://web.mit.edu/eranki/www/tutorials/search/

#include <iostream>
#include <vector>
#include <algorithm>
#include <list>
#include <queue>
#include <unordered_map>
#include <string>
#include <sstream>
#include <stdexcept>
#include <memory>
#include <Windows.h>


using namespace std;

class Board;
class Solver;

class Board
{
	friend class Solver;
	friend class compare_node;
public:
	typedef enum
	{
		TL_SPACE,
		TL_1,
		TL_2,
		TL_3,
		TL_4,
		TL_5,
		TL_6,
		TL_7,
		TL_8,
	}TILE;

	Board()
	{
		for (int i = 0; i != 8; tiles.push_back(TILE(++i)));
		tiles.push_back(TL_SPACE);
	}
	Board(int t[3][3])
	{
		for (int i = 0; i != 3; ++i)
			for (int j = 0; j != 3; ++j)
				tiles.push_back(TILE(t[i][j]));
	}

	int hamming()
	{
		int count = 0;
		for (int i = 0; i != tiles.size(); ++i)
			if (tiles[i] != TILE((i + 1)) && tiles[i] != TL_SPACE)
				++count;
		return count;
	}

	int manhattan()
	{
		int count = 0;
		int num;
		for (int i = 0; i != tiles.size(); ++i)
		{
			if (tiles[i] == TL_SPACE)
				continue;
			num = (tiles[i] + 8) % 9;
			count = count + (abs(num / 3 - i / 3) + abs(num % 3 - i % 3));
		}
		return count;
	}

	bool equals(const Board &b) const
	{
		bool ret = true;
		for (int i = 0; i != b.tiles.size(); ++i)
		{
			if (this->tiles[i] != b.tiles[i])
			{
				ret = false;
				break;
			}
		}
		return ret;
	}

	void neighbors(vector<Board> &nghbrs)
	{
		nghbrs.clear();
		int pos = 0;
		for (; pos != tiles.size() && tiles[pos] != TL_SPACE; ++pos);
		int row = pos / 3;
		int col = pos % 3;
		if (row > 0)
		{
			nghbrs.push_back(*this);
			nghbrs.back().swap(pos, (row - 1) * 3 + col);
		}
		if (row < 2)
		{
			nghbrs.push_back(*this);
			nghbrs.back().swap(pos, (row + 1) * 3 + col);
		}
		if (col > 0)
		{
			nghbrs.push_back(*this);
			nghbrs.back().swap(pos, row * 3 + col - 1);
		}
		if (col < 2)
		{
			nghbrs.push_back(*this);
			nghbrs.back().swap(pos, row * 3 + col + 1);
		}
	}

	string toString()
	{
		string s;
		ostringstream convert;
		for (int i = 0; i != tiles.size(); ++i)
			convert << static_cast<int>(tiles[i]) << " ";
		s = convert.str();
		return s;
	}

private:
	vector<TILE> tiles;
	Board *parent = nullptr;
	int f = 0;
	int g = 0;
	int h = 0;

	void swap(size_t pos1, size_t pos2)
	{
		if (pos1 >= tiles.size() || pos2 >= tiles.size())
			throw runtime_error("position not match");
		std::swap(tiles[pos1], tiles[pos2]);
	}
};

// function used to compared board in heap
class compare_node
{
public:
	bool operator()(Board *&l, Board *&r)
	{
		return l->f > r->f;
	}
};

// vector for board pointers
class bvector : public vector<Board*>
{
public:
	bvector()
	{
		make_heap(this->begin(), this->end(), compare_node());
	}

	void push_node(Board *b)
	{
		this->push_back(b);
		push_heap(this->begin(), this->end(), compare_node());
	}

	void pop_node()
	{
		pop_heap(this->begin(), this->end(), compare_node());
		this->pop_back();
	}
};

class Solver
{
	friend class Board;
public:
	Solver() = default;
	Solver(Board *p) : initial(p) {}


	void init()
	{
		if (initial == nullptr)
		{
			cerr << "Solver not initialized" << endl;
			return;
		}

		if (!isSolvable())
		{
			cerr << "No solution existed" << endl;
			return;
		}


		initial->g = 0;
		initial->h = initial->manhattan() + initial->hamming();
		initial->f = initial->g + initial->h;

		open_ref[initial->toString()] = initial;
		open_set.push_node(initial);


		while (!open_set.empty())
		{

			Board *closed_node = open_set.front();

			open_ref.erase(closed_node->toString());
			closed_ref[closed_node->toString()] = closed_node;
			open_set.pop_node();

			if (closed_node->equals(*end))
			{
				Board *p = closed_node;
				while (p->parent != nullptr)
				{
					solution.push_back(p->toString());
					p = p->parent;
				}
				solution.push_back(initial->toString());
				reverse(solution.begin(), solution.end());
				return;
			}

			vector<Board> neighbors;

			closed_node->neighbors(neighbors);

			for (auto iter = neighbors.begin(); iter != neighbors.end(); ++iter)
			{
				Board *new_node = new Board(*iter);
				new_node->g = 0;
				new_node->h = new_node->manhattan() + new_node->hamming();
				new_node->f = new_node->f + new_node->h;

				// ignore the neighbor which is already evaluated.
				if (closed_ref.find(new_node->toString()) != closed_ref.end())
				{
					delete new_node;
					continue;
				}

				if (open_ref.find(new_node->toString()) != open_ref.end())
				{
					delete new_node;
					continue;
				}

				new_node->parent = closed_node;


				open_set.push_node(new_node);


				open_ref[new_node->toString()] = new_node;


			}

		}
		return;
	}


	vector<string> getSolution()
	{
		return solution;
	}

	// http://www.geeksforgeeks.org/check-instance-8-puzzle-solvable/
	// It is not possible to solve an instance of 8 puzzle if the number of inversions is odd
	// in the initial state
	bool isSolvable()
	{
		int count = 0;
		for (int i = 0; i != initial->tiles.size(); ++i)
			for (int j = i + 1; j != initial->tiles.size(); ++j)
				if (initial->tiles[i] != Board::TILE::TL_SPACE &&
					initial->tiles[j] != Board::TILE::TL_SPACE &&
					static_cast<int>(initial->tiles[i]) > static_cast<int>(initial->tiles[j]))
					++count;
		return (count % 2 == 0);
	}

	~Solver()
	{
		delete initial;
		delete end;
		for (auto iter = open_ref.begin(); iter != open_ref.end(); ++iter)
			delete iter->second;
		for (auto iter = closed_ref.begin(); iter != closed_ref.end(); ++iter)
			delete iter->second;
	}

private:
	Board *initial = nullptr;
	Board *end = new Board();

	bvector open_set;
	unordered_map<string, Board*> open_ref;
	unordered_map<string, Board*> closed_ref;
	vector<string> solution;
};

int main()
{
	int t1[3][3] = { { 1, 2, 3 },{ 4, 5, 6 },{ 8, 7, 0 } };
	int t2[3][3] = { { 8, 1, 3 },{ 4, 0, 2 },{ 7, 6, 5 } };

	Board *b1 = new Board(t1);
	Board *b2 = new Board(t2);
	
	Solver s1(b1);
	Solver s2(b2);
	
	s1.init(); 
	
	s2.init();
	auto ret2 = s2.getSolution();
	cout << "The solution to " << b1->toString() << endl;
	for (auto str : ret2)
		cout << str << endl;
	system("PAUSE");
	return 0;
}


include using namespace std; struct node{ int nodesun[4][4]; int pre; //上一步在队列中的位置 int flag ; //步数标识,表示当前的步数为有效的 int value; //与目标的差距 int x,y; //空格坐标 }queue[1000]; //移动方向数组 int zx[4]={-1,0,1,0}; int zy[4]={0,-1,0,1}; //当前步数 int top; int desti[4][4];//目标状态 int detect(struct node *p)//检查是否找到 {int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) return 0; return 1; } //打印 void printlj() {int tempt; int i,j; tempt=top; while(tempt!=0) { for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<queue[tempt].nodesun[i][j]; if(j==3) cout<<" "<<endl; } tempt=queue[tempt].pre; } } //现在状态与目标状态有多少个不同位置 int VALUE(struct node *p) {int count=0; int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) count++; return count; } void main() { //初始化 int i,j,m,n,f; int min=10; int temp,find=0,minnumber; top=1; for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; queue[1].nodesun[i][j]=temp; } cout<<"请输入初始状态的空格的位置(行)"<>temp; queue[1].x=temp; cout<<"请输入初始状态的空格的位置(列)"<>temp; queue[1].y=temp; queue[1].value=VALUE(&queue[1]); queue[1].pre=0; //上一步在队列中的位置 queue[1].flag=0; //目标状态 for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入目标状态第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; desti[i][j]=temp; } //根据估价函数 while(!find&&top>0) { for(i=1;i<=top;i++) //////////////////////////////////////////// //min为上一图中与目标图有多少个元素不相同,queue[i]为当前图与目标图有多少个元素不相同通过这两个数的比较,就可以得出当前图较之上一图向目标图接近同时把当前的i记录下来进行下一步比较 {if(queue[i].value<min&&queue[i].flag==0) {minnumber=i;// min=queue[i].value; //还有多少不同的位数 } } queue[minnumber].flag=1; //表示此位有效 ////////////////////////////////////// // for(f=0;f=1&&i=1&&j<=3) {top++; ///////////////////////////////////////////// //位置交换 queue[top]=queue[minnumber]; queue[top].nodesun[m][n]=queue[minnumber].nodesun[i][j]; queue[top].nodesun[i][j]=0; /////////////////////////////////////// //空格移动方向 queue[top].x=i; queue[top].y=j; /////////////////////////////////////// queue[top].pre=minnumber; //上一步在队列中的位置 queue[top].value=VALUE(&queue[top]); //有多少位与目标不同 queue[top].flag=0; //标识位初始化 if(detect(&queue[top])) //检查是否为目标 {printlj(); //打印 find=1; //设找到标识位 break; } } } } }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值