def test_init(self): unit = Unit('type', UnitInfo(10, 11, 12, Coord(1, 2, -3))) self.assertEqual(unit.unit_type, 'type') self.assertEqual(unit.unit_info.unit_id, 10) self.assertEqual(unit.unit_info.user_id, 11) self.assertEqual(unit.unit_info.game_id, 12) self.assertEqual(unit.unit_info.coord, Coord(1, 2, -3))
def test_add(self): unitmgr = UnitMgr(None, Menu(None)) unitmgr.add(Unit('type1', UnitInfo(1, 1, 1, Coord()))) unitmgr.add(Unit('type2', UnitInfo(2, 1, 1, Coord()))) self.assertEqual(len(unitmgr), 2) self.assertTrue(1 in unitmgr) self.assertTrue(2 in unitmgr)
def test_read_write_nonorigin(self): outstream = io.BytesIO() Coord(1000000, 2000000, -3000000).write(outstream) instream = io.BytesIO(outstream.getvalue()) coord = Coord.read(instream) self.assertEqual(coord, Coord(1000000, 2000000, -3000000))
def test_eq(self): # Only the unit id is checked a = Unit('type1', UnitInfo(1, 2, 3, Coord(1, 2, -3))) b = Unit('type1', UnitInfo(2, 2, 3, Coord(1, 2, -3))) self.assertTrue(a == a) self.assertFalse(a == b) self.assertFalse(b == a) self.assertTrue(b == b)
def test_request_write_read(self): outstream = io.BytesIO() UnitRequest(Coord(1, 2, -3), 10).write(outstream) instream = io.BytesIO(outstream.getvalue()) request = UnitRequest.read(instream) self.assertEqual(request.coord, Coord(1, 2, -3)) self.assertEqual(request.distance, 10)
def test_get_by_id(self): unitmgr = UnitMgr(None, Menu(None)) unitmgr.add(Unit('type1', UnitInfo(1, 1, 1, Coord()))) unitmgr.add(Unit('type2', UnitInfo(2, 1, 1, Coord()))) missing = unitmgr.get_by_id('missing') self.assertIsNone(missing) unit1 = unitmgr.get_by_id(1) self.assertIsNotNone(unit1) self.assertEqual(unit1.unit_type, 'type1') self.assertEqual(unit1.unit_info.unit_id, 1)
def test_eq(self): a = Coord(2, -3, 1) b = Coord(2, -2, 0) c = Coord(-2, 2, 0) self.assertTrue(a == a) self.assertFalse(a == b) self.assertFalse(a == c) self.assertFalse(b == a) self.assertTrue(b == b) self.assertFalse(b == c) self.assertFalse(c == a) self.assertFalse(c == b) self.assertTrue(c == c)
def test_get_neighbor_from_non_origin(self): coord = Coord(2, -3, 1) neighbors = [coord.get_neighbor(i) for i in range(6)] self.assertEqual(len(neighbors), 6) self.assertEqual(neighbors[0], Coord(3, -4, 1)) self.assertEqual(neighbors[1], Coord(3, -3, 0)) self.assertEqual(neighbors[2], Coord(2, -2, 0)) self.assertEqual(neighbors[3], Coord(1, -2, 1)) self.assertEqual(neighbors[4], Coord(1, -3, 2)) self.assertEqual(neighbors[5], Coord(2, -4, 2))
def test_get_neighbor_from_origin(self): coord = Coord() neighbors = [coord.get_neighbor(i) for i in range(6)] self.assertEqual(len(neighbors), 6) self.assertEqual(neighbors[0], Coord(1, -1, 0)) self.assertEqual(neighbors[1], Coord(1, 0, -1)) self.assertEqual(neighbors[2], Coord(0, 1, -1)) self.assertEqual(neighbors[3], Coord(-1, 1, 0)) self.assertEqual(neighbors[4], Coord(-1, 0, 1)) self.assertEqual(neighbors[5], Coord(0, -1, 1))
def test_response_nonempty_write_read(self): outstream = io.BytesIO() a = Sun(UnitInfo(10, 1, 1, Coord(1, 2, -3))) b = Planet(UnitInfo(11, 1, 1, Coord(1, -2, 1))) c = ReconDrone(UnitInfo(12, 1, 1, Coord(2, -3, 1))) UnitResponse([a, b, c]).write(outstream) instream = io.BytesIO(outstream.getvalue()) response = UnitResponse.read(instream) self.assertIsNotNone(response.units) self.assertEqual(len(response.units), 3) self.assertEqual(response.units[0], a) self.assertEqual(response.units[1], b) self.assertEqual(response.units[2], c)
def test_write_read(self): a = Sun(UnitInfo(1, 1, 1, Coord(1, 2, -3))) b = Planet(UnitInfo(2, 2, 2, Coord(1, 2, -3))) c = ReconDrone(UnitInfo(3, 3, 3, Coord(1, 2, -3))) for unit in [a, b, c]: outstream = io.BytesIO() UnitFactory.write(outstream, unit) instream = io.BytesIO(outstream.getvalue()) read = UnitFactory.read(instream) self.assertEqual(read.unit_type, unit.unit_type) self.assertEqual(read.unit_info.unit_id, unit.unit_info.unit_id) self.assertEqual(read.unit_info.user_id, unit.unit_info.user_id) self.assertEqual(read.unit_info.game_id, unit.unit_info.game_id) self.assertEqual(read.unit_info.coord, unit.unit_info.coord)
def test_get_selected_default(self): unitmgr = UnitMgr(None, Menu(None)) a = Unit('type1', UnitInfo(1, 1, 1, Coord())) b = Unit('type2', UnitInfo(2, 1, 1, Coord())) unitmgr.add(a) unitmgr.add(b) empty = unitmgr.get_selected() self.assertIsNotNone(empty) self.assertEqual(len(empty), 0) a.selected = True sel = unitmgr.get_selected() self.assertIsNotNone(sel) self.assertEqual(len(sel), 1) self.assertEqual(sel[0].unit_info.unit_id, a.unit_info.unit_id)
def test_remove_by_id(self): unitmgr = UnitMgr(None, Menu(None)) unitmgr.add(Unit('type1', UnitInfo(1, 1, 1, Coord()))) unitmgr.add(Unit('type2', UnitInfo(2, 1, 1, Coord()))) self.assertEqual(len(unitmgr), 2) self.assertTrue(1 in unitmgr) self.assertTrue(2 in unitmgr) unitmgr.remove_by_id(3) self.assertEqual(len(unitmgr), 2) self.assertTrue(1 in unitmgr) self.assertTrue(2 in unitmgr) unitmgr.remove_by_id(1) self.assertEqual(len(unitmgr), 1) self.assertFalse(1 in unitmgr) self.assertTrue(2 in unitmgr)
def __init__(self, surface): self.surface = surface self.hex_radius = HEX_RADIUS_DEFAULT self.hex_height = None self.hex_vertical_increase = None self.hex_width = None self.hex_width_half = None self.__set_size_vars() self.__coord_offset_x = 0 self.__coord_offset_y = 0 self.__center_coord = Coord() self.__hover_coord = Coord() self.__last_mouse_position = None self.__mouse_down_position = None self.__mouse_button = None
def test_get_selected_false(self): unitmgr = UnitMgr(None, Menu(None)) a = Unit('type1', UnitInfo(1, 1, 1, Coord())) b = Unit('type2', UnitInfo(2, 1, 1, Coord())) unitmgr.add(a) unitmgr.add(b) both = unitmgr.get_selected(False) self.assertIsNotNone(both) self.assertEqual(len(both), 2) self.assertEqual(both[0].unit_info.unit_id, a.unit_info.unit_id) self.assertEqual(both[1].unit_info.unit_id, b.unit_info.unit_id) a.selected = True unsel = unitmgr.get_selected(False) self.assertIsNotNone(unsel) self.assertEqual(len(unsel), 1) self.assertEqual(unsel[0].unit_info.unit_id, b.unit_info.unit_id)
def test_get_neighbors_from_origin(self): neighbors = Coord().get_neighbors() self.assertEqual(len(neighbors), 6) self.assertEqual(neighbors[0], Coord(1, -1, 0)) self.assertEqual(neighbors[1], Coord(1, 0, -1)) self.assertEqual(neighbors[2], Coord(0, 1, -1)) self.assertEqual(neighbors[3], Coord(-1, 1, 0)) self.assertEqual(neighbors[4], Coord(-1, 0, 1)) self.assertEqual(neighbors[5], Coord(0, -1, 1))
def test_get_neighbors_from_non_origin(self): neighbors = Coord(2, -3, 1).get_neighbors() self.assertEqual(len(neighbors), 6) self.assertEqual(neighbors[0], Coord(3, -4, 1)) self.assertEqual(neighbors[1], Coord(3, -3, 0)) self.assertEqual(neighbors[2], Coord(2, -2, 0)) self.assertEqual(neighbors[3], Coord(1, -2, 1)) self.assertEqual(neighbors[4], Coord(1, -3, 2)) self.assertEqual(neighbors[5], Coord(2, -4, 2))
def test_get_by_type(self): unitmgr = UnitMgr(None, Menu(None)) unitmgr.add(Unit('type1', UnitInfo(1, 1, 1, Coord()))) unitmgr.add(Unit('type2', UnitInfo(2, 1, 1, Coord()))) unitmgr.add(Unit('type1', UnitInfo(3, 1, 1, Coord()))) empty = unitmgr.get_by_type('missing') self.assertIsNotNone(empty) self.assertEqual(len(empty), 0) type1 = unitmgr.get_by_type('type1') self.assertIsNotNone(type1) self.assertEqual(len(type1), 2) self.assertEqual(1, type1[0].unit_info.unit_id) self.assertEqual(3, type1[1].unit_info.unit_id) type2 = unitmgr.get_by_type('type2') self.assertIsNotNone(type2) self.assertEqual(len(type2), 1) self.assertEqual(2, type2[0].unit_info.unit_id)
def read(iostream): version = int.from_bytes(iostream.read(1), byteorder=BYTE_ORDER, signed=False) if version == 1: coord = Coord.read(iostream) distance = int.from_bytes(iostream.read(4), byteorder=BYTE_ORDER, signed=False) return UnitRequest(coord, distance) else: raise Exception(f'Unsupported version number: {version}')
def get_coord(self, position): position_x, position_y = position # Update the given position based on the coordinate in the middle of the screen. mouse_x = position_x - self.surface.get_width( ) / 2 - self.__coord_offset_x mouse_y = position_y - self.surface.get_height( ) / 2 - self.__coord_offset_y # Determine the coordinate associated with the current mouse position. coord_x = (mouse_x * HexGrid.precomputed_sqrt3 / 3 - mouse_y / 3) / self.hex_radius coord_z = mouse_y * 2 / 3 / self.hex_radius return Coord(coord_x, -coord_x - coord_z, coord_z).add_coord(self.__center_coord)
def run(self): self.log.info('Client running') self.running = True self.serverio.listen() # TODO: When do these happen? self.serverio.send(LoginRequest('user', 'pass')) self.serverio.send(UnitRequest(Coord(), 100)) while self.running: self.clock.tick(FPS_TARGET) self.handle_events() self.draw() self.serverio.stop() pygame.quit()
def read(iostream): version = int.from_bytes(iostream.read(1), byteorder=BYTE_ORDER, signed=False) if version == 1: unit_id = int.from_bytes(iostream.read(4), byteorder=BYTE_ORDER, signed=False) user_id = int.from_bytes(iostream.read(4), byteorder=BYTE_ORDER, signed=False) game_id = int.from_bytes(iostream.read(4), byteorder=BYTE_ORDER, signed=False) coord = Coord.read(iostream) return UnitInfo(unit_id, user_id, game_id, coord) else: raise Exception(f'Unsupported serialization version: {version}')
def get_for_game(self, game_id): log = logging.getLogger(__name__) log.info(f'Fetching units for game: {game_id}') units = [] with self.engine.connect() as conn: select = sqlalchemy.select( [DbUnits.TABLE]).where(DbUnits.TABLE.c.game_id == game_id) result = conn.execute(select) for row in result: coord = Coord(row['coord_x'], -row['coord_x'] - row['coord_z'], row['coord_z']) unit_info = UnitInfo(row['id'], row['user_id'], row['game_id'], coord) unit = UnitFactory.get_unit(row['type']) units.append(unit(unit_info)) result.close() return units
def test_add_float_to_origin_invalid(self): # The z value is calculated based on rounded x and y coord = Coord().add(1.1, 1.9, -3.0) self.assertEqual(coord.x, 1) self.assertEqual(coord.y, 2) self.assertEqual(coord.z, -3)
def test_add_int_to_non_origin_invalid(): Coord(1, 1, -2).add(1, 2, -4)
def test_init_defaults(self): coord = Coord() self.assertEqual(coord.x, 0) self.assertEqual(coord.y, 0) self.assertEqual(coord.z, 0)
def test_add_float_to_origin(self): coord = Coord().add(1.1, 2.9, -4.0) self.assertEqual(coord.x, 1) self.assertEqual(coord.y, 3) self.assertEqual(coord.z, -4)
def test_add_float_to_non_origin(self): coord = Coord(1.1, 2.9, -4.0).add(1.1, 2.9, -4.0) self.assertEqual(coord.x, 2) self.assertEqual(coord.y, 6) self.assertEqual(coord.z, -8)
class HexGrid: precomputed_sqrt3 = math.sqrt(3) precomputed_sqrt3div2 = precomputed_sqrt3 / 2 def __init__(self, surface): self.surface = surface self.hex_radius = HEX_RADIUS_DEFAULT self.hex_height = None self.hex_vertical_increase = None self.hex_width = None self.hex_width_half = None self.__set_size_vars() self.__coord_offset_x = 0 self.__coord_offset_y = 0 self.__center_coord = Coord() self.__hover_coord = Coord() self.__last_mouse_position = None self.__mouse_down_position = None self.__mouse_button = None def handle_events(self, events): for event in events: # print(f'Event: {event}') if event.type == pygame.MOUSEMOTION: self.__set_mouse_position(event.pos) if self.__mouse_down_position and self.__mouse_button == 3: # The user is dragging in the window with the right mouse button. offset = (self.__mouse_down_position[0] - event.pos[0], self.__mouse_down_position[1] - event.pos[1]) self.__mouse_down_position = event.pos self.__move_grid(offset) elif event.type == pygame.MOUSEBUTTONDOWN: self.__mouse_button = event.button self.__mouse_down_position = event.pos elif event.type == pygame.MOUSEBUTTONUP: self.__mouse_button = None self.__mouse_down_position = None elif event.type == pygame.KEYDOWN: if event.key == pygame.K_PLUS or event.key == pygame.K_EQUALS: self.__zoom_in() if event.key == pygame.K_MINUS or event.key == pygame.K_UNDERSCORE: self.__zoom_out() def __set_mouse_position(self, position): # Save the position so we can fix the hover coordinate after zoom changes. self.__last_mouse_position = position self.__hover_coord = self.get_coord(position) def __move_grid(self, offset): self.__coord_offset_x = self.__coord_offset_x - offset[0] self.__coord_offset_y = self.__coord_offset_y - offset[1] if abs(self.__coord_offset_x) > self.hex_width: delta = int(self.__coord_offset_x / self.hex_width) self.__coord_offset_x -= delta * self.hex_width self.__center_coord = self.__center_coord.add(-delta, delta, 0) height_delta = self.hex_height * 3 / 2 if abs(self.__coord_offset_y) > height_delta: delta = int(self.__coord_offset_y / height_delta) self.__coord_offset_y -= delta * height_delta self.__center_coord = self.__center_coord.add( delta, delta, -2 * delta) def __zoom_in(self): if self.hex_radius < HEX_RADIUS_MAX: delta = 4 if self.hex_radius >= HEX_RADIUS_MID else 2 self.hex_radius = self.hex_radius + delta self.__set_size_vars() if self.__last_mouse_position: self.__set_mouse_position(self.__last_mouse_position) def __zoom_out(self): if self.hex_radius > HEX_RADIUS_MIN: delta = 2 if self.hex_radius <= HEX_RADIUS_MID else 4 self.hex_radius = self.hex_radius - delta self.__set_size_vars() if self.__last_mouse_position: self.__set_mouse_position(self.__last_mouse_position) def __set_size_vars(self): self.hex_height = self.hex_radius * 2 self.hex_vertical_increase = self.hex_height * 3 / 4 self.hex_width = HexGrid.precomputed_sqrt3div2 * self.hex_height self.hex_width_half = self.hex_width / 2 def draw(self): # The user is dragging in the window with the left mouse button. self.__draw_selection() self.__draw_hover() def __draw_selection(self): if self.__mouse_down_position and self.__mouse_button == 1: pos1 = self.__mouse_down_position pos2 = self.__last_mouse_position corner_points = [ pos1, (pos1[0], pos2[1]), pos2, (pos2[0], pos1[1]) ] pygame.draw.aalines(self.surface, SELECTION_RECT_COLOR, True, corner_points) def __draw_hover(self): # Hex.draw_circle(self.surface, self.__last_mouse_position, int(self.hex_width_half), HOVER_HEX_COLOR) pass def get_center_position(self, coord): offset = coord.subtract_coord(self.__center_coord) coord_x = self.hex_radius * HexGrid.precomputed_sqrt3 * (offset.x + offset.z / 2) coord_x += self.surface.get_width() / 2 + self.__coord_offset_x coord_y = self.hex_radius * 3 / 2 * offset.z coord_y += self.surface.get_height() / 2 + self.__coord_offset_y return int(coord_x), int(coord_y) def get_coord(self, position): position_x, position_y = position # Update the given position based on the coordinate in the middle of the screen. mouse_x = position_x - self.surface.get_width( ) / 2 - self.__coord_offset_x mouse_y = position_y - self.surface.get_height( ) / 2 - self.__coord_offset_y # Determine the coordinate associated with the current mouse position. coord_x = (mouse_x * HexGrid.precomputed_sqrt3 / 3 - mouse_y / 3) / self.hex_radius coord_z = mouse_y * 2 / 3 / self.hex_radius return Coord(coord_x, -coord_x - coord_z, coord_z).add_coord(self.__center_coord) def __draw_coord(self, coord, color): Hex.draw_circle(self.surface, self.get_center_position(coord), int(self.hex_width_half), color) def is_within(self, pos1, pos2, coord): # We add and subtract half the hex width to give a little bit of selection buffer top_left = min([pos1[0], pos2[0]]) - self.hex_width_half, min( [pos1[1], pos2[1]]) - self.hex_width_half bottom_right = max([pos1[0], pos2[0]]) + self.hex_width_half, max( [pos1[1], pos2[1]]) + self.hex_width_half coord_center = self.get_center_position(coord) return top_left[0] <= coord_center[0] <= bottom_right[0] and top_left[ 1] <= coord_center[1] <= bottom_right[1]
def test_str(self): unit = Unit('type', UnitInfo(10, 11, 12, Coord(1, 2, -3))) self.assertEqual( str(unit), 'Unit[unit_type=type, unit_info=UnitInfo[unit_id=10, user_id=11, game_id=12,' + ' coord=Coord[x=1, y=2, z=-3]]]')