module snake; import raylib; /** * * raylib - classic game: snake * * Sample game developed by Ian Eito, Albert Martos and Ramon Santamaria, * converted to C3 and modified by Christoffer Lerno * * Copyright (c) 2015 Ramon Santamaria (@raysan5) * */ const SNAKE_LENGTH = 256; const SQUARE_SIZE = 32; const int SCREEN_WIDTH = 800; const int SCREEN_HEIGHT = 450; enum SnakeDirection { RIGHT, DOWN, LEFT, UP } struct Snake { Vector2 position; Vector2 size; Color color; } struct Food { Vector2 position; Vector2 size; bool active; Color color; } int frames_counter = 0; bool game_over = false; bool pause = false; Food fruit; SnakeDirection snake_direction; Snake[SNAKE_LENGTH] snake; Vector2[SNAKE_LENGTH] snake_position; bool allow_move = false; Vector2 offset; int counter_tail = 0; fn void main() { raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: snake"); init_game(); raylib::set_target_fps(60); while (!raylib::window_should_close()) // Detect window close button or ESC key { update_draw_frame(); } unload_game(); raylib::close_window(); } // Initialize game variables fn void init_game() { frames_counter = 0; game_over = false; pause = false; counter_tail = 1; allow_move = false; snake_direction = SnakeDirection.RIGHT; offset.x = SCREEN_WIDTH % SQUARE_SIZE; offset.y = SCREEN_HEIGHT % SQUARE_SIZE; for (int i = 0; i < SNAKE_LENGTH; i++) { snake[i].position = { offset.x / 2, offset.y / 2 }; snake[i].size = { SQUARE_SIZE, SQUARE_SIZE }; if (i == 0) { snake[i].color = raylib::DARKBLUE; } else { snake[i].color = raylib::BLUE; } } for (int i = 0; i < SNAKE_LENGTH; i++) { snake_position[i] = { 0.0f, 0.0f }; } fruit.size = { SQUARE_SIZE, SQUARE_SIZE }; fruit.color = raylib::SKYBLUE; fruit.active = false; } fn void update_game() { if (game_over) { if (raylib::is_key_pressed(keyboard::ENTER)) { init_game(); game_over = false; } return; } if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause; if (pause) return; if (raylib::is_key_pressed(keyboard::RIGHT) && allow_move) { snake_direction = (SnakeDirection)((snake_direction + 1) % 4); allow_move = false; } if (raylib::is_key_pressed(keyboard::LEFT) && allow_move) { snake_direction = (SnakeDirection)((snake_direction + 3) % 4); allow_move = false; } // Snake movement for (int i = 0; i < counter_tail; i++) snake_position[i] = snake[i].position; if (frames_counter++ % 5 != 0) return; allow_move = true; switch (snake_direction) { case RIGHT: snake[0].position.x += SQUARE_SIZE; snake[0].position.y += 0; case UP: snake[0].position.x += 0; snake[0].position.y += -SQUARE_SIZE; case DOWN: snake[0].position.x += 0; snake[0].position.y += SQUARE_SIZE; case LEFT: snake[0].position.x += -SQUARE_SIZE; snake[0].position.y += 0; default: unreachable(); } for (int i = 1; i < counter_tail; i++) { snake[i].position = snake_position[i - 1]; } // Wall behaviour if (((snake[0].position.x) > (SCREEN_WIDTH - offset.x)) || ((snake[0].position.y) > (SCREEN_HEIGHT - offset.y)) || (snake[0].position.x < 0) || (snake[0].position.y < 0)) { game_over = true; } // Collision with yourself for (int i = 1; i < counter_tail; i++) { if ((snake[0].position.x == snake[i].position.x) && (snake[0].position.y == snake[i].position.y)) game_over = true; } // Fruit position calculation if (!fruit.active) { fruit.active = true; fruit.position = { raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x/2, raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 }; for (int i = 0; i < counter_tail; i++) { while ((fruit.position.x == snake[i].position.x) && (fruit.position.y == snake[i].position.y)) { fruit.position = { raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x/2, raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 }; i = 0; } } } // Collision if ((snake[0].position.x < (fruit.position.x + fruit.size.x) && (snake[0].position.x + snake[0].size.x) > fruit.position.x) && (snake[0].position.y < (fruit.position.y + fruit.size.y) && (snake[0].position.y + snake[0].size.y) > fruit.position.y)) { snake[counter_tail].position = snake_position[counter_tail - 1]; counter_tail += 1; fruit.active = false; } } // Draw game (one frame) fn void draw_game() { raylib::begin_drawing(); raylib::clear_background(raylib::RAYWHITE); if (!game_over) { // Draw grid lines for (int i = 0; i < SCREEN_WIDTH / SQUARE_SIZE + 1; i++) { raylib::draw_line_v({SQUARE_SIZE * i + offset.x/2, offset.y/2}, {SQUARE_SIZE * i + offset.x/2, SCREEN_HEIGHT - offset.y/2}, raylib::LIGHTGRAY); } for (int i = 0; i < SCREEN_HEIGHT/SQUARE_SIZE + 1; i++) { raylib::draw_line_v({offset.x/2, SQUARE_SIZE * i + offset.y / 2 }, { SCREEN_WIDTH - offset.x/2, SQUARE_SIZE * i + offset.y / 2 }, raylib::LIGHTGRAY); } // Draw snake for (int i = 0; i < counter_tail; i++) raylib::draw_rectangle_v(snake[i].position, snake[i].size, snake[i].color); // Draw fruit to pick raylib::draw_rectangle_v(fruit.position, fruit.size, fruit.color); if (pause) raylib::draw_text("GAME PAUSED", SCREEN_WIDTH/2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT / 2 - 40, 40, raylib::GRAY); } else { raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width()/2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20)/2, raylib::get_screen_height()/2 - 50, 20, raylib::GRAY); } raylib::end_drawing(); } // Unload game variables fn void unload_game() { // TODO: Unload all dynamic loaded data (textures, sounds, models...) } // Update and Draw (one frame) fn void update_draw_frame() { update_game(); draw_game(); }