一个走迷宫的小程序(C)

C.第三版

 

在第二版改进“聪明地”在一定范围内直接查找“直达”路径基础上,第三版解决了“原地”打转的问题:

如果起始点不在迷宫边缘,而是在中央某处,就会有可能出现这种情况:围着某一点转圈。怎么解决呢?我在每次开始走下一步之前看从前的路径里是否重复了,如果是就说明我在打转了,这时候,我为了能够跳出圈子,改变前进方向:方向加一。

1。所谓路径重复,有两点必须一样,首先,前进方向一致,其次,位置一致。因为虽然走过同一点并不说明重复,只有在同一点的前进方向也一样才是重复转圈。所以,分两步,先找同样方向,再找同样位置。

bool foundRepeat(Pos * pos)
{
	bool checkNext(int &start, Direction dir);
	bool samePos(int);
	int start = counter -1;
	int first = start;
	while (checkNext(start, trace[counter - 1]))   //这里是在找所有方向与当前(counter - 1才是当前位置)。一致的点
	{
		if (samePos(start))     //检验是否为同一点
		{		
			return true;
		}
	}
	return false;
}
 
//同一个点当然是转了一圈又恢复原状。
bool samePos(int start)
{
	int row=0, col=0;
	for (int i= start; i<counter - 1; i++)  //circle before current cursor "counter" (counter - 1才是当前位置) 
	{
		switch(trace[i])
		{
		case Right:
			col++;
			break;
		case Left:
			col--;
			break;
		case Up:
			row--;
			break;
		case Down:
			row++;
			break;
		}
	}
	return (row==0&&col==0);   
}
2。如果发现转圈就想办法跳出来。
(direction==Down)?(direction = Right):(direction=(Direction)(direction +1));
在进行通常的方向确定之前,作方向的调整才是正确的步骤,这个次序决不能错,这也是害我debug好辛苦的一点。
	if (foundRepeat(pos))  //if circling, then think to make a change
		{
			(direction==Down)?(direction = Right):(direction=(Direction)(direction +1));
		}	
		direction = findDirection(pos, direction);
		
bool mazeTraverse(Pos* pos, Direction& direction)
{
		
	void progress(Pos* pos, Direction& direction);

	if (checkGoal(pos))
		return true;
	else
	{	
		if (foundRepeat(pos))  //if circling, then think to make a change
		{
			(direction==Down)?(direction = Right):(direction=(Direction)(direction +1));

		}	
		direction = findDirection(pos, direction);
		
		progress(pos, direction);
		return mazeTraverse(pos, direction);
	}
}
3。同时为使起始点处在中央需要把初始化函数作修改。
先找结束的点,因为出口可能只有一个啊,那就是结束的点,起始点在最后确认。
 
void initialize(char * fileName, bool randomStart)
{
	bool isGate(int, int);


	bool endFound= false;
	FILE *stream;
	stream = fopen(fileName, "r");
	if (!stream)
		cout<<"wrong!\n";

	for (int row=0; row< Dimension; row++)
	{
		for (int col=0; col< Dimension; col ++)
		{
			char ch;
			ch = fgetc(stream);
			while (ch == 10 || ch == 32)
				ch = fgetc(stream);
			Maze[row][col] = (bool)(atoi(&ch));

			if (!Maze[row][col]&&isGate(row, col))
			{
				if (!endFound)
				{
					End.row = row;
					End.col = col;
					endFound = true;
				}
				else
				{
					Start.row = row;
					Start.col = col;
				}		

			}
		}
	}

	if (!endFound)     //如果连一个出口都没有是不对的。
	{
		cout<<"but there is no exit!";
		exit(1);
	}

	if (randomStart)     //看起始点的位置旗标决定在中间随机确定。
	{
		do
		{
			Start.row = rand() % Dimension;
			Start.col = rand() % Dimension;
		}
		while (Maze[Start.row][Start.col]);
	}
}
以下为第三版全部代码。
#include <iostream>
#include <ctime>
#include <cstdlib>

using namespace std;

const int Dimension = 10;
const int Chance =4;
const int SearchRange = 4;
const int CheckLength = 10;
enum Direction
{ Right, Up, Left, Down };

struct Pos
{
	int row;
	int col;
};

char * str[]={"Right", "Up", "Left", "Down"};

Pos Start;
Pos End;
Direction findDirection(Pos* pos, Direction direction);

bool foundRepeat(Pos * pos);

void writeRecord(Direction direction);

bool Maze[Dimension][Dimension] = {false};

void display();

Direction initializeDir();

Direction trace[100];
int counter =0;

bool mazeTraverse(Pos* pos, Direction& direction);

void progress(Pos* pos, Direction& direction);

bool checkGoal(Pos* pos);

void initialize(char * fileName, bool randomStart= false);

int main()
{
	Direction  dir;

	srand(time(0));
	initialize("data.txt", true);
	display();
	cout<<"\nStart gate is["<<Start.row<<"]["<<Start.col<<"]"<<endl;
	cout<<"End gate is["<<End.row<<"]["<<End.col<<"]"<<endl;
	Pos* pos = new Pos;
	pos->row = Start.row;
	pos->col = Start.col;

	dir = initializeDir();
	
	if (mazeTraverse(pos, dir))
	{
		for (int i=0; i<counter;i++)
			cout<<str[trace[i]]<<(((i+1)%6==0)?"\n":"\t");
		cout<<endl;
	}
	else
		cout<<"\nfail\n";

	return 0;
}


bool isGate(int row, int col)
{
	return (row==0||row==Dimension -1||col==0||col==Dimension -1);
}


void initialize(char * fileName, bool randomStart)
{
	bool isGate(int, int);


	bool endFound= false;
	FILE *stream;
	stream = fopen(fileName, "r");
	if (!stream)
		cout<<"wrong!\n";

	for (int row=0; row< Dimension; row++)
	{
		for (int col=0; col< Dimension; col ++)
		{
			char ch;
			ch = fgetc(stream);
			while (ch == 10 || ch == 32)
				ch = fgetc(stream);
			Maze[row][col] = (bool)(atoi(&ch));

			if (!Maze[row][col]&&isGate(row, col))
			{
				if (!endFound)
				{
					End.row = row;
					End.col = col;
					endFound = true;
				}
				else
				{
					Start.row = row;
					Start.col = col;
				}		

			}
		}
	}

	if (!endFound)
	{
		cout<<"but there is no exit!";
		exit(1);
	}

	if (randomStart)
	{
		do
		{
			Start.row = rand() % Dimension;
			Start.col = rand() % Dimension;
		}
		while (Maze[Start.row][Start.col]);
	}
}


Direction nextDirection(Direction direction)
{
	if (direction==Down)
		return Right;
	else
		return (Direction)(direction + 1);
}

bool testPos(int row, int col)
{
	if (row>Dimension-1||row<0||col>Dimension-1||col<0)
		return false;
	return  !Maze[row][col];
}

bool checkGoal(Pos* pos)
{
	bool shortCut(Pos* pos);
	void goShortCut(Pos* pos);
	
	
	if (shortCut(pos))
	{		
		goShortCut(pos);
		return true;
	}
	return (pos->row == End.row && pos->col == End.col);
}

void goShortCut(Pos* pos)
{
	int row, col, i=0;
	int rowOffSet=0, colOffSet = 0;
	Direction dir;

	row = pos->row;
	col = pos->col;
	while (i < SearchRange)
	{
		//you have to go step by step, otherwise diagonal is illegal
		if (row != End.row)
		{
			rowOffSet = (pos->row > End.row ?-1: 1);  //-1means up
			row += rowOffSet;
			if (rowOffSet !=0)
			{
				dir = (Direction)(rowOffSet < 0?1:3);
				writeRecord(dir);
			}
		}
		if (col != End.col)
		{
			colOffSet = (pos->col >End.col? -1: 1); //-1 means left
			col += colOffSet;
			if (colOffSet!=0)
			{
				dir = (Direction)(colOffSet < 0? 2:0);
				writeRecord(dir);
			}
		}
		i++;
	}
}


bool shortCut(Pos* pos)
{
	int row, col, i=0;
	
	if (abs(pos->row - End.row)<SearchRange&&abs(pos->col - End.col)< SearchRange)
	{
		
		row = pos->row;
		col = pos->col;
		while (i<SearchRange)
		{
			if (row != End.row)
			{
				row += (pos->row > End.row ?-1: 1);
				if (Maze[row][col])
					return false;
			}
			if (col != End.col)
			{
				col += (pos->col >End.col? -1: 1);
				if (Maze[row][col])
					return false;
			}
			i++;
		}
		return true;
	}
	return false;
}
		

Direction initializeDir()
{
	if (Start.col == 0)
		return Right;
	if (Start.col == Dimension -1)
		return Left;
	if (Start.row == 0)
		return Down;
	if (Start.row == Dimension - 1)
		return Up;
	return Right;
}

bool testDirection(Pos* pos, Direction direction)
{
	bool testPos(int row, int col);

	int row= pos->row, col = pos->col;
	switch(direction)
	{
	case Right:
		col = pos->col + 1;			
		break;
	case Up:
		row = pos->row - 1;			
		break;
	case Left:
		col = pos->col - 1;
		break;		
	case Down:
		row = pos->row + 1;
		break;
	}
	return testPos(row, col);
}

bool checkNext(int &start, Direction dir)
{
	while ((counter - start)< CheckLength&&start>1)
	{		
		start--;
		if (trace[start]== dir)		
			return true;
	}
	return false;
}

bool samePos(int start)
{
	int row=0, col=0;
	for (int i= start; i<counter - 1; i++)  //circle before current cursor "counter"
	{
		switch(trace[i])
		{
		case Right:
			col++;
			break;
		case Left:
			col--;
			break;
		case Up:
			row--;
			break;
		case Down:
			row++;
			break;
		}
	}
	return (row==0&&col==0);
}


bool foundRepeat(Pos * pos)
{
	bool checkNext(int &start, Direction dir);
	bool samePos(int);


	int start = counter -1;
	int first = start;

	while (checkNext(start, trace[counter - 1]))   //same pos and same direction means repeat
	{
		if (samePos(start))
		{		
			return true;
		}
	}
	return false;

}


Direction prevDirection(Direction direction)
{
	if (direction == Right)
		return Down;
	return (Direction)(direction - 1);
}

Direction findDirection(Pos* pos, Direction direction)
{	
	bool testDirection(Pos* pos, Direction direction);
	Direction nextDirection(Direction direction);
	Direction prevDirection(Direction direction);

	Direction newDir;
	newDir = prevDirection(direction);
	while (!testDirection(pos, newDir))
	{
		newDir = nextDirection(newDir);
	}
	return newDir;	
}

bool mazeTraverse(Pos* pos, Direction& direction)
{
		
	void progress(Pos* pos, Direction& direction);

	if (checkGoal(pos))
		return true;
	else
	{	
		if (foundRepeat(pos))  //if circling, then think to make a change
		{
			(direction==Down)?(direction = Right):(direction=(Direction)(direction +1));

		}	
		direction = findDirection(pos, direction);
		
		progress(pos, direction);
		return mazeTraverse(pos, direction);
	}
}

void progress(Pos* pos, Direction& direction)
{
	switch(direction)
	{
	case Right:
		pos->col += 1;
		break;
	case Up:
		pos->row += -1;
		break;
	case Left:
		pos->col += -1;
		break;
	case Down:
		pos->row += 1;
		break;
	}
	writeRecord(direction);
}

void writeRecord(Direction direction)
{
	trace[counter] = direction;
	counter++;
}


void display()
{
	for (int row = 0; row< Dimension; row++)
	{
		for (int col=0; col < Dimension; col++)
		{
			cout<<Maze[row][col]<<((col==Dimension-1)?"\n":"  ");
		}
	}
}
从下图可以看到,实际在第二个Down之后就应该跳出重复圈子,也就是第二个Left应该改变,可是凑巧的是那一点正好“碰壁”,只能在一次
Left,可是下一点就会发现Left重复了要改变,于是,在Left之后改了。真好,改了就好!

 

以下为输入文件样子:

1 1 1 1 1 1 1 1 1 1
1 1 1 1 0 1 1 1 1 1
0 0 0 1 0 1 1 1 1 1
1 1 0 1 0 1 1 0 0 1
1 1 0 0 0 1 0 0 0 1
1 1 1 0 1 1 0 1 0 1
1 0 0 0 0 0 0 1 0 0
1 0 1 0 0 0 1 0 0 1
1 0 1 0 1 1 1 0 0 1
1 1 1 1 1 1 1 1 1 1

 

 

                                                                     back.gif (341 bytes)      up.gif (335 bytes)       next.gif (337 bytes)