<h1>一 课程目的</h1>
<p>主要检验C语言的应用,其中包括</p>
<p>1.常规编程</p>
<p>2.指针的用法</p>
<p>3.关键词的用法</p>
<p>4.结构体的用法</p>
<p>5.链表的用法</p>
<h1>二 贪吃蛇项目的实现</h1>
<h2>一丶搭建项目环境:</h2>
<h3>1.环境配置</h3>
<p>这是在linux系统环境下进行的项目,由于项目需要动态显示,我们可以在linux系统下进行ncurse环境的配置;然后可以开始项目编程,在编程过程中进行环境优化;</p>
<h2>二丶贪吃蛇运动范围</h2>
<p>首先要通过for循环嵌套的方式打出贪吃蛇的运动范围:</p>
<pre><code class='language-c' lang='c'>#include <curses.h>
void gamepic()
{
int hang;
int lie;
move(0,0);
for(hang=0;hang<22;hang++){
for(lie=0;lie<22;lie++){
if(hang == 0 && lie !=21){
printw("--");
}else if(hang==21 && lie !=21){
printw("--");
}else if(lie == 0 || lie == 21 && hang !=0 && hang !=21){
printw("|");
}else{
printw(" ");
}
}
printw("\n");
}
}
int main()
{
initscr();
gamepic();
getch();
endwin();
return 0;
}
</code></pre>
<p>这是一个显示20*20方格的代码;</p>
<h2>三丶显示贪吃蛇</h2>
<p>给贪吃蛇头数据</p>
<pre><code class='language-c' lang='c'>void initsnake()
{
head=(struct snake*)malloc(sizeof(struct snake));
head->hang=4;
head->lie=4;
head->next=NULL;
}
</code></pre>
<p>将数据加到界面中</p>
<pre><code class='language-c' lang='c'>for(hang=0;hang<22;hang++){
for(lie=0;lie<22;lie++){
if(hang == 0 && lie !=21){
printw("--");
}else if(hang==21 && lie !=21){
printw("--");
}else if(lie == 0 || lie == 21 && hang !=0 && hang !=21){
printw("|");
}else if(head->hang == hang && head->lie == lie){
printw("[]");//加贪吃蛇数据
}else{
printw(" ");
}
}
printw("\n");
}
</code></pre>
<p>显示贪吃蛇蛇身,通过遍历的方式将蛇身输出(“在这里不小心用if替代了while,导致只输出一个蛇身,因为if只循环一次,而while会不断循环直至p指到NULL”)</p>
<pre><code class='language-c' lang='c'>void snakemove()
{
struct snake* new=(struct snake*)malloc(sizeof(struct snake));
new->next=NULL;
new->hang=tail->hang;
new->lie=tail->lie+1;
tail->next=new;
tail=new;
}
void initsnake()
{
head=(struct snake*)malloc(sizeof(struct snake));
head->hang=4;
head->lie=4;
head->next=NULL;
tail=head;
snakemove();
snakemove();
}
int snakeon(int x,int y)
{
struct snake* p;
p=head;
while(p != NULL){
if(p->hang==x && p->lie==y){
return 1;
}
p=p->next;
}
return 0;
}
</code></pre>
<h2>四丶贪吃蛇移动</h2>
<p>先实现贪吃蛇的单向向右移动,贪吃蛇的移动是tail向右移动一格,同时head要移动到head->next,然后free掉之前的head,原理是增加1个蛇身然后减去1蛇身,保持蛇的长度不变。</p>
<p>以下代码是控制蛇的方向(每往其方向走一格,就增加一个蛇身)</p>
<pre><code class='language-c' lang='c'>void snakemove()
{
struct snake* new=(struct snake*)malloc(sizeof(struct snake));
new->next=NULL;
switch(dir){
case UP:
new->hang=tail->hang-1;
new->lie=tail->lie;
break;
case DOWN:
new->hang=tail->hang+1;
new->lie=tail->lie;
break;
case LEFT:
new->hang=tail->hang;
new->lie=tail->lie-1;
break;
case RIGHT:
new->hang=tail->hang;
new->lie=tail->lie+1;
break;
}
tail->next=new;
tail=new;
}
</code></pre>
<p>然后是移动后对应的head会移动到head->next,然后free掉之前的head,释放空间;</p>
<pre><code class='language-c' lang='c'>void delesnake()
{
struct snake* p;
p=head;
head=head->next;
free(p);
}
</code></pre>
<p>最后让其移动时不会直接回头,这里采用的是绝对值的方式,先进行宏定义,这里前后和左右相反,只要其绝对值相等,则不执行command</p>
<pre><code class='language-c' lang='c'>#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
void notturn(int direction)
{
while(abs(dir)!=abs(direction)){//abs是绝对值的意思
dir=direction;
}
}
</code></pre>
<p>以下是按键封装的函数</p>
<pre><code class='language-c' lang='c'>void* t2()
{
while(1){
key=getch();
switch(key){
case KEY_DOWN:
notturn(DOWN);
break;
case KEY_UP:
notturn(UP);;
break;
case KEY_LEFT:
notturn(LEFT);;
break;
case KEY_RIGHT:
notturn(RIGHT);;
break;
}
}
}
</code></pre>
<p>由于这是一个while函数,需要和主程序同时进行,这里需要用到Linux的多线程模式,运用的语句为</p>
<pre><code class='language-c' lang='c'> pthread_t th1;//定义增加一个线程
pthread_create(&th1,NULL,t2,NULL);//线程进行的是t2函数(注意,这里的t2是一个指针函数)
</code></pre>
<h2>五丶设置贪吃蛇死亡条件</h2>
<p>当贪吃蛇撞到墙或者自己的时候,进行初始化,重新开始游戏;</p>
<pre><code class='language-c' lang='c'>void snakeover()
{
struct snake *p;
snakemove();
delesnake();
p=head;
while(p->next != NULL){
if(p->hang==tail->hang && p->lie==tail->lie){
initsnake();
}
p=p->next;
}
if(tail->hang==0 || tail->hang==21 || tail->lie==0 || tail->lie==21){
initsnake();
}
}
</code></pre>
<h2>六丶设置食物</h2>
<p>这里需要定义一个全局的结构体作为食物,同时需要把数据给到结构体,用rand随机数求余来给到结构体数值,这里不需要设置一个结构体指针,其不用链表链接,而且每次用完一个食物就要给他free掉,过于麻烦,这里直接使用结构体就行</p>
<pre><code class='language-c' lang='c'>void snakefood()
{
int x;
int y;
x=rand()%21;
y=rand()%21;
if(x==0){
x=x+1;
}
if(y==0)
{
y=y+1;
}
food.hang=x;
food.lie=y;
}
//将位置在地图中输出
void gamepic()
{
int hang;
int lie;
move(0,0);
for(hang=0;hang<22;hang++){
for(lie=0;lie<22;lie++){
if(hang == 0 && lie !=21){
printw("--");
}else if(hang==21 && lie !=21){
printw("--");
}else if(lie == 0 || lie == 21 && hang !=0 && hang !=21){
printw("|");
}else if(snakeon(hang,lie)){
printw("[]");
}else if(food.hang==hang && food.lie==lie){
printw("$$");//贪吃蛇食物在这里
}else{
printw(" ");
}
}
printw("\n");
}
}
</code></pre>
<p>食物被吃掉后随机出现:</p>
<p>由于是结构体,每次与tail值相同时直接再调用一次函数改变其结构体内容即可</p>
<pre><code class='language-c' lang='c'>void snakeover()
{
struct snake *p;
snakemove();
if(food.hang == tail->hang && food.lie == tail->lie){
snakefood();
}else{
delesnake();
}
p=head;
while(p->next != NULL){
if(p->hang==tail->hang && p->lie==tail->lie){
initsnake();
}
p=p->next;
}
if(tail->hang==0 || tail->hang==21 || tail->lie==0 || tail->lie==21){
initsnake();
}
}
//但初始化的时候也需要将食物运行,防止第一次进入地图的时候没有食物出现
void initsnake()
{
struct snake* p;
snakefood();
dir=RIGHT;
while(head != NULL)
{
p=head;
head=head->next;
free(p);
}
head=(struct snake*)malloc(sizeof(struct snake));
head->hang=4;
head->lie=4;
head->next=NULL;
tail=head;
snakemove();
snakemove();
snakemove();
}
//这是每一次初始化都需要进行的操作
</code></pre>
<h1>三丶所有贪吃蛇代码</h1>
<pre><code class='language-c' lang='c'>#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 snake
{
int hang;
int lie;
struct snake* next;
};
void initncurses()//ncurses界面初始化
{
initscr();
keypad(stdscr,1);
noecho();
}
struct snake food;
struct snake* head;
struct snake* tail;
int dir;
int key;
void notturn(int direction)//拿到方向,判断方向是否符合要求
{
while(abs(dir)!=abs(direction)){//abs时绝对值
dir=direction;
}
}
void* t2()//多线程需要用到指针,这里用多线程处理按键
{
while(1){
key=getch();
switch(key){
case KEY_DOWN:
notturn(DOWN);
break;
case KEY_UP:
notturn(UP);;
break;
case KEY_LEFT:
notturn(LEFT);;
break;
case KEY_RIGHT:
notturn(RIGHT);;
break;
}
}
}
int snakeon(int x,int y)//这里是对蛇的身体进行遍历,输出蛇身
{
struct snake* p;
p=head;
while(p != NULL){
if(p->hang==x && p->lie==y){
return 1;
}
p=p->next;
}
return 0;
}
void gamepic()//所有的界面显示都在这里进行显示
{
int hang;
int lie;
move(0,0);
for(hang=0;hang<22;hang++){
for(lie=0;lie<22;lie++){
if(hang == 0 && lie !=21){
printw("--");
}else if(hang==21 && lie !=21){
printw("--");
}else if(lie == 0 || lie == 21 && hang !=0 && hang !=21){
printw("|");
}else if(snakeon(hang,lie)){
printw("[]");
}else if(food.hang==hang && food.lie==lie){
printw("$$");
}else{
printw(" ");
}
}
printw("\n");
}
}
void snakefood()//蛇的食物以及食物随机出现
{
int x;
int y;
x=rand()%21;//rand()是随机数
y=rand()%21;
if(x==0){
x=x+1;
}
if(y==0)
{
y=y+1;
}
food.hang=x;
food.lie=y;
}
void snakemove()//蛇身的移动,tail为移动的方向,这里每移动一次就是加一个格子给蛇身,后面会调用函数去把尾巴相应的剪掉,做到没吃食物的时候蛇身长度不变
{
struct snake* new=(struct snake*)malloc(sizeof(struct snake));
new->next=NULL;
switch(dir){
case UP:
new->hang=tail->hang-1;
new->lie=tail->lie;
break;
case DOWN:
new->hang=tail->hang+1;
new->lie=tail->lie;
break;
case LEFT:
new->hang=tail->hang;
new->lie=tail->lie-1;
break;
case RIGHT:
new->hang=tail->hang;
new->lie=tail->lie+1;
break;
}
tail->next=new;
tail=new;
}
void initsnake()//整个地图和蛇的初始化,再游戏结束和重新开始时调用
{
struct snake* p;
snakefood();
dir=RIGHT;
while(head != NULL)
{
p=head;
head=head->next;
free(p);
}
head=(struct snake*)malloc(sizeof(struct snake));
head->hang=4;
head->lie=4;
head->next=NULL;
tail=head;
snakemove();
snakemove();
snakemove();
}
void delesnake()//这里就是将蛇尾去掉
{
struct snake* p;
p=head;
head=head->next;
free(p);
}
void snakeover()//蛇死亡的条件
{
struct snake *p;
snakemove();
if(food.hang == tail->hang && food.lie == tail->lie){
snakefood();
}else{
delesnake();
}
p=head;
while(p->next != NULL){
if(p->hang==tail->hang && p->lie==tail->lie){
initsnake();
}
p=p->next;
}
if(tail->hang==0 || tail->hang==21 || tail->lie==0 || tail->lie==21){
initsnake();
}
}
int main()
{
pthread_t th1;
initncurses();
initsnake();
pthread_create(&th1,NULL,t2,NULL);
while(1){
usleep(100000);//这里就相当于51中的延时
snakeover();
gamepic();
refresh();//刷新界面
}
getch();
endwin();
return 0;
}
</code></pre>
<h1>四丶关键词</h1>
<p>malloc:</p>
<p>在指针直接给数据时需要开辟一个空间给指针,但如果直接给相同类型就不需要开辟额外空间。</p>
<p>开辟空间用malloc</p>
<p>这里举一个结构体用法的例子:</p>
<pre><code class='language-c' lang='c'>(struct snake*)malloc(sizeof(snake struct))
</code></pre>
<p>用完之后不用的malloc开辟的空间需要用free给释放掉,防止内存占用。</p>