소스 검색

Better server loop

Alexey Dorokhov 3 년 전
부모
커밋
9ed89b63bd
6개의 변경된 파일121개의 추가작업 그리고 49개의 파일을 삭제
  1. 20 0
      bot_example.py
  2. 82 34
      competitive_tetris.py
  3. 3 3
      game_state.py
  4. 8 6
      wire_protocol.fbs
  5. 7 6
      wire_protocol/Command.py
  6. 1 0
      wire_protocol/CommandResponseError.py

+ 20 - 0
bot_example.py

@@ -106,6 +106,18 @@ def main():
     socket = context.socket(zmq.REQ)
     socket.connect("tcp://localhost:5555")
 
+    builder = flatbuffers.Builder()
+    wire_protocol.CommandRequest.Start(builder)
+    wire_protocol.CommandRequest.AddCommand(
+            builder,
+            wire_protocol.Command.Command.StartGame)
+    req = wire_protocol.CommandRequest.End(builder)
+    builder.Finish(req)
+    socket.send(builder.Output())
+
+    rsp_buf = socket.recv()
+    rsp = wire_protocol.CommandResponse.CommandResponse.GetRootAs(rsp_buf)
+
     while True:
         builder = flatbuffers.Builder()
         wire_protocol.CommandRequest.Start(builder)
@@ -118,6 +130,8 @@ def main():
 
         rsp_buf = socket.recv()
         rsp = wire_protocol.CommandResponse.CommandResponse.GetRootAs(rsp_buf)
+        if rsp.Error() != wire_protocol.CommandResponseError.CommandResponseError.OK:
+           return 
 
         grid = rsp.GameState().GridAsNumpy()
         grid = grid.reshape((rsp.GameState().Rows(), rsp.GameState().Cols()))
@@ -150,6 +164,8 @@ def main():
                 socket.send(builder.Output())
                 rsp_buf = socket.recv()
                 rsp = wire_protocol.CommandResponse.CommandResponse.GetRootAs(rsp_buf)
+                if rsp.Error() != wire_protocol.CommandResponseError.CommandResponseError.OK:
+                    return
 
             command = wire_protocol.Command.Command.ShiftRight
             if best_move.col_offset < 0:
@@ -163,6 +179,8 @@ def main():
                 socket.send(builder.Output())
                 rsp_buf = socket.recv()
                 rsp = wire_protocol.CommandResponse.CommandResponse.GetRootAs(rsp_buf)
+                if rsp.Error() != wire_protocol.CommandResponseError.CommandResponseError.OK:
+                    return
 
             builder = flatbuffers.Builder()
             wire_protocol.CommandRequest.Start(builder)
@@ -173,6 +191,8 @@ def main():
             socket.send(builder.Output())
             rsp_buf = socket.recv()
             rsp = wire_protocol.CommandResponse.CommandResponse.GetRootAs(rsp_buf)
+            if rsp.Error() != wire_protocol.CommandResponseError.CommandResponseError.OK:
+                return
 
 if __name__ == "__main__":
     main()

+ 82 - 34
competitive_tetris.py

@@ -32,7 +32,8 @@ TOP_LEFT_Y = S_HEIGHT - PLAY_HEIGHT
 
 
 def draw_text_middle(surface, text, size, color):
-    font = pygame.font.SysFont("comicsans", size, bold=True)
+    font = pygame.font.SysFont(
+            pygame.font.get_default_font(), size, bold=True)
     label = font.render(text, 1, color)
 
     surface.blit(label, (TOP_LEFT_X + PLAY_WIDTH /2 - (label.get_width()/2), TOP_LEFT_Y + PLAY_HEIGHT/2 - label.get_height()/2))
@@ -49,8 +50,8 @@ def draw_grid(surface, grid):
 
 
 def draw_next_shape(shape, surface):
-    font = pygame.font.SysFont('comicsans', 30)
-    label = font.render('Next Shape', 1, (255,255,255))
+    font = pygame.font.SysFont(pygame.font.get_default_font(), 30)
+    label = font.render("Next Shape", 1, (255,255,255))
 
     sx = TOP_LEFT_X + PLAY_WIDTH + 0
     sy = TOP_LEFT_Y + PLAY_HEIGHT/2 - 100
@@ -59,7 +60,7 @@ def draw_next_shape(shape, surface):
     for i, line in enumerate(shape_raw):
         row = list(line)
         for j, column in enumerate(row):
-            if column == '0':
+            if column == "0":
                 pygame.draw.rect(
                         surface, (255,0,0),
                         (sx + j*BLOCK_SIZE, sy + i*BLOCK_SIZE,
@@ -72,14 +73,14 @@ def draw_game_state_to_window(surface, state):
     surface.fill((0, 0, 0))
 
     pygame.font.init()
-    font = pygame.font.SysFont('comicsans', 60)
-    label = font.render('Tetris', 1, (255, 255, 255))
+    font = pygame.font.SysFont(pygame.font.get_default_font(), 60)
+    label = font.render("Tetris", 1, (255, 255, 255))
     surface.blit(label, 
                  (TOP_LEFT_X + PLAY_WIDTH / 2 - (label.get_width() / 2), 30))
 
     # current score
-    font = pygame.font.SysFont('comicsans', 30)
-    label = font.render(f'Score: {state.data.score}', 1, (255,255,255))
+    font = pygame.font.SysFont(pygame.font.get_default_font(), 30)
+    label = font.render(f"Score: {state.data.score}", 1, (255,255,255))
     sx = TOP_LEFT_X + PLAY_WIDTH + 50
     sy = TOP_LEFT_Y + PLAY_HEIGHT/2 - 100
     surface.blit(label, (sx + 20, sy + 160))
@@ -228,30 +229,25 @@ def handle_get_game_state(state, millis_before_down):
     return state, builder
 
 
-def main(win):
-    run = True
+def game_loop(win, poller):
+    is_game_running = True
     frame_time = 0
     clock = pygame.time.Clock()
 
-    zmq_context = zmq.Context()
-    socket = zmq_context.socket(zmq.REP)
-    socket.bind("tcp://*:5555")
-    poller = zmq.Poller()
-    poller.register(socket, zmq.POLLIN)
-
     def _next_piece_func():
         return random.choice(range(len(tetrominos.SHAPES)))
     state = game_state.create_initial_game_state(_next_piece_func)
 
-    while run:
+    while is_game_running:
         clock.tick()
         dt_millis = clock.get_time()
         frame_time += dt_millis
 
         for event in pygame.event.get():
+            if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
+                return None, False
             if event.type == pygame.QUIT:
-                run = False
-                pygame.display.quit()
+                return None, False
 
         for sock, _ in poller.poll(timeout=0):
             req_buf = sock.recv()
@@ -274,9 +270,8 @@ def main(win):
             else:
                 state, builder = handle_other(state)
 
-            socket.send(builder.Output())
+            sock.send(builder.Output())
 
-        is_game_running = True
         if frame_time >= 500:
             state, is_game_running = state.advance()
             frame_time = 0
@@ -285,28 +280,81 @@ def main(win):
         draw_next_shape(state.data.next_piece, win)
         pygame.display.update()
 
-        if not is_game_running:
-            draw_text_middle(win, "YOU LOST!", 80, (255,255,255))
-            pygame.display.update()
-            pygame.time.delay(1500)
-            run = False
+    return state, True
+
+
+def server_loop(win):
+    zmq_context = zmq.Context()
+    socket = zmq_context.socket(zmq.REP)
+    socket.bind("tcp://*:5555")
+    poller = zmq.Poller()
+    poller.register(socket, zmq.POLLIN)
 
+    state = None
 
-def main_menu(win):
     run = True
+    is_game_over_sent = True
     while run:
         win.fill((0,0,0))
-        draw_text_middle(win, 'Press Any Key To Play', 60, (255,255,255))
+        if state is None:
+            draw_text_middle(
+                    win, "Waiting for connection.", 60, (255,255,255))
+        else:
+            draw_text_middle(
+                    win,
+                    f"Score = {state.data.score}. "
+                    "Reconnect?", 60, (255,255,255))
+
         pygame.display.update()
         for event in pygame.event.get():
-            if event.type == pygame.QUIT:
+            if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
+                run = False
+            elif event.type == pygame.QUIT:
                 run = False
-            if event.type == pygame.KEYDOWN:
-                main(win)
+
+        run_game_loop = False
+        for sock, _ in poller.poll(timeout=0):
+            req_buf = sock.recv()
+            req = wire_protocol.CommandRequest.CommandRequest.GetRootAs(req_buf)
+            builder = None
+
+            if req.Command() == wire_protocol.Command.Command.StartGame:
+                builder = flatbuffers.Builder()
+                wire_protocol.CommandResponse.Start(builder)
+                wire_protocol.CommandResponse.AddError(
+                        builder, wire_protocol.CommandResponseError.CommandResponseError.OK)
+                rsp = wire_protocol.CommandResponse.End(builder)
+                builder.Finish(rsp)
+                sock.send(builder.Output())
+                run_game_loop = True
+            elif not is_game_over_sent:
+                is_game_over_sent = True
+
+                builder = flatbuffers.Builder()
+                wire_protocol.CommandResponse.Start(builder)
+                wire_protocol.CommandResponse.AddError(
+                        builder,
+                        wire_protocol.CommandResponseError.CommandResponseError.GAME_OVER)
+                rsp = wire_protocol.CommandResponse.End(builder)
+                builder.Finish(rsp)
+                sock.send(builder.Output())
+            else:
+                builder = flatbuffers.Builder()
+                wire_protocol.CommandResponse.Start(builder)
+                wire_protocol.CommandResponse.AddError(
+                        builder,
+                        wire_protocol.CommandResponseError.CommandResponseError.INVALID_COMMAND)
+                rsp = wire_protocol.CommandResponse.End(builder)
+                builder.Finish(rsp)
+                sock.send(builder.Output())
+
+        if run_game_loop:
+            state, run = game_loop(win, poller)
 
     pygame.display.quit()
 
 
-win = pygame.display.set_mode((S_WIDTH, S_HEIGHT))
-pygame.display.set_caption('Tetris')
-main_menu(win)
+if __name__ == "__main__":
+    win = pygame.display.set_mode((S_WIDTH, S_HEIGHT))
+    pygame.display.set_caption("Tetris")
+    server_loop(win)

+ 3 - 3
game_state.py

@@ -38,9 +38,9 @@ def tetromino_to_field_at(tetromino, row, col):
     return array
 
 
-GameStateData = collections.namedtuple('GameStateData',
-                                       ['grid', 'current_piece', 'next_piece',
-                                        'rotation', 'row', 'col', 'score'])
+GameStateData = collections.namedtuple("GameStateData",
+                                       ["grid", "current_piece", "next_piece",
+                                        "rotation", "row", "col", "score"])
 
 
 class GameState:

+ 8 - 6
wire_protocol.fbs

@@ -1,18 +1,20 @@
 namespace wire_protocol;
 
 enum Command : short {
-  ShiftLeft = 0,
-  ShiftRight = 1,
-  Rotate = 2,
-  Down = 3,
-  Land = 4,
-  GetGameState = 5,
+  StartGame = 0,
+  ShiftLeft = 1,
+  ShiftRight = 2,
+  Rotate = 3,
+  Down = 4,
+  Land = 5,
+  GetGameState = 6,
 }
 
 enum CommandResponseError : short {
   OK = 0,
   INVALID_COMMAND = 1,
   UNKNOWN_COMMAND = 2,
+  GAME_OVER = 3,
 }
 
 table CommandRequest {

+ 7 - 6
wire_protocol/Command.py

@@ -3,9 +3,10 @@
 # namespace: wire_protocol
 
 class Command(object):
-    ShiftLeft = 0
-    ShiftRight = 1
-    Rotate = 2
-    Down = 3
-    Land = 4
-    GetGameState = 5
+    StartGame = 0
+    ShiftLeft = 1
+    ShiftRight = 2
+    Rotate = 3
+    Down = 4
+    Land = 5
+    GetGameState = 6

+ 1 - 0
wire_protocol/CommandResponseError.py

@@ -6,3 +6,4 @@ class CommandResponseError(object):
     OK = 0
     INVALID_COMMAND = 1
     UNKNOWN_COMMAND = 2
+    GAME_OVER = 3