萌新入门C语言第一个项目 《贪吃蛇小游戏》#C语言学习#Linux系统编程
前言
《贪吃蛇小游戏》是我学习完C语言后的关于C语言的项目,是检验我的C语言编程能力的项目,因此我记录一下我的成长,写一下我是如何完成《贪吃蛇小游戏》的制作的。
1.项目预备知识
1.C变量
2.基本输入输出
3.流程控制
4.函数
5.指针
6.结构体
7.链表
8.Linux基础操作
以上知识点我提供了以下博文链接方便查阅
指针
结构体
链表
Linux基础操作
2.贪吃蛇游戏实现的0基本库ncurse图形库
1.什么是ncurse图形库
ncurses是一个开源的文本用户界面(TUI)库,旨在为终端应用程序提供图形化的用户界面。它允许开发者创建基于文本的交互式应用程序,如命令行界面、终端游戏等。
ncurses库提供了一系列函数来控制终端屏幕的输出和输入。它可以处理终端窗口的位置、大小、颜色、字符缓冲区以及键盘和鼠标输入等。通过使用ncurses,开发者可以在控制台环境下创建具有菜单、对话框、窗口、按钮等元素的图形用户界面。
ncurses是一个跨平台的库,可以在多种操作系统上使用,包括Linux、Unix、BSD等。它简化了开发者在终端环境下创建用户友好的应用程序的过程,提供了丰富的功能和灵活的布局选项。
尽管ncurses库可以创建一些图形效果,但它仍然是基于文本的,不能与传统的图形界面库(如GTK+、Qt)相比。它更适用于需要在终端中运行的应用程序,或者在资源有限的环境中开发界面。
2.为什么需要ncurse图形库
C语言的库函数无法实现按下方向键就实现蛇改变方向,所以需要ncurse图形库
3.ncurse实现上下左右获取
1.使用ncurse图形库需包含curses.h头文件
在C语言编程中,curses.h是一个用于控制终端屏幕输出和输入的库。
在使用curses库进行屏幕控制之前,必须首先调用initscr()函数进行初始化。同时,在程序结束时,应该调用endwin()函数来关闭curses库并还原终端的原始设置。
需要注意的是,使用curses库进行屏幕控制需要在支持curses库的终端中运行程序,因为不是所有终端都支持该库。
2.使用ncurse图形库中几个函数重要作用
1.initscr函数
initscr()函数是curses库中的一个重要函数,它用于初始化curses库,并设置终端屏幕以进行屏幕控制。
initscr函数的功能:
1.初始化curses库:调用initscr()函数将初始化curses库,使其准备好在屏幕上进行控制。这意味着你可以使用curses库提供的各种函数来控制终端屏幕的输出和输入。
2.设置终端属性:initscr()函数会将终端设置为"可监控模式"。在这种模式下,curses库可以控制光标的位置、文本的颜色、窗口的大小等终端屏幕的属性。
3.创建stdscr对象:initscr()函数会创建一个名为stdscr的对象,它表示整个屏幕窗口。你可以使用stdscr对象来进行各种屏幕控制操作,如移动光标、输出文本等。
2.endwin函数
endwin()函数是curses库中的一个函数,它用于关闭curses库并恢复终端的原始设置。
endwin()函数功能:
1.关闭curses库:调用endwin()函数将关闭curses库,使其停止对终端屏幕进行控制。这意味着你不能再使用curses库提供的函数进行屏幕控制操作。
2.恢复终端设置:endwin()函数会将终端恢复为程序开始执行之前的状态,包括光标位置、终端属性、窗口大小等。这样可以确保程序结束后,终端保持在原始的状态,而不会影响其他的终端操作。
通常情况下,在程序的最后,都会调用endwin()函数来关闭curses库并还原终端的原始设置。这样可以确保程序执行完成后,终端回到正常的控制状态,避免对其他终端操作造成影响。
需要注意的是,调用endwin()函数后,你将不能再使用curses库提供的函数进行屏幕控制操作,除非再次调用initscr()函数重新初始化curses库。因此,endwin()函数通常在程序的最后调用,以确保在其后不再需要使用curses库进行屏幕控制。
3.keypad函数
keypad()函数是curses库中的一个函数,它用于配置终端以接收功能键和特殊键的输入。
keypad()函数功能:
1.启用功能键和特殊键的输入:调用keypad()函数,将终端配置为接收功能键(如F1、F2等)和特殊键(如方向键、Home键等)的输入。这样,当用户按下这些键时,程序将能够检测到对应的键值。
2.返回特殊键的键值:在启用keypad()函数后,使用getch()函数读取用户输入时,特殊键的按下将返回一个特殊的键值。例如,当用户按下方向键上时,getch()函数将返回一个表示方向键上的键值,以便程序可以根据这个键值来执行相应的操作。
使用keypad()函数前,你需要调用initscr()函数进行curses库的初始化。而且,在程序结束前,应该调用endwin()函数来关闭curses库。
需要注意的是,不是所有终端都支持通过curses库来接收功能键和特殊键的输入。因此,使用keypad()函数前应该检查终端是否支持,可以使用has_key()函数来判断。如果不支持,你可以通过其他方式来处理这些键值的输入。
要使用curses.h库中的keypad函数,你需要执行以下步骤:
1.在程序中包含curses.h头文件:#include <curses.h>
2.在初始化curses库之后,调用keypad函数来启用功能键和特殊键的输入。通常在调用initscr函数之后进行:
initscr();
keypad(stdscr, TRUE);
这里的stdscr是一个表示整个屏幕窗口的对象,通过keypad函数将其配置为接收功能键和特殊键的输入。
3.使用getch函数来读取用户输入,并根据返回的键值进行相应的操作。例如,检测用户是否按下方向键上:
int input = getch();
if (input == KEY_UP) {
// 执行方向键上的操作
}
在这个例子中,KEY_UP代表方向键上的键值,你可以使用它来判断用户是否按下了方向键上,并相应地处理。
4.结束程序时,记得调用endwin函数来关闭curses库以及恢复终端的原始设置:endwin();
5.完整的示例代码如下:
#include <curses.h>
int main() {
initscr();
keypad(stdscr, TRUE);
int input = getch();
if (input == KEY_UP) {
// 执行方向键上的操作
}
endwin();
return 0;
}
这是一个简单的示例,展示了如何使用curses.h中的keypad函数来处理方向键的输入。你可以根据需要扩展并适应其他功能键和特殊键的处理。记得在使用curses库之前和之后,都要调用必要的函数来初始化和关闭curses库。
4.getch函数
getch函数用于从终端获取用户输入的单个字符。它会立即读取用户输入的字符,而不需要用户按下回车键。
getch函数是一个用于获取用户输入的函数。它会等待用户输入一个字符,并返回该字符的ASCII码值。
getch函数通常与其他curses库函数一起使用,用于实现交互式的终端程序。
5.printw函数
printw函数用于在当前光标位置打印格式化的字符串。它类似于printf函数,但是会将输出的内容显示在终端上,而不是输出到标准输出流。
printw函数的原型如下:
int printw(const char *format, …);
其中,format参数是一个格式化字符串,可以包含类似于printf函数中的格式化占位符,如"%d"、“%f”、"%s"等。除了格式化字符串外,printw函数还可以接受额外的参数,用于替换格式化字符串中的占位符。
下面是一个简单的示例程序,演示了如何使用printw函数:
#include <curses.h>
int main() {
// 初始化curses库
initscr();
// 打印格式化的字符串
printw("Hello, %s!\n", "world");
// 刷新屏幕
refresh();
// 等待用户按下任意键后退出
getch();
// 结束curses库
endwin();
return 0;
}
在这个示例中,首先通过调用initscr()函数初始化curses库。然后使用printw函数打印格式化的字符串,其中的"%s"占位符会被后面的"world"字符串替换。接着调refresh()函数刷新屏幕,使打印的字符串立即显示在终端上。最后使用getch()函数等待用户按下任意键后退出程序,并通过endwin()函数结束curses库的使用。
3.ncurse图形库获取上下左右的值
#define KEY_DOWN 0402
#define KEY_UP 0403
#define KEY_LEFT 0404
#define KEY_RIDHT 0405
4.ncurse实现上下左右获取程序代码
过程1
#include <curses.h>
/*
时间: 2023年8月1日23:42:47
程序功能:过程1
*/
int main()
{
char c;
initscr();
while(1)
{
c = getch();
printw("you input : %c\n",c);
}
endwin();
return 0;
}
过程2
#include <curses.h>
/*
时间: 2023年8月2日00:06:41
程序功能:过程2
*/
int main()
{
int key;
initscr();
keypad(stdscr,1);
while(1)
{
key = getch();
printw("you input: %d\n",key);
}
endwin();
return 0;
}
完整程序代码
#include <curses.h>
/*
时间: 2023年8月1日22:31:06
程序功能:ncurse获取上下左右
*/
int main()
{
int key;
initscr();
keypad(stdscr,1);
while(1)
{
key = getch();
switch(key)
{
case KEY_DOWN: printw("DOWN\n"); break;
case KEY_UP: printw("UP\n"); break;
case KEY_LEFT: printw("LEFTn"); break;
case KEY_RIGHT: printw("RIGHT\n"); break;
}
}
endwin();
return 0;
}
4.贪吃蛇地图实现
1.地图规划
1.地图大小为20*20
2.地图竖直方向上的边界"|" 地图水平方向上的边界"–" 贪吃蛇的身子"[]" 贪吃蛇的食物"##"
3.每两个’-‘和一个’|'组成一个格子
4.其中从第1行到第18行每行为首尾为’|‘,中间为空。第一行和十九全为’-‘首尾为’|',
2.地图编程实现
1.地图实现一
1.过程一打印20*20个#号
#include <curses.h>
/*
时间: 2023年8月3日00:03:17
程序功能:过程1
*/
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
void gamePic()
{
int line; //行
int column; //列
for(line=0; line<20; line++)
{
for(column=0; column<20; column++)
{
printw("##");
}
printw("\n");
}
}
int main()
{
initNcurse();
gamePic();
getch();
endwin();
return 0;
}
2.过程二打印20个’–‘和2个’|’
#include <curses.h>
/*
时间: 2023年8月3日00:19:17
程序功能:过程2
*/
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
void gamePic()
{
int line; //行
int column; //列
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
}
}
}
}
int main()
{
initNcurse();
gamePic();
getch();
endwin();
return 0;
}
3.最终实现打印第一行为20个’–‘,第二行为首尾’|‘,中间为19个’两个空格’,目的是实现地图的第0行
#include <curses.h>
/*
时间: 2023年8月3日00:33:22
程序功能:地图实现一
*/
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
void gamePic()
{
int line; //行
int column; //列
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else
{
printw(" ");
}
}
printw("\n");
}
}
}
int main()
{
initNcurse();
gamePic();
getch();
endwin();
return 0;
}
2.贪吃蛇地图实现结束
1核心思想
打印19行首尾为’|‘中间为19个两个空格(目的是贪吃蛇死亡撞墙判断)最后一行打印20个’–'。
2.程序代码
#include <curses.h>
/*
时间: 2023年8月3日02:39:25
程序功能:贪吃蛇地图实现结束
*/
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
void gamePic()
{
int line; //行
int column; //列
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line>0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
}
}
int main()
{
initNcurse();
gamePic();
getch();
endwin();
return 0;
}
4.地图优化及算法说明
1.核心思想
把第一行打印的首尾为’|',中间为’两个空格’和后面打印的第2行到第20行的一起打印,使代码利用率变高。
2.程序代码
#include <curses.h>
/*
时间: 2023年8月3日21:15:45
程序功能:地图算法优化
*/
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
void gamePic()
{
int line; //行
int column; //列
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
}
}
int main()
{
initNcurse();
gamePic();
getch();
endwin();
return 0;
}
5.显示贪吃蛇身子的一个节点
1.贪吃蛇身子节点
定义一个贪吃蛇结构体,里面包含
1.行坐标
2.列坐标
3.下一节点的位置(地址/指针)
struct Snake //定义一个贪吃蛇结构体
{
int line;
int column;
struct Snake *next;
};
2.贪吃蛇身子显示
如何显示蛇身子的一个节点:
设该节点:行坐标2,列坐标为2
struct Snake node1 = {2,2,NULL}; //定义一个贪吃蛇节点
if(node1.line==line && node1.column==column)
{
printw("[]");
}
3.完整程序代码
#include <curses.h>
/*
时间: 2023年8月4日22:28:58
程序功能:显示贪吃蛇身子的一个节点
*/
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
struct Snake //定义一个贪吃蛇结构体
{
int line;
int column;
struct Snake *next;
};
struct Snake node1 = {2,2,NULL}; //定义一个贪吃蛇节点
void gamePic()
{
int line; //行
int column; //列
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else if(node1.line==line && node1.column==column)
{
printw("[]");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
}
}
int main()
{
initNcurse();
gamePic();
getch();
endwin();
return 0;
}
6.显示贪吃蛇完整身子
1.多节点如何构成一条蛇,关键在于节点坐标关系
2.程序代码
1.过程一:先实现单个身子节点显示,具体思路为:封装一个函数hasSnakeNode,在函数中对行与列进行匹配比较,相同返回1,不相同返回0.
1.函数代码
int hasSnakeNode(int line,int column)
{
if(node1.line==line && node1.column==column)
{
return 1;
}
return 0;
}
2.完整代码
#include <curses.h>
/*
时间: 2023年8月5日01:16:52
程序功能:过程
*/
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
struct Snake //定义一个贪吃蛇结构体
{
int line;
int column;
struct Snake *next;
};
struct Snake node1 = {2,2,NULL};
struct Snake node2 = {2,3,NULL};
struct Snake node3 = {2,4,NULL};
struct Snake node4 = {2,5,NULL};
int hasSnakeNode(int line,int column)
{
if(node1.line==line && node1.column==column)
{
return 1;
}
return 0;
}
void gamePic()
{
int line;
int column;
for(line=0; line<20; line++)
{
if(line==0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else if(hasSnakeNode(line,column))
{
printw("[]");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
}
}
int main()
{
node1.next = &node2;
node2.next = &node3;
node3.next = &node4;
initNcurse();
gamePic();
getch();
endwin();
return 0;
}
2.最终 利用链表,定义几个节点,把这几个节点给连接起来,形成链表。最终利用链表的遍历,对遍历后的链表节点内的行与列与gamePic函数中的行与列进行比较,相同返回1,不相同返回0。
1.链表连接代码
struct Snake //定义一个贪吃蛇结构体
{
int line;
int column;
struct Snake *next;
};
struct Snake node1 = {2,2,NULL};
struct Snake node2 = {2,3,NULL};
struct Snake node3 = {2,4,NULL};
struct Snake node4 = {2,5,NULL};
node1.next = &node2;
node2.next = &node3;
node3.next = &node4;
2.判断是否有贪吃蛇身子函数
int hasSnakeNode(int line,int column)
{
struct Snake *p = &node1;
while(p != NULL)
{
if(p->line==line && p->column==column)
{
return 1;
}
p = p->next;
}
return 0;
}
3.最终程序代码
#include <curses.h>
/*
时间: 2023年8月5日01:16:52
程序功能:过程
*/
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
struct Snake //定义一个贪吃蛇结构体
{
int line;
int column;
struct Snake *next;
};
struct Snake node1 = {2,2,NULL};
struct Snake node2 = {2,3,NULL};
struct Snake node3 = {2,4,NULL};
struct Snake node4 = {2,5,NULL};
int hasSnakeNode(int line,int column)
{
struct Snake *p = &node1;
while(p != NULL)
{
if(p->line==line && p->column==column)
{
return 1;
}
p = p->next;
}
return 0;
}
void gamePic()
{
int line;
int column;
for(line=0; line<20; line++)
{
if(line==0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else if(hasSnakeNode(line,column))
{
printw("[]");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
}
}
int main()
{
node1.next = &node2;
node2.next = &node3;
node3.next = &node4;
initNcurse();
gamePic();
getch();
endwin();
return 0;
}
7.显示贪吃蛇完整身子改进
1.改进思路
1.利用动态创建链表来创建贪吃蛇的身子。现将链表头和尾分别定义成全局变量。再在函数中使用,防止错误过多。
struct Snake *head;
struct Snake *tail;
2.封装一个初始化贪吃蛇函数initSnake
void initSnake()
{
head = (struct Snake *)malloc(sizeof(struct Snake));
head->line = 2;
head->column = 2;
head->next = NULL;
tail = head;
addNode(); //在这里每增加一个addNode函数,贪吃蛇多一个[]
}
3.封装一个增加节点函数addNode
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
new->line = tail->line;
new->column = tail->column+1;
new->next = NULL;
tail->next = new; //把新节点new接在tail的后面
tail = new; //把最后的节点tail给替换成new;
}
2.完整程序代码
#include <curses.h>
#include <stdlib.h>
/*
时间: 2023年8月5日23:20:03
程序功能:显示贪吃蛇完整身子改进
*/
struct Snake //定义一个贪吃蛇结构体
{
int line;
int column;
struct Snake *next;
};
struct Snake *head;
struct Snake *tail;
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int line,int column)
{
struct Snake *p = head;
while(p != NULL)
{
if(p->line==line && p->column==column)
{
return 1;
}
p = p->next;
}
return 0;
}
void gamePic()
{
int line;
int column;
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else if(hasSnakeNode(line,column))
{
printw("[]");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
}
}
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
new->line = tail->line;
new->column = tail->column+1;
new->next = NULL;
tail->next = new; //把新节点new接在tail的后面
tail = new; //把最后的节点tail给替换成new;
}
void initSnake()
{
head = (struct Snake *)malloc(sizeof(struct Snake));
head->line = 2;
head->column = 2;
head->next = NULL;
tail = head;
addNode(); //在这里每增加一个addNode函数,贪吃蛇多一个[]
}
int main()
{
initNcurse();
initSnake();
gamePic();
getch();
endwin();
return 0;
}
8.贪吃蛇向右移动
1.贪吃蛇向右移动核心思路
1.向右移动演示图
2.移动算法演示图
1.移动算法思路路及程序实现
1.思路
可以定义一个变量用于存放键入的方向键右 ;用一个死循环来执行移动函数,移动贪吃蛇身子函数执行条件为是否键入方向键右键,键入为右,则执行,反之则不。
2.程序实现
1.在移动贪吃蛇身子函数中分别调用从已有的节点后面添加节点的函数
和调用删除头节点函数。
2.删除头节点函数如何实现呢?可以定义一个结构体Snake指针变量p并指向节点头head。下一步不head中的next赋给head,实现删除链表头,因为head中的next指向下一节点的。最后利用free函数释放掉p所指向的内存空间。
3.移动贪吃蛇身子函数
void moveNode()
{
addNode();
delNode();
}
4.末尾添加节点函数
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
new->line = tail->line;
new->column = tail->column+1;
new->next = NULL;
tail->next = new;
tail = new;
}
5.删除头节点函数
void delNode()
{
struct Snake *p = head;
head = head->next;
free(p);
}
2.完整程序代码
#include <curses.h>
#include <stdlib.h>
/*
时间: 2023年8月7日22:27:11
程序功能:贪吃蛇向右移动
*/
struct Snake
{
int line;
int column;
struct Snake *next;
};
struct Snake *head;
struct Snake *tail;
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int line,int column)
{
struct Snake *p = head;
while(p != NULL)
{
if(p->line==line && p->column==column)
{
return 1;
}
p = p->next;
}
return 0;
}
void gamePic()
{
int line;
int column;
move(0,0); //把光标移动到0,0的位置,实现在相同位置下显示贪吃蛇界面。
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else if(hasSnakeNode(line,column))
{
printw("[]");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
printw("By MaGe");
printw("\n");
}
}
}
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
new->line = tail->line;
new->column = tail->column+1;
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake()
{
head = (struct Snake *)malloc(sizeof(struct Snake));
head->line = 2;
head->column = 2;
head->next = NULL;
tail = head;
addNode();
}
void delNode()
{
struct Snake *p = head;
head = head->next;
free(p);
}
void moveNode()
{
addNode();
delNode();
}
int main()
{
int con;
initNcurse();
initSnake();
gamePic();
while(1)
{
con = getch();
if(con == KEY_RIGHT)
{
moveNode();
gamePic(); //重新输出贪吃蛇界面,实现显示贪吃蛇移动后的结果
}
}
getch();
endwin();
return 0;
}
9.贪吃蛇不想活了撞墙找死
1.核心思路
1.主体思路
1.通过在移动贪吃蛇身子函数内用if判断蛇是否撞墙,撞墙后,初始化贪吃蛇身子。初识化中又会遇到动态创建的链表内存释放问题。
void moveNode()
{
addNode();
delNode();
if(tail->line==0 || tail->column==0 || tail->line==20 || tail->column==20)
{
freeSnake();
initSnake();
}
}
2.协助思路
1.动态创建链表内存释放思路和删除头节点一样,只不过是一直删除到最后指针变量head指向为空时即可。
void freeSnake()
{
struct Snake *p;
while(head != NULL)
{
p = head;
head = head->next;
free(p);
}
}
2.程序代码
1.不释放内存代码
#include <curses.h>
#include <stdlib.h>
/*
时间: 2023年8月8日00:28:13
程序功能:过程1
*/
struct Snake
{
int line;
int column;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int line,int column)
{
struct Snake *p = head;
while(p != NULL)
{
if(p->line==line && p->column==column)
{
return 1;
}
p = p->next;
}
return 0;
}
void gamePic()
{
int line;
int column;
move(0,0);
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else if(hasSnakeNode(line,column))
{
printw("[]");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
printw("By MaGe");
printw("\n");
}
}
}
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
new->line = tail->line;
new->column = tail->column+1;
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake()
{
head = (struct Snake *)malloc(sizeof(struct Snake));
head->line = 1;
head->column = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
}
void delNode()
{
struct Snake *p = head;
head = head->next;
free(p);
}
void moveNode()
{
addNode();
delNode();
if(tail->line==0 || tail->column==0 || tail->line==20 || tail->column==20)
{
initSnake();
}
}
int main()
{
int con;
initNcurse();
initSnake();
gamePic();
while(1)
{
con = getch();
if(con == KEY_RIGHT)
{
moveNode();
gamePic();
}
}
getch();
endwin();
return 0;
}
2.释放内存代码
#include <curses.h>
#include <stdlib.h>
/*
时间: 2023年8月8日02:04:15
程序功能:贪吃蛇不想活了撞墙死了
*/
struct Snake
{
int line;
int column;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int line,int column)
{
struct Snake *p = head;
while(p != NULL)
{
if(p->line==line && p->column==column)
{
return 1;
}
p = p->next;
}
return 0;
}
void gamePic()
{
int line;
int column;
move(0,0);
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else if(hasSnakeNode(line,column))
{
printw("[]");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
printw("By MaGe");
printw("\n");
}
}
}
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
new->line = tail->line;
new->column = tail->column+1;
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake()
{
head = (struct Snake *)malloc(sizeof(struct Snake));
head->line = 1;
head->column = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
}
void delNode()
{
struct Snake *p = head;
head = head->next;
free(p);
}
void freeSnake()
{
struct Snake *p;
while(head != NULL)
{
p = head;
head = head->next;
free(p);
}
}
void moveNode()
{
addNode();
delNode();
if(tail->line==0 || tail->column==0 || tail->line==20 || tail->column==20)
{
freeSnake();
initSnake();
}
}
int main()
{
int con;
initNcurse();
initSnake();
gamePic();
while(1)
{
con = getch();
if(con == KEY_RIGHT)
{
moveNode();
gamePic();
}
}
getch();
endwin();
return 0;
}
10.贪吃蛇脱缰向右自行游走
1.几个重要函数
1.refresh函数
在curses.h头文件中,refresh()函数是用于刷新当前屏幕上的显示内容。屏幕上的内容可能已被更改或更新,但这些变化不会立即反映在屏幕上。refresh()函数的调用将更新屏幕上显示的内容,使其与内部表示一致。
具体来说,refresh()函数会将更新后的字符、属性和其他屏幕控制信息传送到屏幕上。这是因为 curses 库使用内部数据结构来存储和操作屏幕上的文本和样式。refresh()函数确保内部缓冲区中的内容正确显示在终端上。
一般来说,当您对屏幕进行更改后,必须使用refresh()函数才能使更改生效。这样,您就可以立即看到更改后的结果。
以下是 refresh() 函数的简单示例使用方式:
#include <curses.h>
int main() {
initscr(); // 初始化 curses 库
// 更改屏幕上的显示内容
mvprintw(5, 5, "Hello, world!");
refresh(); // 刷新屏幕,使更改生效
getch(); // 等待用户输入
endwin(); // 关闭 curses 库
return 0;
}
在这个示例中,mvprintw()函数用于将字符串 “Hello, world!” 移动到屏幕上的位置 (5, 5)。然后调用 refresh() 函数以使更改生效,然后使用 getch() 函数等待用户输入,最后使用 endwin() 函数关闭 curses 库。
请注意,上述示例中的函数调用顺序很重要,特别是 refresh() 函数必须在对屏幕进行更改后立即调用,然后才能看到更改后的结果。
2.sleep函数
sleep() 函数用于暂停程序的执行一段指定的时间。当调用 sleep(n) 时,程序将被挂起(即暂停执行),直到经过了 n 秒。注意,sleep() 函数的单位是秒。
以下是 sleep() 函数的简单示例使用方式:
#include <curses.h>
#include <unistd.h>
int main() {
initscr(); // 初始化 curses 库
// 显示一条消息
mvprintw(5, 5, "Hello, world!");
refresh(); // 刷新屏幕,使消息可见
sleep(2); // 暂停程序执行 2 秒
endwin(); // 关闭 curses 库
return 0;
}
在这个示例中,mvprintw() 函数用于将字符串 “Hello, world!” 移动到屏幕上的位置 (5, 5)。然后使用 refresh() 函数刷新屏幕以使消息可见。接着调用 sleep(2) 将暂停程序的执行 2 秒。这样,在 2 秒后,程序会继续执行并关闭 curses 库。
3.usleep函数
usleep() 函数用于暂停程序的执行一段指定的微秒数。当调用 usleep(n) 时,程序将会被挂起(即暂停执行),直到经过了 n 微秒。注意,usleep() 函数的单位是微秒(百万分之一秒)。
以下是 usleep() 函数的简单示例使用方式:
#include <curses.h>
#include <unistd.h>
int main() {
initscr(); // 初始化 curses 库
// 显示一条消息
mvprintw(5, 5, "Hello, world!");
refresh(); // 刷新屏幕,使消息可见
usleep(500000); // 暂停程序执行 500 毫秒(0.5 秒)
endwin(); // 关闭 curses 库
return 0;
}
在这个示例中,mvprintw() 函数用于将字符串 “Hello, world!” 移动到屏幕上的位置 (5, 5)。然后使用 refresh() 函数刷新屏幕以使消息可见。接着调用 usleep(500000)将暂停程序的执行 500 毫秒(即 0.5 秒)。然后程序会继续执行并关闭 curses 库。
请注意,usleep() 函数在 POSIX 标准中已经被弃用,应该使用更精确的nanosleep() 函数代替。nanosleep() 函数可以提供更细粒度的暂停时间控制。
2.完整程序代码
#include <curses.h>
#include <stdlib.h>
/*
时间: 2023年8月8日18:17:59
程序功能:贪吃蛇脱缰自行游走
*/
struct Snake
{
int line;
int column;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int line,int column)
{
struct Snake *p = head;
while(p != NULL)
{
if(p->line==line && p->column==column)
{
return 1;
}
p = p->next;
}
return 0;
}
void gamePic()
{
int line;
int column;
move(0,0);
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else if(hasSnakeNode(line,column))
{
printw("[]");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
printw("By MaGe");
printw("\n");
}
}
}
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
new->line = tail->line;
new->column = tail->column+1;
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake()
{
head = (struct Snake *)malloc(sizeof(struct Snake));
head->line = 1;
head->column = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
}
void delNode()
{
struct Snake *p = head;
head = head->next;
free(p);
}
void freeSnake()
{
struct Snake *p;
while(head != NULL)
{
p = head;
head = head->next;
free(p);
}
}
void moveNode()
{
addNode();
delNode();
if(tail->line==20 || tail->line==0 || tail->column==20 || tail->column==0)
{
freeSnake();
initSnake();
}
}
int main()
{
initNcurse();
initSnake();
gamePic();
while(1)
{
moveNode();
gamePic();
refresh(); //刷新显示界面
usleep(100000); //等待100ms后再次执行以上3句代码
}
getch();
endwin();
return 0;
}
11.贪吃蛇方向移动和刷新界面一起实现面临的问题
用于刷新贪吃蛇行走的while死循环和用于获取方向键的while死循环不能同时执行,要同时执行就得用到Linux系统编程线程的知识。
1.获取方向键while死循环
while(1)
{
key = getch();
switch(key)
{
case KEY_DOWN: printw("DOWN\n"); break;
case KEY_UP: printw("UP\n"); break;
case KEY_LEFT: printw("LEFTn"); break;
case KEY_RIGHT: printw("RIGHT\n"); break;
}
}
2.刷新贪吃蛇行走的while死循环
while(1)
{
moveNode();
gamePic();
refresh(); //刷新显示界面
usleep(100000); //等待100ms后再次执行以上3句代码
}
12.Linux线程概念引入及编程实现
1.什么是Linux线程
Linux线程是在Linux操作系统上创建和管理的执行单元。线程是进程内的子任务,它们共享相同的进程资源,包括代码段、数据段和打开的文件。与进程相比,线程更轻量级,它不拥有独立的地址空间,但可以独立地执行代码。在多线程的程序中,多个线程可以同时运行,从而实现并发执行。
Linux提供了多种线程模型,常见的包括POSIX线程(pthread)和Linux原生线程(NPTL)。线程可以通过调用系统函数或使用线程库如pthread库来创建和管理。线程的创建
和销毁、线程间的同步和通信、线程的调度等都是由操作系统来管理的。
使用线程可以使程序更高效地利用系统资源和处理器的多核能力,同时简化了编程模型。通过合理地设计和使用线程,可以实现并发的任务执行,提高程序的响应性和吞吐量。然而,在多线程编程中也需要注意线程安全和同步的问题,以避免竞态条件和数据访问冲突导致的错误。
2.如何用C语言实现Linux线程编程
下面是一个简单的示例代码,演示了如何在C语言中使用pthread库创建和管理线程:
#include <stdio.h>
#include <pthread.h>
// 线程函数
void* thread_function(void* arg) {
int thread_num = *(int*)arg;
printf("Hello from thread %d\n", thread_num);
pthread_exit(NULL);
}
int main() {
pthread_t thread_id;
int thread_num = 1;
// 创建新线程
int ret = pthread_create(&thread_id, NULL, thread_function, (void*)&thread_num);
if (ret != 0) {
printf("Failed to create thread\n");
return -1;
}
// 等待新线程结束
pthread_join(thread_id, NULL);
printf("Main thread exiting\n");
return 0;
}
上述代码中,thread_function 是线程的执行函数,其参数为 void* 类型。在这个例子中,线程函数仅简单地打印出线程编号。
在 main 函数中,首先使用 pthread_create 函数创建新线程,并将线程号存储在 thread_id 变量中。pthread_create 的第一个参数是用于存储线程ID的指针,第二个参数是线程属性(可以为 NULL),第三个参数是线程函数,第四个参数是传递给线程函数的参数。
然后,使用 pthread_join 函数等待新线程的结束。pthread_join 的第一个参数是线程ID,第二个参数是线程返回值的指针(如果不需要返回值,可以传入 NULL)。
最后,在主线程中输出一条消息后,程序结束。
要编译上述代码,你需要使用 -pthread 标志来链接pthread库,例如:
gcc -o program program.c -pthread
这只是一个简单的示例,实际的线程编程可能涉及更复杂的线程操作和同步机制。你可以参考pthread库的文档和其他资源,深入学习Linux线程编程。
3.线程创建
如果在主函数内创建一个线程,那么本程序就有两个线程,一个主线程,一个副线程。相当于是两个通道,通道内执行程序互不影响。
pthread_create(&thread_id, NULL, thread_function, (void芯)&thread_num);
pthread(p-s-red):线程关键字。create:创建。合起来就是线程创建。
&thread_id:线程的描述符;
NULL:不用管,一般都写NULL。
thread_function:线程函数,用于并行执行的函数。线程函数必须为void *类型
(void芯)&thread_num);:线程函数的参数,如果函数内没有形参,直接写NULL
使用线程必须包含头文件<pthread.h>
编译时必须链接线程库(pthread)
例如:
gcc program.c -pthread -o p1
4.线程实例
利用线程实现两个死循环同时执行
#include <stdio.h>
#include <pthread.h>
/*
时间: 2023年8月9日01:24:14
程序功能:利用线程实现两个死循环同时执行
*/
void *func1()
{
while(1)
{
puts("This is thread 1");
sleep(3);
}
}
void func2()
{
while(1)
{
puts("This is thread 2");
sleep(3);
}
}
int main()
{
pthread_t th1;
pthread_create(&th1,NULL,func1,NULL);
func2();
return 0;
}
利用线程实现三个死循环同时执行
#include <stdio.h>
#include <pthread.h>
/*
时间: 2023年8月9日01:44:03
程序功能:利用线程实现三个死循环同时执行
*/
void *func1()
{
while(1)
{
puts("This is thread 1");
sleep(3);
}
}
void *func2()
{
while(1)
{
puts("This is thread 2");
sleep(3);
}
}
int main()
{
pthread_t th1;
pthread_t th2;
pthread_create(&th1,NULL,func1,NULL);
pthread_create(&th2,NULL,func2,NULL);
while(1)
{
puts("This is main thread");
sleep(3);
}
return 0;
}
13.实现贪吃蛇四方向的风骚走位
1.重要知识#define
在C语言中,#define 是一个预处理指令,用于定义一个常量或者宏。
当在代码中使用 #define 时,预处理器会将指定的标识符替换为所定义的值或者代码片段,然后再进行编译。
定义常量:
#define CONSTANT_NAME value
这样,所有出现 CONSTANT_NAME 的地方都会被替换为 value。
例如:
#define PI 3.1415926
int main() {
double radius = 5.0;
double circumference = 2 * PI * radius;
return 0;
}
在这个例子中,PI 被定义为一个常量,所有出现 PI 的地方都会被替换为 3.1415926。
#define 还可以用于定义带有参数的宏,例如:
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main() {
int x = 5;
int y = 10;
int max = MAX(x, y);
return 0;
}
在这个例子中,MAX 被定义为一个宏,求取两个数的最大值。在 main 函数中调用 MAX(x, y) 时,预处理器会替换为 ((x) > (y) ? (x) : (y)),得到最大值为 10。
使用 #define 可以增强代码的可读性和可维护性,并且还可以减少代码中的魔法数,提高代码的可维护性。但是在使用时要遵循编码规范,避免宏定义导致的潜在问题。
2.实现思路
利用前面已运用的Linux线程知识,建立两个线程,一个实现刷新贪吃蛇界面,一个实现键盘方向键检测。
定义一个整型全局变量dir用于键盘检测输入方向键执行相应方向加节点操作的判断条件,因此会运用到#define预处理。分别定义为上下左右,替换的数值为1 2 3 4
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4
1.键盘检测到输入的是什么方向键时执行相应的添加贪吃蛇身子链表节点。
1.键盘检测实现相应操作算法
switch(dir)
{
case UP: new->line = tail->line-1;
new->column = tail->column; break;
case DOWN: new->line = tail->line+1;
new->column = tail->column; break;
case LEFT: new->line = tail->line;
new->column = tail->column-1; break;
case RIGHT: new->line = tail->line;
new->column = tail->column+1; break;
}
2.算法核心思维为,向上改变节点的行坐标减一,向下就改变节点的行坐标减一(上下都不会改列坐标)。向左改变节点的列坐标减一,向右改变节点的列坐标加一。(左右都不会改变行坐标)
2.完整程序代码
#include <curses.h>
#include <pthread.h>
#include <stdlib.h>
#define UP 1 //预处理,把出现UP的地方替换成为1
#define DOWN 2
#define LEFT 3
#define RIGHT 4
/*
时间: 2023年8月10日20:37:58
程序功能:实现贪吃蛇四方向的风骚走位
*/
struct Snake
{
int line;
int column;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
int key; //存放键盘获取的方向键
int dir; //判断键盘获取的方向键
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int line,int column)
{
struct Snake *p = head;
while(p != NULL)
{
if(p->line==line && p->column==column)
{
return 1;
}
p = p->next;
}
return 0;
}
void gamePic()
{
int line;
int column;
move(0,0);
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else if(hasSnakeNode(line,column))
{
printw("[]");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
printw("By MaGe! key = %d",key);
printw("\n");
}
}
}
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
new->next = NULL;
switch(dir)
{
case UP: new->line = tail->line-1;
new->column = tail->column; break;
case DOWN: new->line = tail->line+1;
new->column = tail->column; break;
case LEFT: new->line = tail->line;
new->column = tail->column-1; break;
case RIGHT: new->line = tail->line;
new->column = tail->column+1; break;
}
tail->next = new;
tail = new;
}
void initSnake()
{
dir = RIGHT;
head = (struct Snake *)malloc(sizeof(struct Snake));
head->line = 1;
head->column = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
}
void delNode()
{
struct Snake *p = head;
head = head->next;
free(p);
}
void freeSnake()
{
struct Snake *p;
while(head != NULL)
{
p = head;
head = head->next;
free(p);
}
}
void moveSnake()
{
addNode();
delNode();
if(head->line==0 || head->column==0 || head->line==19 || head->column==20)
{
freeSnake();
initSnake();
}
}
void *refreshInterface()
{
while(1)
{
moveSnake();
gamePic();
refresh();
usleep(120000);
}
}
void *changeDir() //改变贪吃蛇的方向函数
{
while(1)
{
key = getch();
switch(key)
{
case KEY_UP: dir = UP; break;
case KEY_DOWN: dir = DOWN; break;
case KEY_LEFT: dir = LEFT; break;
case KEY_RIGHT: dir = RIGHT; break;
}
}
}
int main()
{
pthread_t t1;
pthread_t t2;
initNcurse();
initSnake();
gamePic();
pthread_create(&t1,NULL,refreshInterface,NULL);
pthread_create(&t2,NULL,changeDir,NULL);
while(1);
getch();
endwin();
return 0;
}
14.绝对值方式解决不合理走位
1.两个重要函数
1.abs函数
在 <stdlib.h> 头文件中,abs() 函数的作用是计算一个整数的绝对值。
abs() 函数接受一个整型参数,并返回它的绝对值作为结果。如果参数是正数或零,函数返回参数本身;如果参数是负数,函数返回参数的相反数。
以下是 abs() 函数的示例使用方式:
#include <curses.h>
int main() {
initscr(); // 初始化 curses 库
int num = -5;
int absNum = abs(num); // 计算绝对值
printw("The absolute value of %d is %d", num, absNum);
endwin(); // 关闭 curses 库
return 0;
}
在这个示例中,我们使用 abs() 函数计算变量 num 的绝对值,并存储在 absNum 中。然后使用 printw() 函数在屏幕上显示 num 和 absNum 的值。
在上述示例中,abs(-5) 的结果是 5,因为 -5 的绝对值是 5。abs() 函数在 <stdlib.h> 头文件中定义,并作为标准 C 库函数被广泛使用。在使用 abs() 函数之前,需要包含 <stdlib.h> 头文件。
2.noecho函数
在 curses.h 头文件中,noecho() 函数用于禁止用户输入字符时在屏幕上显示输入的字符。
当调用 noecho() 函数后,用户在输入字符时,字符不会直接出现在屏幕上。这在需要用户输入敏感信息时非常有用,例如密码输入等。
以下是 noecho() 函数的简单示例使用方式:
#include <curses.h>
int main() {
initscr(); // 初始化 curses 库
noecho(); // 禁止字符回显
printw("Enter a password: ");
char password[20];
getstr(password); // 获取用户输入的字符串,但不显示在屏幕上
printw("\nYou entered: %s", password);
endwin(); // 关闭 curses 库
return 0;
}
在这个示例中,我们使用 noecho() 函数禁止字符回显。然后,使用 printw() 函数在屏幕上显示一条消息提示用户输入密码。接下来,使用 getstr() 函数获取用户输入的字符串,但不显示在屏幕上。最后,使用 printw() 函数显示用户输入的密码。
使用 noecho() 函数后,用户在输入密码时,输入字符不会在屏幕上显示。这有助于保护用户的敏感信息。当需要读取密码或任何其他用户输入时,应该考虑使用 noecho() 函数。使用完毕后,可以通过调用 echo() 函数重新启用字符回显。
2.核心思想
1.将预处理的定义上下分别互为相反数,左右互为相反数。这样当先按左时,dir = -2,再按又时,dir = 2;那么判断前后输入的按键就会得出绝对值一样,就不会执行向右。
2.判断函数
void turn(int direction) //绝对值判断方向方向变化函数。
{
if(abs(dir) != abs(direction))
{
dir = direction;
}
}
3.完整程序代码
#include <curses.h>
#include <pthread.h>
#include <stdlib.h>
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
/*
时间:2023年8月11日21:41:01
程序功能:绝对值方式解决不合理走位
*/
struct Snake
{
int line;
int column;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
int key;
int dir;
void initNcurse()
{
initscr();
keypad(stdscr,1);
noecho(); //禁止用户输入时在屏幕上显示输入的字符。目的是为了防止乱码显示。
}
int hasSnakeNode(int line,int column)
{
struct Snake * p = head;
while(p != NULL)
{
if(p->line==line && p->column==column)
{
return 1;
}
p = p->next;
}
return 0;
}
void gamePic()
{
int line;
int column;
move(0,0);
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else if(hasSnakeNode(line,column))
{
printw("[]");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
printw("By MaGe key = %d",key);
printw("\n");
}
}
}
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
switch(dir)
{
case UP: new->line = tail->line-1;
new->column = tail->column; break;
case DOWN: new->line = tail->line+1;
new->column = tail->column; break;
case LEFT: new->line = tail->line;
new->column = tail->column-1; break;
case RIGHT: new->line = tail->line;
new->column = tail->column+1; break;
}
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake()
{
dir = RIGHT;
head = (struct Snake *)malloc(sizeof(struct Snake));
head->line = 1;
head->column = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
}
void delNode()
{
struct Snake *p = head;
head = head->next;
free(p);
}
void freeSnake()
{
struct Snake *p;
while(head != NULL)
{
p = head;
head = head->next;
free(p);
}
}
void moveSnake()
{
addNode();
delNode();
if(head->line==0 || head->line==19 || head->column==0 || head->column==20)
{
freeSnake();
initSnake();
}
}
void *refreshInterface()
{
int time = 400000; //时间控制变量,随着游戏的进行,暂停时间在减小,速度加快。
while(1)
{
moveSnake();
gamePic();
refresh();
usleep(time);
time = time-1000;
if(time == 10000)
{
time = 400000;
}
}
}
void turn(int direction) //绝对值判断方向方向变化函数。
{
if(abs(dir) != abs(direction))
{
dir = direction;
}
}
void *changeDir()
{
while(1)
{
key = getch();
switch(key)
{
case KEY_UP: turn(UP); break;
case KEY_DOWN: turn(DOWN); break;
case KEY_LEFT: turn(LEFT); break;
case KEY_RIGHT: turn(RIGHT); break;
}
}
}
int main()
{
pthread_t t1;
pthread_t t2;
initNcurse();
initSnake();
gamePic();
pthread_create(&t1,NULL,refreshInterface,NULL);
pthread_create(&t2,NULL,changeDir,NULL);
while(1);
getch();
endwin();
return 0;
}
15.贪吃蛇吃饭了
1.重要关键字static
在C语言中,static关键字可以用于不同的上下文,具体取决于它被应用的位置。下面是static关键字的几种常见用法和作用:
1.函数内部的静态变量:
在函数内部声明的静态变量具有以下特点:
(1)静态变量在程序的整个生命周期内保持其值,而不是在每次函数调用时重新初始化。
(2)静态变量在函数调用之间保持其状态,因此可以用于跟踪函数的调用次数或记录先
前的函数操作。
(3)静态变量在函数内部是私有的,即只能在声明它的函数中访问。
2.文件作用域的静态变量:
在函数外部、文件内部声明的静态变量具有以下特点:
(1)文件作用域的静态变量在整个文件中可见,但对其他文件是不可见的。
(2)静态变量在程序的整个生命周期内保持其值。
(3)与函数内部的静态变量类似,文件作用域的静态变量的默认初始值为0,
只在首次赋值时进行初始化。
3.静态函数:
使用static关键字声明的函数被称为静态函数或内部函数,具有以下特点:
(1)静态函数只在声明它的文件中可见,无法被其他文件中的函数调用。
(2)静态函数的作用主要是实现文件的内部逻辑,避免与其他文件中的同名函
数发生冲突。
(3)静态函数通常被用作辅助函数,只在声明文件内部使用。
4.静态全局变量:
在文件外部、全局范围内使用static关键字声明的变量具有以下特点:
(1)静态全局变量的作用域仅限于声明它的文件内,对其他文件是不可见的。
(2)静态全局变量在整个程序的生命周期内保持其值,与普通全局变量不同的是,它的作用范围仅限于声明文件内部。
5.静态关键字的作用有助于控制变量和函数的可见性和生命周期,并提供了更灵活的程序设计选项。
2.核心思路
一、定义一个食物的全局结果变量food,再分别定义一个初始化food函数和一个判断food在哪个位置函数。然后在移动贪吃蛇身子函数中进行判断,如果蛇头(节点尾)走过食物,就添加新节点,反之则不。
二、贪吃蛇的食物
(1)食物关心的是位置及符号
(2)位置同样可以使用贪吃蛇节点结构体
(3)食物显示为’##’
初识化食物函数
void initFood()
{
static int line = 4;
static int column = 5;
food.line = line;
food.column = column;
line += 2;
column += 2;
}
判断食物在哪个位置函数
int hasFood(int line,int column)
{
if(food.line==line && food.column==column)
{
return 1;
}
return 0;
}
3.完整程序代码
#include <curses.h>
#include <pthread.h>
#include <stdlib.h>
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
/*
时间: 2023年8月12日16:11:00
程序功能:贪吃蛇吃饭了
*/
struct Snake
{
int line;
int column;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
struct Snake food;
int key;
int dir;
void initNcurse()
{
initscr();
keypad(stdscr,1);
noecho();
}
void initFood()
{
static int line = 4;
static int column = 5;
food.line = line;
food.column = column;
line += 2;
column += 2;
}
int hasSnakeNode(int line,int column)
{
struct Snake *p = head;
while(p != NULL)
{
if(p->line==line && p->column==column)
{
return 1;
}
p = p->next;
}
return 0;
}
int hasFood(int line,int column)
{
if(food.line==line && food.column==column)
{
return 1;
}
return 0;
}
void gamePic()
{
int line;
int column;
move(0,0);
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else if(hasSnakeNode(line,column))
{
printw("[]");
}
else if(hasFood(line,column))
{
printw("##");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
printw("By MaGe key = %d\n",key);
}
}
}
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
switch(dir)
{
case UP: new->line = tail->line-1;
new->column = tail->column; break;
case DOWN: new->line = tail->line+1;
new->column = tail->column; break;
case LEFT: new->line = tail->line;
new->column = tail->column-1; break;
case RIGHT: new->line = tail->line;
new->column = tail->column+1; break;
}
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake()
{
dir = RIGHT;
initFood();
head = (struct Snake *)malloc(sizeof(struct Snake));
head->line = 1;
head->column = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
}
void delNode()
{
struct Snake *p = head;
head = head->next;
free(p);
}
void freeSnake()
{
struct Snake *p;
while(head != NULL)
{
p = head;
head = head->next;
free(p);
}
}
void moveSnake()
{
addNode();
if(hasFood(tail->line,tail->column))
{
initFood();
}
else
{
delNode();
}
if(head->line==0 || head->column==0 || head->line==19 || head->column==20)
{
freeSnake();
initSnake();
}
}
void *refreshInterface()
{
int time = 300000;
while(1)
{
moveSnake();
gamePic();
refresh();
usleep(time);
time -= 1000;
if(time == 15000)
{
time = 300000;
}
}
}
void turn(int direction)
{
if(abs(dir) != abs(direction))
{
dir = direction;
}
}
void *changeDir()
{
while(1)
{
key = getch();
switch(key)
{
case KEY_UP: turn(UP); break;
case KEY_DOWN: turn(DOWN); break;
case KEY_LEFT: turn(LEFT); break;
case KEY_RIGHT: turn(RIGHT); break;
}
}
}
int main()
{
pthread_t t1;
pthread_t t2;
initNcurse();
initSnake();
gamePic();
pthread_create(&t1,NULL,refreshInterface,NULL);
pthread_create(&t2,NULL,changeDir,NULL);
while(1);
getch();
endwin();
return 0;
}
16.贪吃蛇食物位置随机
1.重要函数
1.rand函数
在C语言中,rand函数是一个标准库函数,其作用是生成一个伪随机数。具体来说,rand函数会返回一个介于0和RAND_MAX之间的随机整数。
rand函数的使用步骤如下:
包含头文件:#include <stdlib.h>
调用rand函数:int num = rand();
需要注意的是,rand函数是伪随机数生成器,它生成的随机数序列是基于某个初始种子值的,而非真正的随机。因此,每次程序运行时生成的随机数序列是相同的,除非
你在生成随机数之前使用srand函数设置不同的种子值。
下面是一些常用的使用场景和技巧:
1.生成指定范围的随机数:可以使用取模运算将生成的随机数限制在一个特定的范围内。例如,要生成0到9之间的随机数,可以使用(rand() % 10)。
2.生成不同的随机序列:通过使用srand函数设置不同的种子值,可以使每次运行程序时生成不同的随机数序列。常用的种子设置方法包括使用当前时间srand(time(NULL)) 或者使用进程ID srand(getpid())。
3.初始化随机数种子:如果你想要重新开始一个已有的随机数序列,可以使用srand函数重新设置随机数的种子值。这在调试和测试中可能很有用。
4.需要注意的是,rand函数生成的随机数并不能满足加密或安全性要求,如果需要强加密级别的随机数,应该使用加密库提供的相应函数。
2.核心思想
利用rand函数制造的大随机数对20取余,就能获取到0到20的随机数。因为地图为20*20的大小,所以边界为0~20。rand函数用在初始化食物函数中,让line和column的值随机,因此生成的坐标为随机坐标。
修改后的食物初识化函数
void initFood()
{
int line = rand()%20;
int column = rand()%20;
food.line = line;
food.column = column;
}
3.完整程序代码
#include <curses.h>
#include <pthread.h>
#include <stdlib.h>
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
/*
时间: 2023年8月12日21:04:41
程序功能:贪吃蛇食物随机
*/
struct Snake
{
int line;
int column;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
struct Snake food;
int key;
int dir;
void initNcurse()
{
initscr();
keypad(stdscr,1);
noecho();
}
void initFood()
{
int line = rand()%20;
int column = rand()%20;
food.line = line;
food.column = column;
}
int hasSnakeNode(int line,int column)
{
struct Snake *p = head;
while(p != NULL)
{
if(p->line==line && p->column==column)
{
return 1;
}
p = p->next;
}
return 0;
}
int hasFood(int line,int column)
{
if(food.line==line && food.column==column)
{
return 1;
}
return 0;
}
void gamePic()
{
int line;
int column;
move(0,0);
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else if(hasSnakeNode(line,column))
{
printw("[]");
}
else if(hasFood(line,column))
{
printw("##");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
printw("By MaGe key = %d food.line = %d food.column = %d\n"
,key,food.line,food.column);
}
}
}
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
switch(dir)
{
case UP: new->line = tail->line-1;
new->column = tail->column; break;
case DOWN: new->line = tail->line+1;
new->column = tail->column; break;
case LEFT: new->line = tail->line;
new->column = tail->column-1; break;
case RIGHT: new->line = tail->line;
new->column = tail->column+1; break;
}
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake()
{
dir = RIGHT;
initFood();
head = (struct Snake *)malloc(sizeof(struct Snake));
head->line = 1;
head->column = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
}
void delNode()
{
struct Snake *p = head;
head = head->next;
free(p);
}
void freeSnake()
{
struct Snake *p;
while(head != NULL)
{
p = head;
head = head->next;
free(p);
}
}
void moveSnake()
{
addNode();
if(hasFood(tail->line,tail->column))
{
initFood();
}
else
{
delNode();
}
if(head->line==0 || head->column==0 || head->line==19 || head->column==20)
{
freeSnake();
initSnake();
}
}
void *refreshInterface()
{
int time = 300000;
while(1)
{
moveSnake();
gamePic();
refresh();
usleep(time);
time -= 1000;
if(time == 15000)
{
time = 300000;
}
}
}
void turn(int direction)
{
if(abs(dir) != abs(direction))
{
dir = direction;
}
}
void *changeDir()
{
while(1)
{
key = getch();
switch(key)
{
case KEY_UP: turn(UP); break;
case KEY_DOWN: turn(DOWN); break;
case KEY_LEFT: turn(LEFT); break;
case KEY_RIGHT: turn(RIGHT); break;
}
}
}
int main()
{
pthread_t t1;
pthread_t t2;
initNcurse();
initSnake();
gamePic();
pthread_create(&t1,NULL,refreshInterface,NULL);
pthread_create(&t2,NULL,changeDir,NULL);
while(1);
getch();
endwin();
return 0;
}
17.贪吃蛇撞墙死亡和想不开咬死自己结束游戏代码优化
1.优化内容
1.贪吃蛇在地图上最上面和最下面不能存活,只需要把撞墙判断的if
中的条件给修改一下就可以了,把head->line == 0 修改为head->line<0;
把head->line==19修改为head->line==20;
2.贪吃蛇撞到自己不会死亡修改为会死亡。
单独建立一个判断贪吃蛇死亡函数,把对撞墙死亡判读的语句放入本函数。
建立一个新的判断算法以实现对碰撞自身死亡判断。
碰撞自身死亡算法:
struct Snake *p = head; 定义一个结构体Snake指针变量p,p指向head,
head为贪吃蛇身子的尾(链表的头)。
while(p->next != NULL) //让链表遍历到tail的前一个时结束,不然会一直返回1.
if(p->line==tail->line && p->column==tail->column) 判断p中的行与贪吃蛇头的行
坐标是否相同,判断p中的列与贪吃蛇的列是否相同。tail为贪吃蛇的头。
p = p->next; 让p的地址等于下一个节点的地址,实现链表的历循。
2.死亡判断函数
int ifSnakeDie()
{
struct Snake *p = head;
if(head->line<0 || head->column==0 || head->line==20 || head->column==20)
{
return 1;
}
while(p->next != NULL) //让链表遍历到tail的前一个时结束,不然会一直返回1.
{
if(p->line==tail->line && p->column==tail->column)
{
return 1;
}
p = p->next;
}
return 0;
}
3.完整程序代码
#include <curses.h>
#include <pthread.h>
#include <stdlib.h>
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
/*
时间: 2023年8月12日22:56:58
程序功能:贪吃蛇撞墙死亡和想不开咬死自己结束游戏代码优化
*/
struct Snake
{
int line;
int column;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
struct Snake food;
int key;
int dir;
void initNcurse()
{
initscr();
keypad(stdscr,1);
noecho();
}
void initFood()
{
int line = rand()%20;
int column = rand()%20;
food.line = line;
food.column = column;
}
int hasSnakeNode(int line,int column)
{
struct Snake *p = head;
while(p != NULL)
{
if(p->line==line && p->column==column)
{
return 1;
}
p = p->next;
}
return 0;
}
int hasFood(int line,int column)
{
if(food.line==line && food.column==column)
{
return 1;
}
return 0;
}
void gamePic()
{
int line;
int column;
move(0,0);
for(line=0; line<20; line++)
{
if(line == 0)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
}
if(line>=0 && line<=19)
{
for(column=0; column<=20; column++)
{
if(column==0 || column==20)
{
printw("|");
}
else if(hasSnakeNode(line,column))
{
printw("[]");
}
else if(hasFood(line,column))
{
printw("##");
}
else
{
printw(" ");
}
}
printw("\n");
}
if(line == 19)
{
for(column=0; column<20; column++)
{
printw("--");
}
printw("\n");
printw("By MaGe key = %d food.line = %d food.column = %d\n"
,key,food.line,food.column);
}
}
}
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
switch(dir)
{
case UP: new->line = tail->line-1;
new->column = tail->column; break;
case DOWN: new->line = tail->line+1;
new->column = tail->column; break;
case LEFT: new->line = tail->line;
new->column = tail->column-1; break;
case RIGHT: new->line = tail->line;
new->column = tail->column+1; break;
}
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake()
{
dir = RIGHT;
initFood();
head = (struct Snake *)malloc(sizeof(struct Snake));
head->line = 1;
head->column = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
}
void delNode()
{
struct Snake *p = head;
head = head->next;
free(p);
}
void freeSnake()
{
struct Snake *p;
while(head != NULL)
{
p = head;
head = head->next;
free(p);
}
}
int ifSnakeDie()
{
struct Snake *p = head;
if(head->line<0 || head->column==0 || head->line==20 || head->column==20)
{
return 1;
}
while(p->next != NULL) //让链表遍历到tail的前一个时结束,不然会一直返回1.
{
if(p->line==tail->line && p->column==tail->column)
{
return 1;
}
p = p->next;
}
return 0;
}
void moveSnake()
{
addNode();
if(hasFood(tail->line,tail->column))
{
initFood();
}
else
{
delNode();
}
if(ifSnakeDie())
{
freeSnake();
initSnake();
}
}
void *refreshInterface()
{
int time = 300000;
while(1)
{
moveSnake();
gamePic();
refresh();
usleep(time);
time -= 1000;
if(time == 15000)
{
time = 300000;
}
}
}
void turn(int direction)
{
if(abs(dir) != abs(direction))
{
dir = direction;
}
}
void *changeDir()
{
while(1)
{
key = getch();
switch(key)
{
case KEY_UP: turn(UP); break;
case KEY_DOWN: turn(DOWN); break;
case KEY_LEFT: turn(LEFT); break;
case KEY_RIGHT: turn(RIGHT); break;
}
}
}
int main()
{
pthread_t t1;
pthread_t t2;
initNcurse();
initSnake();
gamePic();
pthread_create(&t1,NULL,refreshInterface,NULL);
pthread_create(&t2,NULL,changeDir,NULL);
while(1);
getch();
endwin();
return 0;
}
结束语
您能在百忙之中抽出时间看到这里,我很感谢,也很高兴。贪吃蛇这个游戏,需要你已经掌握了C语言才能完成,Linux系统内要封装了对应的图形库,线程库,VIM编程工具才能完成。可以去网上找相关已经封装好的Linux系统安装到VM虚拟机中就可以进行C语言贪吃蛇游戏的开发。