经典方块游戏一
贪吃蛇的第一次实现(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 "gtest\gtest.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("\n"); } } 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; }