def test_delete(self): self.do_sort() self.assertEqual(len(self.candidates), 5) self.assertEqual(self.candidates.nb_to_be_deleted, 0) self.candidates.delete(Vect(1, 1)) self.assertEqual(len(self.candidates), 4) self.assertEqual(self.candidates.nb_to_be_deleted, 1) self.assertEqual([tile.pos for tile in self.candidates.iterate()], [Vect(0, 0), Vect(2, 2), Vect(3, 3), Vect(4, 4)])
def test_from_edge(self): bottom_lefts = defaultdict(int) for orientation in [Orientation.CLOCKWISE, Orientation.COUNTERCLOCKWISE]: for domain in [Domain.INTERIOR, Domain.EXTERIOR]: border = boundary.from_edge(Vect(3, 5), Vect(1, 0), orientation, domain) self.assertEqual(len(border), 4) self.assertEqual(border.orientation(), orientation) bottom_lefts[border.bottom_left()] += 1 self.assertEqual(bottom_lefts, {Vect(3, 5): 2, Vect(3, 4): 2})
def test_merge(self): border = Boundary() border.merge(boundary.get_tile(Vect(0, 0), 'FFFF')) self.assertEqual(len(border), 4) self.assertEqual(border.labels, list('FFFF')) border.merge(boundary.get_tile(Vect(1, 0), 'FFFF')) self.assertEqual(len(border), 6) self.assertEqual(border.labels, list('FFFFFF'))
def test_segment_labels(self): placed_tile = PlacedTile(self.test_tile, Vect(2, 1), r = 0, segment = (42, 1, 2)) self.assertEqual(list(Boundary.label_getter(placed_tile.iter_segment())), list('PT')) self.assertEqual(list(Boundary.label_getter(placed_tile.iter_complement_segment())), list('PF')) placed_tile = PlacedTile(self.test_tile, Vect(2, 1), r = 1, segment = (42, 1, 2)) self.assertEqual(list(Boundary.label_getter(placed_tile.iter_segment())), list('FP')) self.assertEqual(list(Boundary.label_getter(placed_tile.iter_complement_segment())), list('TP')) placed_tile = PlacedTile(self.test_tile, Vect(2, 1), r = 0, segment = (42, 1, 0)) self.assertEqual(list(Boundary.label_getter(placed_tile.iter_segment())), []) self.assertEqual(list(Boundary.label_getter(placed_tile.iter_complement_segment())), list('PTPF'))
def test_find_matching_rotations(self): tiles_args = [ (Vect(0, 0), 'FFFF'), (Vect(0, 1), 'FFFF'), (Vect(1, 1), 'TFFF')] border = make_border_from_tiles(tiles_args) border.rotate_to_start_with(Vect(0, 0)) self.assertEqual(len(border), 8) self.assertEqual(border.labels, list('FFTFFFFF')) tile = boundary.get_tile(Vect(1, 0), 'TFFF') self.assertEqual(border.common_segments(tile), [(1, 2, 2)]) self.assertEqual(list(border.find_matching_rotations(tile, (1, 2, 2))), [2])
def test_self(self): self.assertEqual(len(self.border), 6) self.assertEqual(self.border.orientation(), Orientation.COUNTERCLOCKWISE) self.assertEqual(self.border.get_point(2), Vect(2, 0)) self.assertEqual(self.border.get_point(8), self.border.get_point(2)) self.assertEqual(self.border.get_point(-4), self.border.get_point(2)) self.assertEqual(self.border.get_label(2), 'T') self.assertEqual(self.border.get_label(8), self.border.get_label(2)) self.assertEqual(self.border.get_label(-4), self.border.get_label(2)) self.assertEqual(self.border.get_edge(0), Vect(1, 0)) self.assertEqual(self.border.get_edge(-1), Vect(0, -1)) self.assertEqual(self.border.bottom_left(), Vect(0, 0))
def test_get_tile(self): bottom_left = Vect(5, 7) tile = boundary.get_tile(bottom_left, 'FFTF') self.assertEqual(len(tile), 4) self.assertEqual(tile.orientation(), Orientation.COUNTERCLOCKWISE) self.assertEqual(tile.get_point(0), bottom_left) self.assertEqual(tile.bottom_left(), bottom_left) self.assertEqual(tile.get_edge(0), Vect(1, 0)) self.assertEqual(tile.get_edge(1), Vect(0, 1)) self.assertEqual(tile.get_edge(2), Vect(-1, 0)) self.assertEqual(tile.get_edge(3), Vect(0, -1)) self.assertEqual(tile.get_label(0), 'F') self.assertEqual(tile.get_label(2), 'T')
def test_rotate_to_start_with(self): border = self.border.copy() border.rotate_to_start_with(Vect(2, 0)) self.assertEqual(len(border), len(self.border)) self.assertEqual(border.points[0], Vect(2, 0)) self.assertEqual(border.labels, list('TPFPFF')) border.rotate_to_start_with(Vect(2, 1)) self.assertEqual(len(border), len(self.border)) self.assertEqual(border.points[0], Vect(2, 1)) self.assertEqual(border.labels, list('PFPFFT')) with self.assertRaises(ValueError): border.rotate_to_start_with(Vect(99, 99))
def test_common_segments_2(self): tiles_bottom_left = [Vect(0, 0), Vect(1, 0), Vect(1, 1), Vect(0, 1)] for single_idx in range(4): single_tile = boundary.get_tile(tiles_bottom_left[single_idx]) three_other_tiles = [(bl,) for bl in tiles_bottom_left[single_idx+1:] + tiles_bottom_left[:single_idx]] border = make_border_from_tiles(three_other_tiles) self.assertEqual(len(border), 8) border.rotate_to_start_with(Vect(1, 1)) for ii in range(len(border)): segments = border.common_segments(single_tile) self.assertEqual(len(segments), 1) (i, j, L) = segments[0] self.assertEqual(L, 2) self.assertEqual(j, (single_idx + 1) % 4) self.assertEqual(i, 7 - ii) border.rotate_to_start_with(border.points[1])
def append(self, tile): offset = None for tag in tile.tags: result = self.vect_re.match(tag) if result: offset = Vect(int(result.group(1)), int(result.group(2))) if offset: self.elts.append(CompositeTile.Elt(tile, offset)) else: warn('Could not find the offset pattern in the tags for tile {}. Tags = {}.'.format(tile, tile.tags))
def test_corner_case_2(self): tiles_args = [ (Vect(2, 0), 'FFFF'), (Vect(2, 1), 'FFFF'), (Vect(2, 2), 'FFFF'), (Vect(1, 2), 'FFFF'), (Vect(0, 2), 'FFFF'), (Vect(0, 1), 'FFFF'), (Vect(0, 0), 'FFFF')] border = make_border_from_tiles(tiles_args) self.assertEqual(len(border), 16) tile = boundary.get_tile(Vect(1, 0), 'FFFF') segments = border.common_segments(tile) self.assertEqual(len(segments), 2)
def setUp(self): self.border = Boundary() self.border.append(Vect(0, 0), 'F') self.border.append(Vect(1, 0), 'F') self.border.append(Vect(2, 0), 'T') self.border.append(Vect(2, 1), 'P') self.border.append(Vect(1, 1), 'F') self.border.append(Vect(0, 1), 'P')
def setUp(self): self.candidates = CandidateTiles() self.candidates.update(PositionedTile(Vect(5, 5), segments = [(0, 0, 1), (1, 1, 1)])) self.candidates.update(PositionedTile(Vect(4, 4), segments = [(0, 0, 1)])) self.candidates.update(PositionedTile(Vect(3, 3), segments = [(0, 0, 1)])) self.candidates.update(PositionedTile(Vect(2, 2), segments = [(0, 0, 2)])) self.candidates.update(PositionedTile(Vect(1, 1), segments = [(0, 0, 2)])) self.candidates.update(PositionedTile(Vect(0, 0), segments = [(0, 0, 3)]))
def test_common_segments_1(self): tile1 = boundary.get_tile(Vect(2, 1), 'FFFF') self.assertEqual(self.border.common_segments(tile1), [(3, 0, 0)]) tile2 = boundary.get_tile(Vect(1, 1), 'FFFF') self.assertEqual(self.border.common_segments(tile2), [(3, 0, 1)]) self.assertEqual(tile1.common_segments(tile2), [(3, 1, 1)]) self.assertEqual(tile2.common_segments(tile1), [(1, 3, 1)]) tile3 = Boundary() tile3.append(Vect(2, 2), 'F') tile3.append(Vect(1, 2), 'F') tile3.append(Vect(1, 1), 'F') tile3.append(Vect(2, 1), 'F') self.assertEqual(tile1.common_segments(tile3), [(3, 3, 1)]) self.assertEqual(tile3.common_segments(tile1), [(3, 3, 1)])
def test_sort(self): self.assertEqual(len(self.candidates), 5) self.do_sort() self.assertEqual([tile.pos for tile in self.candidates.iterate()], [Vect(0, 0), Vect(1, 1), Vect(2, 2), Vect(3, 3), Vect(4, 4)])
def test_operations(self): self.assertTrue(Vect(1, 0) == Vect(1, 0)) self.assertFalse(Vect(1, 0) == Vect(1, 1)) self.assertEqual(Vect(1, 0) + Vect(0, 1), Vect(1, 1)) self.assertEqual(Vect(1, 2) - Vect(1, 2), Vect(0, 0)) self.assertEqual(Vect(1, 2).mult(2), Vect(2, 4)) self.assertEqual(Vect(1, 0).cross_z(Vect(0, 1)), 1) self.assertEqual(Vect(0, 1).cross_z(Vect(1, 0)), -1) self.assertEqual(Vect(-3, 5).l1_distance(), 8)
def main(): parser = argparse.ArgumentParser(description='Display a randomized Carcassonne map') parser.add_argument('files', metavar='FILE', nargs='*', help='Tile description file (JSON format)') parser.add_argument('-d', '--debug', dest='debug_mode', action='store_true', help='Display non-game tiles, etc.') parser.add_argument('-n', metavar='N', type=int, dest='max_tiles', default = 0, help='Number of tiles to display (Default: The whole tileset)') parser.add_argument('-z', '--zoom-factor', metavar='Z', type=float, dest='zoom_factor', default = 1.0, help='Initial zoom factor (Default: 1.0)') parser.add_argument('--draw-all', dest='draw_all', action='store_true', help='Draw all tiles') parser.add_argument('-f', '--full-screen', dest='full_screen', action='store_true', help='Full screen') parser.add_argument('-s', '--screenshot', dest='take_screenshot', action='store_true', help='Take a screenshot of the final display') parser.add_argument('--dump', dest='dump_to_img', action='store_true', help='Dump the final grid to an image') parser.add_argument('--river-policy', type=str, dest='river_policy', choices=[policy.name for policy in RiverPlacement], action='append', default=[], help='Placement policies for the river tileset. Can be used multiple times') parser.add_argument('--river-period', metavar='P', type=int, dest='river_period', default=1, help='Period of repetition of the river tileset. Set to zero for a single use of the river tileset') parser.add_argument('--seed', metavar='INT', type=int, dest='seed', default = 0, help='A seed for the random generator (Default: Use a system generated seed)') args = parser.parse_args() # Set random seed rng_seed = args.seed if rng_seed == 0: rng_seed = secrets.randbits(64) print('Random seed: {}'.format(rng_seed)) random.seed(rng_seed) # Load tileset (JSON files) tileset = list(itertools.chain.from_iterable(parse_tileset_description_file(json_file) for json_file in args.files)) if len(tileset) == 0: error('No tiles loaded') # River tiles placement policy and period river_placement_policies = parse_river_placement_policies([RiverPlacement[policy] for policy in args.river_policy]) river_tileset_period = args.river_period if args.river_period >= 0 else 0 if args.debug_mode and any('river' in tile.tags for tile in tileset): print('river_placement_policies: {}'.format([policy.name for policy in river_placement_policies])) print('river_tileset_period: {}'.format(river_tileset_period)) try: # Load tile images, and draw missing ones graphics.init() tile_size = load_or_draw_tile_images(tileset, args.draw_all) carcassonne_city_tileset, tileset = TileSubset.carcassonne_city().partition_iter(tileset) city_start_flag = len(carcassonne_city_tileset) > 0 river_tileset, regular_tileset = TileSubset.river().partition(tileset) del tileset # Non-game tiles riverside_tile = Tile.from_uniform_color((217, 236, 255), tile_size, 'riverside') forbidden_tile = Tile.from_uniform_color((100, 20, 20), tile_size, 'forbidden') segment_length_tiles = { 0: forbidden_tile, 1: Tile.from_uniform_color((10, 60, 10), tile_size, 'one_side'), 2: Tile.from_uniform_color((40, 120, 40), tile_size, 'two_sides'), 3: Tile.from_uniform_color((70, 180, 70), tile_size, 'three_sides') } # Open display (w, h) = (0, 0) if args.full_screen else (1280, 720) display = graphics.GridDisplay(w, h, tile_size) print('Press ESCAPE in the graphics window to quit', flush = True) # Place random tiles. The map must grow! candidate_tiles = CandidateTiles( on_update = lambda pos_tile: display.set_tile(segment_length_tiles[pos_tile.get_segment_length()].img, pos_tile.pos.x, pos_tile.pos.y) if args.debug_mode else None, on_delete = None) z = args.zoom_factor border = place_carcassonne_city(carcassonne_city_tileset, candidate_tiles, display, z, Vect(-2, -1)) if city_start_flag else Boundary() total_nb_tiles_placed = 0 total_nb_tiles_not_placed = 0 first_tileset_flag = not city_start_flag all_done_flag = False for tileset in iterate_tilesets(river_tileset, regular_tileset, river_tileset_period, infinite = (args.max_tiles > 0)): for tiles_to_place in shuffle_tileset(tileset, first_tileset_flag, river_placement_policies): local_nb_tiles_placed = 0 while len(tiles_to_place) > 0: tiles_not_placed = [] for tile in tiles_to_place: if args.max_tiles > 0 and total_nb_tiles_placed >= args.max_tiles: all_done_flag = True break if len(border) == 0: # The first tile of the map is placed at the center placed_tile = PlacedTile(tile, Vect(0, 0), r = 0) else: forced_segment = 'R' if 'river' in tile.tags and 'source' not in tile.tags else None max_candidates = 1 candidate_placements = find_candidate_placements(tile, border, candidate_tiles, max_candidates, forced_segment) placed_tile = select_tile_placement(candidate_placements) if len(candidate_placements) > 0 else None if placed_tile: update_border_and_candidate_tiles(placed_tile, border, candidate_tiles) placed_tile.draw(display) total_nb_tiles_placed += 1 local_nb_tiles_placed += 1 # z = 0.995 * z # display.update(z, 100) else: tiles_not_placed.append(tile) if all_done_flag: break if len(tiles_not_placed) == len(tiles_to_place): # making no progress, stop there total_nb_tiles_not_placed += len(tiles_not_placed) for tile in tiles_not_placed: warn('Could not place tile: {}'.format(tile)) break assert len(tiles_not_placed) < len(tiles_to_place) tiles_to_place = tiles_not_placed # Done with the current tiles subset if DEBUG_PRINTOUT or args.debug_mode: print('total_nb_tiles_placed: {} (+{})'.format(total_nb_tiles_placed, local_nb_tiles_placed)) if all_done_flag: break # Done with the current tileset if all_done_flag: break first_tileset_flag = False display.update(z) # Completely done! display.update(z) print('Done!') print('total_nb_tiles_not_placed: {}'.format(total_nb_tiles_not_placed)) print('total_nb_tiles_placed: {}'.format(total_nb_tiles_placed)) sys.stdout.flush() # Wait until the user quits while True: display.check_event_queue(200) except graphics.MustQuit: pass finally: if args.debug_mode and 'display' in locals(): print(display.get_debug_info()) if (args.take_screenshot or args.debug_mode) and 'display' in locals(): display.take_screenshot(SCREENSHOT_PATH) print('Screenshot saved in {}'.format(SCREENSHOT_PATH)) if args.dump_to_img and 'display' in locals(): display.dump_to_img(DUMP_PATH, args.zoom_factor) print('Dump grid to {}'.format(DUMP_PATH)) graphics.quit() return 0
def test_update(self): self.do_sort() self.assertEqual(next(self.candidates.iterate()).pos, Vect(0, 0)) self.candidates.update(PositionedTile(Vect(0, 0), segments = [(0, 0, 1)])) self.do_sort() self.assertEqual(next(self.candidates.iterate()).pos, Vect(1, 1))