Compare commits

...

117 Commits

Author SHA1 Message Date
f4aa9741e3 Merge pull request 'added points in stats;' (#41) from stats_update into master
Reviewed-on: s464965/WMICraft#41
2022-06-09 23:57:18 +02:00
Angelika Iskra
3591fedfa2 added points in stats; 2022-06-09 23:56:26 +02:00
050a0359b0 Merge pull request 'final attack version! \o/' (#40) from attack_v3 into master
Reviewed-on: s464965/WMICraft#40
2022-06-09 23:26:20 +02:00
c6cc763fe1 final attack version! \o/ 2022-06-09 23:24:57 +02:00
08ea0e945e Merge pull request 'decision_tree_impl' (#39) from decision_tree_impl into master
Reviewed-on: s464965/WMICraft#39
2022-06-09 22:33:23 +02:00
cb9b8e6b2d Merge pull request 'actually WORKING!!!!! one-direction attack with no ghost-knights (only knights, no monsters and fortress)' (#38) from knights_attack_v2 into master
Reviewed-on: s464965/WMICraft#38
2022-06-09 22:31:50 +02:00
GeorgeTom17
dfa408d5fa new csv for decision tree 2022-06-09 22:31:25 +02:00
GeorgeTom17
ebaf97d82c new csv for decision tree 2022-06-09 22:31:02 +02:00
8666a7dbf9 actually WORKING!!!!! one-direction attack with no ghost-knights (only knights, no monsters and fortress) 2022-06-09 22:29:44 +02:00
952e8663b4 Merge pull request 'removed number of fortresses from stats' (#37) from updated_fortress_count into master
Reviewed-on: s464965/WMICraft#37
2022-06-09 20:39:24 +02:00
5f7fea24fb removed number of fortresses from stats 2022-06-09 20:37:46 +02:00
297bd19336 Merge pull request 'credits' (#36) from credits into master
Reviewed-on: s464965/WMICraft#36
2022-06-09 19:04:29 +02:00
bab75274dc Merge pull request 'cnn' (#35) from cnn into master
Reviewed-on: s464965/WMICraft#35
2022-06-09 14:49:55 +02:00
681bf08424 sprzatanie plikow 2022-06-09 14:47:47 +02:00
c8f0dc76b6 Merge branch 'master' of https://git.wmi.amu.edu.pl/s464965/WMICraft into cnn 2022-06-09 14:43:47 +02:00
e31364b21c fixed knights_hp + basic attack ^<>v (improvement in progress) 2022-06-08 12:51:09 +02:00
GeorgeTom17
1482730bee visual changes 2022-06-07 22:36:13 +02:00
GeorgeTom17
29aea5bec4 credits screen 2022-06-07 21:36:31 +02:00
9afd1f366a Merge pull request 'genetic_alg' (#34) from genetic_alg into master
Reviewed-on: s464965/WMICraft#34
2022-06-06 15:19:58 +02:00
Angelika Iskra
6ddfddb2fd fix monsters count in fitness func; 2022-06-06 14:59:24 +02:00
korzepadawid
3f68f4c8a6 bug fix 2022-06-05 10:57:20 +02:00
59a8c49b2d Merge pull request 'fixed bugs in genetic alg; modified fitness;' (#33) from genetic_alg_angela into genetic_alg
Reviewed-on: s464965/WMICraft#33
2022-06-02 13:33:38 +02:00
Angelika Iskra
fd4b34eed7 fixed bugs in genetic alg; modified fitness; 2022-06-02 13:32:59 +02:00
08329f187b Merge pull request 'fix monsters count;' (#32) from genetic_alg_angela into genetic_alg
Reviewed-on: s464965/WMICraft#32
2022-06-02 01:31:50 +02:00
Angelika Iskra
fd081d5c56 fix monsters count; 2022-06-02 01:31:26 +02:00
ee7a324e5d Merge pull request 'genetic_alg_angela' (#31) from genetic_alg_angela into genetic_alg
Reviewed-on: s464965/WMICraft#31
2022-06-02 00:52:53 +02:00
Angelika Iskra
10e61e724c setup genetic alg; 2022-06-02 00:52:16 +02:00
Angelika Iskra
f255178162 fix file not found error; 2022-06-01 19:52:16 +02:00
d80e83cecf Merge pull request 'fixed return type' (#30) from hotfix into genetic_alg
Reviewed-on: s464965/WMICraft#30
2022-05-31 22:13:46 +02:00
korzepadawid
ea105ad140 fixed return type 2022-05-31 22:13:16 +02:00
32847593e6 Merge pull request 'counting islands with dfs' (#29) from genetic_alg_fitness into genetic_alg
Reviewed-on: s464965/WMICraft#29
2022-05-31 21:37:26 +02:00
korzepadawid
f1f2302acd counting islands with dfs 2022-05-31 21:34:31 +02:00
korzepadawid
e89f564dd6 saving sands, trees, monsters etc positions 2022-05-31 15:50:42 +02:00
162e2df890 FINISH 95% test 99% train 2022-05-31 09:25:36 +02:00
Angelika Iskra
e9ef300dde import and export map; 2022-05-31 01:03:00 +02:00
korzepadawid
c7c1feb82c separated code 2022-05-30 23:34:28 +02:00
korzepadawid
7a9d685701 Genome class responsible for keeping grid and lists of positions 2022-05-30 23:27:31 +02:00
korzepadawid
c06624d79c example genome generation 2022-05-29 23:13:45 +02:00
8d73a85707 poprawki 2022-05-27 01:39:52 +02:00
b6ba817d55 update 2022-05-27 01:38:20 +02:00
5c1a1605b8 update 2022-05-26 13:19:17 +02:00
97628965f1 Merge pull request 'cnn' (#28) from cnn into master
Reviewed-on: s464965/WMICraft#28
2022-05-25 19:57:08 +02:00
e8c32ade2a Merge branch 'master' of https://git.wmi.amu.edu.pl/s464965/WMICraft into cnn
 Conflicts:
	common/constants.py
	common/helpers.py
	requirements.txt
2022-05-25 19:54:47 +02:00
c6697bdc79 usuniecie starego nauczonego modelu 2022-05-25 19:50:30 +02:00
088e90ec5b pytorch lighning addition 2022-05-25 19:47:08 +02:00
4d88b300ae Merge pull request 'fixed hp directions' (#26) from code_fix into master
Reviewed-on: s464965/WMICraft#26
2022-05-21 14:32:17 +02:00
94b4cbef58 fixed hp directions 2022-05-21 14:31:02 +02:00
4e3e68d4c3 FINISH 2022-05-18 15:44:07 +02:00
XsedoX
431113a04c pokazuje zdjecie o jakie pytasz 2022-05-18 12:18:59 +02:00
XsedoX
736dbc9616 nie potrzeba filderu all 2022-05-18 10:29:05 +02:00
7ce2f0cc38 Merge pull request 'decision_tree_impl' (#25) from decision_tree_impl into master
Reviewed-on: s464965/WMICraft#25
2022-05-18 10:05:08 +02:00
dc411fae42 neural network sprawny wraz z interfejsem 2022-05-17 22:54:56 +02:00
XsedoX
3fce0a5b57 uzycie sprite.Group.update() - gdy umrze sprite, to healthbar nie bedzie renderowany 2022-05-13 10:37:00 +02:00
XsedoX
89c307535b Merge branch 'master' of https://git.wmi.amu.edu.pl/s464965/WMICraft into stats
 Conflicts:
	logic/game.py
	ui/stats.py
2022-05-13 10:06:23 +02:00
30e2d39d28 Merge pull request 'healthbar' (#24) from healthbar into master
Reviewed-on: s464965/WMICraft#24
2022-05-11 16:53:14 +02:00
504cfd1af0 poprawki do healthbar oraz dostawania obrazen. TRZEBA UZYWAC FUNKCJI W knight.py 2022-05-11 16:50:14 +02:00
korzepadawid
33577c1824 hotfix 2022-05-09 18:22:01 +02:00
korzepadawid
1a4f245087 decision_tree 2022-05-08 20:36:33 +02:00
korzepadawid
8b76d3635b finding neighbour tiles 2022-05-08 19:26:09 +02:00
XsedoX
9aa9552499 hp_bar pod tarczami liczony na podstawie hp knightow 2022-04-29 10:21:22 +02:00
4ab8065879 Merge branch 'master' of https://git.wmi.amu.edu.pl/s464965/WMICraft 2022-04-29 06:29:51 +02:00
9f3c5fb2d5 liczenie rycerzy dziala 2022-04-28 21:50:12 +02:00
7030488a8a Merge pull request 'ESC chyba naprawiony(ale pewnie i tak zaraz znajdziemy jakiegos buga xd)' (#23) from problemy_z_ESC into master
Reviewed-on: s464965/WMICraft#23
2022-04-28 14:24:49 +02:00
fe964ceb7e ESC chyba naprawiony(ale pewnie i tak zaraz znajdziemy jakiegos buga xd) 2022-04-28 14:23:44 +02:00
f9c5f331f2 Merge pull request 'healthbar' (#22) from healthbar into master
Reviewed-on: s464965/WMICraft#22
2022-04-28 14:16:16 +02:00
dca9c82b70 health_bar.py - gotowy 2022-04-28 14:13:59 +02:00
0f6f2354d3 sprite group w level.py zamienilem na LayeredUpdates() oraz dodałem do każdego sprite'a layer, wiec nie beda na siebie nachodzic nigdy 2022-04-28 10:18:17 +02:00
1998f94c3d Merge branch 'master' of https://git.wmi.amu.edu.pl/s464965/WMICraft
 Conflicts:
	logic/game.py
2022-04-28 09:51:50 +02:00
c11fdae8a5 healthbar WIP - team health bars jako klasa health_bar.py 2022-04-28 09:50:24 +02:00
9f62bb1a93 Merge pull request 'a_star' (#21) from a_star into master
Reviewed-on: s464965/WMICraft#21
2022-04-27 19:50:51 +02:00
e5af464eb2 healthbar WIP - team health bars jako klasa health_bar.py 2022-04-24 22:06:20 +02:00
korzepadawid
55c3ea0aa8 fix: changed sand cost 2022-04-13 18:56:39 +02:00
korzepadawid
1dbb1a0e4c feat: field costs 2022-04-13 18:35:02 +02:00
korzepadawid
af2f61984a feat: logs queue 2022-04-12 20:44:27 +02:00
korzepadawid
04f17e3293 feat: a_star without tile costs 2022-04-12 20:21:29 +02:00
korzepadawid
7131d1f905 feat: movement 2022-04-12 19:12:41 +02:00
korzepadawid
6d4891fab8 fix: integration with level 2022-04-12 00:35:01 +02:00
korzepadawid
286164c1dd feat: a_star implementation 2022-04-12 00:21:30 +02:00
korzepadawid
544c5276d5 feat: added a_star method 2022-04-12 00:05:47 +02:00
korzepadawid
2bfaff8a30 update: changed variable name in a_star.py 2022-04-11 23:56:35 +02:00
korzepadawid
6c27199988 feat: result function 2022-04-11 23:53:50 +02:00
korzepadawid
fbbb521a4b fix: actions 2022-04-11 22:52:58 +02:00
korzepadawid
b68013a0cd feat: actions 2022-04-11 21:52:21 +02:00
korzepadawid
019b3c86be feat: expand and child node 2022-04-11 20:04:53 +02:00
korzepadawid
10485ce39d feat: enums 2022-04-11 19:49:32 +02:00
korzepadawid
f4ec5b5869 feat: models 2022-04-11 19:18:03 +02:00
e2fad68813 Merge branch 'master' of https://git.wmi.amu.edu.pl/s464965/WMICraft
 Conflicts:
	common/constants.py
	logic/game.py
	logic/grid.py
	models/knight.py
2022-04-11 17:54:00 +02:00
0a7b6136a5 healthbar WIP 2022-04-11 17:51:46 +02:00
05fcfc76f3 Merge pull request 'bfs' (#19) from bfs into master
Reviewed-on: s464965/WMICraft#19
2022-04-11 13:49:11 +02:00
Angelika Iskra
404eb8e70d refactor bfs; 2022-04-11 13:13:03 +02:00
Angelika Iskra
e98a4da2aa finished bfs; 2022-04-11 12:56:22 +02:00
Angelika Iskra
88297d0b9b added collision to bfs; 2022-04-11 12:08:03 +02:00
Angelika Iskra
57cdb87820 worked on bfs; 2022-04-11 12:00:15 +02:00
Angelika Iskra
8196a5ec62 bfs - knights are moving but idk where 2022-04-11 00:01:57 +02:00
Angelika Iskra
ac74280271 work on new logic; 2022-04-10 20:28:50 +02:00
62e78ce885 odkryłem że istnieje grupa do jednego sprite'a - GroupSingle i zmieniłem grupe z tile'ami na single. 2022-04-09 21:28:31 +02:00
c1d05eb1d1 Merge pull request 'mozna zgetowac tile po row i column do tego jest funkcja get_tile w grid;' (#18) from field into master
Reviewed-on: s464965/WMICraft#18
2022-04-08 17:16:51 +02:00
deabeeac43 mozna zgetowac tile po row i column do tego jest funkcja get_tile w grid;
step_onto -> put_on(zmiana nazwy), dziala poprawnie(maly bug byl);funkcja put_on_tile(row, column, obj) sluzy polozeniu danego obiektu na danym tilu, przyklad uzycia zakomentowany w game.py(wywolujcie po spawnerze, bo nadpisze pozycje xd); dorobilem wlasny exception, gdy tile jest zajety w pliku exceptions.py - moze sie przyda ten plik
2022-04-08 17:15:01 +02:00
e768a534a4 Merge pull request 'zmiana rzedow z sprite.Group na pythonowa tablice' (#17) from grid into master
Reviewed-on: s464965/WMICraft#17
2022-04-08 11:25:28 +02:00
XsedoX
5700cd89c8 zmiana rzedow z sprite.Group na pythonowa tablice 2022-04-08 11:24:03 +02:00
37eb63705d Merge pull request 'controlling fields with water and trees' (#16) from field into master
Reviewed-on: s464965/WMICraft#16
2022-04-06 21:14:41 +02:00
GeorgeTom17
7286171207 controlling fields with water and trees 2022-04-06 21:08:32 +02:00
9b6f9ea003 Merge pull request 'field' (#15) from field into master
Reviewed-on: s464965/WMICraft#15
2022-04-05 15:39:36 +02:00
08ccd74413 Merge branch 'master' of https://git.wmi.amu.edu.pl/s464965/WMICraft 2022-04-05 15:38:30 +02:00
c75299e152 poprawki field.py 2022-04-05 15:38:26 +02:00
d0f9a66888 Merge pull request 'wejscie na pole' (#14) from field into master
Reviewed-on: s464965/WMICraft#14
2022-04-05 10:55:49 +02:00
c927bab887 wejscie na pole 2022-04-05 10:54:30 +02:00
21c05e4f40 Merge pull request 'field zna swoj stan ale go sam nie odswieza(trzeba recznie zmieniac wartosc - todo); field jest teraz spritem(co przyda sie pozniej); spawner.py dostosowany do nowego kodu; game.py dostosowane do zmian; twierdza dalej jest widziana jako 1x1' (#13) from field into master
Reviewed-on: s464965/WMICraft#13
2022-04-05 10:49:57 +02:00
86aa939433 field zna swoj stan ale go sam nie odswieza(trzeba recznie zmieniac wartosc - todo); field jest teraz spritem(co przyda sie pozniej); spawner.py dostosowany do nowego kodu; game.py dostosowane do zmian; twierdza dalej jest widziana jako 1x1 2022-04-05 10:47:45 +02:00
0d6fdcbfea Merge pull request 'knights_queue' (#12) from knights_queue into master
Reviewed-on: s464965/WMICraft#12
2022-04-04 21:58:14 +02:00
korzepadawid
621a36461c fixed bug :~DDD 2022-04-04 21:51:32 +02:00
korzepadawid
4caba91c7e tests: added edge case 2022-04-04 21:27:47 +02:00
korzepadawid
f06ea9e5b0 tests: added test case 2022-04-04 21:23:32 +02:00
korzepadawid
bef17bccd1 feat: round queue 2022-04-04 21:07:40 +02:00
37c58763f8 Merge pull request 'random monster images and stats added (monsters, knights)' (#11) from monsters_and_stats into master
Reviewed-on: s464965/WMICraft#11
2022-03-24 21:46:02 +01:00
d6c281c814 random monster images and stats added (monsters, knights) 2022-03-24 21:45:17 +01:00
65afafb642 Merge pull request 'added castle;' (#10) from castle into master
Reviewed-on: s464965/WMICraft#10
2022-03-24 16:33:08 +01:00
65 changed files with 3449 additions and 273 deletions

3
.gitignore vendored
View File

@ -149,4 +149,5 @@ cython_debug/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear # and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/ .idea/
/algorithms/neural_network/data/

189
algorithms/a_star.py Normal file
View File

@ -0,0 +1,189 @@
from __future__ import annotations
import heapq
from dataclasses import dataclass, field
from typing import Tuple, Optional, List
from algorithms.genetic.const import MAP_ALIASES
from common.constants import ROWS, COLUMNS, LEFT, RIGHT, UP, DOWN
from common.helpers import directions
EMPTY_FIELDS = [MAP_ALIASES.get("SAND"), MAP_ALIASES.get("GRASS"), ' ']
TURN_LEFT = 'TURN_LEFT'
TURN_RIGHT = 'TURN_RIGHT'
FORWARD = 'FORWARD'
@dataclass
class State:
position: Tuple[int, int]
direction: str
def __eq__(self, other: State) -> bool:
return other.position == self.position and self.direction == other.direction
def __lt__(self, state):
return self.position < state.position
def __hash__(self) -> int:
return hash(self.position)
@dataclass
class Node:
state: State
parent: Optional[Node]
action: Optional[str]
grid: List[List[str]]
cost: int = field(init=False)
depth: int = field(init=False)
def __lt__(self, node) -> None:
return self.state < node.state
def __post_init__(self) -> None:
if self.grid[self.state.position[0]][self.state.position[1]] == 'g':
self.cost = 1 if not self.parent else self.parent.cost + 1
else:
self.cost = 2 if not self.parent else self.parent.cost + 2
self.depth = 0 if not self.parent else self.parent.depth + 1
def __hash__(self) -> int:
return hash(self.state)
def expand(node: Node, grid: List[List[str]]) -> List[Node]:
return [child_node(node=node, action=action, grid=grid) for action in actions(node.state, grid)]
def child_node(node: Node, action: str, grid: List[List[str]]) -> Node:
next_state = result(state=node.state, action=action)
return Node(state=next_state, parent=node, action=action, grid=grid)
def next_position(current_position: Tuple[int, int], direction: str) -> Tuple[int, int]:
next_row, next_col = directions[direction]
row, col = current_position
return next_row + row, next_col + col
def valid_move(position: Tuple[int, int], grid: List[List[str]]) -> bool:
row, col = position
return grid[row][col] in EMPTY_FIELDS
def actions(state: State, grid: List[List[str]]) -> List[str]:
possible_actions = [FORWARD, TURN_LEFT, TURN_RIGHT]
row, col = state.position
direction = state.direction
if direction == UP and row == 0:
remove_forward(possible_actions)
if direction == DOWN and row == ROWS - 1:
remove_forward(possible_actions)
if direction == LEFT and col == 0:
remove_forward(possible_actions)
if direction == RIGHT and col == COLUMNS - 1:
remove_forward(possible_actions)
if FORWARD in possible_actions and not valid_move(next_position(state.position, direction), grid):
remove_forward(possible_actions)
return possible_actions
def remove_forward(possible_actions: List[str]) -> None:
if FORWARD in possible_actions:
possible_actions.remove(FORWARD)
def result(state: State, action: str) -> State:
next_state = State(state.position, state.direction)
if state.direction == UP:
if action == TURN_LEFT:
next_state.direction = LEFT
elif action == TURN_RIGHT:
next_state.direction = RIGHT
elif action == FORWARD:
next_state.position = next_position(state.position, UP)
elif state.direction == DOWN:
if action == TURN_LEFT:
next_state.direction = RIGHT
elif action == TURN_RIGHT:
next_state.direction = LEFT
elif action == FORWARD:
next_state.position = next_position(state.position, DOWN)
elif state.direction == LEFT:
if action == TURN_LEFT:
next_state.direction = DOWN
elif action == TURN_RIGHT:
next_state.direction = UP
elif action == FORWARD:
next_state.position = next_position(state.position, LEFT)
elif state.direction == RIGHT:
if action == TURN_LEFT:
next_state.direction = UP
elif action == TURN_RIGHT:
next_state.direction = DOWN
elif action == FORWARD:
next_state.position = next_position(state.position, RIGHT)
return next_state
def goal_test(state: State, goal_list: List[Tuple[int, int]]) -> bool:
return state.position in goal_list
def h(state: State, goal: Tuple[int, int]) -> int:
"""heuristics that calculates Manhattan distance between current position and goal"""
x1, y1 = state.position
x2, y2 = goal
return abs(x1 - x2) + abs(y1 - y2)
def f(current_node: Node, goal: Tuple[int, int]) -> int:
"""f(n) = g(n) + h(n), g stands for current cost, h for heuristics"""
return current_node.cost + h(state=current_node.state, goal=goal)
def get_path_from_start(node: Node) -> List[str]:
path = [node.action]
while node.parent is not None:
node = node.parent
if node.action:
path.append(node.action)
path.reverse()
return path
def a_star(state: State, grid: List[List[str]], goals: List[Tuple[int, int]]) -> List[str]:
node = Node(state=state, parent=None, action=None, grid=grid)
frontier = list()
heapq.heappush(frontier, (f(node, goals[0]), node))
explored = set()
while frontier:
r, node = heapq.heappop(frontier)
if goal_test(node.state, goals):
return get_path_from_start(node)
explored.add(node.state)
for child in expand(node, grid):
p = f(child, goals[0])
if child.state not in explored and (p, child) not in frontier:
heapq.heappush(frontier, (p, child))
elif (r, child) in frontier and r > p:
heapq.heappush(frontier, (p, child))
return []

120
algorithms/bfs.py Normal file
View File

@ -0,0 +1,120 @@
from __future__ import annotations
from typing import List
from common.constants import ACTION, Direction, ROWS, COLUMNS
class State:
def __init__(self, row, column, direction):
self.row = row
self.column = column
self.direction = direction
class Node:
def __init__(self, state, parent=None, action=None):
self.state = state
self.parent = parent
self.action = action
def goal_test(goal_list, state: State):
if (state.row, state.column) in goal_list:
return True
return False
def get_successors(state: State, map):
successors = list()
state_left = State(state.row, state.column, state.direction.left())
successors.append((ACTION.get("rotate_left"), state_left))
state_right = State(state.row, state.column, state.direction.right())
successors.append((ACTION.get("rotate_right"), state_right))
target = go(state.row, state.column, state.direction)
if is_valid_move(map, target[0], target[1]):
state_go = State(target[0], target[1], state.direction)
successors.append((ACTION.get("go"), state_go))
return successors
def graphsearch(initial_state: State, map, goal_list, fringe: List[Node] = None, explored: List[Node] = None):
# fringe and explored initialization
if fringe is None:
fringe = list()
if explored is None:
explored = list()
explored_states = set()
fringe_states = set()
# train Node
fringe.append(Node(initial_state))
fringe_states.add((initial_state.row, initial_state.column, initial_state.direction))
while True:
# fringe empty -> solution not found
if not any(fringe):
print("Brak rozwiazania")
return []
# get first element from fringe
element = fringe.pop(0)
fringe_states.remove((element.state.row, element.state.column, element.state.direction))
# if solution was found, prepare and return actions sequence
if goal_test(goal_list, element.state):
actions_sequence = [element.action]
parent = element.parent
while parent is not None:
# train's action will be None, don't add it
if parent.action is not None:
actions_sequence.append(parent.action)
parent = parent.parent
actions_sequence.reverse()
return actions_sequence
# add current node to explored (prevents infinite cycles)
explored.append(element)
explored_states.add((element.state.row, element.state.column, element.state.direction))
# loop through every possible next action
for successor in get_successors(element.state, map):
# make sure not to fall into a cycle
successor_state = (successor[1].row, successor[1].column, successor[1].direction)
if successor_state not in fringe_states and successor_state not in explored_states:
# create new Node and add it at the end of fringe
new_node = Node(state=successor[1],
parent=element,
action=successor[0])
fringe.append(new_node)
fringe_states.add((new_node.state.row, new_node.state.column, new_node.state.direction))
# TEMPORARY METHOD
def go(row, column, direction):
target = tuple()
if direction == Direction.RIGHT:
target = row, column + 1
elif direction == Direction.LEFT:
target = row, column - 1
elif direction == Direction.UP:
target = row - 1, column
elif direction == Direction.DOWN:
target = row + 1, column
return target
def is_valid_move(map, target_row, target_column):
if 0 <= target_row < ROWS and 0 <= target_column < COLUMNS and map[target_row][target_column] in ['g', 's', ' ']:
return True
return False

View File

@ -0,0 +1,142 @@
from dataclasses import dataclass
import numpy as np
from const import *
from typing import List, Dict, Tuple
import numpy.typing as npt
@dataclass
class Position:
row: int
col: int
@dataclass
class Area:
position: Position
width: int
height: int
AREAS_TO_CROSS = [
# up above left knights spawn
Area(position=Position(row=0, col=0),
width=KNIGHTS_SPAWN_WIDTH,
height=LEFT_KNIGHTS_SPAWN_FIRST_ROW),
# down below left knights spawn
Area(position=Position(row=LEFT_KNIGHTS_SPAWN_FIRST_ROW + KNIGHTS_SPAWN_HEIGHT, col=0),
width=KNIGHTS_SPAWN_WIDTH,
height=ROWS - LEFT_KNIGHTS_SPAWN_FIRST_ROW - KNIGHTS_SPAWN_HEIGHT),
# between left knights spawn and castle
Area(position=Position(row=0, col=KNIGHTS_SPAWN_WIDTH),
width=CASTLE_SPAWN_FIRST_COL - KNIGHTS_SPAWN_WIDTH,
height=ROWS),
# up above castle
Area(position=Position(row=0, col=CASTLE_SPAWN_FIRST_COL),
width=2,
height=CASTLE_SPAWN_FIRST_ROW),
# down below castle
Area(position=Position(row=CASTLE_SPAWN_FIRST_ROW + 2, col=CASTLE_SPAWN_FIRST_COL),
width=2,
height=ROWS - CASTLE_SPAWN_FIRST_ROW - 2),
# between castle and right knights spawn
Area(position=Position(row=0, col=CASTLE_SPAWN_FIRST_COL + 2),
width=RIGHT_KNIGHTS_SPAWN_FIRST_COL - CASTLE_SPAWN_FIRST_COL - 2,
height=ROWS),
# up above right knights spawn
Area(position=Position(row=0, col=RIGHT_KNIGHTS_SPAWN_FIRST_COL),
width=KNIGHTS_SPAWN_WIDTH,
height=RIGHT_KNIGHTS_SPAWN_FIRST_ROW),
# down below right knights spawn
Area(position=Position(row=RIGHT_KNIGHTS_SPAWN_FIRST_ROW + KNIGHTS_SPAWN_HEIGHT, col=RIGHT_KNIGHTS_SPAWN_FIRST_COL),
width=KNIGHTS_SPAWN_WIDTH,
height=ROWS - RIGHT_KNIGHTS_SPAWN_FIRST_ROW - KNIGHTS_SPAWN_HEIGHT),
]
def dfs(grid: npt.NDArray, visited: Dict[Tuple[int, int], bool], position: Position, rows: int, cols: int) -> None:
visited[(position.row, position.col)] = True
row_vector = [0, 0, 1, -1]
col_vector = [-1, 1, 0, 0]
neighbours = []
for i in range(4):
rr = position.row + row_vector[i]
cc = position.col + col_vector[i]
if rr < 0 or rr >= ROWS:
continue
elif cc < 0 or cc >= COLUMNS:
continue
else:
p = Position(rr, cc)
if (p.row, p.col) in visited:
neighbours.append(p)
for neighbour in neighbours:
if not visited[(neighbour.row, neighbour.col)]:
dfs(grid, visited, neighbour, rows, cols)
def get_islands(grid: npt.NDArray, positions: List[Position], rows: int = ROWS, cols: int = COLUMNS) -> List[Position]:
"""it returns list of all islands roots"""
visited = {}
for position in positions:
visited[(position.row, position.col)] = False
islands = 0
roots = []
for position in positions:
if not visited[(position.row, position.col)]:
dfs(grid, visited, position, rows, cols)
roots.append(position)
islands += 1
return roots
def find_neighbours(grid: npt.NDArray, col: int, row: int) -> List[Position]:
dr = [-1, 1, 0, 0]
dc = [0, 0, -1, 1]
neighbours = []
for i in range(4):
rr = row + dr[i]
cc = col + dc[i]
if 0 <= rr < ROWS and 0 <= cc < COLUMNS and grid[rr][cc] == MAP_ALIASES.get('GRASS'):
neighbours.append(Position(row=rr, col=cc))
return neighbours
def get_tiles_positions(grid: npt.NDArray):
sands = []
trees = []
waters = []
monsters = []
for row_num in range(len(grid)):
for col_num in range(len(grid[row_num])):
if grid[row_num][col_num] == MAP_ALIASES.get('WATER'):
waters.append(Position(row=row_num, col=col_num))
elif grid[row_num][col_num] == MAP_ALIASES.get('TREE'):
trees.append(Position(row=row_num, col=col_num))
elif grid[row_num][col_num] == MAP_ALIASES.get('SAND'):
sands.append(Position(row=row_num, col=col_num))
elif grid[row_num][col_num] == MAP_ALIASES.get('MONSTER'):
monsters.append(Position(row=row_num, col=col_num))
return sands, trees, waters, monsters

View File

@ -0,0 +1,29 @@
# map config
KNIGHTS_PER_TEAM_COUNT = 4
SAND_COUNT = 21
WATER_COUNT = 21
TREE_COUNT = 37
MONSTERS_COUNT = 2
CASTLES_COUNT = 1
ROWS = 19
COLUMNS = 24
KNIGHTS_SPAWN_WIDTH = 4
KNIGHTS_SPAWN_HEIGHT = 7
LEFT_KNIGHTS_SPAWN_FIRST_ROW = 6
LEFT_KNIGHTS_SPAWN_FIRST_COL = 0
RIGHT_KNIGHTS_SPAWN_FIRST_ROW = 6
RIGHT_KNIGHTS_SPAWN_FIRST_COL = 20
CASTLE_SPAWN_FIRST_ROW = 7
CASTLE_SPAWN_FIRST_COL = 11
# map aliases
MAP_ALIASES = {
"GRASS": 0,
"SAND": 1,
"WATER": 2,
"TREE": 3,
"MONSTER": 4,
"CASTLE": 5,
"KNIGHT_RED": 6,
"KNIGHT_BLUE": 7,
}

View File

@ -0,0 +1,166 @@
import math
import random
from copy import deepcopy
from random import randrange
from typing import List
import numpy as np
import numpy.typing as npt
from common import Position, get_islands, AREAS_TO_CROSS, find_neighbours, get_tiles_positions
from const import *
class Genome:
grid: npt.NDArray
knights_red: List[Position]
knights_blue: List[Position]
waters: List[Position]
trees: List[Position]
sands: List[Position]
monsters: List[Position]
fitness: int
sand_islands: List[Position]
tree_islands: List[Position]
water_islands: List[Position]
def __init__(self):
self.grid = np.zeros((ROWS, COLUMNS), dtype=int)
self.fitness = 0
self.knights_red = spawn_objects_in_given_area(
grid=self.grid,
object_alias=MAP_ALIASES.get("KNIGHT_RED"),
objects_count=KNIGHTS_PER_TEAM_COUNT,
spawn_position_start=Position(row=LEFT_KNIGHTS_SPAWN_FIRST_ROW, col=LEFT_KNIGHTS_SPAWN_FIRST_COL),
width=KNIGHTS_SPAWN_WIDTH,
height=KNIGHTS_SPAWN_HEIGHT
)
self.knights_blue = spawn_objects_in_given_area(
grid=self.grid,
object_alias=MAP_ALIASES.get("KNIGHT_BLUE"),
objects_count=KNIGHTS_PER_TEAM_COUNT,
spawn_position_start=Position(row=RIGHT_KNIGHTS_SPAWN_FIRST_ROW, col=RIGHT_KNIGHTS_SPAWN_FIRST_COL),
width=KNIGHTS_SPAWN_WIDTH,
height=KNIGHTS_SPAWN_HEIGHT
)
spawn_objects_in_given_area(
grid=self.grid,
object_alias=MAP_ALIASES.get("CASTLE"),
objects_count=4,
spawn_position_start=Position(row=CASTLE_SPAWN_FIRST_ROW, col=CASTLE_SPAWN_FIRST_COL),
width=2,
height=2
)
self.waters = spawn_objects_in_given_area(grid=self.grid, object_alias=MAP_ALIASES.get("WATER"),
objects_count=WATER_COUNT)
self.trees = spawn_objects_in_given_area(grid=self.grid, object_alias=MAP_ALIASES.get("TREE"),
objects_count=TREE_COUNT)
self.sands = spawn_objects_in_given_area(grid=self.grid, object_alias=MAP_ALIASES.get("SAND"),
objects_count=SAND_COUNT)
self.monsters = spawn_objects_in_given_area(grid=self.grid, object_alias=MAP_ALIASES.get("MONSTER"),
objects_count=MONSTERS_COUNT)
self.sand_islands = get_islands(self.grid, self.sands)
self.tree_islands = get_islands(self.grid, self.trees)
self.water_islands = get_islands(self.grid, self.waters)
def update_map(self):
self.sands, self.trees, self.waters, self.monsters = get_tiles_positions(self.grid)
self.sand_islands = get_islands(self.grid, self.sands)
self.tree_islands = get_islands(self.grid, self.trees)
self.water_islands = get_islands(self.grid, self.waters)
def calc_fitness(self):
score = SAND_COUNT + TREE_COUNT + WATER_COUNT
score = score - len(self.sand_islands) - len(self.tree_islands) - len(self.water_islands)
sands, trees, waters, monsters = get_tiles_positions(self.grid)
if len(monsters) != MONSTERS_COUNT:
self.fitness = 0
return
if len(sands) < SAND_COUNT or len(trees) < TREE_COUNT or len(waters) < WATER_COUNT:
self.fitness = 5
return
self.fitness = score
def crossover(self, partner):
# replace a randomly selected part of the grid with partner's part
child = Genome()
child.grid = deepcopy(self.grid)
area_to_cross = random.choice(AREAS_TO_CROSS)
for row in range(area_to_cross.position.row, area_to_cross.position.row + area_to_cross.height):
for col in range(area_to_cross.position.col, area_to_cross.position.col + area_to_cross.width):
child.grid[row][col] = partner.grid[row][col]
child.update_map()
return child
def mutate(self, mutation_rate: float):
# remove 1 item from a random island and add a neighbor to another island
if random.random() < mutation_rate:
# select islands of the same, random type
islands_of_same_type = random.choice([self.sand_islands, self.tree_islands, self.water_islands])
random_index = random.randint(0, len(islands_of_same_type) - 1)
island = islands_of_same_type[random_index]
next_island = islands_of_same_type[(random_index + 1) % len(islands_of_same_type)]
free_tiles_nearby = find_neighbours(self.grid, next_island.col, next_island.row)
tile_type = self.grid[island.row][island.col]
self.grid[island.row][island.col] = MAP_ALIASES.get('GRASS')
# todo: if there are no free tiles around then randomize another next_island
if len(free_tiles_nearby) > 0:
random_free_tile = random.choice(free_tiles_nearby)
island.row = random_free_tile.row
island.col = random_free_tile.col
self.grid[island.row][island.col] = tile_type
self.update_map()
def is_empty(grid: npt.NDArray, position: Position) -> bool:
return grid[position.row, position.col] in [MAP_ALIASES.get("GRASS"), MAP_ALIASES.get("SAND")]
def is_invalid_area(spawn_position_start, height, width) -> bool:
return spawn_position_start.row + height - 1 < 0 or \
spawn_position_start.row + height - 1 >= ROWS or \
spawn_position_start.col + width - 1 < 0 or \
spawn_position_start.col + width - 1 >= COLUMNS
def spawn_objects_in_given_area(grid: npt.NDArray,
object_alias: str,
objects_count: int = 1,
spawn_position_start: Position = Position(row=0, col=0),
width: int = COLUMNS,
height: int = ROWS) -> List[Position]:
if is_invalid_area(spawn_position_start, height, width):
raise ValueError("Invalid spawn area")
objects_remaining = int(objects_count)
positions = []
while objects_remaining > 0:
row = randrange(spawn_position_start.row, spawn_position_start.row + height)
col = randrange(spawn_position_start.col, spawn_position_start.col + width)
position = Position(row=row, col=col)
if is_empty(grid=grid, position=position):
grid[position.row, position.col] = object_alias
positions.append(position)
objects_remaining -= 1
return positions

View File

@ -0,0 +1,26 @@
from algorithms.genetic.genome import Genome
from algorithms.genetic.map_importer_exporter import export_map
from population import Population
def main() -> None:
population_size = 500
mutation_rate = 0.3
population = Population(mutation_rate, population_size, 55)
while not population.evaluate():
# create next generation
population.generate()
# calc fitness
population.calc_fitness()
print(population.best_genome.grid)
print("Fitness of the best: ", population.best_genome.fitness)
export_map(population.best_genome.grid)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,42 @@
import json
import random
import string
from datetime import datetime
from pathlib import Path
import numpy
import numpy.typing as npt
from os import listdir
from os.path import isfile, join
# Save map to file
def export_map(grid: npt.NDArray):
json_data = {"map": grid.tolist()}
now = datetime.now()
file_name = "map_" + now.strftime("%Y_%m_%d_%H_%M_%S") + ".json"
path = Path("../../resources/maps/")
file_to_open = path / file_name
with open(file_to_open, "w+") as write_file:
json.dump(json_data, write_file)
print("Saved map to file " + file_name)
def import_random_map() -> object:
path = "resources/maps"
files = [f for f in listdir(path) if isfile(join(path, f))]
random_map_name = random.choice(files)
return import_map(random_map_name)
# Read map from file
def import_map(file_name: string) -> object:
file_to_open = "resources/maps/" + file_name
with open(file_to_open, "r") as read_file:
print("Reading map from file " + file_name)
decoded_json = json.load(read_file)
decoded_grid = numpy.asarray(decoded_json["map"])
print(decoded_grid)
return decoded_grid.tolist()

View File

@ -0,0 +1,81 @@
import random
from typing import List
import numpy as np
import numpy.typing as npt
from genome import Genome
class Population:
population: List[Genome] = [] # array to hold the current population
mating_pool: List[Genome] = [] # array which we will use for our "mating pool"
generations: int = 0 # number of generations
finished: bool = False # are we finished evolving?
mutation_rate: float
perfect_score: int
best_genome: Genome
def __init__(self, mutation_rate, population_size, perfect_score=20):
self.mutation_rate = mutation_rate
self.perfect_score = perfect_score
for i in range(0, population_size):
new_genome = Genome()
new_genome.calc_fitness()
self.population.append(new_genome)
# create a new generation
def generate(self):
max_fitness = 0
for genome in self.population:
if genome.fitness > max_fitness:
max_fitness = genome.fitness
print("Max fitness of generation " + str(self.generations) + " = " + str(max_fitness))
# refill the population with children from the mating pool
new_population = []
for genome in self.population:
partner_a = self.accept_reject(max_fitness)
partner_b = self.accept_reject(max_fitness)
child = partner_a.crossover(partner_b)
child.mutate(self.mutation_rate)
new_population.append(child)
self.population = new_population
self.generations += 1
# select random with correct probability from population
def accept_reject(self, max_fitness: int):
safe_flag = 0
while safe_flag < 10000:
partner = random.choice(self.population)
r = random.randint(0, max_fitness)
if r < partner.fitness:
return partner
safe_flag += 1
# compute the current "most fit" member of the population
def evaluate(self):
record = 0
best_index = 0
for index in range(len(self.population)):
genome = self.population[index]
if genome.fitness > record:
record = genome.fitness
best_index = index
self.best_genome = self.population[best_index]
if record >= self.perfect_score:
self.finished = True
return self.finished
def calc_fitness(self):
for genome in self.population:
genome.calc_fitness()

View File

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,64 @@
import torch
import pytorch_lightning as pl
import torch.nn as nn
from torch.optim import SGD, Adam, lr_scheduler
import torch.nn.functional as F
from torch.utils.data import DataLoader
from watersandtreegrass import WaterSandTreeGrass
from common.constants import DEVICE, BATCH_SIZE, NUM_EPOCHS, LEARNING_RATE, SETUP_PHOTOS, ID_TO_CLASS
class NeuralNetwork(pl.LightningModule):
def __init__(self, numChannels=3, batch_size=BATCH_SIZE, learning_rate=LEARNING_RATE, num_classes=4):
super(NeuralNetwork, self).__init__()
self.conv1 = nn.Conv2d(numChannels, 24, (3, 3), padding=1)
self.relu1 = nn.ReLU()
self.maxpool1 = nn.MaxPool2d((2, 2), stride=2)
self.conv2 = nn.Conv2d(24, 48, (3, 3), padding=1)
self.relu2 = nn.ReLU()
self.fc1 = nn.Linear(48*18*18, 800)
self.relu3 = nn.ReLU()
self.fc2 = nn.Linear(800, 400)
self.relu4 = nn.ReLU()
self.fc3 = nn.Linear(400, 4)
self.logSoftmax = nn.LogSoftmax(dim=1)
self.batch_size = batch_size
self.learning_rate = learning_rate
def forward(self, x):
x = self.conv1(x)
x = self.relu1(x)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.relu2(x)
x = x.reshape(x.shape[0], -1)
x = self.fc1(x)
x = self.relu3(x)
x = self.fc2(x)
x = self.relu4(x)
x = self.fc3(x)
x = self.logSoftmax(x)
return x
def configure_optimizers(self):
optimizer = Adam(self.parameters(), lr=self.learning_rate)
return optimizer
def training_step(self, batch, batch_idx):
x, y = batch
scores = self(x)
loss = F.nll_loss(scores, y)
return loss
def validation_step(self, batch, batch_idx):
x, y = batch
scores = self(x)
val_loss = F.nll_loss(scores, y)
self.log("val_loss", val_loss, on_step=True, on_epoch=True, sync_dist=True)
def test_step(self, batch, batch_idx):
x, y = batch
scores = self(x)
test_loss = F.nll_loss(scores, y)
self.log("test_loss", test_loss, on_step=True, on_epoch=True, sync_dist=True)

View File

@ -0,0 +1,125 @@
import torch
import common.helpers
from common.constants import DEVICE, BATCH_SIZE, NUM_EPOCHS, LEARNING_RATE, SETUP_PHOTOS, ID_TO_CLASS
from watersandtreegrass import WaterSandTreeGrass
from torch.utils.data import DataLoader
from neural_network import NeuralNetwork
from torchvision.io import read_image, ImageReadMode
import torch.nn as nn
from torch.optim import Adam
import matplotlib.pyplot as plt
import pytorch_lightning as pl
from pytorch_lightning.callbacks import EarlyStopping
import torchvision.transforms.functional as F
from PIL import Image
def check_accuracy_tiles():
answer = 0
for i in range(100):
if what_is_it('../../resources/textures/grass_with_tree.jpg') == 'tree':
answer = answer + 1
print("Accuracy(%) grass_with_tree.jpg", answer)
answer = 0
for i in range(100):
if what_is_it('../../resources/textures/grass2.png') == 'grass':
answer = answer + 1
print("Accuracy(%) grass2.png", answer)
answer = 0
for i in range(100):
if what_is_it('../../resources/textures/grass3.png') == 'grass':
answer = answer + 1
print("Accuracy(%) grass3.png", answer)
answer = 0
for i in range(100):
if what_is_it('../../resources/textures/grass4.png') == 'grass':
answer = answer + 1
print("Accuracy(%) grass4.png", answer)
answer = 0
for i in range(100):
if what_is_it('../../resources/textures/grass1.png') == 'grass':
answer = answer + 1
print("Accuracy(%) grass1.png", answer)
answer = 0
for i in range(100):
if what_is_it('../../resources/textures/water.png') == 'water':
answer = answer + 1
print("Accuracy(%) water.png", answer)
answer = 0
for i in range(100):
if what_is_it('../../resources/textures/sand.png') == 'sand':
answer = answer + 1
print("Accuracy(%) sand.png", answer)
def what_is_it(img_path, show_img=False):
image = Image.open(img_path).convert('RGB')
if show_img:
plt.imshow(image)
plt.show()
image = SETUP_PHOTOS(image).unsqueeze(0)
model = NeuralNetwork.load_from_checkpoint('./lightning_logs/version_20/checkpoints/epoch=3-step=324.ckpt')
with torch.no_grad():
model.eval()
idx = int(model(image).argmax(dim=1))
return ID_TO_CLASS[idx]
def check_accuracy(tset):
model = NeuralNetwork.load_from_checkpoint('./lightning_logs/version_23/checkpoints/epoch=3-step=324.ckpt')
num_correct = 0
num_samples = 0
model = model.to(DEVICE)
model.eval()
with torch.no_grad():
for photo, label in tset:
photo = photo.to(DEVICE)
label = label.to(DEVICE)
scores = model(photo)
predictions = scores.argmax(dim=1)
num_correct += (predictions == label).sum()
num_samples += predictions.size(0)
print(f'Got {num_correct} / {num_samples} with accuracy {float(num_correct)/float(num_samples)*100:.2f}%')
def check_accuracy_data():
trainset = WaterSandTreeGrass('./data/train_csv_file.csv', transform=SETUP_PHOTOS)
testset = WaterSandTreeGrass('./data/test_csv_file.csv', transform=SETUP_PHOTOS)
train_loader = DataLoader(trainset, batch_size=BATCH_SIZE, shuffle=True)
test_loader = DataLoader(testset, batch_size=BATCH_SIZE)
print("Accuracy of train_set:")
check_accuracy(train_loader)
print("Accuracy of test_set:")
check_accuracy(test_loader)
#CNN = NeuralNetwork()
#common.helpers.createCSV()
#trainer = pl.Trainer(accelerator='gpu', callbacks=EarlyStopping('val_loss'), devices=1, max_epochs=NUM_EPOCHS)
#trainer = pl.Trainer(accelerator='gpu', devices=1, auto_lr_find=True, max_epochs=NUM_EPOCHS)
#trainset = WaterSandTreeGrass('./data/train_csv_file.csv', transform=SETUP_PHOTOS)
#testset = WaterSandTreeGrass('./data/test_csv_file.csv', transform=SETUP_PHOTOS)
#train_loader = DataLoader(trainset, batch_size=BATCH_SIZE, shuffle=True)
#test_loader = DataLoader(testset, batch_size=BATCH_SIZE)
#trainer.fit(CNN, train_loader, test_loader)
#trainer.tune(CNN, train_loader, test_loader)
#print(what_is_it('../../resources/textures/grass2.png', True))
#check_accuracy_data()
#check_accuracy_tiles()

View File

@ -0,0 +1,27 @@
import torch
from torch.utils.data import Dataset
import pandas as pd
from torchvision.io import read_image, ImageReadMode
from common.helpers import createCSV
from PIL import Image
class WaterSandTreeGrass(Dataset):
def __init__(self, annotations_file, transform=None):
createCSV()
self.img_labels = pd.read_csv(annotations_file)
self.transform = transform
def __len__(self):
return len(self.img_labels)
def __getitem__(self, idx):
image = Image.open(self.img_labels.iloc[idx, 0]).convert('RGB')
label = torch.tensor(int(self.img_labels.iloc[idx, 1]))
if self.transform:
image = self.transform(image)
return image, label

View File

@ -2,5 +2,6 @@ BLACK = (0, 0, 0)
WHITE = (255, 255, 255) WHITE = (255, 255, 255)
ORANGE = (249, 141, 42) ORANGE = (249, 141, 42)
RED = (255, 58, 58) RED = (255, 58, 58)
GREEN = (0, 255, 0)
FONT_DARK = (37, 37, 37) FONT_DARK = (37, 37, 37)

View File

@ -1,34 +1,96 @@
from enum import Enum
import torchvision.transforms as transforms
import torch
GAME_TITLE = 'WMICraft' GAME_TITLE = 'WMICraft'
WINDOW_HEIGHT = 800 WINDOW_HEIGHT = 800
WINDOW_WIDTH = 1360 WINDOW_WIDTH = 1360
FPS_COUNT = 60
TURN_INTERVAL = 200
GRID_CELL_PADDING = 5 GRID_CELL_PADDING = 5
GRID_CELL_WIDTH = 36 GRID_CELL_SIZE = 36
GRID_CELL_HEIGHT = 36
ROWS = 19 ROWS = 19
COLUMNS = 24 COLUMNS = 24
BORDER_WIDTH = 10 BORDER_WIDTH = 10
BORDER_RADIUS = 5 BORDER_RADIUS = 5
FPS_COUNT = 60
KNIGHTS_SPAWN_WIDTH = 4 KNIGHTS_SPAWN_WIDTH = 4
KNIGHTS_SPAWN_HEIGHT = 5 KNIGHTS_SPAWN_HEIGHT = 7
KNIGHTS_SPAWN_FIRST_ROW = 8 LEFT_KNIGHTS_SPAWN_FIRST_ROW = 6
KNIGHTS_SPAWN_FIRST_COL = 0 LEFT_KNIGHTS_SPAWN_FIRST_COL = 0
MONSTER_SPAWN_WIDTH = 19 RIGHT_KNIGHTS_SPAWN_FIRST_ROW = 6
MONSTER_SPAWN_HEIGHT = 15 RIGHT_KNIGHTS_SPAWN_FIRST_COL = 20
MONSTER_SPAWN_FIRST_COL = 4
MONSTER_SPAWN_FIRST_ROW = 0
CASTLE_SPAWN_WIDTH = 6 CASTLE_SPAWN_WIDTH = 6
CASTLE_SPAWN_HEIGHT = 5 CASTLE_SPAWN_HEIGHT = 5
CASTLE_SPAWN_FIRST_ROW = 7 CASTLE_SPAWN_FIRST_ROW = 7
CASTLE_SPAWN_FIRST_COL = 9 CASTLE_SPAWN_FIRST_COL = 9
NBR_OF_WATER = 16
NBR_OF_TREES = 20
NBR_OF_MONSTERS = 2
NBR_OF_SANDS = 35
TILES = [ TILES = [
'grass1.png', 'grass1.png',
'grass2.png', 'grass2.png',
'grass3.png', 'grass3.png',
# 'grass4.png', 'grass4.png',
# 'grass5.png',
# 'grass6.png',
'sand.png', 'sand.png',
'water.png', 'water.png',
'grass_with_tree.jpg', 'grass_with_tree.jpg',
] ]
class Direction(Enum):
UP = 0
RIGHT = 1
DOWN = 2
LEFT = 3
def right(self):
v = (self.value + 1) % 4
return Direction(v)
def left(self):
v = (self.value - 1) % 4
return Direction(v)
ACTION = {
"rotate_left": -1,
"rotate_right": 1,
"go": 0,
}
LEFT = 'LEFT'
RIGHT = 'RIGHT'
UP = 'UP'
DOWN = 'DOWN'
# HEALTH_BAR
BAR_ANIMATION_SPEED = 1
BAR_WIDTH_MULTIPLIER = 0.9 # (0;1>
BAR_HEIGHT_MULTIPLIER = 0.1
#NEURAL_NETWORK
LEARNING_RATE = 0.000630957344480193
BATCH_SIZE = 64
NUM_EPOCHS = 9
DEVICE = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
print("Using ", DEVICE)
CLASSES = ['grass', 'sand', 'tree', 'water']
SETUP_PHOTOS = transforms.Compose([
transforms.ToTensor(),
transforms.Resize((36, 36)),
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])
ID_TO_CLASS = {i: j for i, j in enumerate(CLASSES)}
CLASS_TO_ID = {value: key for key, value in ID_TO_CLASS.items()}

View File

@ -1,5 +1,22 @@
from typing import Tuple, List
import pygame import pygame
from algorithms.genetic.const import MAP_ALIASES
from common.constants import GRID_CELL_PADDING, GRID_CELL_SIZE, COLUMNS, ROWS, CLASSES, CLASS_TO_ID
import csv
import os
from common.constants import GRID_CELL_PADDING, GRID_CELL_SIZE
from common.constants import ROWS, COLUMNS, LEFT, RIGHT, UP, DOWN
directions = {
LEFT: (0, -1),
RIGHT: (0, 1),
UP: (-1, 0),
DOWN: (1, 0)
}
def draw_text(text, color, surface, x, y, text_size=30, is_bold=False): def draw_text(text, color, surface, x, y, text_size=30, is_bold=False):
if is_bold: if is_bold:
@ -10,3 +27,93 @@ def draw_text(text, color, surface, x, y, text_size=30, is_bold=False):
textrect = textobj.get_rect() textrect = textobj.get_rect()
textrect.topleft = (x, y) textrect.topleft = (x, y)
surface.blit(textobj, textrect) surface.blit(textobj, textrect)
def createCSV():
train_data_path = './data/train'
test_data_path = './data/test'
if os.path.exists(train_data_path):
train_csvfile = open('./data/train_csv_file.csv', 'w', newline="")
writer = csv.writer(train_csvfile)
writer.writerow(["filepath", "type"])
for class_name in CLASSES:
class_dir = train_data_path + "/" + class_name
for filename in os.listdir(class_dir):
f = os.path.join(class_dir, filename)
if os.path.isfile(f):
writer.writerow([f, CLASS_TO_ID[class_name]])
train_csvfile.close()
else:
print("Brak plików do uczenia")
if os.path.exists(test_data_path):
test_csvfile = open('./data/test_csv_file.csv', 'w', newline="")
writer = csv.writer(test_csvfile)
writer.writerow(["filepath", "type"])
for class_name in CLASSES:
class_dir = test_data_path + "/" + class_name
for filename in os.listdir(class_dir):
f = os.path.join(class_dir, filename)
if os.path.isfile(f):
writer.writerow([f, CLASS_TO_ID[class_name]])
test_csvfile.close()
else:
print("Brak plików do testowania")
def print_numbers():
display_surface = pygame.display.get_surface()
font = pygame.font.SysFont('Arial', 16)
for row_index in range(ROWS):
for col_index in range(COLUMNS):
x = (GRID_CELL_PADDING + GRID_CELL_SIZE) * col_index + GRID_CELL_PADDING + 7
y = (GRID_CELL_PADDING + GRID_CELL_SIZE) * row_index + GRID_CELL_PADDING + 16
display_surface.blit(font.render(f'[{col_index}, {row_index}]', True, (255, 0, 0)), (x, y))
pygame.display.update()
# parse array index to screen x or y coordinate
def parse_cord(cord):
return (GRID_CELL_PADDING + GRID_CELL_SIZE) * cord + GRID_CELL_PADDING + 7
def castle_neighbors(map, castle_bottom_right_row, castle_bottom_right_col):
neighbors = []
for row_add in range(-2, 2):
new_row = castle_bottom_right_row + row_add
if 0 <= new_row <= len(map) - 1:
for col_add in range(-2, 2):
new_col = castle_bottom_right_col + col_add
if 0 <= new_col <= len(map) - 1:
if (new_col == castle_bottom_right_col - 1 and new_row == castle_bottom_right_row - 1) \
or (new_col == castle_bottom_right_col and new_row == castle_bottom_right_row - 1) \
or (new_col == castle_bottom_right_col - 1 and new_row == castle_bottom_right_row) \
or (new_col == castle_bottom_right_col and new_row == castle_bottom_right_row):
continue
neighbors.append((new_col, new_row))
return neighbors
def find_neighbours(grid: List[List[int]], col: int, row: int) -> List[Tuple[int, int]]:
dr = [-1, 1, 0, 0]
dc = [0, 0, -1, 1]
neighbours = []
for i in range(4):
rr = row + dr[i]
cc = col + dc[i]
if rr < 0 or cc < 0: continue
if rr >= ROWS or cc >= COLUMNS: continue
if grid[rr][cc] not in [MAP_ALIASES.get("GRASS"), MAP_ALIASES.get("SAND"), '.']: continue
neighbours.append((rr, cc))
return neighbours

0
learning/__init__.py Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,302 @@
tower_dist;mob1_dist;mob2_dist;opp1_dist;opp2_dist;opp3_dist;opp4_dist;agent_hp;tower_hp;mob1_hp;mob2_hp;opp1_hp;opp2_hp;opp3_hp;opp4_hp;goal
24;37;41;19;37;29;20;3;38;13;3;12;9;0;10;tower
1;35;11;38;37;10;34;4;11;15;7;5;0;6;4;tower
7;10;5;41;17;27;19;8;31;14;2;2;7;4;0;mob2
15;3;12;18;9;25;40;8;44;7;5;10;0;5;9;mob1
18;19;21;12;8;13;10;2;44;5;2;9;7;1;10;opp3
19;30;11;34;17;26;13;3;35;4;2;0;12;2;12;mob2
13;17;10;7;9;30;9;2;40;1;5;10;9;12;3;opp4
14;30;21;11;18;21;7;12;28;5;3;4;0;11;0;tower
7;38;7;21;5;25;15;6;14;1;4;12;10;12;8;tower
40;24;41;39;1;9;38;2;12;6;1;1;1;9;11;opp2
41;2;24;15;17;19;3;10;54;6;6;11;1;2;0;mob1
25;42;32;25;6;19;11;12;25;10;3;8;11;4;6;tower
6;19;38;10;32;17;24;5;67;12;3;8;4;10;2;tower
24;35;32;37;10;7;9;4;75;10;7;11;3;12;10;opp2
16;25;11;23;6;35;30;11;71;9;4;8;9;12;0;mob2
37;38;5;22;23;13;25;7;6;3;0;8;12;10;1;mob2
40;42;34;28;34;8;15;1;54;4;5;10;6;9;1;opp4
6;16;2;4;18;27;5;12;73;8;7;10;12;5;11;mob2
9;1;1;18;40;34;20;6;69;12;5;10;6;1;9;mob1
13;22;4;20;25;29;7;7;27;2;1;9;7;1;3;mob2
34;15;39;5;37;23;1;3;15;2;1;1;9;10;5;opp1
38;20;4;22;43;23;22;6;60;6;3;4;4;8;0;mob1
12;31;5;14;23;15;27;1;50;7;2;6;8;6;3;mob2
14;37;2;25;23;26;8;4;53;10;0;12;9;9;11;mob2
33;8;22;35;42;21;21;8;68;6;6;6;6;7;6;mob1
17;11;7;25;6;16;8;5;52;7;7;0;11;2;2;mob2
28;12;20;3;7;32;28;12;74;0;6;0;5;12;9;opp2
21;9;10;38;1;40;41;1;61;3;5;9;7;9;2;opp2
16;1;39;36;7;36;17;1;37;2;6;10;2;7;4;opp2
22;15;31;27;3;40;26;9;71;1;0;10;9;5;9;mob1
27;26;25;9;13;7;27;2;2;9;4;1;1;3;10;opp1
28;43;18;15;21;16;18;3;51;3;4;10;0;5;0;mob2
41;8;4;4;6;11;23;12;54;14;5;6;9;8;0;mob2
15;34;12;17;18;15;19;1;75;5;4;5;1;1;7;opp3
42;36;1;3;28;24;28;2;54;13;3;6;7;11;7;mob2
25;22;23;34;8;42;14;9;40;14;5;2;1;7;12;opp2
41;14;7;43;15;26;19;4;16;3;0;9;12;1;7;mob1
18;40;1;40;38;10;5;2;54;14;4;11;2;11;9;mob2
14;35;5;3;5;43;37;3;23;2;1;10;9;11;8;mob2
40;10;23;25;33;37;26;1;42;14;6;2;11;5;6;mob1
32;9;26;2;39;27;17;1;36;14;0;6;5;9;1;opp4
17;12;15;16;29;18;5;9;75;15;0;12;10;1;11;opp3
23;19;24;31;37;16;23;1;5;8;1;3;7;1;5;tower
16;23;41;24;20;40;29;2;44;11;0;12;7;0;4;tower
21;39;26;16;33;17;29;7;53;12;6;11;2;8;9;tower
9;28;22;33;10;23;8;7;65;15;0;3;9;11;11;mob1
29;9;34;7;14;7;20;5;69;2;5;4;1;4;4;mob1
40;39;19;5;14;3;5;11;49;8;1;9;5;1;11;opp3
19;22;33;29;24;5;39;2;60;15;4;9;5;2;10;opp3
5;28;7;34;12;6;7;6;42;5;0;8;1;11;2;opp4
31;36;11;5;34;7;2;7;57;8;7;3;11;2;4;opp3
20;37;9;38;29;33;14;9;69;0;1;7;0;10;10;mob2
27;5;9;13;26;8;42;7;0;15;5;3;9;10;4;opp1
17;38;5;23;34;42;21;2;7;2;6;5;11;12;7;mob2
37;14;33;32;7;31;41;8;72;1;0;3;6;1;1;opp2
13;35;21;35;33;7;42;9;57;7;7;4;4;12;9;mob2
16;40;35;15;19;35;21;8;53;10;3;10;4;0;10;mob1
25;11;39;2;9;7;18;6;13;0;6;9;4;1;2;mob1
16;2;41;6;20;21;30;7;45;1;4;9;8;0;2;mob1
29;33;23;36;38;27;34;6;76;10;4;6;3;8;8;mob2
15;40;7;41;5;11;14;6;69;6;0;7;11;4;7;mob2
3;33;35;3;29;20;25;12;22;12;7;4;2;1;7;tower
12;14;27;30;18;6;35;10;21;1;1;9;4;3;10;tower
27;21;21;7;10;17;34;5;77;4;5;3;4;0;12;opp1
29;9;9;18;22;1;13;3;78;12;2;5;1;2;7;opp3
4;8;6;1;20;38;39;7;50;14;2;10;11;2;2;mob2
29;10;20;25;24;9;13;11;16;11;6;10;5;5;1;opp4
35;28;30;42;32;28;29;1;32;11;0;10;2;9;3;mob1
22;25;1;4;40;18;26;9;80;4;5;8;4;10;2;mob1
19;29;21;17;35;17;10;9;49;3;0;4;2;9;11;tower
19;40;5;2;10;5;15;2;14;13;0;6;1;2;0;opp3
21;33;13;32;15;15;40;6;66;3;0;4;12;8;2;mob1
14;5;32;32;25;27;1;7;24;8;1;5;5;1;3;opp4
19;15;24;31;31;40;20;3;54;8;2;8;6;2;0;mob2
38;15;1;35;14;15;27;4;33;11;2;1;6;7;11;mob2
1;24;25;23;37;25;19;6;2;12;6;10;0;10;2;tower
37;19;29;19;2;33;20;12;29;3;6;5;7;8;0;opp2
5;16;28;42;16;15;30;8;11;0;6;6;5;12;11;tower
23;2;37;42;40;3;15;7;28;11;2;3;7;0;8;mob1
11;10;31;11;20;18;9;10;76;5;7;7;5;3;8;mob1
11;4;16;7;1;39;25;4;66;12;1;2;3;3;7;opp2
28;2;31;26;43;29;30;9;23;6;7;7;11;9;1;mob1
9;40;12;13;10;41;29;8;27;2;0;5;3;3;6;tower
26;37;23;14;23;17;6;4;56;0;7;9;6;5;2;opp4
26;14;35;13;11;4;38;8;49;3;6;1;11;0;9;mob1
5;42;19;22;36;16;37;9;46;9;7;2;11;4;10;tower
20;24;34;29;24;16;2;3;23;0;1;2;10;2;12;tower
32;40;7;41;38;43;25;8;5;0;0;4;6;10;12;mob2
28;15;25;14;13;7;30;7;37;11;0;6;10;11;1;mob1
40;24;28;10;35;38;23;1;40;11;7;0;11;3;12;mob1
10;6;5;6;20;20;37;12;13;9;0;6;1;10;7;mob1
36;15;39;28;28;17;14;7;37;6;5;12;12;8;11;mob1
12;12;30;43;36;42;35;5;41;12;6;0;3;0;2;tower
37;21;17;8;18;13;33;11;59;4;2;0;0;0;9;mob2
43;6;20;21;17;29;30;1;55;2;7;5;10;6;4;mob1
28;27;28;40;12;39;20;9;71;13;5;12;6;7;10;opp2
3;17;24;31;12;34;43;6;16;11;5;10;1;6;12;tower
17;35;37;15;16;15;29;5;69;3;2;3;4;7;10;opp1
30;19;24;1;12;21;32;6;57;12;2;5;7;0;10;mob1
35;38;20;7;21;38;41;12;66;1;3;4;2;7;8;opp1
25;13;7;39;18;17;7;12;46;0;1;9;2;10;11;mob2
35;19;33;11;13;32;15;1;48;3;3;4;4;5;10;opp1
24;37;36;7;30;32;10;4;5;8;0;7;0;12;7;tower
18;10;11;35;34;9;39;12;44;6;5;1;0;11;6;mob1
1;12;33;5;27;3;18;5;75;8;5;10;11;1;10;opp3
17;34;39;25;35;31;9;5;75;12;5;11;6;6;3;opp4
35;22;3;24;12;18;32;11;76;5;7;5;10;10;8;mob2
27;14;42;4;22;29;20;12;50;1;1;9;8;3;8;mob1
31;30;34;19;35;36;13;6;26;7;5;1;9;11;12;opp1
29;9;3;27;12;11;25;7;68;13;1;10;11;1;2;opp3
40;14;35;23;2;26;29;7;69;8;6;0;3;1;10;opp2
3;33;40;24;34;39;41;3;11;5;1;4;5;0;7;tower
4;14;35;5;5;1;17;3;37;8;0;5;2;8;0;opp2
28;35;25;18;21;12;3;4;20;4;4;6;12;7;1;opp4
42;23;4;26;37;34;35;3;2;15;2;8;12;5;12;mob2
28;43;9;27;23;5;30;2;48;7;5;5;7;12;6;mob2
1;42;1;39;12;8;12;3;6;11;4;4;0;11;0;tower
17;20;15;2;26;11;25;4;62;1;2;1;6;7;5;opp1
23;5;23;22;11;31;23;11;64;4;7;10;1;11;10;opp2
18;41;29;22;12;14;29;1;17;3;0;12;9;8;9;tower
32;11;22;27;27;22;21;6;27;10;4;10;11;11;9;tower
37;10;39;34;7;4;1;4;72;8;7;7;10;5;5;opp4
29;6;26;21;37;34;18;3;65;8;4;0;9;12;9;mob1
9;25;36;39;37;9;20;3;80;11;5;3;11;8;11;tower
34;23;40;25;23;22;40;6;13;14;0;4;9;12;0;tower
5;1;40;21;11;42;30;4;56;0;2;6;10;2;0;mob1
13;20;17;5;35;17;11;5;20;8;0;0;11;2;6;mob1
35;41;7;14;37;33;25;4;15;1;3;6;1;12;12;mob2
19;35;37;29;11;20;26;12;38;3;2;11;8;10;9;tower
32;8;39;14;5;3;9;5;23;12;4;5;1;12;7;opp2
7;20;1;31;35;15;5;3;72;4;4;12;1;7;1;opp4
7;41;39;30;1;32;22;4;36;13;6;3;0;9;9;tower
17;3;40;22;38;40;23;8;43;13;2;5;3;2;4;mob1
35;34;34;42;35;34;33;8;12;11;7;12;10;11;2;tower
42;29;10;22;30;36;27;8;25;3;7;0;2;1;9;mob2
12;2;9;38;13;15;1;7;63;9;4;0;10;1;0;mob1
23;23;43;24;15;20;36;3;19;4;0;8;8;9;11;tower
30;18;16;32;20;41;8;10;58;0;0;6;7;3;1;opp4
7;25;2;31;26;34;15;4;6;11;0;2;5;11;2;tower
38;20;32;30;37;15;8;7;5;9;6;12;1;4;2;opp4
34;37;9;11;2;7;32;7;79;13;4;9;9;5;6;opp3
33;5;14;34;40;21;26;5;31;5;0;4;11;12;1;mob2
30;36;41;19;9;10;9;7;41;1;6;2;4;4;6;opp2
6;35;28;8;25;3;21;2;79;11;0;6;10;2;4;opp3
42;11;27;28;34;14;37;1;10;6;0;5;11;6;4;mob1
36;38;23;21;15;32;25;2;50;10;0;10;5;2;2;mob1
4;26;31;19;18;32;40;5;25;12;7;7;2;8;9;tower
18;29;38;42;4;13;36;9;61;12;5;3;4;7;7;opp2
5;37;22;24;27;26;32;5;65;4;2;1;6;8;7;tower
7;32;10;37;23;43;18;12;54;15;1;6;5;4;5;mob2
21;36;30;41;29;31;2;3;58;9;0;9;6;8;12;mob1
14;17;5;19;16;39;20;5;43;13;1;2;4;3;4;mob2
16;28;7;12;28;40;9;8;69;13;1;11;0;1;1;opp4
33;25;5;18;12;24;24;12;29;11;1;12;7;3;10;mob2
16;2;31;43;29;16;14;9;25;5;5;6;10;5;1;opp4
32;18;36;13;29;40;20;12;13;14;2;10;10;11;11;tower
23;22;23;3;27;24;2;8;62;1;1;3;5;5;8;opp1
15;2;20;16;10;41;18;2;29;4;3;2;11;7;6;mob1
19;13;20;8;4;29;15;12;32;10;1;9;11;9;9;mob1
13;32;41;1;33;33;11;1;28;5;1;10;5;9;6;tower
27;18;5;2;34;27;17;8;66;9;5;8;2;2;12;mob2
24;21;17;3;24;4;17;12;52;5;5;6;3;9;0;opp1
3;5;29;23;27;24;38;1;62;4;4;1;3;0;11;tower
35;3;28;33;31;6;36;11;69;1;7;4;5;5;0;mob1
2;15;17;39;6;29;39;3;43;4;2;11;6;1;3;tower
17;35;10;36;18;4;27;11;5;0;3;5;1;4;3;mob2
18;15;11;40;24;31;10;7;58;7;0;3;8;4;1;opp4
11;23;8;20;7;38;6;3;51;12;0;11;10;10;2;opp4
7;16;13;27;41;1;13;10;25;9;4;11;10;11;5;mob1
1;37;22;9;20;24;36;10;53;12;0;3;9;10;2;tower
10;27;42;42;19;26;39;7;35;0;5;9;2;2;10;tower
10;1;28;12;9;10;7;9;5;2;4;6;0;9;7;mob1
28;19;27;8;3;37;34;11;25;7;1;11;0;1;3;mob1
39;15;23;9;7;32;1;3;52;8;1;6;7;0;2;opp4
11;9;5;16;17;8;29;4;45;3;6;2;12;6;1;mob2
42;40;37;31;37;37;30;4;6;11;3;9;6;2;4;mob1
39;6;12;16;32;13;20;7;52;4;5;0;4;9;3;mob1
18;8;42;26;27;15;13;6;41;11;1;2;4;7;12;mob1
25;32;15;24;31;18;7;12;24;0;4;12;9;3;2;opp4
38;34;32;6;18;27;30;6;8;12;7;12;11;10;9;tower
39;29;10;29;12;42;10;7;15;6;2;3;8;10;5;mob2
26;9;18;24;1;23;27;2;78;8;2;3;6;0;2;opp2
16;30;13;4;10;29;8;4;78;4;1;7;3;5;10;opp2
2;36;22;20;42;1;15;10;30;11;6;2;4;12;11;tower
21;24;25;17;32;4;10;9;31;14;3;11;7;0;5;mob1
37;10;14;10;2;38;23;1;39;10;5;2;10;12;1;opp1
11;34;26;20;26;30;6;4;50;3;4;4;3;1;12;tower
14;8;30;29;17;41;3;4;77;8;6;8;4;3;9;mob1
16;25;10;14;23;15;41;11;24;1;7;8;9;11;8;mob2
36;16;16;24;25;34;17;1;51;13;5;9;11;0;5;mob2
35;17;2;18;29;38;39;12;31;5;5;3;0;2;11;mob2
11;39;41;5;5;27;17;11;30;6;0;0;8;11;11;tower
14;21;13;17;7;21;16;5;48;8;6;10;11;10;0;mob2
9;23;19;33;12;15;34;11;36;10;1;12;11;7;0;tower
32;2;43;38;28;3;27;9;67;9;3;7;8;8;7;opp3
32;2;32;21;13;6;16;10;37;3;7;9;5;12;2;opp4
5;6;43;9;31;15;15;2;75;11;7;4;12;12;11;mob1
26;20;27;28;40;32;17;2;56;6;2;8;7;3;10;mob1
24;10;25;25;25;38;35;5;47;14;0;6;0;2;11;mob1
26;39;26;33;14;39;14;2;24;12;7;3;2;7;12;opp2
3;32;20;38;40;39;25;2;66;8;3;11;10;3;2;tower
40;6;41;21;1;4;25;3;79;10;2;12;8;5;9;mob1
8;39;19;4;14;15;5;10;52;9;0;3;7;12;4;opp1
18;12;29;42;33;43;23;9;69;12;1;5;1;11;2;mob1
4;26;28;23;20;34;14;8;7;0;0;10;1;12;12;tower
25;24;29;40;25;37;33;1;64;8;0;3;0;6;5;mob1
29;12;41;37;3;42;16;11;43;7;3;10;1;0;0;opp2
19;4;8;34;34;1;7;10;62;5;4;10;1;3;1;opp4
11;24;27;43;10;9;32;12;43;10;2;1;0;11;2;tower
30;42;34;12;41;6;6;8;57;6;5;6;8;11;9;opp1
21;25;26;10;18;19;15;8;13;14;4;8;11;0;8;tower
36;24;25;6;10;30;13;1;64;9;3;5;9;4;6;opp1
18;29;20;19;30;21;3;11;36;1;7;4;12;8;0;tower
32;23;3;40;14;8;19;8;77;13;5;10;5;11;5;mob1
30;31;27;13;8;35;35;8;75;0;7;4;1;1;4;opp2
30;43;1;5;3;2;20;2;33;1;2;6;0;10;1;mob2
33;40;5;36;7;25;40;9;72;5;0;6;6;5;6;mob1
42;32;16;30;7;6;14;2;64;1;5;8;0;6;8;mob2
13;25;6;38;26;23;30;2;5;2;7;9;0;10;8;tower
5;24;10;7;15;1;37;6;69;10;6;0;7;2;11;opp3
13;38;12;18;42;23;16;8;21;12;0;6;12;1;10;tower
15;6;32;40;12;26;11;1;1;3;7;7;4;0;8;mob1
9;38;29;26;19;22;28;6;52;8;5;11;2;5;3;tower
18;19;23;43;21;28;19;8;42;14;4;11;0;5;6;tower
6;37;4;35;10;4;26;11;44;6;1;2;5;4;12;mob2
4;26;36;9;34;11;38;10;18;4;7;6;9;12;5;tower
10;36;8;16;8;42;9;11;67;6;4;1;10;9;3;opp1
15;7;22;13;19;16;28;2;20;7;5;0;3;7;8;mob1
16;22;8;35;10;12;32;5;33;0;3;6;10;4;5;mob2
27;4;3;9;29;26;22;1;1;9;1;3;0;8;6;mob2
31;5;29;5;41;17;5;4;12;12;7;8;0;12;4;opp4
29;42;10;39;5;40;43;12;3;15;4;11;2;12;9;opp2
19;22;17;14;36;11;2;9;69;8;0;12;8;8;12;mob1
11;5;3;34;37;37;20;7;37;15;7;4;4;9;12;mob1
5;12;10;4;34;26;30;3;5;3;5;0;8;11;9;tower
31;9;42;22;10;8;32;9;16;6;7;10;5;1;0;opp3
22;27;31;10;21;18;41;3;39;4;6;5;1;12;11;opp2
5;19;26;28;37;26;22;1;31;4;6;10;7;5;11;tower
3;7;2;8;3;26;24;9;12;10;4;7;6;4;7;tower
41;30;13;25;36;41;7;12;11;2;3;7;12;8;3;opp4
23;26;24;13;17;21;24;9;29;15;5;8;0;4;11;tower
36;6;7;18;6;1;15;1;12;14;6;4;1;11;9;opp2
5;23;43;2;5;6;11;10;75;15;2;0;12;11;4;opp4
17;39;8;7;41;14;16;7;45;8;1;2;2;2;8;opp1
37;26;34;5;9;20;18;12;41;13;6;0;0;6;6;tower
40;35;12;6;10;10;18;10;29;14;1;2;11;0;11;opp1
2;35;18;2;9;34;10;4;42;0;0;2;3;10;6;opp1
1;14;1;3;17;8;39;8;56;3;4;2;1;5;4;mob2
40;28;8;20;37;2;42;10;19;8;1;3;7;8;0;mob2
31;28;14;3;6;17;1;7;45;2;3;2;9;3;0;opp1
24;13;15;11;38;28;13;12;51;3;4;6;4;2;12;mob2
30;38;6;26;11;11;30;4;40;15;2;6;4;1;8;opp3
42;37;32;2;5;19;35;2;64;9;5;12;3;8;3;opp2
23;24;32;40;4;24;1;3;78;14;4;5;9;10;2;opp4
5;22;19;22;41;3;34;11;50;5;2;6;10;1;3;opp3
3;38;20;2;25;6;25;6;59;15;3;3;0;10;8;opp1
33;41;39;42;38;29;27;5;33;14;5;5;2;1;12;opp3
20;10;27;16;14;7;35;1;24;13;6;7;11;4;4;opp3
6;8;4;35;12;40;15;10;38;12;7;0;10;6;5;mob2
18;41;35;8;13;14;4;1;70;6;1;0;7;11;6;opp4
39;42;12;28;11;40;7;3;71;3;7;5;2;1;10;opp2
32;32;24;19;13;14;40;12;18;6;1;1;8;11;10;opp1
31;14;16;12;33;25;5;5;8;8;6;8;6;5;2;opp4
38;31;34;27;5;26;27;8;75;3;6;6;10;1;5;mob1
2;7;23;8;24;28;20;2;31;5;7;9;0;11;11;tower
39;43;17;21;31;13;41;8;43;10;5;4;10;6;8;mob1
3;6;17;7;22;23;22;6;40;8;6;7;1;7;11;tower
3;42;13;24;32;1;33;5;68;7;0;4;6;1;10;opp3
12;26;4;18;2;42;29;10;41;11;3;10;10;10;9;mob2
21;26;7;24;31;10;33;4;51;1;2;8;2;8;3;mob2
39;22;7;20;5;29;38;10;8;4;1;9;12;11;0;mob2
20;19;1;22;36;13;5;7;4;3;7;3;9;1;2;mob2
4;7;42;17;6;14;26;11;45;9;4;10;6;11;2;tower
23;27;11;25;38;26;34;10;77;13;7;4;3;5;9;mob1
40;10;29;8;7;32;32;12;32;9;2;10;11;2;4;mob2
37;22;34;17;17;25;40;5;74;9;3;3;8;2;9;opp1
23;10;36;43;31;26;32;5;59;1;1;2;3;8;3;mob1
14;32;8;4;17;33;8;9;74;5;1;7;12;7;3;mob2
8;14;21;26;16;43;10;5;26;1;1;5;0;1;5;tower
7;3;19;7;39;28;12;11;32;9;0;7;12;11;10;tower
11;21;13;31;30;15;43;10;73;6;1;3;11;9;12;mob2
28;10;32;28;26;36;38;4;15;11;0;12;11;12;7;mob1
39;2;25;11;11;25;35;8;36;14;0;9;10;1;7;mob1
43;41;39;38;16;5;35;12;48;14;4;7;3;11;6;opp2
36;31;28;41;40;23;23;11;27;7;2;11;0;6;11;mob1
28;8;38;39;31;3;7;7;47;0;6;8;0;2;6;opp3
5;8;19;26;26;9;1;8;66;15;3;8;11;7;7;opp4
36;28;27;7;25;1;21;10;63;0;6;4;2;10;4;opp1
19;3;6;35;5;29;3;9;63;2;7;3;12;1;3;opp4
27;5;36;31;6;10;27;12;9;8;6;10;9;6;11;mob1
21;21;27;18;26;9;39;1;43;6;3;5;10;0;7;mob1
12;39;15;31;32;9;39;7;26;4;0;12;5;0;12;tower
41;18;22;33;25;6;37;1;77;14;7;12;7;4;8;opp3
8;33;19;22;5;36;28;3;69;15;5;5;0;7;3;mob1
24;8;15;16;21;18;15;9;78;4;0;4;9;5;4;mob2
38;24;26;28;41;21;43;2;65;15;3;1;10;5;4;mob1
33;17;6;4;34;36;25;6;35;4;4;10;9;0;3;mob2
29;25;30;19;35;38;33;6;68;5;1;0;5;11;6;mob1
23;43;41;25;27;26;19;7;12;8;3;4;10;11;9;tower
7;9;18;31;36;21;16;4;23;8;4;9;8;11;5;tower
35;21;39;36;36;37;33;10;41;9;4;1;0;7;0;mob1
1 tower_dist mob1_dist mob2_dist opp1_dist opp2_dist opp3_dist opp4_dist agent_hp tower_hp mob1_hp mob2_hp opp1_hp opp2_hp opp3_hp opp4_hp goal
2 24 37 41 19 37 29 20 3 38 13 3 12 9 0 10 tower
3 1 35 11 38 37 10 34 4 11 15 7 5 0 6 4 tower
4 7 10 5 41 17 27 19 8 31 14 2 2 7 4 0 mob2
5 15 3 12 18 9 25 40 8 44 7 5 10 0 5 9 mob1
6 18 19 21 12 8 13 10 2 44 5 2 9 7 1 10 opp3
7 19 30 11 34 17 26 13 3 35 4 2 0 12 2 12 mob2
8 13 17 10 7 9 30 9 2 40 1 5 10 9 12 3 opp4
9 14 30 21 11 18 21 7 12 28 5 3 4 0 11 0 tower
10 7 38 7 21 5 25 15 6 14 1 4 12 10 12 8 tower
11 40 24 41 39 1 9 38 2 12 6 1 1 1 9 11 opp2
12 41 2 24 15 17 19 3 10 54 6 6 11 1 2 0 mob1
13 25 42 32 25 6 19 11 12 25 10 3 8 11 4 6 tower
14 6 19 38 10 32 17 24 5 67 12 3 8 4 10 2 tower
15 24 35 32 37 10 7 9 4 75 10 7 11 3 12 10 opp2
16 16 25 11 23 6 35 30 11 71 9 4 8 9 12 0 mob2
17 37 38 5 22 23 13 25 7 6 3 0 8 12 10 1 mob2
18 40 42 34 28 34 8 15 1 54 4 5 10 6 9 1 opp4
19 6 16 2 4 18 27 5 12 73 8 7 10 12 5 11 mob2
20 9 1 1 18 40 34 20 6 69 12 5 10 6 1 9 mob1
21 13 22 4 20 25 29 7 7 27 2 1 9 7 1 3 mob2
22 34 15 39 5 37 23 1 3 15 2 1 1 9 10 5 opp1
23 38 20 4 22 43 23 22 6 60 6 3 4 4 8 0 mob1
24 12 31 5 14 23 15 27 1 50 7 2 6 8 6 3 mob2
25 14 37 2 25 23 26 8 4 53 10 0 12 9 9 11 mob2
26 33 8 22 35 42 21 21 8 68 6 6 6 6 7 6 mob1
27 17 11 7 25 6 16 8 5 52 7 7 0 11 2 2 mob2
28 28 12 20 3 7 32 28 12 74 0 6 0 5 12 9 opp2
29 21 9 10 38 1 40 41 1 61 3 5 9 7 9 2 opp2
30 16 1 39 36 7 36 17 1 37 2 6 10 2 7 4 opp2
31 22 15 31 27 3 40 26 9 71 1 0 10 9 5 9 mob1
32 27 26 25 9 13 7 27 2 2 9 4 1 1 3 10 opp1
33 28 43 18 15 21 16 18 3 51 3 4 10 0 5 0 mob2
34 41 8 4 4 6 11 23 12 54 14 5 6 9 8 0 mob2
35 15 34 12 17 18 15 19 1 75 5 4 5 1 1 7 opp3
36 42 36 1 3 28 24 28 2 54 13 3 6 7 11 7 mob2
37 25 22 23 34 8 42 14 9 40 14 5 2 1 7 12 opp2
38 41 14 7 43 15 26 19 4 16 3 0 9 12 1 7 mob1
39 18 40 1 40 38 10 5 2 54 14 4 11 2 11 9 mob2
40 14 35 5 3 5 43 37 3 23 2 1 10 9 11 8 mob2
41 40 10 23 25 33 37 26 1 42 14 6 2 11 5 6 mob1
42 32 9 26 2 39 27 17 1 36 14 0 6 5 9 1 opp4
43 17 12 15 16 29 18 5 9 75 15 0 12 10 1 11 opp3
44 23 19 24 31 37 16 23 1 5 8 1 3 7 1 5 tower
45 16 23 41 24 20 40 29 2 44 11 0 12 7 0 4 tower
46 21 39 26 16 33 17 29 7 53 12 6 11 2 8 9 tower
47 9 28 22 33 10 23 8 7 65 15 0 3 9 11 11 mob1
48 29 9 34 7 14 7 20 5 69 2 5 4 1 4 4 mob1
49 40 39 19 5 14 3 5 11 49 8 1 9 5 1 11 opp3
50 19 22 33 29 24 5 39 2 60 15 4 9 5 2 10 opp3
51 5 28 7 34 12 6 7 6 42 5 0 8 1 11 2 opp4
52 31 36 11 5 34 7 2 7 57 8 7 3 11 2 4 opp3
53 20 37 9 38 29 33 14 9 69 0 1 7 0 10 10 mob2
54 27 5 9 13 26 8 42 7 0 15 5 3 9 10 4 opp1
55 17 38 5 23 34 42 21 2 7 2 6 5 11 12 7 mob2
56 37 14 33 32 7 31 41 8 72 1 0 3 6 1 1 opp2
57 13 35 21 35 33 7 42 9 57 7 7 4 4 12 9 mob2
58 16 40 35 15 19 35 21 8 53 10 3 10 4 0 10 mob1
59 25 11 39 2 9 7 18 6 13 0 6 9 4 1 2 mob1
60 16 2 41 6 20 21 30 7 45 1 4 9 8 0 2 mob1
61 29 33 23 36 38 27 34 6 76 10 4 6 3 8 8 mob2
62 15 40 7 41 5 11 14 6 69 6 0 7 11 4 7 mob2
63 3 33 35 3 29 20 25 12 22 12 7 4 2 1 7 tower
64 12 14 27 30 18 6 35 10 21 1 1 9 4 3 10 tower
65 27 21 21 7 10 17 34 5 77 4 5 3 4 0 12 opp1
66 29 9 9 18 22 1 13 3 78 12 2 5 1 2 7 opp3
67 4 8 6 1 20 38 39 7 50 14 2 10 11 2 2 mob2
68 29 10 20 25 24 9 13 11 16 11 6 10 5 5 1 opp4
69 35 28 30 42 32 28 29 1 32 11 0 10 2 9 3 mob1
70 22 25 1 4 40 18 26 9 80 4 5 8 4 10 2 mob1
71 19 29 21 17 35 17 10 9 49 3 0 4 2 9 11 tower
72 19 40 5 2 10 5 15 2 14 13 0 6 1 2 0 opp3
73 21 33 13 32 15 15 40 6 66 3 0 4 12 8 2 mob1
74 14 5 32 32 25 27 1 7 24 8 1 5 5 1 3 opp4
75 19 15 24 31 31 40 20 3 54 8 2 8 6 2 0 mob2
76 38 15 1 35 14 15 27 4 33 11 2 1 6 7 11 mob2
77 1 24 25 23 37 25 19 6 2 12 6 10 0 10 2 tower
78 37 19 29 19 2 33 20 12 29 3 6 5 7 8 0 opp2
79 5 16 28 42 16 15 30 8 11 0 6 6 5 12 11 tower
80 23 2 37 42 40 3 15 7 28 11 2 3 7 0 8 mob1
81 11 10 31 11 20 18 9 10 76 5 7 7 5 3 8 mob1
82 11 4 16 7 1 39 25 4 66 12 1 2 3 3 7 opp2
83 28 2 31 26 43 29 30 9 23 6 7 7 11 9 1 mob1
84 9 40 12 13 10 41 29 8 27 2 0 5 3 3 6 tower
85 26 37 23 14 23 17 6 4 56 0 7 9 6 5 2 opp4
86 26 14 35 13 11 4 38 8 49 3 6 1 11 0 9 mob1
87 5 42 19 22 36 16 37 9 46 9 7 2 11 4 10 tower
88 20 24 34 29 24 16 2 3 23 0 1 2 10 2 12 tower
89 32 40 7 41 38 43 25 8 5 0 0 4 6 10 12 mob2
90 28 15 25 14 13 7 30 7 37 11 0 6 10 11 1 mob1
91 40 24 28 10 35 38 23 1 40 11 7 0 11 3 12 mob1
92 10 6 5 6 20 20 37 12 13 9 0 6 1 10 7 mob1
93 36 15 39 28 28 17 14 7 37 6 5 12 12 8 11 mob1
94 12 12 30 43 36 42 35 5 41 12 6 0 3 0 2 tower
95 37 21 17 8 18 13 33 11 59 4 2 0 0 0 9 mob2
96 43 6 20 21 17 29 30 1 55 2 7 5 10 6 4 mob1
97 28 27 28 40 12 39 20 9 71 13 5 12 6 7 10 opp2
98 3 17 24 31 12 34 43 6 16 11 5 10 1 6 12 tower
99 17 35 37 15 16 15 29 5 69 3 2 3 4 7 10 opp1
100 30 19 24 1 12 21 32 6 57 12 2 5 7 0 10 mob1
101 35 38 20 7 21 38 41 12 66 1 3 4 2 7 8 opp1
102 25 13 7 39 18 17 7 12 46 0 1 9 2 10 11 mob2
103 35 19 33 11 13 32 15 1 48 3 3 4 4 5 10 opp1
104 24 37 36 7 30 32 10 4 5 8 0 7 0 12 7 tower
105 18 10 11 35 34 9 39 12 44 6 5 1 0 11 6 mob1
106 1 12 33 5 27 3 18 5 75 8 5 10 11 1 10 opp3
107 17 34 39 25 35 31 9 5 75 12 5 11 6 6 3 opp4
108 35 22 3 24 12 18 32 11 76 5 7 5 10 10 8 mob2
109 27 14 42 4 22 29 20 12 50 1 1 9 8 3 8 mob1
110 31 30 34 19 35 36 13 6 26 7 5 1 9 11 12 opp1
111 29 9 3 27 12 11 25 7 68 13 1 10 11 1 2 opp3
112 40 14 35 23 2 26 29 7 69 8 6 0 3 1 10 opp2
113 3 33 40 24 34 39 41 3 11 5 1 4 5 0 7 tower
114 4 14 35 5 5 1 17 3 37 8 0 5 2 8 0 opp2
115 28 35 25 18 21 12 3 4 20 4 4 6 12 7 1 opp4
116 42 23 4 26 37 34 35 3 2 15 2 8 12 5 12 mob2
117 28 43 9 27 23 5 30 2 48 7 5 5 7 12 6 mob2
118 1 42 1 39 12 8 12 3 6 11 4 4 0 11 0 tower
119 17 20 15 2 26 11 25 4 62 1 2 1 6 7 5 opp1
120 23 5 23 22 11 31 23 11 64 4 7 10 1 11 10 opp2
121 18 41 29 22 12 14 29 1 17 3 0 12 9 8 9 tower
122 32 11 22 27 27 22 21 6 27 10 4 10 11 11 9 tower
123 37 10 39 34 7 4 1 4 72 8 7 7 10 5 5 opp4
124 29 6 26 21 37 34 18 3 65 8 4 0 9 12 9 mob1
125 9 25 36 39 37 9 20 3 80 11 5 3 11 8 11 tower
126 34 23 40 25 23 22 40 6 13 14 0 4 9 12 0 tower
127 5 1 40 21 11 42 30 4 56 0 2 6 10 2 0 mob1
128 13 20 17 5 35 17 11 5 20 8 0 0 11 2 6 mob1
129 35 41 7 14 37 33 25 4 15 1 3 6 1 12 12 mob2
130 19 35 37 29 11 20 26 12 38 3 2 11 8 10 9 tower
131 32 8 39 14 5 3 9 5 23 12 4 5 1 12 7 opp2
132 7 20 1 31 35 15 5 3 72 4 4 12 1 7 1 opp4
133 7 41 39 30 1 32 22 4 36 13 6 3 0 9 9 tower
134 17 3 40 22 38 40 23 8 43 13 2 5 3 2 4 mob1
135 35 34 34 42 35 34 33 8 12 11 7 12 10 11 2 tower
136 42 29 10 22 30 36 27 8 25 3 7 0 2 1 9 mob2
137 12 2 9 38 13 15 1 7 63 9 4 0 10 1 0 mob1
138 23 23 43 24 15 20 36 3 19 4 0 8 8 9 11 tower
139 30 18 16 32 20 41 8 10 58 0 0 6 7 3 1 opp4
140 7 25 2 31 26 34 15 4 6 11 0 2 5 11 2 tower
141 38 20 32 30 37 15 8 7 5 9 6 12 1 4 2 opp4
142 34 37 9 11 2 7 32 7 79 13 4 9 9 5 6 opp3
143 33 5 14 34 40 21 26 5 31 5 0 4 11 12 1 mob2
144 30 36 41 19 9 10 9 7 41 1 6 2 4 4 6 opp2
145 6 35 28 8 25 3 21 2 79 11 0 6 10 2 4 opp3
146 42 11 27 28 34 14 37 1 10 6 0 5 11 6 4 mob1
147 36 38 23 21 15 32 25 2 50 10 0 10 5 2 2 mob1
148 4 26 31 19 18 32 40 5 25 12 7 7 2 8 9 tower
149 18 29 38 42 4 13 36 9 61 12 5 3 4 7 7 opp2
150 5 37 22 24 27 26 32 5 65 4 2 1 6 8 7 tower
151 7 32 10 37 23 43 18 12 54 15 1 6 5 4 5 mob2
152 21 36 30 41 29 31 2 3 58 9 0 9 6 8 12 mob1
153 14 17 5 19 16 39 20 5 43 13 1 2 4 3 4 mob2
154 16 28 7 12 28 40 9 8 69 13 1 11 0 1 1 opp4
155 33 25 5 18 12 24 24 12 29 11 1 12 7 3 10 mob2
156 16 2 31 43 29 16 14 9 25 5 5 6 10 5 1 opp4
157 32 18 36 13 29 40 20 12 13 14 2 10 10 11 11 tower
158 23 22 23 3 27 24 2 8 62 1 1 3 5 5 8 opp1
159 15 2 20 16 10 41 18 2 29 4 3 2 11 7 6 mob1
160 19 13 20 8 4 29 15 12 32 10 1 9 11 9 9 mob1
161 13 32 41 1 33 33 11 1 28 5 1 10 5 9 6 tower
162 27 18 5 2 34 27 17 8 66 9 5 8 2 2 12 mob2
163 24 21 17 3 24 4 17 12 52 5 5 6 3 9 0 opp1
164 3 5 29 23 27 24 38 1 62 4 4 1 3 0 11 tower
165 35 3 28 33 31 6 36 11 69 1 7 4 5 5 0 mob1
166 2 15 17 39 6 29 39 3 43 4 2 11 6 1 3 tower
167 17 35 10 36 18 4 27 11 5 0 3 5 1 4 3 mob2
168 18 15 11 40 24 31 10 7 58 7 0 3 8 4 1 opp4
169 11 23 8 20 7 38 6 3 51 12 0 11 10 10 2 opp4
170 7 16 13 27 41 1 13 10 25 9 4 11 10 11 5 mob1
171 1 37 22 9 20 24 36 10 53 12 0 3 9 10 2 tower
172 10 27 42 42 19 26 39 7 35 0 5 9 2 2 10 tower
173 10 1 28 12 9 10 7 9 5 2 4 6 0 9 7 mob1
174 28 19 27 8 3 37 34 11 25 7 1 11 0 1 3 mob1
175 39 15 23 9 7 32 1 3 52 8 1 6 7 0 2 opp4
176 11 9 5 16 17 8 29 4 45 3 6 2 12 6 1 mob2
177 42 40 37 31 37 37 30 4 6 11 3 9 6 2 4 mob1
178 39 6 12 16 32 13 20 7 52 4 5 0 4 9 3 mob1
179 18 8 42 26 27 15 13 6 41 11 1 2 4 7 12 mob1
180 25 32 15 24 31 18 7 12 24 0 4 12 9 3 2 opp4
181 38 34 32 6 18 27 30 6 8 12 7 12 11 10 9 tower
182 39 29 10 29 12 42 10 7 15 6 2 3 8 10 5 mob2
183 26 9 18 24 1 23 27 2 78 8 2 3 6 0 2 opp2
184 16 30 13 4 10 29 8 4 78 4 1 7 3 5 10 opp2
185 2 36 22 20 42 1 15 10 30 11 6 2 4 12 11 tower
186 21 24 25 17 32 4 10 9 31 14 3 11 7 0 5 mob1
187 37 10 14 10 2 38 23 1 39 10 5 2 10 12 1 opp1
188 11 34 26 20 26 30 6 4 50 3 4 4 3 1 12 tower
189 14 8 30 29 17 41 3 4 77 8 6 8 4 3 9 mob1
190 16 25 10 14 23 15 41 11 24 1 7 8 9 11 8 mob2
191 36 16 16 24 25 34 17 1 51 13 5 9 11 0 5 mob2
192 35 17 2 18 29 38 39 12 31 5 5 3 0 2 11 mob2
193 11 39 41 5 5 27 17 11 30 6 0 0 8 11 11 tower
194 14 21 13 17 7 21 16 5 48 8 6 10 11 10 0 mob2
195 9 23 19 33 12 15 34 11 36 10 1 12 11 7 0 tower
196 32 2 43 38 28 3 27 9 67 9 3 7 8 8 7 opp3
197 32 2 32 21 13 6 16 10 37 3 7 9 5 12 2 opp4
198 5 6 43 9 31 15 15 2 75 11 7 4 12 12 11 mob1
199 26 20 27 28 40 32 17 2 56 6 2 8 7 3 10 mob1
200 24 10 25 25 25 38 35 5 47 14 0 6 0 2 11 mob1
201 26 39 26 33 14 39 14 2 24 12 7 3 2 7 12 opp2
202 3 32 20 38 40 39 25 2 66 8 3 11 10 3 2 tower
203 40 6 41 21 1 4 25 3 79 10 2 12 8 5 9 mob1
204 8 39 19 4 14 15 5 10 52 9 0 3 7 12 4 opp1
205 18 12 29 42 33 43 23 9 69 12 1 5 1 11 2 mob1
206 4 26 28 23 20 34 14 8 7 0 0 10 1 12 12 tower
207 25 24 29 40 25 37 33 1 64 8 0 3 0 6 5 mob1
208 29 12 41 37 3 42 16 11 43 7 3 10 1 0 0 opp2
209 19 4 8 34 34 1 7 10 62 5 4 10 1 3 1 opp4
210 11 24 27 43 10 9 32 12 43 10 2 1 0 11 2 tower
211 30 42 34 12 41 6 6 8 57 6 5 6 8 11 9 opp1
212 21 25 26 10 18 19 15 8 13 14 4 8 11 0 8 tower
213 36 24 25 6 10 30 13 1 64 9 3 5 9 4 6 opp1
214 18 29 20 19 30 21 3 11 36 1 7 4 12 8 0 tower
215 32 23 3 40 14 8 19 8 77 13 5 10 5 11 5 mob1
216 30 31 27 13 8 35 35 8 75 0 7 4 1 1 4 opp2
217 30 43 1 5 3 2 20 2 33 1 2 6 0 10 1 mob2
218 33 40 5 36 7 25 40 9 72 5 0 6 6 5 6 mob1
219 42 32 16 30 7 6 14 2 64 1 5 8 0 6 8 mob2
220 13 25 6 38 26 23 30 2 5 2 7 9 0 10 8 tower
221 5 24 10 7 15 1 37 6 69 10 6 0 7 2 11 opp3
222 13 38 12 18 42 23 16 8 21 12 0 6 12 1 10 tower
223 15 6 32 40 12 26 11 1 1 3 7 7 4 0 8 mob1
224 9 38 29 26 19 22 28 6 52 8 5 11 2 5 3 tower
225 18 19 23 43 21 28 19 8 42 14 4 11 0 5 6 tower
226 6 37 4 35 10 4 26 11 44 6 1 2 5 4 12 mob2
227 4 26 36 9 34 11 38 10 18 4 7 6 9 12 5 tower
228 10 36 8 16 8 42 9 11 67 6 4 1 10 9 3 opp1
229 15 7 22 13 19 16 28 2 20 7 5 0 3 7 8 mob1
230 16 22 8 35 10 12 32 5 33 0 3 6 10 4 5 mob2
231 27 4 3 9 29 26 22 1 1 9 1 3 0 8 6 mob2
232 31 5 29 5 41 17 5 4 12 12 7 8 0 12 4 opp4
233 29 42 10 39 5 40 43 12 3 15 4 11 2 12 9 opp2
234 19 22 17 14 36 11 2 9 69 8 0 12 8 8 12 mob1
235 11 5 3 34 37 37 20 7 37 15 7 4 4 9 12 mob1
236 5 12 10 4 34 26 30 3 5 3 5 0 8 11 9 tower
237 31 9 42 22 10 8 32 9 16 6 7 10 5 1 0 opp3
238 22 27 31 10 21 18 41 3 39 4 6 5 1 12 11 opp2
239 5 19 26 28 37 26 22 1 31 4 6 10 7 5 11 tower
240 3 7 2 8 3 26 24 9 12 10 4 7 6 4 7 tower
241 41 30 13 25 36 41 7 12 11 2 3 7 12 8 3 opp4
242 23 26 24 13 17 21 24 9 29 15 5 8 0 4 11 tower
243 36 6 7 18 6 1 15 1 12 14 6 4 1 11 9 opp2
244 5 23 43 2 5 6 11 10 75 15 2 0 12 11 4 opp4
245 17 39 8 7 41 14 16 7 45 8 1 2 2 2 8 opp1
246 37 26 34 5 9 20 18 12 41 13 6 0 0 6 6 tower
247 40 35 12 6 10 10 18 10 29 14 1 2 11 0 11 opp1
248 2 35 18 2 9 34 10 4 42 0 0 2 3 10 6 opp1
249 1 14 1 3 17 8 39 8 56 3 4 2 1 5 4 mob2
250 40 28 8 20 37 2 42 10 19 8 1 3 7 8 0 mob2
251 31 28 14 3 6 17 1 7 45 2 3 2 9 3 0 opp1
252 24 13 15 11 38 28 13 12 51 3 4 6 4 2 12 mob2
253 30 38 6 26 11 11 30 4 40 15 2 6 4 1 8 opp3
254 42 37 32 2 5 19 35 2 64 9 5 12 3 8 3 opp2
255 23 24 32 40 4 24 1 3 78 14 4 5 9 10 2 opp4
256 5 22 19 22 41 3 34 11 50 5 2 6 10 1 3 opp3
257 3 38 20 2 25 6 25 6 59 15 3 3 0 10 8 opp1
258 33 41 39 42 38 29 27 5 33 14 5 5 2 1 12 opp3
259 20 10 27 16 14 7 35 1 24 13 6 7 11 4 4 opp3
260 6 8 4 35 12 40 15 10 38 12 7 0 10 6 5 mob2
261 18 41 35 8 13 14 4 1 70 6 1 0 7 11 6 opp4
262 39 42 12 28 11 40 7 3 71 3 7 5 2 1 10 opp2
263 32 32 24 19 13 14 40 12 18 6 1 1 8 11 10 opp1
264 31 14 16 12 33 25 5 5 8 8 6 8 6 5 2 opp4
265 38 31 34 27 5 26 27 8 75 3 6 6 10 1 5 mob1
266 2 7 23 8 24 28 20 2 31 5 7 9 0 11 11 tower
267 39 43 17 21 31 13 41 8 43 10 5 4 10 6 8 mob1
268 3 6 17 7 22 23 22 6 40 8 6 7 1 7 11 tower
269 3 42 13 24 32 1 33 5 68 7 0 4 6 1 10 opp3
270 12 26 4 18 2 42 29 10 41 11 3 10 10 10 9 mob2
271 21 26 7 24 31 10 33 4 51 1 2 8 2 8 3 mob2
272 39 22 7 20 5 29 38 10 8 4 1 9 12 11 0 mob2
273 20 19 1 22 36 13 5 7 4 3 7 3 9 1 2 mob2
274 4 7 42 17 6 14 26 11 45 9 4 10 6 11 2 tower
275 23 27 11 25 38 26 34 10 77 13 7 4 3 5 9 mob1
276 40 10 29 8 7 32 32 12 32 9 2 10 11 2 4 mob2
277 37 22 34 17 17 25 40 5 74 9 3 3 8 2 9 opp1
278 23 10 36 43 31 26 32 5 59 1 1 2 3 8 3 mob1
279 14 32 8 4 17 33 8 9 74 5 1 7 12 7 3 mob2
280 8 14 21 26 16 43 10 5 26 1 1 5 0 1 5 tower
281 7 3 19 7 39 28 12 11 32 9 0 7 12 11 10 tower
282 11 21 13 31 30 15 43 10 73 6 1 3 11 9 12 mob2
283 28 10 32 28 26 36 38 4 15 11 0 12 11 12 7 mob1
284 39 2 25 11 11 25 35 8 36 14 0 9 10 1 7 mob1
285 43 41 39 38 16 5 35 12 48 14 4 7 3 11 6 opp2
286 36 31 28 41 40 23 23 11 27 7 2 11 0 6 11 mob1
287 28 8 38 39 31 3 7 7 47 0 6 8 0 2 6 opp3
288 5 8 19 26 26 9 1 8 66 15 3 8 11 7 7 opp4
289 36 28 27 7 25 1 21 10 63 0 6 4 2 10 4 opp1
290 19 3 6 35 5 29 3 9 63 2 7 3 12 1 3 opp4
291 27 5 36 31 6 10 27 12 9 8 6 10 9 6 11 mob1
292 21 21 27 18 26 9 39 1 43 6 3 5 10 0 7 mob1
293 12 39 15 31 32 9 39 7 26 4 0 12 5 0 12 tower
294 41 18 22 33 25 6 37 1 77 14 7 12 7 4 8 opp3
295 8 33 19 22 5 36 28 3 69 15 5 5 0 7 3 mob1
296 24 8 15 16 21 18 15 9 78 4 0 4 9 5 4 mob2
297 38 24 26 28 41 21 43 2 65 15 3 1 10 5 4 mob1
298 33 17 6 4 34 36 25 6 35 4 4 10 9 0 3 mob2
299 29 25 30 19 35 38 33 6 68 5 1 0 5 11 6 mob1
300 23 43 41 25 27 26 19 7 12 8 3 4 10 11 9 tower
301 7 9 18 31 36 21 16 4 23 8 4 9 8 11 5 tower
302 35 21 39 36 36 37 33 10 41 9 4 1 0 7 0 mob1

78
learning/decision_tree.py Normal file
View File

@ -0,0 +1,78 @@
from typing import List, Tuple
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.tree import DecisionTreeClassifier
from common.helpers import castle_neighbors, find_neighbours
from models.castle import Castle
from models.knight import Knight
from models.monster import Monster
def manhattan_distance(p1: Tuple[int, int], p2: Tuple[int, int]) -> int:
x1, y1 = p1
x2, y2 = p2
return abs(x1 - x2) + abs(y1 - y2)
def parse_hp(hp: int) -> int:
return max(0, hp)
def parse_idx_of_opp_or_monster(s: str) -> int:
return int(s[-1]) - 1
class DecisionTree:
def __init__(self) -> None:
data_frame = pd.read_csv('learning/dataset_tree_1000.csv', delimiter=';')
unlabeled_goals = data_frame['goal']
self.goals_label_encoder = LabelEncoder()
self.goals = self.goals_label_encoder.fit_transform(unlabeled_goals)
self.train_set = data_frame.drop('goal', axis='columns')
self.model = DecisionTreeClassifier(criterion='entropy')
self.model.fit(self.train_set.values, self.goals)
def predict_move(self, grid: List[List[int]], current_knight: Knight, castle: Castle, monsters: List[Monster],
opponents: List[Knight]) -> \
List[Tuple[int, int]]:
distance_to_castle = manhattan_distance(current_knight.position, castle.position)
monsters_parsed = []
for monster in monsters:
monsters_parsed.append((manhattan_distance(current_knight.position, monster.position), parse_hp(
monster.health_bar.current_hp)))
opponents_parsed = []
for opponent in opponents:
opponents_parsed.append(
(manhattan_distance(current_knight.position, opponent.position),
parse_hp(opponent.health_bar.current_hp)))
prediction = self.get_prediction(tower_dist=distance_to_castle, tower_hp=castle.health_bar.current_hp,
mob1_dist=monsters_parsed[0][0], mob1_hp=monsters_parsed[0][1],
mob2_dist=monsters_parsed[1][0], mob2_hp=monsters_parsed[1][1],
opp1_dist=opponents_parsed[0][0], opp1_hp=opponents_parsed[0][1],
opp2_dist=opponents_parsed[1][0], opp2_hp=opponents_parsed[1][1],
opp3_dist=opponents_parsed[2][0], opp3_hp=opponents_parsed[2][1],
opp4_dist=opponents_parsed[3][0], opp4_hp=opponents_parsed[3][1],
agent_hp=current_knight.health_bar.current_hp)
print(f'Prediction = {prediction}')
if prediction == 'tower': # castle...
return castle_neighbors(grid, castle_bottom_right_row=castle.position[0],
castle_bottom_right_col=castle.position[1])
elif prediction.startswith('opp'):
idx = parse_idx_of_opp_or_monster(prediction)
return find_neighbours(grid, opponents[idx].position[1], opponents[idx].position[0])
else:
idx = parse_idx_of_opp_or_monster(prediction)
return find_neighbours(grid, monsters[idx].position[1], monsters[idx].position[0])
def get_prediction(self, tower_dist: int, mob1_dist: int, mob2_dist: int, opp1_dist: int, opp2_dist: int,
opp3_dist: int, opp4_dist: int, agent_hp: int, tower_hp: int, mob1_hp: int, mob2_hp: int,
opp1_hp: int, opp2_hp: int, opp3_hp: int, opp4_hp) -> str:
prediction = self.model.predict(
[[tower_dist, mob1_dist, mob2_dist, opp1_dist, opp2_dist, opp3_dist, opp4_dist, agent_hp,
tower_hp, mob1_hp, mob2_hp, opp1_hp, opp2_hp, opp3_hp, opp4_hp]])
return self.goals_label_encoder.inverse_transform(prediction)[0]

View File

View File

@ -0,0 +1,128 @@
import unittest
from logic.knights_queue import KnightsQueue
from models.knight import Knight
class KnightsQueueTest(unittest.TestCase):
def test_should_skip_dead_knights(self):
knight1 = Knight(None)
knight1.max_hp = 0
knight2 = Knight(None)
knight2.max_hp = 0
knight3 = Knight(None)
knight3.max_hp = 1
knight4 = Knight(None)
knight4.max_hp = 0
knight5 = Knight(None)
knight5.max_hp = 0
knight6 = Knight(None)
knight6.max_hp = 1
knights_queue = KnightsQueue([knight1, knight2, knight3], [knight4, knight5, knight6])
res1 = knights_queue.dequeue_knight()
res2 = knights_queue.dequeue_knight()
self.assertEqual(res1.max_hp, 1)
self.assertEqual(res2.max_hp, 1)
def test_should_return_first_alive_knight(self):
knight1 = Knight(None)
knight1.max_hp = 222
knight2 = Knight(None)
knight2.max_hp = -1
knight3 = Knight(None)
knight3.max_hp = 1
knights_queue = KnightsQueue([knight1, knight2], [knight3])
res1 = knights_queue.dequeue_knight()
res2 = knights_queue.dequeue_knight()
res3 = knights_queue.dequeue_knight()
res4 = knights_queue.dequeue_knight()
self.assertEqual(res1, res3)
self.assertEqual(res2, res4)
def test_should_raise_when_knight_died_and_whole_team_dead(self):
with self.assertRaises(Exception):
knight1 = Knight(None)
knight1.max_hp = 222
knight2 = Knight(None)
knight2.max_hp = 1
knights_queue = KnightsQueue([knight1], [knight2])
knights_queue.dequeue_knight()
knights_queue.dequeue_knight()
knight2.max_hp = -2
knights_queue.dequeue_knight()
knights_queue.dequeue_knight()
def test_should_make_valid_next_turn(self):
knight1 = Knight(None)
knight1.max_hp = 222
knight2 = Knight(None)
knight2.max_hp = 1
knights_queue = KnightsQueue([knight1], [knight2])
previous_turn = knights_queue.team_idx_turn
knights_queue.dequeue_knight()
current_turn = knights_queue.team_idx_turn
self.assertNotEqual(previous_turn, current_turn)
def test_should_raise_when_team_has_dead_knights(self):
with self.assertRaises(Exception):
knight1 = Knight(None)
knight1.max_hp = 0
knight2 = Knight(None)
knight2.max_hp = -1
knight3 = Knight(None)
knight3.max_hp = -2
knight4 = Knight(None)
knight4.max_hp = 20
knights_queue = KnightsQueue([knight1, knight2, knight3], [knight4])
knights_queue.dequeue_knight()
knights_queue.dequeue_knight()
def test_should_return_knight_from_any_team_and_add_to_queue_again(self):
knight1 = Knight(None)
knight1.max_hp = 10
knight2 = Knight(None)
knight2.max_hp = 20
knights_queue = KnightsQueue([knight1], [knight2])
result1 = knights_queue.dequeue_knight()
result2 = knights_queue.dequeue_knight()
result3 = knights_queue.dequeue_knight()
self.assertIsNotNone(result1)
self.assertIsNotNone(result2)
self.assertIsNotNone(result3)
self.assertTrue(result1.max_hp == result3.max_hp)
def test_should_raise_when_only_one_team_alive(self):
with self.assertRaises(Exception):
knight = Knight(None)
knight.max_hp = 21
knights_queue = KnightsQueue([knight], [])
knights_queue.dequeue_knight()
def test_should_raise_when_no_team_alive(self):
with self.assertRaises(Exception):
knights_queue = KnightsQueue([], [])
knights_queue.dequeue_knight()
if __name__ == '__main__':
unittest.main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

2
logic/exceptions.py Normal file
View File

@ -0,0 +1,2 @@
class FieldNotWalkable(Exception):
pass

View File

@ -1,4 +0,0 @@
class Field:
def __init__(self, texture_path, converted_texture):
self.texture_path = texture_path
self.converted_texture = converted_texture

View File

@ -2,18 +2,15 @@ import sys
import pygame import pygame
from common.colors import FONT_DARK, WHITE
from common.constants import * from common.constants import *
from common.helpers import draw_text from common.helpers import print_numbers
from models.castle import Castle from logic.level import Level
from models.knight import Knight
from models.monster import Monster
from ui.logs import Logs from ui.logs import Logs
from ui.screens.credits import Credits from ui.screens.credits import Credits
from ui.screens.main_menu import MainMenu
from ui.screens.options import Options from ui.screens.options import Options
from ui.stats import Stats from ui.stats import Stats
from .grid import Grid from logic.health_bar import HealthBar
from .spawner import Spawner
class Game: class Game:
@ -23,98 +20,35 @@ class Game:
pygame.display.set_icon(pygame.image.load('./resources/icons/sword.png')) pygame.display.set_icon(pygame.image.load('./resources/icons/sword.png'))
self.screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) self.screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
self.clock = pygame.time.Clock()
self.tiles = [] self.clock = pygame.time.Clock()
for tile_path in TILES:
converted_tile = pygame.image.load('resources/textures/' + tile_path).convert_alpha()
self.tiles.append((tile_path, converted_tile))
self.bg = pygame.image.load("./resources/textures/bg.jpg") self.bg = pygame.image.load("./resources/textures/bg.jpg")
self.screens = {'credits': Credits(self.screen, self.clock), 'options': Options(self.screen, self.clock)} self.screens = {'credits': Credits(self.screen, self.clock), 'options': Options(self.screen, self.clock)}
click = False
def main_menu(self): def main_menu(self):
while True: menu = MainMenu(self.screen, self.clock, self.bg,
self.screen.blit(self.bg, (0, 0)) self.game,
self.screens['options'].display_screen,
pygame.draw.rect(self.screen, (255, 255, 255), pygame.Rect(800, 100, 400, 500), 0, 5) self.screens['credits'].display_screen)
draw_text('MAIN MENU', FONT_DARK, self.screen, 850, 150, 30, True) menu.display_screen()
mx, my = pygame.mouse.get_pos()
button_1 = pygame.Rect(850, 250, 300, 50)
button_2 = pygame.Rect(850, 350, 300, 50)
button_3 = pygame.Rect(850, 450, 300, 50)
if button_1.collidepoint((mx, my)):
if click:
self.game()
if button_2.collidepoint((mx, my)):
if click:
self.screens['options'].display_screen()
if button_3.collidepoint((mx, my)):
if click:
self.screens['credits'].display_screen()
pygame.draw.rect(self.screen, (0, 191, 255), button_1, 0, 4)
draw_text('PLAY', WHITE, self.screen, 870, 255)
pygame.draw.rect(self.screen, (0, 191, 255), button_2, 0, 4)
draw_text('OPTIONS', WHITE, self.screen, 870, 355)
pygame.draw.rect(self.screen, (0, 191, 255), button_3, 0, 4)
draw_text('CREDITS', WHITE, self.screen, 870, 455)
click = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
click = True
pygame.display.update()
self.clock.tick(60)
def game(self): def game(self):
logs = Logs(self.screen)
level = Level(self.screen, logs)
# setup clock for rounds
NEXT_TURN = pygame.USEREVENT + 1
pygame.time.set_timer(NEXT_TURN, TURN_INTERVAL)
# create level
level.create_map()
stats = Stats(self.screen, level.list_knights_blue, level.list_knights_red)
level.setup_stats(stats)
print_numbers_flag = False
running = True running = True
grid = Grid(self.tiles)
stats = Stats()
logs = Logs()
knights_sprite_group = pygame.sprite.Group()
monsters_sprite_group = pygame.sprite.Group()
castle_sprite_group = pygame.sprite.Group()
knights_left = self.generate_knights_team(knights_sprite_group)
knights_right = self.generate_knights_team(knights_sprite_group)
spawn_left_team = Spawner(grid, knights_left, width=KNIGHTS_SPAWN_WIDTH, height=KNIGHTS_SPAWN_HEIGHT,
pos_row=KNIGHTS_SPAWN_FIRST_ROW, pos_column=KNIGHTS_SPAWN_FIRST_COL)
spawn_right_team = Spawner(grid, knights_right, width=KNIGHTS_SPAWN_WIDTH, height=KNIGHTS_SPAWN_HEIGHT,
pos_row=KNIGHTS_SPAWN_FIRST_ROW,
pos_column=COLUMNS - KNIGHTS_SPAWN_WIDTH - 1) # - 1 to avoid list index out of range
spawn_left_team.spawn()
spawn_right_team.spawn()
spawned_monsters = self.generate_monster(monsters_sprite_group)
monster_spawn = Spawner(grid, spawned_monsters, width=MONSTER_SPAWN_WIDTH, height=MONSTER_SPAWN_HEIGHT,
pos_row=MONSTER_SPAWN_FIRST_ROW, pos_column=MONSTER_SPAWN_FIRST_COL)
monster_spawn.spawn_monsters()
spawned_castle = self.generate_castle(castle_sprite_group)
castle_spawn = Spawner(grid, [spawned_castle], width=CASTLE_SPAWN_WIDTH, height=CASTLE_SPAWN_HEIGHT,
pos_row=CASTLE_SPAWN_FIRST_ROW, pos_column=CASTLE_SPAWN_FIRST_COL)
castle_spawn.spawn()
while running: while running:
self.screen.blit(self.bg, (0, 0)) self.screen.blit(self.bg, (0, 0))
@ -125,37 +59,18 @@ class Game:
if event.type == pygame.KEYDOWN: if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE: if event.key == pygame.K_ESCAPE:
running = False running = False
if event.key == pygame.K_n:
print_numbers_flag = not print_numbers_flag
grid.draw(self.screen) if event.type == NEXT_TURN: # is called every 'TURN_INTERVAL' milliseconds
stats.draw(self.screen) level.handle_turn()
logs.draw(self.screen)
stats.update()
logs.draw()
level.update()
if print_numbers_flag:
print_numbers()
knights_sprite_group.draw(self.screen)
monsters_sprite_group.draw(self.screen)
castle_sprite_group.draw(self.screen)
pygame.display.update() pygame.display.update()
self.clock.tick(FPS_COUNT) self.clock.tick(FPS_COUNT)
@staticmethod
def generate_knights_team(knights_sprite_group):
knights = []
for i in range(4):
knight = Knight("./resources/textures/knight.png")
knights.append(knight)
knights_sprite_group.add(knight)
return knights
@staticmethod
def generate_monster(monsters_sprite_group):
monsters = []
for i in range(2):
monster = Monster()
monsters.append(monster)
monsters_sprite_group.add(monster)
return monsters
@staticmethod
def generate_castle(castle_sprite_group):
castle = Castle()
castle_sprite_group.add(castle)
return castle

View File

@ -1,37 +0,0 @@
import random
import pygame
from common.constants import ROWS, COLUMNS, GRID_CELL_PADDING, GRID_CELL_WIDTH, GRID_CELL_HEIGHT, BORDER_WIDTH, \
BORDER_RADIUS
from .field import Field
class Grid:
def __init__(self, textures):
self.textures = textures
self.grid = []
for row in range(ROWS):
self.grid.append([])
for _ in range(COLUMNS):
texture_path, converted_texture = self.get_random_texture()
field = Field(texture_path, converted_texture)
self.grid[row].append(field)
def get_random_texture(self):
texture_index = random.randint(0, len(self.textures) - 1)
return self.textures[texture_index]
def draw(self, screen):
bg_width = (GRID_CELL_PADDING + GRID_CELL_WIDTH) * COLUMNS + BORDER_WIDTH
bg_height = (GRID_CELL_PADDING + GRID_CELL_HEIGHT) * ROWS + BORDER_WIDTH
pygame.draw.rect(screen, (255, 255, 255), pygame.Rect(5, 5, bg_width, bg_height), 0, BORDER_RADIUS)
for row in range(ROWS):
for column in range(COLUMNS):
box_rect = [(GRID_CELL_PADDING + GRID_CELL_WIDTH) * column + GRID_CELL_PADDING + 7,
(GRID_CELL_PADDING + GRID_CELL_HEIGHT) * row + GRID_CELL_PADDING + 7,
GRID_CELL_WIDTH,
GRID_CELL_HEIGHT]
image = self.grid[row][column].converted_texture
screen.blit(pygame.transform.scale(image, (GRID_CELL_WIDTH, GRID_CELL_HEIGHT)), box_rect)

59
logic/health_bar.py Normal file
View File

@ -0,0 +1,59 @@
import pygame
from common.constants import BAR_ANIMATION_SPEED, BAR_WIDTH_MULTIPLIER, BAR_HEIGHT_MULTIPLIER
from common.colors import FONT_DARK, ORANGE, WHITE, RED, GREEN, BLACK
class HealthBar:
def __init__(self, screen, rect: pygame.rect, current_hp, max_hp, calculate_xy=False, calculate_size=False):
self.health_ratio = None
self.rect = rect
self.screen = screen
self.current_hp = current_hp
self.max_hp = max_hp
self.x = self.rect.x
self.y = self.rect.y
self.calculate_xy = calculate_xy
if calculate_size:
self.width = int(self.rect.width * BAR_WIDTH_MULTIPLIER) - 2
self.height = int(self.rect.width * BAR_HEIGHT_MULTIPLIER) - 2
else:
self.width = self.rect.width - 2
self.height = self.rect.height - 2
self.update_stats()
def update(self):
self.update_stats()
self.show()
def update_stats(self):
if self.calculate_xy:
self.x = int(self.rect.width * (1 - BAR_WIDTH_MULTIPLIER)/2) + self.rect.x + 1
self.y = int(self.rect.height * BAR_HEIGHT_MULTIPLIER/2) + self.rect.y + 1
else:
self.x = self.rect.x + 1
self.y = self.rect.y + 1
self.health_ratio = self.max_hp / self.width
def take_dmg(self, amount):
if self.current_hp - amount > 0:
self.current_hp -= amount
elif self.current_hp - amount <= 0:
self.current_hp = 0
def heal(self, amount):
if self.current_hp + amount < self.max_hp:
self.current_hp += amount
elif self.current_hp + amount >= self.max_hp:
self.current_hp = self.max_hp
def show(self):
pygame.Surface.fill(self.screen, BLACK, (self.x-1, self.y-1, self.width+2, self.height+2))
pygame.Surface.fill(self.screen, RED, (self.x, self.y, self.width, self.height))
pygame.Surface.fill(self.screen, GREEN, (self.x, self.y, int(self.current_hp / self.health_ratio), self.height))

25
logic/knights_queue.py Normal file
View File

@ -0,0 +1,25 @@
import random
from collections import deque
class KnightsQueue:
def __init__(self, blue_team, red_team):
self.queues = deque(blue_team), deque(red_team)
self.team_idx_turn = random.randint(0, 1)
def dequeue_knight(self):
if self.both_teams_alive():
knight = self.queues[self.team_idx_turn].popleft()
if knight.health_bar.current_hp <= 0:
return self.dequeue_knight()
else:
self.queues[self.team_idx_turn].append(knight)
self.next_turn()
return knight
raise Exception('Game has just ended')
def both_teams_alive(self):
return len(self.queues[0]) > 0 and len(self.queues[1]) > 0
def next_turn(self):
self.team_idx_turn = (self.team_idx_turn + 1) % 2

286
logic/level.py Normal file
View File

@ -0,0 +1,286 @@
import random
import pygame
from algorithms.a_star import a_star, State, TURN_RIGHT, TURN_LEFT, FORWARD
from algorithms.genetic.const import MAP_ALIASES
from algorithms.genetic.map_importer_exporter import import_random_map
from common.constants import *
from learning.decision_tree import DecisionTree
from logic.knights_queue import KnightsQueue
from models.castle import Castle
from models.knight import Knight
from models.monster import Monster
from models.tile import Tile
class Level:
def __init__(self, screen, logs):
self.screen = screen
self.logs = logs
self.decision_tree = DecisionTree()
# sprite group setup
self.sprites = pygame.sprite.LayeredUpdates()
self.map = []
self.list_knights_blue = []
self.list_knights_red = []
self.list_monsters = []
self.list_castles = []
self.knights_queue = None
self.stats = None
def setup_stats(self, stats):
self.stats = stats
def add_points(self, team, points_to_add):
if self.stats is not None:
self.stats.add_points(team, points_to_add)
def create_map(self):
self.map = import_random_map()
self.setup_base_tiles()
self.setup_objects()
self.knights_queue = KnightsQueue(self.list_knights_blue, self.list_knights_red)
def setup_base_tiles(self):
textures = []
for texture_path in TILES:
converted_texture = pygame.image.load('resources/textures/' + texture_path).convert_alpha()
converted_texture = pygame.transform.scale(converted_texture, (40, 40))
textures.append((texture_path, converted_texture))
for row_index, row in enumerate(self.map):
for col_index, col in enumerate(row):
# add base tiles, e.g. water, tree, grass
if col == MAP_ALIASES.get('WATER'):
texture_index = 5
texture_surface = textures[texture_index][1]
Tile((col_index, row_index), texture_surface, self.sprites, 'w')
elif col == MAP_ALIASES.get('TREE'):
texture_index = 6
texture_surface = textures[texture_index][1]
Tile((col_index, row_index), texture_surface, self.sprites, 't')
elif col == MAP_ALIASES.get('SAND'):
texture_index = 4
texture_surface = textures[texture_index][1]
Tile((col_index, row_index), texture_surface, self.sprites)
else:
texture_index = random.randint(0, 3)
texture_surface = textures[texture_index][1]
Tile((col_index, row_index), texture_surface, self.sprites)
def setup_objects(self):
castle_count = 0 # TODO: find some smarter method to print castle
for row_index, row in enumerate(self.map):
print(row)
for col_index, col in enumerate(row):
# add objects, e.g. knights, monsters, castle
if col == MAP_ALIASES.get('KNIGHT_BLUE'):
knight = Knight(self.screen, (col_index, row_index), self.sprites, "blue")
self.map[row_index][col_index] = knight
self.list_knights_blue.append(knight)
elif col == MAP_ALIASES.get('KNIGHT_RED'):
knight = Knight(self.screen, (col_index, row_index), self.sprites, "red")
self.map[row_index][col_index] = knight
self.list_knights_red.append(knight)
elif col == MAP_ALIASES.get('MONSTER'):
monster = Monster(self.screen, (col_index, row_index), self.sprites)
self.map[row_index][col_index] = monster
self.list_monsters.append(monster)
elif col == MAP_ALIASES.get('CASTLE'):
castle_count += 1
if castle_count == 4:
castle = Castle(self.screen, (col_index, row_index), self.sprites)
self.map[row_index][col_index] = castle
self.list_castles.append(castle)
#def attack_knight(self, knights_list, positions, current_knight):
# op_pos_1 = current_knight.position[0] - 1, current_knight.position[1]
# positions.append(op_pos_1)
# op_pos_2 = current_knight.position[0], current_knight.position[1] - 1
# positions.append(op_pos_2)
# op_pos_3 = current_knight.position[0] + 1, current_knight.position[1]
# positions.append(op_pos_3)
# op_pos_4 = current_knight.position[0], current_knight.position[1] + 1
# positions.append(op_pos_4)
# for some_knight in knights_list:
# for some_position in positions:
# if (some_knight.position == some_position and some_knight.team != current_knight.team):
# some_knight.health_bar.take_dmg(current_knight.attack)
# if some_knight.health_bar.current_hp == 0:
# some_knight.kill()
# positions.clear()
def attack_knight_left(self, knights_list, current_knight):
position_left = current_knight.position[0] - 1, current_knight.position[1]
for some_knight in knights_list:
if (some_knight.position == position_left and some_knight.team != current_knight.team):
some_knight.health_bar.take_dmg(current_knight.attack)
if some_knight.health_bar.current_hp <= 0:
some_knight.kill()
self.add_points(current_knight.team, 5)
for monster in self.list_monsters:
if monster.position == position_left:
monster.health_bar.take_dmg(current_knight.attack)
if monster.health_bar.current_hp <= 0:
monster.kill()
self.add_points(current_knight.team, monster.points)
else:
current_knight.health_bar.take_dmg(monster.attack)
if current_knight.health_bar.current_hp <= 0:
current_knight.kill()
for castle in self.list_castles:
if castle.position == position_left:
castle.health_bar.take_dmg(current_knight.attack)
def attack_knight_right(self, knights_list, current_knight):
position_right = current_knight.position[0] + 1, current_knight.position[1]
for some_knight in knights_list:
if (some_knight.position == position_right and some_knight.team != current_knight.team):
some_knight.health_bar.take_dmg(current_knight.attack)
if some_knight.health_bar.current_hp == 0:
some_knight.kill()
self.add_points(current_knight.team, 5)
for monster in self.list_monsters:
if monster.position == position_right:
monster.health_bar.take_dmg(current_knight.attack)
if monster.health_bar.current_hp <= 0:
monster.kill()
self.add_points(current_knight.team, monster.points)
else:
current_knight.health_bar.take_dmg(monster.attack)
if current_knight.health_bar.current_hp <= 0:
current_knight.kill()
for castle in self.list_castles:
if castle.position == position_right:
castle.health_bar.take_dmg(current_knight.attack)
def attack_knight_up(self, knights_list, current_knight):
position_up = current_knight.position[0], current_knight.position[1] - 1
for some_knight in knights_list:
if (some_knight.position == position_up and some_knight.team != current_knight.team):
some_knight.health_bar.take_dmg(current_knight.attack)
if some_knight.health_bar.current_hp == 0:
some_knight.kill()
self.add_points(current_knight.team, 5)
for monster in self.list_monsters:
if monster.position == position_up:
monster.health_bar.take_dmg(current_knight.attack)
if monster.health_bar.current_hp <= 0:
monster.kill()
self.add_points(current_knight.team, monster.points)
else:
current_knight.health_bar.take_dmg(monster.attack)
if current_knight.health_bar.current_hp <= 0:
current_knight.kill()
for castle in self.list_castles:
if castle.position == position_up:
castle.health_bar.take_dmg(current_knight.attack)
def attack_knight_down(self, knights_list, current_knight):
position_down = current_knight.position[0], current_knight.position[1] + 1
for some_knight in knights_list:
if (some_knight.position == position_down and some_knight.team != current_knight.team):
some_knight.health_bar.take_dmg(current_knight.attack)
if some_knight.health_bar.current_hp == 0:
some_knight.kill()
self.add_points(current_knight.team, 5)
for monster in self.list_monsters:
if monster.position == position_down:
monster.health_bar.take_dmg(current_knight.attack)
if monster.health_bar.current_hp <= 0:
monster.kill()
self.add_points(current_knight.team, monster.points)
else:
current_knight.health_bar.take_dmg(monster.attack)
if current_knight.health_bar.current_hp <= 0:
current_knight.kill()
for castle in self.list_castles:
if castle.position == position_down:
castle.health_bar.take_dmg(current_knight.attack)
def handle_turn(self):
current_knight = self.knights_queue.dequeue_knight()
knights_list = self.list_knights_red + self.list_knights_blue
print("next turn " + current_knight.team)
knight_pos_x = current_knight.position[0]
knight_pos_y = current_knight.position[1]
positions = []
goal_list = self.decision_tree.predict_move(grid=self.map, current_knight=current_knight,
monsters=self.list_monsters,
opponents=self.list_knights_blue
if current_knight.team_alias() == 'k_r' else self.list_knights_red,
castle=self.list_castles[0])
if (len(self.list_knights_blue) == 0 or len(self.list_knights_red) == 0):
pygame.quit()
if len(goal_list) == 0:
return
state = State((knight_pos_y, knight_pos_x), current_knight.direction.name)
action_list = a_star(state, self.map, goal_list)
print(action_list)
print(goal_list)
if len(action_list) == 0:
return
next_action = action_list.pop(0)
#if current_knight.health_bar.current_hp != 0:
#self.attack_knight(knights_list, positions, current_knight)
if current_knight.direction.name == UP:
self.attack_knight_up(knights_list, current_knight)
elif current_knight.direction.name == DOWN:
self.attack_knight_down(knights_list, current_knight)
elif current_knight.direction.name == RIGHT:
self.attack_knight_right(knights_list, current_knight)
elif current_knight.direction.name == LEFT:
self.attack_knight_left(knights_list, current_knight)
if next_action == TURN_LEFT:
self.logs.enqueue_log(f'AI {current_knight.team}: Obrót w lewo.')
current_knight.rotate_left()
elif next_action == TURN_RIGHT:
self.logs.enqueue_log(f'AI {current_knight.team}: Obrót w prawo.')
current_knight.rotate_right()
elif next_action == FORWARD:
current_knight.step_forward()
self.map[knight_pos_y][knight_pos_x] = MAP_ALIASES.get("GRASS")
# update knight on map
if current_knight.direction.name == UP:
self.logs.enqueue_log(f'AI {current_knight.team}: Ruch do góry.')
self.map[knight_pos_y - 1][knight_pos_x] = current_knight.team_alias()
elif current_knight.direction.name == RIGHT:
self.logs.enqueue_log(f'AI {current_knight.team}: Ruch w prawo.')
self.map[knight_pos_y][knight_pos_x + 1] = current_knight.team_alias()
elif current_knight.direction.name == DOWN:
self.logs.enqueue_log(f'AI {current_knight.team}: Ruch w dół.')
self.map[knight_pos_y + 1][knight_pos_x] = current_knight.team_alias()
elif current_knight.direction.name == LEFT:
self.logs.enqueue_log(f'AI {current_knight.team}: Ruch w lewo.')
self.map[knight_pos_y][knight_pos_x - 1] = current_knight.team_alias()
def update(self):
bg_width = (GRID_CELL_PADDING + GRID_CELL_SIZE) * COLUMNS + BORDER_WIDTH
bg_height = (GRID_CELL_PADDING + GRID_CELL_SIZE) * ROWS + BORDER_WIDTH
pygame.draw.rect(self.screen, (255, 255, 255), pygame.Rect(5, 5, bg_width, bg_height), 0, BORDER_RADIUS)
# update and draw the game
self.sprites.draw(self.screen)
self.sprites.update()

View File

@ -1,43 +1,33 @@
import random import random
from common.constants import GRID_CELL_PADDING, GRID_CELL_WIDTH, GRID_CELL_HEIGHT from common.constants import COLUMNS, ROWS
class Spawner: class Spawner:
def __init__(self, grid, objs_to_spawn_list: list, width, height, pos_row, pos_column): def __init__(self, map):
self.objs_to_spawn_list = objs_to_spawn_list self.map = map
self.width = width
self.height = height
self.pos_row = pos_row
self.pos_column = pos_column
self.grid = grid
def spawn(self): def __is_free_field(self, field):
coords = [] return field in ['g', 's', ' ']
for row in range(self.pos_row, (self.pos_row + self.width)):
for column in range(self.pos_column, (self.pos_column + self.height)):
if self.grid.grid[row][column].texture_path != "water.png" and self.grid.grid[row][column].texture_path != "grass_with_tree.jpg":
coords.append([(GRID_CELL_PADDING + GRID_CELL_WIDTH) * column + GRID_CELL_PADDING + 7,
(GRID_CELL_PADDING + GRID_CELL_HEIGHT) * row + GRID_CELL_PADDING + 7])
for obj in self.objs_to_spawn_list: def spawn_in_area(self, objects: list, spawn_area_pos_row=0, spawn_area_pos_column=0, spawn_area_width=0,
random_tile = random.randint(0, len(coords) - 1) spawn_area_height=0, size=1):
obj.rect.x = coords[random_tile][0] spawned_objects_count = 0
obj.rect.y = coords[random_tile][1]
coords.pop(random_tile)
obj.update()
def spawn_monsters(self): while spawned_objects_count != len(objects):
coords = [] x = random.randint(0, spawn_area_height) + spawn_area_pos_row
for row in range(self.pos_row, (self.pos_row + self.width)): y = random.randint(0, spawn_area_width) + spawn_area_pos_column
for column in range(self.pos_column, (self.pos_column + self.height)): if x < ROWS - 1 and y < COLUMNS - 1 and self.__is_free_field(self.map[x][y]):
if self.grid.grid[row][column].texture_path != "water.png" and self.grid.grid[row][column].texture_path != "grass_with_tree.jpg" and not ((10 <=column <=14) and (8 <= row <= 12)): for i in range(size):
coords.append([(GRID_CELL_PADDING + GRID_CELL_WIDTH) * column + GRID_CELL_PADDING + 7, for j in range(size):
(GRID_CELL_PADDING + GRID_CELL_HEIGHT) * row + GRID_CELL_PADDING + 7]) self.map[x - i][y - j] = objects[spawned_objects_count]
spawned_objects_count += 1
for obj in self.objs_to_spawn_list: def spawn_where_possible(self, objects: list):
random_tile = random.randint(0, len(coords) - 1) spawned_objects_count = 0
obj.rect.x = coords[random_tile][0] while spawned_objects_count != len(objects):
obj.rect.y = coords[random_tile][1] x = random.randint(0, ROWS - 1)
coords.pop(random_tile) y = random.randint(0, COLUMNS - 1)
obj.update() if self.__is_free_field(self.map[x][y]):
self.map[x][y] = objects[spawned_objects_count]
spawned_objects_count += 1

View File

@ -1,15 +1,22 @@
import random
import pygame.image import pygame.image
from common.helpers import parse_cord
from logic.health_bar import HealthBar
class Castle(pygame.sprite.Sprite): class Castle(pygame.sprite.Sprite):
images = [] def __init__(self, screen, position, group):
super().__init__(group)
def __init__(self): self._layer = 1
super().__init__() self.image = pygame.image.load("./resources/textures/castle.png").convert_alpha()
self.image = pygame.image.load("./resources/textures/castle.png")
self.image = pygame.transform.scale(self.image, (78, 78)) self.image = pygame.transform.scale(self.image, (78, 78))
self.images.append(self.image) self.position = position
self.rect = self.image.get_rect() position_in_px = (parse_cord(position[0]), parse_cord(position[1]))
castle_list = pygame.sprite.Group() self.rect = self.image.get_rect(center=position_in_px)
self.health = 80 self.max_hp = 80
self.health_bar = HealthBar(screen, self.rect, current_hp=self.max_hp, max_hp=self.max_hp, calculate_xy=True, calculate_size=True)
def update(self):
self.health_bar.update()

View File

@ -1,15 +1,83 @@
import pygame.image
import random import random
class Knight(pygame.sprite.Sprite): import pygame.image
def __init__(self, img):
super().__init__()
self.images = []
self.image = pygame.image.load("./resources/textures/knight.png")
self.image = pygame.transform.scale(self.image, (40, 40))
self.images.append(self.image)
self.rect = self.image.get_rect()
self.health = random.randrange(7, 12)
self.attack = random.randrange(3, 7)
knights_list = pygame.sprite.Group() from common.constants import GRID_CELL_SIZE, Direction
from common.helpers import parse_cord
from logic.health_bar import HealthBar
def load_knight_textures(team):
if team == "blue":
random_index = 3
else:
random_index = 4
states = [
pygame.image.load(f'resources/textures/knight_{random_index}_up.png').convert_alpha(), # up = 0
pygame.image.load(f'resources/textures/knight_{random_index}_right.png').convert_alpha(), # right = 1
pygame.image.load(f'resources/textures/knight_{random_index}_down.png').convert_alpha(), # down = 2
pygame.image.load(f'resources/textures/knight_{random_index}_left.png').convert_alpha(), # left = 3
]
return states
class Knight(pygame.sprite.Sprite):
def __init__(self, screen, position, group, team):
super().__init__(group)
self.direction = Direction.DOWN
self.states = load_knight_textures(team)
self.image = self.states[self.direction.value]
self.position = position
self._layer = 1
position_in_px = (parse_cord(position[0]), parse_cord(position[1]))
self.rect = self.image.get_rect(topleft=position_in_px)
self.team = team
self.max_hp = random.randint(9, 13)
self.attack = random.randint(2, 4)
self.defense = random.randint(1, 4)
self.points = 1
self.health_bar = HealthBar(screen, self.rect, current_hp=self.max_hp, max_hp=self.max_hp, calculate_xy=True, calculate_size=True)
def rotate_left(self):
self.direction = self.direction.left()
self.image = self.states[self.direction.value]
def update(self):
self.health_bar.update()
def rotate_right(self):
self.direction = self.direction.right()
self.image = self.states[self.direction.value]
def take_dmg(self, amount):
self.health_bar.take_dmg(amount)
def heal(self, amount):
self.health_bar.heal(amount)
def get_current_hp(self):
return self.health_bar.current_hp
def get_max_hp(self):
return self.health_bar.max_hp
def step_forward(self):
if self.direction.name == 'UP':
self.position = (self.position[0], self.position[1] - 1)
self.rect.y = self.rect.y - GRID_CELL_SIZE - 5
elif self.direction.name == 'RIGHT':
self.position = (self.position[0] + 1, self.position[1])
self.rect.x = self.rect.x + GRID_CELL_SIZE + 5
elif self.direction.name == 'DOWN':
self.position = (self.position[0], self.position[1] + 1)
self.rect.y = self.rect.y + GRID_CELL_SIZE + 5
elif self.direction.name == 'LEFT':
self.position = (self.position[0] - 1, self.position[1])
self.rect.x = self.rect.x - GRID_CELL_SIZE - 5
def team_alias(self) -> str:
return "k_b" if self.team == "blue" else "k_r"

View File

@ -1,17 +1,47 @@
import pygame.image
import random import random
import pygame.image
from common.helpers import parse_cord
from logic.health_bar import HealthBar
monster_images = [
pygame.image.load("./resources/textures/dragon2.png"),
pygame.image.load("./resources/textures/birb.png"),
pygame.image.load("./resources/textures/wolfart.png"),
pygame.image.load("./resources/textures/goblin.png"),
]
class Monster(pygame.sprite.Sprite): class Monster(pygame.sprite.Sprite):
images = [] def __init__(self, screen, position, group):
super().__init__(group)
def __init__(self): self._layer = 1
super().__init__() self.image = random.choice(monster_images)
self.image = pygame.image.load("./resources/textures/dragon2.png")
self.image = pygame.transform.scale(self.image, (40, 40)) self.image = pygame.transform.scale(self.image, (40, 40))
self.images.append(self.image) position_in_px = (parse_cord(position[0]), parse_cord(position[1]))
self.rect = self.image.get_rect() self.rect = self.image.get_rect(topleft=position_in_px)
monster_list = pygame.sprite.Group() self.position = position
self.health = random.randrange(15, 25) self.max_hp = random.randrange(15, 20)
self.attack = random.randrange(2, 10) self.health_bar = HealthBar(screen, self.rect, current_hp=self.max_hp, max_hp=self.max_hp,
calculate_xy=True, calculate_size=True)
self.attack = random.randrange(4, 6)
if self.image == monster_images[0]:
self.max_hp = 20
self.attack = 6
self.points = 10
elif self.image == monster_images[1]:
self.max_hp = 15
self.attack = 7
self.points = 7
elif self.image == monster_images[2]:
self.max_hp = 10
self.attack = 4
self.points = 4
elif self.image == monster_images[3]:
self.max_hp = 7
self.attack = 2
self.points = 2
def update(self):
self.health_bar.update()

13
models/tile.py Normal file
View File

@ -0,0 +1,13 @@
import pygame
from common.helpers import parse_cord
class Tile(pygame.sprite.Sprite):
def __init__(self, position, image, group, tile_type=' '):
super().__init__(group)
self.image = image
self._layer = 0
position_in_px = (parse_cord(position[0]), parse_cord(position[1]))
self.rect = self.image.get_rect(topleft=position_in_px)
self.tile_type = tile_type

Binary file not shown.

View File

@ -0,0 +1 @@
{"map": [[0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], [0, 0, 0, 0, 3, 3, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3], [0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 3], [0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0, 3], [0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2], [0, 0, 3, 3, 3, 0, 0, 0, 3, 3, 3, 3, 0, 2, 2, 2, 0, 0, 0, 0, 0, 7, 2, 0], [0, 0, 0, 6, 0, 0, 0, 2, 2, 2, 0, 5, 5, 0, 2, 0, 0, 2, 2, 2, 2, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 5, 5, 0, 2, 0, 0, 0, 0, 2, 2, 1, 1, 7], [0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, 1, 7], [6, 0, 0, 6, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 1, 1, 0], [6, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 3, 3, 0, 0, 0, 0, 3, 3, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 3, 3, 0, 1, 1, 7, 0], [0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 3, 3, 3, 1, 1, 1, 0], [0, 3, 3, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 1, 0, 0], [0, 3, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]}

View File

@ -0,0 +1 @@
{"map": [[0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0], [0, 3, 3, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 3, 3, 3, 0, 0, 0, 2, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0], [0, 0, 3, 2, 2, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 2, 2, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0], [1, 1, 0, 0, 3, 0, 0, 0, 0, 0, 3, 3, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 6, 3, 3, 0, 0, 0, 0, 0, 3, 0, 4, 0, 3, 0, 0, 0, 0, 0, 0, 7, 0, 0], [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 7, 7, 0, 0], [6, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 3, 7, 0, 0, 0], [0, 0, 6, 6, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0], [0, 2, 2, 2, 2, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0], [2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [2, 0, 2, 0, 0, 0, 0, 0, 4, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]}

View File

@ -0,0 +1 @@
{"map": [[0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 3, 3], [0, 0, 0, 0, 2, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 3, 0], [0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 2, 0], [0, 0, 3, 3, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 2, 0], [0, 0, 3, 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0], [0, 0, 3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0], [0, 0, 0, 6, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0], [0, 6, 0, 0, 0, 0, 3, 0, 0, 0, 0, 5, 5, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 5, 5, 1, 2, 0, 0, 0, 0, 0, 0, 7, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 3, 0, 0, 0, 4, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0], [0, 0, 3, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 4], [6, 0, 0, 6, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1, 1, 0, 7, 0, 7, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 3, 2, 0, 0, 0, 0, 0, 0], [0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0], [0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]}

BIN
resources/textures/birb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -1,25 +1,38 @@
from queue import Queue
import pygame import pygame
from common.colors import FONT_DARK, ORANGE, WHITE from common.colors import FONT_DARK, ORANGE, WHITE
from common.constants import COLUMNS, GRID_CELL_PADDING, GRID_CELL_WIDTH, BORDER_WIDTH, BORDER_RADIUS from common.constants import COLUMNS, GRID_CELL_PADDING, GRID_CELL_SIZE, BORDER_WIDTH, BORDER_RADIUS
from common.helpers import draw_text from common.helpers import draw_text
class Logs: class Logs:
def __init__(self): def __init__(self, screen):
self.grid = [] self.log_queue = Queue(maxsize=7)
self.screen = screen
def draw(self, screen): def draw(self):
x = (GRID_CELL_PADDING + GRID_CELL_WIDTH) * COLUMNS + BORDER_WIDTH + 15 x = (GRID_CELL_PADDING + GRID_CELL_SIZE) * COLUMNS + BORDER_WIDTH + 15
y = 470 y = 470
# background # background
pygame.draw.rect(screen, WHITE, pygame.Rect(x, y, 340, 323), 0, BORDER_RADIUS) pygame.draw.rect(self.screen, WHITE, pygame.Rect(x, y, 340, 323), 0, BORDER_RADIUS)
# title # title
draw_text('LOGS', FONT_DARK, screen, x + 120, y + 10, 36) draw_text('LOGS', FONT_DARK, self.screen, x + 120, y + 10, 36)
pygame.draw.rect(screen, ORANGE, pygame.Rect(x, y + 65, 340, 3)) pygame.draw.rect(self.screen, ORANGE, pygame.Rect(x, y + 65, 340, 3))
# texts # texts
draw_text('AI Blue: Zniszczyła fortecę (4, 8).', FONT_DARK, screen, x + 35, y + 90, 16) next_y = y + 90
draw_text('AI Red: Zniszczyła fortecę (12, 5).', FONT_DARK, screen, x + 35, y + 120, 16) i = 0
start = len(self.log_queue.queue) - 1
for idx in range(start, -1, -1):
draw_text(self.log_queue.queue[idx], FONT_DARK, self.screen, x + 35, next_y + i * 30, 16)
i = i + 1
def enqueue_log(self, text):
if self.log_queue.full():
self.log_queue.get()
self.log_queue.put(text)
self.draw()

View File

@ -1,6 +1,31 @@
from ui.screens.screen import Screen import pygame
from ui.screens.screen import Screen
from common.colors import BLACK
from common.helpers import draw_text
class Credits(Screen): class Credits(Screen):
def __init__(self, screen, clock): def __init__(self, screen, clock):
super().__init__('credits', screen, clock) super().__init__('credits', screen, clock)
def display_screen(self):
running = True
while running:
self.screen.fill((252, 164, 12))
draw_text('Twórcy :', BLACK, self.screen, 520, 150)
draw_text('Angelika Iskra', BLACK, self.screen, 520, 250)
draw_text('Dawid Korzępa', BLACK, self.screen, 520, 300)
draw_text('Juliusz Sadowski', BLACK, self.screen, 520, 350)
draw_text('Aleksandra Muczyńska', BLACK, self.screen, 520, 400)
draw_text('Jerzy Tomaszewski', BLACK, self.screen, 520, 450)
draw_text('Mateusz Konofał', BLACK, self.screen, 520, 500)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
pygame.display.update()
self.clock.tick(60)

66
ui/screens/main_menu.py Normal file
View File

@ -0,0 +1,66 @@
import sys
import pygame
from common.colors import WHITE, FONT_DARK
from common.helpers import draw_text
from ui.screens.credits import Credits
from ui.screens.options import Options
from ui.screens.screen import Screen
class MainMenu(Screen):
def __init__(self, screen, clock, bg, btn_play_action, btn_options_action, btn_credits_action):
super().__init__('main_menu', screen, clock)
self.click = False
self.bg = bg
self.btn_play_action = btn_play_action
self.btn_options_action = btn_options_action
self.btn_credits_action = btn_credits_action
def display_screen(self):
running = True
while running:
self.screen.blit(self.bg, (0, 0))
pygame.draw.rect(self.screen, (255, 255, 255), pygame.Rect(800, 100, 400, 500), 0, 5)
draw_text('MAIN MENU', FONT_DARK, self.screen, 850, 150, 30, True)
mx, my = pygame.mouse.get_pos()
button_1 = pygame.Rect(850, 250, 300, 50)
button_2 = pygame.Rect(850, 350, 300, 50)
button_3 = pygame.Rect(850, 450, 300, 50)
if button_1.collidepoint((mx, my)):
if self.click:
self.btn_play_action()
if button_2.collidepoint((mx, my)):
if self.click:
self.btn_options_action()
if button_3.collidepoint((mx, my)):
if self.click:
self.btn_credits_action()
pygame.draw.rect(self.screen, (0, 191, 255), button_1, 0, 4)
draw_text('PLAY', WHITE, self.screen, 870, 255)
pygame.draw.rect(self.screen, (0, 191, 255), button_2, 0, 4)
draw_text('OPTIONS', WHITE, self.screen, 870, 355)
pygame.draw.rect(self.screen, (0, 191, 255), button_3, 0, 4)
draw_text('CREDITS', WHITE, self.screen, 870, 455)
self.click = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
self.click = True
pygame.display.update()
self.clock.tick(60)

View File

@ -1,44 +1,67 @@
import pygame import pygame
import time
from logic.health_bar import HealthBar
from common.colors import FONT_DARK, ORANGE, WHITE, RED from common.colors import FONT_DARK, ORANGE, WHITE, RED
from common.constants import COLUMNS, GRID_CELL_PADDING, GRID_CELL_WIDTH, BORDER_WIDTH, BORDER_RADIUS from common.constants import COLUMNS, GRID_CELL_PADDING, GRID_CELL_SIZE, BORDER_WIDTH, BORDER_RADIUS
from common.helpers import draw_text from common.helpers import draw_text
class Stats: class Stats:
def __init__(self): def __init__(self, screen, list_knights_blue, list_knights_red):
self.grid = [] self.grid = []
self.list_knights_blue = list_knights_blue
self.list_knights_red = list_knights_red
self.screen = screen
self.x = (GRID_CELL_PADDING + GRID_CELL_SIZE) * COLUMNS + BORDER_WIDTH + 15
self.y = 5
self.blue_team_hp_bar = HealthBar(self.screen,
pygame.Rect(self.x + 30, self.y + 210, 100, 15),
current_hp=sum([knight.get_current_hp() for knight in self.list_knights_blue]),
max_hp=sum([knight.get_max_hp() for knight in self.list_knights_blue]))
self.red_team_hp_bar = HealthBar(self.screen,
pygame.Rect(self.x + 210, self.y + 210, 100, 15),
current_hp=sum([knight.get_current_hp() for knight in self.list_knights_red]),
max_hp=sum([knight.get_max_hp() for knight in self.list_knights_red]))
self.blue_team_points = 0
self.red_team_points = 0
def draw(self, screen): def update(self):
x = (GRID_CELL_PADDING + GRID_CELL_WIDTH) * COLUMNS + BORDER_WIDTH + 15
y = 5
# background # background
pygame.draw.rect(screen, WHITE, pygame.Rect(x, y, 340, 450), 0, BORDER_RADIUS) pygame.draw.rect(self.screen, WHITE, pygame.Rect(self.x, self.y, 340, 450), 0, BORDER_RADIUS)
# title # title
draw_text('STATS', FONT_DARK, screen, x + 120, y + 10, 36) draw_text('STATS', FONT_DARK, self.screen, self.x + 120, self.y + 10, 36)
pygame.draw.rect(screen, ORANGE, pygame.Rect(x, y + 65, 340, 3)) pygame.draw.rect(self.screen, ORANGE, pygame.Rect(self.x, self.y + 65, 340, 3))
# shields # shields
shield_blue = pygame.image.load('./resources/textures/shield_blue.png') shield_blue = pygame.image.load('./resources/textures/shield_blue.png')
shield_red = pygame.image.load('./resources/textures/shield_red.png') shield_red = pygame.image.load('./resources/textures/shield_red.png')
screen.blit(shield_blue, (x + 20, y + 80)) self.screen.blit(shield_blue, (self.x + 20, self.y + 80))
screen.blit(shield_red, (x + 200, y + 80)) self.screen.blit(shield_red, (self.x + 200, self.y + 80))
draw_text('VS', FONT_DARK, screen, x + 150, y + 120, 36) draw_text('VS', FONT_DARK, self.screen, self.x + 150, self.y + 120, 36)
# HP bars # HP bars
pygame.draw.rect(screen, RED, pygame.Rect(x + 30, y + 210, 100, 15), 0, 4) self.red_team_hp_bar.take_dmg(self.red_team_hp_bar.current_hp -
pygame.draw.rect(screen, RED, pygame.Rect(x + 210, y + 210, 100, 15), 0, 4) sum([knight.get_current_hp() for knight in self.list_knights_red]))
self.blue_team_hp_bar.take_dmg(self.blue_team_hp_bar.current_hp -
sum([knight.get_current_hp() for knight in self.list_knights_blue]))
self.red_team_hp_bar.update()
self.blue_team_hp_bar.update()
# texts # texts
draw_text('Rycerze: 2', FONT_DARK, screen, x + 35, y + 240, 18) draw_text('Rycerze: ' + str(len(self.list_knights_blue)), FONT_DARK, self.screen, self.x + 35, self.y + 240, 18) # blue
draw_text('Fortece: 1', FONT_DARK, screen, x + 35, y + 270, 18)
draw_text('Rycerze: 4', FONT_DARK, screen, x + 215, y + 240, 18) draw_text('Rycerze: ' + str(len(self.list_knights_red)), FONT_DARK, self.screen, self.x + 215, self.y + 240, 18)
draw_text('Fortece: 0', FONT_DARK, screen, x + 215, y + 270, 18)
# points # points
pygame.draw.rect(screen, ORANGE, pygame.Rect(x, y + 390, 340, 3)) pygame.draw.rect(self.screen, ORANGE, pygame.Rect(self.x, self.y + 390, 340, 3))
draw_text('PUNKTY: 10', FONT_DARK, screen, x + 35, y + 408, 18, True) draw_text('PUNKTY: ' + str(self.blue_team_points), FONT_DARK, self.screen, self.x + 35, self.y + 408, 18, True)
draw_text('PUNKTY: 10', FONT_DARK, screen, x + 215, y + 408, 18, True) draw_text('PUNKTY: ' + str(self.red_team_points), FONT_DARK, self.screen, self.x + 215, self.y + 408, 18, True)
def add_points(self, team, points):
if team == "blue":
self.blue_team_points += points
else:
self.red_team_points += points