def setup(): test_board = FishBoard(4, 3) player_1 = Player("red", 0, 0, []) player_2 = Player("white", 0, 0, []) test_players = [player_1, player_2] state = FishGameState(board=test_board, num_players=len(test_players), players=test_players, current_player=player_1, phase=GameStatePhase.INITIAL) return state
def main(): player1 = Player('first', 'Z') player2 = Player('second', 'O') game = Game(player1=player1, player2=player2) game.check(row=1, column=2) game.check(row=2, column=2) game.check(row=1, column=1) game.check(row=2, column=1) game.check(row=1, column=3) game.check(row=2, column=3) print(game) # print(game.current_player) # game.check() print(game.is_end())
def Demo2(n_fish_per_tile=1) -> FishGameController: """ Randomly Generated Holes with 1 Fish on all tiles... :return: A controller to be execute in main. """ hexgrid = HexGridView() board = FishBoard(num_rows=HEXES_ROWS, num_cols=HEXES_COLUMNS) for row in range(board.num_rows): for col in range(board.num_cols): if board.retrieve_num_fish( row=row, col=col) == 0 and board.is_tile(row=row, col=col): for i in range(n_fish_per_tile): board = board.add_fish(row=row, col=col) player_1 = Player("red", 0, 0, []) state = FishGameState(board=board, num_players=1, players=[player_1], current_player=player_1) behavior = FishGameBehavior(state=state, view=hexgrid) controller = FishGameController(behavior=behavior, view=hexgrid, state=state) return controller
def test_create_game_state(self): board = FishBoard(4, 3) game_state = FishGameState(board, 3, TestGameState.player_list, current_player=TestGameState.player_list[0], phase=GameStatePhase.INITIAL) expected_players = { "red": Player(color="red", age=0, score=0, penguins=[]), "white": Player(color="white", age=0, score=0, penguins=[]), "brown": Player(color="brown", age=0, score=0, penguins=[]) } self.assertEqual(game_state.players, expected_players) self.assertEqual(game_state.board, board) self.assertEqual(game_state.num_players, 3) self.assertEqual(game_state.num_penguins, 3)
def add_penguin(self, row, col, color): """ Add a penguin for the given player id at the given row and column :param row: the row of the tile to add penguin on :param col: the col of the tile to add penguin on :param color: the color who will be adding the penguin :return: Updated GameState with added penguin """ is_valid, err = self.can_add_penguin(row=row, col=col, color=color) if not is_valid: raise ValueError(err) players = [] for player in self.players.values(): if player.color == color: players.append( Player.from_dict( { **player._asdict(), **{ "penguins": player.penguins + [(Coordinate(row, col))] } })) else: players.append(player) return FishGameState(board=self.board, num_players=self.num_players, players=players, current_player=self.next_player(), phase=self.phase)
def Demo3(holes: List[Coordinate]) -> FishGameController: """ Given the list of holes, render a board with holes at those cooridates and 1 fish at every non-hole tile. :param holes: List of hole Coordinates :return: A controller to be execute in main. """ hexgrid = HexGridView() board = FishBoard(num_rows=HEXES_ROWS, num_cols=HEXES_COLUMNS) for hole in holes: board = board.create_hole(row=hole.row, col=hole.col) for row in range(board.num_rows): for col in range(board.num_cols): if board.retrieve_num_fish( row=row, col=col) == 0 and board.is_tile(row=row, col=col): board = board.add_fish(row=row, col=col) player_1 = Player("red", 0, 0, []) state = FishGameState(board=board, num_players=1, players=[player_1], current_player=player_1) behavior = FishGameBehavior(state=state, view=hexgrid) controller = FishGameController(behavior=behavior, view=hexgrid, state=state) return controller
def Demo1() -> FishGameController: """ "Randomly Generate number of holes, hole locations, number of fish, and fish location. :return: A controller to be execute in main. """ hexgrid = HexGridView() board = FishBoard(num_rows=HEXES_ROWS, num_cols=HEXES_COLUMNS) board = RandomlyGenerateHole(board) board = RandomlyGenerateFish(board) player_1 = Player("red", 0, 0, []) state = FishGameState(board=board, num_players=1, players=[player_1], current_player=player_1) behavior = FishGameBehavior(state=state, view=hexgrid) controller = FishGameController(behavior=behavior, view=hexgrid, state=state) return controller
def setup(): test_board = FishBoard(4, 3) player_1 = Player("red", 0, 0, []) player_2 = Player("white", 0, 0, []) test_players = [player_1, player_2] state = FishGameState(board=test_board, num_players=len(test_players), players=test_players, current_player=player_1, phase=GameStatePhase.INITIAL) state = state.add_penguin(0, 0, "red") \ .add_penguin(0, 2, "white") \ .add_penguin(0, 1, "red") \ .add_penguin(1, 2, "white") \ .add_penguin(2, 0, "red") \ .add_penguin(2, 2, "white") \ .add_penguin(3, 0, "red") \ .add_penguin(3, 2, "white") return state.finalize(), state.board, state.players
def Demo1() -> FishGameController: """ Players in but no penguins has been selected. No Penguins Rendered. :return: A controller to be execute in main. """ board, hexgrid = setup() player_1 = Player("red", 0, 0, []) player_2 = Player("white", 0, 0, []) player_3 = Player("brown", 0, 0, []) factory = FishGameStateFactory(board=board, players=[player_1, player_2, player_3]) state = factory.build() behavior = FishGameBehavior(state=state, view=hexgrid) controller = FishGameController(behavior=behavior, view=hexgrid, state=state) return controller
def Demo2() -> FishGameController: """ Non-Hole board where players have each selected 1 penguin. :return: A controller to be execute in main. """ board, hexgrid = setup(no_holes=True) player_1 = Player("red", 0, 0, []) player_2 = Player("white", 0, 0, []) factory = FishGameStateFactory(board=board, players=[player_1, player_2]) factory = factory.add_penguin(row=0, col=0, color="red") factory = factory.add_penguin(row=1, col=1, color="white") state = factory.build() behavior = FishGameBehavior(state=state, view=hexgrid) controller = FishGameController(behavior=behavior, view=hexgrid, state=state) return controller
def new_game(self): inventory = Inventory(9, 3, self.crafts, self) try: # стартовый набор inventory.add_item(self.objects["torch"].generate_item(20)) inventory.add_item(self.objects["dirt"].generate_item(120)) except Exception: pass self.gravity_force = 0 self.chunk_controller = ChunkController( self.block_width + self.additional, self.block_height + self.additional, random.randint(0, 100000)) self.light_controller = LightController( self.block_width + self.additional, self.block_height + self.additional) self.sun = GlobalRotatingObject(self.sun_tile) self.moon = GlobalRotatingObject(self.moon_tile, coff=1) val = self.chunk_controller.get_player_start_pos( 100000 ) # на 0 координате "стык" - мир на отрицательных координатах является отражением мира на положительных self.player = Player(100000, val - 2, inventory, self) self.player.make_model() self.pos_center = (int(self.player.x), int(self.player.y)) self.started_time = time.time()
def gen_load(self, data): self.chunk_controller = ChunkController( self.block_width + self.additional, self.block_height + self.additional, data['chunk_controller']['seed']) self.chunk_controller.load(data['chunk_controller'], self.objects) self.started_time = time.time() - data['started_time'] if not self.is_server: inv = data['player']['inventory'] inventory = Inventory(inv['x_size'], inv['y_size'], self.crafts, self) inventory.get_sync(inv, self.objects) inventory.craft_model.craft_make_handle(None, None, None, None) self.player = Player(data['player']['x'], data['player']['y'], inventory, self) self.player.make_model() self.status = IN_GAME self.light_controller = LightController( self.block_width + self.additional, self.block_height + self.additional) self.sun = GlobalRotatingObject(self.sun_tile) self.moon = GlobalRotatingObject(self.moon_tile, coff=1) self.pos_center = (int(self.player.x), int(self.player.y)) self.gravity_force = 0
def kick_player(self, player_to_kick: Player): updated_players = [ Player.from_dict({ **player._asdict(), **{ "penguins": [] } }) if player.color == player_to_kick.color else player for player in self.players.values() ] return FishGameState(board=self.board, num_players=self.num_players, players=updated_players, current_player=self.next_player(), phase=self.phase)
def connect(self, ip, port, name): try: self.new_game() self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.connect((ip, int(port))) self.connected = True self.send({'request': JOIN_REQUEST, 'data': {'name': name}}) response = self.receive() if not response['success']: # запрос наверное может быть отклонен self.server_leave() self.interface.server_message.generate_text( response['reason'], self.text_font, self.width // 20) self.interface.status = CONNECT_ERROR return self.chunk_controller = ChunkController( self.block_width + self.additional, self.block_height + self.additional, response['level']['chunk_controller']['seed']) self.chunk_controller.load( response['level']['chunk_controller'], self.objects) # загрузка соответствующего мира self.player.x = response['player_data']['x'] self.player.y = response['player_data']['y'] self.player.inventory.get_sync( response['player_data']['inventory'], self.objects) self.started_time = time.time() - response['started_time'] for i in response['players']: self.other_players[i['id']] = { 'name': i['name'], 'model': Player(i['x'], i['y'], None, self), 'selected': self.objects[i['selected']].generate_item(1) if i['selected'] != 'None' else None } self.other_players[i['id']]['model'].make_model() self.other_players[i['id']]['model'].update_model( self.player.x, self.player.y) self.data_handler = threading.Thread(target=self.data_receive) self.data_handler.start() self.start() except socket.error as e: self.server_leave() self.interface.server_message.generate_text( str(e), self.text_font, self.width // 20) self.interface.status = CONNECT_ERROR
def test_create_choose_action_base_case_no_children_for_non_maximizing_player( self): """ If the player has turns > 0 but has no children - end-game - and this is not the maximizing player, then we return None. """ state, *_ = self.setup() production_tree = GameStateTree(state, previous_action=None) production_tree.get_children = MagicMock(return_value=[]) production_tree.previous_action = Action( player_color=production_tree.state.current_player.color, start=Coordinate(0, 0), end=Coordinate(1, 0)) expected = None _, actual = GenericStrategyComponent.minimax(tree=production_tree, num_rounds=2, maximizing_player=Player( "white", 0, 0, [])) self.assertEqual(expected, actual)
def test_create_choose_action_recursive_case_is_maximizing_twice(self): """ In a recursive case where 1 round is played and the all leave nodes are the maximizing players, the minimum of these leave nodes is return. """ state, *_ = self.setup() production_tree = GameStateTree(state, previous_action=None) expected = Action( player_color=production_tree.state.current_player.color, start=Coordinate(0, 0), end=Coordinate(1, 0)) children = [child for child in production_tree.get_children()] for idx, child in enumerate(children): second_players_children = [ second_players_child for second_players_child in child.get_children() ] for jdx, second_players_child in enumerate( second_players_children): if jdx == 1 and idx == 1: score = 0 child.previous_action = expected else: score = 10 modified_player = Player.from_dict({ **second_players_child.state.current_player._asdict(), **{ 'score': score } }) second_players_child.state.current_player = modified_player second_players_child.get_children = MagicMock(return_value=[]) second_players_child.state.check_any_player_can_move = MagicMock( return_value=False) production_tree.get_children = MagicMock(return_value=children) actual = GenericStrategyComponent.choose_action(tree=production_tree, num_turns=2) self.assertEqual(expected, actual)
def data_handle(self, data): # обрабатывает данные с сервера body = data['data'] if data['request'] == PING: model = self.other_players[body['id']]['model'] self.lerp_query[body['id']] = (model.x, model.y, body['x'], body['y'], time.time()) model.x = body['x'] model.y = body['y'] model.update_model(self.player.x, self.player.y) model.model.direction = body['dir'] model.moving = body['moving'] self.other_players[body['id']]['selected'] = self.objects[ body['selected']].generate_item( 1) if body['selected'] != 'None' else None elif data['request'] == JOIN: self.other_players[body['id']] = { 'name': body['name'], 'model': Player(body['x'], body['y'], None, self), 'selected': (self.objects[body['selected']].generate_item(1) if body['selected'] != 'None' else None) } self.other_players[body['id']]['model'].make_model() self.other_players[body['id']]['model'].update_model( self.player.x, self.player.y) elif data['request'] == BLOCK_UPDATE: new = self.chunk_controller.load_block(body['block'], self.objects) self.chunk_controller.place_block(body['x'], body['y'], new, self) elif data['request'] == LEAVE: self.other_players.pop(body['id']) self.lerp_query.pop(body['id']) elif data['request'] == SYNC_BLOCK: self.chunk_controller.get(body['x'], body['y'], self.objects, self).get_sync(body['block'])
def move_penguin(self, color, start_row, start_col, end_row, end_col): """ Move penguin for player with color from start position to end position if possible. :param color: the color of the player who wants to move the penguin :param start_row: the current row of the penguin to be moved :param start_col: the current col of the penguin to be moved :param end_row: the desired row to which the penguin will be moved :param end_col: the desired col to which the penguin will be moved :return: a new FishGameState with the action taken. """ is_valid, err = self.can_move_penguin(color, start_row, start_col, end_row, end_col) if not is_valid: raise ValueError(err) players = [ Player.from_dict({ **self.players[color]._asdict(), **{ "score": (self.players[color].score + self.board.retrieve_num_fish( start_row, start_col)) }, **{ "penguins": [ Coordinate(end_row, end_col) if penguin.row == start_row and penguin.col == start_col else penguin for penguin in self.players[color].penguins ] } }) if player.color == color else player for player in self.players.values() ] board = self.board.create_hole(start_row, start_col) return FishGameState(board=board, num_players=self.num_players, players=players, current_player=self.next_player(), phase=self.phase)
def __init__(self, players: List[PlayerComponent], num_rows: int, num_cols: int): """ A Referee handles the gameplay of a Fish game. A gameplay consists of requesting players for penguin placements, the players' movements, kicking players for invalid moves, and notify players of notable events - kicking, endgame, and winners. A Referee is provided a list of players and the board dimensions from the tournament manager. """ board = FishBoard(num_rows=num_rows, num_cols=num_cols) state_players = [ Player(color=color, age=0, score=0, penguins=[]) for color in PENGUIN_COLORS[:len(players)] ] self.players = { player.color: component for component, player in zip(players, state_players) } self.state = FishGameState(board=board, num_players=len(players), players=state_players, current_player=state_players[0], phase=GameStatePhase.INITIAL)
class Game: def __init__(self): self.light_coff = 1 self.status = IN_GAME self.objects = {} self.crafts = [] self.phys_blocks_group = pygame.sprite.Group() self.all_blocks = pygame.sprite.Group() self.import_objects() self.player = None self.other_players = {} self.lerp_query = {} self.data_stack = deque() self.stack_lock = threading.Lock() self.sun = None self.moon = None self.pos_center = (0, 0) self.gravity_force = 0 self.chunk_controller = None self.light_controller = None self.last_target = None self.interface = None self.running = False self.socket = None self.is_server = False self.connected = False self.data_handler = None self.block_size = 0 self.block_height = 0 self.block_width = 0 self.additional = 0 self.started_time = time.time() def import_objects( self): # сериализация всех json обьектов и загрузка py классов for i in os.listdir(os.path.join(application_path, 'objects')): if os.path.splitext(i)[1] != '.json': continue st = open(os.path.join(application_path, 'objects', i), 'r') object = json.load(st) self.objects[object['name']] = ObjectData( type=object['name'], game=self, path=object['path'], tile_image=TileImage(None, None, None), block_class_path=object['class'], item_class_path=object['items_class'], block_class=custom_import(object['class'] + '.Object'), item_class=custom_import(object['items_class'] + '.Object'), is_phys=object['is_phys'], is_item=object['is_item'], transparent=object['transparent'], hp=object['hp'], lighting=object['lighting'], drop=BlockDrop( *object['drop']) if len(object['drop']) else None) for i in os.listdir(application_path + '/' + 'recipes'): if os.path.splitext(i)[1] != '.json': continue st = open(os.path.join(application_path, 'recipes/', i), 'r') recipe = json.load(st) self.crafts.append((recipe['recipe'], recipe['result'])) def initialise(self, width=BASE_BLOCK_WIDTH, height=BASE_BLOCK_HEIGHT, block_size=BASE_BLOCK_SIZE): # загрузка всего и всякого self.screen = pygame.display.set_mode( (width * block_size, height * block_size), pygame.OPENGL | pygame.DOUBLEBUF) glLoadIdentity() glMatrixMode(GL_PROJECTION) gluOrtho2D(0, width * block_size, height * block_size, 0) glMatrixMode(GL_MODELVIEW) glLoadIdentity() glEnable(GL_TEXTURE_2D) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) self.block_size = block_size self.block_width = width self.block_height = height self.width = self.block_size * self.block_width self.height = self.block_size * self.block_height self.x_center = self.width // 2 self.y_center = self.height // 2 self.block_x_center = self.width // self.block_size self.block_y_center = self.height // self.block_size self.additional = 20 # дополнительное ко-во блоков для счета физики и освещения # считается, что этого достаточно чтобы не делать слишком частую подгрузку и при этом подгрузка не была тяжелой for i in self.objects.values(): if i.tile_image is not None and i.tile_image.gl_image is not None: glDeleteTextures(1, i.tile_image.gl_image) i.tile_image = TileImage(load_image(i.path), self.block_size, self.block_size) self.text_font = pygame.font.Font( os.path.join(application_path, 'lobster.ttf'), min(self.block_width * self.block_size // 25, self.block_height * self.block_size // 25)) self.destroy_images = [ createTexDL( make_gl_image( pygame.transform.scale( load_image("destroy_stage_" + str(i) + ".png"), (self.block_size, self.block_size)))[0], self.block_size, self.block_size) for i in range(10) ] self.sun_tile = TileImage(load_image('sun.png'), self.block_size, self.block_size) self.moon_tile = TileImage(load_image('moon.png'), self.block_size, self.block_size) self.interface = GameInterface(self) def get_view_start(self): # начальные координаты обзора игрока y, x = int(-(self.pos_center[1] - self.player.y) * self.block_size), int( -(self.pos_center[0] - self.player.x) * self.block_size) startx, starty = x // self.block_size + self.additional // 2, y // self.block_size + self.additional // 2 return startx, starty def gen_load(self, data): self.chunk_controller = ChunkController( self.block_width + self.additional, self.block_height + self.additional, data['chunk_controller']['seed']) self.chunk_controller.load(data['chunk_controller'], self.objects) self.started_time = time.time() - data['started_time'] if not self.is_server: inv = data['player']['inventory'] inventory = Inventory(inv['x_size'], inv['y_size'], self.crafts, self) inventory.get_sync(inv, self.objects) inventory.craft_model.craft_make_handle(None, None, None, None) self.player = Player(data['player']['x'], data['player']['y'], inventory, self) self.player.make_model() self.status = IN_GAME self.light_controller = LightController( self.block_width + self.additional, self.block_height + self.additional) self.sun = GlobalRotatingObject(self.sun_tile) self.moon = GlobalRotatingObject(self.moon_tile, coff=1) self.pos_center = (int(self.player.x), int(self.player.y)) self.gravity_force = 0 def load( self, path='save.txt' ): # загрузка всех тех jsonов от всех всех объектов - в будующем это будет сделано попроще... data = json.loads( open(os.path.join(application_path, path), 'r').read()) self.gen_load(data) def server_leave(self): if not self.connected: return self.connected = False self.data_handler = None try: self.send({'request': LEAVE_REQUEST, 'data': {}}) self.socket.close() except Exception: pass finally: self.socket = None self.other_players = {} self.data_stack = deque() self.lerp_query = {} self.status = MENU def send(self, data): prep = bytes(json.dumps(data), encoding='ascii') self.socket.send(len(prep).to_bytes( 16, 'big')) # сначала отправляется длина пакета self.socket.send(prep) # сам пакет def receive(self): ln = int.from_bytes(self.socket.recv(16), 'big') data = json.loads(self.socket.recv(ln)) return data def connect(self, ip, port, name): try: self.new_game() self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.connect((ip, int(port))) self.connected = True self.send({'request': JOIN_REQUEST, 'data': {'name': name}}) response = self.receive() if not response['success']: # запрос наверное может быть отклонен self.server_leave() self.interface.server_message.generate_text( response['reason'], self.text_font, self.width // 20) self.interface.status = CONNECT_ERROR return self.chunk_controller = ChunkController( self.block_width + self.additional, self.block_height + self.additional, response['level']['chunk_controller']['seed']) self.chunk_controller.load( response['level']['chunk_controller'], self.objects) # загрузка соответствующего мира self.player.x = response['player_data']['x'] self.player.y = response['player_data']['y'] self.player.inventory.get_sync( response['player_data']['inventory'], self.objects) self.started_time = time.time() - response['started_time'] for i in response['players']: self.other_players[i['id']] = { 'name': i['name'], 'model': Player(i['x'], i['y'], None, self), 'selected': self.objects[i['selected']].generate_item(1) if i['selected'] != 'None' else None } self.other_players[i['id']]['model'].make_model() self.other_players[i['id']]['model'].update_model( self.player.x, self.player.y) self.data_handler = threading.Thread(target=self.data_receive) self.data_handler.start() self.start() except socket.error as e: self.server_leave() self.interface.server_message.generate_text( str(e), self.text_font, self.width // 20) self.interface.status = CONNECT_ERROR def new_game(self): inventory = Inventory(9, 3, self.crafts, self) try: # стартовый набор inventory.add_item(self.objects["torch"].generate_item(20)) inventory.add_item(self.objects["dirt"].generate_item(120)) except Exception: pass self.gravity_force = 0 self.chunk_controller = ChunkController( self.block_width + self.additional, self.block_height + self.additional, random.randint(0, 100000)) self.light_controller = LightController( self.block_width + self.additional, self.block_height + self.additional) self.sun = GlobalRotatingObject(self.sun_tile) self.moon = GlobalRotatingObject(self.moon_tile, coff=1) val = self.chunk_controller.get_player_start_pos( 100000 ) # на 0 координате "стык" - мир на отрицательных координатах является отражением мира на положительных self.player = Player(100000, val - 2, inventory, self) self.player.make_model() self.pos_center = (int(self.player.x), int(self.player.y)) self.started_time = time.time() def start(self): self.status = IN_GAME self.redraw_all() def gen_save( self ): # там много вызовов save у разных обьектов и все это в один json схлопывается data = {} data['chunk_controller'] = self.chunk_controller.save() if self.player is not None: data['player'] = self.player.save() data['started_time'] = time.time() - self.started_time return data def save(self, path='save.txt'): out = open(os.path.join(application_path, path), 'w') out.write(json.dumps(self.gen_save())) out.close() def update_changes(self, was, block): # обновляет группы if was.is_phys: self.phys_blocks_group.remove(was) if block.is_phys: self.phys_blocks_group.add(block) self.all_blocks.remove(was) self.all_blocks.add(block) if block.worldpos[0] not in self.chunk_controller.changes: self.chunk_controller.changes[block.worldpos[0]] = {} self.chunk_controller.changes[block.worldpos[0]][ block.worldpos[1]] = block def place_block(self, x, y, player): block = self.chunk_controller.chunk[y][x] if block.type != 'empty' or player.inventory.get_selected( ) is None: # если уже занято return if player.inventory.get_selected().is_item: # если не блок return if pygame.sprite.collide_rect( block, player.model.get_current_tile()): # если в персонаже return new = self.objects[ player.inventory.get_selected().type].generate_block( x, y, block.worldpos) self.update_changes(block, new) self.chunk_controller.chunk[y][x] = new self.chunk_controller.chunk[y][x].on_place(self) player.inventory.place_selected() if not new.transparent: self.light_controller.max_y[x] = min( self.light_controller.max_y[x], new.worldpos[1]) elif not block.transparent: self.light_controller.calc_max_y_for_x(new.x, self.pos_center, self.chunk_controller, self.objects) self.light_controller.calculate_light( self.chunk_controller.chunk, self.chunk_controller.ground_height) if self.connected: self.send({ 'request': PLACE_BLOCK, 'data': { 'x': new.worldpos[0], 'y': new.worldpos[1] } }) def draw_single_light(self, block): # рисует свет на блоке if not block.transparent: color = (0, 0, 0, 1 - self.light_controller.lighting[block.y][block.x] * self.light_coff) else: # фон остается немного видимым (упрощает навигацию в шахте) color = (0, 0, 0, min( 0.95, 1 - self.light_controller.lighting[block.y][block.x] * self.light_coff)) glColor4f(*color) glRectf(0, 0, block.rect.width, block.rect.height) glColor4f(1, 1, 1, 1) def draw_all_light(self): glBindTexture(GL_TEXTURE_2D, 0) startx, starty = GAME.get_view_start( ) # определяет область от общей области, которая видна игроку for g in range(startx - 1, startx + self.block_width + 1): for k in range(starty - 1, starty + self.block_height + 1): i = GAME.chunk_controller.chunk[k][g] glLoadIdentity() glTranslate(i.rect.x, i.rect.y, 0) self.draw_single_light(i) def destroy_block(self, x, y, player): block = self.chunk_controller.chunk[y][x] block.on_destroy(self) new = self.objects['empty'].generate_block(x, y, block.worldpos) self.update_changes(block, new) self.last_target = None if block.drop is not None: drop = block.drop.get_drop() player.inventory.add_item(self.objects[drop[0]].generate_item( drop[1])) self.chunk_controller.chunk[y][x] = new self.light_controller.calc_max_y_for_x(new.x, self.pos_center, self.chunk_controller, self.objects) self.light_controller.calculate_light( self.chunk_controller.chunk, self.chunk_controller.ground_height) if self.connected: self.send({ 'request': DESTROY_BLOCK, 'data': { 'x': new.worldpos[0], 'y': new.worldpos[1] } }) def damage_block(self, x, y, dmg, player): block = self.chunk_controller.chunk[y][x] if block.type == 'empty': return if self.last_target is not None and self.last_target[ 0] is not None and self.last_target[1] != block.worldpos: self.last_target[0].hp = self.objects[self.last_target[0].type].hp if block.damage(dmg): # если сломал self.destroy_block(x, y, player) else: self.last_target = (block, block.worldpos, block.hp) def exit(self): self.server_leave() self.running = False def redraw_all(self): # обновляет ВСЕ self.all_blocks.remove(self.all_blocks) self.phys_blocks_group.remove(self.phys_blocks_group) self.chunk_controller.update_chunk(self.pos_center[0], self.pos_center[1], self.objects) for i in self.chunk_controller.chunk: for g in i: self.all_blocks.add(g) if not g.is_phys: continue self.phys_blocks_group.add(g) self.all_blocks.update(self.block_width, GAME.block_height, GAME.pos_center, GAME.block_size, GAME.player.x, self.player.y) self.light_controller.calc_max_y(self.pos_center, self.chunk_controller, self.objects) self.light_controller.calculate_light( self.chunk_controller.chunk, self.chunk_controller.ground_height) def draw_blocks_in_screen(self, draw_order): startx, starty = GAME.get_view_start( ) # определяет область от общей области, которая видна игроку for g in range(startx - 1, startx + self.block_width + 1): for k in range(starty - 1, starty + self.block_height + 1): i = GAME.chunk_controller.chunk[k][g] if draw_order == 1: if not (i.type == 'empty' or i.transparent): continue elif draw_order == 2: if i.type == 'empty': continue i.update(self.block_width, self.block_height, self.pos_center, self.block_size, self.player.x, self.player.y) glLoadIdentity() glTranslate(i.rect.x, i.rect.y, 0) if i.transparent and draw_order != 2: glCallList(self.objects['empty'].tile_image.gl_image) if not (i.transparent and draw_order == 1): i.draw() #self.draw_single_light(i) if i.type != 'empty' and i.hp != self.objects[i.type].hp: glCallList(self.destroy_images[int( min(9, max(0, (1 - i.hp / self.objects[i.type].hp)) / 0.1))]) def update_mouse(self, pos, events, fps): y, x = int(pos[1] - (self.pos_center[1] - self.player.y) * self.block_size), int(pos[0] - (self.pos_center[0] - self.player.x) * self.block_size) x, y = x // self.block_size + self.additional // 2, y // self.block_size + self.additional // 2 if int( math.sqrt( # проверка расстояние от персонажа abs(x - (self.block_width + self.additional) // 2 + int(self.pos_center[0] - self.player.x))**2 + abs(y - (self.block_height + self.additional) // 2 + int(self.pos_center[1] - self.player.y) - 1)**2)) > 4: return block = GAME.chunk_controller.chunk[y][x] glBindTexture(GL_TEXTURE_2D, 0) glLoadIdentity() glTranslate(block.rect.x, block.rect.y, 0) glBegin(GL_LINE_LOOP) glVertex2f(0, 0) glVertex2f(block.rect.width, 0) glVertex2f(block.rect.width, block.rect.height) glVertex2f(0, block.rect.height) glEnd() if events[0]: self.damage_block(x, y, self.player.mine_speed / fps, self.player) elif events[2]: block.on_use(self) if self.player.inventory.get_selected() is not None: self.place_block(x, y, self.player) def update_inputs(self, fps): all_keys = pygame.key.get_pressed() # проверка ввода key_inp = all_keys[273:277] # стрелки alt_key_inp = [ all_keys[119], all_keys[115], all_keys[100], all_keys[97] ] # wasd move_vec = (0, 0) if self.status == IN_GAME: for i in range(4): move_vec = (move_vec[0] + dirs[i][0] * self.player.speed * max(alt_key_inp[i], key_inp[i]), move_vec[1] + dirs[i][1] * max(alt_key_inp[i], key_inp[i])) bf = self.player.model.get_current_tile() if check_square_collision( bf.rect.x + 3, bf.rect.y + bf.rect.height, bf.rect.x + bf.rect.width - 3, bf.rect.y + bf.rect.height + 1, self.phys_blocks_group): # прыжки если персонаж стоит if move_vec[1] < 0 <= self.gravity_force: self.gravity_force = -(GRAV_CONST * 12.5) else: self.gravity_force = 0 else: self.gravity_force += GRAV_CONST # ускорение if move_vec[0] == 0 and move_vec[1] > 0: self.player.model.direction = 0 self.player.move(move_vec[0] / fps, self.gravity_force, 300 / fps) if move_vec[0] != 0: self.player.moving = True else: self.player.moving = False self.light_coff = self.sun.update_pos(self.started_time, self.x_center, self.y_center, self.width, self.height) self.moon.update_pos(self.started_time, self.x_center, self.y_center, self.width, self.height) def redraw(self): xoffset, yoffset = self.player.x - self.pos_center[ 0], self.player.y - self.pos_center[1] if abs(xoffset) >= self.additional // 2 or abs( yoffset) >= self.additional // 2: # подгрузка self.pos_center = (int(self.player.x), int(self.player.y)) self.redraw_all() self.draw_blocks_in_screen(1) # чтобы солнце под землей не рисовалось self.sun.draw() self.moon.draw() self.draw_blocks_in_screen(2) for i in self.other_players.values(): if i['model'].moving: i['model'].simulate_move(i['model'].model.direction, 300 / fps) else: i['model'].simulate_move(0, 300 / fps) i['model'].update_model(self.player.x, self.player.y) i['model'].model.draw(i['selected'], self.block_size) glLoadIdentity() bf = i['model'].model.get_current_tile().rect glTranslate(bf.x + bf.width // 2, bf.y - self.block_size // 2, 0) draw_text(i['name'], self.text_font, color=(225, 225, 225, 125)) self.player.draw() self.draw_all_light() def data_receive( self ): # получает данные с сервера в отдельном потоке (чтобы основной не ложить) try: while True: data = self.receive() if not data or not self.connected: return self.stack_lock.acquire() try: self.data_stack.append(data) finally: self.stack_lock.release() except socket.error as e: self.interface.server_message.generate_text( str(e), self.text_font, self.width // 20) self.server_leave() self.interface.status = CONNECT_ERROR return except Exception: pass # жсоны выбивает если сокет умирает def data_handle(self, data): # обрабатывает данные с сервера body = data['data'] if data['request'] == PING: model = self.other_players[body['id']]['model'] self.lerp_query[body['id']] = (model.x, model.y, body['x'], body['y'], time.time()) model.x = body['x'] model.y = body['y'] model.update_model(self.player.x, self.player.y) model.model.direction = body['dir'] model.moving = body['moving'] self.other_players[body['id']]['selected'] = self.objects[ body['selected']].generate_item( 1) if body['selected'] != 'None' else None elif data['request'] == JOIN: self.other_players[body['id']] = { 'name': body['name'], 'model': Player(body['x'], body['y'], None, self), 'selected': (self.objects[body['selected']].generate_item(1) if body['selected'] != 'None' else None) } self.other_players[body['id']]['model'].make_model() self.other_players[body['id']]['model'].update_model( self.player.x, self.player.y) elif data['request'] == BLOCK_UPDATE: new = self.chunk_controller.load_block(body['block'], self.objects) self.chunk_controller.place_block(body['x'], body['y'], new, self) elif data['request'] == LEAVE: self.other_players.pop(body['id']) self.lerp_query.pop(body['id']) elif data['request'] == SYNC_BLOCK: self.chunk_controller.get(body['x'], body['y'], self.objects, self).get_sync(body['block']) def run(self): pygame.init() pygame.font.init() clock = pygame.time.Clock() self.running = True self.status = MENU self.initialise() self.started_time = time.time() last_send = time.time() while self.running: try: if self.connected: if time.time() - last_send > PING_TIME: # пинг на сервер self.send({ 'request': PING, 'data': { 'x': self.player.x, 'y': self.player.y, 'dir': self.player.model.direction, 'moving': self.player.moving, 'selected': self.player.inventory.selected[0] } }) last_send = time.time() self.stack_lock.acquire() try: # обрабатываю полученные данные с сервера while len(self.data_stack): data = self.data_stack.popleft() self.data_handle(data) finally: self.stack_lock.release() for i in self.lerp_query.items( ): # интерполирую координаты других игроков delta = time.time() - i[1][-1] x1, y1 = i[1][0], i[1][1] x2, y2 = i[1][2], i[1][3] #self.other_players[i[0]]['model'].x = x1 + (1 - math.cos(4 * delta * math.pi)) * (x2 - x1) #self.other_players[i[0]]['model'].y = y1 + (1 - math.cos(4 * delta * math.pi)) * (y2 - y1) self.other_players[ i[0]]['model'].x = x1 + 10 * delta * (x2 - x1) self.other_players[ i[0]]['model'].y = y1 + 10 * delta * (y2 - y1) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) for event in pygame.event.get(): if event.type == pygame.QUIT: self.exit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE and self.status in [ IN_GAME, PAUSE ]: self.status = PAUSE if self.status == IN_GAME else IN_GAME elif event.key == pygame.K_e and self.status not in [ MENU, PAUSE, SETTINGS, CONNECT ]: self.status = INVENTORY if self.status == IN_GAME else IN_GAME if event.type == pygame.MOUSEBUTTONDOWN and GAME.status == IN_GAME: self.player.inventory.wheel_event( -1 if event.button == 4 else ( 1 if event.button == 5 else 0)) self.interface.handle_event(event) if self.status not in [MENU, SETTINGS, CONNECT]: self.update_inputs(fps) if self.status != MENU: self.redraw() self.player.inventory.draw_bar_only() if self.status == IN_GAME: self.update_mouse(pygame.mouse.get_pos(), pygame.mouse.get_pressed(), fps) if self.status != IN_GAME: glLoadIdentity() glBindTexture(GL_TEXTURE_2D, 0) glColor4f(0.75, 0.75, 0.75, 0.75) glRectf(0, 0, self.width, self.height) glColor4f(1, 1, 1, 1) if self.status == INVENTORY: self.player.inventory.draw() elif self.status == PAUSE: self.interface.draw() elif self.status == MENU: self.interface.draw() for i in self.all_blocks: i.on_tick(self) pygame.display.flip() clock.tick(fps) except socket.error as e: self.interface.server_message.generate_text( str(e), self.text_font, self.width // 20) self.server_leave() self.interface.status = CONNECT_ERROR except Exception as e: print(e) self.server_leave() self.running = False pygame.quit()
class TestGameState(unittest.TestCase): currentResult = None # holds last result object passed to run method player_1 = Player("red", 0, 0, []) player_2 = Player("white", 0, 0, []) player_3 = Player("brown", 0, 0, []) player_list = [player_1, player_2, player_3] @classmethod def setResult(cls, amount, errors, failures, skipped): cls.amount, cls.errors, cls.failures, cls.skipped = \ amount, errors, failures, skipped def tearDown(self): amount = self.currentResult.testsRun errors = self.currentResult.errors failures = self.currentResult.failures skipped = self.currentResult.skipped self.setResult(amount, errors, failures, skipped) @classmethod def tearDownClass(cls): print("\ntests run: " + str(cls.amount)) print("errors: " + str(len(cls.errors))) print("failures: " + str(len(cls.failures))) print("success: " + str(cls.amount - len(cls.errors) - len(cls.failures))) print("skipped: " + str(len(cls.skipped))) def run(self, result=None): self.currentResult = result # remember result for use in tearDown unittest.TestCase.run(self, result) # call superclass run method def test_create_game_state(self): board = FishBoard(4, 3) game_state = FishGameState(board, 3, TestGameState.player_list, current_player=TestGameState.player_list[0], phase=GameStatePhase.INITIAL) expected_players = { "red": Player(color="red", age=0, score=0, penguins=[]), "white": Player(color="white", age=0, score=0, penguins=[]), "brown": Player(color="brown", age=0, score=0, penguins=[]) } self.assertEqual(game_state.players, expected_players) self.assertEqual(game_state.board, board) self.assertEqual(game_state.num_players, 3) self.assertEqual(game_state.num_penguins, 3) def test_validate_input_true(self): board = FishBoard(4, 3) game_state = FishGameState(board, 3, TestGameState.player_list, current_player=TestGameState.player_list[0], phase=GameStatePhase.FINAL) is_valid, error_msg = game_state.validate_input("red", 0, 0) self.assertTrue(is_valid) self.assertEqual(error_msg, "") def test_validate_input_invalid_player_id(self): board = FishBoard(4, 3) game_state = FishGameState(board, 3, TestGameState.player_list, current_player=TestGameState.player_list[0], phase=GameStatePhase.FINAL) is_valid, error_msg = game_state.validate_input(-1, 0, 0) self.assertFalse(is_valid) self.assertEqual(error_msg, "Player color does not exist") def test_validate_input_invalid_player_id_upperbound(self): board = FishBoard(4, 3) game_state = FishGameState(board, 3, TestGameState.player_list, current_player=TestGameState.player_list[0], phase=GameStatePhase.FINAL) is_valid, error_msg = game_state.validate_input("", 0, 0) self.assertFalse(is_valid) self.assertEqual(error_msg, "Player color does not exist") def test_validate_input_invalid_row_and_col_out_of_bounds(self): board = FishBoard(4, 3) game_state = FishGameState(board, 3, TestGameState.player_list, current_player=TestGameState.player_list[0], phase=GameStatePhase.FINAL) is_valid, error_msg = game_state.validate_input("red", 6, 6) self.assertFalse(is_valid) self.assertEqual(error_msg, "Row and column do not exist in this board") def test_validate_input_invalid_row_and_col_has_hole(self): board = FishBoard(4, 3) board = board.create_hole(1, 1) game_state = FishGameState(board, 3, TestGameState.player_list, current_player=TestGameState.player_list[0], phase=GameStatePhase.FINAL) is_valid, error_msg = game_state.validate_input("red", 1, 1) self.assertFalse(is_valid) self.assertEqual(error_msg, "Row and column is a hole") def test_validate_input_invalid_row_and_col_has_penguin(self): board = FishBoard(4, 3) factory = FishGameState(board=board, num_players=2, players=TestGameState.player_list, phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) factory = factory.add_penguin(2, 2, "red") is_valid, error_msg = factory.validate_input("white", 2, 2) self.assertFalse(is_valid) self.assertEqual(error_msg, "This tile already has a penguin") def test_add_penguin_success(self): board = FishBoard(4, 3) factory = FishGameState(board=board, num_players=2, players=TestGameState.player_list, phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) factory = factory.add_penguin(0, 0, "red") expected_penguins = [Coordinate(0, 0)] self.assertEqual(factory.players["red"].penguins, expected_penguins) def test_add_penguin_failure(self): board = FishBoard(4, 3) factory = FishGameState(board=board, num_players=2, players=TestGameState.player_list, phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) factory = factory.add_penguin(0, 0, "red") factory = factory.add_penguin(0, 1, "white") with self.assertRaises(ValueError): factory = factory.add_penguin(0, 2, "white") def test_move_penguin(self): board = FishBoard(4, 3) factory = FishGameState(board=board, num_players=3, players=TestGameState.player_list, phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) factory = factory.add_penguin(row=0, col=1, color="red") factory = factory.add_penguin(row=0, col=0, color="white") factory = factory.add_penguin(row=3, col=0, color="brown") game_state = factory.finalize() game_state = game_state.move_penguin("red", 0, 1, 2, 1) expected_penguins = [Coordinate(row=2, col=1)] self.assertEqual(game_state.players["red"].penguins, expected_penguins) def test_move_penguin_failure(self): board = FishBoard(4, 3) factory = FishGameState(board=board, num_players=3, players=TestGameState.player_list, phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) factory = factory.add_penguin(row=0, col=0, color="red") factory = factory.add_penguin(row=0, col=1, color="white") factory = factory.add_penguin(row=0, col=2, color="brown") game_state = factory.finalize() with self.assertRaises(ValueError): game_state.move_penguin("red", 0, 0, 0, 2) def test_has_penguin(self): board = FishBoard(4, 3) factory = FishGameState(board=board, num_players=3, players=TestGameState.player_list, phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) factory = factory.add_penguin(0, 0, "red") self.assertTrue(factory.has_penguin(0, 0)) self.assertFalse(factory.has_penguin(1, 1)) def test_any_player_can_move_success(self): board = FishBoard(4, 3) factory = FishGameState(board=board, num_players=3, players=TestGameState.player_list, phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) factory = factory.add_penguin(0, 0, "red") factory = factory.add_penguin(1, 1, "white") game_state = factory.finalize() self.assertTrue(game_state.check_any_player_can_move()) def test_any_player_can_move_failure(self): board = FishBoard(4, 3) board = board.create_hole(2, 0) board = board.create_hole(3, 0) board = board.create_hole(2, 1) board = board.create_hole(0, 1) factory = FishGameState(board=board, num_players=3, players=TestGameState.player_list, phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) factory = factory.add_penguin(0, 0, "red") factory = factory.add_penguin(1, 0, "white") game_state = factory.finalize() self.assertFalse(game_state.check_any_player_can_move()) def test_get_player_id_success(self): board = FishBoard(4, 3) board = board.create_hole(2, 0) board = board.create_hole(3, 0) board = board.create_hole(2, 1) board = board.create_hole(0, 1) factory = FishGameState(board=board, num_players=3, players=TestGameState.player_list, phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) factory = factory.add_penguin(0, 0, "red") factory = factory.add_penguin(1, 0, "white") game_state = factory.finalize() self.assertEqual(game_state.get_player_color(0, 0), "red") self.assertEqual(game_state.get_player_color(3, 2), "") def test_is_equal(self): board1 = FishBoard(4, 3) board1 = board1.create_hole(2, 0) board1 = board1.create_hole(3, 0) board1 = board1.create_hole(2, 1) board1 = board1.create_hole(0, 1) board2 = FishBoard(4, 3) board2 = board2.create_hole(2, 0) board2 = board2.create_hole(3, 0) board2 = board2.create_hole(2, 1) board2 = board2.create_hole(0, 1) factory1 = FishGameState(board=board1, num_players=3, players=TestGameState.player_list, phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) factory1 = factory1.add_penguin(0, 0, "red") factory1 = factory1.add_penguin(1, 0, "white") factory2 = FishGameState(board=board2, num_players=3, players=TestGameState.player_list, phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) factory2 = factory2.add_penguin(0, 0, "red") factory2 = factory2.add_penguin(1, 0, "white") state1 = factory1.finalize() state2 = factory2.finalize() self.assertTrue(state1.is_equal(state2)) def test_is_equal_false_players_only(self): board1 = FishBoard(4, 3) board2 = FishBoard(4, 3) state1 = FishGameState(board=board1, num_players=3, players=TestGameState.player_list[0:2], phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) state2 = FishGameState(board=board2, num_players=3, players=TestGameState.player_list, phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) self.assertFalse(state1.is_equal(state2)) def test_is_equal_false_board_only(self): board1 = FishBoard(5, 3) board2 = FishBoard(4, 3) state1 = FishGameState(board=board1, num_players=3, players=TestGameState.player_list[0:2], phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) state2 = FishGameState(board=board2, num_players=3, players=TestGameState.player_list, phase=GameStatePhase.INITIAL, current_player=TestGameState.player_list[0]) self.assertFalse(state1.is_equal(state2))