WMICraft/learning/decision_tree.py

79 lines
3.8 KiB
Python

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]