jag

snake_c

public

Small snake game in C

Star0
Watch0
538a98a3ec0329deee9df8032705b0bfc11ccf91
2
ssh://git@git.jaglekon.de:30/jag/snake_c.git
snake.cC355 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
            if (head_y == fruit.y && head_x == fruit.x) {
164
        my_snake.length++;
165
        fruit = new_fruit(board);
166
      }
167
168
169
      update_field(my_snake, board, fruit);
170
      draw_board(board, true);
171
172
      for (i = 1; i < my_snake.length; i++) {
173
        int16_t x = my_snake.position[i].x;
174
        int16_t y = my_snake.position[i].y;
175
176
        if (
177
          x == head_x && 
178
          y == head_y
179
        ) {
180
          printf("\n\nHAHA LOST!!!\n\n");
181
          goto end_of_program;
182
        }
183
      }
184
185
    }
186
187
  }
188
189
end_of_program:
190
191
  tcsetattr(STDIN_FILENO, TCSANOW, &old);
192
  printf("\033[?25h");
193
194
  return 0;
195
}
196
197
198
199
200
201
void update_field(snake my_snake, field_content board[HEIGHT][WIDTH], selected fruit) {
202
  
203
  uint8_t i, j;
204
205
  for (i = 0; i < HEIGHT; i++) {
206
    for (j = 0; j < WIDTH; j++) {   
207
      board[i][j] = EMPTY;
208
    }
209
  }
210
211
  for (i = 0; i < my_snake.length; i++) {
212
    int16_t x = my_snake.position[i].x;
213
    int16_t y = my_snake.position[i].y;
214
    if (x >= 0 && y >= 0)
215
      board[y][x] = (i == 0) ? HEAD : SNAKE;
216
  }
217
  
218
  if (fruit.x >= 0 && fruit.y >= 0)
219
    board[fruit.y][fruit.x] = FRUIT;
220
}
221
222
223
selected new_fruit(field_content board[HEIGHT][WIDTH]) {
224
  bool found_space = false;
225
226
  uint8_t x;
227
  uint8_t y;
228
229
  while (!found_space) {
230
    x = rand() % WIDTH;
231
    y = rand() % HEIGHT;
232
233
    if (board[y][x] == EMPTY)
234
      found_space = true;
235
  }
236
237
  selected fruit = {y, x};
238
239
  return fruit;
240
}
241
242
243
void draw_board (field_content board[HEIGHT][WIDTH], bool redraw) {
244
  
245
  uint8_t i, j; 
246
247
  if (redraw) {
248
    for (i = 0; i < HEIGHT + 2; i++)
249
      printf("\033[A");
250
    printf("\r");
251
  }
252
253
  printf("+");
254
  for (i = 0; i < WIDTH; i++)
255
    printf("--");
256
  printf("+\n");
257
258
  for ( i = 0; i < HEIGHT; i++) {
259
260
    printf("|");
261
262
    for (j = 0; j < WIDTH; j++) {
263
264
      switch (board[i][j]) {
265
        case EMPTY:
266
          printf("  ");
267
          break;
268
        case SNAKE:
269
          printf("\033[31m x\033[0m");
270
          break;
271
        case HEAD:
272
          printf("\033[32m X\033[0m");
273
          break;
274
        case FRUIT:
275
          printf("\033[34m O\033[0m");
276
          break;
277
      }
278
    }
279
    printf("|\n");
280
  }
281
282
    printf("+");
283
  for (i = 0; i < WIDTH; i++)
284
    printf("--");
285
  printf("+\n");
286
}
287
288
289
void draw_pause() {
290
291
  const uint8_t height = 5;
292
  const uint8_t width = 14;
293
294
  char *pause_menu [] = {
295
    "+------------+",
296
    "|            |",
297
    "|   PAUSED   |",
298
    "|            |",
299
    "+------------+"
300
  };
301
302
  uint8_t i, j;
303
304
  const uint8_t going_up = (HEIGHT + height) >> 1;
305
  const uint8_t going_right = (WIDTH * 2 - width) >> 1;
306
307
  for (i = 0; i < going_up; i++)
308
    printf("\033[%c", UP_CODE);
309
  printf("\r");
310
311
  for (i = 0; i < height; i++) {
312
    for (j = 0; j < going_right; j++)
313
      printf("\033[%c", RIGHT_CODE);
314
315
    printf("\033[30m%s\033[0m\033[%c\r", pause_menu[i], DOWN_CODE);
316
  }
317
318
  for (i = 0; i < going_up - height; i++)
319
    printf("\033[%c", DOWN_CODE);
320
  printf("\r");
321
322
  fflush(stdout);
323
}
324
325
326
direction read_direction (char* string, direction shift) {
327
328
  if (strlen(string) >= 3) {
329
    if (string[0] == '\033') {
330
      if (string[1] == '[') {
331
       
332
        switch (string[2]) {
333
          case RIGHT_CODE:
334
            if (shift != LEFT)
335
              return RIGHT;
336
            break;
337
          case LEFT_CODE:
338
            if (shift != RIGHT)
339
              return LEFT;
340
            break;
341
          case UP_CODE:
342
            if (shift != DOWN)
343
              return UP;
344
            break;
345
          case DOWN_CODE:
346
            if (shift != UP)
347
              return DOWN;
348
            break;
349
        }          
350
      }
351
    }
352
  }
353
354
  return shift;
355
}