snake.odin (4082B)
1 package snake 2 3 import rl "../raylib" 4 import "core:math" 5 import "core:fmt" 6 7 WINDOW_SIZE :: 1000 8 GRID_WIDTH :: 20 9 CELL_SIZE :: 16 10 CANVAS_SIZE :: GRID_WIDTH*CELL_SIZE 11 TICK_RATE :: 0.13 12 Vec2i :: [2]int 13 MAX_SNAKE_LENGTH :: GRID_WIDTH*GRID_WIDTH 14 15 snake: [MAX_SNAKE_LENGTH]Vec2i 16 snake_length: int 17 tick_timer: f32 = TICK_RATE 18 move_direction: Vec2i 19 game_over: bool 20 food_pos: Vec2i 21 22 place_food :: proc() { 23 occupied: [GRID_WIDTH][GRID_WIDTH]bool 24 25 for i in 0..<snake_length { 26 occupied[snake[i].x][snake[i].y] = true 27 } 28 29 free_cells := make([dynamic]Vec2i, context.temp_allocator) 30 31 for x in 0..<GRID_WIDTH { 32 for y in 0..<GRID_WIDTH { 33 if !occupied[x][y] { 34 append(&free_cells, Vec2i {x, y}) 35 } 36 } 37 } 38 39 if len(free_cells) > 0 { 40 random_cell_index := rl.GetRandomValue(0, i32(len(free_cells) - 1)) 41 food_pos = free_cells[random_cell_index] 42 } 43 } 44 45 restart :: proc() { 46 start_head_pos := Vec2i { GRID_WIDTH / 2, GRID_WIDTH / 2 } 47 snake[0] = start_head_pos 48 snake[1] = start_head_pos - {0, 1} 49 snake[2] = start_head_pos - {0, 2} 50 snake_length = 3 51 move_direction = {0, 1} 52 game_over = false 53 place_food() 54 } 55 56 main :: proc() { 57 rl.SetConfigFlags({.VSYNC_HINT}) 58 rl.InitWindow(WINDOW_SIZE, WINDOW_SIZE, "Snake") 59 rl.InitAudioDevice() 60 61 restart() 62 63 food_sprite := rl.LoadTexture("food.png") 64 head_sprite := rl.LoadTexture("head.png") 65 body_sprite := rl.LoadTexture("body.png") 66 tail_sprite := rl.LoadTexture("tail.png") 67 68 eat_sound := rl.LoadSound("eat.wav") 69 crash_sound := rl.LoadSound("crash.wav") 70 71 for !rl.WindowShouldClose() { 72 if rl.IsKeyDown(.UP) { 73 move_direction = {0, -1} 74 } 75 76 if rl.IsKeyDown(.DOWN) { 77 move_direction = {0, 1} 78 } 79 80 if rl.IsKeyDown(.LEFT) { 81 move_direction = {-1, 0} 82 } 83 84 if rl.IsKeyDown(.RIGHT) { 85 move_direction = {1, 0} 86 } 87 88 if game_over { 89 if rl.IsKeyPressed(.ENTER) { 90 restart() 91 } 92 } else { 93 tick_timer -= rl.GetFrameTime() 94 } 95 96 if tick_timer <= 0 { 97 next_part_pos := snake[0] 98 snake[0] += move_direction 99 head_pos := snake[0] 100 101 if head_pos.x < 0 || head_pos.y < 0 || head_pos.x >= GRID_WIDTH || head_pos.y >= GRID_WIDTH { 102 game_over = true 103 rl.PlaySound(crash_sound) 104 } 105 106 for i in 1..<snake_length { 107 cur_pos := snake[i] 108 109 if cur_pos == head_pos { 110 game_over = true 111 rl.PlaySound(crash_sound) 112 } 113 114 snake[i] = next_part_pos 115 next_part_pos = cur_pos 116 } 117 118 if head_pos == food_pos { 119 snake_length += 1 120 snake[snake_length - 1] = next_part_pos 121 place_food() 122 rl.PlaySound(eat_sound) 123 } 124 125 tick_timer = TICK_RATE + tick_timer 126 } 127 128 rl.BeginDrawing() 129 rl.ClearBackground({76, 53, 83, 255}) 130 131 camera := rl.Camera2D { 132 zoom = f32(WINDOW_SIZE) / CANVAS_SIZE, 133 } 134 135 rl.BeginMode2D(camera) 136 137 rl.DrawTextureV(food_sprite, {f32(food_pos.x), f32(food_pos.y)}*CELL_SIZE, rl.WHITE) 138 139 for i in 0..<snake_length { 140 part_sprite := body_sprite 141 dir: Vec2i 142 143 if i == 0 { 144 part_sprite = head_sprite 145 dir = snake[i] - snake[i + 1] 146 } else if i == snake_length - 1 { 147 part_sprite = tail_sprite 148 dir = snake[i - 1] - snake[i] 149 } else { 150 dir = snake[i - 1] - snake[i] 151 } 152 153 rot := math.atan2(f32(dir.y), f32(dir.x)) * math.DEG_PER_RAD 154 155 source := rl.Rectangle { 156 0, 0, 157 f32(part_sprite.width), f32(part_sprite.height), 158 } 159 160 dest := rl.Rectangle { 161 f32(snake[i].x)*CELL_SIZE + 0.5*CELL_SIZE, 162 f32(snake[i].y)*CELL_SIZE + 0.5*CELL_SIZE, 163 CELL_SIZE, 164 CELL_SIZE, 165 } 166 167 rl.DrawTexturePro(part_sprite, source, dest, {CELL_SIZE, CELL_SIZE}*0.5, rot, rl.WHITE) 168 } 169 170 if game_over { 171 rl.DrawText("Game Over!", 4, 4, 25, rl.RED) 172 rl.DrawText("Press Enter to play again", 4, 30, 15, rl.BLACK) 173 } 174 175 score := snake_length - 3 176 score_str := fmt.ctprintf("Score: %v", score) 177 rl.DrawText(score_str, 4, CANVAS_SIZE - 14, 10, rl.GRAY) 178 179 rl.EndMode2D() 180 rl.EndDrawing() 181 182 free_all(context.temp_allocator) 183 } 184 185 rl.UnloadTexture(head_sprite) 186 rl.UnloadTexture(food_sprite) 187 rl.UnloadTexture(body_sprite) 188 rl.UnloadTexture(tail_sprite) 189 190 rl.UnloadSound(eat_sound) 191 rl.UnloadSound(crash_sound) 192 193 rl.CloseAudioDevice() 194 rl.CloseWindow() 195 }