Skip to content

Commit 95fd11f

Browse files
authored
Merge pull request #79 from Kiyotoko/master
Fix multiple open issues
2 parents d6bab8c + 484fb0e commit 95fd11f

26 files changed

+1205
-1047
lines changed

.github/workflows/pre-release.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ jobs:
3232
3333
- name: "Create archives"
3434
run: |
35-
zip -r seekers-stubs.zip seekers/grpc/stubs
36-
zip -r seekers.zip *
35+
zip -r seekers-linux-stubs.zip seekers/grpc/stubs
36+
zip -r seekers-linux.zip *
3737
3838
- name: "Build binaries"
3939
run: |
@@ -73,8 +73,8 @@ jobs:
7373
7474
- name: "Create archives"
7575
run: |
76-
powershell Compress-Archive ".\" "seekers.zip"
77-
powershell Compress-Archive ".\seekers\grpc\stubs" "seekers-stubs.zip"
76+
powershell Compress-Archive ".\" "seekers-win32.zip"
77+
powershell Compress-Archive ".\seekers\grpc\stubs" "seekers-win32-stubs.zip"
7878
7979
- name: "Build binaries"
8080
run: |

freeze.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ echo Building binaries ...
99
.\venv\Scripts\python setup.py build
1010

1111
echo Compress artifacts ...
12-
for /d %%a in (build\*) do (powershell Compress-Archive ".\%%a\*" "seekers-bin.zip")
12+
for /d %%a in (build\*) do (powershell Compress-Archive ".\%%a\*" "seekers-win32-bin.zip")
1313
echo Finished!

freeze.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ echo "Building binaries ..."
1010
venv/bin/python setup.py build
1111

1212
echo "Create archive"
13-
zip -r seekers-bin.zip build
13+
zip -r seekers-linux-bin.zip build

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
pygame>=2.1.0
1+
pygame~=2.6.0
22
grpcio==1.64.1
33
protobuf==5.27.2

run_client.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import sys
44
import logging
55
import seekers.grpc.client
6-
import seekers.seekers_types
6+
7+
from seekers.game.player import LocalPlayerAi
78

89

910
def run_ai(args: argparse.Namespace):
@@ -14,7 +15,7 @@ def run_ai(args: argparse.Namespace):
1415
stream=sys.stdout, force=True
1516
)
1617

17-
ai = seekers.seekers_types.LocalPlayerAi.from_file(args.ai_file)
18+
ai = LocalPlayerAi.from_file(args.ai_file)
1819

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

3435
def main():
3536
parser = argparse.ArgumentParser(description='Run a Python Seekers AI as a gRPC client.')
36-
parser.add_argument("-address", "-a", type=str, default="localhost:7777",
37+
parser.add_argument("--address", "-a", type=str, default="localhost:7777",
3738
help="Address of the Seekers game. (default: localhost:7777)")
38-
parser.add_argument("-loglevel", "-log", "-l", type=str, default="INFO",
39+
parser.add_argument("--loglevel", "--log", "-l", type=str, default="INFO",
3940
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"])
4041
parser.add_argument("--careful", action="store_true", help="Enable careful mode for the gRPC clients. This will "
4142
"raise an exception and stop the client when errors "

run_seekers.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import sys
66

77
from seekers import *
8-
from seekers.game import SeekersGame
98

109

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

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

4140
args = parser.parse_args()
4241

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

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

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

5958
seekers_game = SeekersGame(
6059
local_ai_locations=args.ai_files,
6160
config=config,
6261
grpc_address=address,
6362
debug=args.debug,
64-
dont_kill=args.nokill
63+
dont_kill=args.no_kill
6564
)
6665
seekers_game.listen()
6766
seekers_game.start()

seekers/__init__.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
from __future__ import annotations
2-
1+
from .vector import *
2+
from .config import *
33
from .colors import Color
4-
from .seekers_types import (
5-
Config,
6-
Vector,
7-
Physical,
8-
Goal,
9-
Magnet,
10-
Seeker,
11-
Player,
12-
World,
13-
Camp,
14-
)
4+
5+
from .player import *
6+
from .goal import *
7+
from .seeker import *
8+
from .physical import *
9+
from .camp import *
10+
from .world import *
11+
12+
from . import debug_drawing

seekers/camp.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from __future__ import annotations
2+
3+
import dataclasses
4+
5+
from .vector import *
6+
from . import player
7+
8+
9+
__all__ = [
10+
"Camp"
11+
]
12+
13+
14+
@dataclasses.dataclass
15+
class Camp:
16+
id: str
17+
owner: player.Player
18+
position: Vector
19+
width: float
20+
height: float
21+
22+
def contains(self, pos: Vector) -> bool:
23+
delta = self.position - pos
24+
return 2 * abs(delta.x) < self.width and 2 * abs(delta.y) < self.height
25+
26+
@property
27+
def top_left(self) -> Vector:
28+
return self.position - Vector(self.width, self.height) / 2
29+
30+
@property
31+
def bottom_right(self) -> Vector:
32+
return self.position + Vector(self.width, self.height) / 2

seekers/colors.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import random
24
import typing
35
import colorsys

seekers/config.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
from __future__ import annotations
2+
3+
import configparser
4+
import dataclasses
5+
import typing
6+
7+
__all__ = [
8+
"Config",
9+
]
10+
11+
12+
@dataclasses.dataclass
13+
class Config:
14+
"""Configuration for the Seekers game."""
15+
global_wait_for_players: bool
16+
global_playtime: int
17+
global_seed: int
18+
global_fps: int
19+
global_speed: int
20+
global_players: int
21+
global_seekers: int
22+
global_goals: int
23+
global_color_threshold: float
24+
25+
map_width: int
26+
map_height: int
27+
28+
camp_width: int
29+
camp_height: int
30+
31+
seeker_thrust: float
32+
seeker_magnet_slowdown: float
33+
seeker_disabled_time: int
34+
seeker_radius: float
35+
seeker_mass: float
36+
seeker_friction: float
37+
38+
goal_scoring_time: int
39+
goal_radius: float
40+
goal_mass: float
41+
goal_thrust: float
42+
goal_friction: float
43+
44+
@property
45+
def map_dimensions(self):
46+
return self.map_width, self.map_height
47+
48+
@classmethod
49+
def from_file(cls, file) -> "Config":
50+
cp = configparser.ConfigParser()
51+
cp.read_file(file)
52+
53+
return cls(
54+
global_wait_for_players=cp.getboolean("global", "wait-for-players"),
55+
global_playtime=cp.getint("global", "playtime"),
56+
global_seed=cp.getint("global", "seed"),
57+
global_fps=cp.getint("global", "fps"),
58+
global_speed=cp.getint("global", "speed"),
59+
global_players=cp.getint("global", "players"),
60+
global_seekers=cp.getint("global", "seekers"),
61+
global_goals=cp.getint("global", "goals"),
62+
global_color_threshold=cp.getfloat("global", "color-threshold"),
63+
64+
map_width=cp.getint("map", "width"),
65+
map_height=cp.getint("map", "height"),
66+
67+
camp_width=cp.getint("camp", "width"),
68+
camp_height=cp.getint("camp", "height"),
69+
70+
seeker_thrust=cp.getfloat("seeker", "thrust"),
71+
seeker_magnet_slowdown=cp.getfloat("seeker", "magnet-slowdown"),
72+
seeker_disabled_time=cp.getint("seeker", "disabled-time"),
73+
seeker_radius=cp.getfloat("seeker", "radius"),
74+
seeker_mass=cp.getfloat("seeker", "mass"),
75+
seeker_friction=cp.getfloat("seeker", "friction"),
76+
77+
goal_scoring_time=cp.getint("goal", "scoring-time"),
78+
goal_radius=cp.getfloat("goal", "radius"),
79+
goal_mass=cp.getfloat("goal", "mass"),
80+
goal_thrust=cp.getfloat("goal", "thrust"),
81+
goal_friction=cp.getfloat("goal", "friction"),
82+
)
83+
84+
@classmethod
85+
def from_filepath(cls, filepath: str) -> "Config":
86+
with open(filepath) as f:
87+
return cls.from_file(f)
88+
89+
@staticmethod
90+
def value_to_str(value: bool | float | int | str) -> str:
91+
if isinstance(value, bool):
92+
return str(value).lower()
93+
elif isinstance(value, float):
94+
return f"{value:.2f}"
95+
else:
96+
return str(value)
97+
98+
@staticmethod
99+
def value_from_str(value: str, type_: typing.Literal["bool", "float", "int", "str"]) -> bool | float | int | str:
100+
if type_ == "bool":
101+
return value.lower() == "true"
102+
elif type_ == "float":
103+
return float(value)
104+
elif type_ == "int":
105+
return int(float(value))
106+
else:
107+
return value
108+
109+
@staticmethod
110+
def get_section_and_key(attribute_name: str) -> tuple[str, str]:
111+
"""Split an attribute name into the config header name and the key name."""
112+
113+
section, key = attribute_name.split("_", 1)
114+
115+
return section, key.replace("_", "-")
116+
117+
@staticmethod
118+
def get_attribute_name(section: str, key: str) -> str:
119+
return f"{section}_{key.replace('-', '_')}"
120+
121+
@classmethod
122+
def get_field_type(cls, field_name: str) -> typing.Literal["bool", "float", "int", "str"]:
123+
field_types = {f.name: f.type for f in dataclasses.fields(cls)}
124+
return field_types[field_name]
125+
126+
def import_option(self, section: str, key: str, value: str):
127+
field_name = self.get_attribute_name(section, key)
128+
field_type = self.get_field_type(field_name)
129+
130+
setattr(self, field_name, self.value_from_str(value, field_type))

0 commit comments

Comments
 (0)