一个2048游戏

前言

偶然在网上看到一个很巧妙用C语言实现的2048游戏,只有487个字符,简直被作者的代码震撼到:原来c还能这么写。

源代码来自于Jay ChanGithub

1
2
3
4
5
6
7
8
9
10
11
M[16],X=16,W,k;main(){T(system("stty cbreak")
);puts(W&1?"WIN":"LOSE");}K[]={2,3,1};s(f,d,i
,j,l,P){for(i=4;i--;)for(j=k=l=0;k<4;)j<4?P=M
[w(d,i,j++)],W|=P>>11,l*P&&(f?M[w(d,i,k)]=l<<
(l==P):0,k++),l=l?P?l-P?P:0:l:P:(f?M[w(d,i,k)
]=l:0,++k,W|=2*!l,l=0);}w(d,i,j){return d?w(d
-1,j,3-i):4*i+j;}T(i){for(i=X+rand()%X;M[i%X]
*i;i--);i?M[i%X]=2<<rand()%2:0;for(W=i=0;i<4;
)s(0,i++);for(i=X,puts("\e[2J\e[H");i--;i%4||
puts(""))printf(M[i]?"%4d|":" |",M[i]);W-2
||read(0,&k,3)|T(s (1,K[(k>>X)%4]));}//[2048]

看到这么一大堆东西是不是很头疼,那么就格式规范化后仔细看一下。

重新格式化

这部分参考了 catull 的代码,原地址:https://gist.github.com/justecorruptio/9967738#

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
* Original file by Jay Chan: ;
* https://gist.github.com/justecorruptio/9967738
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#define GRID_LEN 16
int M[GRID_LEN];
int X = GRID_LEN;
int W;
int k;
int K[] = { 2, 3, 1 };
//array out of bouds.
//int K[] = {2, 3, 1, 0};
int w(int d, int i, int j)
{
if(d <= 0)
{
return 4 * i + j;
}
return w(d - 1, j, 3 - i);
}

void s(int f, int d)
{
int i = 4, j, l, P;

for(; i--;)
{
j = k = l = 0;

for(; k < 4;)
{
if(j < 4)
{
P = M[w(d, i, j++)];
W |= P >> 11;
l * P && (f ? M[w(d, i, k)] = l << (l == P) : 0, k++);
l = l ? (P ? (l - P ? P : 0) : l) : P;
}
else
{
f ? M[w(d, i, k)] = l : 0;
++k;
W |= 2 *!l;
l = 0;
}
}
}
}

void T()
{
int i = X + rand() % X;
for(; M[i % X] * i; i--);
i ? M[i % X] = 2 << rand() % 2 : 0;
W = i = 0;
for(; i < 4; i++)
{
s(0, i);
}
//Prints the tiles onto the terminal
i = X;
//Clear screen on UNIX-based system
puts("\e[2J\e[H");
for(; i--;)
{
if(M[i])
{
printf("%4d|", M[i]);
}
else
{
printf("%s", " |");
}
// every 4th cell is followed by a line-break
if(0 == (i & 3))
{
putchar('\n');
// puts("");
//printf() does not include a newline, ;
//so output isn't flushed. ;
// puts() includes a newline, ;
//so output is flushed.
}
}
//read input from keyboard
if(!(W - 2))
{
read(0, &k, 3);
s(1, K[(k >> X) % 4]);
T();
}
}

int main(void)
{
//Uses stty to read just one key without waiting for a return key.
//cbreak:
//If set, enables brkint, ixon, imaxbel, opost, isig, iexten,
//and -icanon. If unset, same as sane.
system("stty cbreak");

//Intializes random number generator
srand((unsigned) time(NULL));

T();

//Game has finished by this point ;
//If win, display "WIN". Otherwise, display "LOSE".
puts(W & 1 ? "WIN" : "LOSE");

return 0;
}

运行

现在在 linux 下运行一下,结果如下图:

  • 游戏开始时界面:
    start

  • 游戏运行中界面:
    run

  • 游戏结束时界面:
    end

解析

  1. 作者充分利用了C语言的一些语法,比如大量用到了&&||在条件判断过程中的顺序来代替if判断。

    比如:

    1
    i % 4 || puts("")

    代替了:

    1
    2
    if(i % 4 == 0)
    printf('\n');

    这里提一下puts()printf()的区别:

    • puts()会在结尾输出 \nprintf()不会换行。
  1. 作者利用puts("\e[2J\e[H");作为清屏。

  2. 作者利用read(0, &k, 3);读取键盘输入,

    read()在头文件#include <unistd.h>中,函数定义为:

    1
    size_t read(int fildes, void *buf, size_t nbytes);

    其中fildes0, 1, 2时分别表示standard inputstandard outputstandard error