From 09e8c6cd05f7499d65d44e3255fe7b83f464244d Mon Sep 17 00:00:00 2001 From: Masha Shtivelberg Date: Wed, 31 Jul 2024 13:46:04 +0300 Subject: [PATCH] Introduce the --seed flag This patch makes each map generated from a random seed, the length of the seed is controllerd by config.seed_length. Since each run is seeded, it is possible to use a custom seed to start the server, using: ``` $ ./rose-server --seed ``` This makes every run reproducible for debugging and testing. Co-authored-by: Masha Shtivelberg Co-authored-by: Zoe Sasportas --- rose/common/config.py | 1 + rose/common/obstacles.py | 6 ------ rose/server/game.py | 9 +++++---- rose/server/main.py | 23 ++++++++++++++++++++++- rose/server/track.py | 12 ++++++++---- 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/rose/common/config.py b/rose/common/config.py index 613a2459..0d2a90b5 100644 --- a/rose/common/config.py +++ b/rose/common/config.py @@ -11,6 +11,7 @@ game_duration = 60 number_of_cars = 4 is_track_random = True +seed_length = 5 # Matrix diff --git a/rose/common/obstacles.py b/rose/common/obstacles.py index 3b84f9ca..9c28a0af 100644 --- a/rose/common/obstacles.py +++ b/rose/common/obstacles.py @@ -1,7 +1,5 @@ """ Game obstacles """ -import random - NONE = "" # NOQA CRACK = "crack" # NOQA TRASH = "trash" # NOQA @@ -11,7 +9,3 @@ BARRIER = "barrier" # NOQA ALL = (NONE, CRACK, TRASH, PENGUIN, BIKE, WATER, BARRIER) - - -def get_random_obstacle(): - return random.choice(ALL) diff --git a/rose/server/game.py b/rose/server/game.py index 9c72daa1..a97e3895 100644 --- a/rose/server/game.py +++ b/rose/server/game.py @@ -17,9 +17,10 @@ class Game(object): Implements the server for the car race """ - def __init__(self): + def __init__(self, seed): self.hub = None - self.track = track.Track() + self.rng = random.Random(seed) + self.track = track.Track(seed=seed) self.looper = task.LoopingCall(self.loop) self.players = {} self.free_cars = set(range(config.number_of_cars)) @@ -68,9 +69,9 @@ def add_player(self, name): raise error.PlayerExists(name) if not self.free_cars: raise error.TooManyPlayers() - car = random.choice(tuple(self.free_cars)) + car = self.rng.choice(tuple(self.free_cars)) self.free_cars.remove(car) - lane = random.choice(tuple(self.free_lanes)) + lane = self.rng.choice(tuple(self.free_lanes)) self.free_lanes.remove(lane) log.info("add player: %r, lane: %r, car: %r", name, lane, car) self.players[name] = player.Player(name, car, lane) diff --git a/rose/server/main.py b/rose/server/main.py index 1f8774ba..f5ce2334 100644 --- a/rose/server/main.py +++ b/rose/server/main.py @@ -1,6 +1,8 @@ +import random import socket import logging import argparse +import string from twisted.internet import reactor from twisted.web import server, static @@ -25,6 +27,13 @@ def main(): help="Definition of driver tracks: random or same." "If not specified, random will be used.", ) + parser.add_argument( + "--seed", + "-s", + dest="seed", + default="", + help="Optional, use a custom seed for the map generation", + ) args = parser.parse_args() """ @@ -37,8 +46,16 @@ def main(): else: config.is_track_random = True + if args.seed: + seed = args.seed + else: + seed = generate_seed(config.seed_length) + + log.info(f"Seed for map: {seed}") + g = game.Game(seed=seed) + log.info("starting server") - g = game.Game() + h = net.Hub(g) reactor.listenTCP(config.game_port, net.PlayerFactory(h)) root = static.File(config.web_root) @@ -51,3 +68,7 @@ def main(): site = server.Site(root) reactor.listenTCP(config.web_port, site) reactor.run() + + +def generate_seed(seed_length=5): + return "".join(random.choice(string.ascii_lowercase) for _ in range(seed_length)) diff --git a/rose/server/track.py b/rose/server/track.py index 308283db..fec91f04 100644 --- a/rose/server/track.py +++ b/rose/server/track.py @@ -3,8 +3,9 @@ class Track(object): - def __init__(self): + def __init__(self, seed=""): self._matrix = None + self.rng = random.Random(seed) self.reset() # Game state interface @@ -53,15 +54,18 @@ def _generate_row(self): Otherwise, the tracks will be identical. """ row = [obstacles.NONE] * config.matrix_width - obstacle = obstacles.get_random_obstacle() + obstacle = self.get_random_obstacle() if config.is_track_random: for lane in range(config.max_players): low = lane * config.cells_per_player high = low + config.cells_per_player - cell = random.choice(range(low, high)) + cell = self.rng.choice(range(low, high)) row[cell] = obstacle else: - cell = random.choice(range(0, config.cells_per_player)) + cell = self.rng.choice(range(0, config.cells_per_player)) for lane in range(config.max_players): row[cell + lane * config.cells_per_player] = obstacle return row + + def get_random_obstacle(self): + return self.rng.choice(obstacles.ALL)