- C语言学习笔记第十四天
- 一、Makefile的编写
- 二、项目:2048小游戏(多文件编程)
Makefile是由一系列编译指令组成的可执行文本文件,也叫编译脚本
在终端执行 make 命令,就会自动执行Makefile脚本文件中的编译指令,它可根据文件的最后修改时间来判断哪些文件需要重新编译、哪些不需要重新编译,从而提高编译效率。
编译规则(大概知道即可):
- 果这个工程没有编译过,那么我们的所有c 文件都要编译并被链接。
- 如果这个工程的某几个c 文件被修改,那么我们只编译被修改的c 文件,并链接目标程序。
- 如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的c 文件,并链接目标程序。
一个最简单Makefile格式:
执行目标: 依赖
编译指令
被依赖的目标1:依赖文件
编译指令
被依赖的目标2:依赖文件
编译指令
...
二、项目:2048小游戏(多文件编程)接下来的项目我会用到Makefile,可以参照如何编写一个简单的Makefile,编写Makefile后,在命令行中输入make命令即可运行Makefile中的命令,大型项目必备哦
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
#include#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#include #include #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("%sn",__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("%sn",__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=%dn",f); } } //释放相关资源,保存数据 void end_game(void) { debug("%sn",__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#include #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("您的目前得分为:%dn",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#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的编写,本项目最难的在于移动的逻辑,其他的都相对简单,所有用到的知识都在前面的文章中有讲,还看不懂的就该补课啦……



