Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
zmx0142857
GitHub Repository: zmx0142857/mini-games
Path: blob/master/c/snake/snake.c
363 views
1
#include "game.h"
2
#include <time.h> // rand(), srand(), time()
3
#include <ctype.h> // tolower()
4
5
// 地图
6
#define ROWS 20
7
#define COLS 30
8
9
// 分数
10
unsigned score;
11
12
// 用一队列保存蛇身信息
13
// 蛇身初始长度为 2, 初始位置为 (9,9), (9,8)
14
#define MAX_LEN ROWS * COLS
15
struct {
16
unsigned head; // 队头
17
unsigned tail; // 尾后位置
18
struct {
19
unsigned y[MAX_LEN+1];
20
unsigned x[MAX_LEN+1];
21
} body;
22
} snake;
23
24
// 食物
25
struct {
26
unsigned y;
27
unsigned x;
28
bool exist;
29
} food;
30
31
// 判断此坐标是否是墙
32
bool is_wall(unsigned y, unsigned x)
33
{
34
return y <= 0 || y >= ROWS-1 || x <= 0 || x >= COLS-1;
35
}
36
37
// 判断此坐标是否是食物
38
bool is_food(unsigned y, unsigned x)
39
{
40
return food.exist && y == food.y && x == food.x;
41
}
42
43
// 判断此坐标是否是蛇身
44
bool is_body(unsigned y, unsigned x)
45
{
46
unsigned i;
47
for (i = snake.head; i != snake.tail; i = (i+1) % (MAX_LEN+1))
48
if (y == snake.body.y[i] && x == snake.body.x[i])
49
break;
50
// i == snake.tail 当且仅当 (y, x) 与蛇身不重合
51
return i != snake.tail;
52
}
53
54
// 生成食物
55
void food_create()
56
{
57
if (!food.exist) {
58
do {
59
food.y = rand() % (ROWS-2) + 1;
60
food.x = rand() % (COLS-2) + 1;
61
} while (is_body(food.y, food.x));
62
food.exist = true;
63
}
64
}
65
66
// 打印地图
67
void map_print()
68
{
69
unsigned y, x;
70
for (y = 0; y != ROWS; ++y) {
71
for (x = 0; x != COLS; ++x) {
72
if (is_wall(y, x))
73
printf(SYM_SQUARE);
74
else if (is_body(y, x))
75
printf(SYM_SQUARE);
76
else if (is_food(y, x))
77
printf(SYM_DIAMOND);
78
else
79
printf(SYM_BLANK);
80
}
81
putchar('\n');
82
}
83
printf("\nScores: 0\nUse wasd to play.\n");
84
}
85
86
// 初始化
87
void init()
88
{
89
screen_clear();
90
srand(time(NULL));
91
92
score = 0;
93
94
snake.head = 0;
95
snake.tail = 2;
96
snake.body.y[0] = 9;
97
snake.body.x[0] = 9;
98
snake.body.y[1] = 9;
99
snake.body.x[1] = 8;
100
101
food.y = 0;
102
food.x = 0;
103
food.exist = false;
104
105
food_create();
106
map_print();
107
}
108
109
// 主循环
110
void play()
111
{
112
char key;
113
char last_key = 'd'; // 蛇头初始向右
114
115
// 读键盘,不回显
116
key = tolower(getch());
117
while (true) {
118
bool valid = ( (key == 'w' && last_key != 's')
119
|| (key == 's' && last_key != 'w')
120
|| (key == 'a' && last_key != 'd')
121
|| (key == 'd' && last_key != 'a') );
122
if (valid)
123
last_key = key;
124
else
125
key = last_key;
126
127
unsigned new_y = snake.body.y[snake.head];
128
unsigned new_x = snake.body.x[snake.head];
129
switch (key) {
130
case 'w': --new_y; break;
131
case 's': ++new_y; break;
132
case 'a': --new_x; break;
133
case 'd': ++new_x; break;
134
}
135
136
if (is_wall(new_y, new_x)) { // 撞墙了吗
137
break;
138
} else if (is_food(new_y, new_x)) { // 吃到食物了吗
139
score += 10 + rand() % 11;
140
mvprint(ROWS+1, 8, "%d", score);
141
food.exist = false;
142
food_create();
143
mvprint(food.y, 2*food.x, SYM_DIAMOND);
144
} else { // 消去尾部
145
snake.tail = (snake.tail == 0 ? MAX_LEN : snake.tail-1);
146
mvprint(snake.body.y[snake.tail], 2*snake.body.x[snake.tail], SYM_BLANK);
147
if (is_body(new_y, new_x)) // 咬到自己了吗
148
break;
149
}
150
151
// 现在安全,可以绘制头部
152
mvprint(new_y, 2*new_x, SYM_SQUARE);
153
154
// 将新坐标入队
155
snake.head = (snake.head == 0 ? MAX_LEN : snake.head-1);
156
snake.body.y[snake.head] = new_y;
157
snake.body.x[snake.head] = new_x;
158
159
Sleep(32765/(score+200)+40); // 分数越高,移动越快
160
161
// 判断是否按下按键
162
if (kbhit())
163
key = tolower(getch());
164
165
}
166
}
167
168
bool new_game()
169
{
170
mvprint(ROWS+2, 0, "Game over! <ESC> to quit, any other key to restart\n");
171
return getch() != 27;
172
}
173
174
int main()
175
{
176
game_init();
177
screen_size(ROWS+4, COLS*2+1);
178
179
do {
180
init();
181
play();
182
} while (new_game());
183
184
game_over();
185
return 0;
186
}
187
188