def board_private_company_info(): coord = request.args.get("coord") company = request.args.get("company") phase = request.args.get("phase") default_offset = {"x": 0, "y": 0} private_company_offsets = get_private_offsets(get_game(g.game_name)) offset_data = private_company_offsets.get(company, {}) coord_offset = offset_data.get(coord, {}).get("offset", default_offset) if set(coord_offset.keys()) == {"x", "y"}: offset = coord_offset else: game = get_game(g.game_name) for offset_phase in sorted(coord_offset, key=game.phases.index, reverse=True): if game.compare_phases(offset_phase, phase) >= 0: offset = coord_offset[offset_phase] break else: # This indicates a special offset will be needed in later phases, # but we can use the default offset for now. offset = default_offset info = {"offset": offset} return jsonify({"info": info})
def legal_tiles(): game = get_game(g.game_name) coord = request.args.get("coord") LOG.info(f"Legal tiles request for {coord}.") legal_tile_ids = _legal_tile_ids_by_coord(get_game(g.game_name), coord) legal_tile_ids.sort(key=lambda tile_id: f"{game.tiles[tile_id].upgrade_level}-{tile_id:0>3}") LOG.info(f"Legal tiles response for {coord}: {legal_tile_ids}") return jsonify({"legal-tile-ids": legal_tile_ids})
def _get_split_city_stations(coord, tile_id, orientation): game = get_game(g.game_name) board = get_board(game) cell = board.cell(coord) if tile_id and orientation: split_city_space = placedtile.SplitCity.place(cell, game.tiles[tile_id], orientation) else: split_city_space = board.get_space(cell) split_city_station_coords = set() for branch in split_city_space.capacity.keys(): unique_exit_coords = [ branch_key for branch_key in branch if len(branch_key) == 1 ] if unique_exit_coords: # A unique exit coord is a sequence of length 1, so we need to extract it split_city_station_coords.add(str( sorted(unique_exit_coords)[0][0])) else: split_city_station_coords.add(str(sorted(branch)[0])) return split_city_station_coords
def _phase_from_trains(train_strs_json): train_strs = json.loads(train_strs_json) if train_strs: train_info = get_train_info(g.game_name) return max( train.phase for train in trains_mod.convert(train_info, ",".join(train_strs))) else: return get_game(g.game_name).phases[0]
def legal_token_coords(): game = get_game(g.game_name) private_companies = game.get_game_submodule("private_companies") if not private_companies: return jsonify({"coords": {}}) LOG.info("Legal private company token coordinates request.") private_company_coords = private_companies.PRIVATE_COMPANY_COORDS LOG.info( f"Legal private company token coordinates response: {private_company_coords}" ) return jsonify({"coords": private_company_coords})
def calculate_worker(game_name, railroads_state_rows, private_companies_rows, board_state_rows, railroad_name): game = get_game(game_name) board_state = boardstate.load(game, [dict(zip(boardstate.FIELDNAMES, row)) for row in board_state_rows if any(val for val in row)]) railroad_dict = railroads.load(game, board_state, [dict(zip(railroads.FIELDNAMES, row)) for row in railroads_state_rows if any(val for val in row)]) game.capture_phase(railroad_dict) private_companies = game.get_game_submodule("private_companies") if private_companies: private_companies.load(game, board_state, railroad_dict, [dict(zip(private_companies.FIELDNAMES, row)) for row in private_companies_rows if any(val for val in row)]) board_state.validate() if railroad_name not in railroad_dict: valid_railroads = ", ".join(railroad_dict.keys()) raise ValueError(f"Railroad chosen: \"{railroad_name}\". Valid railroads: {valid_railroads}") return find_best_routes(game, board_state, railroad_dict, railroad_dict[railroad_name])
def closable_railroads(): LOG.info("Closable railroads request.") game = get_game(g.game_name) closable_railroads = _get_closable_railroads(game) LOG.info(f"Closable railroads response: {closable_railroads}") railroads_info = get_railroad_info(game) return jsonify({ "railroads": list(sorted(closable_railroads)), "home-cities": { railroad: railroads_info[railroad]["home"] for railroad in closable_railroads } })
def private_companies_open(): game = get_game(g.game_name) private_companies = game.get_game_submodule("private_companies") if not private_companies: return jsonify({"private-companies": []}) LOG.info("Open private companies request.") phase = _phase_from_trains(request.args.get("trains")) private_companies = [ private_company for private_company in private_companies.COMPANIES if not game.private_is_closed(private_company, phase) ] LOG.info(f"Open private companies response: {private_companies}") return jsonify({"private-companies": private_companies})
def legal_orientations(): coord = request.args.get("coord") tile_id = request.args.get("tileId") LOG.info(f"Legal orientations request for {tile_id} at {coord}.") orientations, translations = _get_orientations(get_game(g.game_name), coord, tile_id) LOG.info( f"Legal orientations response for {tile_id} at {coord}: {orientations}" ) return jsonify({ "legal-orientations": list(sorted(orientations)) if orientations is not None else orientations, "translations": translations })
def main(): migration_data = migrate.process_migrate_data() game = get_game(g.game_name) board = get_board(game) stop_names = {} for cell in board.cells: space = board.get_space(cell) if space and space.is_stop: stop_names[str(cell)] = space.nickname termini_boundaries = { name: info["boundaries"] for name, info in get_termini_boundaries(game).items() } private_companies = game.get_game_submodule("private_companies") private_company_names = private_companies.COMPANIES.keys( ) if private_companies else [] private_company_column_names = [ PRIVATE_COMPANY_COLUMN_MAP[colname] for colname in private_companies.FIELDNAMES ] if private_companies else [] return render_template( "index.html", private_company_default_token_coords=private_companies. PRIVATE_COMPANY_DEFAULT_COORDS if private_companies else {}, private_company_rownames=private_company_names, placed_tiles_colnames=PLACED_TILES_COLUMN_NAMES, tile_coords=get_tile_coords(board), stop_names=stop_names, termini_boundaries=termini_boundaries, removable_railroads=_get_removable_railroads(), closable_railroads=_get_closable_railroads(game), board_layout=_get_board_layout_info(), migration_data=migration_data)
def _convert_migration_data(migration_data): converted_data = { "removedRailroadsTable": migration_data.get("removedRailroadsTable", ""), "privateCompaniesTable": migration_data.get("privateCompaniesTable", ""), "hideStopPaths": migration_data.get("hideCityPaths", "") } if "placedTilesTable" in migration_data: rotation_map = _get_placement_info(get_game("1846"), "rotation-map.json") converted_data["placedTilesTable"] = [] for tile_row in json.loads(migration_data["placedTilesTable"]): converted_data["placedTilesTable"].append([ tile_row[0], str(tile_row[1]), str((int(tile_row[2]) - rotation_map[str(tile_row[1])]) % 6) ]) converted_data["placedTilesTable"] = json.dumps( converted_data["placedTilesTable"]) if "railroadsTable" in migration_data: converted_data["railroadsTable"] = [] for railroad_row in json.loads(migration_data["railroadsTable"]): new_stations_coords = [] for station_coord in railroad_row[2].split(','): new_stations_coords.append( f"{station_coord}:{railroad_row[3]}" if station_coord == "D6" else station_coord) converted_data["railroadsTable"].append([ railroad_row[0], railroad_row[1], ",".join(new_stations_coords) ]) converted_data["railroadsTable"] = json.dumps( converted_data["railroadsTable"]) return converted_data
def _validate_migration_data(migration_data): if not isinstance(migration_data, dict): LOG.debug( f"Migration validation failure: migration_data is not a dict") return {"error": "Failed to load migration data."} game = get_game("1846") railroad_info = get_railroad_info(game) private_companies = game.get_game_submodule("private_companies") for key, value in migration_data.items(): # Make sure no control characters or non-ASCII characters are present. if not key.isascii() or not key.isprintable() or not value.isascii( ) or not value.isprintable(): LOG.debug( f"Migration failure: key or value is not printable ASCII") return False try: migration_data[key] = json.dumps(json.loads(value)) except Exception as exc: LOG.debug( f"Migration validation failure[{key}]: value not valid JSON: {value}" ) return False if key == "placedTilesTable": tiles_json = json.loads(value) if not all(len(row) == 3 for row in tiles_json) or not all( str(col).isalnum() for row in tiles_json for col in row): LOG.debug(f"Migration validation failure[{key}]: {value}") return False elif key == "railroadsTable": railroads_json = json.loads(value) for row in railroads_json: if len(row) not in (3, 4) or row[0].strip() not in railroad_info: LOG.debug( f"Migration validation failure[{key}]: row wrong length, or railroad name invalid: {value}" ) return False if row[1] and row[1].strip(): train_strs = [ train_str.strip().split("/", 1) for train_str in row[1].strip().split(",") ] if not all(val.strip().isdigit() for train_str in train_strs for val in train_str): LOG.debug( f"Migration validation failure[{key}]: trains malformed: {value}" ) return False if row[2] and row[2].strip(): if not all(station_str.strip().isalnum() for station_str in row[2].strip().split(",")): LOG.debug( f"Migration validation failure[{key}]: stations malformed: {value}" ) return False elif key == "removedRailroadsTable": removed_railroads = json.loads(value) for railroad in removed_railroads: if railroad.strip() not in railroad_info: LOG.debug( f"Migration validation failure[{key}]: railroad name invalid: {value}" ) return False elif key == "privateCompaniesTable": private_companies_json = json.loads(value) for row in private_companies_json: if len(row) != 3 \ or row[0].strip() not in private_companies.COMPANIES \ or (row[1] and row[1].strip() not in railroad_info) \ or (row[2] and not row[2].strip().isalnum()): LOG.debug( f"Migration validation failure[{key}]: invalid row: {row}" ) return False elif key == "hideCityPaths": if value not in ("true", "false"): LOG.debug(f"Migration validation failure[{key}]: {value}") return False else: LOG.debug(f"Migration validation failure: invalid key: {key}") return False return True
def board_space_info(): coord = request.args["coord"].strip() tile_id = request.args.get("tileId", "").strip() orientation = request.args.get("orientation", "").strip() phase = request.args.get("phase") game = get_game(g.game_name) board = get_board(game) railroad_info = get_railroad_info(game) cell = board.cell(coord) station_offsets = get_station_offsets(game) if tile_id and orientation: offset_data = station_offsets.get("tile", {}) offset = _get_station_offset(offset_data, tile_id) space = placedtile.PlacedTile.place(cell, game.tiles[tile_id], orientation, board.get_space(cell)) if isinstance(space, placedtile.SplitCity): # In the offset file, branches are indicated by side. To translate # into cells, add the orientation, and modulo by 6 to retrieve its # neighbor given the orientation. offset = { str(cell.neighbors[(int(exit) + int(orientation)) % 6]): offsetCoords for exit, offsetCoords in offset.items() } if "rotation" in offset: offset["rotation"] = math.radians(offset["rotation"]) else: offset_data = station_offsets.get("board", {}) offset = _get_station_offset(offset_data, coord) space = board.get_space(cell) if "rotation" in offset: offset["rotation"] = math.radians(offset["rotation"]) if hasattr(space, "capacity"): # Stop-gap. I need to figure out what to actually do with capacity keys. capacity = sum(space.capacity.values()) if isinstance( space.capacity, dict) else space.capacity else: capacity = 0 home = [ railroad for railroad, info_dict in railroad_info.items() if info_dict.get("home") == coord ] if not phase or (game.rules.stations_reserved_until and game.compare_phases( game.rules.stations_reserved_until, phase) < 0): reserved = [ railroad for railroad, info_dict in railroad_info.items() if coord in info_dict.get("reserved", []) ] else: reserved = [] info = { "capacity": capacity, "offset": offset, "phase": getattr(space, "upgrade_level", 0), "is-split-city": isinstance(space, (boardtile.SplitCity, placedtile.SplitCity)), "home-to": home, "reserved-for": reserved } return jsonify({"info": info})