competitive_tetris.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. #!/usr/bin/python3
  2. """
  3. Competitive tetris server
  4. """
  5. import random
  6. import math
  7. import pygame
  8. import zmq
  9. import flatbuffers
  10. import tetrominos
  11. import game_state
  12. import wire_protocol.Command
  13. import wire_protocol.CommandRequest
  14. import wire_protocol.CommandResponse
  15. import wire_protocol.Playfield
  16. import wire_protocol.CommandResponseError
  17. import wire_protocol.GameState
  18. pygame.font.init()
  19. # GLOBALS VARS
  20. S_WIDTH = 800
  21. S_HEIGHT = 700
  22. PLAY_WIDTH = 300 # meaning 300 // 10 = 30 width per block
  23. PLAY_HEIGHT = 600 # meaning 600 // 20 = 30 height per block
  24. BLOCK_SIZE = 30
  25. TOP_LEFT_X = (S_WIDTH - PLAY_WIDTH) // 2
  26. TOP_LEFT_Y = S_HEIGHT - PLAY_HEIGHT
  27. def draw_text_middle(surface, text, size, color):
  28. font = pygame.font.SysFont("comicsans", size, bold=True)
  29. label = font.render(text, 1, color)
  30. surface.blit(label, (TOP_LEFT_X + PLAY_WIDTH /2 - (label.get_width()/2), TOP_LEFT_Y + PLAY_HEIGHT/2 - label.get_height()/2))
  31. def draw_grid(surface, grid):
  32. sx = TOP_LEFT_X
  33. sy = TOP_LEFT_Y
  34. for i in range(len(grid)):
  35. pygame.draw.line(surface, (128,128,128), (sx, sy + i*BLOCK_SIZE), (sx+PLAY_WIDTH, sy+ i*BLOCK_SIZE))
  36. for j in range(len(grid[i])):
  37. pygame.draw.line(surface, (128, 128, 128), (sx + j*BLOCK_SIZE, sy),(sx + j*BLOCK_SIZE, sy + PLAY_HEIGHT))
  38. def draw_next_shape(shape, surface):
  39. font = pygame.font.SysFont('comicsans', 30)
  40. label = font.render('Next Shape', 1, (255,255,255))
  41. sx = TOP_LEFT_X + PLAY_WIDTH + 0
  42. sy = TOP_LEFT_Y + PLAY_HEIGHT/2 - 100
  43. shape_raw = tetrominos.SHAPES_RAW[shape][0]
  44. for i, line in enumerate(shape_raw):
  45. row = list(line)
  46. for j, column in enumerate(row):
  47. if column == '0':
  48. pygame.draw.rect(
  49. surface, (255,0,0),
  50. (sx + j*BLOCK_SIZE, sy + i*BLOCK_SIZE,
  51. BLOCK_SIZE, BLOCK_SIZE),
  52. 0)
  53. surface.blit(label, (sx + 10, sy - 30))
  54. def draw_game_state_to_window(surface, state):
  55. surface.fill((0, 0, 0))
  56. pygame.font.init()
  57. font = pygame.font.SysFont('comicsans', 60)
  58. label = font.render('Tetris', 1, (255, 255, 255))
  59. surface.blit(label,
  60. (TOP_LEFT_X + PLAY_WIDTH / 2 - (label.get_width() / 2), 30))
  61. # current score
  62. font = pygame.font.SysFont('comicsans', 30)
  63. label = font.render(f'Score: {state.data.score}', 1, (255,255,255))
  64. sx = TOP_LEFT_X + PLAY_WIDTH + 50
  65. sy = TOP_LEFT_Y + PLAY_HEIGHT/2 - 100
  66. surface.blit(label, (sx + 20, sy + 160))
  67. field = state.get_field()
  68. for row in range(field.shape[0]):
  69. for col in range(field.shape[1]):
  70. color = (0,0,0)
  71. tile = field[row,col]
  72. if tile == 1:
  73. color = (0,255,0)
  74. elif tile == 2:
  75. color = (255,0,0)
  76. pygame.draw.rect(surface, color,
  77. (TOP_LEFT_X + col*BLOCK_SIZE,
  78. TOP_LEFT_Y + row*BLOCK_SIZE,
  79. BLOCK_SIZE, BLOCK_SIZE), 0)
  80. pygame.draw.rect(surface, (255, 0, 0),
  81. (TOP_LEFT_X, TOP_LEFT_Y, PLAY_WIDTH, PLAY_HEIGHT), 5)
  82. draw_grid(surface, field)
  83. def handle_shift_left(state):
  84. error_code = wire_protocol.CommandResponseError.CommandResponseError.OK
  85. next_state = state.shift_left()
  86. if next_state is None:
  87. error_code = wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND
  88. else:
  89. state = next_state
  90. builder = flatbuffers.Builder()
  91. wire_protocol.CommandResponse.Start(builder)
  92. wire_protocol.CommandResponse.AddError(builder, error_code)
  93. rsp = wire_protocol.CommandResponse.End(builder)
  94. builder.Finish(rsp)
  95. return state, builder
  96. def handle_shift_right(state):
  97. error_code = wire_protocol.CommandResponseError.CommandResponseError.OK
  98. next_state = state.shift_right()
  99. if next_state is None:
  100. error_code = wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND
  101. else:
  102. state = next_state
  103. builder = flatbuffers.Builder()
  104. wire_protocol.CommandResponse.Start(builder)
  105. wire_protocol.CommandResponse.AddError(builder, error_code)
  106. rsp = wire_protocol.CommandResponse.End(builder)
  107. builder.Finish(rsp)
  108. return state, builder
  109. def handle_rotate(state):
  110. error_code = wire_protocol.CommandResponseError.CommandResponseError.OK
  111. next_state = state.rotate()
  112. if next_state is None:
  113. error_code = wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND
  114. else:
  115. state = next_state
  116. builder = flatbuffers.Builder()
  117. wire_protocol.CommandResponse.Start(builder)
  118. wire_protocol.CommandResponse.AddError(builder, error_code)
  119. rsp = wire_protocol.CommandResponse.End(builder)
  120. builder.Finish(rsp)
  121. return state, builder
  122. def handle_down(state):
  123. error_code = wire_protocol.CommandResponseError.CommandResponseError.OK
  124. next_state = state.down()
  125. if next_state is None:
  126. error_code = wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND
  127. else:
  128. state = next_state
  129. builder = flatbuffers.Builder()
  130. wire_protocol.CommandResponse.Start(builder)
  131. wire_protocol.CommandResponse.AddError(builder, error_code)
  132. rsp = wire_protocol.CommandResponse.End(builder)
  133. builder.Finish(rsp)
  134. return state, builder
  135. def handle_land(state):
  136. error_code = wire_protocol.CommandResponseError.CommandResponseError.OK
  137. next_state = state.land()
  138. if next_state is None:
  139. error_code = wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND
  140. else:
  141. state = next_state
  142. builder = flatbuffers.Builder()
  143. wire_protocol.CommandResponse.Start(builder)
  144. wire_protocol.CommandResponse.AddError(builder, error_code)
  145. rsp = wire_protocol.CommandResponse.End(builder)
  146. builder.Finish(rsp)
  147. return state, builder
  148. def handle_other(state):
  149. builder = flatbuffers.Builder()
  150. wire_protocol.CommandResponse.Start(builder)
  151. wire_protocol.CommandResponse.AddError(
  152. builder,
  153. wire_protocol.CommandResponseError.CommandResponseError.UNKNOWN_COMMAND)
  154. rsp = wire_protocol.CommandResponse.End(builder)
  155. builder.Finish(rsp)
  156. return state, builder
  157. def handle_get_game_state(state, millis_before_down):
  158. error_code = wire_protocol.CommandResponseError.CommandResponseError.OK
  159. builder = flatbuffers.Builder()
  160. wire_protocol.GameState.StartGridVector(builder, state.data.grid.size)
  161. builder.head -= state.data.grid.size
  162. builder.Bytes[builder.head:(builder.head + state.data.grid.size)] = state.data.grid.tobytes()
  163. grid_vector = builder.EndVector(state.data.grid.size)
  164. wire_protocol.GameState.Start(builder)
  165. wire_protocol.GameState.AddRows(builder, state.data.grid.shape[0])
  166. wire_protocol.GameState.AddCols(builder, state.data.grid.shape[1])
  167. wire_protocol.GameState.AddNextPiece(builder, state.data.next_piece)
  168. wire_protocol.GameState.AddCurrentPiece(builder, state.data.current_piece)
  169. wire_protocol.GameState.AddPieceRotation(builder, state.data.rotation)
  170. wire_protocol.GameState.AddPieceRow(builder, state.data.row)
  171. wire_protocol.GameState.AddPieceCol(builder, state.data.col)
  172. wire_protocol.GameState.AddScore(builder, state.data.score)
  173. wire_protocol.GameState.AddMillisBeforeDown(builder, millis_before_down)
  174. wire_protocol.GameState.AddGrid(builder, grid_vector)
  175. game_state_msg = wire_protocol.GameState.End(builder)
  176. wire_protocol.CommandResponse.Start(builder)
  177. wire_protocol.CommandResponse.AddError(builder, error_code)
  178. wire_protocol.CommandResponse.AddGameState(builder, game_state_msg)
  179. rsp = wire_protocol.CommandResponse.End(builder)
  180. builder.Finish(rsp)
  181. return state, builder
  182. def main(win):
  183. run = True
  184. frame_time = 0
  185. clock = pygame.time.Clock()
  186. zmq_context = zmq.Context()
  187. socket = zmq_context.socket(zmq.REP)
  188. socket.bind("tcp://*:5555")
  189. poller = zmq.Poller()
  190. poller.register(socket, zmq.POLLIN)
  191. def _next_piece_func():
  192. return random.choice(range(len(tetrominos.SHAPES)))
  193. state = game_state.create_initial_game_state(_next_piece_func)
  194. while run:
  195. clock.tick()
  196. dt_millis = clock.get_time()
  197. frame_time += dt_millis
  198. for event in pygame.event.get():
  199. if event.type == pygame.QUIT:
  200. run = False
  201. pygame.display.quit()
  202. for sock, _ in poller.poll(timeout=0):
  203. req_buf = sock.recv()
  204. req = wire_protocol.CommandRequest.CommandRequest.GetRootAs(req_buf)
  205. builder = None
  206. if req.Command() == wire_protocol.Command.Command.ShiftLeft:
  207. state, builder = handle_shift_left(state)
  208. elif req.Command() == wire_protocol.Command.Command.ShiftRight:
  209. state, builder = handle_shift_right(state)
  210. elif req.Command() == wire_protocol.Command.Command.Rotate:
  211. state, builder = handle_rotate(state)
  212. elif req.Command() == wire_protocol.Command.Command.Down:
  213. state, builder = handle_down(state)
  214. elif req.Command() == wire_protocol.Command.Command.Land:
  215. state, builder = handle_land(state)
  216. elif req.Command() == wire_protocol.Command.Command.GetGameState:
  217. state, builder = handle_get_game_state(
  218. state, int(math.floor(max(0, 500 - frame_time))))
  219. else:
  220. state, builder = handle_other(state)
  221. socket.send(builder.Output())
  222. is_game_running = True
  223. if frame_time >= 500:
  224. state, is_game_running = state.advance()
  225. frame_time = 0
  226. draw_game_state_to_window(win, state)
  227. draw_next_shape(state.data.next_piece, win)
  228. pygame.display.update()
  229. if not is_game_running:
  230. draw_text_middle(win, "YOU LOST!", 80, (255,255,255))
  231. pygame.display.update()
  232. pygame.time.delay(1500)
  233. run = False
  234. def main_menu(win):
  235. run = True
  236. while run:
  237. win.fill((0,0,0))
  238. draw_text_middle(win, 'Press Any Key To Play', 60, (255,255,255))
  239. pygame.display.update()
  240. for event in pygame.event.get():
  241. if event.type == pygame.QUIT:
  242. run = False
  243. if event.type == pygame.KEYDOWN:
  244. main(win)
  245. pygame.display.quit()
  246. win = pygame.display.set_mode((S_WIDTH, S_HEIGHT))
  247. pygame.display.set_caption('Tetris')
  248. main_menu(win)