Skip to content

Commit

Permalink
Fixed various issues from artifact code from other project.
Browse files Browse the repository at this point in the history
Codebase Quality: Fixed various issues with code documentation and formatting.
Fixed spelling issues, added new issues for missing code and docstrings.
  • Loading branch information
lunathanael authored Mar 13, 2024
2 parents 82c86ef + 1db94e2 commit 1cd23be
Show file tree
Hide file tree
Showing 16 changed files with 219 additions and 542 deletions.
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

0 comments on commit 1cd23be

Please sign in to comment.