C语言学习笔记第十四天
一、Makefile的编写
Makefile是由一系列编译指令组成的可执行文本文件,也叫编译脚本
在终端执行 make 命令,就会自动执行Makefile脚本文件中的编译指令,它可根据文件的最后修改时间来判断哪些文件需要重新编译、哪些不需要重新编译,从而提高编译效率。
编译规则(大概知道即可):
- 果这个工程没有编译过,那么我们的所有c 文件都要编译并被链接。
- 如果这个工程的某几个c 文件被修改,那么我们只编译被修改的c 文件,并链接目标程序。
- 如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的c 文件,并链接目标程序。
一个最简单Makefile格式:
执行目标: 依赖
编译指令
被依赖的目标1:依赖文件
编译指令
被依赖的目标2:依赖文件
编译指令
...
接下来的项目我会用到Makefile,可以参照如何编写一个简单的Makefile,编写Makefile后,在命令行中输入make命令即可运行Makefile中的命令,大型项目必备哦
二、项目:2048小游戏(多文件编程)
2048游戏项目说明:具体游戏规则请自行百度搜索”2048小游戏“
要求需要以下基本文件:
main.c | 程序入口 |
---|---|
game2048.c、game2048.h | 游戏的业务逻辑 |
direction.c、direction.h | 四个方向处理 |
tools.c、tools.h | 工具函数 |
Makefile | 编译脚本 |
在创建文件时,可以使用脚本文件进行批量创建文件,和gedit批量打开文件
1. 创建一个脚本用来批量创建文件
编写文件如下:
create_project.sh
#程序入口
touch main.c game2048.c game2048.h direction.c direction.h tools.h tools.c
#编译脚本
ls > Makefile
#编辑脚本
ls > game2048.conf
然后添加权限:chmod +x create_project.sh
执行create_project.sh脚本:./create_project.sh
使用完之后删除权限:chmod -x create_project.sh
2. 编辑game2048.conf
game2048.conf
gedit main.c direction.c direction.h game2048.c game2048.h tools.c tools.h
改变执行权限(和上面的步骤一样的,最后不要删除权限就行了),该脚本用gedit批量打开文件
3. 编写工程文件
工程文件编写,具体如下
main.c
/************************************
> 作者:杭电羊皮卷
> QQ:2997675141
> weixin:QQ2997675141
************************************/
#include <stdio.h>
#include "game2048.h"
int main(int argc,const char* argv[])
{
init_game();
start_game();
end_game();
return 0;
}
direction.c
#include "direction.h"
#include "game2048.h"
//f 表示是否发生了移动,没有发生移动则不会产生新的零
int moveleft(void)
{
int f=0;
for(int i=0;i<4;i++)
{
int sign=0;
for(int j=1;j<4;j++)
{
if(map[i][j]!=0)
{
for(int k=j-1;k>=sign;k--)
{
if(map[i][k]==0)
{
map[i][k]+=map[i][k+1];
map[i][k+1]=0;
f=1;
}else if(map[i][k]==map[i][k+1])
{
score+=map[i][k];
map[i][k]*=2;
map[i][k+1]=0;
sign=k+1;
f=1;
}
}
}
}
}
return f;
}
int moveright(void)
{
int f=0;
for(int i=3;i>=0;i--)
{
int sign=3;
for(int j=2;j>=0;j--)
{
if(map[i][j]!=0)
{
for(int k=j+1;k<=sign;k++)
{
if(map[i][k]==0)
{
map[i][k]+=map[i][k-1];
map[i][k-1]=0;
f=1;
}else if(map[i][k]==map[i][k-1])
{
score+=map[i][k];
map[i][k]*=2;
map[i][k-1]=0;
sign=k-1;
f=1;
}
}
}
}
}
return f;
}
int moveup(void)
{
int f=0;
for(int i=0;i<4;i++)
{
int sign=0;
for(int j=1;j<4;j++)
{
if(map[j][i]!=0)
{
for(int k=j-1;k>=sign;k--)
{
if(map[k][i]==0)
{
map[k][i]+=map[k+1][i];
map[k+1][i]=0;
f=1;
}else if(map[k][i]==map[k+1][i])
{
score+=map[k][i];
map[k][i]*=2;
map[k+1][i]=0;
sign=k+1;
f=1;
}
}
}
}
}
return f;
}
int movedown(void)
{
int f=0;
for(int j=3;j>=0;j--)
{
int sign=3;
for(int i=2;i>=0;i--)
{
if(map[i][j]!=0)
{
for(int k=i+1;k<=sign;k++)
{
if(map[k][j]==0)
{
map[k][j]+=map[k-1][j];
map[k-1][j]=0;
f=1;
}else if(map[k][j]==map[k-1][j])
{
score+=map[k][j];
map[k][j]*=2;
map[k-1][j]=0;
sign=k-1;
f=1;
}
}
}
}
}
return f;
}
direction.h
#ifndef DIRECTION_H
#define DIRECTION_H
int moveleft(void);
int moveright(void);
int moveup(void);
int movedown(void);
#endif
game2048.c
#include <stdlib.h>
#include <getch.h>
#include <time.h>
#include "game2048.h"
#include "tools.h"
#include "direction.h"
int (*map)[4]=NULL;
int score=0;
int (*direction[])(void)={
moveup,
movedown,
moveright,
moveleft,
};
//初始化相关数据,加载数据
void init_game(void)
{
debug("%s\n",__func__);
//申请堆内存
map = calloc(4*4,4);
//初始化数据
srand(time(NULL));
int x=0,y=0;
x=rand()%4;
y=rand()%4;
map[x][y]=2;
//读取文件
}
//运行游戏
void start_game(void)
{
int f=0;
debug("%s\n",__func__);
for(;;)
{
//显示界面
system("clear");
show_map();
//获取方向键并处理
int ch=getch();//获取键盘值,详细请见最开始那个项目,里面有getch.h的配置方式(ubuntu16.04)
if(ch>182 && ch<187)
{
f=direction[ch-183]();
}
//随机位置生成2
if(f)
{
rand_two();
}
//检查是否能继续
if(is_loss())
{
system("clear");
show_map();
printf("\n游戏结束……\n");
break;
}
debug("f=%d\n",f);
}
}
//释放相关资源,保存数据
void end_game(void)
{
debug("%s\n",__func__);
free(map);
map=NULL;
}
game2048.h
#ifndef GAME2048_H
#define GAME2048_H
extern int (*map)[4];
extern int score;
//初始化相关数据,加载数据
void init_game(void);
//运行游戏
void start_game(void);
//释放相关资源,保存数据
void end_game(void);
#endif
tools.c
#include <stdio.h>
#include <stdlib.h>
#include "tools.h"
#include "game2048.h"
static int is_empty(void);
void show_map(void)
{
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
printf("|");
if(map[i][j]==0)
{
printf(" ");
}else
{
printf("%-4d",map[i][j]);
}
}
printf("|\n");
printf("---------------------\n");
}
printf("您的目前得分为:%d\n",score);
}
//判断是否有空的可以插入的位置
static int is_empty(void)
{
int f=0;
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
if(map[i][j]==0)
{
f=1;
break;
}
}
}
return f;
}
//生成2,调用之前必须判断是否能插入,返回值表示是否成功生成
int rand_two(void)
{
int x=0,y=0;
if(is_empty()==0)
{
return 0;
}
while(1)
{
x=rand()%4;
y=rand()%4;
if(0 == map[x][y])
{
map[x][y]=2;
break;
}
}
return 1;
}
int is_loss(void)
{
if(is_empty())
{
return 0;
}
for(int i=0;i<4;i++)
{
for(int j=0;j<3;j++)
{
if(map[i][j]==map[i][j+1])
{
return 0;
}
}
}
for(int j=0;j<4;j++)
{
for(int i=0;i<3;i++)
{
if(map[i][j]==map[i+1][j])
{
return 0;
}
}
}
return 1;
}
tools.h
#ifndef TOOLS_H
#define TOOLS_H
#include <stdio.h>
#ifdef DEBUG
#define debug(...) printf(__VA_ARGS__)
#else
#define debug(...)
#endif
//error
#define error(...) printf("%s:%s %d %m%s %s %s",__FILE__,__FUNCTION__,__LINE__,__VA_ARGS__,__DATE__,__TIME__)
void show_map(void);
int rand_two(void);
int is_loss(void);
#endif
Makefile
CC=gcc
BIN=game2048
OBJ=direction.o game2048.o main.o tools.o
FLAG=-Wall -Werror -DDEBUG
STD=-std=gnu99
all:$(OBJ)
$(CC) -o $(BIN) $(OBJ) && ./$(BIN)
direction.o:direction.c game2048.h
gcc -c $(STD) $(FLAG) direction.c
game2048.o:game2048.c tools.h direction.h
gcc -c $(STD) $(FLAG) game2048.c
tools.o:tools.c game2048.h
gcc -c $(STD) $(FLAG) tools.c
clean:
rm -f $(BIN) $(OBJ)
Makefile中$(BIN)就相当于最开始BIN赋值的字符串,其他同理
4. 命令行输入make即可自动编译并且执行
总结,练习了多文件编写,宏的使用,脚本文件的编写,Makefile的编写,本项目最难的在于移动的逻辑,其他的都相对简单,所有用到的知识都在前面的文章中有讲,还看不懂的就该补课啦……