a_star #21
198
algorithms/a_star.py
Normal file
198
algorithms/a_star.py
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import heapq
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Tuple, Optional, List
|
||||||
|
|
||||||
|
from common.constants import ROWS, COLUMNS
|
||||||
|
|
||||||
|
EMPTY_FIELDS = ['s', 'g', ' ']
|
||||||
|
LEFT = 'LEFT'
|
||||||
|
RIGHT = 'RIGHT'
|
||||||
|
UP = 'UP'
|
||||||
|
DOWN = 'DOWN'
|
||||||
|
|
||||||
|
TURN_LEFT = 'TURN_LEFT'
|
||||||
|
TURN_RIGHT = 'TURN_RIGHT'
|
||||||
|
FORWARD = 'FORWARD'
|
||||||
|
|
||||||
|
directions = {
|
||||||
|
LEFT: (0, -1),
|
||||||
|
RIGHT: (0, 1),
|
||||||
|
UP: (-1, 0),
|
||||||
|
DOWN: (1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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 []
|
@ -114,7 +114,7 @@ def go(row, column, direction):
|
|||||||
|
|
||||||
|
|
||||||
def is_valid_move(map, target_row, target_column):
|
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] == ' ':
|
if 0 <= target_row < ROWS and 0 <= target_column < COLUMNS and map[target_row][target_column] in ['g', 's', ' ']:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
@ -4,7 +4,7 @@ GAME_TITLE = 'WMICraft'
|
|||||||
WINDOW_HEIGHT = 800
|
WINDOW_HEIGHT = 800
|
||||||
WINDOW_WIDTH = 1360
|
WINDOW_WIDTH = 1360
|
||||||
FPS_COUNT = 60
|
FPS_COUNT = 60
|
||||||
TURN_INTERVAL = 1000
|
TURN_INTERVAL = 300
|
||||||
|
|
||||||
GRID_CELL_PADDING = 5
|
GRID_CELL_PADDING = 5
|
||||||
GRID_CELL_SIZE = 36
|
GRID_CELL_SIZE = 36
|
||||||
@ -29,6 +29,7 @@ CASTLE_SPAWN_FIRST_COL = 9
|
|||||||
NBR_OF_WATER = 16
|
NBR_OF_WATER = 16
|
||||||
NBR_OF_TREES = 20
|
NBR_OF_TREES = 20
|
||||||
NBR_OF_MONSTERS = 2
|
NBR_OF_MONSTERS = 2
|
||||||
|
NBR_OF_SANDS = 35
|
||||||
|
|
||||||
TILES = [
|
TILES = [
|
||||||
'grass1.png',
|
'grass1.png',
|
||||||
|
@ -19,12 +19,13 @@ 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.logs = Logs(self.screen)
|
||||||
self.clock = pygame.time.Clock()
|
self.clock = pygame.time.Clock()
|
||||||
|
|
||||||
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)}
|
||||||
|
|
||||||
self.level = Level(self.screen)
|
self.level = Level(self.screen, self.logs)
|
||||||
|
|
||||||
def main_menu(self):
|
def main_menu(self):
|
||||||
menu = MainMenu(self.screen, self.clock, self.bg,
|
menu = MainMenu(self.screen, self.clock, self.bg,
|
||||||
@ -35,7 +36,6 @@ class Game:
|
|||||||
|
|
||||||
def game(self):
|
def game(self):
|
||||||
stats = Stats()
|
stats = Stats()
|
||||||
logs = Logs()
|
|
||||||
|
|
||||||
# setup clock for rounds
|
# setup clock for rounds
|
||||||
NEXT_TURN = pygame.USEREVENT + 1
|
NEXT_TURN = pygame.USEREVENT + 1
|
||||||
@ -62,7 +62,7 @@ class Game:
|
|||||||
self.level.handle_turn()
|
self.level.handle_turn()
|
||||||
|
|
||||||
stats.draw(self.screen)
|
stats.draw(self.screen)
|
||||||
logs.draw(self.screen)
|
self.logs.draw()
|
||||||
|
|
||||||
self.level.update()
|
self.level.update()
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import random
|
|||||||
|
|
||||||
import pygame
|
import pygame
|
||||||
|
|
||||||
from algorithms.bfs import graphsearch, State
|
from algorithms.a_star import a_star, State, TURN_RIGHT, TURN_LEFT, FORWARD, UP, DOWN, LEFT, RIGHT
|
||||||
from common.constants import *
|
from common.constants import *
|
||||||
from common.helpers import castle_neighbors
|
from common.helpers import castle_neighbors
|
||||||
from logic.knights_queue import KnightsQueue
|
from logic.knights_queue import KnightsQueue
|
||||||
@ -14,13 +14,13 @@ from models.tile import Tile
|
|||||||
|
|
||||||
|
|
||||||
class Level:
|
class Level:
|
||||||
def __init__(self, screen):
|
def __init__(self, screen, logs):
|
||||||
self.screen = screen
|
self.screen = screen
|
||||||
|
self.logs = logs
|
||||||
# sprite group setup
|
# sprite group setup
|
||||||
self.sprites = pygame.sprite.Group()
|
self.sprites = pygame.sprite.Group()
|
||||||
|
|
||||||
self.map = [[' ' for x in range(COLUMNS)] for y in range(ROWS)]
|
self.map = [['g' for _ in range(COLUMNS)] for y in range(ROWS)]
|
||||||
|
|
||||||
self.list_knights_blue = []
|
self.list_knights_blue = []
|
||||||
self.list_knights_red = []
|
self.list_knights_red = []
|
||||||
@ -37,18 +37,19 @@ class Level:
|
|||||||
|
|
||||||
def generate_map(self):
|
def generate_map(self):
|
||||||
spawner = Spawner(self.map)
|
spawner = Spawner(self.map)
|
||||||
spawner.spawn_where_possible(['w' for x in range(NBR_OF_WATER)])
|
spawner.spawn_where_possible(['w' for _ in range(NBR_OF_WATER)])
|
||||||
spawner.spawn_where_possible(['t' for x in range(NBR_OF_TREES)])
|
spawner.spawn_where_possible(['t' for _ in range(NBR_OF_TREES)])
|
||||||
|
spawner.spawn_where_possible(['s' for _ in range(NBR_OF_SANDS)])
|
||||||
|
|
||||||
spawner.spawn_in_area(['k_b' for x in range(4)], LEFT_KNIGHTS_SPAWN_FIRST_ROW, LEFT_KNIGHTS_SPAWN_FIRST_COL,
|
spawner.spawn_in_area(['k_b' for _ in range(4)], LEFT_KNIGHTS_SPAWN_FIRST_ROW, LEFT_KNIGHTS_SPAWN_FIRST_COL,
|
||||||
KNIGHTS_SPAWN_WIDTH, KNIGHTS_SPAWN_HEIGHT)
|
KNIGHTS_SPAWN_WIDTH, KNIGHTS_SPAWN_HEIGHT)
|
||||||
spawner.spawn_in_area(['k_r' for x in range(4)], RIGHT_KNIGHTS_SPAWN_FIRST_ROW, RIGHT_KNIGHTS_SPAWN_FIRST_COL,
|
spawner.spawn_in_area(['k_r' for _ in range(4)], RIGHT_KNIGHTS_SPAWN_FIRST_ROW, RIGHT_KNIGHTS_SPAWN_FIRST_COL,
|
||||||
KNIGHTS_SPAWN_WIDTH, KNIGHTS_SPAWN_HEIGHT)
|
KNIGHTS_SPAWN_WIDTH, KNIGHTS_SPAWN_HEIGHT)
|
||||||
|
|
||||||
spawner.spawn_in_area(['c'], CASTLE_SPAWN_FIRST_ROW, CASTLE_SPAWN_FIRST_COL, CASTLE_SPAWN_WIDTH,
|
spawner.spawn_in_area(['c'], CASTLE_SPAWN_FIRST_ROW, CASTLE_SPAWN_FIRST_COL, CASTLE_SPAWN_WIDTH,
|
||||||
CASTLE_SPAWN_HEIGHT, 2)
|
CASTLE_SPAWN_HEIGHT, 2)
|
||||||
|
|
||||||
spawner.spawn_where_possible(['m' for x in range(NBR_OF_MONSTERS)])
|
spawner.spawn_where_possible(['m' for _ in range(NBR_OF_MONSTERS)])
|
||||||
|
|
||||||
def setup_base_tiles(self):
|
def setup_base_tiles(self):
|
||||||
textures = []
|
textures = []
|
||||||
@ -69,8 +70,12 @@ class Level:
|
|||||||
texture_index = 6
|
texture_index = 6
|
||||||
texture_surface = textures[texture_index][1]
|
texture_surface = textures[texture_index][1]
|
||||||
Tile((col_index, row_index), texture_surface, self.sprites, 't')
|
Tile((col_index, row_index), texture_surface, self.sprites, 't')
|
||||||
|
elif col == "s":
|
||||||
|
texture_index = 4
|
||||||
|
texture_surface = textures[texture_index][1]
|
||||||
|
Tile((col_index, row_index), texture_surface, self.sprites)
|
||||||
else:
|
else:
|
||||||
texture_index = random.randint(0, 4)
|
texture_index = random.randint(0, 3)
|
||||||
texture_surface = textures[texture_index][1]
|
texture_surface = textures[texture_index][1]
|
||||||
Tile((col_index, row_index), texture_surface, self.sprites)
|
Tile((col_index, row_index), texture_surface, self.sprites)
|
||||||
|
|
||||||
@ -106,34 +111,41 @@ class Level:
|
|||||||
current_knight = self.knights_queue.dequeue_knight()
|
current_knight = self.knights_queue.dequeue_knight()
|
||||||
knight_pos_x = current_knight.position[0]
|
knight_pos_x = current_knight.position[0]
|
||||||
knight_pos_y = current_knight.position[1]
|
knight_pos_y = current_knight.position[1]
|
||||||
state = State(knight_pos_y, knight_pos_x, current_knight.direction)
|
|
||||||
|
|
||||||
castle_cords = (self.list_castles[0].position[0], self.list_castles[0].position[1])
|
castle_cords = (self.list_castles[0].position[0], self.list_castles[0].position[1])
|
||||||
goal_list = castle_neighbors(self.map, castle_cords[0], castle_cords[1]) # list of castle neighbors
|
goal_list = castle_neighbors(self.map, castle_cords[0], castle_cords[1]) # list of castle neighbors
|
||||||
action_list = graphsearch(state, self.map, goal_list)
|
|
||||||
|
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(action_list)
|
||||||
|
|
||||||
if len(action_list) == 0:
|
if len(action_list) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
next_action = action_list.pop(0)
|
next_action = action_list.pop(0)
|
||||||
if next_action == ACTION.get("rotate_left"):
|
if next_action == TURN_LEFT:
|
||||||
|
self.logs.enqueue_log(f'AI {current_knight.team}: Obrót w lewo.')
|
||||||
current_knight.rotate_left()
|
current_knight.rotate_left()
|
||||||
elif next_action == ACTION.get("rotate_right"):
|
elif next_action == TURN_RIGHT:
|
||||||
|
self.logs.enqueue_log(f'AI {current_knight.team}: Obrót w prawo.')
|
||||||
current_knight.rotate_right()
|
current_knight.rotate_right()
|
||||||
elif next_action == ACTION.get("go"):
|
elif next_action == FORWARD:
|
||||||
current_knight.step_forward()
|
current_knight.step_forward()
|
||||||
self.map[knight_pos_y][knight_pos_x] = ' '
|
self.map[knight_pos_y][knight_pos_x] = 'g'
|
||||||
|
|
||||||
# update knight on map
|
# update knight on map
|
||||||
if current_knight.direction.name == 'UP':
|
if current_knight.direction.name == UP:
|
||||||
self.map[knight_pos_y - 1][knight_pos_x] = current_knight
|
self.logs.enqueue_log(f'AI {current_knight.team}: Ruch do góry.')
|
||||||
elif current_knight.direction.name == 'RIGHT':
|
self.map[knight_pos_y - 1][knight_pos_x] = current_knight.team_alias()
|
||||||
self.map[knight_pos_y][knight_pos_x + 1] = current_knight
|
elif current_knight.direction.name == RIGHT:
|
||||||
elif current_knight.direction.name == 'DOWN':
|
self.logs.enqueue_log(f'AI {current_knight.team}: Ruch w prawo.')
|
||||||
self.map[knight_pos_y + 1][knight_pos_x] = current_knight
|
self.map[knight_pos_y][knight_pos_x + 1] = current_knight.team_alias()
|
||||||
elif current_knight.direction.name == 'LEFT':
|
elif current_knight.direction.name == DOWN:
|
||||||
self.map[knight_pos_y][knight_pos_x - 1] = current_knight
|
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):
|
def update(self):
|
||||||
bg_width = (GRID_CELL_PADDING + GRID_CELL_SIZE) * COLUMNS + BORDER_WIDTH
|
bg_width = (GRID_CELL_PADDING + GRID_CELL_SIZE) * COLUMNS + BORDER_WIDTH
|
||||||
|
@ -8,7 +8,7 @@ class Spawner:
|
|||||||
self.map = map
|
self.map = map
|
||||||
|
|
||||||
def __is_free_field(self, field):
|
def __is_free_field(self, field):
|
||||||
return field == ' '
|
return field in ['g', 's', ' ']
|
||||||
|
|
||||||
def spawn_in_area(self, objects: list, spawn_area_pos_row=0, spawn_area_pos_column=0, spawn_area_width=0,
|
def spawn_in_area(self, objects: list, spawn_area_pos_row=0, spawn_area_pos_column=0, spawn_area_width=0,
|
||||||
spawn_area_height=0, size=1):
|
spawn_area_height=0, size=1):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import pygame.image
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
import pygame.image
|
||||||
|
|
||||||
from common.constants import GRID_CELL_SIZE, Direction
|
from common.constants import GRID_CELL_SIZE, Direction
|
||||||
from common.helpers import parse_cord
|
from common.helpers import parse_cord
|
||||||
|
|
||||||
@ -56,3 +57,6 @@ class Knight(pygame.sprite.Sprite):
|
|||||||
elif self.direction.name == 'LEFT':
|
elif self.direction.name == 'LEFT':
|
||||||
self.position = (self.position[0] - 1, self.position[1])
|
self.position = (self.position[0] - 1, self.position[1])
|
||||||
self.rect.x = self.rect.x - GRID_CELL_SIZE - 5
|
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"
|
||||||
|
29
ui/logs.py
29
ui/logs.py
@ -1,3 +1,5 @@
|
|||||||
|
from queue import Queue
|
||||||
|
|
||||||
import pygame
|
import pygame
|
||||||
|
|
||||||
from common.colors import FONT_DARK, ORANGE, WHITE
|
from common.colors import FONT_DARK, ORANGE, WHITE
|
||||||
@ -6,20 +8,31 @@ 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_SIZE) * 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()
|
||||||
|
Loading…
Reference in New Issue
Block a user