Compare commits

...

15 Commits

6 changed files with 553 additions and 146 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
*.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
import os
import random
import jsonpickle
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 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()
from src.Circus import Circus
from src.TreasureCard import TreasureCard
from src.Player import Player, ConcessionCard
from src.MamaOConnell import MamaOConnell
class Game:
# 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
self.year = 1858
self.yearId = 0
self.concession = False
# General data to generate
self.circus = Circus(playersNb)
# Mama O'Connell
self.treasure = TreasureCard()
self.mamaoc = MamaOConnell()
while not self.mamaoc.solver():
self.mamaoc = MamaOConnell()
# Players
self.players = []
@ -122,70 +37,178 @@ class Game:
color = input("Enter player " + str(n + 1) + "'s 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):
print("")
print("---------------------------------------------------------------------")
print("")
print("Ticket to Ride Legacy: Legends of the West")
print("")
print("Campaign - Year: " + str(self.year))
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 pepites: " + str(player.concession.getRemainingPepites()))
print(" Concession card remaining nuggets: " + str(player.concession.getRemainingPepites()))
print("")
if self.circus.state > 0:
print("Circus next sticker color: " + self.circus.getNextColor())
print("")
if self.treasure.state > -1:
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):
savefile = open('./savefile.json', 'w+')
def saveData(self, path):
savefile = open('./' + path, 'w+')
savefile.write(jsonpickle.encode(self))
savefile.close()
def loadData():
with open('./savefile.json', 'r') as savefile:
def loadData(path):
with open('./' + path, 'r') as savefile:
content = savefile.read()
data = jsonpickle.decode(content)
return data
# TESTS
playersNb = 2
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)
# Test of data generation
# myGame = Game()
# myGame.initGame(playersNb)
# myGame.saveData()
# Test of data loading
# myGame = loadData()
# print(myGame.year)
# print(myGame.circus.wagons)
# Test of ConcessionCard
# cc = ConcessionCard()
# userChoice = 3
# print(cc.pepites)
# cc.takeReward(userChoice - 1)
# print(cc.pepites)
# print(cc.taken)
# print("Remaining: " + str(cc.getRemainingPepites()))
# cc.takeReward(userChoice - 1)
# print(cc.pepites)
# print(cc.taken)
# print("Remaining: " + str(cc.getRemainingPepites()))
# Test of Player
myGame = Game()
myGame.initGame(playersNb)
myGame.printStatus()
myGame.treasure.enable()
myGame.printStatus()
myGame.treasure.takeTreasure()
myGame.treasure.takeTreasure()
myGame.treasure.takeTreasure()
myGame.treasure.takeTreasure()
myGame.printStatus()
myGame.treasure.takeTreasure()
myGame.printStatus()
while True:
myGame.printStatus()
command = input("==> ")
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',\
'mama hint 1', 'mama hint 2', 'mama hint 3', 'mama location'):
print("")
print("Available commands:")
print(" Main: 'load', 'save', 'next year', 'exit'")
print(" Stickers: 'take sticker', 'batch sticker'")
print(" Circus: 'enable circus', 'take circus'")
print(" Treasure: 'enable treasure', 'take treasure'")
print(" Concessions: 'enable concession', 'take concession', 'disable concession'")
print(" Mama O'Connell tracking: 'mama hint 1', 'mama hint 2', 'mama hint 3', 'mama location'")
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 'take sticker':
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