1/*******************************************************************************************
2*
3* raylib - sample game: snake
4*
5* Sample game developed by Ian Eito, Albert Martos and Ramon Santamaria
6*
7* This game has been created using raylib v1.3 (www.raylib.com)
8* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
9*
10* Copyright (c) 2015 Ramon Santamaria (@raysan5)
11*
12********************************************************************************************/
13
14#include "raylib.h"
15
16#if defined(PLATFORM_WEB)
17 #include <emscripten/emscripten.h>
18#endif
19
20//----------------------------------------------------------------------------------
21// Some Defines
22//----------------------------------------------------------------------------------
23#define SNAKE_LENGTH 256
24#define SQUARE_SIZE 31
25
26//----------------------------------------------------------------------------------
27// Types and Structures Definition
28//----------------------------------------------------------------------------------
29typedef struct Snake {
30 Vector2 position;
31 Vector2 size;
32 Vector2 speed;
33 Color color;
34} Snake;
35
36typedef struct Food {
37 Vector2 position;
38 Vector2 size;
39 bool active;
40 Color color;
41} Food;
42
43//------------------------------------------------------------------------------------
44// Global Variables Declaration
45//------------------------------------------------------------------------------------
46static const int screenWidth = 800;
47static const int screenHeight = 450;
48
49static int framesCounter = 0;
50static bool gameOver = false;
51static bool pause = false;
52
53static Food fruit = { 0 };
54static Snake snake[SNAKE_LENGTH] = { 0 };
55static Vector2 snakePosition[SNAKE_LENGTH] = { 0 };
56static bool allowMove = false;
57static Vector2 offset = { 0 };
58static int counterTail = 0;
59
60//------------------------------------------------------------------------------------
61// Module Functions Declaration (local)
62//------------------------------------------------------------------------------------
63static void InitGame(void); // Initialize game
64static void UpdateGame(void); // Update game (one frame)
65static void DrawGame(void); // Draw game (one frame)
66static void UnloadGame(void); // Unload game
67static void UpdateDrawFrame(void); // Update and Draw (one frame)
68
69//------------------------------------------------------------------------------------
70// Program main entry point
71//------------------------------------------------------------------------------------
72int main(void)
73{
74 // Initialization (Note windowTitle is unused on Android)
75 //---------------------------------------------------------
76 InitWindow(screenWidth, screenHeight, "sample game: snake");
77
78 InitGame();
79
80#if defined(PLATFORM_WEB)
81 emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
82#else
83 SetTargetFPS(60);
84 //--------------------------------------------------------------------------------------
85
86 // Main game loop
87 while (!WindowShouldClose()) // Detect window close button or ESC key
88 {
89 // Update and Draw
90 //----------------------------------------------------------------------------------
91 UpdateDrawFrame();
92 //----------------------------------------------------------------------------------
93 }
94#endif
95 // De-Initialization
96 //--------------------------------------------------------------------------------------
97 UnloadGame(); // Unload loaded data (textures, sounds, models...)
98
99 CloseWindow(); // Close window and OpenGL context
100 //--------------------------------------------------------------------------------------
101
102 return 0;
103}
104
105//------------------------------------------------------------------------------------
106// Module Functions Definitions (local)
107//------------------------------------------------------------------------------------
108
109// Initialize game variables
110void InitGame(void)
111{
112 framesCounter = 0;
113 gameOver = false;
114 pause = false;
115
116 counterTail = 1;
117 allowMove = false;
118
119 offset.x = screenWidth%SQUARE_SIZE;
120 offset.y = screenHeight%SQUARE_SIZE;
121
122 for (int i = 0; i < SNAKE_LENGTH; i++)
123 {
124 snake[i].position = (Vector2){ offset.x/2, offset.y/2 };
125 snake[i].size = (Vector2){ SQUARE_SIZE, SQUARE_SIZE };
126 snake[i].speed = (Vector2){ SQUARE_SIZE, 0 };
127
128 if (i == 0) snake[i].color = DARKBLUE;
129 else snake[i].color = BLUE;
130 }
131
132 for (int i = 0; i < SNAKE_LENGTH; i++)
133 {
134 snakePosition[i] = (Vector2){ 0.0f, 0.0f };
135 }
136
137 fruit.size = (Vector2){ SQUARE_SIZE, SQUARE_SIZE };
138 fruit.color = SKYBLUE;
139 fruit.active = false;
140}
141
142// Update game (one frame)
143void UpdateGame(void)
144{
145 if (!gameOver)
146 {
147 if (IsKeyPressed('P')) pause = !pause;
148
149 if (!pause)
150 {
151 // Player control
152 if (IsKeyPressed(KEY_RIGHT) && (snake[0].speed.x == 0) && allowMove)
153 {
154 snake[0].speed = (Vector2){ SQUARE_SIZE, 0 };
155 allowMove = false;
156 }
157 if (IsKeyPressed(KEY_LEFT) && (snake[0].speed.x == 0) && allowMove)
158 {
159 snake[0].speed = (Vector2){ -SQUARE_SIZE, 0 };
160 allowMove = false;
161 }
162 if (IsKeyPressed(KEY_UP) && (snake[0].speed.y == 0) && allowMove)
163 {
164 snake[0].speed = (Vector2){ 0, -SQUARE_SIZE };
165 allowMove = false;
166 }
167 if (IsKeyPressed(KEY_DOWN) && (snake[0].speed.y == 0) && allowMove)
168 {
169 snake[0].speed = (Vector2){ 0, SQUARE_SIZE };
170 allowMove = false;
171 }
172
173 // Snake movement
174 for (int i = 0; i < counterTail; i++) snakePosition[i] = snake[i].position;
175
176 if ((framesCounter%5) == 0)
177 {
178 for (int i = 0; i < counterTail; i++)
179 {
180 if (i == 0)
181 {
182 snake[0].position.x += snake[0].speed.x;
183 snake[0].position.y += snake[0].speed.y;
184 allowMove = true;
185 }
186 else snake[i].position = snakePosition[i-1];
187 }
188 }
189
190 // Wall behaviour
191 if (((snake[0].position.x) > (screenWidth - offset.x)) ||
192 ((snake[0].position.y) > (screenHeight - offset.y)) ||
193 (snake[0].position.x < 0) || (snake[0].position.y < 0))
194 {
195 gameOver = true;
196 }
197
198 // Collision with yourself
199 for (int i = 1; i < counterTail; i++)
200 {
201 if ((snake[0].position.x == snake[i].position.x) && (snake[0].position.y == snake[i].position.y)) gameOver = true;
202 }
203
204 // Fruit position calculation
205 if (!fruit.active)
206 {
207 fruit.active = true;
208 fruit.position = (Vector2){ GetRandomValue(0, (screenWidth/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.x/2, GetRandomValue(0, (screenHeight/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.y/2 };
209
210 for (int i = 0; i < counterTail; i++)
211 {
212 while ((fruit.position.x == snake[i].position.x) && (fruit.position.y == snake[i].position.y))
213 {
214 fruit.position = (Vector2){ GetRandomValue(0, (screenWidth/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.x/2, GetRandomValue(0, (screenHeight/SQUARE_SIZE) - 1)*SQUARE_SIZE + offset.y/2 };
215 i = 0;
216 }
217 }
218 }
219
220 // Collision
221 if ((snake[0].position.x < (fruit.position.x + fruit.size.x) && (snake[0].position.x + snake[0].size.x) > fruit.position.x) &&
222 (snake[0].position.y < (fruit.position.y + fruit.size.y) && (snake[0].position.y + snake[0].size.y) > fruit.position.y))
223 {
224 snake[counterTail].position = snakePosition[counterTail - 1];
225 counterTail += 1;
226 fruit.active = false;
227 }
228
229 framesCounter++;
230 }
231 }
232 else
233 {
234 if (IsKeyPressed(KEY_ENTER))
235 {
236 InitGame();
237 gameOver = false;
238 }
239 }
240}
241
242// Draw game (one frame)
243void DrawGame(void)
244{
245 BeginDrawing();
246
247 ClearBackground(RAYWHITE);
248
249 if (!gameOver)
250 {
251 // Draw grid lines
252 for (int i = 0; i < screenWidth/SQUARE_SIZE + 1; i++)
253 {
254 DrawLineV((Vector2){SQUARE_SIZE*i + offset.x/2, offset.y/2}, (Vector2){SQUARE_SIZE*i + offset.x/2, screenHeight - offset.y/2}, LIGHTGRAY);
255 }
256
257 for (int i = 0; i < screenHeight/SQUARE_SIZE + 1; i++)
258 {
259 DrawLineV((Vector2){offset.x/2, SQUARE_SIZE*i + offset.y/2}, (Vector2){screenWidth - offset.x/2, SQUARE_SIZE*i + offset.y/2}, LIGHTGRAY);
260 }
261
262 // Draw snake
263 for (int i = 0; i < counterTail; i++) DrawRectangleV(snake[i].position, snake[i].size, snake[i].color);
264
265 // Draw fruit to pick
266 DrawRectangleV(fruit.position, fruit.size, fruit.color);
267
268 if (pause) DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/2 - 40, 40, GRAY);
269 }
270 else DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, GRAY);
271
272 EndDrawing();
273}
274
275// Unload game variables
276void UnloadGame(void)
277{
278 // TODO: Unload all dynamic loaded data (textures, sounds, models...)
279}
280
281// Update and Draw (one frame)
282void UpdateDrawFrame(void)
283{
284 UpdateGame();
285 DrawGame();
286}
287