snake.odin 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package snake
  2. import kl "../.."
  3. import "core:math"
  4. import "core:fmt"
  5. import "core:time"
  6. import "core:math/rand"
  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. snake: [MAX_SNAKE_LENGTH]Vec2i
  15. snake_length: int
  16. tick_timer: f32 = TICK_RATE
  17. move_direction: Vec2i
  18. game_over: bool
  19. food_pos: Vec2i
  20. place_food :: proc() {
  21. occupied: [GRID_WIDTH][GRID_WIDTH]bool
  22. for i in 0..<snake_length {
  23. occupied[snake[i].x][snake[i].y] = true
  24. }
  25. free_cells := make([dynamic]Vec2i, context.temp_allocator)
  26. for x in 0..<GRID_WIDTH {
  27. for y in 0..<GRID_WIDTH {
  28. if !occupied[x][y] {
  29. append(&free_cells, Vec2i {x, y})
  30. }
  31. }
  32. }
  33. if len(free_cells) > 0 {
  34. random_cell_index := rand.int31_max(i32(len(free_cells)))
  35. food_pos = free_cells[random_cell_index]
  36. }
  37. }
  38. restart :: proc() {
  39. start_head_pos := Vec2i { GRID_WIDTH / 2, GRID_WIDTH / 2 }
  40. snake[0] = start_head_pos
  41. snake[1] = start_head_pos - {0, 1}
  42. snake[2] = start_head_pos - {0, 2}
  43. snake_length = 3
  44. move_direction = {0, 1}
  45. game_over = false
  46. place_food()
  47. }
  48. main :: proc() {
  49. kl.init(WINDOW_SIZE, WINDOW_SIZE, "Snake")
  50. prev_time := time.now()
  51. restart()
  52. food_sprite := kl.load_texture_from_file("food.png")
  53. head_sprite := kl.load_texture_from_file("head.png")
  54. body_sprite := kl.load_texture_from_file("body.png")
  55. tail_sprite := kl.load_texture_from_file("tail.png")
  56. for !kl.window_should_close() {
  57. time_now := time.now()
  58. dt := f32(time.duration_seconds(time.diff(prev_time, time_now)))
  59. prev_time = time_now
  60. kl.process_events()
  61. if kl.key_is_held(.Up) {
  62. move_direction = {0, -1}
  63. }
  64. if kl.key_is_held(.Down) {
  65. move_direction = {0, 1}
  66. }
  67. if kl.key_is_held(.Left) {
  68. move_direction = {-1, 0}
  69. }
  70. if kl.key_is_held(.Right) {
  71. move_direction = {1, 0}
  72. }
  73. if game_over {
  74. if kl.key_went_down(.Enter) {
  75. restart()
  76. }
  77. } else {
  78. tick_timer -= dt
  79. }
  80. if tick_timer <= 0 {
  81. next_part_pos := snake[0]
  82. snake[0] += move_direction
  83. head_pos := snake[0]
  84. if head_pos.x < 0 || head_pos.y < 0 || head_pos.x >= GRID_WIDTH || head_pos.y >= GRID_WIDTH {
  85. game_over = true
  86. }
  87. for i in 1..<snake_length {
  88. cur_pos := snake[i]
  89. if cur_pos == head_pos {
  90. game_over = true
  91. }
  92. snake[i] = next_part_pos
  93. next_part_pos = cur_pos
  94. }
  95. if head_pos == food_pos {
  96. snake_length += 1
  97. snake[snake_length - 1] = next_part_pos
  98. place_food()
  99. }
  100. tick_timer = TICK_RATE + tick_timer
  101. }
  102. kl.clear({76, 53, 83, 255})
  103. camera := kl.Camera {
  104. zoom = f32(WINDOW_SIZE) / CANVAS_SIZE,
  105. }
  106. kl.set_camera(camera)
  107. kl.draw_texture(food_sprite, {f32(food_pos.x), f32(food_pos.y)}*CELL_SIZE)
  108. for i in 0..<snake_length {
  109. part_sprite := body_sprite
  110. dir: Vec2i
  111. if i == 0 {
  112. part_sprite = head_sprite
  113. dir = snake[i] - snake[i + 1]
  114. } else if i == snake_length - 1 {
  115. part_sprite = tail_sprite
  116. dir = snake[i - 1] - snake[i]
  117. } else {
  118. dir = snake[i - 1] - snake[i]
  119. }
  120. rot := math.atan2(f32(dir.y), f32(dir.x)) * math.DEG_PER_RAD
  121. source := kl.Rect {
  122. 0, 0,
  123. f32(part_sprite.width), f32(part_sprite.height),
  124. }
  125. dest := kl.Rect {
  126. f32(snake[i].x)*CELL_SIZE + 0.5*CELL_SIZE,
  127. f32(snake[i].y)*CELL_SIZE + 0.5*CELL_SIZE,
  128. CELL_SIZE,
  129. CELL_SIZE,
  130. }
  131. kl.draw_texture_ex(part_sprite, source, dest, {CELL_SIZE, CELL_SIZE}*0.5, rot)
  132. }
  133. if game_over {
  134. kl.draw_text("Game Over!", {4, 4}, 25, kl.RED)
  135. kl.draw_text("Press Enter to play again", {4, 30}, 15, kl.BLACK)
  136. }
  137. score := snake_length - 3
  138. score_str := fmt.tprintf("Score: %v", score)
  139. kl.draw_text(score_str, {4, CANVAS_SIZE - 14}, 10, kl.GRAY)
  140. kl.present()
  141. free_all(context.temp_allocator)
  142. }
  143. kl.destroy_texture(head_sprite)
  144. kl.destroy_texture(food_sprite)
  145. kl.destroy_texture(body_sprite)
  146. kl.destroy_texture(tail_sprite)
  147. kl.shutdown()
  148. }