Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5404558
  • 博文数量: 763
  • 博客积分: 12108
  • 博客等级: 上将
  • 技术积分: 15717
  • 用 户 组: 普通用户
  • 注册时间: 2007-09-28 21:21
个人简介

业精于勤,荒于嬉

文章分类

全部博文(763)

文章存档

2018年(6)

2017年(15)

2016年(2)

2015年(31)

2014年(14)

2013年(87)

2012年(75)

2011年(94)

2010年(190)

2009年(38)

2008年(183)

2007年(28)

分类: C/C++

2009-11-28 21:24:13

/****************************************************************
* 文件名 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;
}


阅读(1323) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~