算法刷题:模拟法 0201-Robocaode

前言

hello,大家好,这期文章带大家刷一个略微复杂的题目,准备好了吗?让我们开始喽。

1. 题目描述

Robocode是一个帮助你学Java的教学游戏。它是玩家编写的程序,控制坦克在战场上互相攻击。这个游戏的思想看似简单,但编写一个获胜坦克的程序还需要很多的努力。本题不要求编写一个智能坦克的程序,只要设计一个简化的Robocode 游戏引擎即可。
本题设定整个战场是120x120(像素)。每辆坦克只能沿水平和垂直方向在固定的路径上移动(在战场中的水平和垂直方向路径每10个像素1格,总共有13条垂直路径和13条水平路径坦克可以行走,如图2-1所示)。本题忽略坦克的形状和大小,对于一辆坦克,用(x,y)(x,yE[0,120])表示它的坐标位置,用α(αE{0,90,180,270})表示坦克面对的方向(α=0、90、180或270,分别表示坦克面向右、上、左或下)。坦克以10像素/秒的恒定速度行驶,不能跑到边界之外(当坦克冲到战场的边界上的时候,就会停止移动,保持当前所面对的方向)。坦克可以向它所面对的方向射击,无论它是在行进还是停止。射击时炮弹以20像素/秒的恒定速度射出,炮弹的大小也被忽略。炮弹在路径上遇到一辆坦克时,就会发生爆炸。如果一发以上的炮弹几乎在同一时刻命中坦克,在同一个地方发生爆炸是可能的。被击中的坦克将被销毁,并从战场上被立即移走。爆炸的炮弹或飞出边界的炮弹也将被移走。
在这里插入图片描述
输入
有若干个测试用例。对所有的测试用例,战场和路径都是一样的。每个测试用例者先量,M表示控制坦克移动的指令的0。和的N行给出每辆坦克的初始信息(在时间为0),格式如下:
Name x y a
一辆坦克的Name由不超过10个字母组成。x、y和a是整数,其中x,yE{0,10,20,120},aE{0,90,180,270},每个项之间用一个空格分开。
接下来的M行给出指令,格式如下:
Time Name Content
每个项之间用一个空格分开。按Time的升序给出所有的指令(0≤Time≤30),Time是一个正整数,表示指令发出的时间戳。Name表示接收指令的坦克。Content的类型如下:
·MOVE:当接收到这条指令时,坦克开始朝它所面向的方向移动。如果坦克已经在运动,那么这条指令无效。
·STOP:当接收到这条指令时,坦克停止移动。如果坦克已经停下,那么这条指令无效。
·TURN angle:当接收到这条指令时,坦克改变它面对的方向,从α改为((a+angle+360)mod 360),无论其是否在移动中。本题确定((α+angle+360)mod 360)e{0,90,180,270}。TURN指令不会影响坦克的移动状态。
·SHOOT:当接收到这条指令时,坦克向它所面对的方向发射一枚炮弹。
坦克一旦接收到指令,就采取相应的行动。例如,如果一辆坦克位置为(0,0),α=90,在Time 1接收到指令MOVE,它就开始移动,在Time2到达位置(0,1)。要注意的是,坦克可以在一秒内接收多条指令,一条接一条地根据指令采取相应的行动。例如,如果坦克位置为(0,0),α=90,接收到的指令序列为"TURN 90;SHOOT;TURN-90",它就转到方向a=180,发射一枚炮弹,然后再转回来。如果坦克接收到"MOVE;STOP"指令序列,它仍然待在原来的位置。
请注意一些要点:
·如果一辆坦克被击中爆炸,在那一刻,它对所有收到的指令都不会采取任何行动。当然,所有发送到已经摧毁的坦克的指令也被略去。
·虽然指令在确定的时间点发出,但是坦克和炮弹的运动和爆炸发生在连续时间段内。
·输入数据保证不会有两辆坦克在路上相撞,因此你的程序不必考虑这种情况。
·所有的输入内容都是合法的。
以N=M=0的测试用例终止输人,程序不用处理这一情况。
输出
对每个测试用例,在一行中输出赢家的名字。赢家是最后生存下来的坦克。如果在最后没有坦克了,或者有多于一辆坦克生存下来,则在一行中输出"NO WINNER!"。
在这里插入图片描述

2. 题目分析

这是一道时序模拟题,指令发出的时间范围为30秒,考虑到最后一条指令执行后可能会有变化,则可以一直模拟到45秒后。
如果位于(0,0)位置的坦克朝向(0,1)位置开炮,而位于(0,1)位置的坦克朝向(0,0)位置驶来,那么会在中间 13的位置处被击中,同时也可能在 12时间出现事故。于是解题要将时间和地图都扩大6倍,即等价于原来的图每 16秒考虑一次。
坦克和炮弹的属性有4个:位置、方向、行进(或停止)状态、移走(或未移走)状态。
我们从0时刻出发,依次处理每条指令。若当前指令的发出时间为t2,上条指令的发出时间为t1,则先依序模拟t1、2时刻的战况,然后根据指令设定受令坦克的状态:
·若为“MOVE"指令,则受令坦克进入行进状态;
·若为“STOP"指令,则受令坦克进入停止状态;
若为"SHOOT”指令且受令坦克未移走,则新增一发炮弹,除设行进状态外,其他属性如同受令坦克;
·若为"TURN angle"指令,则受令坦克的方向调整为
在这里插入图片描述

处理完所有指令后,再模拟15秒的战况,以处理最后指令的后继影响。
最后,统计生存下来的坦克数:若所有坦克移走,或有一辆以上的坦克未移走,则没有赢家,否则则为未移走的那辆坦克。

3. 代码实现

#include<iostream>
#include<map>
#include<cstdio>
#include<cstring>
#include<string.h>
#include<string>

using namespace std;

const int DirX[4] = { 10, 0, -10, 0 };
const int DirY[4] = { 0, 10, 0, -10 };

#define mp make_pair;

int N, M, Shoot;
int x[1050], y[1050], d[1050];
bool run[1050], die[1050];
string symbol[1050];

map<string, int>Name;

void Init()
{
	Name.clear();
	for (int i = 1; i <= N; i++)
	{
		cin >> symbol[i] >> x[i] >> y[i] >> d[i];
		x[i] *= 6;
		y[i] *= 6;
		d[i] /= 90;
		run[i] = false;
		die[i] = false;
		Name[symbol[i]] = i;
	}
	Shoot = N;
}

bool In(int x, int y)
{
	if (x >= 0 && x <= 6 * 120 && y >= 0 && y <= 6 * 120)
		return true;
	else
		return false;
}

void RunAll()
{
	for (int i = 1; i <= N; i++)
	{
		if (run[i] && !die[i])
		{
			if (In(x[i] + DirX[d[i]], y[i] + DirY[d[i]]))
			{
				x[i] += DirX[d[i]];
				y[i] += DirY[d[i]];
			}
			else
				run[i] = false;
		}
	}
	for (int i = N + 1; i <= Shoot; i++)
	{
		if (!die[i])
		{
			if (In(x[i] + DirX[d[i]] * 2, y[i] + DirY[d[i]] * 2))
			{
				x[i] += DirX[d[i]] * 2;
				y[i] += DirY[d[i]] * 2;
			}
			else
				die[i] = true;
		}
	}
	for (int i = 1; i <= N; i++)
	{
		if (die[i])
			continue;
		for (int j = N + 1; j <= Shoot; j++)
		if (!die[j])
		{
			if (x[i] == x[j] && y[i] == y[j])
			{
				die[j] = true;
				die[i] = true;
			}
		}
	}
}

void Slove()
{
	int now = 0;
	for (int i = 1; i <= M; i++)
	{
		int t;
		string sym, s;
		int th;
		cin >> t >> sym >> s;
		t *= 6;
		while (t > now)
		{
			RunAll();
			now++;
		}

		int symId = Name[sym];
		if (s == "MOVE")
		{
			run[symId] = true;
		}
		else if (s == "STOP")
		{
			run[symId] = false;
		}
		else if (s == "SHOOT")
		{
			if (!die[symId])
			{
				Shoot++;
				run[Shoot] = true;
				die[Shoot] = false;
				d[Shoot] = d[symId];
				x[Shoot] = x[symId];
				y[Shoot] = y[symId];
			}
		}
		else
		{
			cin >> th;
			th /= 90;
			d[symId] = (d[symId] + (th % 4) + 4) % 4;
		}
	}
	for (int i = 1; i <= 15 * 6; i++)
		RunAll();
	int cnt = 0;
	for (int i = 1; i <= N; i++)
	if (!die[i])
		cnt++;
	if (cnt != 1)
		cout << "NO WINNER!" << endl;
	else
	{
		for (int i = 1; i <= N;i++)
		if (!die[i])
			cout << symbol[i] << endl;
	}
}

int main()
{
	while (cin >> N >> M && (N || M))
	{
		Init();
		Slove();
	}
}

在这里插入图片描述

后记

好的,这期文章就分享到这里了,希望对大家有所帮助。下期再见。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lxkeepcoding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值