河南大学面向过程程序设计C/C++期末大作业-基于控制台的贪吃蛇实现

基于控制台的贪吃蛇实现

  这是本人大一下学期的期末大作业,灵感是system(“cls”)清屏,还有键盘监听函数单链表数据结构,核心是单链表system(“cls”)函数。灵感来源于网络,启发了我基于单链表设计。由于system(“cls”)刷屏自身的缺陷和不足,游戏运行起来会不可避免地有些“闪眼睛”。我记得当时还有一种方法不用system(“cls”)清屏,但我没使用那种方法,大家可以找找别的博客或视频学习学习。

  代码完全由本人编写和设计,由于时间比较久远,很多实现细节我也记不清了。现在决定对以前写的小史山开源,以供后来者学习,以下内容仅供学习和参考

#include <iostream>
#include <Windows.h>
#include <cstdio>
#include <iomanip>
#include <conio.h>
#include <cstdlib>
#include <ctime>
#include <cstring>
#define lim 1000

using namespace std;

typedef struct SNAKE {//贪吃蛇特征
    int snakeX;//行
    int snakeY;//列
    struct SNAKE * next;
}SNAKE;

SNAKE * head = NULL;
SNAKE * tail = NULL;
SNAKE * newone = NULL;

char border[lim][lim];//地图
char point = 'D';//游标
int Rank = 13;
int Column = 13;//地图大小
int results;//分数
int ranx;
int rany;//随机数(食物)
int foodNum = 27;//总食物数
int len;//蛇的长度
int key = 0;//监听标准呼入
int tempX;
int tempY;//中间变量
bool getFood = false;

void mabor(char (*border)[lim], int, int);//设置边界
void prtbor(char (*border)[lim], int rank, int column);//打印边界(打印地图)
void turnUp();//向上位移
void turnDown();//向下位移
void turnLeft();//向左位移
void turnRight();//向右位移
int move(char);//蛇的移动
void prtlink();//打印蛇身(打印链表)
void apply();//申请一个节点(蛇身长度加一)
void setSnake();//初始化蛇的属性
void settle();//游戏结算
void exLocation(int *a, int *b);//交换变量
void handle();//操控方向
void instruction();//一级菜单


int main() {
    system("cls");//清屏

    system("color 09");//发现WindowAPI还可以调控制台背景颜色和字体颜色
    instruction();//说明书
    
    mabor(border, Rank, Column);//初始化地图
    prtbor(border, Rank, Column);//打印地图

    while (move(point)) {//当键入o或O时退出游戏
        Sleep(40);
        handle();//监听标准输入事件
        system("cls");
        handle();//监听标准输入事件
        prtbor(border, Rank, Column);//打印地图
        handle();//监听标准输入事件
    }
}

void mabor(char (*border)[lim], int rank, int column) {//设置地图  
    for (int x = 0; x < rank; x++) {
        for (int y = 0; y < column; y++) {
            if (x != 0 && x != column - 1) {
                y == 0 || y == column - 1 ? border[x][y] = '#' : border[x][y] = ' ';
            } else {
                border[x][y] = '#';
            }
        }
    }
    setSnake();
    //食物随机坐标
    for (int i = 0; i < foodNum; i++) {
        srand(time(NULL) + i);
        ranx = rand() % (Rank - 2) + 1;
        rany = rand() % (Column - 2) + 1;
        if (border[ranx][rany] != '@' && border[ranx][rany] != 'O') {
            border[ranx][rany] = '@';
        } else {
            i--;
        }
    }
}

void prtbor(char (*border)[lim], int rank, int column) {//打印地图
    for (int x = 0; x < rank; x++) {
        for (int y = 0; y < column; y++) {
            cout << setw(2) << border[x][y];
        }
        cout << endl;
    }
    handle();
    cout << "Food: " << foodNum << endl << endl;
    cout << "Scores: " << results << endl << endl;
    cout << "Len: " << len << endl << endl;
    handle();
}

int move(char point) {//操作台,接收point来控制蛇的位移方向
    switch (point) {
        case 'W':
            turnUp();//向上位移
            break;
        case 'S':
            turnDown();//向下位移
            break;
        case 'A':
            turnLeft();//向左位移
            break;
        case 'D':
            turnRight();//向右位移
            break;
        case 'L':
            settle();//直接结束
            break;
    }
    return 1;//返回1维系死循环
}

void turnUp() {
    if ((head -> snakeX) - 1 == 0 || border[(head -> snakeX) - 1][head -> snakeY] == 'O') {
        Sleep(1000);
        settle();//撞墙或撞到自己身体 -> 游戏结束 -> 游戏结算
    } else {
        tail = head;
        tempX = head -> snakeX;//temp变量保存变化前的头节点坐标
        tempY = head -> snakeY;
        (head -> snakeX)--;//头节点发生变化
        if (border[head -> snakeX][head -> snakeY] == '@') {
            getFood = true;//吃到食物
        }
        border[head -> snakeX][head -> snakeY] = 'O';
        tail = tail -> next;
        while (tail != NULL) {//遍历链表
            exLocation(&tempX, &(tail -> snakeX));
            exLocation(&tempY, &(tail -> snakeY));
            border[tail -> snakeX][tail -> snakeY] = 'O';
            tail = tail -> next;
        }
        if (getFood == true) {//吃到食物
            apply();
            newone -> snakeX = tempX;
            newone -> snakeY = tempY;
            border[tempX][tempY] = 'O';
            getFood = false;
        } else {
            border[tempX][tempY] = ' ';//没吃到食物
        }
    }
}

void turnDown() {
    if ((head -> snakeX) + 1 == Rank - 1 || border[(head -> snakeX) + 1][head -> snakeY] == 'O') {
        Sleep(1000);
        settle();//撞墙或撞到自己身体 -> 游戏结束 -> 游戏结算
    } else {
        tail = head;
        tempX = head -> snakeX;//temp变量保存变化前的头节点坐标
        tempY = head -> snakeY;
        (head -> snakeX)++;//头节点发生变化
        if (border[head -> snakeX][head -> snakeY] == '@') {
            getFood = true;//吃到食物
        }
        border[head -> snakeX][head -> snakeY] = 'O';
        tail = tail -> next;
        while (tail != NULL) {//遍历到蛇尾
            exLocation(&tempX, &(tail -> snakeX));
            exLocation(&tempY, &(tail -> snakeY));
            border[tail -> snakeX][tail -> snakeY] = 'O';
            tail = tail -> next;
        }
        if (getFood == true) {//吃到食物
            apply();
            newone -> snakeX = tempX;
            newone -> snakeY = tempY;
            border[tempX][tempY] = 'O';
            getFood = false;
        } else {
            border[tempX][tempY] = ' ';//没吃到食物
        }
    }
}
//标记
void turnRight() {
    if ((head -> snakeY) + 1 == Column - 1 || border[head -> snakeX][(head -> snakeY) + 1] == 'O') {
        Sleep(1000);
        settle();//撞墙或撞到自己身体 -> 游戏结束 -> 游戏结算
    } else {
        tail = head;
        tempX = head -> snakeX;//temp变量保存变化前的头节点坐标
        tempY = head -> snakeY;
        (head -> snakeY)++;//头节点发生变化
        if (border[head -> snakeX][head -> snakeY] == '@') {
            getFood = true;//吃到食物
        }
        border[head -> snakeX][head -> snakeY] = 'O';
        tail = tail -> next;
        while (tail != NULL) {
            exLocation(&tempX, &(tail -> snakeX));
            exLocation(&tempY, &(tail -> snakeY));
            border[tail -> snakeX][tail -> snakeY] = 'O';
            tail = tail -> next;//遍历到蛇尾
        }
        if (getFood == true) {//吃到食物
            apply();
            newone -> snakeX = tempX;
            newone -> snakeY = tempY;
            border[tempX][tempY] = 'O';
            getFood = false;
        } else {
            border[tempX][tempY] = ' ';//没吃到食物
        }
    }
}

void turnLeft() {
    if ((head -> snakeY) - 1 == 0 || border[head -> snakeX][(head -> snakeY) - 1] == 'O') {
        Sleep(1000);
        settle();//撞墙或撞到自己身体 -> 游戏结束 -> 游戏结算
    } else {
        tail = head;
        tempX = head -> snakeX;//temp变量保存变化前的头节点坐标
        tempY = head -> snakeY;
        (head -> snakeY)--;//头节点发生变化
        if (border[head -> snakeX][head -> snakeY] == '@') {
            getFood = true;//吃到食物
        }
        border[head -> snakeX][head -> snakeY] = 'O';
        tail = tail -> next;
        while (tail != NULL) {//遍历到蛇尾
            exLocation(&tempX, &(tail -> snakeX));
            exLocation(&tempY, &(tail -> snakeY));
            border[tail -> snakeX][tail -> snakeY] = 'O';
            tail = tail -> next;
        }
        if (getFood == true) {//吃到食物
            apply();
            newone -> snakeX = tempX;
            newone -> snakeY = tempY;
            border[tempX][tempY] = 'O';
            getFood = false;
        } else {
            border[tempX][tempY] = ' ';//没吃到食物
        }
    }
}

void apply() {//尾插法
    len++;//蛇身长度加一
    results += 10;//积分加10,一个食物10分
    foodNum--;//食物数量减一
    newone = (SNAKE *)malloc(sizeof(SNAKE));//申请新的节点
    
    tail = head;
    if (head == NULL) {
        head = newone;
        head -> next = NULL;
    } else {
        while (tail -> next != NULL) {
            tail = tail -> next;
        }

        tail -> next = newone;
        newone -> next = NULL;
    }
    if (foodNum == 0) {
        Sleep(1000);
        settle();//食物剩余为0,直接结束
    }
}

void prtlink() {//打印蛇身(打印链表)
    tail = head;
    while (tail != NULL) {
        cout << "Snake " << tail -> snakeX << ' ' << tail -> snakeY << endl;
        tail = tail -> next;
    }
}

void setSnake() {//初始化蛇的相关属性
    tail = head;
    for (int i = 1; i <= 3; i++) {
        apply();//初始化蛇的长度,长度为3
        len = i;
    }
    tail = head;
    for (int i = len; tail != NULL; i--) {
        tail -> snakeX = 1;
        tail -> snakeY = i;//初始化坐标
        border[1][i] = 'O';
        tail = tail -> next;
    }
}

void settle() {//游戏结算
    system("cls");//进入游戏结算画面
    for (int i = 3; i > 0; --i) {
        cout << "Game Over!" << endl << endl;
        cout << "Scores: " << results << endl << endl;
        cout << "Len: " << len << endl << endl;
        cout << "quit in " << i <<" seconds..." << endl << endl;
        Sleep(1000);
        system("cls");
    }
    system("pause");
    exit(0);
}

void exLocation(int *a, int *b) {//交换两个变量的值
    int mid = *a;
    *a = *b;
    *b = mid;
}

void handle() {//监听标准输入事件
    key = GetKeyState('W');
    if (key < 0 && point != 'S') {
        point = 'W';
        key = 0;
    }
    key = GetKeyState('S');
    if (key < 0 && point != 'W') {
        point = 'S';
        key = 0;
    }
    key = GetKeyState('A');
    if (key < 0 && point != 'D') {
        point = 'A';
        key = 0;
    }
    key = GetKeyState('D');
    if (key < 0 && point != 'A') {
        point = 'D';
        key = 0;
    }
    key = GetKeyState('L');
    if (key < 0) {
        settle();
    }
}

void instruction() {//说明书
    cout << "Hungry Snake" << endl << endl;
    cout << "Instruction" << endl;
    cout << "w or W -> move up" << endl;//按下w键向上移动
    cout << "s or S -> move down" << endl;//按下s键向下移动
    cout << "a or A -> move left" << endl;//按下a键向左移动
    cout << "d or D -> move right" << endl;//按下d键向右移动
    cout << "l or L -> game over" << endl << endl;//按下l键退出游戏
    cout << "Continue to play?(Please enter 'y' or 'n')" << endl;
    char code = _getch();
    if (code == 'y' || code == 'Y') {
        system("cls");
    } else {
        settle();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

书接上回

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值