jag

snake_c

public

Small snake game in C

Star0
Watch0
eb9aaa56ed1eafb7a92f2828049e235d656424a7
2
ssh://git@git.jaglekon.de:30/jag/snake_c.git
snake.cC354 lines6.7 KB
1
#include <unistd.h>
2
3
#include <stdio.h>
4
#include <stdint.h>
5
#include <stdbool.h>
6
#include <stdlib.h>
7
8
#include <time.h>
9
#include <sys/time.h>
10
#include <string.h>
11
#include <termios.h>
12
13
#define RIGHT_CODE 'C'
14
#define LEFT_CODE 'D'
15
#define UP_CODE 'A'
16
#define DOWN_CODE 'B'
17
18
#define WIDTH 50
19
#define HEIGHT 50
20
21
#define SPEED 100 /* in ms */
22
23
#define MAX_LENGTH 1000
24
#define STARTING_LENGTH 2
25
26
typedef enum {
27
  UP, DOWN, LEFT, RIGHT
28
} direction;
29
30
typedef enum {
31
  EMPTY, SNAKE, HEAD, FRUIT
32
} field_content;
33
34
typedef struct selected {
35
  int16_t x;
36
  int16_t y;
37
} selected;
38
39
typedef struct snake {
40
  selected position[MAX_LENGTH];
41
  uint16_t length;
42
} snake;
43
44
45
void update_field(snake my_snake, field_content board[HEIGHT][WIDTH], selected fruit);
46
selected new_fruit(field_content board[HEIGHT][WIDTH]);
47
48
void draw_board(field_content board[HEIGHT][WIDTH], bool redraw);
49
void draw_pause();
50
51
direction read_direction (char* string, direction shift);
52
53
54
int main() {
55
56
  struct termios old, new;
57
58
  tcgetattr(STDIN_FILENO, &old);
59
60
  new = old;
61
62
  new.c_lflag &= ~(ECHO | ICANON);
63
  new.c_cc[VMIN] = 0;
64
  new.c_cc[VTIME] = 1;
65
66
  tcsetattr(STDIN_FILENO, TCSANOW, &new);
67
68
  srand(time(NULL));
69
70
71
  snake my_snake = {};  
72
  my_snake.length = STARTING_LENGTH;
73
74
  {
75
    uint16_t i;
76
    
77
    my_snake.position[0].x = HEIGHT >> 1;
78
    my_snake.position[0].y = WIDTH >> 1;
79
    for (i = 1; i < my_snake.length; i++) {
80
      my_snake.position[i].y = -1;
81
      my_snake.position[i].x = -1;
82
    }
83
  }
84
85
  selected fruit = {-1, -1};
86
87
  field_content board[HEIGHT][WIDTH] = {0};
88
89
  update_field(my_snake, board, fruit);
90
91
  fruit = new_fruit(board);
92
  update_field(my_snake, board, fruit);
93
94
95
  printf("\033[?25l");
96
  draw_board(board, false);
97
98
99
  direction shift = RIGHT;
100
101
102
  struct timeval start, current;
103
104
  gettimeofday(&start, NULL);
105
106
107
  bool paused = false;
108
  bool run = true;
109
110
  while (run) {
111
112
    char string[1000] = {};
113
    ssize_t n = read(STDIN_FILENO, string, sizeof(string) - 1);
114
    
115
    gettimeofday(&current, NULL);
116
117
    if (n > 0) {
118
      string[n] = '\0';
119
  
120
      if (!paused)
121
        shift = read_direction(string, shift);
122
123
      if (strstr(string, "q"))
124
        run = false;
125
126
       if (strstr(string, "p")) {
127
          paused = !paused;
128
          if (paused)
129
            draw_pause();
130
      }
131
    }
132
133
    uint64_t time_diff = (current.tv_sec - start.tv_sec) * 1000000 + (current.tv_usec - start.tv_usec);
134
135
    if ((time_diff >= SPEED * 1000) && !paused) {
136
      
137
      start = current;
138
139
      uint16_t i;
140
      for (i = my_snake.length; i > 0; i--) {
141
        my_snake.position[i].y = my_snake.position[i - 1].y;
142
        my_snake.position[i].x = my_snake.position[i - 1].x;
143
      }
144
145
      switch (shift) {
146
        case UP:
147
          my_snake.position[0].y = ((my_snake.position[0].y + HEIGHT - 1) % HEIGHT);
148
          break;
149
        case DOWN:
150
          my_snake.position[0].y = ((my_snake.position[0].y + 1) % HEIGHT);
151
          break;
152
        case LEFT:
153
          my_snake.position[0].x = ((my_snake.position[0].x + WIDTH - 1) % WIDTH);
154
          break;
155
        case RIGHT:
156
          my_snake.position[0].x = ((my_snake.position[0].x + 1) % WIDTH);
157
          break;
158
      }
159
    
160
      uint8_t head_y = my_snake.position[0].y;
161
      uint8_t head_x = my_snake.position[0].x;
162
163
      for (i = 1; i < my_snake.length; i++) {
164
        int16_t x = my_snake.position[i].x;
165
        int16_t y = my_snake.position[i].y;
166
167
        if (
168
          x == head_x && 
169
          y == head_y
170
        ) {
171
          printf("\n\nHAHA LOST!!!\n\n");
172
          goto end_of_program;
173
        }
174
      }
175
176
      if (head_y == fruit.y && head_x == fruit.x) {
177
        my_snake.length++;
178
        fruit = new_fruit(board);
179
      }
180
181
182
      update_field(my_snake, board, fruit);
183
      draw_board(board, true);
184
    }
185
186
  }
187
188
end_of_program:
189
190
  tcsetattr(STDIN_FILENO, TCSANOW, &old);
191
  printf("\033[?25h");
192
193
  return 0;
194
}
195
196
197
198
199
200
void update_field(snake my_snake, field_content board[HEIGHT][WIDTH], selected fruit) {
201
  
202
  uint8_t i, j;
203
204
  for (i = 0; i < HEIGHT; i++) {
205
    for (j = 0; j < WIDTH; j++) {   
206
      board[i][j] = EMPTY;
207
    }
208
  }
209
210
  for (i = 0; i < my_snake.length; i++) {
211
    int16_t x = my_snake.position[i].x;
212
    int16_t y = my_snake.position[i].y;
213
    if (x >= 0 && y >= 0)
214
      board[y][x] = (i == 0) ? HEAD : SNAKE;
215
  }
216
  
217
  if (fruit.x >= 0 && fruit.y >= 0)
218
    board[fruit.y][fruit.x] = FRUIT;
219
}
220
221
222
selected new_fruit(field_content board[HEIGHT][WIDTH]) {
223
  bool found_space = false;
224
225
  uint8_t x;
226
  uint8_t y;
227
228
  while (!found_space) {
229
    x = rand() % WIDTH;
230
    y = rand() % HEIGHT;
231
232
    if (board[y][x] == EMPTY)
233
      found_space = true;
234
  }
235
236
  selected fruit = {y, x};
237
238
  return fruit;
239
}
240
241
242
void draw_board (field_content board[HEIGHT][WIDTH], bool redraw) {
243
  
244
  uint8_t i, j; 
245
246
  if (redraw) {
247
    for (i = 0; i < HEIGHT + 2; i++)
248
      printf("\033[A");
249
    printf("\r");
250
  }
251
252
  printf("+");
253
  for (i = 0; i < WIDTH; i++)
254
    printf("--");
255
  printf("+\n");
256
257
  for ( i = 0; i < HEIGHT; i++) {
258
259
    printf("|");
260
261
    for (j = 0; j < WIDTH; j++) {
262
263
      switch (board[i][j]) {
264
        case EMPTY:
265
          printf("  ");
266
          break;
267
        case SNAKE:
268
          printf("\033[31m x\033[0m");
269
          break;
270
        case HEAD:
271
          printf("\033[32m X\033[0m");
272
          break;
273
        case FRUIT:
274
          printf("\033[34m O\033[0m");
275
          break;
276
      }
277
    }
278
    printf("|\n");
279
  }
280
281
    printf("+");
282
  for (i = 0; i < WIDTH; i++)
283
    printf("--");
284
  printf("+\n");
285
}
286
287
288
void draw_pause() {
289
290
  const uint8_t height = 5;
291
  const uint8_t width = 14;
292
293
  char *pause_menu [] = {
294
    "+------------+",
295
    "|            |",
296
    "|   PAUSED   |",
297
    "|            |",
298
    "+------------+"
299
  };
300
301
  uint8_t i, j;
302
303
  const uint8_t going_up = (HEIGHT + height) >> 1;
304
  const uint8_t going_right = (WIDTH * 2 - width) >> 1;
305
306
  for (i = 0; i < going_up; i++)
307
    printf("\033[%c", UP_CODE);
308
  printf("\r");
309
310
  for (i = 0; i < height; i++) {
311
    for (j = 0; j < going_right; j++)
312
      printf("\033[%c", RIGHT_CODE);
313
314
    printf("\033[30m%s\033[0m\033[%c\r", pause_menu[i], DOWN_CODE);
315
  }
316
317
  for (i = 0; i < going_up - height - 1; i++)
318
    printf("\033[%c", DOWN_CODE);
319
  printf("\n\r");
320
321
  fflush(stdout);
322
}
323
324
325
direction read_direction (char* string, direction shift) {
326
327
  if (strlen(string) >= 3) {
328
    if (string[0] == '\033') {
329
      if (string[1] == '[') {
330
       
331
        switch (string[2]) {
332
          case RIGHT_CODE:
333
            if (shift != LEFT)
334
              return RIGHT;
335
            break;
336
          case LEFT_CODE:
337
            if (shift != RIGHT)
338
              return LEFT;
339
            break;
340
          case UP_CODE:
341
            if (shift != DOWN)
342
              return UP;
343
            break;
344
          case DOWN_CODE:
345
            if (shift != UP)
346
              return DOWN;
347
            break;
348
        }          
349
      }
350
    }
351
  }
352
353
  return shift;
354
}