0
点赞
收藏
分享

微信扫一扫

常见手撕项目C++

登高且赋 2024-03-30 阅读 14

游戏说明:


linux 环境下的,基于Ncursse 图形图的C语言小游戏

基础要求:


C语言基础
Linux 基本操作:  
如何编写代码 如何编译代码  如何运行程序  如何创建文件夹


为什么需要应用ncurse 库:


C语言的键盘输入 函数  没法满足实时性 -- scanf gets getchar -- 都需要 回车确认

curse 输入和输出:

上面四个键是功能键 -- 使用的时候需要我们 引入 keypad函数

贪吃蛇地图:


20 * 20 的格子 :
地图上界"--"
地图左界"|"
苹果(食物)"##"
蛇身 "[][][[]"

=========================================


贪吃蛇身子 节点 -- 结构体--使用链表实现

=======================================


链表添加蛇的节点:

=========================================


实现贪吃蛇的向右移动:

1.右移:
原理 删除第一个, 在最右边一个节点就没添加一个

===============================

========================


贪吃蛇的自行游走

 -- while -- 一直执行, move函数 usleep() -- 控制时间

 warning: implicit declaration of function ‘usleep’ [-Wimplicit-function-declaration]
  156 |        usleep(100000);
// 这种警报 可以直接man usleep 查看他的头文件

===============================

方向变化 -- 多线程


引入 -- .界面一边在刷新,我们也要求一边能接收我们的按键  -- 多线程 --让两个while 同时进行


怎么实现多线程:


Linux 线程:


===================================


蛇的转弯


定义全局 的方向变量dir 
在键入 方向键的时候进行修改 -- 然后在moveSncak()的addSnack() 的时候 方向 被改变


===================================

绝对值优化蛇的不合理走位;

=====================================


苹果 -- 食物

食物可以使用之前的snack 结构体里面的坐标表示

static -- 静态变量 -- 函数被再次调用打的时候他的值不会发生变化

rand()  --实现食物随机生成: % 20 -- 限制范围

==================================


蛇能吃自己 -- 链表尾巴 和 身体的重合

//这里我们再明确一点 ,由于我们使用的是尾插法,so我们得到的链表尾巴就是蛇头

代码整体实现

#include <curses.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2



struct Snack //定义蛇类
{
int line;
int row;

struct Snack *next; //链表实现贪吃蛇变长
};
// 定义蛇头,蛇尾
struct Snack *head = NULL;
struct Snack *tail = NULL;
struct Snack food; //定义食物类
int key;
int dir; //记录方向

void initNcurse() //对Ncurse初始化
{

initscr();
keypad(stdscr, 1);
noecho(); //避免键入方向键的时候屏幕错乱,返回值
}

void initFood() //初始化食物
{
int x=rand()%20; //随机生成0-19的位置
int y=rand()%20;

food.line=x;
food.row=y;



}
int haveFood(int line, int row) //判断这个格子上是否存在食物
{
if(food.line==line && food.row==row)
return 1;

return 0;
}


int haveSnack(int line, int row)//判断这个格子上是否存在蛇
{
struct Snack *p = head;

while (p != NULL)
{
if (p->line == line && p->row == row)
return 1;
p = p->next;
}
return 0;
}

void addSnack() //增加蛇的长度
{
// 尾插法实现
struct Snack *new = (struct Snack *)malloc(sizeof(struct Snack));


switch (dir) //依据当前的方向判断
{
case UP:
new->line = tail->line-1;
new->row = tail->row;
new->next = NULL; // 作为新的尾巴
break;

case DOWN:
new->line = tail->line+1;
new->row = tail->row;
new->next = NULL; // 作为新的尾巴
break;

case LEFT:
new->line = tail->line;
new->row = tail->row-1;
new->next = NULL; // 作为新的尾巴
break;

case RIGHT:
new->line = tail->line;
new->row = tail->row+1;
new->next = NULL; // 作为新的尾巴
break;

}


tail->next = new;
tail = new;
}

void initSnack() //初始化蛇 - 死后 复活
{
dir=RIGHT;
initFood();
struct Snack *new = NULL;
while (head != NULL)
{
new = head;
head = head->next;
free(new);
}

head = (struct Snack *)malloc(sizeof(struct Snack));
head->line = 2;
head->row = 2;
head->next = NULL;
tail = head;

addSnack();
addSnack();
addSnack();
}

void myPrintMap() //打印地图
{
int line, row;

move(0, 0); //每次新的地图都移动到(0,0),覆盖之前的地图实现实时性
for (line = 0; line < 20; ++line)
{
if (line == 0)
{
for (row = 0; row < 20; row++)
printw("--");
printw("\n");
}

if (line >= 0 && line < 20)
{
for (row = 0; row <= 20; row++)
{
if (row == 0)
printw("|");
else if (row == 20)
printw("|\n");
else if (haveSnack(line, row))
printw("[]");
else if (haveFood(line, row))
printw("##");
else
printw(" ");
}
if (line == 19)
{
for (row = 0; row < 20; row++)
{
if (row == 19)
printw("--\nby mxjun");
else
printw("--");
}
}
}
}
}

void delSnack()
{
struct Snack *new;
new = head;
head = head->next;
free(new);
}
int ifSnackDead() //判断边界
{
if(tail->line<0 || tail->line==20 || tail->row==0 || tail->row==20)
return 1;
struct Snack *p=head;
while(p->next!=NULL)
{
if(p->line==tail->line && p->row==tail->row)
return 1;
p=p->next;
}
return 0;

}


void moveSnack()
{
if(tail->line==food.line && tail->row==food.row) //吃到食物蛇变长
{
addSnack();
initFood();
}
else //不然只是移动
{
addSnack();
delSnack();
}
// 判断是否撞到墙
if (ifSnackDead())
{
initSnack();//死了重开
}
}

void *refreshInterface() //线程之一,实现蛇的自动行走
{

while (1)
{

moveSnack();
myPrintMap();
refresh();
usleep(100000);
}
}

void turn(int newDir) //防止蛇出行头变为尾巴这样的逆天走位
{
if(abs(newDir)!=abs(dir))
dir=newDir;

}

void *inputKey() //第二个线程,时刻等待键盘输入
{

while (1)
{
key = getch();
switch (key)
{
case KEY_DOWN:
turn(DOWN);
break;
case KEY_UP:
turn(UP);
break;
case KEY_LEFT:
turn(LEFT);
break;
case KEY_RIGHT:
turn(RIGHT);
break;
}
}
}

int main()
{
//定义两个线程
pthread_t th1;
pthread_t th2;
//初始化蛇和Ncurse
initNcurse();
initSnack();

pthread_create(&th1, NULL, refreshInterface, NULL);
pthread_create(&th2, NULL, inputKey, NULL);
while (1) //避免程序退出
{
}
getch();
endwin(); //结束使用ncurse,退出程序,避免乱码

getch();
return 0;
}
举报

相关推荐

0 条评论