development #11
@ -1,3 +1,5 @@
|
||||
import random
|
||||
|
||||
import pygame
|
||||
|
||||
from settings import SCREEN_WIDTH, SCREEN_HEIGHT
|
||||
|
@ -1,5 +1,6 @@
|
||||
class PathfindingComponent:
|
||||
def __init__(self, target_pos):
|
||||
def __init__(self, target_pos, searching_for_resource=False):
|
||||
self.target_grid_pos = (int(target_pos[0] / 32), int(target_pos[1] / 32))
|
||||
self.searching_for_resource = False
|
||||
self.current_target = None
|
||||
self.path = None
|
||||
|
@ -1,3 +1,29 @@
|
||||
import random
|
||||
|
||||
from survival.generators.resource_type import ResourceType
|
||||
|
||||
|
||||
class ResourceComponent:
|
||||
def __init__(self, resource_type):
|
||||
self.resource_type = resource_type
|
||||
w, e, t = self.generate_attributes(resource_type)
|
||||
self.weight = w
|
||||
self.eatable = e
|
||||
self.toughness = t
|
||||
|
||||
@staticmethod
|
||||
def generate_attributes(resource_type):
|
||||
if resource_type == ResourceType.WOOD:
|
||||
weight = random.randint(10, 15)
|
||||
eatable = False
|
||||
toughness = random.randint(10, 15)
|
||||
elif resource_type == ResourceType.WATER:
|
||||
weight = random.randint(1, 2)
|
||||
eatable = True
|
||||
toughness = 0
|
||||
else:
|
||||
weight = random.randint(1, 7)
|
||||
eatable = True
|
||||
toughness = random.randint(2, 5)
|
||||
|
||||
return weight, eatable, toughness
|
||||
|
48
survival/decision_tree.py
Normal file
48
survival/decision_tree.py
Normal file
@ -0,0 +1,48 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from sklearn import tree
|
||||
from sklearn.feature_extraction import DictVectorizer
|
||||
|
||||
from survival.components.resource_component import ResourceComponent
|
||||
|
||||
|
||||
class DecisionTree:
|
||||
def __init__(self):
|
||||
self.clf = None
|
||||
self.vec = None
|
||||
|
||||
def build(self, depth: int):
|
||||
path = os.path.join("..", "data.txt")
|
||||
|
||||
samples = list()
|
||||
results = list()
|
||||
|
||||
with open(path, "r") as training_file:
|
||||
for sample in training_file:
|
||||
sample, result = self.process_input(sample)
|
||||
samples.append(sample)
|
||||
results.append(result)
|
||||
|
||||
self.vec = DictVectorizer()
|
||||
self.clf = tree.DecisionTreeClassifier(max_depth=depth)
|
||||
self.clf = self.clf.fit(self.vec.fit_transform(samples).toarray(), results)
|
||||
# print(tree.export_text(self.clf, feature_names=self.vec.get_feature_names()))
|
||||
|
||||
def predict_answer(self, resource: ResourceComponent):
|
||||
params = {
|
||||
"weight": resource.weight,
|
||||
"eatable": resource.eatable,
|
||||
"toughness": resource.toughness
|
||||
}
|
||||
return self.clf.predict(self.vec.transform(params).toarray())
|
||||
|
||||
@staticmethod
|
||||
def process_input(line):
|
||||
data = json.loads(line.strip())
|
||||
result = data['resource']
|
||||
del data['resource']
|
||||
sample = data
|
||||
|
||||
return sample, result
|
||||
|
0
survival/generators/__init__.py
Normal file
0
survival/generators/__init__.py
Normal file
@ -6,6 +6,7 @@ from survival.components.movement_component import MovementComponent
|
||||
from survival.components.position_component import PositionComponent
|
||||
from survival.components.sprite_component import SpriteComponent
|
||||
from survival.components.time_component import TimeComponent
|
||||
from survival.systems.automation_system import AutomationComponent
|
||||
|
||||
|
||||
class PlayerGenerator:
|
||||
@ -20,6 +21,7 @@ class PlayerGenerator:
|
||||
world.add_component(player, InventoryComponent())
|
||||
camera_target = CameraTargetComponent(pos)
|
||||
world.add_component(player, camera_target)
|
||||
# world.add_component(player, AutomationComponent())
|
||||
game_map.add_entity(player, pos)
|
||||
sprite = SpriteComponent('stevenson.png')
|
||||
sprite.set_scale(1)
|
||||
|
@ -1,26 +1,23 @@
|
||||
import random
|
||||
|
||||
from enum import Enum
|
||||
from survival import GameMap
|
||||
from survival.components.OnCollisionComponent import OnCollisionComponent
|
||||
from survival.components.inventory_component import InventoryComponent
|
||||
from survival.components.position_component import PositionComponent
|
||||
from survival.components.resource_component import ResourceComponent
|
||||
from survival.components.sprite_component import SpriteComponent
|
||||
from survival.decision_tree import DecisionTree
|
||||
from survival.esper import World
|
||||
from survival.generators.resource_type import ResourceType
|
||||
from survival.settings import RESOURCES_AMOUNT
|
||||
|
||||
|
||||
class ResourceType(Enum):
|
||||
FOOD = 1
|
||||
WATER = 2
|
||||
WOOD = 3
|
||||
|
||||
|
||||
class ResourceGenerator:
|
||||
def __init__(self, world, game_map):
|
||||
self.world = world
|
||||
self.map = game_map
|
||||
self.decision_tree = DecisionTree()
|
||||
self.built_tree = self.decision_tree.build(10)
|
||||
|
||||
def generate_resources(self, player: int):
|
||||
for x in range(RESOURCES_AMOUNT):
|
||||
@ -37,7 +34,7 @@ class ResourceGenerator:
|
||||
resource_type = random.choice(list(ResourceType))
|
||||
sprite = SpriteComponent(sprites[resource_type])
|
||||
col = OnCollisionComponent()
|
||||
col.addCallback(self.remove_resource, world=self.world, game_map=self.map, entity=obj, player=player)
|
||||
col.addCallback(self.remove_resource, world=self.world, game_map=self.map, resource_ent=obj, player=player, decision_tree=self.decision_tree)
|
||||
self.world.add_component(obj, pos)
|
||||
self.world.add_component(obj, sprite)
|
||||
self.world.add_component(obj, col)
|
||||
@ -51,10 +48,12 @@ class ResourceGenerator:
|
||||
return free_pos
|
||||
|
||||
@staticmethod
|
||||
def remove_resource(world: World, game_map: GameMap, entity: int, player: int):
|
||||
pos = world.component_for_entity(entity, PositionComponent)
|
||||
resource = world.component_for_entity(entity, ResourceComponent)
|
||||
def remove_resource(world: World, game_map: GameMap, resource_ent: int, player: int, decision_tree: DecisionTree):
|
||||
pos = world.component_for_entity(resource_ent, PositionComponent)
|
||||
resource = world.component_for_entity(resource_ent, ResourceComponent)
|
||||
inventory = world.component_for_entity(player, InventoryComponent)
|
||||
answer = decision_tree.predict_answer(resource)
|
||||
print(answer)
|
||||
inventory.add_item(resource.resource_type, 1)
|
||||
game_map.remove_entity(pos.grid_position)
|
||||
world.delete_entity(entity, immediate=True)
|
||||
world.delete_entity(resource_ent, immediate=True)
|
||||
|
7
survival/generators/resource_type.py
Normal file
7
survival/generators/resource_type.py
Normal file
@ -0,0 +1,7 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ResourceType(Enum):
|
||||
FOOD = 1
|
||||
WATER = 2
|
||||
WOOD = 3
|
@ -76,3 +76,6 @@ class TileGenerator:
|
||||
found_biome = TileGenerator.Biomes[0]
|
||||
|
||||
return found_biome
|
||||
|
||||
|
||||
# {"weight": 2, "size": 3, "eatable": true, "resource": "food"}
|
||||
|
@ -1,5 +1,7 @@
|
||||
from survival import esper
|
||||
from survival.systems.automation_system import AutomationSystem
|
||||
from survival.systems.camera_system import CameraSystem
|
||||
from survival.systems.collection_system import ResourceCollectionSystem
|
||||
from survival.systems.collision_system import CollisionSystem
|
||||
from survival.systems.direction_system import DirectionSystem
|
||||
from survival.systems.draw_system import DrawSystem
|
||||
@ -13,13 +15,15 @@ class WorldGenerator:
|
||||
|
||||
def create_world(self, camera, game_map):
|
||||
world = esper.World()
|
||||
world.add_processor(InputSystem(camera))
|
||||
world.add_processor(InputSystem(camera, game_map))
|
||||
world.add_processor(CameraSystem(camera))
|
||||
world.add_processor(MovementSystem(game_map), priority=1)
|
||||
world.add_processor(CollisionSystem(game_map), priority=2)
|
||||
world.add_processor(MovementSystem(game_map), priority=2)
|
||||
world.add_processor(CollisionSystem(game_map), priority=3)
|
||||
world.add_processor(DrawSystem(camera))
|
||||
world.add_processor(ResourceCollectionSystem(), priority=1)
|
||||
world.add_processor(TimeSystem())
|
||||
world.add_processor(PathfindingMovementSystem(game_map), priority=3)
|
||||
world.add_processor(AutomationSystem(game_map))
|
||||
world.add_processor(PathfindingMovementSystem(game_map), priority=4)
|
||||
world.add_processor(DirectionSystem())
|
||||
|
||||
return world
|
||||
|
@ -4,7 +4,9 @@ from typing import Tuple, List
|
||||
|
||||
from survival import GameMap
|
||||
from survival.components.position_component import PositionComponent
|
||||
from survival.components.resource_component import ResourceComponent
|
||||
from survival.enums import Direction
|
||||
from survival.esper import World
|
||||
|
||||
|
||||
class Action(Enum):
|
||||
@ -38,7 +40,7 @@ def get_moved_position(position: Tuple[int, int], direction: Direction):
|
||||
return position[0] + vector[0], position[1] + vector[1]
|
||||
|
||||
|
||||
def get_states(state: State, game_map: GameMap) -> List[Tuple[Action, State, int]]:
|
||||
def get_states(state: State, game_map: GameMap, world: World) -> List[Tuple[Action, State, int]]:
|
||||
states = list()
|
||||
|
||||
states.append((Action.ROTATE_LEFT, State(state.position, state.direction.rotate_left(state.direction)), 1))
|
||||
@ -47,6 +49,10 @@ def get_states(state: State, game_map: GameMap) -> List[Tuple[Action, State, int
|
||||
target_position = get_moved_position(state.position, state.direction)
|
||||
if not game_map.is_colliding(target_position):
|
||||
states.append((Action.MOVE, State(target_position, state.direction), game_map.get_cost(target_position)))
|
||||
elif game_map.get_entity(target_position) is not None:
|
||||
ent = game_map.get_entity(target_position)
|
||||
if world.has_component(ent, ResourceComponent):
|
||||
states.append((Action.MOVE, State(target_position, state.direction), 3))
|
||||
|
||||
return states
|
||||
|
||||
@ -68,7 +74,7 @@ def heuristic(new_node: Node, goal: Tuple[int, int]):
|
||||
return abs(new_node.state.position[0] - goal[0]) + abs(new_node.state.position[1] - goal[1])
|
||||
|
||||
|
||||
def graph_search(game_map: GameMap, start: PositionComponent, goal: tuple):
|
||||
def graph_search(game_map: GameMap, start: PositionComponent, goal: tuple, world: World):
|
||||
fringe = PriorityQueue()
|
||||
explored = list()
|
||||
|
||||
@ -97,7 +103,7 @@ def graph_search(game_map: GameMap, start: PositionComponent, goal: tuple):
|
||||
explored_states.add((tuple(node.state.position), node.state.direction))
|
||||
|
||||
# Get all possible states
|
||||
for state in get_states(node.state, game_map):
|
||||
for state in get_states(node.state, game_map, world):
|
||||
sub_state = (tuple(state[1].position), state[1].direction)
|
||||
new_node = Node(state=state[1],
|
||||
parent=node,
|
||||
|
62
survival/systems/automation_system.py
Normal file
62
survival/systems/automation_system.py
Normal file
@ -0,0 +1,62 @@
|
||||
from survival import esper, GameMap
|
||||
from survival.components.moving_component import MovingComponent
|
||||
from survival.components.pathfinding_component import PathfindingComponent
|
||||
from survival.components.position_component import PositionComponent
|
||||
from survival.components.resource_component import ResourceComponent
|
||||
|
||||
|
||||
class AutomationComponent:
|
||||
pass
|
||||
|
||||
|
||||
class AutomationSystem(esper.Processor):
|
||||
def __init__(self, game_map: GameMap):
|
||||
self.game_map = game_map
|
||||
|
||||
def process(self, dt):
|
||||
for ent, (automation, pos) in self.world.get_components(AutomationComponent, PositionComponent):
|
||||
if self.world.has_component(ent, PathfindingComponent):
|
||||
continue
|
||||
|
||||
resource = self.detect_closest_resource(pos, ent)
|
||||
if resource is None:
|
||||
# TODO: Check if target position is not out of map bounds
|
||||
self.world.add_component(ent, PathfindingComponent((pos.grid_position[0] * 32 + 64, pos.grid_position[1] * 32 + 64)))
|
||||
# Move somewhere else
|
||||
else:
|
||||
target = self.world.component_for_entity(resource, PositionComponent).grid_position
|
||||
self.world.add_component(ent, PathfindingComponent((target[0] * 32, target[1] * 32), True))
|
||||
# Go collect target resource
|
||||
|
||||
def detect_closest_resource(self, position: PositionComponent, target_entity: int):
|
||||
entity_position = position.grid_position
|
||||
x_range = [entity_position[0] - 5, entity_position[0] + 5]
|
||||
y_range = [entity_position[1] - 5, entity_position[1] + 5]
|
||||
|
||||
# Check if range is not out of map bounds
|
||||
if x_range[0] < 0:
|
||||
x_range[0] = 0
|
||||
if x_range[1] >= self.game_map.width:
|
||||
x_range[1] = self.game_map.width - 1
|
||||
if y_range[0] < 0:
|
||||
y_range[0] = 0
|
||||
if y_range[1] >= self.game_map.height:
|
||||
y_range[1] = self.game_map.height - 1
|
||||
|
||||
found_resource = [-1, 200000]
|
||||
|
||||
for y in range(y_range[0], y_range[1]):
|
||||
for x in range(x_range[0], x_range[1]):
|
||||
ent = self.game_map.get_entity([x, y])
|
||||
if ent == target_entity:
|
||||
continue
|
||||
if ent is not None and self.world.has_component(ent, ResourceComponent):
|
||||
res_position = self.world.component_for_entity(ent, PositionComponent).grid_position
|
||||
distance = abs(entity_position[0] - res_position[0]) + abs(entity_position[1] - res_position[1])
|
||||
if found_resource[1] > distance:
|
||||
found_resource = [ent, distance]
|
||||
|
||||
if found_resource[0] == -1:
|
||||
return None
|
||||
else:
|
||||
return found_resource[0]
|
21
survival/systems/collection_system.py
Normal file
21
survival/systems/collection_system.py
Normal file
@ -0,0 +1,21 @@
|
||||
from survival import esper
|
||||
from survival.components.direction_component import DirectionChangeComponent
|
||||
from survival.components.moving_component import MovingComponent
|
||||
from survival.components.position_component import PositionComponent
|
||||
from survival.graph_search import Action
|
||||
from survival.systems.pathfinding_movement_system import CollectingResourceComponent
|
||||
|
||||
|
||||
class ResourceCollectionSystem(esper.Processor):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def process(self, dt):
|
||||
for ent, (collect, pos) in self.world.get_components(CollectingResourceComponent, PositionComponent):
|
||||
if self.world.has_component(ent, MovingComponent) or self.world.has_component(ent, DirectionChangeComponent):
|
||||
continue
|
||||
|
||||
if collect.action == Action.MOVE:
|
||||
self.world.remove_component(ent, CollectingResourceComponent)
|
||||
print("HERE")
|
||||
self.world.add_component(ent, MovingComponent())
|
@ -21,6 +21,7 @@ class CollisionSystem(esper.Processor):
|
||||
|
||||
vector = Direction.get_vector(pos.direction)
|
||||
moving.target = tuple(map(operator.add, vector, pos.grid_position))
|
||||
print(moving.target)
|
||||
moving.direction_vector = vector
|
||||
if self.check_collision(moving.target):
|
||||
self.world.remove_component(ent, MovingComponent)
|
||||
|
@ -1,16 +1,18 @@
|
||||
import pygame
|
||||
|
||||
from survival import esper
|
||||
from survival import esper, GameMap
|
||||
from survival.components.direction_component import DirectionChangeComponent
|
||||
from survival.components.input_component import InputComponent
|
||||
from survival.components.moving_component import MovingComponent
|
||||
from survival.components.pathfinding_component import PathfindingComponent
|
||||
from survival.components.position_component import PositionComponent
|
||||
from survival.components.resource_component import ResourceComponent
|
||||
|
||||
|
||||
class InputSystem(esper.Processor):
|
||||
def __init__(self, camera):
|
||||
def __init__(self, camera, game_map: GameMap):
|
||||
self.camera = camera
|
||||
self.game_map = game_map
|
||||
|
||||
def process(self, dt):
|
||||
for ent, (inp, pos) in self.world.get_components(InputComponent, PositionComponent):
|
||||
@ -20,7 +22,11 @@ class InputSystem(esper.Processor):
|
||||
pos = pygame.mouse.get_pos()
|
||||
pos = (pos[0] - self.camera.camera.left, pos[1] - self.camera.camera.top)
|
||||
if not self.world.has_component(ent, PathfindingComponent):
|
||||
self.world.add_component(ent, PathfindingComponent(pos))
|
||||
target_ent = self.game_map.get_entity([int(pos[0] / 32), int(pos[1]/ 32)])
|
||||
if target_ent is not None and self.world.has_component(target_ent, ResourceComponent):
|
||||
self.world.add_component(ent, PathfindingComponent(pos, True))
|
||||
else:
|
||||
self.world.add_component(ent, PathfindingComponent(pos))
|
||||
|
||||
if self.world.has_component(ent, MovingComponent):
|
||||
continue
|
||||
|
@ -14,8 +14,8 @@ class MovementSystem(esper.Processor):
|
||||
MovingComponent,
|
||||
SpriteComponent):
|
||||
cost = self.map.get_cost(moving.target)
|
||||
pos.position[0] += moving.direction_vector[0] * mov.speed * dt / 100 / cost
|
||||
pos.position[1] += moving.direction_vector[1] * mov.speed * dt / 100 / cost
|
||||
pos.position[0] += moving.direction_vector[0] * mov.speed * dt / 100 / cost
|
||||
pos.position[1] += moving.direction_vector[1] * mov.speed * dt / 100 / cost
|
||||
|
||||
if abs(moving.target[0] * 32 - pos.position[0]) < 0.1 * mov.speed and abs(
|
||||
pos.position[1] - moving.target[1] * 32) < 0.1 * mov.speed:
|
||||
|
@ -8,21 +8,31 @@ from survival.graph_search import graph_search, Action
|
||||
from survival.systems.input_system import PathfindingComponent
|
||||
|
||||
|
||||
class CollectingResourceComponent:
|
||||
def __init__(self, action):
|
||||
self.action = action
|
||||
print(action)
|
||||
|
||||
|
||||
class PathfindingMovementSystem(esper.Processor):
|
||||
def __init__(self, game_map):
|
||||
self.game_map = game_map
|
||||
pass
|
||||
|
||||
def process(self, dt):
|
||||
for ent, (pos, pathfinding, movement) in self.world.get_components(PositionComponent, PathfindingComponent,
|
||||
MovementComponent):
|
||||
if pathfinding.path is None:
|
||||
pathfinding.path = graph_search(self.game_map, pos, pathfinding.target_grid_pos)
|
||||
pathfinding.path = graph_search(self.game_map, pos, pathfinding.target_grid_pos, self.world)
|
||||
|
||||
if len(pathfinding.path) < 1:
|
||||
self.world.remove_component(ent, PathfindingComponent)
|
||||
continue
|
||||
|
||||
if pathfinding.searching_for_resource and len(pathfinding.path) == 1:
|
||||
self.world.add_component(ent, CollectingResourceComponent(pathfinding.path.pop(0)))
|
||||
self.world.remove_component(ent, PathfindingComponent)
|
||||
continue
|
||||
|
||||
if self.world.has_component(ent, MovingComponent) or self.world.has_component(ent, DirectionChangeComponent):
|
||||
continue
|
||||
|
||||
|
@ -2,7 +2,7 @@ import pygame.font
|
||||
|
||||
from survival import settings
|
||||
from survival.components.inventory_component import InventoryComponent
|
||||
from survival.generators.resource_generator import ResourceType
|
||||
from survival.generators.resource_type import ResourceType
|
||||
from survival.image import Image
|
||||
|
||||
|
||||
|
28
test.py
Normal file
28
test.py
Normal file
@ -0,0 +1,28 @@
|
||||
import random
|
||||
|
||||
|
||||
def generate_data():
|
||||
f = open("data.txt", "w")
|
||||
for i in range(1000):
|
||||
weight = random.randint(0, 11)
|
||||
eatable = bool(random.randint(0, 1))
|
||||
toughness = random.randint(0, 3)
|
||||
f.write('{')
|
||||
f.write(
|
||||
f'"weight": {weight}, "eatable": {str(eatable).lower()}, "toughness": {toughness}, "resource": "{get_resource_type(weight, eatable, toughness)}"')
|
||||
f.write('}')
|
||||
f.write('\n')
|
||||
f.close()
|
||||
|
||||
|
||||
def get_resource_type(weight, eatable, toughness):
|
||||
if weight > 10 or eatable is False:
|
||||
return "wood"
|
||||
|
||||
if toughness < 1:
|
||||
return "water"
|
||||
|
||||
return "food"
|
||||
|
||||
|
||||
generate_data()
|
Loading…
Reference in New Issue
Block a user