贪吃蛇

    1. '''
    2. MiniSnake.py
    3. A game of snake in one .py file
    4. This program by Daniel Westbrook
    5. website: www.pixelatedawesome.com
    6. email: thepixelator72@gmail.com
    7. (or whatever email I list on my site, if I stop using that one)
    8. Legal shit:
    9. Copyright (C) 2008 Daniel Westbrook
    10. This program is free software: you can redistribute it and/or modify
    11. it under the terms of the GNU Lesser General Public License as published by
    12. the Free Software Foundation, either version 3 of the License, or
    13. (at your option) any later version.
    14. This program is distributed in the hope that it will be useful,
    15. but WITHOUT ANY WARRANTY; without even the implied warranty of
    16. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    17. GNU Lesser General Public License for more details.
    18. You should have received a copy of the GNU Lesser General Public License
    19. along with this program. If not, see <http://www.gnu.org/licenses/>.
    20. '''
    21. import pygame
    22. from pygame.locals import *
    23. import random
    24. # ---------- constants ---------- #
    25. SCREENSIZE = (800, 600)
    26. SCREENRECT = pygame.Rect(0, 0, SCREENSIZE[0], SCREENSIZE[1])
    27. CAPTION = 'MiniSnake'
    28. FPS = 40
    29. START_TILE = (20, 20)
    30. START_SEGMENTS = 7
    31. MOVE_RATE = 2
    32. DIFFICULTY_INCREASE_RATE = .05
    33. MOVE_THRESHOLD = 5 # when moverate counts up to this the snake moves
    34. BLOCK_SPAWN_RATE = 2
    35. TILE_SIZE = (10, 10)
    36. TILE_RECT = pygame.Rect(0, 0, TILE_SIZE[0], TILE_SIZE[1])
    37. SCREENTILES = ((SCREENSIZE[0] / TILE_SIZE[0]) - 1, (SCREENSIZE[1] / TILE_SIZE[1]) - 1)
    38. SNAKE_HEAD_RADIUS = 5
    39. SNAKE_SEGMENT_RADIUS = 4
    40. FOOD_RADIUS = 4
    41. BACKGROUND_COLOR = (255, 255, 255)
    42. SNAKE_HEAD_COLOR = (150, 0, 0)
    43. SNAKE_SEGMENT_COLOR = (255, 0, 0)
    44. FOOD_COLOR = (0, 255, 0)
    45. BLOCK_COLOR = (0, 0, 150)
    46. COLORKEY_COLOR = (255, 255, 0)
    47. SCORE_COLOR = (0, 0, 0)
    48. SCORE_POS = (20, 20)
    49. SCORE_PREFIX = 'Score: '
    50. MOVE_VECTORS = {'left' : (-1, 0),
    51. 'right' : (1, 0),
    52. 'up' : (0, -1),
    53. 'down' : (0, 1)
    54. }
    55. MOVE_VECTORS_PIXELS = {'left' : (-TILE_SIZE[0], 0),
    56. 'right' : (TILE_SIZE[0], 0),
    57. 'up' : (0, -TILE_SIZE[1]),
    58. 'down' : (0, TILE_SIZE[1])
    59. }
    60. # ----------- game objects ----------- #
    61. class snake_segment(pygame.sprite.Sprite):
    62. def __init__(self, tilepos, segment_groups, color = SNAKE_SEGMENT_COLOR, radius = SNAKE_SEGMENT_RADIUS):
    63. pygame.sprite.Sprite.__init__(self)
    64. self.image = self.image = pygame.Surface(TILE_SIZE).convert()
    65. self.image.fill(COLORKEY_COLOR)
    66. self.image.set_colorkey(COLORKEY_COLOR)
    67. pygame.draw.circle(self.image, color, TILE_RECT.center, radius)
    68. self.tilepos = tilepos
    69. self.rect = self.image.get_rect()
    70. self.rect.topleft = (tilepos[0] * TILE_SIZE[0], tilepos[1] * TILE_SIZE[1])
    71. self.segment_groups = segment_groups
    72. for group in segment_groups:
    73. group.add(self)
    74. self.behind_segment = None
    75. self.movedir = 'left'
    76. def add_segment(self):
    77. seg = self
    78. while True:
    79. if seg.behind_segment == None:
    80. x = seg.tilepos[0]
    81. y = seg.tilepos[1]
    82. if seg.movedir == 'left':
    83. x += 1
    84. elif seg.movedir == 'right':
    85. x -= 1
    86. elif seg.movedir == 'up':
    87. y += 1
    88. elif seg.movedir == 'down':
    89. y -= 1
    90. seg.behind_segment = snake_segment((x, y), seg.segment_groups)
    91. seg.behind_segment.movedir = seg.movedir
    92. break
    93. else:
    94. seg = seg.behind_segment
    95. def update(self):
    96. pass
    97. def move(self):
    98. self.tilepos = (self.tilepos[0] + MOVE_VECTORS[self.movedir][0], self.tilepos[1] + MOVE_VECTORS[self.movedir][1])
    99. self.rect.move_ip(MOVE_VECTORS_PIXELS[self.movedir])
    100. if self.behind_segment != None:
    101. self.behind_segment.move()
    102. self.behind_segment.movedir = self.movedir
    103. class snake_head(snake_segment):
    104. def __init__(self, tilepos, movedir, segment_groups):
    105. snake_segment.__init__(self, tilepos, segment_groups, color = SNAKE_HEAD_COLOR, radius = SNAKE_HEAD_RADIUS)
    106. self.movedir = movedir
    107. self.movecount = 0
    108. def update(self):
    109. self.movecount += MOVE_RATE
    110. if self.movecount > MOVE_THRESHOLD:
    111. self.move()
    112. self.movecount = 0
    113. class food(pygame.sprite.Sprite):
    114. def __init__(self, takenupgroup):
    115. pygame.sprite.Sprite.__init__(self)
    116. self.image = self.image = pygame.Surface(TILE_SIZE).convert()
    117. self.image.fill(COLORKEY_COLOR)
    118. self.image.set_colorkey(COLORKEY_COLOR)
    119. pygame.draw.circle(self.image, FOOD_COLOR, TILE_RECT.center, FOOD_RADIUS)
    120. self.rect = self.image.get_rect()
    121. while True:
    122. self.rect.topleft = (random.randint(0, SCREENTILES[0]) * TILE_SIZE[0], random.randint(0, SCREENTILES[1]) * TILE_SIZE[1])
    123. for sprt in takenupgroup:
    124. if self.rect.colliderect(sprt):
    125. continue # collision, food cant go here
    126. break # no collision, food can go here
    127. class block(pygame.sprite.Sprite):
    128. def __init__(self, takenupgroup):
    129. pygame.sprite.Sprite.__init__(self)
    130. self.image = self.image = pygame.Surface(TILE_SIZE).convert()
    131. self.image.fill(BLOCK_COLOR)
    132. self.rect = self.image.get_rect()
    133. while True:
    134. self.rect.topleft = (random.randint(0, SCREENTILES[0]) * TILE_SIZE[0], random.randint(0, SCREENTILES[1]) * TILE_SIZE[1])
    135. for sprt in takenupgroup:
    136. if self.rect.colliderect(sprt):
    137. continue # collision, food cant go here
    138. break # no collision, food can go here
    139. # -------------- game logic ------------ #
    140. def main():
    141. pygame.init()
    142. screen = pygame.display.set_mode(SCREENSIZE)
    143. pygame.display.set_caption(CAPTION)
    144. bg = pygame.Surface(SCREENSIZE).convert()
    145. bg.fill(BACKGROUND_COLOR)
    146. screen.blit(bg, (0, 0))
    147. snakegroup = pygame.sprite.Group()
    148. snakeheadgroup = pygame.sprite.Group()
    149. foodgroup = pygame.sprite.Group()
    150. blockgroup = pygame.sprite.Group()
    151. takenupgroup = pygame.sprite.Group()
    152. all = pygame.sprite.RenderUpdates()
    153. snake = snake_head(START_TILE, 'right', [snakegroup, all, takenupgroup])
    154. snakeheadgroup.add(snake)
    155. for index in range(START_SEGMENTS):
    156. snake.add_segment()
    157. currentfood = 'no food'
    158. block_frame = 0
    159. currentscore = 0
    160. pygame.display.flip()
    161. # mainloop
    162. quit = False
    163. clock = pygame.time.Clock()
    164. lose = False
    165. while not quit:
    166. # events
    167. for event in pygame.event.get():
    168. if event.type == QUIT:
    169. quit = True
    170. elif event.type == KEYDOWN:
    171. currentmovedir = snake.movedir
    172. if event.key == K_UP:
    173. tomove = 'up'
    174. dontmove = 'down'
    175. elif event.key == K_DOWN:
    176. tomove = 'down'
    177. dontmove = 'up'
    178. elif event.key == K_LEFT:
    179. tomove = 'left'
    180. dontmove = 'right'
    181. elif event.key == K_RIGHT:
    182. tomove = 'right'
    183. dontmove = 'left'
    184. else:
    185. raise 'RuntimeError, not expected'
    186. if not currentmovedir == dontmove:
    187. snake.movedir = tomove
    188. # clearing
    189. all.clear(screen, bg)
    190. # updates
    191. all.update()
    192. if currentfood == 'no food':
    193. currentfood = food(takenupgroup)
    194. foodgroup.add(currentfood)
    195. takenupgroup.add(currentfood)
    196. all.add(currentfood)
    197. pos = snake.rect.topleft
    198. if pos[0] < 0:
    199. quit = True
    200. lose = True
    201. if pos[0] >= SCREENSIZE[0]:
    202. quit = True
    203. lose = True
    204. if pos[1] < 0:
    205. quit = True
    206. lose = True
    207. if pos[1] >= SCREENSIZE[1]:
    208. quit = True
    209. lose = True
    210. # collisions
    211. # head -> tail
    212. col = pygame.sprite.groupcollide(snakeheadgroup, snakegroup, False, False)
    213. for head in col:
    214. for tail in col[head]:
    215. if not tail is snake:
    216. quit = True
    217. lose = True
    218. # head -> food
    219. col = pygame.sprite.groupcollide(snakeheadgroup, foodgroup, False, True)
    220. for head in col:
    221. for tail in col[head]:
    222. currentfood = 'no food'
    223. snake.add_segment()
    224. currentscore += 1
    225. global MOVE_RATE, DIFFICULTY_INCREASE_RATE
    226. MOVE_RATE += DIFFICULTY_INCREASE_RATE
    227. block_frame += 1
    228. if block_frame >= BLOCK_SPAWN_RATE:
    229. block_frame = 0
    230. b = block(takenupgroup)
    231. blockgroup.add(b)
    232. takenupgroup.add(b)
    233. all.add(b)
    234. # head -> blocks
    235. col = pygame.sprite.groupcollide(snakeheadgroup, blockgroup, False, False)
    236. for head in col:
    237. for collidedblock in col[head]:
    238. quit = True
    239. lose = True
    240. # score
    241. d = screen.blit(bg, SCORE_POS, pygame.Rect(SCORE_POS, (50, 100)))
    242. f = pygame.font.Font(None, 12)
    243. scoreimage = f.render(SCORE_PREFIX + str(currentscore), True, SCORE_COLOR)
    244. d2 = screen.blit(scoreimage, SCORE_POS)
    245. # drawing
    246. dirty = all.draw(screen)
    247. dirty.append(d)
    248. dirty.append(d2)
    249. # updating
    250. pygame.display.update(dirty)
    251. # waiting
    252. clock.tick(FPS)
    253. # game over
    254. if lose == True:
    255. f = pygame.font.Font(None, 300)
    256. failmessage = f.render('FAIL', True, (0, 0, 0))
    257. failrect = failmessage.get_rect()
    258. failrect.center = SCREENRECT.center
    259. screen.blit(failmessage, failrect)
    260. pygame.display.flip()
    261. pygame.time.wait(2000)
    262. if __name__ == "__main__":
    263. main()