From 02d599a2846b4610ac4a952793b9870dac0032d5 Mon Sep 17 00:00:00 2001 From: Tim Arnett Date: Wed, 3 Jul 2024 14:24:50 +0900 Subject: [PATCH] Added random asteroid split option into game settings, updated version, changelog, and test scenario with multiple controllers --- CHANGELOG.md | 6 ++++++ examples/scenario_test_multi.py | 6 ++++-- src/kesslergame/_version.py | 2 +- src/kesslergame/asteroid.py | 13 ++++++++----- src/kesslergame/kessler_game.py | 7 ++++--- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64afa57..61908be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [2.1.7] - 3 July 2024 + +- Added optional Boolean setting `random_ast_splits` for game object instantiation. If `True`, left and right asteroid + child asteroid vectors will be be random within a bounded range about the main child asteroid vector. By default + is set to `False`. + ## [2.1.6] - 2 July 2024 - Changed `stop_if_no_ammo` condition to also check mines. Also changed condition such that sum of mines and bullets diff --git a/examples/scenario_test_multi.py b/examples/scenario_test_multi.py index 864135e..850792d 100644 --- a/examples/scenario_test_multi.py +++ b/examples/scenario_test_multi.py @@ -20,14 +20,16 @@ map_size=(1000, 800), time_limit=60, ammo_limit_multiplier=0, - stop_if_no_ammo=False) + stop_if_no_ammo=False, + seed=8) # Define Game Settings game_settings = {'perf_tracker': True, 'graphics_type': GraphicsType.Tkinter, 'realtime_multiplier': 1, 'graphics_obj': None, - 'frequency': 30} + 'frequency': 60, + 'random_ast_splits': True} game = KesslerGame(settings=game_settings) # Use this to visualize the game scenario # game = TrainerEnvironment(settings=game_settings) # Use this for max-speed, no-graphics simulation diff --git a/src/kesslergame/_version.py b/src/kesslergame/_version.py index 5fef1ff..e1ae982 100644 --- a/src/kesslergame/_version.py +++ b/src/kesslergame/_version.py @@ -1 +1 @@ -__version__ = '2.1.6' \ No newline at end of file +__version__ = '2.1.7' \ No newline at end of file diff --git a/src/kesslergame/asteroid.py b/src/kesslergame/asteroid.py index a75376d..acdc277 100644 --- a/src/kesslergame/asteroid.py +++ b/src/kesslergame/asteroid.py @@ -86,7 +86,7 @@ def update(self, delta_time: float = 1/30) -> None: self.position = (self.position[0] + self.velocity[0] * delta_time, self.position[1] + self.velocity[1] * delta_time) self.angle += delta_time * self.turnrate - def destruct(self, impactor: Union['Bullet', 'Mine', 'Ship']) -> list['Asteroid']: + def destruct(self, impactor: Union['Bullet', 'Mine', 'Ship'], random_ast_split: bool) -> list['Asteroid']: """ Spawn child asteroids""" if self.size != 1: @@ -107,7 +107,7 @@ def destruct(self, impactor: Union['Bullet', 'Mine', 'Ship']) -> list['Asteroid' v = math.sqrt(vfx*vfx + vfy*vfy) # Split angle is the angle off of the new velocity vector for the two asteroids to the sides, the center child # asteroid continues on the new velocity path - split_angle = 15.0 + split_angle_bound = 30.0 else: vfx = self.vx vfy = self.vy @@ -117,7 +117,7 @@ def destruct(self, impactor: Union['Bullet', 'Mine', 'Ship']) -> list['Asteroid' v = math.sqrt(vfx*vfx + vfy*vfy + a*a) # Split angle is the angle off of the new velocity vector for the two asteroids to the sides, the center child # asteroid continues on the new velocity path - split_angle = 120.0 + split_angle_bound = 120.0 else: # Calculating new velocity vector of asteroid children based on bullet-asteroid collision/momentum # Currently collisions are considered perfectly inelastic i.e. the bullet is absorbed by the asteroid @@ -135,10 +135,13 @@ def destruct(self, impactor: Union['Bullet', 'Mine', 'Ship']) -> list['Asteroid' v = math.sqrt(vfx*vfx + vfy*vfy) # Split angle is the angle off of the new velocity vector for the two asteroids to the sides, the center child # asteroid continues on the new velocity path - split_angle = 15.0 + split_angle_bound = 30.0 # Calculate angle of center asteroid for split (degrees) theta = math.degrees(math.atan2(vfy, vfx)) - angles = [theta + split_angle, theta, theta - split_angle] + if random_ast_split: + angles = [theta + split_angle_bound*random.random(), theta, theta - split_angle_bound*random.random()] + else: + angles = [theta + split_angle_bound/2.0, theta, theta - split_angle_bound/2.0] return [Asteroid(position=self.position, size=self.size-1, speed=v, angle=angle) for angle in angles] diff --git a/src/kesslergame/kessler_game.py b/src/kesslergame/kessler_game.py index 6f38cf1..957cb21 100644 --- a/src/kesslergame/kessler_game.py +++ b/src/kesslergame/kessler_game.py @@ -54,6 +54,7 @@ def __init__(self, settings: Optional[Dict[str, Any]] = None) -> None: self.graphics_obj: Optional[KesslerGraphics] = settings.get("graphics_obj", None) self.realtime_multiplier: float = settings.get("realtime_multiplier", 0 if self.graphics_type==GraphicsType.NoGraphics else 1) self.time_limit: float = settings.get("time_limit", float("inf")) + self.random_ast_splits = settings.get("random_ast_splits", False) # UI settings default_ui = {'ships': True, 'lives_remaining': True, 'accuracy': True, @@ -209,7 +210,7 @@ def run(self, scenario: Scenario, controllers: List[KesslerController]) -> Tuple bullet.destruct() bullet_remove_idxs.append(idx_bul) # Asteroid destruct function and mark for removal - asteroids.extend(asteroid.destruct(impactor=bullet)) + asteroids.extend(asteroid.destruct(impactor=bullet, random_ast_split=self.random_ast_splits)) asteroid_remove_idxs.add(idx_ast) # Stop checking this bullet break @@ -230,7 +231,7 @@ def run(self, scenario: Scenario, controllers: List[KesslerController]) -> Tuple if dx * dx + dy * dy <= radius_sum * radius_sum: mine.owner.asteroids_hit += 1 mine.owner.mines_hit += 1 - new_asteroids.extend(asteroid.destruct(impactor=mine)) + new_asteroids.extend(asteroid.destruct(impactor=mine, random_ast_split=self.random_ast_splits)) asteroid_remove_idxs.add(idx_ast) for ship in liveships: if not ship.is_respawning: @@ -263,7 +264,7 @@ def run(self, scenario: Scenario, controllers: List[KesslerController]) -> Tuple # Most of the time no collision occurs, so use early exit to optimize collision check if abs(dx) <= radius_sum and abs(dy) <= radius_sum and dx * dx + dy * dy <= radius_sum * radius_sum: # Asteroid destruct function and mark for removal - asteroids.extend(asteroid.destruct(impactor=ship)) + asteroids.extend(asteroid.destruct(impactor=ship, random_ast_split=self.random_ast_splits)) asteroid_remove_idxs.add(idx_ast) # Ship destruct function. Add one to asteroids_hit ship.asteroids_hit += 1