| 240 |
Kevin |
1 |
#include "defines.h"
|
|
|
2 |
#include "CONTROLLERS.h"
|
|
|
3 |
#include "SNAKE.h"
|
| 276 |
Kevin |
4 |
#include "TIMER4.h"
|
| 240 |
Kevin |
5 |
|
| 278 |
Kevin |
6 |
static SNAKE_DATA *snake_data_p;
|
| 242 |
Kevin |
7 |
static uint32_t rand_value __attribute__((persistent));
|
| 240 |
Kevin |
8 |
|
|
|
9 |
void Snake_Init(SNAKE_DATA *data) {
|
| 278 |
Kevin |
10 |
snake_data_p = data;
|
| 240 |
Kevin |
11 |
|
|
|
12 |
// Set starting point
|
| 278 |
Kevin |
13 |
snake_data_p->body[0] = (SNAKE_POINT){0,0,7};
|
| 240 |
Kevin |
14 |
|
| 278 |
Kevin |
15 |
snake_data_p->pos_head = 0;
|
|
|
16 |
snake_data_p->pos_tail = 0;
|
|
|
17 |
snake_data_p->length = 1;
|
|
|
18 |
snake_data_p->level = 0;
|
|
|
19 |
snake_data_p->delay = SNAKE_MAXIMUM_DELAY;
|
| 240 |
Kevin |
20 |
|
| 278 |
Kevin |
21 |
// Initialize the starting direction
|
|
|
22 |
snake_data_p->direction = (SNAKE_POINT){1,0,7};
|
|
|
23 |
snake_data_p->last_direction = 0x08;
|
|
|
24 |
|
| 242 |
Kevin |
25 |
srand(rand_value);
|
|
|
26 |
|
| 240 |
Kevin |
27 |
// Generate a starting location for the candy
|
| 278 |
Kevin |
28 |
snake_data_p->candy_loc = Snake_Generate_Candy();
|
| 240 |
Kevin |
29 |
|
|
|
30 |
// Draw the snake (head)
|
|
|
31 |
Cube_Clear();
|
| 278 |
Kevin |
32 |
uint32_t index = snake_data_p->pos_head;
|
|
|
33 |
Cube_Set_Pixel(snake_data_p->body[index].z, snake_data_p->body[index].x, snake_data_p->body[index].y, SNAKE_HEAD_COLOR);
|
|
|
34 |
while (index != snake_data_p->pos_tail) {
|
|
|
35 |
if (snake_data_p->length > 1) {
|
| 240 |
Kevin |
36 |
index = (index == 0) ? CUBE_PIXELS - 1 : index - 1;
|
| 278 |
Kevin |
37 |
Cube_Set_Pixel(snake_data_p->body[index].z, snake_data_p->body[index].x, snake_data_p->body[index].y, SNAKE_BODY_COLOR);
|
| 240 |
Kevin |
38 |
}
|
|
|
39 |
}
|
|
|
40 |
}
|
|
|
41 |
|
|
|
42 |
void Snake_Main(void) {
|
| 241 |
Kevin |
43 |
// Main function, loops and delays while updating the frame every x milliseconds
|
| 276 |
Kevin |
44 |
|
| 278 |
Kevin |
45 |
// Ensure that a controller is connected before starting
|
| 276 |
Kevin |
46 |
while(!Controller_Get_Connected()) {
|
|
|
47 |
Delay_MS(100);
|
|
|
48 |
Controller_Poll_Connected();
|
|
|
49 |
}
|
|
|
50 |
|
| 278 |
Kevin |
51 |
// Set the first controller as active and indicate it on its LEDs
|
| 276 |
Kevin |
52 |
Controller_Set_Active(0);
|
|
|
53 |
Delay_MS(20);
|
|
|
54 |
Controller_Set_Left_Leds(0, 0x1);
|
|
|
55 |
TIMER4_Start();
|
|
|
56 |
Delay_MS(1000);
|
| 240 |
Kevin |
57 |
while (1) {
|
| 242 |
Kevin |
58 |
// Regenerate the seed upon each update so that the candy starts somewhere new every time
|
|
|
59 |
rand_value = rand();
|
| 276 |
Kevin |
60 |
|
| 240 |
Kevin |
61 |
Snake_Update_Frame();
|
| 278 |
Kevin |
62 |
Delay_MS(snake_data_p->delay);
|
| 240 |
Kevin |
63 |
}
|
|
|
64 |
}
|
|
|
65 |
|
| 276 |
Kevin |
66 |
void Snake_Update_Direction(uint8_t controller, CTRL_BTN_STATUS value) {
|
| 240 |
Kevin |
67 |
// Determine the next direction for the snake based off the last button press
|
| 276 |
Kevin |
68 |
if (controller == 0) {
|
| 278 |
Kevin |
69 |
snake_data_p->last_direction = value.w;
|
|
|
70 |
SNAKE_POINT point = snake_data_p->body[snake_data_p->pos_head];
|
| 240 |
Kevin |
71 |
|
| 276 |
Kevin |
72 |
if (value.BTN_L_N || value.BTN_L_E) { // Up
|
|
|
73 |
point.z = (point.z == CUBE_LAYER_COUNT - 1) ? 0 : point.z + 1;
|
|
|
74 |
} else if (value.BTN_L_W || value.BTN_L_S) { // Down
|
|
|
75 |
point.z = (point.z == 0) ? CUBE_LAYER_COUNT - 1 : point.z - 1;
|
|
|
76 |
} else if (value.BTN_R_N) { // Forward
|
|
|
77 |
point.x = (point.x == CUBE_ROW_COUNT - 1) ? 0 : point.x + 1;
|
|
|
78 |
} else if (value.BTN_R_W) { // Right
|
|
|
79 |
point.y = (point.y == CUBE_COLUMN_COUNT - 1) ? 0 : point.y + 1;
|
|
|
80 |
} else if (value.BTN_R_S) { // Backward
|
|
|
81 |
point.x = (point.x == 0) ? CUBE_ROW_COUNT - 1 : point.x - 1;
|
|
|
82 |
} else if (value.BTN_R_E) { // Left
|
|
|
83 |
point.y = (point.y== 0) ? CUBE_COLUMN_COUNT - 1 : point.y - 1;
|
|
|
84 |
}
|
|
|
85 |
|
| 278 |
Kevin |
86 |
snake_data_p->direction = point;
|
| 240 |
Kevin |
87 |
}
|
|
|
88 |
|
|
|
89 |
// Update the overlay with the candy location
|
|
|
90 |
Cube_Overlay_Clear();
|
| 278 |
Kevin |
91 |
Cube_Overlay_Set_Pixel(snake_data_p->candy_loc.z, snake_data_p->candy_loc.x, snake_data_p->candy_loc.y, SNAKE_CANDY_COLOR);
|
| 240 |
Kevin |
92 |
}
|
|
|
93 |
|
|
|
94 |
void Snake_Update_Frame(void) {
|
|
|
95 |
uint8_t om_nom_nom = 0;
|
|
|
96 |
|
|
|
97 |
// Check if we are moving onto a candy, if so extend body
|
| 278 |
Kevin |
98 |
if (snake_data_p->direction.x == snake_data_p->candy_loc.x &&
|
|
|
99 |
snake_data_p->direction.y == snake_data_p->candy_loc.y &&
|
|
|
100 |
snake_data_p->direction.z == snake_data_p->candy_loc.z) {
|
|
|
101 |
snake_data_p->pos_head = (snake_data_p->pos_head == CUBE_PIXELS - 1) ? 0 : snake_data_p->pos_head + 1;
|
|
|
102 |
snake_data_p->body[snake_data_p->pos_head] = snake_data_p->direction;
|
|
|
103 |
snake_data_p->length++;
|
|
|
104 |
snake_data_p->candy_loc = Snake_Generate_Candy();
|
| 240 |
Kevin |
105 |
om_nom_nom = 1;
|
|
|
106 |
}
|
|
|
107 |
|
|
|
108 |
// Check if the location that we are moving to is overlapping the body
|
| 278 |
Kevin |
109 |
uint32_t pos = snake_data_p->pos_tail;
|
|
|
110 |
while (pos != snake_data_p->pos_head) {
|
|
|
111 |
if (snake_data_p->direction.x == snake_data_p->body[pos].x &&
|
|
|
112 |
snake_data_p->direction.y == snake_data_p->body[pos].y &&
|
|
|
113 |
snake_data_p->direction.z == snake_data_p->body[pos].z) {
|
| 241 |
Kevin |
114 |
// Indicate the overlapping pixel, delay, then return to idle state
|
| 278 |
Kevin |
115 |
Cube_Set_Pixel(snake_data_p->direction.z, snake_data_p->direction.x, snake_data_p->direction.y, SNAKE_COLLISION_COLOR);
|
| 240 |
Kevin |
116 |
Delay_MS(3000);
|
| 242 |
Kevin |
117 |
Cube_Overlay_Clear();
|
|
|
118 |
Animation_Cube_In_Out(200, ORANGE);
|
| 240 |
Kevin |
119 |
Reset_Board(BOARD_MODE_IDLE);
|
|
|
120 |
}
|
|
|
121 |
pos = (pos == CUBE_PIXELS - 1) ? 0 : pos + 1;
|
|
|
122 |
}
|
|
|
123 |
|
|
|
124 |
// If we didnt eat a candy, increment the frame to move the body along
|
|
|
125 |
if (!om_nom_nom) {
|
| 278 |
Kevin |
126 |
snake_data_p->pos_head = (snake_data_p->pos_head == CUBE_PIXELS - 1) ? 0 : snake_data_p->pos_head + 1;
|
|
|
127 |
snake_data_p->pos_tail = (snake_data_p->pos_tail == CUBE_PIXELS - 1) ? 0 : snake_data_p->pos_tail + 1;
|
|
|
128 |
snake_data_p->body[snake_data_p->pos_head] = snake_data_p->direction;
|
| 240 |
Kevin |
129 |
}
|
|
|
130 |
|
|
|
131 |
// Draw updated snake location
|
|
|
132 |
Cube_Clear();
|
| 278 |
Kevin |
133 |
uint32_t index = snake_data_p->pos_head;
|
|
|
134 |
Cube_Set_Pixel(snake_data_p->body[index].z, snake_data_p->body[index].x, snake_data_p->body[index].y, SNAKE_HEAD_COLOR);
|
|
|
135 |
while (index != snake_data_p->pos_tail) {
|
|
|
136 |
if (snake_data_p->length > 1) {
|
| 240 |
Kevin |
137 |
index = (index == 0) ? CUBE_PIXELS - 1 : index - 1;
|
| 278 |
Kevin |
138 |
Cube_Set_Pixel(snake_data_p->body[index].z, snake_data_p->body[index].x, snake_data_p->body[index].y, SNAKE_BODY_COLOR);
|
| 240 |
Kevin |
139 |
}
|
|
|
140 |
}
|
|
|
141 |
|
| 278 |
Kevin |
142 |
// Determine the next point to move to
|
|
|
143 |
Snake_Update_Direction(0, (CTRL_BTN_STATUS)snake_data_p->last_direction);
|
| 240 |
Kevin |
144 |
|
|
|
145 |
// If we ate a candy, delay for a bit to rest
|
|
|
146 |
if (om_nom_nom) {
|
| 276 |
Kevin |
147 |
// Increase the level by one
|
| 278 |
Kevin |
148 |
snake_data_p->level += 1;
|
| 276 |
Kevin |
149 |
|
|
|
150 |
TIMER4_Stop();
|
| 278 |
Kevin |
151 |
Controller_Set_Middle_Leds(0, snake_data_p->level);
|
|
|
152 |
if (snake_data_p->level >= 256)
|
| 276 |
Kevin |
153 |
Controller_Set_Left_Leds(0, 0x9);
|
|
|
154 |
TIMER4_Start();
|
|
|
155 |
|
| 241 |
Kevin |
156 |
// Decrease the delay between frame updates by 5ms
|
| 278 |
Kevin |
157 |
if (snake_data_p->delay > SNAKE_MINIMUM_DELAY)
|
|
|
158 |
snake_data_p->delay -= 5;
|
| 241 |
Kevin |
159 |
// Clear the watchdog timer to prevent resets in a middle of a game
|
|
|
160 |
ClearWDT();
|
| 240 |
Kevin |
161 |
}
|
|
|
162 |
}
|
|
|
163 |
|
|
|
164 |
SNAKE_POINT Snake_Generate_Candy(void) {
|
|
|
165 |
// Generates a random position within the cube that doesnt overlap anything
|
|
|
166 |
SNAKE_POINT ret;
|
|
|
167 |
uint32_t x, y, z, brk = 0;
|
|
|
168 |
while(1) {
|
|
|
169 |
x = rand() % 8;
|
|
|
170 |
y = rand() % 8;
|
|
|
171 |
z = rand() % 8;
|
|
|
172 |
|
| 278 |
Kevin |
173 |
if (snake_data_p->length != 1) {
|
|
|
174 |
uint32_t pos = snake_data_p->pos_tail;
|
| 240 |
Kevin |
175 |
uint32_t overlap = 0;
|
|
|
176 |
// Iterate through the frame till we finish or find an overlap
|
| 278 |
Kevin |
177 |
while (pos != snake_data_p->pos_head) {
|
|
|
178 |
if (snake_data_p->body[pos].x == x &&
|
|
|
179 |
snake_data_p->body[pos].y == y &&
|
|
|
180 |
snake_data_p->body[pos].z == z) {
|
| 240 |
Kevin |
181 |
overlap = 1;
|
|
|
182 |
break;
|
|
|
183 |
} else {
|
|
|
184 |
pos = (pos == CUBE_PIXELS - 1) ? 0 : pos + 1;
|
|
|
185 |
}
|
|
|
186 |
}
|
|
|
187 |
if (!overlap)
|
|
|
188 |
brk = 1;
|
|
|
189 |
} else {
|
| 278 |
Kevin |
190 |
uint32_t pos = snake_data_p->pos_tail;
|
|
|
191 |
if (snake_data_p->body[pos].x != x &&
|
|
|
192 |
snake_data_p->body[pos].y != y &&
|
|
|
193 |
snake_data_p->body[pos].z != z) {
|
| 240 |
Kevin |
194 |
brk = 1;
|
|
|
195 |
}
|
|
|
196 |
}
|
|
|
197 |
|
|
|
198 |
if (brk)
|
|
|
199 |
break;
|
|
|
200 |
}
|
|
|
201 |
|
|
|
202 |
ret.x = x;
|
|
|
203 |
ret.y = y;
|
|
|
204 |
ret.z = z;
|
|
|
205 |
return ret;
|
|
|
206 |
}
|