经典方块游戏一

贪吃蛇的第一次实现(C++):

base.hpp

#ifndef _BASE_H
#define _BASE_H

#pragma once

#include <time.h>
#include <stdlib.h>

using namespace std;

enum Block { EMPTY = 0, FILL };

enum Direct { UP = 0, DOWN, LEFT, RIGHT };

enum State { OVER = 0, RUNNING, PAUSE, PASSED };

enum Button { RESET = 0, SOUND, PAUSE_START, OPTIONS, ROTATA_DIRECTION, LEFT_SPEED, RIGHT_SPEED, QUICK_LEVELS, DOWN_LEVELS };

class Point
{
public:
    int x, y;

    Point(int x = 0, int y = 0) : x(x), y(y) {}
    bool operator==(const Point&point)
    {
        return point.x == x && point.y == y;
    }
};

/* 基类 */
class Base
{
public:
    const static int WIDTH = 10;
    const static int HEIGHT = 20;

protected:
    int     score;
    State   gameState;

public:
    Block area[WIDTH][HEIGHT];

    virtual void refreshArea() = 0;
    int getScore() { return score; }
    Base(int s = 0) : score(s), gameState(RUNNING) {
        srand((unsigned int)time(0));
    }

    static void forwardFrom(Point &point, Direct dir);
    static bool checkPoint(const Point &point);
    static int randint(int start, int end);
};

int Base::randint(int start, int end)
{
    return (rand() % (end - start)) + start;
}

void Base::forwardFrom(Point &point, Direct dir)
{
    switch (dir)
    {
    case UP:
        point.y++;
        break;
    case DOWN:
        point.y--;
        break;
    case LEFT:
        point.x--;
        break;
    case RIGHT:
        point.x++;
        break;
    }
}

bool Base::checkPoint(const Point &point)
{
    return point.x >= 0 && point.x < WIDTH && point.y >= 0 && point.y < HEIGHT;
}

#endif

snake.hpp

#ifndef _SNAKE_H
#define _SNAKE_H

#pragma once

#include "../base/base.hpp"
#include <iostream>
#include <List>

using namespace std;

class Snake : public Base
{
private:
    const static int FIRST_LEN = 3;
    const static Direct FIRST_DIRECT = RIGHT;
    const static int SCORE_PER_FOOD = 100;

private:
    list<Point> body;       // 0-蛇首
    Direct      direct;
    Point       food;

    void clearArea();
    bool addFood();
    void updateScore();
    void turnDirection(Direct dir);
    bool checkBody(Point point);

public:
    void refreshArea();
    void forward();
    void pressButton(Button btn);
    Snake();
    Point const&getFood() { return food; }
};

void Snake::updateScore()
{
    score += SCORE_PER_FOOD;
}

Snake::Snake()
{
    int startX = (WIDTH - FIRST_LEN) / 2;
    int startY = HEIGHT / 2;
    //body.clear();
    // 初始化 body 和 direct
    for (int loop = 0; loop < FIRST_LEN; loop++)
    {
        body.push_front(Point(startX + loop, startY));
    }
    direct = FIRST_DIRECT;
    // 初始化食物
    Snake::addFood();
    //gameState = State::RUNNING;
    //score = 0;
}

bool Snake::checkBody(Point point)
{
    for (list<Point>::iterator i = body.begin(); i != body.end(); i++)
    {
        if ((*i) == point) return false;
    }
    return true;
}

void Snake::forward()
{
    if (gameState != RUNNING) return;

    Point &lastPoint = body.front();
    Point nextPoint = lastPoint;
    // 获取当前方向下一格的坐标
    Base::forwardFrom(nextPoint, direct);

    // 下一步合法(未出界且未碰撞到 body )
    if (checkPoint(nextPoint) && checkBody(nextPoint))
    {
        // 吃到食物,蛇首增加一格
        if (nextPoint == food)
        {
            // 更新分数
            Snake::updateScore();
            body.push_front(nextPoint); // 不能提取出来!
            // 无法生成食物,通关
            if (!Snake::addFood())
            {
                gameState = PASSED;
            }
        }
        // 未吃到食物,蛇尾最后一格移动到蛇首
        else
        {
            body.pop_back();
            body.push_front(nextPoint);
        }
    }
    // 游戏结束
    else
    {
        gameState = OVER;
    }
}

bool Snake::addFood()
{
    int randIdx = Base::randint(0, WIDTH * HEIGHT - body.size());

    Snake::refreshArea();

    for (int x = 0; x < WIDTH; x++)
    {
        for (int y = 0; y < HEIGHT; y++)
        {
            if (area[x][y] != EMPTY) continue;
            if ((randIdx--) == 0)
            {
                food.x = x; food.y = y;
                return true;
            }
        }
    }
    return false;
}

void Snake::clearArea()
{
    for (int x = 0; x < WIDTH; x++)
    {
        for (int y = 0; y < HEIGHT; y++)
        {
            area[x][y] = EMPTY;
        }
    }
}

void Snake::refreshArea()
{
    Snake::clearArea();
    for (list<Point>::iterator i = body.begin(); i != body.end(); i++)
    {
        area[(*i).x][(*i).y] = FILL;
    }
    //cout<<body.size()<<endl;
}

void Snake::turnDirection(Direct dir)
{
    if (direct == dir) return;

    list<Point>::iterator i = body.begin();
    Point &p1 = (*i++);
    Point &p2 = (*i++);

    if ((dir == LEFT) && (p1.x == p2.x + 1) && (p1.y == p2.y))
        return;
    else if ((dir == DOWN) && (p1.x == p2.x) && (p1.y == p2.y + 1))
        return;
    else if ((dir == RIGHT) && (p1.x == p2.x - 1) && (p1.y == p2.y))
        return;
    else if ((dir == UP) && (p1.x == p2.x) && (p1.y == p2.y - 1))
        return;
    direct = dir;
}

void Snake::pressButton(Button btn)
{
    switch (btn)
    {
    case RESET:
        Snake::Snake();
        break;
    case SOUND:
        break;
    case PAUSE_START:
        if (gameState == PAUSE)
            gameState = RUNNING;
        else if (gameState == RUNNING)
            gameState = PAUSE;
        break;
    case OPTIONS:
        break;
    case ROTATA_DIRECTION:
        Snake::forward();
        break;
    case LEFT_SPEED:
        Snake::turnDirection(LEFT);
        break;
    case RIGHT_SPEED:
        Snake::turnDirection(RIGHT);
        break;
    case QUICK_LEVELS:
        Snake::turnDirection(UP);
        break;
    case DOWN_LEVELS:
        Snake::turnDirection(DOWN);
        break;
    }
}

#endif

test_snake.cpp

#include "gtestgtest.h"
#include "snake.hpp"

/* 0-RESET, 1-SOUND, 2-PAUSE_START, 3-OPTIONS, 4-ROTATA_DIRECTION, 5-LEFT_SPEED, 6-RIGHT_SPEED, 7-QUICK_LEVELS, 8-DOWN_LEVELS  */
void SnakeOperator(Snake &snake, const char *str)
{
    for (int i = 0; i < strlen(str); i++)
    {
        snake.pressButton((Button)i);
    }
}

bool checkSnakeArea(Snake &snake, const Block area[Snake::WIDTH][Snake::HEIGHT])
{
    snake.refreshArea();
    if (memcmp(snake.area, area, sizeof(area)) == 0) return true;
    return false;
}

TEST(testSnake, snake_origin)
{
    Block area[Snake::WIDTH][Snake::HEIGHT] =
    {
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, FILL, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, FILL, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, FILL, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY }
    };

    Snake snake;
    ASSERT_TRUE(checkSnakeArea(snake, area));
}

TEST(testSnake, snake_forward_one_step)
{
    Block area[Snake::WIDTH][Snake::HEIGHT] =
    {
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, FILL, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, FILL, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, FILL, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY }
    };
    const char *str = "4";

    Snake snake;
    ASSERT_TRUE(checkSnakeArea(snake, area));
}

TEST(testSnake, snake_turn_and_forward)
{
    Block area[Snake::WIDTH][Snake::HEIGHT] =
    {
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, FILL, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, FILL, FILL, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY },
        { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY }
    };
    const char *str = "84";

    Snake snake;
    ASSERT_TRUE(checkSnakeArea(snake, area));
}

#include <stdio.h>

void printArea(Snake &snake)
{
    snake.refreshArea();
    const Point &food = snake.getFood();
    snake.area[food.x][food.y] = Block::FILL;
    system("cls");

    for (int y = 0; y < Base::HEIGHT; y++)
    {
        for (int x = 0; x < Base::WIDTH; x++)
        {
            if (snake.area[x][y] == Block::FILL)
            {
                printf("%c", '1');
            }
            else
            {
                printf("..");
            }
        }
        printf("
");
    }
}

void cycle()
{
    Snake snake;
    int c;

    printArea(snake);

    while ((c = getchar()) != 'e')
    {
        switch (c)
        {
        case 'w':
            snake.pressButton(Button::QUICK_LEVELS);
            break;
        case 'a':
            snake.pressButton(Button::LEFT_SPEED);
            break;
        case 's':
            snake.pressButton(Button::DOWN_LEVELS);
            break;
        case 'd':
            snake.pressButton(Button::RIGHT_SPEED);
            break;
        case ' ':
            snake.pressButton(Button::PAUSE_START);
            break;
        case 'r':
            snake.pressButton(Button::RESET);
            break;
        case 'u':
            snake.pressButton(Button::ROTATA_DIRECTION);
            break;

        }
        
        printArea(snake);
    }
}

int main(int argc, char **argv)
{
    int ret = 0;
    //::testing::InitGoogleTest(&argc, argv);
    //ret = RUN_ALL_TESTS();
    cycle();
    return ret;
}
原文地址:https://www.cnblogs.com/rmthy/p/8179137.html