C++贪吃蛇,源码,编译命令,环境都附上了


前言

每年学校都要求娃参加各种科技比赛,这哪里是娃参加啊,明明就是难为老爹嘛,娃才3年级,能做什么科技作品?我要是不搞点东西应付一下的话,娃他妈那关过不了,算了,花点时间写个C++的贪吃蛇吧。整体写的比较简陋,娃他妈催的很急,很多想法没来得及写,代码也没好好的整,凑合看吧。

网上也有不少C语言写的贪吃蛇的源码,代码都很精炼,我也看过,觉得写的很好,奈何做C++好多年,写代码不封装几个类出来浑身难受。代码比较简单,egg就是贪吃蛇里面那个蛋,map就是地图,player就是指蛇,director就是字面意思,控制整体流程。从我自己的角度来看,分类之后分工明确了,什么代码该写在什么类里面一目了然。


一、目录结构

第一层目录:

include目录:

二、直接上代码

egg.h

#ifndef __CPPS_EGG_H__
#define __CPPS_EGG_H__

class CPPSEgg
{
public:
    CPPSEgg();
    ~CPPSEgg()=default;

    void display(int offset);
    char getStyle() const { return mStyle; };

private:
    int mX;
    int mY;
    char mStyle;
};

#endif

map.h

#ifndef __CPPS_MAP_H__
#define __CPPS_MAP_H__

#define DEFAULT_MAP_WALL_X_THICKNESS 1
#define DEFAULT_MAP_WALL_Y_THICKNESS 1

class CPPSMap
{
public:
    CPPSMap();
    CPPSMap(int width, int height);
    ~CPPSMap()=default;

public:
    int getWidth() const { return mMapWidth; };
    int getHeight() const { return mMapHeight; };

    int setWidth(int width) { mMapWidth = width; };
    int setheight(int height) { mMapHeight = height; };

    char getStyleLandscape() const { return mStyleLandscape; };
    char getStylePortrait() const { return mStylePortrait; };
    char getStyleFill() const { return mStyleFill; };

private:
    int mMapWidth;
    int mMapHeight;
    char mStyleLandscape; //width
    char mStylePortrait;
    char mStyleFill;
};

#endif

player.h

#ifndef __CPPS_PLAYER_H__
#define __CPPS_PLAYER_H__

#include <vector>

using namespace std;

struct CPPSPlayerCoordinate
{
public:
    CPPSPlayerCoordinate(int _x, int _y) {
        x = _x;
        y = _y;
    };
    int x;
    int y;
};

class CPPSPlayer
{
public:
    CPPSPlayer();
    ~CPPSPlayer() {};

    enum PLAYER_MOVE_DIRECTION {
        PLAYER_MOVE_DIRECTION_UP = 5,
        PLAYER_MOVE_DIRECTION_DOWN = -5,
        PLAYER_MOVE_DIRECTION_LEFT = 10,
        PLAYER_MOVE_DIRECTION_RIGHT = -10,
        PLAYER_MOVE_DIRECTION_INVALID = -1,
    };

    enum PLAYER_EAT_STATE {
        PLAYER_EAT_STATE_PROGRESS = 1,
        PLAYER_EAT_STATE_FINISHED,
    };

    void eat();
    int getPlayerLength() const { return mvecCoordinate.size(); };
    vector<CPPSPlayerCoordinate> getCoordinate() const { return mvecCoordinate; };
    bool setMoveDirection(PLAYER_MOVE_DIRECTION direction);
    PLAYER_MOVE_DIRECTION getMoveDirection() const { return mMoveDirection; };
    char getStyleHead() const { return mStyleHead; };
    char getStyleBody() const { return mStyleBody; };


private:
    char mStyleHead;
    char mStyleBody;
    PLAYER_MOVE_DIRECTION mMoveDirection;
    // index 0 means head
    vector<CPPSPlayerCoordinate> mvecCoordinate;
    PLAYER_EAT_STATE mEatState;

    void initCoordinate();
    void updateCoordinate();
    void eatInternal();
};

#endif

director.h

#ifndef __CPPS_DIRECTOR_H__
#define __CPPS_DIRECTOR_H__

#include "egg.h"
#include "map.h"
#include "player.h"

#include <vector>
#include <map>
#include <string>
#include <pthread.h>

class CPPSDirector
{
public:
    CPPSDirector();
    ~CPPSDirector();

    void start();
    void refresh();

    enum CPPSSTATE {
        CPPSSTATE_WAIT_START = 1,
        CPPSSTATE_RUNNING,
        CPPSSTATE_STOP,
        CPPSSTATE_COLLISION_WALL        
    };

private:
    //map
    map<int, map<int, string>> mmapRealMapData;
    CPPSMap* mMap;

    //player
    CPPSPlayer* mPlayer;

    //egg
    CPPSEgg* mEgg;
    int mEggX;
    int mEggY;

    // game
    // game-display
    string mstrFormat;
    bool mNeedToCleanScreen;
    pthread_t mthUI;
    CPPSPlayer::PLAYER_MOVE_DIRECTION mPlyaerMoveDirection;
    bool mExitThreadUI;
    CPPSSTATE mGameState;


private:
    // map
    void initMapInternal();
    // player
    void initPlayerInternal();
    // egg
    void initEggInternal();
    void resetEggCoordinateInternal();

    // game
    // return true is Collision
    bool mathCollisionPlayerAndWallInternal();
    // return true is Collision
    bool mathCollisionPlayerAndEggInternal();

    void formatInternal();
    void updateAllCoordinateDataInternal();
    void resetCoordinateInternal(short x, short y);
    void cleanScreenInternal();
    void initUIThreadInternal();
    void refreshInternal();
    void setPlyaerMoveDirection(CPPSPlayer::PLAYER_MOVE_DIRECTION direction);
    CPPSPlayer::PLAYER_MOVE_DIRECTION getPlyaerMoveDirection() const;
};

#endif

egg.cpp

#include "include/egg.h"

CPPSEgg::CPPSEgg()
{
    mX = 0;
    mY = 0;
    mStyle = 'X';
}

map.cpp

#include "include/map.h"

CPPSMap::CPPSMap()
{
    mMapWidth = 50;
    mMapHeight = 10;

    mStyleLandscape = '-';
    mStylePortrait = '|';
    mStyleFill = ' ';
}

CPPSMap::CPPSMap(int width, int height)
{
    mMapWidth = width;
    mMapHeight = height;

    mStyleLandscape = '-';
    mStylePortrait = '|';
    mStyleFill = ' ';
}

player.cpp

#include "include/player.h"

CPPSPlayer::CPPSPlayer()
{
    mMoveDirection = PLAYER_MOVE_DIRECTION_RIGHT;
    mEatState = PLAYER_EAT_STATE_FINISHED;
    mStyleHead = 'O';
    mStyleBody = 'o';
    initCoordinate();
}

void CPPSPlayer::initCoordinate()
{
    mvecCoordinate.clear();
    mvecCoordinate.push_back(CPPSPlayerCoordinate(2, 0));
    mvecCoordinate.push_back(CPPSPlayerCoordinate(1, 0));
    mvecCoordinate.push_back(CPPSPlayerCoordinate(0, 0));
}

bool CPPSPlayer::setMoveDirection(PLAYER_MOVE_DIRECTION direction)
{
    if (direction != PLAYER_MOVE_DIRECTION_INVALID &&
        (mMoveDirection + direction != 0)) {
        mMoveDirection = direction;
    }
    updateCoordinate();
    return true;
}

void CPPSPlayer::updateCoordinate()
{
    auto maxSize = mvecCoordinate.size();
    if (mEatState == PLAYER_EAT_STATE_PROGRESS) {
        mvecCoordinate.push_back(mvecCoordinate[mvecCoordinate.size() - 1]);
        mEatState = PLAYER_EAT_STATE_FINISHED;
    }

    for (auto i = maxSize - 1; i > 0; --i) {
        mvecCoordinate[i].x = mvecCoordinate[i - 1].x;
        mvecCoordinate[i].y = mvecCoordinate[i - 1].y;
    }

    switch (mMoveDirection) {
        case PLAYER_MOVE_DIRECTION_UP:
            mvecCoordinate[0].y -= 1;
            break;
        case PLAYER_MOVE_DIRECTION_DOWN:
            mvecCoordinate[0].y += 1;
            break;
        case PLAYER_MOVE_DIRECTION_LEFT:
            mvecCoordinate[0].x -= 1;
            break;
        case PLAYER_MOVE_DIRECTION_RIGHT:
            mvecCoordinate[0].x += 1;
            break;                                                                                           
    }
}

void CPPSPlayer::eat()
{
    eatInternal();
}

void CPPSPlayer::eatInternal()
{
    mEatState = PLAYER_EAT_STATE_PROGRESS;
}

director.cpp

#include "include/director.h"
#include "include/player.h"

#include <Windows.h>
#include <conio.h>
#include <ctime>
#include <cstdlib>
#include <iostream>

void* refreshThreadFun(void* arg)
{
    CPPSDirector* driector = (CPPSDirector*)arg;
    driector->refresh();
    return NULL;
}

CPPSDirector::CPPSDirector()
{
#define DEFAULT_MAP_WIDTH   50
#define DEFAULT_MAP_HEIGHT  10
// 0 means free
#define DEFAULT_MAP_DATA_VALUE  0

    initMapInternal();
    initPlayerInternal();
    initEggInternal();
    
    mGameState = CPPSSTATE_WAIT_START;
    mPlyaerMoveDirection = mPlayer->getMoveDirection();
    mNeedToCleanScreen = true;

    updateAllCoordinateDataInternal();

    srand(unsigned(time(NULL)));

    // last init ui thread 
    mExitThreadUI = false;
    initUIThreadInternal();
}

void CPPSDirector::initMapInternal()
{
    mMap = new CPPSMap(DEFAULT_MAP_WIDTH, DEFAULT_MAP_HEIGHT);
}

void CPPSDirector::initPlayerInternal()
{
    mPlayer = new CPPSPlayer();
}

void CPPSDirector::initEggInternal()
{
#define DEFAULT_EGG_COORDINATE_X    5
#define DEFAULT_EGG_COORDINATE_Y    5
    mEgg = new CPPSEgg();
    mEggX = DEFAULT_EGG_COORDINATE_X + DEFAULT_MAP_WALL_X_THICKNESS;
    mEggY = DEFAULT_EGG_COORDINATE_Y + DEFAULT_MAP_WALL_Y_THICKNESS;
}

void CPPSDirector::resetEggCoordinateInternal()
{
    while (1) {
        mEggX = 1 + (mMap->getWidth() - 1) * rand() / RAND_MAX;
        mEggY = 1 + (mMap->getHeight() - 1) * rand() / RAND_MAX;
        if ((mmapRealMapData[mEggY])[mEggX] == " ") {
            //cout << mEggX << "+" << mEggY << endl;
            break;
        }
    }
}

void CPPSDirector::initUIThreadInternal()
{
    int ret = pthread_create(&mthUI, NULL, refreshThreadFun, this);
    if (ret != 0) {
        cout << "[error]pthread_create failed, errno="<< ret << endl;
    }
}

void CPPSDirector::start()
{
    // wait for start
    while (1) {
        if (mGameState == CPPSSTATE_WAIT_START) {
            Sleep(1000);
            continue;
        } else {
            break;
        }
    }

    // wait for input
    char input;
    while (1) {
        if (mGameState == CPPSSTATE_STOP ||
            mGameState == CPPSSTATE_COLLISION_WALL) {
            break;
        }
        input = getch();
        if ((int)input == -32) {
            input = getch();
        }
        switch (int(input)) {
            case 72://up
                setPlyaerMoveDirection(CPPSPlayer::PLAYER_MOVE_DIRECTION_UP);
                break;
            case 80://down
                setPlyaerMoveDirection(CPPSPlayer::PLAYER_MOVE_DIRECTION_DOWN);
                break;
            case 75://left
                setPlyaerMoveDirection(CPPSPlayer::PLAYER_MOVE_DIRECTION_LEFT);
                break;
            case 77://right
                setPlyaerMoveDirection(CPPSPlayer::PLAYER_MOVE_DIRECTION_RIGHT);
                break;
            case int('q'):
                mGameState = CPPSSTATE_STOP;
                mExitThreadUI = true;
                goto END;
                break;
            default :
                setPlyaerMoveDirection(CPPSPlayer::PLAYER_MOVE_DIRECTION_INVALID);
                break;
        };
    }
    END:;
}

void CPPSDirector::updateAllCoordinateDataInternal()
{
    // update map Coordinate
    for (auto i = 0; i < DEFAULT_MAP_HEIGHT; ++i) {
        map<int, string> mapData;
        for (auto j = 0; j < DEFAULT_MAP_WIDTH; ++j) {
            if ((i == 0) || (i == DEFAULT_MAP_HEIGHT - 1)) {
                if (j == 0) {
                    mapData[j] = mMap->getStylePortrait();
                    continue;
                } else if (j > 0 && j < DEFAULT_MAP_WIDTH - 1) {
                    mapData[j] = mMap->getStyleLandscape();
                    continue;
                } else if (j == DEFAULT_MAP_WIDTH - 1) {
                    mapData[j] = mMap->getStylePortrait();
                    continue;
                } else {
                    mapData[j] = mMap->getStyleFill();
                    continue;
                }
            } else if (i > 0 && i < DEFAULT_MAP_HEIGHT - 1) {
                if (j == 0) {
                    mapData[j] = mMap->getStylePortrait();
                    continue;
                } else if (j == DEFAULT_MAP_WIDTH - 1) {
                    mapData[j] = mMap->getStylePortrait();
                    continue;
                } else {
                    mapData[j] = mMap->getStyleFill();
                    continue;
                }
            } else {
                mapData[j] = mMap->getStyleFill();
            }
        }
        mmapRealMapData[i] = mapData;
    }

    // update egg Coordinate
    (mmapRealMapData[mEggY])[mEggX] = mEgg->getStyle();

    // update player Coordinate
    for (auto i = 0; i < mPlayer->getPlayerLength(); ++i) {
        if (i == 0) {
            (mmapRealMapData[mPlayer->getCoordinate()[i].y +
                1])[mPlayer->getCoordinate()[i].x + 1] = mPlayer->getStyleHead();//"^";//"T";//"鈽?;
        } else {
            (mmapRealMapData[mPlayer->getCoordinate()[i].y +
                1])[mPlayer->getCoordinate()[i].x + 1] = mPlayer->getStyleBody();
        }
    }
}

CPPSDirector::~CPPSDirector()
{
    pthread_join(mthUI, NULL);
    if (!mMap) {
        delete mMap;
        mMap = NULL;
    }
    if (!mPlayer) {
        delete mPlayer;
        mPlayer = NULL;
    }
    if (!mEgg) {
        delete mEgg;
        mEgg = NULL;
    }
}

void CPPSDirector::formatInternal()
{
    mstrFormat = "";
    auto height = mMap->getHeight();
    auto width  = mMap->getWidth();

    for (auto i = 0; i < mmapRealMapData.size();  ++i) {
        for (auto j = 0; j < (mmapRealMapData[i]).size(); ++j) {
            mstrFormat.append((mmapRealMapData[i])[j]);
        }
        mstrFormat.append("\n");
    }
}

void CPPSDirector::resetCoordinateInternal(short x, short y) {
    COORD pos;
    pos.X = x;
    pos.Y = y;
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hOut, pos);
    cout << std::flush;
}

void CPPSDirector::cleanScreenInternal()
{
    if (mNeedToCleanScreen) {
        system("cls");
        mNeedToCleanScreen = false;
    }
}

void CPPSDirector::refreshInternal()
{
// control move speed
#define DEFAULT_REFRESH_RATE 200
    unsigned long time_start, time_end, time_sleep;
    mGameState = CPPSSTATE_RUNNING;
    cleanScreenInternal();
    while (1) {
        if (mExitThreadUI) {
            break;
        }
        time_start = GetTickCount();
        updateAllCoordinateDataInternal();
        formatInternal();
        resetCoordinateInternal(0, 2);
        cout << mstrFormat << std::flush;

        if (mathCollisionPlayerAndWallInternal()) {
            mGameState = CPPSSTATE_COLLISION_WALL;
            cout << "game over" << endl;
            cout << "game over" << endl;
            cout << "game over" << endl;
            cout << std::flush;
            break;
        }
        if (mathCollisionPlayerAndEggInternal()) {
            mPlayer->eat();
            resetEggCoordinateInternal();
        }

        time_end = GetTickCount();
        time_sleep = (time_end - time_start) > DEFAULT_REFRESH_RATE ?
            0 : DEFAULT_REFRESH_RATE - (time_end - time_start);

        Sleep(time_sleep);
        mPlayer->setMoveDirection(getPlyaerMoveDirection());
        if (mExitThreadUI) {
            break;
        }
    }
}

void CPPSDirector::refresh()
{
    refreshInternal();
}

void CPPSDirector::setPlyaerMoveDirection(CPPSPlayer::PLAYER_MOVE_DIRECTION direction)
{
    mPlyaerMoveDirection = direction;
}

CPPSPlayer::PLAYER_MOVE_DIRECTION CPPSDirector::getPlyaerMoveDirection() const
{
    return mPlyaerMoveDirection;
}

bool CPPSDirector::mathCollisionPlayerAndWallInternal()
{
    if ((mPlayer->getCoordinate()[0].x + DEFAULT_MAP_WALL_X_THICKNESS == 0 ||
            mPlayer->getCoordinate()[0].x + DEFAULT_MAP_WALL_X_THICKNESS ==
            DEFAULT_MAP_WIDTH - DEFAULT_MAP_WALL_X_THICKNESS) ||
            (mPlayer->getCoordinate()[0].y + DEFAULT_MAP_WALL_Y_THICKNESS == 0 ||
            mPlayer->getCoordinate()[0].y + DEFAULT_MAP_WALL_Y_THICKNESS ==
            DEFAULT_MAP_HEIGHT - DEFAULT_MAP_WALL_Y_THICKNESS)) {
        return true;
    }

    return false;
}

bool CPPSDirector::mathCollisionPlayerAndEggInternal()
{
    if (mPlayer->getCoordinate()[0].x + 1 == mEggX &&
        mPlayer->getCoordinate()[0].y + 1 == mEggY) {
        return true;
    }

    return false;
}

main.cpp

#include "include/director.h"

#include <windows.h>
#include <stdio.h>

int main(int /*argc*/, char** /*argv*/)
{
    CPPSDirector director;
    director.start();

    system("pause");
    return 0;
}

三、编译

我本地是windows+mingw32的环境。编译命令是:

g++ src/main.cpp src/map.cpp src/egg.cpp src/director.cpp src/player.cpp -lpthread

总结

今年学校的科技活动总算应付过去了,本来想写的好一点的,再增加线程用来控制速度,再封装一个消息在几个线程之间传递控制。cout输出的时候也有优化的空间,不需要全部重新输出,代码也需要整理整理。奈何学校要的急,凑合着赶快交上去吧。等有空了再优化优化吧。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值