Skip to content

Commit

Permalink
Merge pull request #79 from Kiyotoko/master
Browse files Browse the repository at this point in the history
Fix multiple open issues
  • Loading branch information
Kiyotoko authored Sep 28, 2024
2 parents d6bab8c + 484fb0e commit 95fd11f
Show file tree
Hide file tree
Showing 26 changed files with 1,205 additions and 1,047 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/pre-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ jobs:
- name: "Create archives"
run: |
zip -r seekers-stubs.zip seekers/grpc/stubs
zip -r seekers.zip *
zip -r seekers-linux-stubs.zip seekers/grpc/stubs
zip -r seekers-linux.zip *
- name: "Build binaries"
run: |
Expand Down Expand Up @@ -73,8 +73,8 @@ jobs:
- name: "Create archives"
run: |
powershell Compress-Archive ".\" "seekers.zip"
powershell Compress-Archive ".\seekers\grpc\stubs" "seekers-stubs.zip"
powershell Compress-Archive ".\" "seekers-win32.zip"
powershell Compress-Archive ".\seekers\grpc\stubs" "seekers-win32-stubs.zip"
- name: "Build binaries"
run: |
Expand Down
2 changes: 1 addition & 1 deletion freeze.bat
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ echo Building binaries ...
.\venv\Scripts\python setup.py build

echo Compress artifacts ...
for /d %%a in (build\*) do (powershell Compress-Archive ".\%%a\*" "seekers-bin.zip")
for /d %%a in (build\*) do (powershell Compress-Archive ".\%%a\*" "seekers-win32-bin.zip")
echo Finished!
2 changes: 1 addition & 1 deletion freeze.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ echo "Building binaries ..."
venv/bin/python setup.py build

echo "Create archive"
zip -r seekers-bin.zip build
zip -r seekers-linux-bin.zip build
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pygame>=2.1.0
pygame~=2.6.0
grpcio==1.64.1
protobuf==5.27.2
9 changes: 5 additions & 4 deletions run_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import sys
import logging
import seekers.grpc.client
import seekers.seekers_types

from seekers.game.player import LocalPlayerAi


def run_ai(args: argparse.Namespace):
Expand All @@ -14,7 +15,7 @@ def run_ai(args: argparse.Namespace):
stream=sys.stdout, force=True
)

ai = seekers.seekers_types.LocalPlayerAi.from_file(args.ai_file)
ai = LocalPlayerAi.from_file(args.ai_file)

service_wrapper = seekers.grpc.client.GrpcSeekersServiceWrapper(address=args.address)
client = seekers.grpc.client.GrpcSeekersClient(service_wrapper, ai, careful_mode=args.careful)
Expand All @@ -33,9 +34,9 @@ def run_ai(args: argparse.Namespace):

def main():
parser = argparse.ArgumentParser(description='Run a Python Seekers AI as a gRPC client.')
parser.add_argument("-address", "-a", type=str, default="localhost:7777",
parser.add_argument("--address", "-a", type=str, default="localhost:7777",
help="Address of the Seekers game. (default: localhost:7777)")
parser.add_argument("-loglevel", "-log", "-l", type=str, default="INFO",
parser.add_argument("--loglevel", "--log", "-l", type=str, default="INFO",
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"])
parser.add_argument("--careful", action="store_true", help="Enable careful mode for the gRPC clients. This will "
"raise an exception and stop the client when errors "
Expand Down
19 changes: 9 additions & 10 deletions run_seekers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import sys

from seekers import *
from seekers.game import SeekersGame


def parse_config_overrides(overrides: list[str]) -> dict[str, str]:
Expand All @@ -25,22 +24,22 @@ def parse_config_overrides(overrides: list[str]) -> dict[str, str]:

def main():
parser = argparse.ArgumentParser(description="Run python seekers AIs.")
parser.add_argument("--nogrpc", action="store_true", help="Don't host a gRPC server.")
parser.add_argument("--nokill", action="store_true", help="Don't kill the process after the game is over.")
parser.add_argument("--no-grpc", action="store_true", help="Don't host a gRPC server.")
parser.add_argument("--no-kill", action="store_true", help="Don't kill the process after the game is over.")
parser.add_argument("--debug", action="store_true", help="Enable debug mode. This will enable debug drawing.")
parser.add_argument("-address", "-a", type=str, default="localhost:7777",
parser.add_argument("--address", "-a", type=str, default="localhost:7777",
help="Address of the server. (default: localhost:7777)")
parser.add_argument("-config", "-c", type=str, default="config.ini",
parser.add_argument("--config", "-c", type=str, default="config.ini",
help="Path to the config file. (default: config.ini)")
parser.add_argument("-config-override", "-co", action="append",
parser.add_argument("--config-override", "--override", "-o", action="append",
help="Override a config option. Use the form option=value, e.g. global.seed=43.")
parser.add_argument("-loglevel", "-log", "-l", type=str, default="INFO",
parser.add_argument("--loglevel", "--log", "-l", type=str, default="INFO",
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"])
parser.add_argument("ai_files", type=str, nargs="*", help="Paths to the AIs.")

args = parser.parse_args()

if args.nogrpc and not args.ai_files:
if args.no_grpc and not args.ai_files:
raise ValueError("At least one AI file must be provided if gRPC is disabled.")

config = Config.from_filepath(args.config)
Expand All @@ -54,14 +53,14 @@ def main():

logging.basicConfig(level=args.loglevel, style="{", format=f"[{{name}}] {{levelname}}: {{message}}",
stream=sys.stdout)
address = args.address if not args.nogrpc else False
address = args.address if not args.no_grpc else False

seekers_game = SeekersGame(
local_ai_locations=args.ai_files,
config=config,
grpc_address=address,
debug=args.debug,
dont_kill=args.nokill
dont_kill=args.no_kill
)
seekers_game.listen()
seekers_game.start()
Expand Down
24 changes: 11 additions & 13 deletions seekers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
from __future__ import annotations

from .vector import *
from .config import *
from .colors import Color
from .seekers_types import (
Config,
Vector,
Physical,
Goal,
Magnet,
Seeker,
Player,
World,
Camp,
)

from .player import *
from .goal import *
from .seeker import *
from .physical import *
from .camp import *
from .world import *

from . import debug_drawing
32 changes: 32 additions & 0 deletions seekers/camp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from __future__ import annotations

import dataclasses

from .vector import *
from . import player


__all__ = [
"Camp"
]


@dataclasses.dataclass
class Camp:
id: str
owner: player.Player
position: Vector
width: float
height: float

def contains(self, pos: Vector) -> bool:
delta = self.position - pos
return 2 * abs(delta.x) < self.width and 2 * abs(delta.y) < self.height

@property
def top_left(self) -> Vector:
return self.position - Vector(self.width, self.height) / 2

@property
def bottom_right(self) -> Vector:
return self.position + Vector(self.width, self.height) / 2
2 changes: 2 additions & 0 deletions seekers/colors.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import random
import typing
import colorsys
Expand Down
130 changes: 130 additions & 0 deletions seekers/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
from __future__ import annotations

import configparser
import dataclasses
import typing

__all__ = [
"Config",
]


@dataclasses.dataclass
class Config:
"""Configuration for the Seekers game."""
global_wait_for_players: bool
global_playtime: int
global_seed: int
global_fps: int
global_speed: int
global_players: int
global_seekers: int
global_goals: int
global_color_threshold: float

map_width: int
map_height: int

camp_width: int
camp_height: int

seeker_thrust: float
seeker_magnet_slowdown: float
seeker_disabled_time: int
seeker_radius: float
seeker_mass: float
seeker_friction: float

goal_scoring_time: int
goal_radius: float
goal_mass: float
goal_thrust: float
goal_friction: float

@property
def map_dimensions(self):
return self.map_width, self.map_height

@classmethod
def from_file(cls, file) -> "Config":
cp = configparser.ConfigParser()
cp.read_file(file)

return cls(
global_wait_for_players=cp.getboolean("global", "wait-for-players"),
global_playtime=cp.getint("global", "playtime"),
global_seed=cp.getint("global", "seed"),
global_fps=cp.getint("global", "fps"),
global_speed=cp.getint("global", "speed"),
global_players=cp.getint("global", "players"),
global_seekers=cp.getint("global", "seekers"),
global_goals=cp.getint("global", "goals"),
global_color_threshold=cp.getfloat("global", "color-threshold"),

map_width=cp.getint("map", "width"),
map_height=cp.getint("map", "height"),

camp_width=cp.getint("camp", "width"),
camp_height=cp.getint("camp", "height"),

seeker_thrust=cp.getfloat("seeker", "thrust"),
seeker_magnet_slowdown=cp.getfloat("seeker", "magnet-slowdown"),
seeker_disabled_time=cp.getint("seeker", "disabled-time"),
seeker_radius=cp.getfloat("seeker", "radius"),
seeker_mass=cp.getfloat("seeker", "mass"),
seeker_friction=cp.getfloat("seeker", "friction"),

goal_scoring_time=cp.getint("goal", "scoring-time"),
goal_radius=cp.getfloat("goal", "radius"),
goal_mass=cp.getfloat("goal", "mass"),
goal_thrust=cp.getfloat("goal", "thrust"),
goal_friction=cp.getfloat("goal", "friction"),
)

@classmethod
def from_filepath(cls, filepath: str) -> "Config":
with open(filepath) as f:
return cls.from_file(f)

@staticmethod
def value_to_str(value: bool | float | int | str) -> str:
if isinstance(value, bool):
return str(value).lower()
elif isinstance(value, float):
return f"{value:.2f}"
else:
return str(value)

@staticmethod
def value_from_str(value: str, type_: typing.Literal["bool", "float", "int", "str"]) -> bool | float | int | str:
if type_ == "bool":
return value.lower() == "true"
elif type_ == "float":
return float(value)
elif type_ == "int":
return int(float(value))
else:
return value

@staticmethod
def get_section_and_key(attribute_name: str) -> tuple[str, str]:
"""Split an attribute name into the config header name and the key name."""

section, key = attribute_name.split("_", 1)

return section, key.replace("_", "-")

@staticmethod
def get_attribute_name(section: str, key: str) -> str:
return f"{section}_{key.replace('-', '_')}"

@classmethod
def get_field_type(cls, field_name: str) -> typing.Literal["bool", "float", "int", "str"]:
field_types = {f.name: f.type for f in dataclasses.fields(cls)}
return field_types[field_name]

def import_option(self, section: str, key: str, value: str):
field_name = self.get_attribute_name(section, key)
field_type = self.get_field_type(field_name)

setattr(self, field_name, self.value_from_str(value, field_type))
25 changes: 17 additions & 8 deletions seekers/debug_drawing.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
from __future__ import annotations

import abc
import dataclasses
import typing
from contextvars import ContextVar

from .seekers_types import Vector
from .draw import GameRenderer
from .vector import *
from . import draw

__all__ = [
"draw_text",
"draw_line",
"draw_circle",
]


@dataclasses.dataclass
class DebugDrawing(abc.ABC):
@abc.abstractmethod
def draw(self, game_renderer: GameRenderer):
def draw(self, game_renderer: draw.GameRenderer):
...


Expand All @@ -21,7 +29,7 @@ class TextDebugDrawing(DebugDrawing):
color: tuple[int, int, int] = (255, 255, 255)
center: bool = True

def draw(self, game_renderer: GameRenderer):
def draw(self, game_renderer: draw.GameRenderer):
# draw the text centered at the position
game_renderer.draw_text(self.text, self.color, self.position, center=self.center)

Expand All @@ -33,7 +41,7 @@ class LineDebugDrawing(DebugDrawing):
color: tuple[int, int, int] = (255, 255, 255)
width: int = 2

def draw(self, game_renderer: GameRenderer):
def draw(self, game_renderer: draw.GameRenderer):
game_renderer.draw_line(self.color, self.start, self.end, self.width)


Expand All @@ -44,7 +52,7 @@ class CircleDebugDrawing(DebugDrawing):
color: tuple[int, int, int] = (255, 255, 255)
width: int = 2

def draw(self, game_renderer: GameRenderer):
def draw(self, game_renderer: draw.GameRenderer):
game_renderer.draw_circle(self.color, self.position, self.radius, self.width)


Expand All @@ -60,5 +68,6 @@ def draw_circle(position: Vector, radius: float, color: tuple[int, int, int] = (
add_debug_drawing_func_ctxtvar.get()(CircleDebugDrawing(position, radius, color, width))


add_debug_drawing_func_ctxtvar: \
ContextVar[typing.Callable[[DebugDrawing], None]] = ContextVar("add_debug_drawing_func", default=lambda _: None)
add_debug_drawing_func_ctxtvar: ContextVar[typing.Callable[[DebugDrawing], None]] = (
ContextVar("add_debug_drawing_func", default=lambda _: None)
)
Loading

0 comments on commit 95fd11f

Please sign in to comment.