diff --git a/algorithms/bfs.py b/algorithms/bfs.py index b2ed3d3..959a684 100644 --- a/algorithms/bfs.py +++ b/algorithms/bfs.py @@ -51,7 +51,7 @@ def graphsearch(initial_state: State, map, goal_list, fringe: List[Node] = None, explored_states = set() fringe_states = set() - # root Node + # train Node fringe.append(Node(initial_state)) fringe_states.add((initial_state.row, initial_state.column, initial_state.direction)) @@ -71,7 +71,7 @@ def graphsearch(initial_state: State, map, goal_list, fringe: List[Node] = None, parent = element.parent while parent is not None: - # root's action will be None, don't add it + # train's action will be None, don't add it if parent.action is not None: actions_sequence.append(parent.action) parent = parent.parent diff --git a/algorithms/neural_network/data/test/grass/grass1.png b/algorithms/neural_network/data/test/grass/grass1.png new file mode 100644 index 0000000..dd88981 Binary files /dev/null and b/algorithms/neural_network/data/test/grass/grass1.png differ diff --git a/algorithms/neural_network/data/test/grass/grass2.png b/algorithms/neural_network/data/test/grass/grass2.png new file mode 100644 index 0000000..c07ab8f Binary files /dev/null and b/algorithms/neural_network/data/test/grass/grass2.png differ diff --git a/algorithms/neural_network/data/test/grass/grass3.png b/algorithms/neural_network/data/test/grass/grass3.png new file mode 100644 index 0000000..4fac085 Binary files /dev/null and b/algorithms/neural_network/data/test/grass/grass3.png differ diff --git a/algorithms/neural_network/data/test/grass/grass4.png b/algorithms/neural_network/data/test/grass/grass4.png new file mode 100644 index 0000000..74cb3d1 Binary files /dev/null and b/algorithms/neural_network/data/test/grass/grass4.png differ diff --git a/algorithms/neural_network/data/test/sand/sand.png b/algorithms/neural_network/data/test/sand/sand.png new file mode 100644 index 0000000..51c072d Binary files /dev/null and b/algorithms/neural_network/data/test/sand/sand.png differ diff --git a/algorithms/neural_network/data/test/tree/grass_with_tree.jpg b/algorithms/neural_network/data/test/tree/grass_with_tree.jpg new file mode 100644 index 0000000..56af6b8 Binary files /dev/null and b/algorithms/neural_network/data/test/tree/grass_with_tree.jpg differ diff --git a/algorithms/neural_network/data/test/water/water.png b/algorithms/neural_network/data/test/water/water.png new file mode 100644 index 0000000..28b45db Binary files /dev/null and b/algorithms/neural_network/data/test/water/water.png differ diff --git a/algorithms/neural_network/lightning_logs/version_0/checkpoints/epoch=6-step=630.ckpt b/algorithms/neural_network/lightning_logs/version_0/checkpoints/epoch=6-step=630.ckpt new file mode 100644 index 0000000..e3fcb43 Binary files /dev/null and b/algorithms/neural_network/lightning_logs/version_0/checkpoints/epoch=6-step=630.ckpt differ diff --git a/algorithms/neural_network/lightning_logs/version_0/events.out.tfevents.1653283421.DESKTOP-97QK98R.19372.0 b/algorithms/neural_network/lightning_logs/version_0/events.out.tfevents.1653283421.DESKTOP-97QK98R.19372.0 new file mode 100644 index 0000000..8f13c49 Binary files /dev/null and b/algorithms/neural_network/lightning_logs/version_0/events.out.tfevents.1653283421.DESKTOP-97QK98R.19372.0 differ diff --git a/algorithms/neural_network/lightning_logs/version_0/hparams.yaml b/algorithms/neural_network/lightning_logs/version_0/hparams.yaml new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/algorithms/neural_network/lightning_logs/version_0/hparams.yaml @@ -0,0 +1 @@ +{} diff --git a/algorithms/neural_network/lightning_logs/version_1/checkpoints/epoch=7-step=720.ckpt b/algorithms/neural_network/lightning_logs/version_1/checkpoints/epoch=7-step=720.ckpt new file mode 100644 index 0000000..bb7429b Binary files /dev/null and b/algorithms/neural_network/lightning_logs/version_1/checkpoints/epoch=7-step=720.ckpt differ diff --git a/algorithms/neural_network/lightning_logs/version_1/events.out.tfevents.1653288273.DESKTOP-97QK98R.9316.0 b/algorithms/neural_network/lightning_logs/version_1/events.out.tfevents.1653288273.DESKTOP-97QK98R.9316.0 new file mode 100644 index 0000000..07da97d Binary files /dev/null and b/algorithms/neural_network/lightning_logs/version_1/events.out.tfevents.1653288273.DESKTOP-97QK98R.9316.0 differ diff --git a/algorithms/neural_network/lightning_logs/version_1/hparams.yaml b/algorithms/neural_network/lightning_logs/version_1/hparams.yaml new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/algorithms/neural_network/lightning_logs/version_1/hparams.yaml @@ -0,0 +1 @@ +{} diff --git a/algorithms/neural_network/lightning_logs/version_2/checkpoints/epoch=0-step=90.ckpt b/algorithms/neural_network/lightning_logs/version_2/checkpoints/epoch=0-step=90.ckpt new file mode 100644 index 0000000..46e80c2 Binary files /dev/null and b/algorithms/neural_network/lightning_logs/version_2/checkpoints/epoch=0-step=90.ckpt differ diff --git a/algorithms/neural_network/lightning_logs/version_2/events.out.tfevents.1653290364.DESKTOP-97QK98R.8432.0 b/algorithms/neural_network/lightning_logs/version_2/events.out.tfevents.1653290364.DESKTOP-97QK98R.8432.0 new file mode 100644 index 0000000..37da598 Binary files /dev/null and b/algorithms/neural_network/lightning_logs/version_2/events.out.tfevents.1653290364.DESKTOP-97QK98R.8432.0 differ diff --git a/algorithms/neural_network/lightning_logs/version_2/hparams.yaml b/algorithms/neural_network/lightning_logs/version_2/hparams.yaml new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/algorithms/neural_network/lightning_logs/version_2/hparams.yaml @@ -0,0 +1 @@ +{} diff --git a/algorithms/neural_network/lightning_logs/version_3/checkpoints/epoch=8-step=810.ckpt b/algorithms/neural_network/lightning_logs/version_3/checkpoints/epoch=8-step=810.ckpt new file mode 100644 index 0000000..beb5dcd Binary files /dev/null and b/algorithms/neural_network/lightning_logs/version_3/checkpoints/epoch=8-step=810.ckpt differ diff --git a/algorithms/neural_network/lightning_logs/version_3/events.out.tfevents.1653310536.DESKTOP-97QK98R.15588.0 b/algorithms/neural_network/lightning_logs/version_3/events.out.tfevents.1653310536.DESKTOP-97QK98R.15588.0 new file mode 100644 index 0000000..8e95fd1 Binary files /dev/null and b/algorithms/neural_network/lightning_logs/version_3/events.out.tfevents.1653310536.DESKTOP-97QK98R.15588.0 differ diff --git a/algorithms/neural_network/lightning_logs/version_3/hparams.yaml b/algorithms/neural_network/lightning_logs/version_3/hparams.yaml new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/algorithms/neural_network/lightning_logs/version_3/hparams.yaml @@ -0,0 +1 @@ +{} diff --git a/algorithms/neural_network/neural_network.py b/algorithms/neural_network/neural_network.py new file mode 100644 index 0000000..d439d5b --- /dev/null +++ b/algorithms/neural_network/neural_network.py @@ -0,0 +1,48 @@ +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().__init__() + self.layer = nn.Sequential( + nn.Linear(36*36*3, 300), + nn.ReLU(), + nn.Linear(300, 4), + nn.LogSoftmax(dim=-1) + ) + self.batch_size = batch_size + self.learning_rate = learning_rate + + def forward(self, x): + x = x.reshape(x.shape[0], -1) + x = self.layer(x) + return x + + def configure_optimizers(self): + optimizer = SGD(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) diff --git a/algorithms/neural_network/neural_network_interface.py b/algorithms/neural_network/neural_network_interface.py new file mode 100644 index 0000000..5098e45 --- /dev/null +++ b/algorithms/neural_network/neural_network_interface.py @@ -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 + + +def train(model): + model = model.to(DEVICE) + model.train() + 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, shuffle=True) + + criterion = nn.CrossEntropyLoss() + optimizer = Adam(model.parameters(), lr=LEARNING_RATE) + + for epoch in range(NUM_EPOCHS): + for batch_idx, (data, targets) in enumerate(train_loader): + data = data.to(device=DEVICE) + targets = targets.to(device=DEVICE) + + scores = model(data) + loss = criterion(scores, targets) + + optimizer.zero_grad() + loss.backward() + + optimizer.step() + + if batch_idx % 4 == 0: + print("epoch: %d loss: %.4f" % (epoch, loss.item())) + + print("FINISHED TRAINING!") + torch.save(model.state_dict(), "./learnednetwork.pth") + + print("Checking accuracy for the train set.") + check_accuracy(train_loader) + print("Checking accuracy for the test set.") + check_accuracy(test_loader) + print("Checking accuracy for the tiles.") + check_accuracy_tiles() + + +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 = read_image(img_path, mode=ImageReadMode.RGB) + if show_img: + plt.imshow(plt.imread(img_path)) + plt.show() + image = SETUP_PHOTOS(image).unsqueeze(0) + model = NeuralNetwork.load_from_checkpoint('./lightning_logs/version_3/checkpoints/epoch=8-step=810.ckpt') + + with torch.no_grad(): + model.eval() + idx = int(model(image).argmax(dim=1)) + return ID_TO_CLASS[idx] + + +CNN = NeuralNetwork() + + +trainer = pl.Trainer(accelerator='gpu', devices=1, auto_scale_batch_size=True, callbacks=[EarlyStopping('val_loss')], 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) +check_accuracy_tiles() +print(what_is_it('../../resources/textures/sand.png', True)) diff --git a/algorithms/neural_network/watersandtreegrass.py b/algorithms/neural_network/watersandtreegrass.py new file mode 100644 index 0000000..93525d0 --- /dev/null +++ b/algorithms/neural_network/watersandtreegrass.py @@ -0,0 +1,25 @@ +import torch +from torch.utils.data import Dataset +import pandas as pd +from torchvision.io import read_image, ImageReadMode +from common.helpers import createCSV + + +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 = read_image(self.img_labels.iloc[idx, 0], mode=ImageReadMode.RGB) + label = torch.tensor(int(self.img_labels.iloc[idx, 1])) + + if self.transform: + image = self.transform(image) + + return image, label + diff --git a/common/constants.py b/common/constants.py index 6d91768..29ffa0c 100644 --- a/common/constants.py +++ b/common/constants.py @@ -1,4 +1,6 @@ from enum import Enum +import torchvision.transforms as transforms +import torch GAME_TITLE = 'WMICraft' WINDOW_HEIGHT = 800 @@ -63,12 +65,34 @@ ACTION = { "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 -LEFT = 'LEFT' -RIGHT = 'RIGHT' -UP = 'UP' -DOWN = 'DOWN' + +#NEURAL_NETWORK +LEARNING_RATE = 0.13182567385564073 +BATCH_SIZE = 64 +NUM_EPOCHS = 50 + +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.Resize(36), + transforms.CenterCrop(36), + transforms.ToPILImage(), + transforms.ToTensor(), + 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()} + diff --git a/common/helpers.py b/common/helpers.py index b8d68e9..3b0aa8c 100644 --- a/common/helpers.py +++ b/common/helpers.py @@ -1,6 +1,9 @@ from typing import Tuple, List import pygame +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 @@ -24,6 +27,44 @@ def draw_text(text, color, surface, x, y, text_size=30, is_bold=False): 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) diff --git a/logic/health_bar.py b/logic/health_bar.py index c362fc7..0b8bc3a 100644 --- a/logic/health_bar.py +++ b/logic/health_bar.py @@ -46,7 +46,7 @@ class HealthBar: def heal(self, amount): if self.current_hp + amount < self.max_hp: self.current_hp += amount - elif self.current_hp + amount > self.max_hp: + elif self.current_hp + amount >= self.max_hp: self.current_hp = self.max_hp def show(self): diff --git a/logic/level.py b/logic/level.py index 0491a4d..73dcd8e 100644 --- a/logic/level.py +++ b/logic/level.py @@ -155,19 +155,6 @@ class Level: 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_health_bars(self): - for knight in self.list_knights_blue: - knight.health_bar.update() - - for knight in self.list_knights_red: - knight.health_bar.update() - - for monster in self.list_monsters: - monster.health_bar.update() - - for castle in self.list_castles: - castle.health_bar.update() - 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 @@ -175,4 +162,4 @@ class Level: # update and draw the game self.sprites.draw(self.screen) - self.update_health_bars() # has to be called last + self.sprites.update() diff --git a/models/castle.py b/models/castle.py index 915b514..6484c63 100644 --- a/models/castle.py +++ b/models/castle.py @@ -18,3 +18,6 @@ class Castle(pygame.sprite.Sprite): self.max_hp = 80 self.current_hp = random.randint(1, self.max_hp) self.health_bar = HealthBar(screen, self.rect, current_hp=self.current_hp, max_hp=self.max_hp, calculate_xy=True, calculate_size=True) + + def update(self): + self.health_bar.update() diff --git a/models/knight.py b/models/knight.py index 93432e1..05eabfc 100644 --- a/models/knight.py +++ b/models/knight.py @@ -43,6 +43,9 @@ class Knight(pygame.sprite.Sprite): 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] diff --git a/models/monster.py b/models/monster.py index 42dc424..9ecbe09 100644 --- a/models/monster.py +++ b/models/monster.py @@ -43,3 +43,6 @@ class Monster(pygame.sprite.Sprite): self.max_hp = 7 self.attack = 2 self.points = 2 + + def update(self): + self.health_bar.update() diff --git a/requirements.txt b/requirements.txt index b4b38c6..8223619 100644 Binary files a/requirements.txt and b/requirements.txt differ