Compare commits

..

15 Commits

6 changed files with 553 additions and 146 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
*.json *.json
*__pycache__

315
main.py
View File

@ -1,120 +1,35 @@
# Generation of data for a campaign of Ticket to Ride Legacy Legends of the West # Generation of data for a campaign of Ticket to Ride Legacy: Legends of the West
# Imports # Imports
import os import os
import random
import jsonpickle import jsonpickle
class Circus: from src.Circus import Circus
# There are: from src.TreasureCard import TreasureCard
# 4 wagons of each colors (until 2 players' stop) from src.Player import Player, ConcessionCard
# 2 wagons of each colors (until 3 players' stop) from src.MamaOConnell import MamaOConnell
# 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 not or no longer 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
self.state = self.state + 1
if self.state == 5:
print("All treasures have been found.")
self.state = -1 # effectively disable treasure card
class ConcessionCard: # name may be different in English
# Each card contains:
# 6 cities to connect to the player's concession. There is no need to implement those.
# 7 gold "pepites", as rewards when the player succefully connects a city to their concession. The player can choose
# which "pepite" to earn, and then discovers the associated reward (between 11 and 17 coins).
# A ConcessionCard is then composed of 7 pepites, with the rewards randomly distributed.
def __init__(self):
availableValues = [11, 12, 13, 14, 15, 16, 17]
pepitesNb = len(availableValues) - 1
self.pepites = []
for n in range(0, 7):
choice = random.randint(0, pepitesNb - n)
self.pepites.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 choice in self.taken:
print(str(choice) + " has already been obtained.")
return False
self.taken.add(choice)
print("Congrats! You obtained $" + str(self.pepites[choice]))
class Player:
def __init__(self, color):
self.color = color
self.concession = ConcessionCard()
class Game: class Game:
# initGame() is used to generate all data for a campaign and initialize status variables # initGame() is used to generate all data for a campaign and initialize status variables
def initGame(self, playersNb): 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 # Game status
self.year = 1858 self.yearId = 0
self.concession = False self.concession = False
# General data to generate # General data to generate
self.circus = Circus(playersNb) self.circus = Circus(playersNb)
# Mama O'Connell
self.treasure = TreasureCard() self.treasure = TreasureCard()
self.mamaoc = MamaOConnell()
while not self.mamaoc.solver():
self.mamaoc = MamaOConnell()
# Players # Players
self.players = [] self.players = []
@ -122,70 +37,178 @@ class Game:
color = input("Enter player " + str(n + 1) + "'s color: ") color = input("Enter player " + str(n + 1) + "'s color: ")
self.players.append(Player(color)) self.players.append(Player(color))
# Stickers pools
self.stickers = {'White': 0, 'Red': 0, 'Blue': 0, 'Yellow': 0, 'Green': 0, 'Black': 0}
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
# if number = 0 print Mama O'Connell's location, else print hint #number
def printMamaHint(self, number):
if number not in range(0, 4):
return False
os.system('clear')
input("When you are ready, press Enter.")
print("")
match number:
case 0:
print("Mama O'Connell is at: " + self.mamaoc.location.name)
case 1:
print("Mama O'Connell is " + str(self.mamaoc.hint1[1]) + " cities from " + self.mamaoc.hint1[0].name)
case 2:
print("Mama O'Connell is " + str(self.mamaoc.hint2[1]) + " cities from " + self.mamaoc.hint2[0].name)
case 3:
print("Mama O'Connell is " + str(self.mamaoc.hint3[1]) + " cities from " + self.mamaoc.hint3[0].name)
print("")
input("When you are done, press Enter.")
os.system('clear')
def takeSticker(self, color, nb):
if not nb.isdigit() or int(nb) < 1:
print("ERROR: Not a positive number")
return False
nb = int(nb)
for c, n in self.stickers.items():
if c.lower() == color.lower():
if nb > n:
print("ERROR: not enough stickers remaining.")
return False
else:
self.stickers[c] = n - nb
return True
print("ERROR: color not found.")
return False
def newStickersBatch(self):
self.stickers = {x: self.stickers[x] + 7 for x in self.stickers}
def printStatus(self): def printStatus(self):
print("")
print("---------------------------------------------------------------------")
print("")
print("Ticket to Ride Legacy: Legends of the West") print("Ticket to Ride Legacy: Legends of the West")
print("") print("")
print("Campaign - Year: " + str(self.year)) print("Campaign - Year: " + str(self.years[self.yearId]))
print("") print("")
print("Players:") print("Players:")
for player in self.players: for player in self.players:
print(" " + player.color) print(" " + player.color)
if self.concession: if self.concession:
print(" Concession card remaining pepites: " + str(player.concession.getRemainingPepites())) print(" Concession card remaining nuggets: " + str(player.concession.getRemainingPepites()))
print("") print("")
if self.circus.state > 0: if self.circus.state > 0:
print("Circus next sticker color: " + self.circus.getNextColor()) print("Circus next sticker color: " + self.circus.getNextColor())
print("")
if self.treasure.state > -1: if self.treasure.state > -1:
print(f"Treasures found: {self.treasure.getFoundNumber()}") print(f"Treasures found: {self.treasure.getFoundNumber()}")
print("")
print("Road stickers:")
for color, nb in self.stickers.items():
print(" " + color + ": " + str(nb))
def saveData(self): def saveData(self, path):
savefile = open('./savefile.json', 'w+') savefile = open('./' + path, 'w+')
savefile.write(jsonpickle.encode(self)) savefile.write(jsonpickle.encode(self))
savefile.close() savefile.close()
def loadData(): def loadData(path):
with open('./savefile.json', 'r') as savefile: with open('./' + path, 'r') as savefile:
content = savefile.read() content = savefile.read()
data = jsonpickle.decode(content) data = jsonpickle.decode(content)
return data return data
# TESTS print("")
playersNb = 2 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)
# Test of data generation while True:
# myGame = Game() myGame.printStatus()
# myGame.initGame(playersNb) command = input("==> ")
# myGame.saveData() while command not in ('load', 'save', 'next year', 'exit', 'take sticker', 'batch sticker', 'enable circus',\
'take circus', 'enable concession', 'take concession', 'disable concession', 'enable treasure', 'take treasure',\
# Test of data loading 'mama hint 1', 'mama hint 2', 'mama hint 3', 'mama location'):
# myGame = loadData() print("")
# print(myGame.year) print("Available commands:")
# print(myGame.circus.wagons) print(" Main: 'load', 'save', 'next year', 'exit'")
print(" Stickers: 'take sticker', 'batch sticker'")
# Test of ConcessionCard print(" Circus: 'enable circus', 'take circus'")
# cc = ConcessionCard() print(" Treasure: 'enable treasure', 'take treasure'")
# userChoice = 3 print(" Concessions: 'enable concession', 'take concession', 'disable concession'")
# print(cc.pepites) print(" Mama O'Connell tracking: 'mama hint 1', 'mama hint 2', 'mama hint 3', 'mama location'")
# cc.takeReward(userChoice - 1) command = input("==> ")
# print(cc.pepites) print("")
# print(cc.taken) match command:
# print("Remaining: " + str(cc.getRemainingPepites())) case 'load':
# cc.takeReward(userChoice - 1) choice = input("WARNING! If you load, you will loose all unsaved changes. Are you sure? (type YES if you are) ")
# print(cc.pepites) if choice == "YES":
# print(cc.taken) path = input("Path to save file (defaults to savefile.json)? ")
# print("Remaining: " + str(cc.getRemainingPepites())) if path == "":
path = "savefile.json"
# Test of Player myGame = loadData(path)
myGame = Game() case 'save':
myGame.initGame(playersNb) path = input("Path to save file (defaults to savefile.json)? ")
myGame.printStatus() if path == "":
myGame.treasure.enable() path = "savefile.json"
myGame.printStatus() myGame.saveData(path)
myGame.treasure.takeTreasure() print("Game saved!")
myGame.treasure.takeTreasure() case 'next year':
myGame.treasure.takeTreasure() myGame.yearId = myGame.yearId + 1
myGame.treasure.takeTreasure() case 'exit':
myGame.printStatus() exit()
myGame.treasure.takeTreasure() case 'take sticker':
myGame.printStatus() choice = input("Color ? ==> ")
number = input("Nb ? ==> ")
myGame.takeSticker(choice, number)
case 'batch sticker':
myGame.newStickersBatch()
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()
case 'mama hint 1':
myGame.printMamaHint(1)
case 'mama hint 2':
myGame.printMamaHint(2)
case 'mama hint 3':
myGame.printMamaHint(3)
case 'mama location':
myGame.printMamaHint(0)

43
src/Circus.py Normal file
View File

@ -0,0 +1,43 @@
import random
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

275
src/MamaOConnell.py Normal file
View File

@ -0,0 +1,275 @@
import random
class City:
def __init__(self, name, neighbors):
self.name = name
self.neighbors = neighbors
class Board:
def __init__(self):
self.cities = []
# NorthEast (8 cities)
neighbors = "Quebec", "Montreal", "Boston"
self.cities.append(City("Bangor", neighbors))
neighbors = "Bangor", "Montreal"
self.cities.append(City("Quebec", neighbors))
neighbors = "Quebec", "Bangor", "Boston", "Albany", "Buffalo"
self.cities.append(City("Montreal", neighbors))
neighbors = "Bangor", "Montreal", "Albany", "New York"
self.cities.append(City("Boston", neighbors))
neighbors = "Buffalo", "Montreal", "Boston", "New York"
self.cities.append(City("Albany", neighbors))
neighbors = "Detroit", "Montreal", "Albany", "New York", "Philadelphia", "Pittsburgh"
self.cities.append(City("Buffalo", neighbors))
neighbors = "Buffalo", "Albany", "Boston", "Philadelphia"
self.cities.append(City("New York", neighbors))
neighbors = "Buffalo", "New York", "Norfolk", "Baltimore", "Pittsburgh"
self.cities.append(City("Philadelphia", neighbors))
# Appalachia (8 cities)
neighbors = "Buffalo", "Pittsburgh", "Cincinnati", "Chicago"
self.cities.append(City("Detroit", neighbors))
neighbors = "Buffalo", "Philadelphia", "Baltimore", "Lewisburg", "Cincinnati", "Detroit"
self.cities.append(City("Pittsburgh", neighbors))
neighbors = "Philadelphia", "Norfolk", "Lewisburg", "Pittsburgh"
self.cities.append(City("Baltimore", neighbors))
neighbors = "Norfolk", "Charleston", "Atlanta", "Knoxville", "Lewisburg"
self.cities.append(City("Charlotte", neighbors))
neighbors = "Lewisburg", "Charlotte", "Atlanta", "Nashville", "Cincinnati"
self.cities.append(City("Knoxville", neighbors))
neighbors = "Detroit", "Pittsburgh", "Lewisburg", "Knoxville", "Nashville", "St Louis"
self.cities.append(City("Cincinnati", neighbors))
neighbors = "Detroit", "Cincinnati", "St Louis", "Davenport", "St Paul", "Duluth"
self.cities.append(City("Chicago", neighbors))
neighbors = "Pittsburgh", "Baltimore", "Norfolk", "Charlotte", "Knoxville", "Cincinnati"
self.cities.append(City("Lewisburg", neighbors))
# Atlantic Coast (3 cities)
neighbors = "Philadelphia", "Charleston", "Charlotte", "Lewisburg", "Baltimore"
self.cities.append(City("Norfolk", neighbors))
neighbors = "Norfolk", "Savannah", "Atlanta", "Charlotte"
self.cities.append(City("Charleston", neighbors))
neighbors = "Charleston", "Jacksonville", "Mobile", "Atlanta"
self.cities.append(City("Savannah", neighbors))
# The South (4 cities)
neighbors = "Cincinnati", "Knoxville", "Atlanta", "New Orleans", "Little Rock", "St Louis"
self.cities.append(City("Nashville", neighbors))
neighbors = "Charlotte", "Charleston", "Savannah", "Mobile", "Nashville", "Knoxville"
self.cities.append(City("Atlanta", neighbors))
neighbors = "Atlanta", "Savannah", "Jacksonville", "Tampa", "New Orleans"
self.cities.append(City("Mobile", neighbors))
neighbors = "Nashville", "Mobile", "Houston", "Little Rock"
self.cities.append(City("New Orleans", neighbors))
# Florida (3 cities)
neighbors = "Savannah", "Miami", "Tampa", "Mobile"
self.cities.append(City("Jacksonville", neighbors))
neighbors = "Jacksonville", "Tampa"
self.cities.append(City("Miami", neighbors))
neighbors = "Jacksonville", "Miami", "Mobile"
self.cities.append(City("Tampa", neighbors))
# Great Plains (6 cities)
neighbors = "Chicago", "St Paul", "Fargo", "Winnipeg"
self.cities.append(City("Duluth", neighbors))
neighbors = "Duluth", "Chicago", "Davenport", "Omaha", "Fargo"
self.cities.append(City("St Paul", neighbors))
neighbors = "Chicago", "St Louis", "Kansas City", "Omaha", "St Paul"
self.cities.append(City("Davenport", neighbors))
neighbors = "St Paul", "Davenport", "Kansas City", "Denver", "Cheyenne"
self.cities.append(City("Omaha", neighbors))
neighbors = "Chicago", "Cincinnati", "Nashville", "Little Rock", "Kansas City", "Davenport"
self.cities.append(City("St Louis", neighbors))
neighbors = "Davenport", "St Louis", "Little Rock", "Oklahoma City", "Dodge City", "Omaha"
self.cities.append(City("Kansas City", neighbors))
# Open Range (6 cities)
neighbors = "Kansas City", "Oklahoma City", "Santa Fe", "Denver"
self.cities.append(City("Dodge City", neighbors))
neighbors = "Kansas City", "Little Rock", "Dallas", "Santa Fe", "Dodge City"
self.cities.append(City("Oklahoma City", neighbors))
neighbors = "Nashville", "New Orleans", "Houston", "Dallas", "Oklahoma City", "Kansas City", "St Louis"
self.cities.append(City("Little Rock", neighbors))
neighbors = "Little Rock", "Houston", "San Antonio", "El Paso", "Oklahoma City"
self.cities.append(City("Dallas", neighbors))
neighbors = "Little Rock", "New Orleans", "San Antonio", "Dallas"
self.cities.append(City("Houston", neighbors))
neighbors = "Dallas", "Houston", "Monterrey", "El Paso"
self.cities.append(City("San Antonio", neighbors))
# Badlands (4 cities)
neighbors = "Winnipeg", "Miles City", "Calgary"
self.cities.append(City("Regina", neighbors))
neighbors = "Duluth", "Fargo", "Miles City", "Regina"
self.cities.append(City("Winnipeg", neighbors))
neighbors = "Regina", "Winnipeg", "Fargo", "Cheyenne", "Helena", "Calgary"
self.cities.append(City("Miles City", neighbors))
neighbors = "Duluth", "St Paul", "Cheyenne", "Miles City", "Winnipeg"
self.cities.append(City("Fargo", neighbors))
# Haunted Wastes (5 cities)
neighbors = "Fargo", "Omaha", "Denver", "Salt Lake City", "Helena", "Miles City"
self.cities.append(City("Cheyenne", neighbors))
neighbors = "Helena", "Cheyenne", "Denver", "Phoenix", "Cemetery City", "Portland", "Spokane"
self.cities.append(City("Salt Lake City", neighbors))
neighbors = "Cheyenne", "Omaha", "Dodge City", "Santa Fe", "Phoenix", "Salt Lake City"
self.cities.append(City("Denver", neighbors))
neighbors = "Denver", "Dodge City", "Oklahoma City", "El Paso", "Phoenix"
self.cities.append(City("Santa Fe", neighbors))
neighbors = "Salt Lake City", "Denver", "Santa Fe", "El Paso", "Hermosillo", "Baja", "Nuevos Angeles"
self.cities.append(City("Phoenix", neighbors))
# Sierra Madre (4 cities)
neighbors = "Dallas", "San Antonio", "Monterrey", "Chihuhua", "Hermosillo", "Phoenix", "Santa Fe"
self.cities.append(City("El Paso", neighbors))
neighbors = "El Paso", "Chihuhua", "Baja", "Phoenix"
self.cities.append(City("Hermosillo", neighbors))
neighbors = "El Paso", "Monterrey", "Hermosillo"
self.cities.append(City("Chihuhua", neighbors))
neighbors = "San Antonio", "Chihuhua", "El Paso"
self.cities.append(City("Monterrey", neighbors))
# Cascadia (6 cities)
neighbors = "Calgary", "Spokane", "Seattle"
self.cities.append(City("Vancouver", neighbors))
neighbors = "Regina", "Miles City", "Helena", "Spokane", "Vancouver"
self.cities.append(City("Calgary", neighbors))
neighbors = "Vancouver", "Spokane", "Portland"
self.cities.append(City("Seattle", neighbors))
neighbors = "Seattle", "Spokane", "Salt Lake City", "San Francisco"
self.cities.append(City("Portland", neighbors))
neighbors = "Calgary", "Helena", "Salt Lake City", "Portland", "Seattle", "Vancouver"
self.cities.append(City("Spokane", neighbors))
neighbors = "Miles City", "Cheyenne", "Salt Lake City", "Spokane"
self.cities.append(City("Helena", neighbors))
# California (6 cities)
neighbors = "Cemetery City", "Nuevos Angeles", "San Francisco"
self.cities.append(City("Sacramento", neighbors))
neighbors = "Salt Lake City", "Sacramento"
self.cities.append(City("Cemetery City", neighbors))
neighbors = "Portland", "Sacramento", "Nuevos Angeles", "Pacific Haven"
self.cities.append(City("San Francisco", neighbors))
neighbors = "San Francisco", "Nuevos Angeles"
self.cities.append(City("Pacific Haven", neighbors))
neighbors = "Sacramento", "Phoenix", "Baja", "Pacific Haven", "San Francisco"
self.cities.append(City("Nuevos Angeles", neighbors))
neighbors = "Phoenix", "Hermosillo", "Nuevos Angeles"
self.cities.append(City("Baja", neighbors))
def getCityByName(self, name):
for city in self.cities:
if city.name == name:
return city
print("ERROR: city '" + name + "' is not defined")
return 0
# Returns the length of the shortest way between two cities
def distance(self, start, target):
if start == target:
return 0
dist = 1
cities = [start]
passedCities = []
while True:
for city in cities:
if target.name in city.neighbors:
return dist
passedCities.extend(cities)
newCities = []
for city in cities:
for neighbor in city.neighbors:
if neighbor not in passedCities:
newCities.append(self.getCityByName(neighbor))
cities = newCities
dist = dist + 1
def getNeighbors(self, city, n): # n the degree
if n < 1:
print("ERROR: getNeighbors() needs n > 0")
return False
cities = [city]
i = 1
while n > 0:
result = []
for c in cities:
for neighbor in c.neighbors:
neighbor = self.getCityByName(neighbor)
if self.distance(city, neighbor) == i and neighbor not in result:
result.append(neighbor)
cities = result
n = n - 1
i = i + 1
return result
class MamaOConnell:
def __init__(self):
board = Board() # TODO Board should be a "static" or "constant" class, see what we can do
self.location = board.cities[random.randint(0, len(board.cities) - 1)]
# We need at least 3 accesses to the city, to provide hints through different axes (but even cities with 3 accesses
# can generate a triangle (like Bangor or Tampa), hence the need for 4 accesses)
# It could be more proper to have a dedicated method to check if a city can be the location (using distance?)
# Moreover, due to their location and their corner neighbors, Montreal and Jacksonville can never have enough hints
# TODO Albany & Boston sometimes run in an infinte loop as well
while len(self.location.neighbors) < 4 or self.location.name in ("Montreal", "Jacksonville"):
self.location = board.cities[random.randint(0, len(board.cities) - 1)]
hints = [], [], []
hintsDistance = random.randint(2, 3), random.randint(2, 3), random.randint(2, 3)
while hintsDistance == (2, 2, 2):
hintsDistance = random.randint(2, 3), random.randint(2, 3), random.randint(2, 3)
for h in range(0, 3):
i = -1
while i < hintsDistance[h] - 1:
if i == -1:
# First hop of hint
city = board.getCityByName(self.location.neighbors[random.randint(0, len(self.location.neighbors) - 1)])
if h == 1:
while city in hints[0]:
city = board.getCityByName(self.location.neighbors[random.randint(0, len(self.location.neighbors) - 1)])
if h == 2:
while city in hints[0] or city in hints[1]:
city = board.getCityByName(self.location.neighbors[random.randint(0, len(self.location.neighbors) - 1)])
hints[h].insert(0, city)
i = 0
else:
do = True
city = board.getCityByName(hints[h][i].neighbors[random.randint(0, len(hints[h][i].neighbors) - 1)])
counter = 0
while board.distance(city, self.location) < i + 2 or h >= 1 and city in hints[0] or h == 2 and city in hints[1]:
if counter > len(hints[h][i].neighbors) + 5: # arbitrary failsafe
i = i - 1
do = False
hints[h].pop()
break
city = board.getCityByName(hints[h][i].neighbors[random.randint(0, len(hints[h][i].neighbors) - 1)])
counter = counter + 1
if do:
hints[h].insert(i + 1, city)
i = i + 1
self.hint1 = hints[0][-1], hintsDistance[0]
self.hint2 = hints[1][-1], hintsDistance[1]
self.hint3 = hints[2][-1], hintsDistance[2]
# solver() returns False if criteria are not met (see comments in method), True if they are
def solver(self):
board = Board()
hint1nodes = board.getNeighbors(self.hint1[0], self.hint1[1])
hint2nodes = board.getNeighbors(self.hint2[0], self.hint2[1])
cross = [city for city in hint1nodes if city in hint2nodes]
if len(cross) == 1: # only one city remaining after 2 hints
return False
hint3nodes = board.getNeighbors(self.hint3[0], self.hint3[1])
cross = [city for city in cross if city in hint3nodes]
if len(cross) > 1: # multiple cities remaining after 3 hints
return False
return True

41
src/Player.py Normal file
View File

@ -0,0 +1,41 @@
import random
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()

24
src/TreasureCard.py Normal file
View File

@ -0,0 +1,24 @@
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