基于广度优先搜索的拼图游戏算法

1 问题描述

拼图游戏,通常简单的也叫做八数码问题,就是解决如何通过移动有限的步数,从图的一个状态移动到另外一个状态,如下面的图所示:

基于广度优先搜索的拼图游戏算法_第1张图片

2 问题解决的思路

一个简单的思路就是采取广度优先搜索算法,算法思路如下所示:

       把起始状态图存储在哈希表中;

       把起始状态加入队列当中;

       循环判断当前状态是否等于结束状态

从队列中取出元素

寻找所有可以移动的操作,产生新状态X

若新位置状态X不在哈希表中

       将X加入队列和哈希表

 

3 算法的实现

Puzzle.h

/*************************************************************************************/
/*"NITE and DAY" Puzzle                                                              */
/*************************************************************************************/

/*Special symbols.*/
#define SymbolBlank '.' /*Symbol that denotes a blank square.*/
#define SymbolFixed '$' /*Symbol that cannot move.*/

/*Board dimensiona.*/
#define BoardHeight 3
#define BoardWidth 4
#define BoardSize  12

/*Start position.*/
/* N I T E */
/* $ . $ . */
/* D A Y . */
#define StartBoard "NITE$.$.DAY."

/*Goal position.*/
/* D A Y . */
/* $ . $ . */
/* N I T E */
#define GoalBoard  "DAY.$.$.NITE"

/*Number of moves in a minimal length solution for this puzzle,*/
/*   where one move is defined as s moving a piece vertically or horizontally*/
/*   to an adjacent unoccupied square (and the SymbolFixed piece cannot move).*/
#define MinSolution 66

/*Data structure parameters.*/
#define QueueArraySize 50000  /*Queue goes from 0 to QueueArraySize-1.*/
#define HashArraySize 1000003 /*Hash table goes from 0 to HashArraySize-1.*/
#define HashStatsMAX 50       /*Max number of hash bucket size statistics to keep.*/

Output.h

/*Output a position; takes 4 arguments:
   *POS:  String that lists pieces in row major order.
   step:  The step number (ignore next two args when step=0).
   piece: Piece that moved.
   dir:   Direction that the piece moved; 0=north, 1=east, 2=south, 3=west.
   */
void OutputPosition(char *POS, int step, char piece, int dir) {
static char *DirectionString[4] = {"north", "east", "south", "west"};
int i;
printf("\nStep %d",step);
if (step>0) printf(", move %c %s",piece,DirectionString[dir]);
printf(":\n");
for (i=0; i0);
sprintf(s,"%d",block[i]);
if (block[i]<10) x=s+1; else if (block[i]<100) x=s+2; else x=s+3;
for (j=(i-1); j>=0; j--) {
   if (block[j]<10) sprintf(x,",00%d",block[j]);
   else if (block[j]<100) sprintf(x,",0%d",block[j]);
   else sprintf(x,",%d",block[j]);
   x += 4;
   }
sprintf(x,"%c",'\0');
return(s);
}


/*Print queue statistics.*/
void QueueStats(int Qarray, int Qmax, int Qfront, int Qrear) {
printf("\nQueue statistics:\n");
printf("   Queue array size: %s\n", MakeString(Qarray));
printf("   Max positions ever in queue: %s\n", MakeString(Qmax));
printf("   Index of Qfront: %s\n", MakeString(Qfront));
printf("   Index of Qrear: %s\n", MakeString(Qrear));
printf("   |Qfront-Qrear| = %s\n", MakeString(abs(Qfront-Qrear)));
}


/*Print hash statistics.*/
void HashStats(int Harray, int Hcount, int Hmax) {
printf("\nHash table statistics:\n");
printf("   Hash array size: %s\n", MakeString(Harray));
printf("   No. positions in hash table: %s\n", MakeString(Hcount));
printf("   Max bucket size: %s\n", MakeString(Hmax));
printf("\nHash buckets at the end:\n");
}


/*Print number of buckets of given size.*/
/*Call this function once for each bucket size, startiing at 0 and going*/
/*   up to the maximum bucket size (the Hmax argument to HashStats).*/
void BucketStat(int Bsize, int Bcount) {
printf("   buckets of size %d = %s\n", Bsize, MakeString(Bcount));
}

main.c

#include 
#include 
#include "Puzzle.h"
#include "Output.h"

/*Position data type.*/
typedef struct pnode
{
	struct pnode *next; /*next pointer for the hash bucket*/
	struct pnode *back; /*position from which this one came*/
	int cost;			/*number of moves to get to this position*/
	char piece;			/*piece that moved to this position*/
	int dir;			/*direction moved to enter this position*/
	char board[BoardSize];	/*this position's board*/
} PositionBody;
typedef PositionBody *TypePosition;

/*Queue data type.*/
typedef struct Queue_
{
	unsigned int head;
	unsigned int tail;
	TypePosition arr[QueueArraySize];
}Queue;
typedef Queue* TypeQueue;

typedef struct List_
{
	TypePosition pos;
	struct List_* next;
}List;

enum Move { NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3 };

void initQueue();
void initHash();
void initStartPos();
void enqueue(TypePosition pos);
void runGame();
TypePosition dequeue();
TypePosition newPosition();
List* newList();
unsigned int hash(char* str);
void copyStr(char* str1, char* str2);
int cmpStr(char* str1, char* str2);
TypePosition insertPos(char* str, char piece, int direct, int cost, TypePosition back);
void printResult(TypePosition p);
void statisticQueue();
void statisticHash(char* str);
void clearHash();

List hashTable[HashArraySize];
Queue ques;
char start[BoardSize] = StartBoard;
char goal[BoardSize] = GoalBoard;
unsigned int maxQueueNum = 0;
unsigned int QueueNum = 0;
unsigned int hashNum[HashArraySize];
unsigned int maxBucketSize;

int main() {
	initHash();
	initQueue();
	initStartPos();
	runGame();
	clearHash();
	//system("pause");
	return 0;
}


void initQueue()
{
	ques.head = 0;
	ques.tail = 0;
}

void initHash()
{
	for (int i = 0; i < HashArraySize; i++)
	{
		hashTable[i].pos = NULL;
		hashTable[i].next = NULL;
	}
}

void initStartPos()
{
	TypePosition startPos = insertPos(start, 0, 0, -1, NULL);
	enqueue(startPos);
}

void enqueue(TypePosition pos)
{
	if (pos == NULL)
		return;
	unsigned int newTail = (ques.tail + 1) % QueueArraySize;
	if (newTail == ques.head)
	{
		printf("queue overflow!!!");
		clearHash();
		exit(1);
	}
	else
	{
		ques.arr[ques.tail] = pos;
		ques.tail = newTail;
		QueueNum++;
		if (maxQueueNum < QueueNum)
			maxQueueNum = QueueNum;
	}
}

void runGame()
{
	while (1)
	{
		TypePosition p = dequeue();
		if (p == NULL)
			return;
		if (cmpStr(goal, p->board))
		{
			//over;
			printResult(p);
			statisticQueue();
			statisticHash(p->board);
			return;
		}

		for (int i = 0; i < BoardSize; i++)
		{
			int x = i % BoardWidth;
			int y = i / BoardWidth;
			int dx[4] = { 0,1,0,-1 };
			int dy[4] = { -1,0,1,0 };
			for (int k = 0; k < 4; k++)
			{
				int newx = x + dx[k];
				int newy = y + dy[k];
				if (newx >= 0 && newx < BoardWidth && newy >= 0 && newy < BoardHeight && p->board[i] != SymbolFixed && p->board[i] != SymbolBlank)
				{
					int newIndex = newy * BoardWidth + newx;
					if (p->board[newIndex] == SymbolBlank)
					{
						char newCs[BoardSize] = { 0 };
						copyStr(newCs, p->board);
						newCs[i] = SymbolBlank;
						newCs[newIndex] = p->board[i];
						TypePosition next = insertPos(newCs, p->board[i], k, p->cost, p);
						enqueue(next);
					}
				}
			}
		}
	}
}

TypePosition dequeue()
{
	//judge if empty queue
	if (ques.head == ques.tail)
		return NULL;

	//return the head element
	unsigned int head = ques.head;
	ques.head = (ques.head + 1) % QueueArraySize;
	QueueNum--;
	return ques.arr[head];
}

unsigned int hash(char* str)
{
	unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
	unsigned int hash = 0;
	unsigned int cnt = BoardSize;
	while (cnt--)
	{
		hash = hash * seed + (*str++);
	}
	return (hash & 0x7FFFFFFF) % HashArraySize;
}

TypePosition insertPos(char* str, char piece, int direct, int cost, TypePosition back)
{
	
	unsigned int key = hash(str);
	//the first element in key's bucket
	if (hashTable[key].pos == NULL)
	{
		TypePosition ppos = newPosition();
		ppos->back = back;
		ppos->next = NULL;
		ppos->cost = cost + 1;
		ppos->dir = direct;
		ppos->piece = piece;
		copyStr(ppos->board, str);
		hashTable[key].pos = ppos;
		hashNum[key]++;
		return ppos;
	}

	//the key's bucket is not empty
	List* p = hashTable + key;
	List* q = p;
	while (p != NULL)
	{
		if (cmpStr(p->pos->board, str))
			return NULL;
		q = p;
		p = p->next;
	}
	TypePosition ppos = newPosition();
	ppos->back = back;
	ppos->next = NULL;
	ppos->cost = cost + 1;
	ppos->dir = direct;
	ppos->piece = piece;
	copyStr(ppos->board, str);
	List* newNode = newList();
	newNode->pos = ppos;
	newNode->next = NULL;
	q->next = newNode;

	//increase the key's biggest bucket size
	hashNum[key]++;
	if (maxBucketSize < hashNum[key])
		maxBucketSize = hashNum[key];
	return ppos;
}

void printResult(TypePosition p)
{
	if (p->cost == 0)
	{
		OutputPosition(p->board, p->cost, p->piece, p->dir);
		return;
	}
	else if (p->cost > 0)
		printResult(p->back);
	OutputPosition(p->board, p->cost, p->piece, p->dir);

}

void copyStr(char* str1, char* str2)
{
	for (int i = 0; i < BoardSize; i++)
	{
		str1[i] = str2[i];
	}
}

TypePosition newPosition() {
	TypePosition p = (TypePosition)malloc(sizeof(PositionBody));
	if (p == NULL) {
		printf("Malloc for a new position failed.");
		clearHash();
		exit(1);
	}
	return p;
}

List* newList()
{
	List* p = (List*)malloc(sizeof(List));
	if (p == NULL) {
		printf("Malloc for a new List node failed.");
		clearHash();
		exit(1);
	}
	return p;
}

int cmpStr(char* str1, char* str2)
{
	for (int i = 0; i < BoardSize; i++)
	{
		if (str1[i] != str2[i])
			return 0;
	}
	return 1;
}

void statisticQueue()
{
	QueueStats(QueueArraySize, maxQueueNum, ques.head, ques.tail);
}
void statisticHash(char* str) {
	unsigned int key = hash(str);
	HashStats(HashArraySize, key, maxBucketSize);

	//int* arrSize = (int*)malloc(sizeof(int) * (maxBucketSize + 1));
	int* arrSize = (int*)calloc(maxBucketSize + 1, sizeof(int));
	for (unsigned int i = 0; i < HashArraySize; i++)
	{
		arrSize[hashNum[i]]++;
	}

	for (int i = 0; i <= maxBucketSize; i++)
	{
		BucketStat(i, arrSize[i]);
	}
	free(arrSize);

}

void clearHash()
{
	for (int i = 0; i < HashArraySize; i++)
	{
		if (hashTable[i].pos != NULL)
		{
			List* node = hashTable + i;
			free(node->pos);
			node = node->next;
			while (node != NULL)
			{
				List* p = node->next;
				free(node->pos);
				free(node);
				node = p;
			}
		}

	}
}


4 输出

Step 0:
N I T E
$ . $ .
D A Y .


Step 1, move I south:
N . T E
$ I $ .
D A Y .


Step 2, move N east:
. N T E
$ I $ .
D A Y .


Step 3, move Y east:
. N T E
$ I $ .
D A . Y

......

Step 63, move E south:
D A Y .
$ . $ E
N . I T


Step 64, move I west:
D A Y .
$ . $ E
N I . T


Step 65, move T west:
D A Y .
$ . $ E
N I T .


Step 66, move E south:
D A Y .
$ . $ .
N I T E


Queue statistics:
   Queue array size: 50,000
   Max positions ever in queue: 35,919
   Index of Qfront: 4,797
   Index of Qrear: 4,800
   |Qfront-Qrear| = 3


Hash table statistics:
   Hash array size: 1,000,003
   No. positions in hash table: 652,885
   Max bucket size: 7


Hash buckets at the end:
   buckets of size 0 = 546,392
   buckets of size 1 = 330,171
   buckets of size 2 = 99,613
   buckets of size 3 = 20,372
   buckets of size 4 = 3,030
   buckets of size 5 = 387
   buckets of size 6 = 34
   buckets of size 7 = 4








你可能感兴趣的:(算法)