/****************************************************************
* 文件名 snake.cpp
* 功能描述 贪吃蛇小游戏,初级版本(V1.0.0)
* 作者 武立强
* 时间 2009-08-15
* 备注 无
* 联系方式 qq:724992537
****************************************************************/
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include <string.h>
#define ROW_Y 10 // 行数(Y坐标)
#define COL_X 15 // 列数(X坐标)
#define NUM_CAN_EAT 30 // 定义能吃的单元个数
struct stPoint
{
int X;
int Y;
};
typedef stPoint Point;
// 贪吃蛇基本单元
struct stSnake
{
int nIsSnakeHead; // 是否是蛇头
int nIsMoveFlag; // 是否是移动状态, 0 非移动状态, 1 移动状态
int nIsSnake; // 是否是蛇能吃的部分, 0表示不能吃 ,1表示能吃
int nHaveNext; // 是否有下一个点, 0 不存在, 1 存在下一个点
Point nextPoint; // 相对与当前的一个下个蛇身的坐标位置
};
typedef struct stSnake Snake;
Snake SnakeArray[ROW_Y][COL_X]; // 定义贪吃蛇数组
// 方向键枚举值
enum EKey
{
EUpKey, // 上
EDownKey, // 下
ELeftKey, // 左
ERightKey, // 右
EOtherKey // 其它按键
};
// 函数声明部分
EKey GetKeyType(void);
void PrintScreen(void);
void InitSnakeArray(void);
void GetSnakeHeadPoint(Point* _pHeadPoint);
void GetSnakeTailPoint(Point* _pTailPoint);
int GetSnakeLength(void);
int GetSnakeTailFront(Point* _pTailFrontPoint );
void SwapSnakeInfo(Point _point1, Point _point2);
int GetHeadFront(enum EKey _key, Point _pointHead, Point* _pHeadFrontPoint);
int CheckMoveSuccess(enum EKey _eKeyType, Point _pointHead);
void MoveSnake(enum EKey _keyType);
int main(int argc, char* argv[])
{
enum EKey eInputKey = EOtherKey;
InitSnakeArray(); // 初始化贪吃蛇数组
while(1)
{
system("cls");
PrintScreen();
eInputKey = GetKeyType();
// 只有上、下、左、右四种按键才会被处理
if( eInputKey != EOtherKey )
{
MoveSnake( eInputKey );
}
}
return 0;
}
/*
* 函数说明: 得到用户的按键类型
* 参数: void
* 返回值: Ekey ,代表按键的类型
* 备注: 无
*/
EKey GetKeyType(void)
{
int nInput = 0;
fflush(stdin);
clearerr(stdin);
nInput = getch();
fflush(stdin);
clearerr(stdin);
nInput = getch();
switch( nInput )
{
case 72:
{
return EUpKey;
break;
}
case 80:
{
return EDownKey;
break;
}
case 75:
{
return ELeftKey;
break;
}
case 77:
{
return ERightKey;
break;
}
default:
{
return EOtherKey;
break;
}
}
}
/*
* 函数说明: 输出屏幕显示
* 参数: void
* 返回值: void
* 备注: 无
*/
void PrintScreen(void)
{
int i = 0;
int j = 0;
for( i =0; i<ROW_Y; i++ ) // 行
{
for( j=0; j<COL_X; j++ ) // 列
{
if( 1 == SnakeArray[i][j].nIsSnake && 1 == SnakeArray[i][j].nIsSnakeHead ) // 蛇头
{
printf("H ");
}
else if( 1 == SnakeArray[i][j].nIsSnake ) // 蛇身
{
printf("s ");
}
else
{
printf("0 ");
}
}
printf("\n");
}
}
/*
* 函数说明: 初始化贪吃蛇数组
* 参数: void
* 返回值: void
* 备注: 无
*/
void InitSnakeArray(void)
{
int i = 0;
int j = 0;
for( i =0; i<ROW_Y; i++ ) // 行
{
for( j=0; j<COL_X; j++ ) // 列
{
SnakeArray[i][j].nIsSnakeHead = 0; // 非蛇头
SnakeArray[i][j].nIsMoveFlag = 0; // 非移动状态
SnakeArray[i][j].nIsSnake = 0; // 不能吃
SnakeArray[i][j].nHaveNext = 0; // 不存在下一个点的坐标
SnakeArray[i][j].nextPoint.X = 0;
SnakeArray[i][j].nextPoint.Y = 0;
}
}
// 设置蛇头
SnakeArray[0][0].nIsSnakeHead = 1;
SnakeArray[0][0].nIsMoveFlag = 1;
SnakeArray[0][0].nIsSnake = 1;
srand(time(NULL)); // 设置随机数的种子
for( i=0; i<NUM_CAN_EAT; i++ ) // 随即设置能吃的基本单元
{
int col = rand()%COL_X;
int row = rand()%ROW_Y;
SnakeArray[row][col].nIsSnake = 1;
}
}
/*
* 函数说明: 贪吃蛇移动函数
* 参数: _keyType,表示移动的方向
* 返回值: void
* 备注: 无
*/
void MoveSnake(enum EKey _keyType)
{
Point pointHead; // 蛇头位置
Point pointHeadFront; // 蛇头的前一个坐标位置(根据按不同的方向键,结果不同)
Point pointTail; // 蛇尾的位置
Point pointTailFront; // 蛇尾的前一个坐标的位置
int nSnakeLength = 0; // 蛇的长度
int nRet = 0;
// 清零处理
memset(&pointHead, 0, sizeof(Point));
memset(&pointHeadFront, 0, sizeof(Point));
memset(&pointTail, 0, sizeof(Point));
memset(&pointTailFront, 0, sizeof(Point));
GetSnakeHeadPoint( &pointHead ); // 找到蛇头
nRet = CheckMoveSuccess( _keyType, pointHead );
if( -1 == nRet ) // 撞墙了
{
printf("撞墙了!\n");
printf("游戏结束...\n");
exit(0);
}
else if( -2 == nRet )
{
printf("撞自己了!\n");
printf("游戏结束...\n");
exit(0);
}
nRet = GetHeadFront( _keyType, pointHead, &pointHeadFront );
if( 0 != nRet )
{
return ; // 已经移动到数组边界了
}
// 如果前一个单元能被吃掉, 则直接设置蛇头为前面的单元,就可以完成移动
if( 1 == SnakeArray[pointHeadFront.Y][pointHeadFront.X].nIsSnake )
{
SnakeArray[pointHead.Y][pointHead.X].nIsSnakeHead = 0;
// 设置蛇头为前面的单元为蛇头
SnakeArray[pointHeadFront.Y][pointHeadFront.X].nIsSnakeHead = 1;
SnakeArray[pointHeadFront.Y][pointHeadFront.X].nIsMoveFlag = 1;
SnakeArray[pointHeadFront.Y][pointHeadFront.X].nHaveNext = 1;
SnakeArray[pointHeadFront.Y][pointHeadFront.X].nextPoint.X = pointHead.X;
SnakeArray[pointHeadFront.Y][pointHeadFront.X].nextPoint.Y = pointHead.Y;
return ;
}
nSnakeLength = GetSnakeLength();
if( 1 == nSnakeLength )
{
SwapSnakeInfo( pointHead, pointHeadFront ); // 交换两点的结构信息(只有一个蛇头)
}
else
{
GetSnakeTailPoint( &pointTail ); // 找到蛇尾
GetSnakeTailFront( &pointTailFront ); // 找到蛇尾的前一个单元
// 第一步,断开蛇尾
SnakeArray[pointTailFront.Y][pointTailFront.X].nHaveNext = 0;
SnakeArray[pointTailFront.Y][pointTailFront.X].nextPoint.X = 0;
SnakeArray[pointTailFront.Y][pointTailFront.X].nextPoint.Y = 0;
// 第二步,将蛇尾和蛇头前面的结构信息进行交换
SwapSnakeInfo( pointTail, pointHeadFront );
// 第三步,重新设置蛇头结构
SnakeArray[pointHead.Y][pointHead.X].nIsSnakeHead = 0;
SnakeArray[pointHeadFront.Y][pointHeadFront.X].nIsSnakeHead = 1;
SnakeArray[pointHeadFront.Y][pointHeadFront.X].nHaveNext = 1;
SnakeArray[pointHeadFront.Y][pointHeadFront.X].nextPoint.X = pointHead.X;
SnakeArray[pointHeadFront.Y][pointHeadFront.X].nextPoint.Y = pointHead.Y;
}
}
/*
* 函数说明: 得到蛇头的坐标位置
* 参数: _pHeadPoint,输出参数,蛇头的坐标位置
* 返回值: void
* 备注: 无
*/
void GetSnakeHeadPoint( Point* _pHeadPoint )
{
assert( NULL != _pHeadPoint );
int i = 0;
int j = 0;
for( i =0; i<ROW_Y; i++ ) // 行
{
for( j=0; j<COL_X; j++ ) // 列
{
if( 1 == SnakeArray[i][j].nIsSnakeHead )
{
_pHeadPoint->Y = i;
_pHeadPoint->X = j;
break;
}
}
}
}
/*
* 函数说明: 得到蛇尾的坐标位置
* 参数: _pTailPoint,输出参数,蛇尾的坐标位置
* 返回值: void
* 备注: 无
*/
void GetSnakeTailPoint(Point* _pTailPoint)
{
assert( NULL != _pTailPoint );
Point tempPoint;
int nTempX = 0;
int nTempY = 0;
int i = 0;
int nSnakeLength = GetSnakeLength();
GetSnakeHeadPoint( &tempPoint );
for( i=0; i<nSnakeLength - 1; i++ )
{
// 移动向后一个坐标
nTempX = SnakeArray[tempPoint.Y][tempPoint.X].nextPoint.X;
nTempY = SnakeArray[tempPoint.Y][tempPoint.X].nextPoint.Y;
tempPoint.X = nTempX;
tempPoint.Y = nTempY;
}
_pTailPoint->X = tempPoint.X;
_pTailPoint->Y = tempPoint.Y;
}
/*
* 函数说明: 得到蛇体当前的长度
* 参数: void
* 返回值: 蛇体当前的长度
* 备注: 无
*/
int GetSnakeLength(void)
{
Point tempPoint;
int nTempX = 0;
int nTempY = 0;
int nLength = 1; // 蛇长度最小为 1
GetSnakeHeadPoint( &tempPoint );
while(1)
{
if ( 1 != SnakeArray[tempPoint.Y][tempPoint.X].nHaveNext ) // 不存在下一个坐标
{
break;
}
// 移动向后一个坐标
nTempX = SnakeArray[tempPoint.Y][tempPoint.X].nextPoint.X;
nTempY = SnakeArray[tempPoint.Y][tempPoint.X].nextPoint.Y;
tempPoint.X = nTempX;
tempPoint.Y = nTempY;
nLength ++;
}
return nLength;
}
/*
* 函数说明: 得到蛇尾的前一个坐标的位置
* 参数: _pTailFrontPoint,输出参数,表示蛇尾的前一个单元的位置
* 返回值: 0,成功
* -1,失败
* 备注: 无
*/
int GetSnakeTailFront( Point* _pTailFrontPoint )
{
assert( NULL != _pTailFrontPoint );
Point tempPoint;
int nTempX = 0;
int nTempY = 0;
int i = 0;
int nSnakeLength = GetSnakeLength();
if( 1 == nSnakeLength )
{
return (-1); // 当蛇身长度为1时,不存在蛇尾的前一个坐标的位置
}
GetSnakeHeadPoint( &tempPoint );
for( i=0; i<nSnakeLength - 2; i++ )
{
// 移动向后一个坐标
nTempX = SnakeArray[tempPoint.Y][tempPoint.X].nextPoint.X;
nTempY = SnakeArray[tempPoint.Y][tempPoint.X].nextPoint.Y;
tempPoint.X = nTempX;
tempPoint.Y = nTempY;
}
_pTailFrontPoint->X = tempPoint.X;
_pTailFrontPoint->Y = tempPoint.Y;
return 0;
}
/*
* 函数说明: 计算蛇头的下一个坐标点的位置
* 参数: _key,按键的类型
* pointHead,蛇头的坐标位置
* _pHeadFrontPoint,输出参数,蛇头的下一个坐标点的位置
* 返回值: 0,成功
* -1,失败
* 备注: 无
*/
int GetHeadFront(enum EKey _key, Point pointHead, Point* _pHeadFrontPoint)
{
assert( NULL != _pHeadFrontPoint );
switch( _key )
{
case ELeftKey:
{
_pHeadFrontPoint->X = pointHead.X - 1;
_pHeadFrontPoint->Y = pointHead.Y ;
if( _pHeadFrontPoint->X < 0 ) // 移动到区域尽头了
{
_pHeadFrontPoint->X = 0;
return (-1);
}
break;
}
case ERightKey:
{
_pHeadFrontPoint->X = pointHead.X + 1;
_pHeadFrontPoint->Y = pointHead.Y;
if( _pHeadFrontPoint->X >= COL_X ) // 移动到区域尽头了
{
_pHeadFrontPoint->X = COL_X - 1;
return (-1);
}
break;
}
case EUpKey:
{
_pHeadFrontPoint->X = pointHead.X ;
_pHeadFrontPoint->Y = pointHead.Y - 1;
if( _pHeadFrontPoint->Y < 0 ) // 移动到区域尽头了
{
_pHeadFrontPoint->Y = 0;
return (-1);
}
break;
}
case EDownKey:
{
_pHeadFrontPoint->X = pointHead.X ;
_pHeadFrontPoint->Y = pointHead.Y + 1;
if( _pHeadFrontPoint->Y >= ROW_Y ) // 移动到区域尽头了
{
_pHeadFrontPoint->Y = ROW_Y - 1;
return (-1);
}
break;
}
default:
{
printf(" press key error ....\n ");
return (-1);
break;
}
}
return 0;
}
/*
* 函数说明: 交换两个基本单元的信息
* 参数: point1,第一个坐标位置
* point2,第二个坐标位置
* 返回值: void
* 备注: 无
*/
void SwapSnakeInfo(Point _point1, Point _point2)
{
Snake tempSanke;
// 备份 point1 点的结构信息
tempSanke.nIsSnakeHead = SnakeArray[_point1.Y][_point1.X].nIsSnakeHead;
tempSanke.nIsMoveFlag = SnakeArray[_point1.Y][_point1.X].nIsMoveFlag;
tempSanke.nIsSnake = SnakeArray[_point1.Y][_point1.X].nIsSnake;
tempSanke.nHaveNext = SnakeArray[_point1.Y][_point1.X].nHaveNext;
tempSanke.nextPoint.X = SnakeArray[_point1.Y][_point1.X].nextPoint.X;
tempSanke.nextPoint.Y = SnakeArray[_point1.Y][_point1.X].nextPoint.Y;
// 设置 point1 点的结构信息
SnakeArray[_point1.Y][_point1.X].nIsSnakeHead = SnakeArray[_point2.Y][_point2.X].nIsSnakeHead;
SnakeArray[_point1.Y][_point1.X].nIsMoveFlag = SnakeArray[_point2.Y][_point2.X].nIsMoveFlag;
SnakeArray[_point1.Y][_point1.X].nIsSnake = SnakeArray[_point2.Y][_point2.X].nIsSnake;
SnakeArray[_point1.Y][_point1.X].nHaveNext = SnakeArray[_point2.Y][_point2.X].nHaveNext;
SnakeArray[_point1.Y][_point1.X].nextPoint.X = SnakeArray[_point2.Y][_point2.X].nextPoint.X;
SnakeArray[_point1.Y][_point1.X].nextPoint.Y = SnakeArray[_point2.Y][_point2.X].nextPoint.Y;
// 设置 point2 点的结构信息
SnakeArray[_point2.Y][_point2.X].nIsSnakeHead = tempSanke.nIsSnakeHead;
SnakeArray[_point2.Y][_point2.X].nIsMoveFlag = tempSanke.nIsMoveFlag;
SnakeArray[_point2.Y][_point2.X].nIsSnake = tempSanke.nIsSnake;
SnakeArray[_point2.Y][_point2.X].nHaveNext = tempSanke.nHaveNext;
SnakeArray[_point2.Y][_point2.X].nextPoint.X = tempSanke.nextPoint.X;
SnakeArray[_point2.Y][_point2.X].nextPoint.Y = tempSanke.nextPoint.Y;
}
/*
* 函数说明: 检查蛇的移动是否能成功
* 参数: _eKeyType,用户按键的方向
* _pointHead,蛇头的位置
* 返回值: 0, 成功
* -1,已经移动到数组的边界(撞墙了)
* -2,前方是自己舍身(撞到自己蛇身了)
* -3,按键参数传入错误
* 备注: 无
*/
int CheckMoveSuccess(enum EKey _eKeyType, Point _pointHead)
{
int nMoveFlag = 0;
Point nextPoint;
switch( _eKeyType )
{
case ELeftKey:
{
nextPoint.X = _pointHead.X - 1;
nextPoint.Y = _pointHead.Y ;
if( nextPoint.X < 0 ) // 移动到区域尽头了
{
nMoveFlag = -1;
}
break;
}
case ERightKey:
{
nextPoint.X = _pointHead.X + 1;
nextPoint.Y = _pointHead.Y;
if( nextPoint.X >= COL_X ) // 移动到区域尽头了
{
nMoveFlag = -1;
}
break;
}
case EUpKey:
{
nextPoint.X = _pointHead.X ;
nextPoint.Y = _pointHead.Y - 1;
if( nextPoint.Y < 0 ) // 移动到区域尽头了
{
nMoveFlag = -1;
}
break;
}
case EDownKey:
{
nextPoint.X = _pointHead.X ;
nextPoint.Y = _pointHead.Y + 1;
if( nextPoint.Y >= ROW_Y ) // 移动到区域尽头了
{
nMoveFlag = -1;
}
break;
}
default:
{
return (-3);
break;
}
}
if( 1 == SnakeArray[nextPoint.Y][nextPoint.X].nIsMoveFlag ) // 撞上自己了
{
nMoveFlag = -2;
}
return nMoveFlag;
}
|