snake.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. from typing import List
  2. import pygame
  3. from random import randint
  4. class Pos:
  5. def __init__(self, x, y):
  6. self.x, self.y = int(x), int(y)
  7. @classmethod
  8. def random(cls):
  9. return cls(randint(0, Game.MAP.x - 1), randint(0, Game.MAP.y - 1))
  10. def copy(self):
  11. return Pos(self.x, self.y)
  12. def __eq__(self, other):
  13. return self.x == other.x and self.y == other.y
  14. class Colours:
  15. BACKGROUND = (0, 0, 0)
  16. FOREGROUND = (255, 0, 0)
  17. SNAKE = (0, 255, 0)
  18. FOOD = (0, 0, 255)
  19. class Game:
  20. MAP = Pos(20, 20)
  21. CUBE = 20
  22. FPS = 60
  23. SPEED = 120
  24. DEBUG = False
  25. def __init__(self):
  26. start_pos = Pos(self.MAP.x / 2, self.MAP.y / 2)
  27. self.snake = Snake(start_pos)
  28. self.dead = False
  29. self.running = True
  30. self.score = 0
  31. self.screen = pygame.display.set_mode((self.MAP.y * self.CUBE, self.MAP.x * self.CUBE))
  32. self.clock = pygame.time.Clock()
  33. self.timer = 0.0
  34. self.font = pygame.font.SysFont('Arial', 20, True, False)
  35. def proc_events(self, e):
  36. if e.type == pygame.QUIT:
  37. self.running = False
  38. if e.type != pygame.KEYDOWN:
  39. return
  40. if e.key == pygame.K_SPACE and self.dead:
  41. start_pos = Pos(self.MAP.x / 2, self.MAP.y / 2)
  42. self.snake = Snake(start_pos)
  43. self.dead = False
  44. if e.key == pygame.K_UP:
  45. if self.snake.dir != 1:
  46. self.snake.dir_next = 0
  47. elif e.key == pygame.K_DOWN:
  48. if self.snake.dir != 0:
  49. self.snake.dir_next = 1
  50. elif e.key == pygame.K_LEFT:
  51. if self.snake.dir != 3:
  52. self.snake.dir_next = 2
  53. elif e.key == pygame.K_RIGHT:
  54. if self.snake.dir != 2:
  55. self.snake.dir_next = 3
  56. elif e.key == pygame.K_d:
  57. self.DEBUG = not self.DEBUG
  58. elif e.key == pygame.K_ESCAPE:
  59. self.running = False
  60. def loop(self):
  61. for event in pygame.event.get():
  62. self.proc_events(event)
  63. if self.dead:
  64. text = self.font.render(f"SCORE: {self.score}", True, Colours.FOREGROUND)
  65. self.screen.blit(text, (self.MAP.x * self.CUBE / 2, self.MAP.y * self.CUBE / 2))
  66. pygame.display.flip()
  67. self.clock.tick(30)
  68. return
  69. if 0 > self.snake.head.x or self.snake.head.x >= self.MAP.x or \
  70. 0 > self.snake.head.y or self.snake.head.y >= self.MAP.y:
  71. self.dead = True
  72. self.screen.fill(Colours.BACKGROUND)
  73. pygame.draw.rect(
  74. self.screen, Colours.FOOD,
  75. (self.snake.food.y * self.CUBE, self.snake.food.x * self.CUBE, self.CUBE - 1, self.CUBE - 1), 0)
  76. for c in self.snake.body:
  77. pygame.draw.rect(
  78. self.screen, Colours.SNAKE,
  79. (c.y * self.CUBE, c.x * self.CUBE, self.CUBE - 1, self.CUBE - 1), 0)
  80. self.timer += self.clock.get_time()
  81. if self.timer >= self.SPEED:
  82. self.timer = 0.0
  83. if self.snake.dir_next == 0:
  84. self.snake.head.x -= 1
  85. elif self.snake.dir_next == 1:
  86. self.snake.head.x += 1
  87. elif self.snake.dir_next == 2:
  88. self.snake.head.y -= 1
  89. else:
  90. self.snake.head.y += 1
  91. self.snake.dir = self.snake.dir_next
  92. if self.snake.collide(self.snake.head):
  93. self.dead = True
  94. if self.snake.head == self.snake.food:
  95. self.snake.add_food()
  96. self.snake.size += 1
  97. self.score += 1
  98. self.snake.move()
  99. if self.DEBUG:
  100. text1 = self.font.render(
  101. f"X: {self.snake.head.x:02d} Y: {self.snake.head.y:02d} "
  102. f"S: {self.snake.size} FPS: {self.clock.get_fps():.1f}",
  103. True, Colours.FOREGROUND)
  104. text2 = self.font.render(
  105. f"F X: {self.snake.food.x:02d} Y: {self.snake.food.y:02d} S: {self.score}",
  106. True, Colours.FOREGROUND)
  107. self.screen.blit(text1, (10, 10))
  108. self.screen.blit(text2, (10, 32))
  109. pygame.display.flip()
  110. self.clock.tick(60)
  111. class Snake:
  112. def __init__(self, start: Pos, size=3, dir=0):
  113. self.size: int = size
  114. self.head: Pos = start
  115. self.body: List[Pos] = [start.copy()]
  116. self.dir: int = dir # Direction 0=N, 1=S, 2=W, 3=E
  117. self.dir_next: int = dir # Next direction
  118. self.food: Pos = None
  119. self.add_food()
  120. def add_food(self):
  121. self.food = Pos.random()
  122. while self.collide(self.food):
  123. self.food = Pos.random()
  124. def collide(self, pos: Pos) -> bool:
  125. for l in self.body:
  126. if pos == l:
  127. return True
  128. return False
  129. def move(self):
  130. self.body.append(self.head.copy())
  131. if len(self.body) > self.size:
  132. self.body.pop(0)
  133. if __name__ == '__main__':
  134. pygame.init()
  135. pygame.display.set_caption("Snake")
  136. game = Game()
  137. game.DEBUG = True
  138. while game.running:
  139. game.loop()
  140. pygame.quit()