C++:【练习题】Project-1 The robots in a warehouse

Project-1 The robots in a warehouse

工程题目:

In a modernized warehouse, robots are used to fetch the goods. Careful planning is needed to ensure that the robots reach their destinations without crashing into each other. Of course, all warehouses are rectangular, and all robots occupy a circular floor space with a diameter of 1 meter. Assume there are N robots, numbered from 1 through N. You will get to know the position and orientation of each robot, and all the instructions, which are carefully (and mindlessly) followed by the robots. Instructions are processed in the order they come. No two robots move simultaneously; a robot always completes its move before the next one starts moving.

A robot crashes with a wall if it attempts to move outside the area of the warehouse, and two robots crash with each other if they ever try to occupy the same spot.

The first line of input is K, the number of test cases. Each test case starts with one line consisting of two integers, 1 <= A, B <= 100, giving the size of the warehouse in meters. A is the length in the EW-direction, and B in the NS-direction.

The second line contains two integers, 1 <= N, M <= 100, denoting the numbers of robots and instructions respectively.

Then follow N lines with two integers, 1 <= Xi <= A, 1 <= Yi <= B and one letter (N, S, E or W), giving the starting position and direction of each robot, in order from 1 through N. No two robots start at the same position.

Finally there are M lines, giving the instructions in sequential order.

An instruction has the following format:

< robot #> < action> < repeat> 

Where is one of

  • L: turn left 90 degrees,
  • R: turn right 90 degrees, or
  • F: move forward one meter,

and 1 <= < repeat> <= 100 is the number of times the robot should perform this single move.

Output one line for each test case:

  • Robot i crashes into the wall, if robot i crashes into a wall. (A robot crashes into a wall if Xi = 0, Xi = A + 1, Yi = 0 or Yi = B + 1.)
  • Robot i crashes into robot j, if robots i and j crash, and i is the moving robot.
  • OK, if no crashing occurs.

Only the first crash is to be reported.

Define two classes at least:

  • Class Warehouse
  • Class Robot

解题思路:

  • 构建Robot类,设置三个成员变量direction,location_a,location_b存储方向和坐标信息

  • 将位置坐标’N’,‘E’,‘S’,'W’四个方向分别抽象成数字0-3,有:

    右旋操作可以抽象成:(direction + 4 - 1)% 4

    左旋操作可以抽象成:(direction + 1)% 4

    重复操作抽象成:(repeated times)% 4

  • 构建Warehouse类,设置成员变量:

    Robot * first:指向Robot数组对象的第一个Robot对象

    robot_num:Robot总个数

    len_A:长度

    len_B:宽度

    state:Warehouse状态,0表示OK,1表示Crash Wall,2表示Crash Robot

    crash_robot[]:存储发生碰撞的Robot对象的编号

  • 编写异常检查函数:

    CheckPostiveInteger()函数判断是否输入为正整数

    CheckDirection()函数判断输入的字符是否是’N’ ‘E’ ‘S’ 'W’之一

    CheckInstruction()函数判断输入指令是否是’F’ ‘R’ 'L’之一

  • 主函数内:

    创建Warehouse指针访问Warehouse数组对象进行测试

    利用double,char[]对用户输入信息进行缓存并利用异常检查函数判断类型格式是否满足题目要求(1-100的整数和单个字符的指令)

    如果程序运行错误,利用try-catch语句捕获错误信息内容并输出

    如果程序运行成功,重载cout函数调用Warehouse对象内的ShowMessage成员函数展示Warehouse的状态

创新点:

  1. 根据成员函数大小适当使用在类内和类外进行定义,提高编译效率。

  2. 将机器人方向抽象数字化,采用取模法化简对方向的多次重复操作。

  3. 编写一系列函数判断不规范输入,其中包括:

    检查输入的数字类型是否为题目规定范围内的正整数类型。

    检查输入的字符类型是否为单个字符且满足题目规定的字符类型。

  4. 利用try-catch结构对输入类型的异常进行捕获并输入提示,错误效果图如下:

    1.3
    
    ERROR :
      You should input a suitable Postive Integer!
    
    D:\Private\Files\C++\高级语言程序设计2-2\ConsoleApplication4\Debug\ConsoleApplication4.exe (进程 22920)已退出,代码为 1 。
    按任意键关闭此窗口. . .
    
  5. 对ostream类的cout进行运算符重载输出Warehouse类的状态信息。

代码及注释:

#include<iostream>
using namespace std;

/* 
定义Robot类
成员函数代码均很短,故采用类内方式定义
*/
class Robot {
public:
	friend class Warehouse;
	Robot() {
		direction = 1;
		location_A = 1;
		location_B = 1;
	}

	// 设置Robot对象的方向
	// parameter(s): dir - direction 'N' , 'W' , 'S' or 'E'
	void SetDirection(char dir) {
		if (dir == 'N')direction = 0;
		if (dir == 'E')direction = 1;
		if (dir == 'S')direction = 2;
		if (dir == 'W')direction = 3;
	}

	// 获取重复命令'L'或'R'改变Robot对象的方向(函数重载)
	// parameter(s): dir - instruction 'L' or 'R'
	//               len - repeated times
	void SetDirection(char dir, int len) {
		int op;
		if (dir == 'L')op = -1;
		else op = 1;
		direction = (direction + 4 + op * (len % 4)) % 4;
	}

	// 设置Robot对象的位置
	// parameter(s): a - location A
	//               b - location B
	void SetLocation(int a, int b) {
		location_A = a;
		location_B = b;
	}

	// 对Robot进行前进操作
	// parameter(s): len - length of movement
	void Move(int len) {
		if (direction == 0)location_B += len;
		else if (direction == 1)location_A += len;
		else if (direction == 2)location_B -= len;
		else if (direction == 3)location_A -= len;
	}

private:
	// Robot的方向(四个方位进行数字化编号存储)
	// define: N=0 E=1 S=2 W=3
	int direction;

	// Robot的位置坐标
	int location_A;
	int location_B;
};

// 定义Warehouse类
class Warehouse {
public:
	// 利用指针获取Robot对象数组
	Robot* first;

	// 设置Warehouse的成员变量
	void SetWarehouse(int a, int b, int num);

	// 设置Warehouse内的Robot数组对象的成员变量
	void SetRobot(int label, int a, int b, char dir);

	// 判断当前Robot对象是否撞到其他Robot对象,并设置Warehouse对象的状态
	bool IsCrashRobot(int label, int a, int b, int num);

	// 判断当前Robot对象是否撞到墙,并设置Warehouse对象的状态
	bool IsCrashWall(int label, int a, int b);

	// 对给定指令的Robot对象进行'F' 'L' 'R'的重复操作
	void MoveRobot(int label, char dir, int len);

	// 显示最后的状态结果
	void ShowMessage(ostream& out) const;
private:
	// Warehouse的长度和宽度
	int len_A;
	int len_B;

	// Robot对象的总数
	int robot_num;

	// Warehouse的状态
	// define: 0-ok   1-crash wall   2-crash robot
	int state;

	// 存储发生碰撞的Robot对象的编号
	int crash_robot[2];
};

/*
设置Warehouse的成员变量
parameter(s): a - length of warehouse
              b - width of warehouse
	          num - total number of robots in this warehouse
*/
void Warehouse::SetWarehouse(int a, int b, int num) {
	Robot* p = new Robot[num];
	first = p;
	robot_num = num;
	len_A = a;
	len_B = b;
	state = 0;
}

/* 
设置Warehouse内的Robot数组对象的成员变量
    parameter(s): label - label of robots in this warehouse
	              a - robot's location A
	              b - robot's location B
*/
void Warehouse::SetRobot(int label, int a, int b, char dir) {
	first[label - 1].SetLocation(a, b);
	first[label - 1].SetDirection(dir);
	IsCrashWall(label, a, b);
	IsCrashRobot(label, a, b, label - 1);
}

/*
判断当前Robot对象是否撞到其他Robot对象,并设置Warehouse对象的状态
	parameter(s): label - label of robot that may crashes other robot
	              a - robot's location A
	              b - robot's location B
	              num - check length
*/
bool Warehouse::IsCrashRobot(int label, int a, int b, int num) {
	for (int i = 0; i < num; i++) {
		if (first[i].location_A == a && first[i].location_B == b) {
			if (state == 0) {
				state = 2;
				crash_robot[0] = label;
				crash_robot[1] = i + 1;
			}
			return true;
		}
	}
	return false;
}

/*
判断当前Robot对象是否撞到墙,并设置Warehouse对象的状态
	parameter(s): label - label of robot that may crashes other robot
                  a - robot's location A
	              b - robot's location B
*/
bool Warehouse::IsCrashWall(int label, int a, int b) {
	if (a<0 || b<0 || a>len_A || b>len_B) {
		if (state == 0) {
			state = 1;
			crash_robot[0] = label;
		}
		return true;
	}
	return false;
}

/*
对给定指令的Robot对象进行'F' 'L' 'R'的重复操作
	parameter(s): label - label of robot that need to move
	              dir - instruction 'F' , 'L' or 'R'
	              len - repeated times
*/
void Warehouse::MoveRobot(int label, char dir, int len) {
	if (dir == 'F') {
		int dir = first[label - 1].direction;
		for (int i = 0; i < len; i++) {
			first[label - 1].Move(1);
			int a = first[label - 1].location_A;
			int b = first[label - 1].location_B;
			first[label - 1].Move(-1);
			if (IsCrashRobot(label, a, b, robot_num) || IsCrashWall(label, a, b))
				break;
			first[label - 1].Move(1);
		}
	}
	else
		first[label - 1].SetDirection(dir, len);
}

// 显示最后的状态结果
void Warehouse::ShowMessage(ostream& out) const {
	if (state == 0)
		out << "OK" << endl;
	else if (state == 1)
		out << "Robot " << crash_robot[0] << " crashes into the wall" << endl;
	else if (state == 2)
		out << "Robot " << crash_robot[0] << " crashes into robot " << crash_robot[1] << endl;
}

// 运算符重载ostream类的cout,显示Warehouse最后的状态信息
ostream& operator<<(ostream& out, const Warehouse& a) {
	a.ShowMessage(out);
	return out;
}


/*
以下三个函数为异常输入检测函数,如果出现异常,则返回值为true:
    CheckPostiveInteger函数:判断是否输入为正整数
    CheckLocation函数      :判断输入的字符是否是'N' 'E' 'S' 'W'之一
    CheckInstruction函数   :判断输入指令是否是'F' 'R' 'L'之一
*/
bool CheckPostiveInteger(double n) {
	if (n < 1 || abs(round(n) - n) > 0.00000000001 || n > 100)
		return true;
	return false;
}

bool CheckDirection(char n[]) {
	if (n[0] != 'N' && n[0] != 'E' && n[0] != 'S' && n[0] != 'W')
		return true;
	return false;
}

bool CheckInstruction(char n[]) {
	if (n[0] != 'F' && n[0] != 'R' && n[0] != 'L')
		return true;
	return false;
}


int main() {
	// 利用try-catch进行异常捕获
	try {
		int test_num;

		// 检查输入是否符合规则
		double input_test_num;
		cin >> input_test_num;
		if (cin.fail() || CheckPostiveInteger(input_test_num))
			throw "You should input a suitable Postive Integer!";
		test_num = static_cast<int>(input_test_num);

		// 利用指针访问Warehouse数组对象
		Warehouse* warehouse = new Warehouse[test_num];
		for (int i = 0; i < test_num; i++) {
			int A, B, robot_num, instruction_num;

			// 检查输入是否符合规则
			double input_A, input_B, input_robot_num, input_instruction_num;
			cin >> input_A >> input_B;
			cin >> input_robot_num >> input_instruction_num;
			if (cin.fail() || CheckPostiveInteger(input_A) || CheckPostiveInteger(input_B)
				|| CheckPostiveInteger(input_robot_num) || CheckPostiveInteger(input_instruction_num))
				throw "You should input a suitable Postive Integer!";
			A = static_cast<int>(input_A);
			B = static_cast<int>(input_B);
			robot_num = static_cast<int>(input_robot_num);
			instruction_num = static_cast<int>(input_instruction_num);

			warehouse[i].SetWarehouse(A, B, robot_num);
			for (int j = 0; j < robot_num; j++) {
				int location_a, location_b;
				char dir;

				// 检查输入是否符合规则
				double input_location_a, input_location_b;
				char input_dir[2000];
				cin >> input_location_a >> input_location_b >> input_dir;
				if (cin.fail())
					throw "You should input a suitable Postive Integer!";
				if (CheckPostiveInteger(input_location_a) || CheckPostiveInteger(input_location_b))
					throw "You should input a suitable Postive Integer!";
				if (input_dir[1] || input_dir[0] == 0)
					throw "Robot's direction should be only a character!";
				if (CheckDirection(input_dir))
					throw "Robot's direction should be one of the four characters 'N' 'E' 'W' 'S'!";
				location_a = static_cast<int>(input_location_a);
				location_b = static_cast<int>(input_location_b);
				dir = input_dir[0];

				warehouse[i].SetRobot(j + 1, location_a, location_b, dir);
			}
			for (int j = 0; j < instruction_num; j++) {
				int label, len;
				char dir;

				// 检查输入是否符合规则
				double input_label, input_len;
				char input_dir[2000];
				cin >> input_label >> input_dir >> input_len;
				if (cin.fail())
					throw "You should input a suitable Postive Integer!";
				if (CheckPostiveInteger(input_label) || CheckPostiveInteger(input_len))
					throw "You should input a suitable Postive Integer!";
				if (input_dir[1] || input_dir[0] == 0)
					throw "Robot's instruction should be only a character!";
				if (CheckInstruction(input_dir))
					throw "Robot's instruction should be one of the three characters 'F' 'R' 'L'!";
				label = static_cast<int>(input_label);
				len = static_cast<int>(input_len);
				dir = input_dir[0];

				warehouse[i].MoveRobot(label, dir, len);
			}
		}
		for (int i = 0; i < test_num; i++) {
			cout << warehouse[i];
		}
	}
	catch (char* str) {
		// 输出错误信息
		cout << endl << "ERROR :" << endl;
		cout << "  " << str << endl << endl;
		return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}

程序运行结果分析:

对实验数据进行检测,结果如下:

4
5 4
2 2
1 1 E
5 4 W
1 F 7
2 F 7
5 4
2 4
1 1 E
5 4 W
1 F 3
2 F 1
1 L 1
1 F 3
5 4
2 2
1 1 E
5 4 W
1 L 96
1 F 2
5 4
2 3
1 1 E
5 4 W
1 F 4
1 L 1
1 F 20
Robot 1 crashes into the wall
Robot 1 crashes into robot 2
OK
Robot 1 crashes into robot 2

按任意键关闭此窗口. . .

验证可得程序执行正确。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值