-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
371 lines (328 loc) · 11.2 KB
/
main.c
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <string.h>
#include "fadecube.h"
#include "main.h"
#include "snake.h"
#define CUBE_IP "192.168.1.99"
#define CUBE_PORT 1125
#define UP 0
#define DOWN 1
#define RIGHT 2
#define LEFT 3
#define FORWARD 4
#define BACKWARD 5
#define FOODTIMEOUT 30
#define MOVE_TIME 200
#define SNAKE_INIT_LENGTH 9
int main()
{
unsigned char gameover = 0;
#ifdef DEBUG
puts( "DEBUG --- debug mode on" );
#endif
puts( "Hello world!" );
do
{
int client_socket;
struct sockaddr_in cube_address;
char user_direction = FORWARD;
coord_t food;
snake_node_t *snake_head = NULL;
pthread_mutex_t draw_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t draw_condition_var = PTHREAD_COND_INITIALIZER;
int handle_user_thread_rc;
pthread_t handle_user_thread;
handle_user_params_t handle_user_params;
int handle_snake_thread_rc;
pthread_t handle_snake_thread;
handle_snake_params_t handle_snake_params;
int handle_render_thread_rc;
pthread_t handle_render_thread;
handle_render_params_t handle_render_params;
client_socket = create_udp_socket();
cube_address = create_sockaddr( CUBE_IP, CUBE_PORT );
handle_user_params.user_direction = &user_direction;
handle_user_params.gameover = &gameover;
handle_snake_params.user_direction = &user_direction;
handle_snake_params.snake_head = &snake_head;
handle_snake_params.food = &food;
handle_snake_params.gameover = &gameover;
handle_snake_params.draw_mutex = &draw_mutex;
handle_snake_params.draw_condition_var = &draw_condition_var;
handle_render_params.client_socket = client_socket;
handle_render_params.cube_address = cube_address;
handle_render_params.snake_head = &snake_head;
handle_render_params.food = &food;
handle_render_params.gameover = &gameover;
handle_render_params.draw_mutex = &draw_mutex;
handle_render_params.draw_condition_var = &draw_condition_var;
gameover = 0;
#ifdef DEBUG
printf("thread 1, %d\n", gameover);
#endif
if( ( handle_render_thread_rc = pthread_create( &handle_render_thread, NULL, (void *)handle_render, &handle_render_params ) ) )
{
printf("Unable to create render handler thread, error: %d\n", handle_render_thread_rc );
}
if( ( handle_snake_thread_rc = pthread_create( &handle_snake_thread, NULL, (void *)handle_snake, &handle_snake_params ) ) )
{
printf("Unable to create snake handler thread, error: %d\n", handle_snake_thread_rc );
}
if( ( handle_user_thread_rc = pthread_create( &handle_user_thread, NULL, (void *)handle_user, &handle_user_params ) ) )
{
printf("Unable to create user handler thread, error: %d\n", handle_user_thread_rc );
}
#ifdef DEBUG
printf("thread 2, %d\n", gameover);
#endif
pthread_join( handle_snake_thread, NULL );
#ifdef DEBUG
printf("thread 3, %d\n", gameover);
#endif
pthread_join( handle_render_thread, NULL );
#ifdef DEBUG
printf("thread 4, %d\n", gameover);
#endif
pthread_join( handle_user_thread, NULL );
#ifdef DEBUG
printf("thread 5, %d\n", gameover);
#endif
} while( gameover != 2 );
return 0;
}
//render thread function, always blocked while something happened which modifies the cube data
void *handle_render( handle_render_params_t *params )
{
cube_frame_t cube_frame;
snake_node_t *temp_snake_node;
memset( &cube_frame, 0, sizeof( cube_frame ) );
fill_frame( &cube_frame, 0 );
send_frame_to_cube( params->client_socket, params->cube_address, &cube_frame ); //draw an empty frame
while( 1 )
{
pthread_mutex_lock( params->draw_mutex ); //lock the draw mutex
pthread_cond_wait( params->draw_condition_var, params->draw_mutex ); //unlock the mutex until there is something to draw
if( *params->gameover )
{
pthread_mutex_unlock( params->draw_mutex ); //release the mutex
return NULL;
}
fill_frame( &cube_frame, 0 ); //start the rendering
if( params->snake_head )
{
temp_snake_node = *params->snake_head; //render the snake START
while( temp_snake_node )
{
set_led( &cube_frame, temp_snake_node->data, 3 );
temp_snake_node = temp_snake_node->next;
} //render the snake END
set_led( &cube_frame, *params->food, 3 ); //render the food
}
pthread_mutex_unlock( params->draw_mutex ); //release the mutex
send_frame_to_cube( params->client_socket, params->cube_address, &cube_frame );
}
}
//snake handler, calculates the snake parts in a chained list
void *handle_snake( handle_snake_params_t *params )
{
coord_t next_coord;
char can_move = 0; //currenctly the snake stops at the wall, when this happens, this var indicates it
char last_direction = *params->user_direction;
char used_direction = *params->user_direction;
unsigned char snake_length = SNAKE_INIT_LENGTH, snake_actual_length = 0;
unsigned char food_timer = 0;
unsigned int score = 0;
memset( &next_coord, 0, sizeof( next_coord ) );
puts( "Game started");
while(1)
{
can_move = 0;
//deny the direction update if the user pressed the opposite
if( !( ( ( last_direction == FORWARD ) && ( *params->user_direction == BACKWARD ) ) ||
( ( last_direction == BACKWARD ) && ( *params->user_direction == FORWARD ) ) ||
( ( last_direction == LEFT ) && ( *params->user_direction == RIGHT ) ) ||
( ( last_direction == RIGHT ) && ( *params->user_direction == LEFT ) ) ||
( ( last_direction == UP ) && ( *params->user_direction == DOWN ) ) ||
( ( last_direction == DOWN ) && ( *params->user_direction == UP ) ) ) )
{
used_direction = *params->user_direction;
last_direction = used_direction;
}
switch( used_direction ) //calculate the next (from the last added) HEAD item coordinate for the snake
{
case FORWARD:
if( next_coord.y < 9 )
{
next_coord.y++;
can_move = 1;
}
break;
case BACKWARD:
if( next_coord.y )
{
next_coord.y--;
can_move = 1;
}
break;
case LEFT:
if( next_coord.x < 9 )
{
next_coord.x++;
can_move = 1;
}
break;
case RIGHT:
if( next_coord.x )
{
next_coord.x--;
can_move = 1;
}
break;
case UP:
if( next_coord.z < 9 )
{
next_coord.z++;
can_move = 1;
}
break;
case DOWN:
if( next_coord.z )
{
next_coord.z--;
can_move = 1;
}
break;
}
pthread_mutex_lock( params->draw_mutex ); //lock the mutex while updating the snake (avoid render thread to read now)
if( can_move ) //now this var indicates when there is a place to move forward
{
snake_node_t *temp_node;
snake_add( params->snake_head, next_coord );
temp_node = ( **params->snake_head ).next;
while( temp_node ) //check trough the snake
{
if( !( memcmp( &((**params->snake_head).data), &((*temp_node).data), sizeof( coord_t ) ) ) ) //if the head reached a part of the body
{
can_move = 0;
puts( "You catched your tail!" );
}
temp_node = temp_node->next;
}
}
if( can_move ) //now this var indicates when there is no crash between the snake head and the body
{
if( !( food_timer ) ) //time to put down another food
{
#ifdef DEBUG
puts( "new food\n" );
#endif
make_food( params->food );
}
if( ++food_timer == FOODTIMEOUT ) food_timer = 0;
if( !( memcmp( &((**params->snake_head).data), params->food, sizeof( coord_t ) ) ) ) //detect if the snake has just hit the food
{
snake_length++;
food_timer = 0;
score += 10;
#ifdef DEBUG
puts( "got it!\n" );
#endif
}
snake_actual_length = snake_count( *params->snake_head );
if( ( snake_actual_length > snake_length ) && snake_actual_length ) //if the snake was not currently growing, remove the last item
{ //therefore it will look like moving
snake_remove_last( params->snake_head );
}
#ifdef DEBUG
snake_print( *params->snake_head );
#endif
pthread_cond_signal( params->draw_condition_var ); //call render thread that there is something new to draw
pthread_mutex_unlock( params->draw_mutex );
}
else
{
while( (**params->snake_head).next )
{
snake_remove_last( params->snake_head );
#ifdef DEBUG
//printf( "a" );
#endif
}
free( *params->snake_head );
*params->gameover = 1;
printf( "Game over!\nYour score is %d.\n\nPress ESC to quit or ENTER to start new game!\n", score );
fflush(stdout);
pthread_cond_signal( params->draw_condition_var ); //call render thread that there is something new to draw
pthread_mutex_unlock( params->draw_mutex );
return NULL;
}
if( *params->gameover )
{
return NULL;
}
usleep( MOVE_TIME * 1000 ); //sleep the snake thread, this represents the speed
}
}
//user thread, it does nothing but always waits for a keyboard press
void *handle_user( handle_user_params_t *params )
{
while( 1 )
{
char my_char;
my_char = mygetch();
#ifdef DEBUG
printf( "%d ", my_char );
#endif
if( my_char == 27 )
{
*params->gameover = 2;
return NULL;
}
if( *params->gameover )
{
if( my_char == 10 ) return NULL; /* ENTER */
}
switch( my_char )
{
case 'w':
*params->user_direction = FORWARD;
break;
case 's':
*params->user_direction = BACKWARD;
break;
case 'a':
*params->user_direction = LEFT;
break;
case 'd':
*params->user_direction = RIGHT;
break;
case '\'':
*params->user_direction = UP;
break;
case '/':
*params->user_direction = DOWN;
break;
}
}
}
//the magic function, does the same as getch should
int mygetch(void)
{
struct termios oldt,
newt;
int ch;
tcgetattr( STDIN_FILENO, &oldt );
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newt );
ch = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
return ch;
}