# Generation of data for a campaign of Ticket to Ride Legacy: Legends of the West # Imports import os import random import jsonpickle import time class Circus: # There are: # 4 wagons of each colors (until 2 players' stop) # 2 wagons of each colors (until 3 players' stop) # 2 wagons of each colors (until 4 players' stop) # 2 wagons of each colors (until 5 players' stop) # NOTE: In the original distribution, the same color never appears twice in a row # We chose to remove this constraint, for the sake of simplicity def __init__(self, playersNb): wagonsPerColors = 2 * playersNb colors = "blue", "green", "black", "red", "yellow" remaining = {"blue": wagonsPerColors, "green": wagonsPerColors, "black": wagonsPerColors, "red": wagonsPerColors,\ "yellow": wagonsPerColors} self.wagons = [] for n in range(0, 5 * wagonsPerColors): color = colors[random.randint(0, 4)] while remaining[color] == 0: color = colors[random.randint(0, 4)] self.wagons.append(color) remaining[color] = remaining[color] - 1 self.state = 0 # 0 means Circus is not used in game yet, 1 means next wagon in list is #1, and so on def enable(self): if self.state > 0: print("Circus is already in game.") return False self.state = 1 def getNextColor(self): return self.wagons[self.state - 1] def takeWagon(self): if self.state == 0: print("Circus is not or no longer in game.") return False print("Player obtained a " + self.wagons[self.state - 1] + " circus sticker!") self.state = self.state + 1 if self.state > len(self.wagons): print("Circus stickers are now depleted.") self.state = 0 # effectively disable circus class TreasureCard: def __init__(self): self.treasures = [36, 33, 29, 27, 24] self.state = -1 # disabled def enable(self): self.state = 0 # 0 means 0 treasures have been found def getFoundNumber(self): if self.state == -1: print("Treasure hunt has not started yet or is completed.") return False return self.state def takeTreasure(self): if self.state == -1: print("Treasure hunt has not started yet or is completed.") return False print("Congrats! You obtained $" + str(self.treasures[self.state])) self.state = self.state + 1 if self.state == 5: print("All treasures have been found.") self.state = -1 # effectively disable treasure card class ConcessionCard: # Each card contains: # 6 cities to connect to the player's concession. There is no need to implement those. # 7 gold nuggets, as rewards when the player succefully connects a city to their concession. The player can choose # which nugget to earn, and then discovers the associated reward (between 11 and 17 coins). # A ConcessionCard is then composed of 7 nuggets, with the rewards randomly distributed. def __init__(self): availableValues = [11, 12, 13, 14, 15, 16, 17] nuggetsNb = len(availableValues) - 1 self.nuggets = [] for n in range(0, 7): choice = random.randint(0, nuggetsNb - n) self.nuggets.append(availableValues[choice]) availableValues.pop(choice) self.taken = set() def getRemainingPepites(self): return {1, 2, 3, 4, 5, 6, 7} - self.taken def takeReward(self, choice): if not choice.isdigit(): print("Please choose a number.") return False choice = int(choice) if choice not in range(1, 8): print("This is not a valid number") return False if choice in self.taken: print(str(choice) + " has already been obtained.") return False self.taken.add(choice) print("Congrats! You obtained $" + str(self.nuggets[choice - 1])) class Player: def __init__(self, color): self.color = color self.concession = ConcessionCard() class Game: # initGame() is used to generate all data for a campaign and initialize status variables def initGame(self): self.years = 1865, 1868, 1871, 1874, 1877, 1880, 1883, 1886, 1889, 1892, 1895, 1898 playersNb = input("Enter the number of players (2-5): ") while not playersNb.isdigit() or int(playersNb) not in range(2, 6): playersNb = input("OUT OF RANGE! Enter the number of players (2-5): ") playersNb = int(playersNb) # Game status self.yearId = 0 self.concession = False # General data to generate self.circus = Circus(playersNb) # Mama O'Connell self.treasure = TreasureCard() # Players self.players = [] for n in range(0, playersNb): color = input("Enter player " + str(n + 1) + "'s color: ") self.players.append(Player(color)) def getPlayerByColor(self, color): for player in self.players: if player.color.lower() == color.lower(): return player print("ERROR: player '" + color + "' is not defined") return False def enableConcession(self): self.concession = True def disableConcession(self): self.concession = False def printStatus(self): print("") print("---------------------------------------------------------------------") print("") print("Ticket to Ride Legacy: Legends of the West") print("") print("Campaign - Year: " + str(self.years[self.yearId])) print("") print("Players:") for player in self.players: print(" " + player.color) if self.concession: print(" Concession card remaining nuggets: " + str(player.concession.getRemainingPepites())) print("") if self.circus.state > 0: print("Circus next sticker color: " + self.circus.getNextColor()) if self.treasure.state > -1: print(f"Treasures found: {self.treasure.getFoundNumber()}") def saveData(self, path): savefile = open('./' + path, 'w+') savefile.write(jsonpickle.encode(self)) savefile.close() def loadData(path): with open('./' + path, 'r') as savefile: content = savefile.read() data = jsonpickle.decode(content) return data print("") print("Ticket to Ride Legacy: Legends of the West") print("") choice = input("What do you want to do (new/load)? ") while choice not in ("new", "load"): choice = input("UNDEFINED! What do you want to do (new/load)? ") if choice == "new": myGame = Game() myGame.initGame() elif choice == "load": path = input("Path to save file (defaults to savefile.json)? ") if path == "": path = "savefile.json" myGame = loadData(path) while True: myGame.printStatus() command = input("==> ") while command not in ('load', 'save', 'next year', 'exit', 'enable circus', 'take circus', 'enable concession',\ 'take concession', 'disable concession', 'enable treasure', 'take treasure'): print("") print("Available commands:") print(" Main: 'load', 'save', 'next year', 'exit'") print(" Circus: 'enable circus', 'take circus'") print(" Treasure: 'enable treasure', 'take treasure'") print(" Concessions: 'enable concession', 'take concession', 'disable concession'") command = input("==> ") print("") match command: case 'load': choice = input("WARNING! If you load, you will loose all unsaved changes. Are you sure? (type YES if you are) ") if choice == "YES": path = input("Path to save file (defaults to savefile.json)? ") if path == "": path = "savefile.json" myGame = loadData(path) case 'save': path = input("Path to save file (defaults to savefile.json)? ") if path == "": path = "savefile.json" myGame.saveData(path) print("Game saved!") case 'next year': myGame.yearId = myGame.yearId + 1 case 'exit': exit() case 'enable circus': myGame.circus.enable() case 'take circus': myGame.circus.takeWagon() case 'enable treasure': myGame.treasure.enable() case 'take treasure': myGame.treasure.takeTreasure() case 'enable concession': myGame.enableConcession() case 'take concession': choice = input("Player? ==> ") player = myGame.getPlayerByColor(choice) if player == False: print("Not a valid player.") else: choice = input("Which nugget? ==> ") print("") player.concession.takeReward(choice) case 'disable concession': myGame.disableConcession()