如题。100行包括预处理命令,声明之类的。排除很多语句塞在一行的情况。


中午试了一下写个极简的(除了核心玩法什么都没有),不压缩大概 32 行。使用跨平台的 Ncurses 库做控制台的输入输出。

代码

#include &
#include & // usleep()
#include & // rand()
#include & // time()
#define W 40
#define H 24
int m[W * H], q[W * H], p = H / 2 * W + (W / 2), a, h = 0, t = 0, d = 1, i;
int main(void) {
initscr(); noecho(); keypad(stdscr, 1); nodelay(stdscr, 1); curs_set(0);
srand(time(NULL));
for (i = 0; i &< W * H; i++) m[i] = !(i / W % (H - 1) i % W % (W - 1)); m[q[t = (t + 1) % (W * H)] = p] = 1; do { a = rand() % (W * H); } while (m[a]); while ((i = getch()) != 27) { if (i == KEY_UP d != W) d = -W; else if (i == KEY_DOWN d != -W) d = W; else if (i == KEY_LEFT d != 1) d = -1; else if (i == KEY_RIGHT d != -1) d = 1; if (m[p += d]) break; m[q[t = (t + 1) % (W * H)] = p] = 1; if (p == a) do { a = rand() % (W * H); } while (m[a]); else m[q[h = (h + 1) % (W * H)]] = 0; for (i = 0; i &< W * H; i++) mvaddstr(i / W, (i % W) * 2, m[i] ? "[]" : " "); mvaddstr(a / W, (a % W) * 2, "()"); refresh(); usleep(100000); } while (getch() == ERR); endwin(); }

编译与运行结果

gcc -lncurses snake.c ./a.out

代码解释

  • W, H:地图的宽度和高度
  • m:地图数组,0 表示无障碍物,1 表是障碍物(包括蛇及墙)
  • q:循环队列,存储蛇的坐标
  • p:当前位置,以 y * H + w 形式存储坐标
  • a:苹果位置
  • h, t:队列的头和尾(有点混淆的是,蛇尾是队列的头)
  • d:当前的步进(delta)

把其游戏循环写成伪代码的话:

当没有按 ESC 离开时
  检测按键改变方向
  使用方向移动位置
  如新位置碰撞障碍物,跳出
  在地图中的新位置设为障碍物
  在队列中压入新位置
  如吃到苹果,计算新苹果位置;否则,弹出队列,并清空该位置的障碍物
  渲染地图及苹果


更新2020/2/14:压缩

似乎很多人都想知道压缩后大概是多少代码,中午试了一下,把非必要的代码去除后,以一行80个字元限制来说,共 6 行:

main(){int W=40,H=24,S=W*H,m[S],q[S],p=H/2*W+W/2,a,h=0,t=1,d=1,i=S;initscr();
noecho();keypad(stdscr,1);nodelay(stdscr,1);curs_set(0);srand(time(0));while(i--
)m[i]=!(i/W%(H-1)i%W%(W-1));m[q[t]=p]=1;r:while(m[a=rand()%S]);for(;(i=getch()
)-27;refresh(),usleep(100000)){d=!(i-=258)d+W?W:!--id-W?-W:!--id-1?-1:!--i
d+1?1:d;if(m[p+=d])break;m[q[t=++t%S]=p]=1;if(p-a)m[q[h=++h%S]]=0;else goto r;
for(i=S;i--;)mvaddstr(i/W,i%W*2,m[i]?"[]":" ");mvaddstr(a/W,a%W*2 ,"()");}}

这样编译:

clang -Wno-everything -include curses.h -lncurses a.c

统计一下这 6 行里,变数定义大概花了 1 行,输出 2 行,输入 1 行,余下的 2 行是游戏逻辑。而按字元来算是472个。


这段代码会发生内存泄漏,特此备注。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

没学到一个小小的贪吃蛇会有怎么多赞,当时想能有100个赞已经很不错了,这个完全超出我的预期啊,啊啊啊~飘了飘了~

小声bb:其实这个代码写得不咋地,有些函数为了能用 return 语句,故意写了一个返回值,但是返回值没有用到,还有就是 Run 这个函数本来不应该接受一个参数,但是为了省一行的代码,把init写进去,才出此下策。这样写的后果就是代码不符合我的预期,不这样写的话又不符合题主的要求,两难啊。所以我才会比较郁闷,说了点脏话,实在抱歉 。(可能自己水平不够,代码还有很多优化空间,我看别的答主根本就用不到100行)

继续努力吧 ~

~~~~~~~~~~~~原答案~~~~~~~~~~~

我本来怀著很好的心情来写这个100的贪吃蛇的,不过写到后面就出口成脏了。

mmp,写到后面很是郁闷。有图为证。

忍不住说脏话了

刚刚好一百行代码,我去,这是那我之前写的350多行代码压缩的得到的。

写得很垃圾,有些东西就是乱写一通,就为了压缩一两行逻辑代码。

然后UI部分,这个也没办法压缩,能压缩的都在核心代码上面了。

尽量体现出来格式,但是难受啊

贪吃蛇的核心代码就在这个switch里面了,10行。也就是占比 1/10。其实还可以...

最后的效果图是这个

按空格开始和暂停

直接贴代码吧。

#include &
#include &
#include &
#include &
#define PANIC(err) (fprintf(stderr,"PANIC Line %d : %s",__LINE__,err),exit(-1),1)
#define PANICIFNULL(EXP) ((EXP)==NULL PANIC("NULL"))
typedef enum { EMPTY=0, WALL, BODY, FOOD } MAP;
typedef int POSITION;
struct { int color; const char* shape; } UI[] = {
{2,""},{4,"□"},{6,""},{4,"●"}
};
struct {
int WIDTH, HEIGHT, direction, delay;
MAP* map;
POSITION* body, head, tail, len;
} C;
void initConsole(int width, int height) {
char cmd[100];
sprintf_s(cmd,100, "mode con cols=%d lines=%d title C语言贪吃蛇 By dreamer2q %s", width, height,__DATE__);
system(cmd);
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cur_info;
GetConsoleCursorInfo(handle, cur_info);
cur_info.bVisible = FALSE;
SetConsoleCursorInfo(handle, cur_info);
}
void updatePosition(POSITION pos) {
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD coord = { (pos % (C.WIDTH)) * 2 ,pos / (C.WIDTH) };
SetConsoleCursorPosition(handle, coord);
SetConsoleTextAttribute(handle, UI[C.map[pos]].color);
printf("%s", UI[C.map[pos]].shape);
}
MAP food(int t) {
POSITION pos = (rand() % ((C.WIDTH - 2) * (C.HEIGHT - 2))) + C.WIDTH + 1;
if (C.map[pos]) return food(t);
else return (C.map[pos] = FOOD) ? updatePosition(pos), BODY : BODY;
}
int init() {
C.WIDTH = C.HEIGHT = 30;
initConsole(C.WIDTH * 2, C.HEIGHT);
PANICIFNULL(C.map = (MAP*)malloc((C.WIDTH) * (C.HEIGHT) * sizeof(MAP)));
PANICIFNULL(C.body = (POSITION*)malloc(C.WIDTH * C.HEIGHT * sizeof(POSITION)));
C.head = (C.len = 3) - 1;
C.direction = (C.tail = 0) + 1;
C.delay = -150;
memset(C.map, EMPTY, (C.WIDTH) * (C.HEIGHT) * sizeof(MAP));
for (int i = 0; i &< (C.WIDTH) * (C.HEIGHT); i++) { i &< C.WIDTH (C.map[i] = C.map[C.WIDTH * (C.HEIGHT - 1) + i] = WALL); i &< C.HEIGHT (C.map[C.WIDTH * i] = C.map[C.WIDTH * i + C.WIDTH - 1] = WALL); i &< C.len (C.map[C.body[i] = C.WIDTH * C.HEIGHT / 2 + C.WIDTH / 2 - 1 + i] = BODY); updatePosition(i); } srand(time(NULL)); return food(0); } int Run(int shit) { int prv = 77; while (1) { if (_kbhit()) { int t = _getch(); if ((prv + t) == 152)continue; switch (t) { case 72:C.direction = -C.WIDTH; break; case 80:C.direction = C.WIDTH; break; case 75:C.direction = -1; break; case 77:C.direction = 1; break; case :C.delay = -C.delay; break; default:continue; } prv = t; } #define INC(p) (((p)+1)%(C.WIDTH*C.HEIGHT)) if (C.delay &> 0) Sleep(C.delay);
else continue;
switch (C.map[C.body[INC(C.head)] = C.body[C.head] + C.direction]) {
case FOOD:food(C.len = -C.len - 1);
case EMPTY:
C.map[C.body[C.head = INC(C.head)]] = BODY;
updatePosition(C.body[C.head]);
if (C.len &> 0) updatePosition((C.map[C.body[C.tail]] = EMPTY) ? BODY : C.body[C.tail]), C.tail = INC(C.tail);
else C.len = -C.len;
break;
case WALL:case BODY:
return -1;//dead
}
}
}
int main() {
while (1) {
initConsole(25, 10);
printf("
C语言贪吃蛇

1. 开始游戏
2. 关于
q. 退出
%");
switch (_getch()) {
case q:return 0;
case 2:MessageBoxA(GetConsoleWindow(), "100行代码?", "有病吧你?", MB_OK|MB_ICONASTERISK); continue;
case 1:Run(init());
MessageBoxA(GetConsoleWindow(), "你死了。有病去看看吧", "SHIT", MB_OK | MB_ICONERROR);
}
}
}

觉得还行就thumbup,写代码也不容易


更新:好多人问那个18行版本,我也给一个压缩版本吧。22行,实在不想进一步牺牲可读性了。除了秀这一下,并没有什么意义。

#include & /* setlocale, 使用特殊字元 */
#include & /* 绘图 */
#include & /* 随机数 */
#define DRAW(pos, c) !mvprintw(pos / W, pos % W * 2, c)
int const W = 30, H = 30, MSIZE = W * H, MoveHints[4] = {W, 2 * W + 1, 2 + W, 1};
int Board[MSIZE] = {0}, Snake[MSIZE] = {W / 2 + H * W / 2};
int main() {
/* 使用特殊字元,初始化屏幕, 隐藏游标 使用游标键控制, 不缓冲按键, 随机种子 */
setlocale(0, ""), initscr(), curs_set(0), keypad(stdscr, TRUE), nodelay(stdscr, TRUE), srand(0);
int food = Snake[0] - 1, h = 0, t = 0, x = 0, moveDirection = 0;
for (int i = 0, j = 1; i != MSIZE; i += j, j = (i &< W || i &>= MSIZE - W) ? 1 : W - j) Board[i] = DRAW(i, "");
do {
int key = (KEY_LEFT == x) + (KEY_RIGHT == x) * 3 + (KEY_UP == x) * 4 + (KEY_DOWN == x) * 2;
if (key (key - 1 1) != (moveDirection 1)) moveDirection = key - 1;
int next = Snake[h] + MoveHints[moveDirection] - W - 1;
if (Board[next]) break; /* end game */
Board[Snake[h = (h == MSIZE - 1) ? 0 : h + 1] = next] = DRAW(next, "");
if (next != food) Board[Snake[t]] = !DRAW(Snake[t], " "), t = (t == MSIZE - 1) ? 0 : t + 1;
while (Board[food] || !DRAW(food, "◆")) food = rand() / (RAND_MAX / ((W - 1) * (H - 1))) + W + 1;
timeout(500); /* 等待按键的最大时间 */
} while ((x = getch()) != q);
endwin();
}

这个版本基本上与下面的解析基本一致,主要改进是从Milo Yip 的答案中抄的通过Board记录蛇的位置,优化了is_snake函数。以及大量语句压缩。


役情原因,在家无事,来优化一下,保证可读性的情况下压缩在100行以内。使用ncurses库,完成命令行绘图和非同步按键处理。window下应有pdcurses,可以相应处理。

关键是一些数据结构设计,如果用C++还可以少很多代码。

这类小游戏可以简单分成绘图(clear和go),输入和逻辑处理(process)两到三个部分,基本游戏循环如下:

int main() {
clear();
while (process() != q) {
go();
timeout(500);
}
}

绘图部分为了最大程度的复用,可以只使用一个函数(draw)来绘一个图标,贪吃蛇身,苹果,底图边框都可以复用。示例如下:

如何表示一个坐标位,用一个整数就可以。基本操作如下

typedef unsigned int POS;
const POS W = 30, H = 30, MSIZE = (W - 1) * (H - 1);

#define X(pos) (pos % W) /* 分量 */
#define Y(pos) (pos / W) /* 分量 */
#define XY(x, y) ((y)*W + (x))

当然也可以用结构体,但后续函数需要用指针操作,代码会多一点。

typedef struct Pos { unsigned short x, y; } POS;

绘边框,底图和食物

void clear() {
for (POS i = 0; i != W * H; ++i) {
if (Y(i) == 0 || Y(i) == H - 1 || X(i) == 0 || X(i) == W - 1) DRAW(i, "");
// else DRAW(i, 「 」)
}
new_food();
}

else 部分是绘底图,我的平台上不用,如果其它平台上需要也可以绘。这里利用了POS是一个整数,把二维循环压缩成了一维。

绘食物,这里可以暂时不看is_snake这个函数。

void new_food() {
do {
food = rand() / (RAND_MAX / MSIZE) + XY(1, 1);
} while (is_snake(food));
DRAW(food, "◆");
}

这里同样利用了POS是一个整数,把生成食物的位置控制在边框以内。

贪吃蛇身设计成了一个循环队列,可以减少最多的移动,贪吃蛇本身保存在一个预分配的数组中,用两个指针表示当前队列头和队列尾。贪吃蛇最少会有一个节点,所以队列头和队列尾不会重叠。

当指针超过最后一个预分配结点时,会绕回开头,这个操作代码很简单。

POS snake[20] = {XY(H / 2, W / 2)};
POS *head = snake, *tail = snake,
*end = snake + sizeof(snake) / sizeof(POS);
#define INC(p) if (++(p) == end) (p) = snake

判断食物是否在蛇身上,会先在head头上放一个哨兵,再从tail查找到head。

bool is_snake(POS pos) {
POS *p = tail, *pflag = head;
INC(pflag);
*pflag = pos;
while (*p != pos) {
INC(p);
}
return p != pflag;
}

蛇移动逻辑如下

POS const MoveHints[4] = {XY(0, 1), XY(1, 2), XY(2, 1), XY(1, 0)};
int moveDirection = 2;

void go() {
POS next = *head + MoveHints[moveDirection] - XY(1, 1);
if (X(*head) == 0 || Y(*head) % H == 0 || is_snake(next)) {
/* end game */
exit(0);
}
INC(head);
*head = next;
DRAW(*head, "");

if (*head == food) {
new_food();
return;
}
DRAW(*tail, " ");
INC(tail);
}

蛇会保持一个移动方向(moveDirection),如果用户不按键,蛇则一直向这个方向前进(MoveHints)。一直到撞到什么东西,则游戏结束。

蛇移动就是头指针向前,多绘一个节点。如果这个位置有食物,那么蛇增长一格,不需要消除自己的尾巴。如果没有食物,则需要消除尾巴节点,尾指针也向前移动。此处理未处理预分配空间不足的情况,理论上这个空间应等于MSIZE。

按键处理部分,特别需注意getch在没有按键时,也会马上返回。

int process() {
int x = getch();

POS key = (KEY_LEFT == x) + (KEY_RIGHT == x) * 3 + (KEY_UP == x) * 4 +
(KEY_DOWN == x) * 2;
if (key (key - 1 1) != (moveDirection 1)) {
moveDirection = key - 1;
}
return x;
}

这里只用到了四个方向和Q键,用于退出。

Key的计算:左和右安排在1和3,上和下安排在2和4,四个逻辑表达式只有一个会取值为1,所以可以计算得到方向键。当蛇向左走时,右键不能用;向上走时,下键不能用,反之也是。这个key数值设计保证,key 的最低位与方向(moveDirection 1)相同。最后,moveDirection的范围是0-3,而key的范围是1-4,所以会有减一操作。

完整代码在 https://gitee.com/hl4/codes/y3p5lzke68fj4xhnamrq045

环境所限,只在mac下测试过。


UPD 20191229

重写了一个C89兼容的…大概吧…

总之在VC6下能跑起来了(对stdafx.h的依赖不会去除不是我的问题哦)…

然后…诸位…

快换一个完整支持C99的编译器吧!!!

不过代码又变脏了好多…对不起我已经不纯洁了嘤嘤嘤…

恰好100,如下

#include &
#define MAX_WIDTH (30)
#define MAX_HEIGHT (30)
#define MAX_SIZE (MAX_WIDTH*MAX_HEIGHT)
#define inc(x) (x=(x+1)%MAX_SIZE)
#define cmppos(left, right) (left.X == right.X left.Y == right.Y)
#define isborder(pos) (pos.X &>= MAX_WIDTH+1 || pos.X &<= 0 || pos.Y &>= MAX_HEIGHT+1 || pos.Y &<= 0) COORD snake[MAX_SIZE] = { {MAX_WIDTH / 2,MAX_HEIGHT / 2} }; COORD food = { MAX_WIDTH + 1, MAX_HEIGHT + 1 }, head = { MAX_WIDTH / 2, MAX_HEIGHT / 2 }; unsigned int QueueHead = 1, QueueTail = 0, movement = 2; const signed int MoveHints[4] = { 0 + 1 * 3,1 + 0 * 3,2 + 1 * 3,1 + 2 * 3 }; DWORD retdword; int IsSnake(COORD chk) { unsigned int ptr = QueueTail; while (ptr != QueueHead) { if (cmppos(snake[ptr], chk) == 1) return 1; inc(ptr); } return cmppos(snake[ptr], chk); } void genfood() { do { food.X = rand() % MAX_WIDTH + 1; food.Y = rand() % MAX_HEIGHT + 1; } while (IsSnake(food)); WriteConsoleOutputCharacterA(GetStdHandle(STD_OUTPUT_HANDLE), "o", 1, food, retdword); } void cls(int mode) { COORD pos={0,0}; for(pos.X=0;pos.X&VK_DOWN)
continue;
if ((((ir.Event.KeyEvent.wVirtualKeyCode - VK_LEFT) ^ movement) 1) == 0)
continue;
movement = ir.Event.KeyEvent.wVirtualKeyCode - VK_LEFT;
}
}
int main()
{
if (FALSE == GetStdHandle(STD_OUTPUT_HANDLE))
AllocConsole();
SMALL_RECT rect = { 0,0,MAX_WIDTH + 1,MAX_HEIGHT + 1 };
SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), TRUE, rect);
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),food);
srand(GetTickCount());
cls(1);
genfood();
while (1)
{
ChkKey();
head.X += (SHORT)(MoveHints[movement] % 3 - 1);
head.Y += (SHORT)(MoveHints[movement] / 3 - 1);
(cmppos(food, head)) ? genfood() : removetail();
go(head);
Sleep(500);
}
}


能…

试著写了一下…总之我的原则是尽量避免不规范表达,然后在100行内尽量保证可读性…

不过毕竟为了压到100行的话…写了点比较脏的东西…

如果发现这里有不规范的用法的话…请务必提醒我…多谢了…

#include &
#include &
#define MAX_WIDTH (30)
#define MAX_HEIGHT (30)
#define MAX_SIZE (MAX_WIDTH*MAX_HEIGHT)
typedef struct POS
{
signed int x;
signed int y;
}POS;
const signed int MoveHints[4] = { 0+1*3,1+0*3,2+1*3,1+2*3 };
POS snake[MAX_SIZE] = { {MAX_WIDTH / 2,MAX_HEIGHT / 2} }, food;
unsigned int QueueHead = 0, QueueTail = 0, movement = 2;
void inc(unsigned int* pint)
{
if (++(*pint) == MAX_SIZE)
*pint = 0;
}
#define cmppos(left, right) ((left.x == right.x left.y == right.y) ? 1 : 0)
int IsSnake(POS chk)
{
unsigned int ptr = QueueTail;
while (ptr != QueueHead)
{
if (cmppos(snake[ptr], chk) == 1)
return 1;
inc(ptr);
}
return cmppos(snake[ptr], chk);
}
void genfood()
{
do
{
food.x = rand() % MAX_WIDTH;
food.y = rand() % MAX_HEIGHT;
}while (IsSnake(food));
printf(" 33[%d;%dHo 33[%d;%dH", food.y,food.x,MAX_HEIGHT,MAX_WIDTH);
}
void cls(int mode) {
for (int i = 0; i &<= MAX_HEIGHT; i++) for (int j = 0; j &<= MAX_WIDTH; j++) if(mode==1 (i==MAX_HEIGHT||j==MAX_WIDTH)) printf(" 33[%d;%dH#", i, j); else printf(" 33[%d;%dH ", i, j); } void go(POS p) { if (p.x &>= MAX_WIDTH || p.x &<= 0 || p.y &>= MAX_HEIGHT || p.y &<= 0 || IsSnake(p)) { cls(0); printf(" 33[0;0HDIE! Score:%d ",(QueueHead+MAX_SIZE-QueueTail)%MAX_SIZE); exit(0); } inc(QueueHead); snake[QueueHead] = p; printf(" 33[%d;%dHx 33[%d;%dH", p.y, p.x,MAX_HEIGHT,MAX_WIDTH); } void removetail() { printf(" 33[%d;%dH 33[%d;%dH", snake[QueueTail].y, snake[QueueTail].x,MAX_HEIGHT,MAX_WIDTH); inc(QueueTail); } void ChkKey() { while (1) { INPUT_RECORD ir; DWORD dw; PeekConsoleInput(GetStdHandle(STD_INPUT_HANDLE), ir, 1, dw); if (dw==0) break; ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), ir, 1, dw); if (ir.EventType != KEY_EVENT || !ir.Event.KeyEvent.bKeyDown || ir.Event.KeyEvent.wVirtualKeyCode&VK_DOWN)
continue;
if ((((ir.Event.KeyEvent.wVirtualKeyCode -VK_LEFT)^movement)1)==0)
continue;
movement = ir.Event.KeyEvent.wVirtualKeyCode - VK_LEFT;
}
}
int main()
{
srand(GetTickCount());
cls(1);
genfood();
while (1)
{
ChkKey();
POS t = (POS){
.x = snake[QueueHead].x + MoveHints[movement] % 3 - 1,
.y = snake[QueueHead].y + MoveHints[movement] / 3 - 1
};
go(t);
(cmppos(food, t)) ? genfood() : removetail();
Sleep(500);
}
}


不想额外调用ncurses或者conio,所以代码就冗长了一些(86行,含空行和注释)。当然那一串常量定义可以省略掉,但那样可读性就更差了:

/* NOTE: Only tested on x86-64 Linux / macOS. */
#include & #include &
#include &
#include &
#include &
#include &
#include &

const int kWidth = 77;
const int kHeight = 21;
const int kMapSize = (kWidth + 2) * (kHeight + 2);
const int kHorizontalTickTimeUs = 100000;
const int kVerticalTickTimeUs = 175000; /* Vertical moves look faster. */
const int kFoodFlag = -1;
const int kEmpty = 0;
const int kWall = INT_MAX;
const char kClearScreen[] = "e[1;1He[2J";
const char kEscapeChar = 27;
const char kEscapeCharBeforeArrow = 91;
const char kKeyUpChar = 65; /* Not A! Same below. */
const char kKeyDownChar = 66;
const char kKeyLeftChar = 68;
const char kKeyRightChar = 67;

struct termios old_attr;

void restore_termios() { tcsetattr(STDIN_FILENO, TCSANOW, old_attr); }

int main(int argc, char* argv[]) {
struct termios attr;
tcgetattr(STDIN_FILENO, attr);
memcpy(old_attr, attr, sizeof(attr));
attr.c_lflag = ~(ICANON | ECHO);
attr.c_cc[VMIN] = attr.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, attr); /* Get chars without a newline. */
atexit(restore_termios); /* Restore old settings at exit. */
int map[kMapSize];
for (int i = 0; i &< kMapSize; ++i) { map[i] = i / (kWidth + 2) &< 1 || i / (kWidth + 2) &> kHeight ||
i % (kWidth + 2) &< 1 || i % (kWidth + 2) &> kWidth ? kWall : kEmpty;
}
int tick = 1, length = 1, move = 1, new_move = 1, head = kMapSize / 2 + 1;
int food = 0;
srand(time(/* timer = */ NULL));
do { food = rand() % kMapSize; } while (food == head || map[food] != kEmpty);
map[food] = kFoodFlag;
while (length &< kWidth * kHeight) { struct timeval zero_duration = { .tv_sec = 0L, .tv_usec = 0L }; fd_set fds; FD_ZERO(fds); FD_SET(/* fd = */ 0, fds); while (!select(FD_SETSIZE, fds, /* writefds = */ NULL, /* exceptfds = */ NULL, zero_duration)) { if (new_move + move != 0) { move = new_move; } if (map[head] &> tick - length) { return EXIT_FAILURE; }
if (map[head] == kFoodFlag) {
if (++length == kWidth * kHeight) { return EXIT_SUCCESS; }
do { food = rand() % kMapSize; }
while (food == head || map[food] &> tick - length);
map[food] = kFoodFlag;
}
map[head] = tick;
for (int i = 0; i &< kMapSize; ++i) { printf("%c", map[i] == kWall ? X : map[i] &> tick - length ? O :
map[i] == kFoodFlag ? + : );
printf("%s", i % (kWidth + 2) &> kWidth ? "
" : "");
}
printf(kClearScreen);
usleep(abs(move) &> 1 ? kVerticalTickTimeUs : kHorizontalTickTimeUs);
head += move;
if (++tick == kWall) { return EXIT_FAILURE; }
zero_duration.tv_sec = zero_duration.tv_usec = 0L;
FD_ZERO(fds);
FD_SET(/* fd = */ 0, fds);
}
char c; /* Ignore non-escape chars. Quit for real escapes. */
if ((c = getchar()) != kEscapeChar) { continue; }
if ((c = getchar()) != kEscapeCharBeforeArrow) { return 0; }
c = getchar();
new_move = c == kKeyUpChar ? -(kWidth + 2) :
c == kKeyDownChar ? kWidth + 2 : c == kKeyLeftChar ? -1 :
c == kKeyRightChar ? 1 : move;
}
return 0;
}

这样写用gcc编译的时候,不需要链接额外的库:

gcc snake.c -o snake

效果如下:


推荐阅读:
相关文章