added fitness function, first version of main and genetics integration
This commit is contained in:
parent
120cc7489e
commit
8e7e279c4b
0
algorithms/learn/genetic_algorithm/__init__.py
Normal file
0
algorithms/learn/genetic_algorithm/__init__.py
Normal file
@ -5,6 +5,8 @@ from itertools import permutations
|
|||||||
from numpy.random import randint
|
from numpy.random import randint
|
||||||
from numpy.random import rand
|
from numpy.random import rand
|
||||||
|
|
||||||
|
from algorithms.learn.genetic_algorithm import helpers
|
||||||
|
|
||||||
# this is helper function for sum_distance function, it counts the taxi cab distance between 2 vectors [x,y]
|
# this is helper function for sum_distance function, it counts the taxi cab distance between 2 vectors [x,y]
|
||||||
def distance(x,y):
|
def distance(x,y):
|
||||||
temp1 = abs(x[0]-y[0])
|
temp1 = abs(x[0]-y[0])
|
||||||
@ -76,22 +78,20 @@ def mutation(speciment, r_mut):
|
|||||||
|
|
||||||
|
|
||||||
# genetic algorithm
|
# genetic algorithm
|
||||||
def genetic_algorithm(objective, n_iter, n_pop, r_cross, r_mut):
|
def genetic_algorithm(minefield, objective, n_iter, n_pop, r_cross, r_mut):
|
||||||
# this is hardcoded list of coordinates of all mines (for tests only) which represents one speciment in population
|
# this is hardcoded list of coordinates of all mines (for tests only) which represents one speciment in population
|
||||||
# it is then permutated to get set number of species and create population
|
# it is then permutated to get set number of species and create population
|
||||||
speciment=[[1,1],[5,2],[1,3],[2,5],[2,1],[3,2],[3,4],[5,0]]
|
speciment=helpers.get_mines_coords(minefield)
|
||||||
pop = [random.choice(list(permutations(speciment,len(speciment)))) for _ in range(n_pop)]
|
pop = [random.sample(speciment, len(speciment)) for _ in range(n_pop)]
|
||||||
# permutation function returns tuples so I change them to lists
|
# permutation function returns tuples so I change them to lists
|
||||||
for i in range(len(pop)):
|
|
||||||
pop[i] = list(pop[i])
|
|
||||||
|
|
||||||
# keep track of best solution
|
# keep track of best solution
|
||||||
best, best_eval = 0, objective(pop[0])
|
best, best_eval = 0, objective(minefield, pop[0])
|
||||||
|
|
||||||
# enumerate generations
|
# enumerate generations
|
||||||
for gen in range(n_iter):
|
for gen in range(n_iter):
|
||||||
# evaluate all candidates in the population
|
# evaluate all candidates in the population
|
||||||
scores = [objective(c) for c in pop]
|
scores = [objective(minefield, c) for c in pop]
|
||||||
# check for new best solution
|
# check for new best solution
|
||||||
for i in range(n_pop):
|
for i in range(n_pop):
|
||||||
if scores[i] < best_eval:
|
if scores[i] < best_eval:
|
||||||
@ -115,19 +115,27 @@ def genetic_algorithm(objective, n_iter, n_pop, r_cross, r_mut):
|
|||||||
return [best, best_eval]
|
return [best, best_eval]
|
||||||
|
|
||||||
|
|
||||||
# define the total iterations
|
if __name__ == "__main__":
|
||||||
n_iter = 100
|
|
||||||
# bits
|
# define the total iterations
|
||||||
n_bits = 20
|
n_iter = 100
|
||||||
# define the population size
|
# bits
|
||||||
n_pop = 100
|
n_bits = 20
|
||||||
# crossover rate
|
# define the population size
|
||||||
r_cross = 0.9
|
n_pop = 100
|
||||||
# mutation rate
|
# crossover rate
|
||||||
r_mut = 0.05
|
r_cross = 0.9
|
||||||
# perform the genetic algorithm search
|
# mutation rate
|
||||||
best, score = genetic_algorithm(sum_distance, n_iter, n_pop, r_cross, r_mut)
|
r_mut = 0.05
|
||||||
print('Done!')
|
|
||||||
print('f(%s) = %f' % (best, score))
|
# create new minefield instance
|
||||||
|
import os
|
||||||
|
from minefield import Minefield
|
||||||
|
minefield = Minefield(os.path.join("..", "..", "..", "resources", "minefields", "fourthmap.json"))
|
||||||
|
|
||||||
|
# perform the genetic algorithm search
|
||||||
|
best, score = genetic_algorithm(minefield, helpers.get_score, n_iter, n_pop, r_cross, r_mut)
|
||||||
|
print('Done!')
|
||||||
|
print('f(%s) = %f' % (best, score))
|
||||||
|
|
||||||
|
|
48
algorithms/learn/genetic_algorithm/helpers.py
Normal file
48
algorithms/learn/genetic_algorithm/helpers.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
from minefield import Minefield
|
||||||
|
import project_constants as const
|
||||||
|
|
||||||
|
from project_constants import Direction
|
||||||
|
from algorithms.search.a_star import State, graphsearch
|
||||||
|
from objects.mine_models.chained_mine import ChainedMine
|
||||||
|
|
||||||
|
|
||||||
|
def get_score(minefield, speciment):
|
||||||
|
initial_state = State(row=0, column=0, direction=Direction.RIGHT)
|
||||||
|
score = 0
|
||||||
|
|
||||||
|
for el_index in range(len(speciment) - 1):
|
||||||
|
action_sequence, initial_state, cost = \
|
||||||
|
graphsearch(initial_state,
|
||||||
|
minefield,
|
||||||
|
target_type="mine",
|
||||||
|
tox=speciment[el_index][0],
|
||||||
|
toy=speciment[el_index][1],
|
||||||
|
with_data=True)
|
||||||
|
|
||||||
|
mine = minefield.matrix[speciment[el_index][0]][speciment[el_index][1]].mine
|
||||||
|
|
||||||
|
if isinstance(mine, ChainedMine) and mine.predecessor is not None and mine.predecessor.active:
|
||||||
|
cost += 10000
|
||||||
|
|
||||||
|
mine.active = False
|
||||||
|
|
||||||
|
for _ in range(cost):
|
||||||
|
minefield.next_turn()
|
||||||
|
|
||||||
|
score += cost
|
||||||
|
|
||||||
|
score += 10000 * minefield.explosions
|
||||||
|
return score
|
||||||
|
|
||||||
|
|
||||||
|
def get_mines_coords(minefield: Minefield):
|
||||||
|
mines = list()
|
||||||
|
|
||||||
|
for row in range(const.V_GRID_VER_TILES):
|
||||||
|
for column in range(const.V_GRID_VER_TILES):
|
||||||
|
mine = minefield.matrix[row][column].mine
|
||||||
|
|
||||||
|
if mine is not None:
|
||||||
|
mines.append([row, column])
|
||||||
|
|
||||||
|
return mines
|
@ -34,10 +34,6 @@ def get_node_cost(node: Node, minefield: Minefield):
|
|||||||
if node.action != Action.GO:
|
if node.action != Action.GO:
|
||||||
return node.parent.cost + 1
|
return node.parent.cost + 1
|
||||||
|
|
||||||
# if Tile considered its mine in cost calculation, this code would be priettier
|
|
||||||
if minefield.matrix[row][column].mine is not None:
|
|
||||||
return node.parent.cost + 500
|
|
||||||
else:
|
|
||||||
return node.parent.cost + minefield.matrix[row][column].cost.value
|
return node.parent.cost + minefield.matrix[row][column].cost.value
|
||||||
|
|
||||||
|
|
||||||
@ -95,7 +91,8 @@ def graphsearch(initial_state: State,
|
|||||||
explored: List[Node] = None,
|
explored: List[Node] = None,
|
||||||
target_type: str = "tile",
|
target_type: str = "tile",
|
||||||
tox: int = None,
|
tox: int = None,
|
||||||
toy: int = None):
|
toy: int = None,
|
||||||
|
with_data=False):
|
||||||
|
|
||||||
# reset global priority queue helpers
|
# reset global priority queue helpers
|
||||||
global entry_finder
|
global entry_finder
|
||||||
@ -158,6 +155,10 @@ def graphsearch(initial_state: State,
|
|||||||
parent = parent.parent
|
parent = parent.parent
|
||||||
|
|
||||||
actions_sequence.reverse()
|
actions_sequence.reverse()
|
||||||
|
|
||||||
|
if with_data:
|
||||||
|
return actions_sequence, element.state, element.cost
|
||||||
|
|
||||||
return actions_sequence
|
return actions_sequence
|
||||||
|
|
||||||
# add current node to explored (prevents infinite cycles)
|
# add current node to explored (prevents infinite cycles)
|
||||||
|
@ -58,17 +58,21 @@ def blit_graphics(minefield):
|
|||||||
pass # darken_tile(tile.position)
|
pass # darken_tile(tile.position)
|
||||||
|
|
||||||
# draw a mine on top if there is one
|
# draw a mine on top if there is one
|
||||||
if tile.mine is not None:
|
if tile.mine is not None and tile.mine.active:
|
||||||
if isinstance(tile.mine, StandardMine):
|
if isinstance(tile.mine, StandardMine):
|
||||||
const.SCREEN.blit(mine_asset_options["MINE"], tile_screen_coords)
|
const.SCREEN.blit(mine_asset_options["MINE"], tile_screen_coords)
|
||||||
|
|
||||||
if isinstance(tile.mine, ChainedMine):
|
if isinstance(tile.mine, ChainedMine):
|
||||||
predecessor = tile.mine.predecessor
|
predecessor = tile.mine.predecessor
|
||||||
predecessor_position = predecessor.position if predecessor is not None else None
|
predecessor_position = \
|
||||||
|
predecessor.position if predecessor is not None and predecessor.active else None
|
||||||
|
|
||||||
display_chained_mine(tile.position, predecessor_position)
|
display_chained_mine(tile.position, predecessor_position)
|
||||||
|
|
||||||
elif isinstance(tile.mine, TimeMine):
|
elif isinstance(tile.mine, TimeMine):
|
||||||
display_time_mine(tile.position, "0" + str(tile.mine.timer))
|
minutes = int(tile.mine.timer / 60)
|
||||||
|
seconds = tile.mine.timer % 60
|
||||||
|
display_time_mine(tile.position, minutes, "0" + str(seconds))
|
||||||
|
|
||||||
# changed sapper's movement from jumping to continuous movement (moved everything to Agent's class)
|
# changed sapper's movement from jumping to continuous movement (moved everything to Agent's class)
|
||||||
# # sapper
|
# # sapper
|
||||||
@ -234,7 +238,7 @@ def display_chained_mine(coords, parent_coords=None):
|
|||||||
display_number(const.Coords.Y, parent_coords[1])
|
display_number(const.Coords.Y, parent_coords[1])
|
||||||
|
|
||||||
|
|
||||||
def display_time_mine(coords, time):
|
def display_time_mine(coords, minutes, seconds):
|
||||||
|
|
||||||
def display_number(which_digit, number):
|
def display_number(which_digit, number):
|
||||||
|
|
||||||
@ -264,6 +268,8 @@ def display_time_mine(coords, time):
|
|||||||
number_coords = (mine_coords[0] + 44, mine_coords[1] + 22)
|
number_coords = (mine_coords[0] + 44, mine_coords[1] + 22)
|
||||||
elif which_digit == const.Digit.TENS:
|
elif which_digit == const.Digit.TENS:
|
||||||
number_coords = (mine_coords[0] + 36, mine_coords[1] + 22)
|
number_coords = (mine_coords[0] + 36, mine_coords[1] + 22)
|
||||||
|
elif which_digit == const.Digit.MINUTES:
|
||||||
|
number_coords = (mine_coords[0] + 18, mine_coords[1] + 22)
|
||||||
|
|
||||||
const.SCREEN.blit(
|
const.SCREEN.blit(
|
||||||
number_asset,
|
number_asset,
|
||||||
@ -275,5 +281,6 @@ def display_time_mine(coords, time):
|
|||||||
calculate_screen_position(coords)
|
calculate_screen_position(coords)
|
||||||
)
|
)
|
||||||
|
|
||||||
display_number(const.Digit.ONES, int(str(time)[-1]))
|
display_number(const.Digit.ONES, int(str(seconds)[-1]))
|
||||||
display_number(const.Digit.TENS, int(str(time)[-2]))
|
display_number(const.Digit.TENS, int(str(seconds)[-2]))
|
||||||
|
display_number(const.Digit.MINUTES, minutes)
|
||||||
|
@ -532,7 +532,7 @@ class SampleWindow:
|
|||||||
|
|
||||||
def run(self, mine: Mine):
|
def run(self, mine: Mine):
|
||||||
timed_event = pygame.USEREVENT + 1
|
timed_event = pygame.USEREVENT + 1
|
||||||
pygame.time.set_timer(timed_event, 6000)
|
pygame.time.set_timer(timed_event, 1000)
|
||||||
|
|
||||||
step = 0
|
step = 0
|
||||||
handler = DisarmingHandler(mine)
|
handler = DisarmingHandler(mine)
|
||||||
|
34
game.py
34
game.py
@ -5,6 +5,8 @@ import project_constants as const
|
|||||||
from assets.display_assets import blit_graphics
|
from assets.display_assets import blit_graphics
|
||||||
from algorithms.search import a_star
|
from algorithms.search import a_star
|
||||||
|
|
||||||
|
from algorithms.learn.genetic_algorithm import genetic_algorithm, helpers
|
||||||
|
|
||||||
from minefield import Minefield
|
from minefield import Minefield
|
||||||
|
|
||||||
from objects.mine_models.time_mine import TimeMine
|
from objects.mine_models.time_mine import TimeMine
|
||||||
@ -35,6 +37,8 @@ class Game:
|
|||||||
self.action_timer = 0
|
self.action_timer = 0
|
||||||
self.action_delta_time = 0
|
self.action_delta_time = 0
|
||||||
|
|
||||||
|
self.genetic_sequence = None
|
||||||
|
|
||||||
# declaring and initializing gui components
|
# declaring and initializing gui components
|
||||||
# ui_component managers
|
# ui_component managers
|
||||||
self.in_game_gui_components_manager = UiComponentsManager()
|
self.in_game_gui_components_manager = UiComponentsManager()
|
||||||
@ -109,6 +113,9 @@ class Game:
|
|||||||
# resetting timer
|
# resetting timer
|
||||||
self.millisecond_timer %= const.TURN_INTERVAL
|
self.millisecond_timer %= const.TURN_INTERVAL
|
||||||
|
|
||||||
|
def revert_minefield_turn(self):
|
||||||
|
self.minefield.turn -= 1
|
||||||
|
|
||||||
# returns turn number
|
# returns turn number
|
||||||
def get_turn_number(self):
|
def get_turn_number(self):
|
||||||
return self.turn
|
return self.turn
|
||||||
@ -131,6 +138,33 @@ class Game:
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def run_genetics(self):
|
||||||
|
genetics_minefield = Minefield(const.MAP_RANDOM_10x10)
|
||||||
|
|
||||||
|
sequence, score = \
|
||||||
|
genetic_algorithm.genetic_algorithm(genetics_minefield, helpers.get_score, 10, 100, 0.9, 0.05)
|
||||||
|
|
||||||
|
print('Done!')
|
||||||
|
print('f(%s) = %f' % (sequence, score))
|
||||||
|
self.genetic_sequence = sequence
|
||||||
|
|
||||||
|
def set_next_genetic_target(self):
|
||||||
|
if any(self.genetic_sequence):
|
||||||
|
self.goal = self.genetic_sequence.pop(0)
|
||||||
|
|
||||||
|
# display new destination
|
||||||
|
self.input_box_row.set_texts(user_input=str(self.goal[0]))
|
||||||
|
self.input_box_column.set_texts(user_input=str(self.goal[1]))
|
||||||
|
|
||||||
|
# prevents highlighting input_box_row,
|
||||||
|
# couldn't find any better solution w/o major Game class changes
|
||||||
|
self.input_box_row.set_is_selected(False)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
# gets action sequence for agent
|
# gets action sequence for agent
|
||||||
def get_action_sequence(self, target_type: str = "tile"):
|
def get_action_sequence(self, target_type: str = "tile"):
|
||||||
return a_star.graphsearch(
|
return a_star.graphsearch(
|
||||||
|
27
main.py
27
main.py
@ -25,6 +25,8 @@ def main():
|
|||||||
running = True
|
running = True
|
||||||
in_menu = True
|
in_menu = True
|
||||||
auto = False
|
auto = False
|
||||||
|
genetics = False
|
||||||
|
genetics_ready = False
|
||||||
is_game_over = False
|
is_game_over = False
|
||||||
|
|
||||||
# create and initialize_gui_components game instance
|
# create and initialize_gui_components game instance
|
||||||
@ -61,6 +63,12 @@ def main():
|
|||||||
# if auto button is clicked then leave menu and start auto-mode
|
# if auto button is clicked then leave menu and start auto-mode
|
||||||
auto = game.button_auto.is_clicked(pygame.mouse.get_pos(), events)
|
auto = game.button_auto.is_clicked(pygame.mouse.get_pos(), events)
|
||||||
|
|
||||||
|
# if genetics button is clocked then start genetic algorithm
|
||||||
|
genetics = game.button_genetic_algorithm.is_clicked(pygame.mouse.get_pos(), events)
|
||||||
|
|
||||||
|
if genetics:
|
||||||
|
auto = True
|
||||||
|
|
||||||
# ========================== #
|
# ========================== #
|
||||||
# ==== BEFORE GAME LOOP ==== #
|
# ==== BEFORE GAME LOOP ==== #
|
||||||
# ========================== #
|
# ========================== #
|
||||||
@ -68,9 +76,19 @@ def main():
|
|||||||
# initializing action_sequence variable
|
# initializing action_sequence variable
|
||||||
action_sequence = None
|
action_sequence = None
|
||||||
|
|
||||||
|
if genetics and not genetics_ready:
|
||||||
|
game.run_genetics()
|
||||||
|
genetics_ready = True
|
||||||
|
|
||||||
# getting action sequence for agent
|
# getting action sequence for agent
|
||||||
if auto and running:
|
if auto and running:
|
||||||
in_menu = False
|
in_menu = False
|
||||||
|
|
||||||
|
if genetics:
|
||||||
|
auto = game.set_next_genetic_target()
|
||||||
|
action_sequence = game.get_action_sequence("mine")
|
||||||
|
|
||||||
|
else:
|
||||||
auto = game.set_random_mine_as_target()
|
auto = game.set_random_mine_as_target()
|
||||||
action_sequence = game.get_action_sequence("mine")
|
action_sequence = game.get_action_sequence("mine")
|
||||||
|
|
||||||
@ -80,6 +98,9 @@ def main():
|
|||||||
# initializing game attributes before the game loop
|
# initializing game attributes before the game loop
|
||||||
game.initialize_before_game_loop()
|
game.initialize_before_game_loop()
|
||||||
|
|
||||||
|
# prevent incrementing minefield turn before first action
|
||||||
|
first_turn_flag = True
|
||||||
|
|
||||||
# =================== #
|
# =================== #
|
||||||
# ==== GAME LOOP ==== #
|
# ==== GAME LOOP ==== #
|
||||||
# =================== #
|
# =================== #
|
||||||
@ -115,6 +136,10 @@ def main():
|
|||||||
game.update_time(time)
|
game.update_time(time)
|
||||||
game.update_turns()
|
game.update_turns()
|
||||||
|
|
||||||
|
if first_turn_flag:
|
||||||
|
game.revert_minefield_turn()
|
||||||
|
first_turn_flag = False
|
||||||
|
|
||||||
# make the next move from sequence of actions
|
# make the next move from sequence of actions
|
||||||
if game.agent_should_take_next_action(action_sequence):
|
if game.agent_should_take_next_action(action_sequence):
|
||||||
# give agent next action
|
# give agent next action
|
||||||
@ -132,7 +157,7 @@ def main():
|
|||||||
if auto:
|
if auto:
|
||||||
if not game.agent.defuse_a_mine(game.get_mine(game.goal)):
|
if not game.agent.defuse_a_mine(game.get_mine(game.goal)):
|
||||||
print("BOOOOOOM\n\n")
|
print("BOOOOOOM\n\n")
|
||||||
is_game_over = True
|
# is_game_over = True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print("guess you will live a little longer...\n\n")
|
print("guess you will live a little longer...\n\n")
|
||||||
|
12
minefield.py
12
minefield.py
@ -7,8 +7,9 @@ import json_generator as jg
|
|||||||
class Minefield:
|
class Minefield:
|
||||||
def __init__(self, json_path):
|
def __init__(self, json_path):
|
||||||
self.turn = 0
|
self.turn = 0
|
||||||
|
self.explosions = 0
|
||||||
|
|
||||||
self.agent = ag.Agent(const.MAP_RANDOM_10x10)
|
self.agent = ag.Agent(json_path)
|
||||||
self.json_path = json_path
|
self.json_path = json_path
|
||||||
|
|
||||||
# open JSON with minefield info
|
# open JSON with minefield info
|
||||||
@ -32,7 +33,7 @@ class Minefield:
|
|||||||
successor_row, successor_column = successor_position
|
successor_row, successor_column = successor_position
|
||||||
predecessor_row, predecessor_column = predecessor_position
|
predecessor_row, predecessor_column = predecessor_position
|
||||||
|
|
||||||
predecessor = self.matrix[predecessor_row][predecessor_column]
|
predecessor = self.matrix[predecessor_row][predecessor_column].mine
|
||||||
self.matrix[successor_row][successor_column].mine.predecessor = predecessor
|
self.matrix[successor_row][successor_column].mine.predecessor = predecessor
|
||||||
|
|
||||||
def next_turn(self):
|
def next_turn(self):
|
||||||
@ -43,7 +44,12 @@ class Minefield:
|
|||||||
mine = self.matrix[row][column].mine
|
mine = self.matrix[row][column].mine
|
||||||
|
|
||||||
if mine is not None and isinstance(mine, TimeMine):
|
if mine is not None and isinstance(mine, TimeMine):
|
||||||
mine.timer = max(0, mine.starting_time - int(self.turn / 4))
|
mine.timer = max(0, mine.starting_time - int(self.turn))
|
||||||
|
|
||||||
|
if mine.timer == 0 and mine.active:
|
||||||
|
# TODO: BOOM
|
||||||
|
self.explosions += 1
|
||||||
|
mine.active = False
|
||||||
|
|
||||||
def get_active_mines(self):
|
def get_active_mines(self):
|
||||||
mines = list()
|
mines = list()
|
||||||
|
@ -16,6 +16,7 @@ class ChainedMine(Mine):
|
|||||||
return super().disarm(wire)
|
return super().disarm(wire)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
self.active = False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def investigate(self):
|
def investigate(self):
|
||||||
|
@ -25,6 +25,7 @@ class Mine(ABC):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
self.active = False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
@ -10,7 +10,11 @@ class TimeMine(Mine):
|
|||||||
super().__init__(TypeHash.TIME, position, active)
|
super().__init__(TypeHash.TIME, position, active)
|
||||||
|
|
||||||
def disarm(self, wire):
|
def disarm(self, wire):
|
||||||
|
if self.active:
|
||||||
return super().disarm(wire)
|
return super().disarm(wire)
|
||||||
|
else:
|
||||||
|
# mine has already exploded, no need to return failure
|
||||||
|
return True
|
||||||
|
|
||||||
def investigate(self):
|
def investigate(self):
|
||||||
return super().investigate()
|
return super().investigate()
|
||||||
|
@ -5,9 +5,8 @@ from project_constants import Terrain
|
|||||||
# on what it is returns value of Terrein Enum
|
# on what it is returns value of Terrein Enum
|
||||||
# It is used in Tile.cost (giving the value to the tile)
|
# It is used in Tile.cost (giving the value to the tile)
|
||||||
|
|
||||||
|
|
||||||
def assume_cost(terrain_type, mine):
|
def assume_cost(terrain_type, mine):
|
||||||
if mine is not None:
|
if mine is not None and mine.active:
|
||||||
return Terrain.MINE
|
return Terrain.MINE
|
||||||
if terrain_type == "CONCRETE":
|
if terrain_type == "CONCRETE":
|
||||||
return Terrain.CONCRETE
|
return Terrain.CONCRETE
|
||||||
@ -21,6 +20,16 @@ class Tile:
|
|||||||
def __init__(self, position, terrain_type=None, mine=None):
|
def __init__(self, position, terrain_type=None, mine=None):
|
||||||
self.position = position
|
self.position = position
|
||||||
self.terrain_type = terrain_type
|
self.terrain_type = terrain_type
|
||||||
self.cost = assume_cost(terrain_type, mine)
|
self._cost = assume_cost(terrain_type, mine)
|
||||||
# mine is an instance of Mine class
|
# mine is an instance of Mine class
|
||||||
self.mine = mine
|
self.mine = mine
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cost(self):
|
||||||
|
self._cost = assume_cost(self.terrain_type, self.mine)
|
||||||
|
return self._cost
|
||||||
|
|
||||||
|
@cost.setter
|
||||||
|
def cost(self, new_value):
|
||||||
|
self._cost = new_value
|
||||||
|
|
||||||
|
@ -24,7 +24,8 @@ V_FPS = 60
|
|||||||
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
DIR_ASSETS = os.path.join(ROOT_DIR, "resources", "assets")
|
DIR_ASSETS = os.path.join(ROOT_DIR, "resources", "assets")
|
||||||
|
|
||||||
TURN_INTERVAL = 0.3 # interval between two turns in seconds
|
# If below 1, in-game second flies faster than the real one
|
||||||
|
TURN_INTERVAL = 0.5 # interval between two turns in seconds
|
||||||
|
|
||||||
V_TILE_SIZE = 60
|
V_TILE_SIZE = 60
|
||||||
V_GRID_VER_TILES = 10 # vertical (number of rows)
|
V_GRID_VER_TILES = 10 # vertical (number of rows)
|
||||||
@ -72,6 +73,7 @@ class Action(Enum):
|
|||||||
class Digit(Enum):
|
class Digit(Enum):
|
||||||
ONES = 0
|
ONES = 0
|
||||||
TENS = 1
|
TENS = 1
|
||||||
|
MINUTES = 2
|
||||||
|
|
||||||
|
|
||||||
class Coords(Enum):
|
class Coords(Enum):
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 503 B |
@ -48,7 +48,7 @@
|
|||||||
"terrain": "MUD",
|
"terrain": "MUD",
|
||||||
"mine": {
|
"mine": {
|
||||||
"mine_type": "time",
|
"mine_type": "time",
|
||||||
"timer": 20
|
"timer": 320
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"1,0": {
|
"1,0": {
|
||||||
@ -154,7 +154,7 @@
|
|||||||
"terrain": "GRASS",
|
"terrain": "GRASS",
|
||||||
"mine": {
|
"mine": {
|
||||||
"mine_type": "time",
|
"mine_type": "time",
|
||||||
"timer": 19
|
"timer": 40
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"3,2": {
|
"3,2": {
|
||||||
@ -363,7 +363,7 @@
|
|||||||
"terrain": "MUD",
|
"terrain": "MUD",
|
||||||
"mine": {
|
"mine": {
|
||||||
"mine_type": "time",
|
"mine_type": "time",
|
||||||
"timer": 39
|
"timer": 240
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"7,7": {
|
"7,7": {
|
||||||
@ -386,7 +386,7 @@
|
|||||||
"terrain": "MUD",
|
"terrain": "MUD",
|
||||||
"mine": {
|
"mine": {
|
||||||
"mine_type": "time",
|
"mine_type": "time",
|
||||||
"timer": 24
|
"timer": 150
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"8,2": {
|
"8,2": {
|
||||||
|
Loading…
Reference in New Issue
Block a user