development #11

Merged
s452701 merged 3 commits from development into master 2021-06-03 15:42:55 +02:00
20 changed files with 1254 additions and 28 deletions
Showing only changes of commit 840eab678b - Show all commits

1000
data.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,5 @@
import random
import pygame
from settings import SCREEN_WIDTH, SCREEN_HEIGHT

View File

@ -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

View File

@ -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
View 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

View File

View 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)

View File

@ -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)

View File

@ -0,0 +1,7 @@
from enum import Enum
class ResourceType(Enum):
FOOD = 1
WATER = 2
WOOD = 3

View File

@ -76,3 +76,6 @@ class TileGenerator:
found_biome = TileGenerator.Biomes[0]
return found_biome
# {"weight": 2, "size": 3, "eatable": true, "resource": "food"}

View File

@ -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

View File

@ -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,

View 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]

View 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())

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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
View 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()