competitive_tetris.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. #!/usr/bin/python3
  2. """
  3. Competitive tetris server
  4. """
  5. import random
  6. import pygame
  7. import zmq
  8. import flatbuffers
  9. import tetrominos
  10. import game_state
  11. import wire_protocol.Command
  12. import wire_protocol.CommandRequest
  13. import wire_protocol.CommandResponse
  14. import wire_protocol.Playfield
  15. import wire_protocol.CommandResponseError
  16. pygame.font.init()
  17. # GLOBALS VARS
  18. S_WIDTH = 800
  19. S_HEIGHT = 700
  20. PLAY_WIDTH = 300 # meaning 300 // 10 = 30 width per block
  21. PLAY_HEIGHT = 600 # meaning 600 // 20 = 30 height per block
  22. BLOCK_SIZE = 30
  23. TOP_LEFT_X = (S_WIDTH - PLAY_WIDTH) // 2
  24. TOP_LEFT_Y = S_HEIGHT - PLAY_HEIGHT
  25. def draw_text_middle(surface, text, size, color):
  26. font = pygame.font.SysFont("comicsans", size, bold=True)
  27. label = font.render(text, 1, color)
  28. surface.blit(label, (TOP_LEFT_X + PLAY_WIDTH /2 - (label.get_width()/2), TOP_LEFT_Y + PLAY_HEIGHT/2 - label.get_height()/2))
  29. def draw_grid(surface, grid):
  30. sx = TOP_LEFT_X
  31. sy = TOP_LEFT_Y
  32. for i in range(len(grid)):
  33. pygame.draw.line(surface, (128,128,128), (sx, sy + i*BLOCK_SIZE), (sx+PLAY_WIDTH, sy+ i*BLOCK_SIZE))
  34. for j in range(len(grid[i])):
  35. pygame.draw.line(surface, (128, 128, 128), (sx + j*BLOCK_SIZE, sy),(sx + j*BLOCK_SIZE, sy + PLAY_HEIGHT))
  36. def draw_next_shape(shape, surface):
  37. font = pygame.font.SysFont('comicsans', 30)
  38. label = font.render('Next Shape', 1, (255,255,255))
  39. sx = TOP_LEFT_X + PLAY_WIDTH + 0
  40. sy = TOP_LEFT_Y + PLAY_HEIGHT/2 - 100
  41. shape_raw = tetrominos.SHAPES_RAW[shape][0]
  42. for i, line in enumerate(shape_raw):
  43. row = list(line)
  44. for j, column in enumerate(row):
  45. if column == '0':
  46. pygame.draw.rect(
  47. surface, (255,0,0),
  48. (sx + j*BLOCK_SIZE, sy + i*BLOCK_SIZE,
  49. BLOCK_SIZE, BLOCK_SIZE),
  50. 0)
  51. surface.blit(label, (sx + 10, sy - 30))
  52. def draw_game_state_to_window(surface, state):
  53. surface.fill((0, 0, 0))
  54. pygame.font.init()
  55. font = pygame.font.SysFont('comicsans', 60)
  56. label = font.render('Tetris', 1, (255, 255, 255))
  57. surface.blit(label,
  58. (TOP_LEFT_X + PLAY_WIDTH / 2 - (label.get_width() / 2), 30))
  59. # current score
  60. font = pygame.font.SysFont('comicsans', 30)
  61. label = font.render(f'Score: {state.data.score}', 1, (255,255,255))
  62. sx = TOP_LEFT_X + PLAY_WIDTH + 50
  63. sy = TOP_LEFT_Y + PLAY_HEIGHT/2 - 100
  64. surface.blit(label, (sx + 20, sy + 160))
  65. field = state.get_field()
  66. for row in range(field.shape[0]):
  67. for col in range(field.shape[1]):
  68. color = (0,0,0)
  69. tile = field[row,col]
  70. if tile == 1:
  71. color = (0,255,0)
  72. elif tile == 2:
  73. color = (255,0,0)
  74. pygame.draw.rect(surface, color,
  75. (TOP_LEFT_X + col*BLOCK_SIZE,
  76. TOP_LEFT_Y + row*BLOCK_SIZE,
  77. BLOCK_SIZE, BLOCK_SIZE), 0)
  78. pygame.draw.rect(surface, (255, 0, 0),
  79. (TOP_LEFT_X, TOP_LEFT_Y, PLAY_WIDTH, PLAY_HEIGHT), 5)
  80. draw_grid(surface, field)
  81. def main(win):
  82. run = True
  83. frame_time = 0
  84. clock = pygame.time.Clock()
  85. zmq_context = zmq.Context()
  86. socket = zmq_context.socket(zmq.REP)
  87. socket.bind("tcp://*:5555")
  88. poller = zmq.Poller()
  89. poller.register(socket, zmq.POLLIN)
  90. def _next_piece_func():
  91. return random.choice(range(len(tetrominos.SHAPES)))
  92. state = game_state.create_initial_game_state(_next_piece_func)
  93. while run:
  94. clock.tick()
  95. dt_millis = clock.get_time()
  96. frame_time += dt_millis
  97. for event in pygame.event.get():
  98. if event.type == pygame.QUIT:
  99. run = False
  100. pygame.display.quit()
  101. for sock, _ in poller.poll(timeout=0):
  102. req_buf = sock.recv()
  103. req = wire_protocol.CommandRequest.CommandRequest.GetRootAs(req_buf)
  104. error_code = wire_protocol.CommandResponseError.CommandResponseError.OK
  105. cells = None
  106. if req.Command() == wire_protocol.Command.Command.ShiftLeft:
  107. next_state = state.shift(-1)
  108. if next_state is None:
  109. error_code = wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND
  110. else:
  111. state = next_state
  112. elif req.Command() == wire_protocol.Command.Command.ShiftRight:
  113. next_state = state.shift(1)
  114. if next_state is None:
  115. error_code = wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND
  116. else:
  117. state = next_state
  118. elif req.Command() == wire_protocol.Command.Command.Rotate:
  119. next_state = state.rotate()
  120. if next_state is None:
  121. error_code = wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND
  122. else:
  123. state = next_state
  124. elif req.Command() == wire_protocol.Command.Command.Land:
  125. next_state = state.land()
  126. if next_state is None:
  127. error_code = wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND
  128. else:
  129. state = next_state
  130. elif req.Command() == wire_protocol.Command.Command.GetGameState:
  131. cells = state.get_field()
  132. else:
  133. error_code = wire_protocol.CommandResponseError.CommandResponseError.UNKNOWN_COMMAND
  134. builder = flatbuffers.Builder()
  135. playfield = None
  136. if cells is not None:
  137. wire_protocol.Playfield.StartCellsVector(builder, cells.size)
  138. builder.head -= cells.size
  139. builder.Bytes[builder.head:(builder.head + cells.size)] = cells.tobytes()
  140. cells_vector = builder.EndVector(cells.size)
  141. wire_protocol.Playfield.Start(builder)
  142. wire_protocol.Playfield.AddRows(builder, cells.shape[0])
  143. wire_protocol.Playfield.AddCols(builder, cells.shape[1])
  144. wire_protocol.Playfield.AddCells(builder, cells_vector)
  145. playfield = wire_protocol.Playfield.End(builder)
  146. wire_protocol.CommandResponse.Start(builder)
  147. wire_protocol.CommandResponse.AddError(builder, error_code)
  148. if playfield is not None:
  149. wire_protocol.CommandResponse.AddPlayfield(builder, playfield)
  150. rsp = wire_protocol.CommandResponse.End(builder)
  151. builder.Finish(rsp)
  152. socket.send(builder.Output())
  153. is_game_running = True
  154. if frame_time >= 500:
  155. state, is_game_running = state.advance()
  156. frame_time = 0
  157. draw_game_state_to_window(win, state)
  158. draw_next_shape(state.data.next_piece, win)
  159. pygame.display.update()
  160. if not is_game_running:
  161. draw_text_middle(win, "YOU LOST!", 80, (255,255,255))
  162. pygame.display.update()
  163. pygame.time.delay(1500)
  164. run = False
  165. def main_menu(win):
  166. run = True
  167. while run:
  168. win.fill((0,0,0))
  169. draw_text_middle(win, 'Press Any Key To Play', 60, (255,255,255))
  170. pygame.display.update()
  171. for event in pygame.event.get():
  172. if event.type == pygame.QUIT:
  173. run = False
  174. if event.type == pygame.KEYDOWN:
  175. main(win)
  176. pygame.display.quit()
  177. win = pygame.display.set_mode((S_WIDTH, S_HEIGHT))
  178. pygame.display.set_caption('Tetris')
  179. main_menu(win)