mirror of
https://github.com/csd4ni3l/fleet-commander.git
synced 2026-01-01 04:23:47 +01:00
add RL training which doesnt work that wall yet, and start to make UI for model training
This commit is contained in:
43
game/play.py
43
game/play.py
@@ -3,6 +3,8 @@ import arcade, arcade.gui, random, time
|
|||||||
from utils.constants import button_style, ENEMY_ROWS, ENEMY_COLS, PLAYER_ATTACK_SPEED
|
from utils.constants import button_style, ENEMY_ROWS, ENEMY_COLS, PLAYER_ATTACK_SPEED
|
||||||
from utils.preload import button_texture, button_hovered_texture
|
from utils.preload import button_texture, button_hovered_texture
|
||||||
|
|
||||||
|
from stable_baselines3 import PPO
|
||||||
|
|
||||||
from game.sprites import Enemy, Player, Bullet
|
from game.sprites import Enemy, Player, Bullet
|
||||||
|
|
||||||
class Game(arcade.gui.UIView):
|
class Game(arcade.gui.UIView):
|
||||||
@@ -16,19 +18,29 @@ class Game(arcade.gui.UIView):
|
|||||||
|
|
||||||
self.spritelist = arcade.SpriteList()
|
self.spritelist = arcade.SpriteList()
|
||||||
|
|
||||||
self.player = Player(100, 100) # not actually player
|
self.player = Player(self.window.width / 2 + random.randint(int(-self.window.width / 3), int(self.window.width / 3)), 100) # not actually player
|
||||||
self.spritelist.append(self.player)
|
self.spritelist.append(self.player)
|
||||||
|
|
||||||
self.last_player_shoot = time.perf_counter() # not actually player
|
self.last_player_shoot = time.perf_counter() # not actually player
|
||||||
|
|
||||||
|
self.model = PPO.load("invader_agent.zip")
|
||||||
|
|
||||||
self.enemies: list[Enemy] = []
|
self.enemies: list[Enemy] = []
|
||||||
self.bullets: list[Bullet] = []
|
self.player_bullets: list[Bullet] = []
|
||||||
|
self.enemy_bullets: list[Bullet] = []
|
||||||
|
|
||||||
self.summon_enemies()
|
self.summon_enemies()
|
||||||
|
|
||||||
def on_show_view(self):
|
def on_show_view(self):
|
||||||
super().on_show_view()
|
super().on_show_view()
|
||||||
|
|
||||||
|
self.back_button = self.anchor.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50), anchor_x="left", anchor_y="top")
|
||||||
|
self.back_button.on_click = lambda event: self.main_exit()
|
||||||
|
|
||||||
|
def main_exit(self):
|
||||||
|
from menus.main import Main
|
||||||
|
self.window.show_view(Main(self.pypresence_client))
|
||||||
|
|
||||||
def summon_enemies(self):
|
def summon_enemies(self):
|
||||||
enemy_start_x = self.window.width * 0.15
|
enemy_start_x = self.window.width * 0.15
|
||||||
enemy_start_y = self.window.height * 0.9
|
enemy_start_y = self.window.height * 0.9
|
||||||
@@ -45,7 +57,7 @@ class Game(arcade.gui.UIView):
|
|||||||
|
|
||||||
bullets_to_remove = []
|
bullets_to_remove = []
|
||||||
|
|
||||||
for bullet in self.bullets:
|
for bullet in self.player_bullets + self.enemy_bullets:
|
||||||
bullet.update()
|
bullet.update()
|
||||||
|
|
||||||
bullet_hit = False
|
bullet_hit = False
|
||||||
@@ -68,9 +80,22 @@ class Game(arcade.gui.UIView):
|
|||||||
|
|
||||||
for bullet_to_remove in bullets_to_remove:
|
for bullet_to_remove in bullets_to_remove:
|
||||||
self.spritelist.remove(bullet_to_remove)
|
self.spritelist.remove(bullet_to_remove)
|
||||||
self.bullets.remove(bullet_to_remove)
|
|
||||||
|
|
||||||
self.player.update(self.enemies) # not actually player
|
if bullet_to_remove in self.enemy_bullets:
|
||||||
|
self.enemy_bullets.remove(bullet_to_remove)
|
||||||
|
elif bullet_to_remove in self.player_bullets:
|
||||||
|
self.player_bullets.remove(bullet_to_remove)
|
||||||
|
|
||||||
|
self.player.update(self.model, self.enemies, self.enemy_bullets, self.window.width, self.window.height) # not actually player
|
||||||
|
|
||||||
|
if self.player.center_x > self.window.width:
|
||||||
|
self.player.center_x = self.window.width
|
||||||
|
elif self.player.center_x < 0:
|
||||||
|
self.player.center_x = 0
|
||||||
|
|
||||||
|
if self.player.shoot:
|
||||||
|
self.player.shoot = False
|
||||||
|
self.shoot(self.player.center_x, self.player.center_y, 1)
|
||||||
|
|
||||||
if time.perf_counter() - self.last_player_shoot >= PLAYER_ATTACK_SPEED:
|
if time.perf_counter() - self.last_player_shoot >= PLAYER_ATTACK_SPEED:
|
||||||
self.last_player_shoot = time.perf_counter()
|
self.last_player_shoot = time.perf_counter()
|
||||||
@@ -79,7 +104,13 @@ class Game(arcade.gui.UIView):
|
|||||||
def shoot(self, x, y, direction_y):
|
def shoot(self, x, y, direction_y):
|
||||||
bullet = Bullet(x, y, direction_y)
|
bullet = Bullet(x, y, direction_y)
|
||||||
self.spritelist.append(bullet)
|
self.spritelist.append(bullet)
|
||||||
self.bullets.append(bullet)
|
|
||||||
|
if direction_y == 1:
|
||||||
|
bullets = self.player_bullets
|
||||||
|
else:
|
||||||
|
bullets = self.enemy_bullets
|
||||||
|
|
||||||
|
bullets.append(bullet)
|
||||||
|
|
||||||
def on_key_press(self, symbol, modifiers):
|
def on_key_press(self, symbol, modifiers):
|
||||||
if symbol == arcade.key.SPACE:
|
if symbol == arcade.key.SPACE:
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import arcade, random, time
|
import arcade, time
|
||||||
|
|
||||||
from utils.constants import PLAYER_SPEED, BULLET_SPEED, BULLET_RADIUS
|
from stable_baselines3 import PPO
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from utils.constants import PLAYER_SPEED, BULLET_SPEED, BULLET_RADIUS, PLAYER_ATTACK_SPEED, ENEMY_COLS, ENEMY_ROWS
|
||||||
from utils.preload import player_texture, enemy_texture
|
from utils.preload import player_texture, enemy_texture
|
||||||
|
|
||||||
class Bullet(arcade.Sprite):
|
class Bullet(arcade.Sprite):
|
||||||
@@ -21,17 +25,55 @@ class Player(arcade.Sprite): # Not actually the player
|
|||||||
super().__init__(player_texture, center_x=x, center_y=y)
|
super().__init__(player_texture, center_x=x, center_y=y)
|
||||||
|
|
||||||
self.last_target_change = time.perf_counter()
|
self.last_target_change = time.perf_counter()
|
||||||
|
self.last_shoot = time.perf_counter()
|
||||||
self.target = None
|
self.target = None
|
||||||
|
self.shoot = False
|
||||||
|
|
||||||
def update(self, enemies):
|
self.player_speed = 0
|
||||||
if not enemies:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not self.target or time.perf_counter() - self.last_target_change >= 1:
|
def update(self, model: PPO, enemies, bullets, width, height):
|
||||||
self.last_target_change = time.perf_counter()
|
if enemies:
|
||||||
self.target = random.choice(enemies)
|
nearest_enemy = min(enemies, key=lambda e: abs(e.center_y - self.center_y) + abs(e.center_x - self.center_x))
|
||||||
|
enemy_x = (nearest_enemy.center_x - self.center_x) / width
|
||||||
|
enemy_y = (nearest_enemy.center_y - self.center_y) / height
|
||||||
|
else:
|
||||||
|
enemy_x = 2
|
||||||
|
enemy_y = 2
|
||||||
|
|
||||||
if self.target.center_x > self.center_x:
|
enemy_count = len(enemies) / float(max(1, ENEMY_ROWS * ENEMY_COLS))
|
||||||
self.center_x += PLAYER_SPEED
|
player_x_norm = self.center_x / width
|
||||||
elif self.target.center_x < self.center_x:
|
|
||||||
|
curr_bullet = min(bullets, key=lambda b: abs(b.center_x - self.center_x) + abs(b.center_y - self.center_y)) if bullets else None
|
||||||
|
if curr_bullet is not None:
|
||||||
|
curr_bx = (curr_bullet.center_x - self.center_x) / float(width)
|
||||||
|
curr_by = (curr_bullet.center_y - self.center_y) / float(height)
|
||||||
|
else:
|
||||||
|
curr_bx = 2.0
|
||||||
|
curr_by = 2.0
|
||||||
|
|
||||||
|
lowest = max(enemies, key=lambda e: e.center_y) if enemies else None
|
||||||
|
if lowest is not None:
|
||||||
|
lowest_dy = (lowest.center_y - self.center_y) / float(height)
|
||||||
|
else:
|
||||||
|
lowest_dy = 2.0
|
||||||
|
|
||||||
|
enemy_dispersion = 0.0
|
||||||
|
if enemies:
|
||||||
|
xs = np.array([e.center_x for e in enemies], dtype=np.float32)
|
||||||
|
enemy_dispersion = float(xs.std()) / float(width)
|
||||||
|
|
||||||
|
obs = np.array([player_x_norm, enemy_x, enemy_y, lowest_dy, curr_bx, curr_by, self.player_speed, enemy_count, enemy_dispersion], dtype=np.float32)
|
||||||
|
action, _ = model.predict(obs, deterministic=True)
|
||||||
|
|
||||||
|
self.prev_x = self.center_x
|
||||||
|
if action == 0:
|
||||||
self.center_x -= PLAYER_SPEED
|
self.center_x -= PLAYER_SPEED
|
||||||
|
elif action == 1:
|
||||||
|
self.center_x += PLAYER_SPEED
|
||||||
|
elif action == 2:
|
||||||
|
t = time.perf_counter()
|
||||||
|
if t - self.last_shoot >= PLAYER_ATTACK_SPEED:
|
||||||
|
self.last_shoot = t
|
||||||
|
self.shoot = True
|
||||||
|
|
||||||
|
self.player_speed = (self.center_x - self.prev_x) / max(1e-6, PLAYER_SPEED)
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ class Main(arcade.gui.UIView):
|
|||||||
self.play_button = self.box.add(arcade.gui.UITextureButton(text="Play", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=150, style=big_button_style))
|
self.play_button = self.box.add(arcade.gui.UITextureButton(text="Play", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=150, style=big_button_style))
|
||||||
self.play_button.on_click = lambda event: self.play()
|
self.play_button.on_click = lambda event: self.play()
|
||||||
|
|
||||||
|
self.train_button = self.box.add(arcade.gui.UITextureButton(text="Train", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=150, style=big_button_style))
|
||||||
|
self.train_button.on_click = lambda event: self.train()
|
||||||
|
|
||||||
self.settings_button = self.box.add(arcade.gui.UITextureButton(text="Settings", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=150, style=big_button_style))
|
self.settings_button = self.box.add(arcade.gui.UITextureButton(text="Settings", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=150, style=big_button_style))
|
||||||
self.settings_button.on_click = lambda event: self.settings()
|
self.settings_button.on_click = lambda event: self.settings()
|
||||||
|
|
||||||
|
|||||||
60
menus/train_model.py
Normal file
60
menus/train_model.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import arcade, arcade.gui
|
||||||
|
|
||||||
|
from utils.constants import button_style, MODEL_SETTINGS
|
||||||
|
from utils.preload import button_texture, button_hovered_texture
|
||||||
|
|
||||||
|
from stable_baselines3 import PPO
|
||||||
|
from utils.ml import SpaceInvadersEnv
|
||||||
|
|
||||||
|
class TrainModel(arcade.gui.UIView):
|
||||||
|
def __init__(self, pypresence_client):
|
||||||
|
self.pypresence_client = pypresence_client
|
||||||
|
self.pypresence_client.update(state="Model Training")
|
||||||
|
|
||||||
|
self.current_state = "settings"
|
||||||
|
|
||||||
|
self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
|
||||||
|
self.box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=10))
|
||||||
|
|
||||||
|
self.settings = MODEL_SETTINGS.copy()
|
||||||
|
|
||||||
|
def on_show_view(self):
|
||||||
|
super().on_show_view()
|
||||||
|
|
||||||
|
self.show_menu(self.current_state)
|
||||||
|
|
||||||
|
def show_menu(self, state):
|
||||||
|
if state == "settings":
|
||||||
|
self.box.add(arcade.gui.UILabel("Settings", font_size=48))
|
||||||
|
|
||||||
|
for setting, data in MODEL_SETTINGS:
|
||||||
|
default, min_value, max_value, step = data
|
||||||
|
self.box.add(arcade.gui.UILabel(text=f"{setting.replace('_', ' ').capitalize()}: {default}"))
|
||||||
|
|
||||||
|
slider = self.box.add(arcade.gui.UISlider(value=default, min_value=min_value, max_value=max_value, step=step))
|
||||||
|
slider._render_steps = lambda surface: None
|
||||||
|
slider.on_change = lambda e, key=setting: self.change_value(key, e.new_value)
|
||||||
|
|
||||||
|
train_button = self.box.add(arcade.gui.UITextureButton(width=self.window.width / 2, height=self.window.height / 10, text="Train", style=button_style, texture=button_texture, texture_hovered=button_hovered_texture))
|
||||||
|
train_button.on_click = lambda e: self.train()
|
||||||
|
|
||||||
|
def change_value(self, key, value):
|
||||||
|
...
|
||||||
|
|
||||||
|
def train(self):
|
||||||
|
env = SpaceInvadersEnv()
|
||||||
|
model = PPO(
|
||||||
|
"MlpPolicy",
|
||||||
|
env,
|
||||||
|
n_steps=2048,
|
||||||
|
batch_size=64,
|
||||||
|
n_epochs=10,
|
||||||
|
learning_rate=3e-4,
|
||||||
|
verbose=1,
|
||||||
|
device="cpu",
|
||||||
|
gamma=0.99,
|
||||||
|
ent_coef=0.01,
|
||||||
|
clip_range=0.2
|
||||||
|
)
|
||||||
|
model.learn(1_000_000)
|
||||||
|
model.save("invader_agent")
|
||||||
@@ -6,5 +6,7 @@ readme = "README.md"
|
|||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arcade>=3.3.3",
|
"arcade>=3.3.3",
|
||||||
|
"gymnasium>=1.2.2",
|
||||||
|
"numpy>=2.3.4",
|
||||||
"pypresence>=4.3.0",
|
"pypresence>=4.3.0",
|
||||||
]
|
]
|
||||||
|
|||||||
20
train.py
Normal file
20
train.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
from stable_baselines3 import PPO
|
||||||
|
from utils.ml import SpaceInvadersEnv
|
||||||
|
|
||||||
|
env = SpaceInvadersEnv()
|
||||||
|
model = PPO(
|
||||||
|
"MlpPolicy",
|
||||||
|
env,
|
||||||
|
n_steps=2048,
|
||||||
|
batch_size=64,
|
||||||
|
n_epochs=10,
|
||||||
|
learning_rate=3e-4,
|
||||||
|
verbose=1,
|
||||||
|
device="cpu",
|
||||||
|
gamma=0.99,
|
||||||
|
ent_coef=0.02,
|
||||||
|
clip_range=0.2,
|
||||||
|
gae_lambda=0.95
|
||||||
|
)
|
||||||
|
model.learn(1_000_000)
|
||||||
|
model.save("invader_agent")
|
||||||
@@ -12,6 +12,16 @@ PLAYER_ATTACK_SPEED = 0.75
|
|||||||
BULLET_SPEED = 3
|
BULLET_SPEED = 3
|
||||||
BULLET_RADIUS = 10
|
BULLET_RADIUS = 10
|
||||||
|
|
||||||
|
MODEL_SETTINGS = {
|
||||||
|
"n_steps": [2048, 256, 8192, 256],
|
||||||
|
"batch_size": 64,
|
||||||
|
"n_epochs": 10,
|
||||||
|
"learning_rate": 3e-4,
|
||||||
|
"gamma": 0.99,
|
||||||
|
"ent_coef": 0.01,
|
||||||
|
"clip_range": 0.2
|
||||||
|
}
|
||||||
|
|
||||||
menu_background_color = (30, 30, 47)
|
menu_background_color = (30, 30, 47)
|
||||||
log_dir = 'logs'
|
log_dir = 'logs'
|
||||||
discord_presence_id = 1438214877343907881
|
discord_presence_id = 1438214877343907881
|
||||||
|
|||||||
221
utils/ml.py
Normal file
221
utils/ml.py
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
import gymnasium as gym
|
||||||
|
import numpy as np
|
||||||
|
import arcade
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
|
||||||
|
from game.sprites import Enemy, Player, Bullet
|
||||||
|
from utils.constants import PLAYER_SPEED, BULLET_SPEED, PLAYER_ATTACK_SPEED, ENEMY_ROWS, ENEMY_COLS
|
||||||
|
|
||||||
|
class SpaceInvadersEnv(gym.Env):
|
||||||
|
def __init__(self, width=800, height=600):
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
|
||||||
|
self.action_space = gym.spaces.Discrete(3)
|
||||||
|
self.observation_space = gym.spaces.Box(low=-10.0, high=10.0, shape=(9,), dtype=np.float32)
|
||||||
|
|
||||||
|
self.enemies = []
|
||||||
|
self.bullets = []
|
||||||
|
self.dir_history = []
|
||||||
|
self.last_shot = 0.0
|
||||||
|
self.player = None
|
||||||
|
self.prev_x = 0.0
|
||||||
|
self.player_speed = 0.0
|
||||||
|
self.prev_bx = 2.0
|
||||||
|
self.steps_since_direction_change = 0
|
||||||
|
self.last_direction = 0
|
||||||
|
|
||||||
|
def reset(self, seed=None, options=None):
|
||||||
|
self.enemies = []
|
||||||
|
self.bullets = []
|
||||||
|
self.dir_history = []
|
||||||
|
self.player = Player(self.width / 2 + random.randint(int(-self.width / 3), int(self.width / 3)), 100)
|
||||||
|
self.prev_x = self.player.center_x
|
||||||
|
self.player_speed = 0.0
|
||||||
|
self.prev_bx = 2.0
|
||||||
|
self.steps_since_direction_change = 0
|
||||||
|
self.last_direction = 0
|
||||||
|
|
||||||
|
start_x = self.width * 0.15
|
||||||
|
start_y = self.height * 0.9
|
||||||
|
|
||||||
|
for r in range(ENEMY_ROWS):
|
||||||
|
for c in range(ENEMY_COLS):
|
||||||
|
e = Enemy(start_x + c * 100, start_y - r * 100)
|
||||||
|
self.enemies.append(e)
|
||||||
|
|
||||||
|
self.last_shot = time.perf_counter()
|
||||||
|
return self._obs(), {}
|
||||||
|
|
||||||
|
def _nearest_enemy(self):
|
||||||
|
if not self.enemies:
|
||||||
|
return None
|
||||||
|
return min(self.enemies, key=lambda e: abs(e.center_y - self.player.center_y) + abs(e.center_x - self.player.center_x))
|
||||||
|
|
||||||
|
def _lowest_enemy(self):
|
||||||
|
if not self.enemies:
|
||||||
|
return None
|
||||||
|
return max(self.enemies, key=lambda e: e.center_y)
|
||||||
|
|
||||||
|
def _nearest_enemy_bullet(self):
|
||||||
|
enemy_bullets = [b for b in self.bullets if b.direction_y == -1]
|
||||||
|
if not enemy_bullets:
|
||||||
|
return None
|
||||||
|
return min(enemy_bullets, key=lambda b: abs(b.center_x - self.player.center_x) + abs(b.center_y - self.player.center_y))
|
||||||
|
|
||||||
|
def _obs(self):
|
||||||
|
if self.enemies:
|
||||||
|
nearest = self._nearest_enemy()
|
||||||
|
enemy_x = (nearest.center_x - self.player.center_x) / float(self.width)
|
||||||
|
enemy_y = (nearest.center_y - self.player.center_y) / float(self.height)
|
||||||
|
else:
|
||||||
|
enemy_x = 2.0
|
||||||
|
enemy_y = 2.0
|
||||||
|
|
||||||
|
lowest = self._lowest_enemy()
|
||||||
|
|
||||||
|
if lowest is not None:
|
||||||
|
lowest_dy = (lowest.center_y - self.player.center_y) / float(self.height)
|
||||||
|
else:
|
||||||
|
lowest_dy = 2.0
|
||||||
|
|
||||||
|
nb = self._nearest_enemy_bullet()
|
||||||
|
if nb is not None:
|
||||||
|
bx = (nb.center_x - self.player.center_x) / float(self.width)
|
||||||
|
by = (nb.center_y - self.player.center_y) / float(self.height)
|
||||||
|
else:
|
||||||
|
bx = 2.0
|
||||||
|
by = 2.0
|
||||||
|
|
||||||
|
enemy_count = len(self.enemies) / float(max(1, ENEMY_ROWS * ENEMY_COLS))
|
||||||
|
player_x_norm = self.player.center_x / float(self.width)
|
||||||
|
enemy_dispersion = 0.0
|
||||||
|
|
||||||
|
if self.enemies:
|
||||||
|
xs = np.array([e.center_x for e in self.enemies], dtype=np.float32)
|
||||||
|
enemy_dispersion = float(xs.std()) / float(self.width)
|
||||||
|
|
||||||
|
obs = np.array([player_x_norm, enemy_x, enemy_y, lowest_dy, bx, by, self.player_speed, enemy_count, enemy_dispersion], dtype=np.float32)
|
||||||
|
return obs
|
||||||
|
|
||||||
|
def step(self, action):
|
||||||
|
reward = 0.0
|
||||||
|
terminated = False
|
||||||
|
truncated = False
|
||||||
|
|
||||||
|
nearest = self._nearest_enemy()
|
||||||
|
if nearest is not None:
|
||||||
|
enemy_x = (nearest.center_x - self.player.center_x) / float(self.width)
|
||||||
|
else:
|
||||||
|
enemy_x = 2.0
|
||||||
|
|
||||||
|
prev_bullet = self._nearest_enemy_bullet()
|
||||||
|
if prev_bullet is not None:
|
||||||
|
prev_bx = (prev_bullet.center_x - self.player.center_x) / float(self.width)
|
||||||
|
else:
|
||||||
|
prev_bx = 2.0
|
||||||
|
|
||||||
|
prev_x = self.player.center_x
|
||||||
|
current_action_dir = 0
|
||||||
|
|
||||||
|
if action == 0:
|
||||||
|
self.player.center_x -= PLAYER_SPEED
|
||||||
|
current_action_dir = -1
|
||||||
|
elif action == 1:
|
||||||
|
self.player.center_x += PLAYER_SPEED
|
||||||
|
current_action_dir = 1
|
||||||
|
elif action == 2:
|
||||||
|
t = time.perf_counter()
|
||||||
|
if t - self.last_shot >= PLAYER_ATTACK_SPEED:
|
||||||
|
self.last_shot = t
|
||||||
|
b = Bullet(self.player.center_x, self.player.center_y, 1)
|
||||||
|
self.bullets.append(b)
|
||||||
|
if enemy_x != 2.0 and abs(enemy_x) < 0.04:
|
||||||
|
reward += 8.0
|
||||||
|
elif enemy_x != 2.0 and abs(enemy_x) < 0.1:
|
||||||
|
reward += 3.0
|
||||||
|
|
||||||
|
if self.player.center_x > self.width:
|
||||||
|
self.player.center_x = self.width
|
||||||
|
elif self.player.center_x < 0:
|
||||||
|
self.player.center_x = 0
|
||||||
|
|
||||||
|
self.player_speed = (self.player.center_x - prev_x) / max(1e-6, PLAYER_SPEED)
|
||||||
|
|
||||||
|
if current_action_dir != 0:
|
||||||
|
if self.last_direction != 0 and current_action_dir != self.last_direction:
|
||||||
|
if self.steps_since_direction_change < 8:
|
||||||
|
reward -= 3.0
|
||||||
|
self.steps_since_direction_change = 0
|
||||||
|
else:
|
||||||
|
self.steps_since_direction_change += 1
|
||||||
|
self.last_direction = current_action_dir
|
||||||
|
|
||||||
|
if enemy_x != 2.0:
|
||||||
|
if abs(enemy_x) < 0.03:
|
||||||
|
reward += 3.0
|
||||||
|
elif abs(enemy_x) < 0.08:
|
||||||
|
reward += 1.0
|
||||||
|
|
||||||
|
for b in list(self.bullets):
|
||||||
|
b.center_y += b.direction_y * BULLET_SPEED
|
||||||
|
if b.center_y > self.height or b.center_y < 0:
|
||||||
|
try:
|
||||||
|
self.bullets.remove(b)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for b in list(self.bullets):
|
||||||
|
if b.direction_y == 1:
|
||||||
|
for e in list(self.enemies):
|
||||||
|
if arcade.check_for_collision(b, e):
|
||||||
|
try:
|
||||||
|
self.enemies.remove(e)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
self.bullets.remove(b)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
reward += 25.0
|
||||||
|
break
|
||||||
|
|
||||||
|
for b in list(self.bullets):
|
||||||
|
if b.direction_y == -1:
|
||||||
|
if arcade.check_for_collision(b, self.player):
|
||||||
|
try:
|
||||||
|
self.bullets.remove(b)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
reward -= 100.0
|
||||||
|
terminated = True
|
||||||
|
|
||||||
|
if not self.enemies:
|
||||||
|
reward += 200.0
|
||||||
|
terminated = True
|
||||||
|
|
||||||
|
if self.enemies and random.random() < 0.02:
|
||||||
|
e = random.choice(self.enemies)
|
||||||
|
b = Bullet(e.center_x, e.center_y, -1)
|
||||||
|
self.bullets.append(b)
|
||||||
|
|
||||||
|
curr_bullet = self._nearest_enemy_bullet()
|
||||||
|
if curr_bullet is not None:
|
||||||
|
curr_bx = (curr_bullet.center_x - self.player.center_x) / float(self.width)
|
||||||
|
curr_by = (curr_bullet.center_y - self.player.center_y) / float(self.height)
|
||||||
|
else:
|
||||||
|
curr_bx = 2.0
|
||||||
|
curr_by = 2.0
|
||||||
|
|
||||||
|
if prev_bx != 2.0 and curr_bx != 2.0:
|
||||||
|
if abs(curr_bx) > abs(prev_bx):
|
||||||
|
reward += 0.3
|
||||||
|
|
||||||
|
if curr_bx != 2.0 and abs(curr_bx) < 0.08 and curr_by < 0.5:
|
||||||
|
reward -= 0.3
|
||||||
|
|
||||||
|
obs = self._obs()
|
||||||
|
self.prev_bx = curr_bx
|
||||||
|
|
||||||
|
return obs, float(reward), bool(terminated), bool(truncated), {}
|
||||||
118
uv.lock
generated
118
uv.lock
generated
@@ -96,21 +96,139 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload_time = "2025-09-08T23:23:43.004Z" },
|
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload_time = "2025-09-08T23:23:43.004Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cloudpickle"
|
||||||
|
version = "3.1.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload_time = "2025-11-03T09:25:26.604Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload_time = "2025-11-03T09:25:25.534Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "farama-notifications"
|
||||||
|
version = "0.0.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/2e/2c/8384832b7a6b1fd6ba95bbdcae26e7137bb3eedc955c42fd5cdcc086cfbf/Farama-Notifications-0.0.4.tar.gz", hash = "sha256:13fceff2d14314cf80703c8266462ebf3733c7d165336eee998fc58e545efd18", size = 2131, upload_time = "2023-02-27T18:28:41.047Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/05/2c/ffc08c54c05cdce6fbed2aeebc46348dbe180c6d2c541c7af7ba0aa5f5f8/Farama_Notifications-0.0.4-py3-none-any.whl", hash = "sha256:14de931035a41961f7c056361dc7f980762a143d05791ef5794a751a2caf05ae", size = 2511, upload_time = "2023-02-27T18:28:39.447Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fleet-commander"
|
name = "fleet-commander"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "arcade" },
|
{ name = "arcade" },
|
||||||
|
{ name = "gymnasium" },
|
||||||
|
{ name = "numpy" },
|
||||||
{ name = "pypresence" },
|
{ name = "pypresence" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "arcade", specifier = ">=3.3.3" },
|
{ name = "arcade", specifier = ">=3.3.3" },
|
||||||
|
{ name = "gymnasium", specifier = ">=1.2.2" },
|
||||||
|
{ name = "numpy", specifier = ">=2.3.4" },
|
||||||
{ name = "pypresence", specifier = ">=4.3.0" },
|
{ name = "pypresence", specifier = ">=4.3.0" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gymnasium"
|
||||||
|
version = "1.2.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "cloudpickle" },
|
||||||
|
{ name = "farama-notifications" },
|
||||||
|
{ name = "numpy" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/1b/1c/d70b2ddd067992fa0332deae08293ef2dc489917ac5342e0e4b8850641f6/gymnasium-1.2.2.tar.gz", hash = "sha256:46d927328f8518bb5a689dbe270d228c1da2b08bcb71ae0152c10aa66f48d530", size = 829250, upload_time = "2025-11-04T15:21:06.048Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/53/39cd8c2f85e213fce1f32367c4bdbd3402d3bcde7d0826a1172a0f2c5cc0/gymnasium-1.2.2-py3-none-any.whl", hash = "sha256:f04ec362b1fdf73a8b327db5ef89384a3f2ba411e05d3521513414fbbb2199c8", size = 952118, upload_time = "2025-11-04T15:21:03.484Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "numpy"
|
||||||
|
version = "2.3.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/b5/f4/098d2270d52b41f1bd7db9fc288aaa0400cb48c2a3e2af6fa365d9720947/numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a", size = 20582187, upload_time = "2025-10-15T16:18:11.77Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/60/e7/0e07379944aa8afb49a556a2b54587b828eb41dc9adc56fb7615b678ca53/numpy-2.3.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e78aecd2800b32e8347ce49316d3eaf04aed849cd5b38e0af39f829a4e59f5eb", size = 21259519, upload_time = "2025-10-15T16:15:19.012Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d0/cb/5a69293561e8819b09e34ed9e873b9a82b5f2ade23dce4c51dc507f6cfe1/numpy-2.3.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7fd09cc5d65bda1e79432859c40978010622112e9194e581e3415a3eccc7f43f", size = 14452796, upload_time = "2025-10-15T16:15:23.094Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/04/ff11611200acd602a1e5129e36cfd25bf01ad8e5cf927baf2e90236eb02e/numpy-2.3.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:1b219560ae2c1de48ead517d085bc2d05b9433f8e49d0955c82e8cd37bd7bf36", size = 5381639, upload_time = "2025-10-15T16:15:25.572Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ea/77/e95c757a6fe7a48d28a009267408e8aa382630cc1ad1db7451b3bc21dbb4/numpy-2.3.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:bafa7d87d4c99752d07815ed7a2c0964f8ab311eb8168f41b910bd01d15b6032", size = 6914296, upload_time = "2025-10-15T16:15:27.079Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a3/d2/137c7b6841c942124eae921279e5c41b1c34bab0e6fc60c7348e69afd165/numpy-2.3.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36dc13af226aeab72b7abad501d370d606326a0029b9f435eacb3b8c94b8a8b7", size = 14591904, upload_time = "2025-10-15T16:15:29.044Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bb/32/67e3b0f07b0aba57a078c4ab777a9e8e6bc62f24fb53a2337f75f9691699/numpy-2.3.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7b2f9a18b5ff9824a6af80de4f37f4ec3c2aab05ef08f51c77a093f5b89adda", size = 16939602, upload_time = "2025-10-15T16:15:31.106Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/95/22/9639c30e32c93c4cee3ccdb4b09c2d0fbff4dcd06d36b357da06146530fb/numpy-2.3.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9984bd645a8db6ca15d850ff996856d8762c51a2239225288f08f9050ca240a0", size = 16372661, upload_time = "2025-10-15T16:15:33.546Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/12/e9/a685079529be2b0156ae0c11b13d6be647743095bb51d46589e95be88086/numpy-2.3.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:64c5825affc76942973a70acf438a8ab618dbd692b84cd5ec40a0a0509edc09a", size = 18884682, upload_time = "2025-10-15T16:15:36.105Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cf/85/f6f00d019b0cc741e64b4e00ce865a57b6bed945d1bbeb1ccadbc647959b/numpy-2.3.4-cp311-cp311-win32.whl", hash = "sha256:ed759bf7a70342f7817d88376eb7142fab9fef8320d6019ef87fae05a99874e1", size = 6570076, upload_time = "2025-10-15T16:15:38.225Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7d/10/f8850982021cb90e2ec31990291f9e830ce7d94eef432b15066e7cbe0bec/numpy-2.3.4-cp311-cp311-win_amd64.whl", hash = "sha256:faba246fb30ea2a526c2e9645f61612341de1a83fb1e0c5edf4ddda5a9c10996", size = 13089358, upload_time = "2025-10-15T16:15:40.404Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/ad/afdd8351385edf0b3445f9e24210a9c3971ef4de8fd85155462fc4321d79/numpy-2.3.4-cp311-cp311-win_arm64.whl", hash = "sha256:4c01835e718bcebe80394fd0ac66c07cbb90147ebbdad3dcecd3f25de2ae7e2c", size = 10462292, upload_time = "2025-10-15T16:15:42.896Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/96/7a/02420400b736f84317e759291b8edaeee9dc921f72b045475a9cbdb26b17/numpy-2.3.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef1b5a3e808bc40827b5fa2c8196151a4c5abe110e1726949d7abddfe5c7ae11", size = 20957727, upload_time = "2025-10-15T16:15:44.9Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/18/90/a014805d627aa5750f6f0e878172afb6454552da929144b3c07fcae1bb13/numpy-2.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2f91f496a87235c6aaf6d3f3d89b17dba64996abadccb289f48456cff931ca9", size = 14187262, upload_time = "2025-10-15T16:15:47.761Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/e4/0a94b09abe89e500dc748e7515f21a13e30c5c3fe3396e6d4ac108c25fca/numpy-2.3.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f77e5b3d3da652b474cc80a14084927a5e86a5eccf54ca8ca5cbd697bf7f2667", size = 5115992, upload_time = "2025-10-15T16:15:50.144Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/88/dd/db77c75b055c6157cbd4f9c92c4458daef0dd9cbe6d8d2fe7f803cb64c37/numpy-2.3.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ab1c5f5ee40d6e01cbe96de5863e39b215a4d24e7d007cad56c7184fdf4aeef", size = 6648672, upload_time = "2025-10-15T16:15:52.442Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e1/e6/e31b0d713719610e406c0ea3ae0d90760465b086da8783e2fd835ad59027/numpy-2.3.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77b84453f3adcb994ddbd0d1c5d11db2d6bda1a2b7fd5ac5bd4649d6f5dc682e", size = 14284156, upload_time = "2025-10-15T16:15:54.351Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f9/58/30a85127bfee6f108282107caf8e06a1f0cc997cb6b52cdee699276fcce4/numpy-2.3.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4121c5beb58a7f9e6dfdee612cb24f4df5cd4db6e8261d7f4d7450a997a65d6a", size = 16641271, upload_time = "2025-10-15T16:15:56.67Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/06/f2/2e06a0f2adf23e3ae29283ad96959267938d0efd20a2e25353b70065bfec/numpy-2.3.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:65611ecbb00ac9846efe04db15cbe6186f562f6bb7e5e05f077e53a599225d16", size = 16059531, upload_time = "2025-10-15T16:15:59.412Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b0/e7/b106253c7c0d5dc352b9c8fab91afd76a93950998167fa3e5afe4ef3a18f/numpy-2.3.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dabc42f9c6577bcc13001b8810d300fe814b4cfbe8a92c873f269484594f9786", size = 18578983, upload_time = "2025-10-15T16:16:01.804Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/73/e3/04ecc41e71462276ee867ccbef26a4448638eadecf1bc56772c9ed6d0255/numpy-2.3.4-cp312-cp312-win32.whl", hash = "sha256:a49d797192a8d950ca59ee2d0337a4d804f713bb5c3c50e8db26d49666e351dc", size = 6291380, upload_time = "2025-10-15T16:16:03.938Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/a8/566578b10d8d0e9955b1b6cd5db4e9d4592dd0026a941ff7994cedda030a/numpy-2.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:985f1e46358f06c2a09921e8921e2c98168ed4ae12ccd6e5e87a4f1857923f32", size = 12787999, upload_time = "2025-10-15T16:16:05.801Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/58/22/9c903a957d0a8071b607f5b1bff0761d6e608b9a965945411f867d515db1/numpy-2.3.4-cp312-cp312-win_arm64.whl", hash = "sha256:4635239814149e06e2cb9db3dd584b2fa64316c96f10656983b8026a82e6e4db", size = 10197412, upload_time = "2025-10-15T16:16:07.854Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/57/7e/b72610cc91edf138bc588df5150957a4937221ca6058b825b4725c27be62/numpy-2.3.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c090d4860032b857d94144d1a9976b8e36709e40386db289aaf6672de2a81966", size = 20950335, upload_time = "2025-10-15T16:16:10.304Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/46/bdd3370dcea2f95ef14af79dbf81e6927102ddf1cc54adc0024d61252fd9/numpy-2.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a13fc473b6db0be619e45f11f9e81260f7302f8d180c49a22b6e6120022596b3", size = 14179878, upload_time = "2025-10-15T16:16:12.595Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ac/01/5a67cb785bda60f45415d09c2bc245433f1c68dd82eef9c9002c508b5a65/numpy-2.3.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:3634093d0b428e6c32c3a69b78e554f0cd20ee420dcad5a9f3b2a63762ce4197", size = 5108673, upload_time = "2025-10-15T16:16:14.877Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c2/cd/8428e23a9fcebd33988f4cb61208fda832800ca03781f471f3727a820704/numpy-2.3.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:043885b4f7e6e232d7df4f51ffdef8c36320ee9d5f227b380ea636722c7ed12e", size = 6641438, upload_time = "2025-10-15T16:16:16.805Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/d1/913fe563820f3c6b079f992458f7331278dcd7ba8427e8e745af37ddb44f/numpy-2.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7", size = 14281290, upload_time = "2025-10-15T16:16:18.764Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9e/7e/7d306ff7cb143e6d975cfa7eb98a93e73495c4deabb7d1b5ecf09ea0fd69/numpy-2.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc8a63918b04b8571789688b2780ab2b4a33ab44bfe8ccea36d3eba51228c953", size = 16636543, upload_time = "2025-10-15T16:16:21.072Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/47/6a/8cfc486237e56ccfb0db234945552a557ca266f022d281a2f577b98e955c/numpy-2.3.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40cc556d5abbc54aabe2b1ae287042d7bdb80c08edede19f0c0afb36ae586f37", size = 16056117, upload_time = "2025-10-15T16:16:23.369Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b1/0e/42cb5e69ea901e06ce24bfcc4b5664a56f950a70efdcf221f30d9615f3f3/numpy-2.3.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ecb63014bb7f4ce653f8be7f1df8cbc6093a5a2811211770f6606cc92b5a78fd", size = 18577788, upload_time = "2025-10-15T16:16:27.496Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/86/92/41c3d5157d3177559ef0a35da50f0cda7fa071f4ba2306dd36818591a5bc/numpy-2.3.4-cp313-cp313-win32.whl", hash = "sha256:e8370eb6925bb8c1c4264fec52b0384b44f675f191df91cbe0140ec9f0955646", size = 6282620, upload_time = "2025-10-15T16:16:29.811Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/09/97/fd421e8bc50766665ad35536c2bb4ef916533ba1fdd053a62d96cc7c8b95/numpy-2.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:56209416e81a7893036eea03abcb91c130643eb14233b2515c90dcac963fe99d", size = 12784672, upload_time = "2025-10-15T16:16:31.589Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ad/df/5474fb2f74970ca8eb978093969b125a84cc3d30e47f82191f981f13a8a0/numpy-2.3.4-cp313-cp313-win_arm64.whl", hash = "sha256:a700a4031bc0fd6936e78a752eefb79092cecad2599ea9c8039c548bc097f9bc", size = 10196702, upload_time = "2025-10-15T16:16:33.902Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/11/83/66ac031464ec1767ea3ed48ce40f615eb441072945e98693bec0bcd056cc/numpy-2.3.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:86966db35c4040fdca64f0816a1c1dd8dbd027d90fca5a57e00e1ca4cd41b879", size = 21049003, upload_time = "2025-10-15T16:16:36.101Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5f/99/5b14e0e686e61371659a1d5bebd04596b1d72227ce36eed121bb0aeab798/numpy-2.3.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:838f045478638b26c375ee96ea89464d38428c69170360b23a1a50fa4baa3562", size = 14302980, upload_time = "2025-10-15T16:16:39.124Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/44/e9486649cd087d9fc6920e3fc3ac2aba10838d10804b1e179fb7cbc4e634/numpy-2.3.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d7315ed1dab0286adca467377c8381cd748f3dc92235f22a7dfc42745644a96a", size = 5231472, upload_time = "2025-10-15T16:16:41.168Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/51/902b24fa8887e5fe2063fd61b1895a476d0bbf46811ab0c7fdf4bd127345/numpy-2.3.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:84f01a4d18b2cc4ade1814a08e5f3c907b079c847051d720fad15ce37aa930b6", size = 6739342, upload_time = "2025-10-15T16:16:43.777Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/34/f1/4de9586d05b1962acdcdb1dc4af6646361a643f8c864cef7c852bf509740/numpy-2.3.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:817e719a868f0dacde4abdfc5c1910b301877970195db9ab6a5e2c4bd5b121f7", size = 14354338, upload_time = "2025-10-15T16:16:46.081Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/06/1c16103b425de7969d5a76bdf5ada0804b476fed05d5f9e17b777f1cbefd/numpy-2.3.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85e071da78d92a214212cacea81c6da557cab307f2c34b5f85b628e94803f9c0", size = 16702392, upload_time = "2025-10-15T16:16:48.455Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/34/b2/65f4dc1b89b5322093572b6e55161bb42e3e0487067af73627f795cc9d47/numpy-2.3.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2ec646892819370cf3558f518797f16597b4e4669894a2ba712caccc9da53f1f", size = 16134998, upload_time = "2025-10-15T16:16:51.114Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d4/11/94ec578896cdb973aaf56425d6c7f2aff4186a5c00fac15ff2ec46998b46/numpy-2.3.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:035796aaaddfe2f9664b9a9372f089cfc88bd795a67bd1bfe15e6e770934cf64", size = 18651574, upload_time = "2025-10-15T16:16:53.429Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/62/b7/7efa763ab33dbccf56dade36938a77345ce8e8192d6b39e470ca25ff3cd0/numpy-2.3.4-cp313-cp313t-win32.whl", hash = "sha256:fea80f4f4cf83b54c3a051f2f727870ee51e22f0248d3114b8e755d160b38cfb", size = 6413135, upload_time = "2025-10-15T16:16:55.992Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/43/70/aba4c38e8400abcc2f345e13d972fb36c26409b3e644366db7649015f291/numpy-2.3.4-cp313-cp313t-win_amd64.whl", hash = "sha256:15eea9f306b98e0be91eb344a94c0e630689ef302e10c2ce5f7e11905c704f9c", size = 12928582, upload_time = "2025-10-15T16:16:57.943Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/63/871fad5f0073fc00fbbdd7232962ea1ac40eeaae2bba66c76214f7954236/numpy-2.3.4-cp313-cp313t-win_arm64.whl", hash = "sha256:b6c231c9c2fadbae4011ca5e7e83e12dc4a5072f1a1d85a0a7b3ed754d145a40", size = 10266691, upload_time = "2025-10-15T16:17:00.048Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/72/71/ae6170143c115732470ae3a2d01512870dd16e0953f8a6dc89525696069b/numpy-2.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81c3e6d8c97295a7360d367f9f8553973651b76907988bb6066376bc2252f24e", size = 20955580, upload_time = "2025-10-15T16:17:02.509Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/af/39/4be9222ffd6ca8a30eda033d5f753276a9c3426c397bb137d8e19dedd200/numpy-2.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7c26b0b2bf58009ed1f38a641f3db4be8d960a417ca96d14e5b06df1506d41ff", size = 14188056, upload_time = "2025-10-15T16:17:04.873Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6c/3d/d85f6700d0a4aa4f9491030e1021c2b2b7421b2b38d01acd16734a2bfdc7/numpy-2.3.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:62b2198c438058a20b6704351b35a1d7db881812d8512d67a69c9de1f18ca05f", size = 5116555, upload_time = "2025-10-15T16:17:07.499Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bf/04/82c1467d86f47eee8a19a464c92f90a9bb68ccf14a54c5224d7031241ffb/numpy-2.3.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:9d729d60f8d53a7361707f4b68a9663c968882dd4f09e0d58c044c8bf5faee7b", size = 6643581, upload_time = "2025-10-15T16:17:09.774Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/d3/c79841741b837e293f48bd7db89d0ac7a4f2503b382b78a790ef1dc778a5/numpy-2.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd0c630cf256b0a7fd9d0a11c9413b42fef5101219ce6ed5a09624f5a65392c7", size = 14299186, upload_time = "2025-10-15T16:17:11.937Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e8/7e/4a14a769741fbf237eec5a12a2cbc7a4c4e061852b6533bcb9e9a796c908/numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5e081bc082825f8b139f9e9fe42942cb4054524598aaeb177ff476cc76d09d2", size = 16638601, upload_time = "2025-10-15T16:17:14.391Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/93/87/1c1de269f002ff0a41173fe01dcc925f4ecff59264cd8f96cf3b60d12c9b/numpy-2.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15fb27364ed84114438fff8aaf998c9e19adbeba08c0b75409f8c452a8692c52", size = 16074219, upload_time = "2025-10-15T16:17:17.058Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cd/28/18f72ee77408e40a76d691001ae599e712ca2a47ddd2c4f695b16c65f077/numpy-2.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:85d9fb2d8cd998c84d13a79a09cc0c1091648e848e4e6249b0ccd7f6b487fa26", size = 18576702, upload_time = "2025-10-15T16:17:19.379Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c3/76/95650169b465ececa8cf4b2e8f6df255d4bf662775e797ade2025cc51ae6/numpy-2.3.4-cp314-cp314-win32.whl", hash = "sha256:e73d63fd04e3a9d6bc187f5455d81abfad05660b212c8804bf3b407e984cd2bc", size = 6337136, upload_time = "2025-10-15T16:17:22.886Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dc/89/a231a5c43ede5d6f77ba4a91e915a87dea4aeea76560ba4d2bf185c683f0/numpy-2.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:3da3491cee49cf16157e70f607c03a217ea6647b1cea4819c4f48e53d49139b9", size = 12920542, upload_time = "2025-10-15T16:17:24.783Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0d/0c/ae9434a888f717c5ed2ff2393b3f344f0ff6f1c793519fa0c540461dc530/numpy-2.3.4-cp314-cp314-win_arm64.whl", hash = "sha256:6d9cd732068e8288dbe2717177320723ccec4fb064123f0caf9bbd90ab5be868", size = 10480213, upload_time = "2025-10-15T16:17:26.935Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/4b/c4a5f0841f92536f6b9592694a5b5f68c9ab37b775ff342649eadf9055d3/numpy-2.3.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:22758999b256b595cf0b1d102b133bb61866ba5ceecf15f759623b64c020c9ec", size = 21052280, upload_time = "2025-10-15T16:17:29.638Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/80/90308845fc93b984d2cc96d83e2324ce8ad1fd6efea81b324cba4b673854/numpy-2.3.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9cb177bc55b010b19798dc5497d540dea67fd13a8d9e882b2dae71de0cf09eb3", size = 14302930, upload_time = "2025-10-15T16:17:32.384Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/4e/07439f22f2a3b247cec4d63a713faae55e1141a36e77fb212881f7cda3fb/numpy-2.3.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0f2bcc76f1e05e5ab58893407c63d90b2029908fa41f9f1cc51eecce936c3365", size = 5231504, upload_time = "2025-10-15T16:17:34.515Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ab/de/1e11f2547e2fe3d00482b19721855348b94ada8359aef5d40dd57bfae9df/numpy-2.3.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dc20bde86802df2ed8397a08d793da0ad7a5fd4ea3ac85d757bf5dd4ad7c252", size = 6739405, upload_time = "2025-10-15T16:17:36.128Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3b/40/8cd57393a26cebe2e923005db5134a946c62fa56a1087dc7c478f3e30837/numpy-2.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e199c087e2aa71c8f9ce1cb7a8e10677dc12457e7cc1be4798632da37c3e86e", size = 14354866, upload_time = "2025-10-15T16:17:38.884Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/93/39/5b3510f023f96874ee6fea2e40dfa99313a00bf3ab779f3c92978f34aace/numpy-2.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85597b2d25ddf655495e2363fe044b0ae999b75bc4d630dc0d886484b03a5eb0", size = 16703296, upload_time = "2025-10-15T16:17:41.564Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/41/0d/19bb163617c8045209c1996c4e427bccbc4bbff1e2c711f39203c8ddbb4a/numpy-2.3.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04a69abe45b49c5955923cf2c407843d1c85013b424ae8a560bba16c92fe44a0", size = 16136046, upload_time = "2025-10-15T16:17:43.901Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e2/c1/6dba12fdf68b02a21ac411c9df19afa66bed2540f467150ca64d246b463d/numpy-2.3.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e1708fac43ef8b419c975926ce1eaf793b0c13b7356cfab6ab0dc34c0a02ac0f", size = 18652691, upload_time = "2025-10-15T16:17:46.247Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f8/73/f85056701dbbbb910c51d846c58d29fd46b30eecd2b6ba760fc8b8a1641b/numpy-2.3.4-cp314-cp314t-win32.whl", hash = "sha256:863e3b5f4d9915aaf1b8ec79ae560ad21f0b8d5e3adc31e73126491bb86dee1d", size = 6485782, upload_time = "2025-10-15T16:17:48.872Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/17/90/28fa6f9865181cb817c2471ee65678afa8a7e2a1fb16141473d5fa6bacc3/numpy-2.3.4-cp314-cp314t-win_amd64.whl", hash = "sha256:962064de37b9aef801d33bc579690f8bfe6c5e70e29b61783f60bcba838a14d6", size = 13113301, upload_time = "2025-10-15T16:17:50.938Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/54/23/08c002201a8e7e1f9afba93b97deceb813252d9cfd0d3351caed123dcf97/numpy-2.3.4-cp314-cp314t-win_arm64.whl", hash = "sha256:8b5a9a39c45d852b62693d9b3f3e0fe052541f804296ff401a72a1b60edafb29", size = 10547532, upload_time = "2025-10-15T16:17:53.48Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b1/b6/64898f51a86ec88ca1257a59c1d7fd077b60082a119affefcdf1dd0df8ca/numpy-2.3.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6e274603039f924c0fe5cb73438fa9246699c78a6df1bd3decef9ae592ae1c05", size = 21131552, upload_time = "2025-10-15T16:17:55.845Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ce/4c/f135dc6ebe2b6a3c77f4e4838fa63d350f85c99462012306ada1bd4bc460/numpy-2.3.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d149aee5c72176d9ddbc6803aef9c0f6d2ceeea7626574fc68518da5476fa346", size = 14377796, upload_time = "2025-10-15T16:17:58.308Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d0/a4/f33f9c23fcc13dd8412fc8614559b5b797e0aba9d8e01dfa8bae10c84004/numpy-2.3.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:6d34ed9db9e6395bb6cd33286035f73a59b058169733a9db9f85e650b88df37e", size = 5306904, upload_time = "2025-10-15T16:18:00.596Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/28/af/c44097f25f834360f9fb960fa082863e0bad14a42f36527b2a121abdec56/numpy-2.3.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:fdebe771ca06bb8d6abce84e51dca9f7921fe6ad34a0c914541b063e9a68928b", size = 6819682, upload_time = "2025-10-15T16:18:02.32Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c5/8c/cd283b54c3c2b77e188f63e23039844f56b23bba1712318288c13fe86baf/numpy-2.3.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e92defe6c08211eb77902253b14fe5b480ebc5112bc741fd5e9cd0608f847", size = 14422300, upload_time = "2025-10-15T16:18:04.271Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b0/f0/8404db5098d92446b3e3695cf41c6f0ecb703d701cb0b7566ee2177f2eee/numpy-2.3.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13b9062e4f5c7ee5c7e5be96f29ba71bc5a37fed3d1d77c37390ae00724d296d", size = 16760806, upload_time = "2025-10-15T16:18:06.668Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/95/8e/2844c3959ce9a63acc7c8e50881133d86666f0420bcde695e115ced0920f/numpy-2.3.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:81b3a59793523e552c4a96109dde028aa4448ae06ccac5a76ff6532a85558a7f", size = 12973130, upload_time = "2025-10-15T16:18:09.397Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pillow"
|
name = "pillow"
|
||||||
version = "11.3.0"
|
version = "11.3.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user