[简 介]贪吃蛇又名贪食蛇,是一款经典的小游戏。玩家使用方向键操控一条长长的蛇不断吞下豆子,同时蛇身随着吞下的豆子不断变长,当蛇头撞到蛇身或障壁时游戏 结束。贪吃蛇最初为人们所知的是诺基亚手机附带的一个小游戏,它伴随着诺基亚手机走向世界。现在的贪吃蛇出现了许多衍生版本,并被移植到各种平台上。
[历史] 1976年,Gremlin平台推出了一款经典街机游戏Blockade。游戏中,两名玩家分别控制一个角色在屏幕上移动,所经之处砌起围栏。角色只能向左、右方向90度转弯,游戏目标保证让对方先撞上屏幕或围栏。听起来有点复杂,其实就是下面这个样子:基本上就是两条每走一步都会长大的贪吃蛇比谁后完蛋,玩家要做的就是避免撞上障碍物和越来越长的身体。更多照片、视频可以看 GamesDBase 的介绍。Blockade 很受欢迎,类似的游戏先后出现在 Atari 2600、TRS-80、苹果 2 等早期游戏机、计算机上。但真正让这种游戏形式红遍全球的还是21年后随诺基亚手机走向世界的贪吃蛇游戏——Snake。
Source: ZOJ 1056(浙江大学Online Judge)
Problem——整个游戏棋盘是50*50大小的,左上角在(1,1),贪吃蛇由20个节点组成,头部位置在(25,30),水平延展到(25,11),可以有四个运动方向:东,西,南,北。题目就是给你一个运动序列,判断最终结果是下面3种情况的哪一种:1)正常。2)头撞到自己身体。3)出界。
Input——有很多实例,输入的每个问题实例有两行。第一行是一个整数(n<100),表示下一行中移动的步数。(n=0表示输入结束)下一行包含n个字符(向东,西,南或北),表示移动的序列,字母之间没有空格。
Output——对每个问题实例输出一行,输出格式应该是下面三种情况之一:
(1) 蠕虫在第m步撞到了自己。
(2) 蠕虫在第m步爬出了游戏界面。
(3) 蠕虫成功完成了m步移动。
其中的m需要用计算机判定,第1个移动是move 1.
什么是程序?程序=数据结构+算法,其中,数据结构应该说是骨架,而算法则是一个程序的灵魂所在,在这里,我将这个小程序拆成蠕虫的数据结构+蠕虫移动以及判定的模拟算法。
(1)蠕虫的数据结构:
蠕虫一系列移动的步数: int n;
蠕虫移动的方向: char data[MAXN];(这里的MAXN是一个常量,开一个比最大步数多5左右的就比较合适了)
蠕虫实际移动的步数:int step(这里就是Output中的m)
蠕虫的头部和尾部的坐标:(结构体)
Struct location
{
Int x,y;
}head,nail;
游戏界面:(用二维数组来实现)
bool board[Row][Column];//蠕虫占据的位置设置为true,否则设置为false。这里用布尔逻辑来定义游戏界面是否占据位置,是一个很形象的做法!这是由于“棋盘”上的每一个“棋子”都是相同“属性”的,所以,我们可以这样做。
(2)模拟蠕虫在屏幕上的游动:
分三步进行:
第一步:初始化。
将蠕虫在界面中所占据的位置,记录到数组board中;蠕虫的头(25,30)尾(25,11)坐标。
第二步:移动头部。
根据给定的移动方向,确定头部移动的坐标,如果(A)头部移动到界面的外面或者(B)移动到其身体的内部,则结束模拟,输出相应的标志信息。
第三步:移动尾部。
注意:蠕虫尾部的移动和头部的移动,其规则是不一样的,比如说:游戏刚开始时,不管头部怎么移动,其尾部总是跟随身体向右平移(这个属于初始状态),直到蠕虫的尾部移到蠕虫头部刚开始时的移动一致,这一步乃是本问题编程的关键!
#include<stdio.h>
#include<memory.h>//这是为了开启memset,进行初始化
//定义贪吃蛇操作的上限
#define MAXN 100 //假设最多有100个操作
//定义整个棋盘[0,51],注意了,是闭区间!
#define Row 51
#define Column 51
//看整个蛇的时候,由于每次移动只有头部和尾部在变化,所以,可以只关心首尾
struct location
{
int x,y;
}head,nail;
//在这一步,蛇会遇到“挫折”
int step;
//吴昊注释:这里的坐标和我们的坐标不同,x和y是倒转的
int moves(int n,char data[])
{
bool board[Row][Column];
//最初的时候,棋盘空无一物
memset(board,false,sizeof(board));
int i;
for(i=11;i<=30;i++)
board[25][i]=true;
head.x=25;
head.y=30;
nail.x=25;
nail.y=11;
for(step=0;step<n;step++)
{
switch(data[step])
{
case 'N':
head.x--;break;//break跳出,for循环自增
case 'W':
head.y--;break;
case 'E':
head.y++;break;
case 'S':
head.x++;break;
}
if(head.x>=Row||head.x<=0||head.y>=Column||head.y<=0)
return 2;
board[nail.x][nail.y]=false;
//如果该位置已经被占据,就return 3,经过观察可以得到,唯有头结点
//可能自己碰自己
if(board[head.x][head.y]) return 3;
else board[head.x][head.y]=true;
//见分析,在小于19以前,尾端的移动都是一致的
if(step<19) nail.y++;
else
{
//超过19之后,可以说,后者比前者慢19拍
switch(data[step-19])
{
case 'N':
nail.x--;break;
case 'W':
nail.y--;break;
case 'E':
nail.y++;break;
case 'S':
nail.x++;break;
}
}
}
return 1;
}
int main()
{
char data[MAXN]; //读入整个操作数据
int n;
while(scanf("%d",&n)==1&&n) //n为0时就退出
{
scanf("%s",data);
switch(moves(n,data))
{
case 1:
printf("The worm successfully made all %d moves.\n",n);
break;
//这里注意应该是(step+1)!
case 2:
printf("The worm ran off the board on move %d.\n",step+1);
break;
case 3:
printf("The worm ran into itself on move %d.\n",step+1);
break;
}
}
return 0;
}