Python RGB Matrix games and animations https://www.xythobuz.de/ledmatrix_v2.html
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

tetris.py 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. #!/usr/bin/env python3
  2. # ----------------------------------------------------------------------------
  3. # "THE BEER-WARE LICENSE" (Revision 42):
  4. # <xythobuz@xythobuz.de> wrote this file. As long as you retain this notice
  5. # you can do whatever you want with this stuff. If we meet some day, and you
  6. # think this stuff is worth it, you can buy me a beer in return. Thomas Buck
  7. # ----------------------------------------------------------------------------
  8. from scroll import ScrollText
  9. import time
  10. import random
  11. class Tetris:
  12. def __init__(self, g, i, ts = 0.5, to = 60.0):
  13. self.gui = g
  14. self.input = i
  15. self.timestep = ts
  16. self.timeout = to
  17. self.endText = ScrollText(self.gui, "Game Over!", "uushi",
  18. 2, 75, (251, 72, 196))
  19. self.scoreText = ScrollText(self.gui, "Score:", "uushi",
  20. 2, 75, (63, 255, 33))
  21. self.bg = (0, 0, 0)
  22. self.colors = [
  23. (251, 72, 196), # camp23 pink
  24. (63, 255, 33), # camp23 green
  25. (255, 0, 0),
  26. #(0, 255, 0),
  27. (0, 0, 255),
  28. (255, 255, 0),
  29. (0, 255, 255),
  30. (255, 0, 255),
  31. #(255, 255, 255),
  32. ]
  33. # all [y][x] sub-lists must be the same length
  34. self.pieces = [
  35. # "I"
  36. [
  37. [1],
  38. [1],
  39. [1],
  40. [1],
  41. ],
  42. # "L"
  43. [
  44. [1, 0],
  45. [1, 0],
  46. [1, 1],
  47. ],
  48. # "J"
  49. [
  50. [0, 1],
  51. [0, 1],
  52. [1, 1],
  53. ],
  54. # "T"
  55. [
  56. [1, 1, 1],
  57. [0, 1, 0],
  58. ],
  59. # "O"
  60. [
  61. [1, 1],
  62. [1, 1],
  63. ],
  64. # "S"
  65. [
  66. [0, 1, 1],
  67. [1, 1, 0],
  68. ],
  69. # "Z"
  70. [
  71. [1, 1, 0],
  72. [0, 1, 1],
  73. ],
  74. ]
  75. random.seed()
  76. self.restart()
  77. def restart(self):
  78. self.start = time.time()
  79. self.last = time.time()
  80. self.button = None
  81. self.score = 0
  82. self.done = False
  83. self.data = [[self.bg for y in range(self.gui.height)] for x in range(self.gui.width)]
  84. self.piece = None
  85. def finished(self):
  86. if self.input == None:
  87. # backup timeout for "AI"
  88. if (time.time() - self.start) >= self.timeout:
  89. return True
  90. if self.done:
  91. # game over screen
  92. return self.scoreText.finished()
  93. return False
  94. # TODO collision detection is broken
  95. # TODO not working for pixels outside of vertical center line???
  96. # TODO something like that...
  97. def collision(self):
  98. # check for collision of piece with data
  99. pos = (self.piece[2], self.piece[3])
  100. for y in range(0, len(self.piece[0])):
  101. for x in range(0, len(self.piece[0][y])):
  102. # only check where piece actually is
  103. if self.piece[0][y][x] == 0:
  104. continue
  105. # check for collision with bottom wall
  106. if (y + pos[1]) >= self.gui.height:
  107. return True
  108. # check for collision with previous pieces
  109. if self.data[x + pos[0]][y + pos[1]] != self.bg:
  110. return True
  111. return False
  112. # copy piece into data buffer
  113. def put(self, clear, pos = None, pie = None):
  114. position = pos
  115. if position == None:
  116. position = (self.piece[2], self.piece[3])
  117. piece = pie
  118. if piece == None:
  119. piece = self.piece[0]
  120. for y in range(0, len(piece)):
  121. for x in range(0, len(piece[y])):
  122. remove = False
  123. if piece[y][x] == 0:
  124. remove = True
  125. if clear or remove:
  126. self.data[x + position[0]][y + position[1]] = self.bg
  127. else:
  128. self.data[x + position[0]][y + position[1]] = self.piece[1]
  129. def step(self):
  130. if self.piece == None:
  131. justPlaced = True
  132. # select a new piece type and color
  133. self.piece = [
  134. self.pieces[random.randrange(0, len(self.pieces))], # piece
  135. self.colors[random.randrange(0, len(self.colors))], # color
  136. 0, # x
  137. 0, # y
  138. ]
  139. # center the piece on top of the playing board
  140. self.piece[2] = int((self.gui.width - len(self.piece[0][0])) / 2)
  141. if self.collision():
  142. # new piece immediately collided. game over!
  143. return False
  144. # copy piece into data buffer
  145. self.put(False)
  146. # don't move in the placement-step
  147. return True
  148. oldPosition = (self.piece[2], self.piece[3])
  149. oldPiece = [x[:] for x in self.piece[0]]
  150. # button input
  151. if self.button == "u":
  152. # rotate piece
  153. # https://stackoverflow.com/a/48444999
  154. self.piece[0] = [list(x) for x in zip(*self.piece[0][::-1])]
  155. elif self.button == "l":
  156. if self.piece[2] > 0:
  157. self.piece[2] -= 1
  158. elif self.button == "r":
  159. if self.piece[2] < (self.gui.width - 1):
  160. self.piece[2] += 1
  161. else:
  162. # one pixel down
  163. self.piece[3] += 1
  164. # clear out piece from its old location
  165. self.put(True, oldPosition, oldPiece)
  166. collision = self.collision()
  167. if collision:
  168. # piece collided, put it back
  169. self.put(False, oldPosition, oldPiece)
  170. self.piece[0] = oldPiece
  171. self.piece[2] = oldPosition[0]
  172. self.piece[3] = oldPosition[1]
  173. if (self.button != "l") and (self.button != "r"):
  174. # but only stop playing it if it was moving down
  175. self.piece = None
  176. else:
  177. # copy piece at new location into buffer
  178. self.put(False)
  179. # clear previous input
  180. self.button = None
  181. return True
  182. def buttons(self):
  183. keys = self.input.get()
  184. if keys["left"]:
  185. self.button = "l"
  186. elif keys["right"]:
  187. self.button = "r"
  188. elif keys["up"]:
  189. self.button = "u"
  190. elif keys["down"]:
  191. self.button = "d"
  192. def draw(self):
  193. if self.done:
  194. if self.endText.finished():
  195. self.scoreText.draw()
  196. else:
  197. self.endText.draw()
  198. self.scoreText.restart()
  199. return
  200. if self.input != None:
  201. self.buttons()
  202. else:
  203. # TODO "AI"
  204. self.button = None
  205. now = time.time()
  206. if (self.button != None) or ((now - self.last) >= self.timestep) or (now < self.last):
  207. self.last = now
  208. cont = self.step()
  209. if cont == False:
  210. self.done = True
  211. self.scoreText.setText("Score: " + str(self.score), "uushi")
  212. self.endText.restart()
  213. for x in range(0, self.gui.width):
  214. for y in range(0, self.gui.height):
  215. self.gui.set_pixel(x, y, self.data[x][y])
  216. if __name__ == "__main__":
  217. import util
  218. t = util.getTarget()
  219. from gamepad import InputWrapper
  220. i = InputWrapper()
  221. d = Tetris(t, i, 0.1)
  222. t.loop(d.draw)