程序设计语言:C语言
游戏:2048
玩法:用WSAD控制上下左右方向,按Q退出游戏;
当两个相同数字方块发生碰撞,它们会累加起来成为一个新的数字方块,出现数字方块为2048时即可通关
源代码地址: https://github.com/gitforhzc/demo-2048-c
编译源代码,运行demo./demo

Framwork
- include
- define
- implement
- main
design
include
- stdafx.h # pre-compile include file
define
- demo2048c.h
- declare global variable
- declare global function
implement
1 | // file: stdafx.h |
1 | // file: demo2048c.h |
main
1 | // file: main.c |
coding
编程思想:模块化
// file: demo2048c.c
#include “stdafx.h”
#include “demo2048c.h”
init()
1 | // init 函数首先初始化屏幕,在方格中随机生成两个位置并放入随机数字2,4。 |
srand() — why call it only once?
add_number()
初始化数字和生成新数字整合成这一个函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// add_number 函数用于生成随机数
void add_number() {
int new_x, new_y, tmp;
// 随机空位置
do {
new_x = rand() % 4;
new_y = rand() % 4;
} while (a[new_y][new_x] != 0);
// 随机2,4数字
do {
tmp = rand() % 4;
} while (tmp == 0 || tmp == 2);
a[new_y][new_x] = tmp + 1;
empty--;
}
draw()
关键点在于横线和竖线for循环的对称性1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// draw绘制方格和数字
void draw() {
int n, m, x, y;
clear();
for (n = 0; n < 9; n += 2) //横线
for (m = 0; m < 21; m++) {
move(n, m);
addch('-');
refresh();
}
for (m = 0; m < 22; m += 5) //竖线
for (n = 1; n < 8; n++) {
move(n, m);
addch('|');
refresh();
}
for (y = 0; y < 4; y++) //数字
for (x = 0; x < 4; x++) {
draw_one(y, x);
if (a[y][x] == 2048) { // 胜利条件,出现2048
endflag = win;
}
}
}
draw_one(int y, int x)
1 | // draw_one 函数用于绘制单个数字 |
play()
1 | // 用 wsad 来表示上下左右方向 |
left()
1 | void left() { |

right()
1 | void right() { |
up()
1 | void up() { |
down()
1 | void down() { |
test_lose()
1 | // 判断矩阵各相邻数字是否相同,若都不同,则游戏失败 |
game_over()
1 | // 游戏结束子函数 |
draw_over()
1 | // 游戏结束后绘制结束提醒 |
testing
caution: compile
我们用了一个文本界面的屏幕绘图库 ncurses,编译时需要加上 -lcurses 选项1
sudo apt-get install libncurses5-dev
编译1
gcc demo2048.c main.c -o demo -lcurses
error: re-define
1 | /home/hzc/demo-2048-c/main.c:12: `a'被多次定义 |
solution: declare int a[4][4]; on demo2048c.h, define int a[4][4] = {0};on int main()
bonus
MarkDown-jump
markdown实现页内跳转:[](#jump)
[]内可任意文本,点击即跳转到jump处,jump必须是标题级纯英文字母
vim-ctags
ctags -R # 生成tags文件ctrl+] 跳转到函数定义处ctrl+T 回到函数被调用处
gdb-tty
tty命令可以指写输入输出的终端设备。如:tty /dev/ttybtty # echo /dev/pts/8gdb demo-2048-c -tty /dev/pts/8
ncurses
curses 是一個建構於 termcap/terminfo 之上的通用程式庫; ncurses 是 curses 的改良版本
- 在 C 程式最前面, 要 #include <curses/curses.h> 並且在連結時要加 -lncurses
- 所有使用 ncurses 的程式, 一開始都要 initscr. 這會設定 ncurses 程式庫的初始狀態, 並且把程式執行之前的螢幕存起來.
- 所有使用 ncurses 的程式, 結束時都要 endwin. 這會讓 ncurses 程式庫做一些撿場的動作, 例如把螢幕恢復成程式執行之前的狀態.
- 為了提高執行效率, ncurses 中的大部分副程式所製造的效果, 都不是馬上會顯現出來的, 而是等到程式設計師呼叫 refresh 時才把先前所有指定的效果一次顯現出來.
- cbreak 可讓終端機進入 “立即反應狀態”: 使用者只要敲一個鍵 (而不必按 Enter 鍵) 程式立即可以看到該字元 (透過 getch). 另一方面, noecho 可避免終端機當下就印出使用者敲進去的鍵, 而讓程式設計師自行安排在其他地方或時間才印出來 (甚或不印或印其他字串)
[2] FreeBSD Man Pages for ncurses
API manual
- int curs_set(int visibility);
The curs_set routine sets the cursor state is set to invisible, normal,
or very visible for visibility equal to 0, 1, or 2 respectively.- int move(int y, int x);
These routines move the cursor associated with the window to line y and column x.
This routine does not move the physical cursor of the terminal until
refresh is called. The position specified is relative to the
upper left-hand corner of the window, which is (0,0).- int addch(const chtype ch);
The addch, waddch, mvaddch and mvwaddch routines put the character ch
into the given window at its current window position, which is then advanced.
update log
This project is based on https://www.shiyanlou.com/courses/running
- code refactoring
- integrate new number into one function, introduce add_number()
- add game over warning, introduce draw_over()
- add endflag,when to quit, lose or win
- add lose, introduce test_lose()