Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Codebase Quality: Fixed various issues with code documentation and formatting. #41

Merged
merged 51 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
90fe18e
deleted changes.md, fixed super() call
lunathanael Mar 11, 2024
14c315c
fixing types and bugs from previous prs
lunathanael Mar 11, 2024
54132c6
fixing entity documentation and using fstrings, added more type annot…
lunathanael Mar 11, 2024
7d7c090
fixed spelling errors
lunathanael Mar 11, 2024
7fa3871
Entitiy collection stop method corrected spelling error
lunathanael Mar 11, 2024
d12e8b5
fixed _load_entity entity loading method
lunathanael Mar 11, 2024
4a4982b
fixed mod->entity artifacts, fixed entities spelling error
lunathanael Mar 11, 2024
5912e13
entity.py linting finished
lunathanael Mar 11, 2024
b9037ce
entity.py is actually done now
lunathanael Mar 11, 2024
14585b4
added none return type to entity.py
lunathanael Mar 11, 2024
5640449
shorten line length, rename __init__.py
lunathanael Mar 11, 2024
36f254a
rename __init__.py
lunathanael Mar 11, 2024
8b86e1c
rename tentity->target_entity in entity_distance in attack.py
lunathanael Mar 11, 2024
381b8c0
rename target_ent to target_entity
lunathanael Mar 11, 2024
ca03d75
linted movement.py
lunathanael Mar 11, 2024
876fc0d
rename target_entity
lunathanael Mar 11, 2024
3140342
linted file and spelling errors
lunathanael Mar 11, 2024
e77cd9d
renamed arena.py
lunathanael Mar 11, 2024
a13dbed
linted arena.py
lunathanael Mar 11, 2024
833f32e
card added newline, missing module docstring
lunathanael Mar 11, 2024
048f761
removed entity collection inheretance in game_engine
lunathanael Mar 11, 2024
f3b7f0b
fixed game engine dangerous default value
lunathanael Mar 11, 2024
c417893
fixed list typing in player.py
lunathanael Mar 11, 2024
f88e1c0
linted init
lunathanael Mar 11, 2024
eadca1b
missing entity stuff
lunathanael Mar 11, 2024
76794d6
added template playcard function
lunathanael Mar 11, 2024
901638a
removed assert space
lunathanael Mar 11, 2024
34469dd
added fps to initializer player
lunathanael Mar 12, 2024
f9a3c18
adding pseudovalues for return value of template functions
lunathanael Mar 12, 2024
1fe63a4
updated card.py
lunathanael Mar 12, 2024
099cca7
updated player1 spelling error
lunathanael Mar 12, 2024
4569c39
added is_game_over template function in struct
lunathanael Mar 12, 2024
4f88b04
added tower_count function
lunathanael Mar 12, 2024
f9d0aea
updated game_scheduler type annotation
lunathanael Mar 12, 2024
ac5f441
finished game_engine linting
lunathanael Mar 12, 2024
138a8e1
added L to list in pseudo legal cards return type
lunathanael Mar 12, 2024
653d70b
changed elixir to type float
lunathanael Mar 12, 2024
d089218
fixed pseudolegalcards to be card_index and not card
lunathanael Mar 12, 2024
0e1344b
added is_overtime template function
lunathanael Mar 12, 2024
ea61435
added line after docstring
lunathanael Mar 12, 2024
dea1078
added extra check
lunathanael Mar 12, 2024
18e8a10
added elixir cost for template
lunathanael Mar 12, 2024
09ea9ba
finished linting player
lunathanael Mar 12, 2024
927cf90
added newline after docstring
lunathanael Mar 12, 2024
7c225ee
missing stuff in struct
lunathanael Mar 12, 2024
a5065e9
renamed clash_royale_env class
lunathanael Mar 12, 2024
c2bf698
give up on clash_royale_env for now
lunathanael Mar 12, 2024
4822ed1
add module dependencies for pylint github actions
lunathanael Mar 12, 2024
00c7634
fixed pyling yml
lunathanael Mar 12, 2024
b7ecab8
cleared clashRoyaleEnv code, we can review the commits if we need it …
lunathanael Mar 12, 2024
1db94e2
added List import for arena.py
lunathanael Mar 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pylint
pip install pylint gymnasium pygame
- name: Analysing the code with pylint
run: |
pylint $(git ls-files '*.py')
41 changes: 0 additions & 41 deletions CHANGES.md

This file was deleted.

308 changes: 18 additions & 290 deletions clash_royale/envs/clash_royale_env.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from __future__ import annotations
from typing import Tuple

import numpy as np
import pygame

Expand All @@ -6,103 +9,18 @@

from clash_royale.envs.game_engine.game_engine import GameEngine

MAX_NUMBER_TROOPS = 32
MAX_TROOP_TYPES = 32
MAX_TROOP_HEALTH = 1000

KING_TOWER_RANGE = 7.0
KING_TOWER_DAMAGE = 109
KING_TOWER_HEALTH = 4824

TROOP_SPEED_MAP = {'slow': 0.75, 'medium': 1.0, 'fast': 1.5, 'very fast': 2.0}

# barbarian, archer, giant, skeleton
TROOP_COLORS = [(153, 255, 51), (255, 102, 155), (139, 69, 19), (250, 250, 250)]
TROOP_SPEEDS = [1.0, 1.0, 0.75, 1.5]
TROOP_SIZES = [0.3, 0.3, 0.3, 0.3]
TROOP_ATTACK_RANGE = [0.8, 5, 0.8, 0.8]
TROOP_SIGHT_RANGE = [5.5, 5.5, 7.5, 5.5]
TROOP_HEALTH = [670, 304, 4091, 81]
TROOP_DAMAGE = [147, 118, 169, 81]
TROOP_BUILDING_TARGETING = [False, False, True, False]



HEALTH_BAR_HEIGHT = 0.15


def calculate_health_bar_length(max_health):
#return 0.5529703695314562 * np.log(max_health + 1)
return 1

def draw_rectangle_from_center(canvas, color, location, size):
pygame.draw.rect(
canvas,
color,
pygame.Rect(
location - size / 2,
size,
),
)

def draw_health_bar(canvas, health_bar_color, center, health, max_health, pix_square_size):
draw_rectangle_from_center(canvas, health_bar_color,
center * pix_square_size,
[(health / max_health) * calculate_health_bar_length(max_health),
HEALTH_BAR_HEIGHT] * pix_square_size)

def draw_king_tower(canvas, side, location, pix_square_size, health):
color = (190, 0, 0) if side == 'red' else (0,0,190)
health_color = (155, 0, 0) if side == 'red' else (0, 0, 155)
direction = [0, -0.7] if side == 'red' else [0, 0.7]
draw_rectangle_from_center(canvas, (0, 0, 0), location * pix_square_size, pix_square_size)
draw_rectangle_from_center(canvas, color, location * pix_square_size, pix_square_size*0.9)
draw_health_bar(canvas, health_color,
location + direction,
health, KING_TOWER_HEALTH, pix_square_size)

def draw_troop(canvas, troop, health_bar_color, pix_square_size):
# draws using circles
troop_type = int(troop[2])
radius = TROOP_SIZES[troop_type]
pygame.draw.circle(
canvas,
(0,0,0),
troop[:2] * pix_square_size,
radius * pix_square_size[1],
)
pygame.draw.circle(
canvas,
TROOP_COLORS[troop_type],
troop[:2] * pix_square_size,
radius * pix_square_size[1]*0.95,
)
draw_health_bar(canvas, health_bar_color,
[troop[0], troop[1] - radius - 0.15],
troop[3],
TROOP_HEALTH[troop_type],
pix_square_size)

class ArenaEnv(gym.Env):
class ClashRoyaleEnv(gym.Env):
metadata = {"render_modes": ["human", "rgb_array"], "render_fps": 16}

def __init__(self, render_mode=None, width=8, height=18):
self.width = width # The size of the square grid
self.height = height
self.window_size_width = 360 # The size of the PyGame window width
self.window_size_height = 810 # The size of the PyGame window height
def __init__(self, render_mode: str | None=None, width: int=18, height: int=32):
self.width: int = width # The size of the square grid
self.height: int = height
resolution: Tuple[int, int] = (128, 128)

# Observations are dictionaries with the agent's and the target's location.
# Each location is encoded as an element of {0, ..., `size`}^2, i.e. MultiDiscrete([size, size]).
self.observation_space = spaces.Dict(
{
"blue-troops": spaces.Box(0, np.tile(np.array([width, height, MAX_TROOP_TYPES, MAX_TROOP_HEALTH]), (MAX_NUMBER_TROOPS,1,)), shape=(MAX_NUMBER_TROOPS, 4,), dtype=np.float32),
"red-troops": spaces.Box(0, np.tile(np.array([width, height, MAX_TROOP_TYPES, MAX_TROOP_HEALTH]), (MAX_NUMBER_TROOPS,1,)), shape=(MAX_NUMBER_TROOPS, 4,), dtype=np.float32),
}
)

# We have 4 actions, corresponding to "right", "up", "left", "down"
self.action_space = spaces.Discrete(4)
self.action_space = spaces.Discrete(1)


assert render_mode is None or render_mode in self.metadata["render_modes"]
Expand All @@ -119,212 +37,22 @@ def __init__(self, render_mode=None, width=8, height=18):
self.clock = None

def _get_obs(self):
return {"blue-troops": self._blue_troops, "red-troops": self._red_troops}

def _get_info(self):
return {
}

def reset(self, seed=None, options=None):
# We need the following line to seed self.np_random
super().reset(seed=seed)

self._king_blue_tower_center_location = np.array([self.width//2, self.height-1])
self._king_red_tower_center_location = np.array([self.width//2, 1])

self._king_blue_tower_health = KING_TOWER_HEALTH
self._king_red_tower_health = KING_TOWER_HEALTH

self._king_KING_TOWER_RANGE = KING_TOWER_RANGE
pass

self._blue_troops = np.zeros((MAX_NUMBER_TROOPS,4,), dtype=np.float32)
self._red_troops = np.zeros((MAX_NUMBER_TROOPS,4,), dtype=np.float32)

#Test
self._blue_troops[0] = [3.2, 5.6, 0, TROOP_HEALTH[0]]
self._blue_troops[1] = [0.5, 3.2, 2, TROOP_HEALTH[2]]
self._red_troops[0] = [5.8, 3.76, 1, TROOP_HEALTH[1]]
self._red_troops[1] = [1.3, 10.75, 3, TROOP_HEALTH[3]]
self._red_troops[2] = [2.1, 3.7, 0, TROOP_HEALTH[0] - 100]

observation = self._get_obs()
info = self._get_info()
def _get_info(self):
pass

if self.render_mode == "human":
self._render_frame()
def reset(self, seed=None, options=None):
pass

return observation, info

def step(self, action):
move_direction = [[[None, 10000] for j in range(MAX_NUMBER_TROOPS)] for i in range(2)]

for i in range(MAX_NUMBER_TROOPS):
troop_type_i = int(self._blue_troops[i][2])
if self._blue_troops[i][3] <= 0 or TROOP_BUILDING_TARGETING[troop_type_i]:
continue

min_v = None
min_dist = 1000
for j in range(MAX_NUMBER_TROOPS):
if self._red_troops[j][3] <= 0:
continue
v = (self._red_troops[j] - self._blue_troops[i])[:2]
dist = np.linalg.norm(v)
if min_dist > dist and dist <= TROOP_SIGHT_RANGE[troop_type_i]:
min_dist = dist
min_v = v / dist

if not min_v is None:
move_direction[0][i] = min_v, min_dist

for j in range(MAX_NUMBER_TROOPS):
troop_type_j = int(self._red_troops[j][2])
if self._red_troops[j][3] <= 0 or TROOP_BUILDING_TARGETING[troop_type_j]:
continue

min_v = None
min_dist = 1000
for i in range(MAX_NUMBER_TROOPS):
if self._blue_troops[i][3] <= 0:
continue
v = (self._blue_troops[i] - self._red_troops[j])[:2]
dist = np.linalg.norm(v)
if min_dist > dist and dist <= TROOP_SIGHT_RANGE[troop_type_j]:
min_dist = dist
min_v = v / dist

if not min_v is None:
move_direction[1][j] = min_v, min_dist

for i in range(MAX_NUMBER_TROOPS):
troop = self._blue_troops[i]
if troop[3] > 0:
v = self._king_red_tower_center_location - troop[:2]
dist = np.linalg.norm(v)
if move_direction[0][i][1] > dist:
move_direction[0][i] = v / dist, dist

v, dist = move_direction[0][i]
if dist > TROOP_ATTACK_RANGE[int(troop[2])]:
v = v * TROOP_SPEEDS[int(troop[2])] / self.metadata["render_fps"]
troop[0] += v[0]
troop[1] += v[1]

troop = self._red_troops[i]
if troop[3] > 0:
v = self._king_blue_tower_center_location - troop[:2]
dist = np.linalg.norm(v)
if move_direction[1][i][1] > dist:
move_direction[1][i] = v/dist, dist
v, dist = move_direction[1][i]
if dist > TROOP_ATTACK_RANGE[int(troop[2])]:
v = v * TROOP_SPEEDS[int(troop[2])] / self.metadata["render_fps"]
troop[0] += v[0]
troop[1] += v[1]

# An episode is done iff the agent has reached the target
terminated = False
reward = 1 if terminated else 0 # Binary sparse rewards
observation = self._get_obs()
info = self._get_info()

if self.render_mode == "human":
self._render_frame()

return observation, reward, terminated, False, info
pass

def render(self):
if self.render_mode == "rgb_array":
return self._render_frame()
pass

def _render_frame(self):
if self.window is None and self.render_mode == "human":
pygame.init()
pygame.display.init()
self.window = pygame.display.set_mode(
(self.window_size_width, self.window_size_height)
)
if self.clock is None and self.render_mode == "human":
self.clock = pygame.time.Clock()

canvas = pygame.Surface((self.window_size_width, self.window_size_height))
canvas.fill((255, 255, 255))
pix_square_size_height = (
self.window_size_height / self.height
) # The height of a single grid square in pixels
pix_square_size_width = (
self.window_size_width / self.width
)

pix_square_size = np.array([pix_square_size_width, pix_square_size_height])


# Draw tower ranges
pygame.draw.circle(
canvas,
(211, 211, 211),
self._king_blue_tower_center_location * pix_square_size,
self._king_KING_TOWER_RANGE * pix_square_size_height,
)
pygame.draw.circle(
canvas,
(211, 211, 211),
self._king_red_tower_center_location * pix_square_size,
self._king_KING_TOWER_RANGE * pix_square_size_height,
)

# Add some gridlines
for x in range(self.height + 1):
pygame.draw.line(
canvas,
(0, 0, (x == self.height // 2 - 1 or x == self.height // 2 + 1) * 255),
(0, pix_square_size_height * x),
(self.window_size_width, pix_square_size_height * x),
width=(1 + (x == self.height // 2 - 1 or x == self.height // 2 + 1)),
)

for x in range(self.width + 1):
pygame.draw.line(
canvas,
0,
(pix_square_size_width * x, 0),
(pix_square_size_width * x, self.window_size_height),
width=1,
)

draw_king_tower(canvas, "red",
self._king_red_tower_center_location,
pix_square_size, self._king_red_tower_health)


for troop in self._blue_troops:
if troop[3] > 0:
draw_troop(canvas, troop, (0,0,155), pix_square_size)

for troop in self._red_troops:
if troop[3] > 0:
draw_troop(canvas, troop, (155,0, 0), pix_square_size)


draw_king_tower(canvas, "blue",
self._king_blue_tower_center_location,
pix_square_size, self._king_blue_tower_health)

if self.render_mode == "human":
# The following line copies our drawings from `canvas` to the visible window
self.window.blit(canvas, canvas.get_rect())
pygame.event.pump()
pygame.display.update()
pass

# We need to ensure that human-rendering occurs at the predefined framerate.
# The following line will automatically add a delay to keep the framerate stable.
self.clock.tick(self.metadata["render_fps"])
else: # rgb_array
return np.transpose(
np.array(pygame.surfarray.pixels3d(canvas)), axes=(1, 0, 2)
)

def close(self):
if self.window is not None:
pygame.display.quit()
pygame.quit()
pass
Loading
Loading