Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Master controller #14

Merged
merged 12 commits into from
Apr 15, 2023
Prev Previous commit
Next Next commit
changes stuff :)
JuliaCaesar committed Apr 1, 2023
commit 3b964313df0335f330f244b3a24c2e3cacefdf64
2 changes: 1 addition & 1 deletion game/common/map/game_board.py
Original file line number Diff line number Diff line change
@@ -98,7 +98,7 @@ def __init__(self, seed: int | None = None, map_size: Vector = Vector(),
self.walled: bool = walled

# game_map is initially going to be None. Since generation is slow, call generate_map() as needed
self.game_map: list[list[GameObject]] | None = None
self.game_map: list[list[Tile]] | None = None

@property
def seed(self) -> int:
31 changes: 16 additions & 15 deletions game/common/player.py
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@


class Player(GameObject):
def __init__(self, code: object | None = None, team_name: str | None = None, action: ActionType | None = None,
def __init__(self, code: object | None = None, team_name: str | None = None, actions: list[ActionType] | list = [],
JuliaCaesar marked this conversation as resolved.
Show resolved Hide resolved
avatar: Avatar | None = None):
super().__init__()
self.object_type: ObjectType = ObjectType.PLAYER
@@ -14,21 +14,23 @@ def __init__(self, code: object | None = None, team_name: str | None = None, act
self.team_name: str | None = team_name
self.code: object = code
# self.action: Action = action
self.action: ActionType | None = action
self.actions: list[ActionType] | list = actions
self.avatar: Avatar | None = avatar

@property
def action(self) -> ActionType | None: # change to Action if you want to use the action object
return self.__action
def actions(self) -> list[ActionType] | list: # change to Action if you want to use the action object
return self.__actions

@action.setter
def action(self, action: ActionType | None) -> None: # showing it returns nothing(like void in java)
@actions.setter
def actions(self, actions: list[ActionType] | list) -> None: # showing it returns nothing(like void in java)
# if it's (not none = and) if its (none = or)
if action is not None and not isinstance(action, ActionType): # since it can be either none or action type we need it to be and not or
# ^change to Action if you want to use the action object
raise ValueError(f'{self.__class__.__name__}.action must be ActionType or None')
# going across all action types and making it a boolean, if any are true this will be true\/
if actions is None or not isinstance(actions, list) \
or (len(actions) > 0
and any(map(lambda action_type: not isinstance(action_type, ActionType), actions))):
raise ValueError(f'{self.__class__.__name__}.action must be an empty list or a list of action types')
# ^if it's not either throw an error
self.__action = action
self.__actions = actions

@property
def functional(self) -> bool:
@@ -76,22 +78,21 @@ def to_json(self):
data['functional'] = self.functional
# data['error'] = self.error # .to_json() if self.error is not None else None
data['team_name'] = self.team_name
data['action'] = self.actiontype.to_json() if self.actiontype is not None else None
data['actions'] = self.actions
data['avatar'] = self.avatar.to_json() if self.avatar is not None else None

return data

def from_json(self, data):
super().from_json(data)

self.functional = data['functional']
# self.error = data['error'] # .from_json(data['action']) if data['action'] is not None else None
self.team_name = data['team_name']

action: ActionType | None = data['action']
self.actions: list[ActionType] | list = data['actions']
avatar: Avatar | None = data['avatar']
if action is None and avatar is None:
self.action = None
if avatar is None:
self.avatar = None
return self
# match case for action
6 changes: 4 additions & 2 deletions game/controllers/controller.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from game.common.enums import DebugLevel
from game.common.enums import DebugLevel, ActionType
from game.config import Debug
from game.common.player import Player
from game.common.map.game_board import GameBoard


class Controller:
@@ -8,7 +10,7 @@ def __init__(self):
self.debug_level = DebugLevel.controller
self.debug = False

def handle_actions(self, client, world):
def handle_actions(self, action: ActionType, client: Player, world: GameBoard):
return

def print(self, *args):
4 changes: 2 additions & 2 deletions game/controllers/interact_controller.py
Original file line number Diff line number Diff line change
@@ -12,10 +12,10 @@ class InteractController(Controller):
def __init__(self):
super().__init__()

def handle_actions(self, client: Player, world: GameBoard):
def handle_actions(self, action: ActionType, client: Player, world: GameBoard):
# match interaction type with x and y
vector: Vector
match client.action:
match action:
case ActionType.INTERACT_UP:
vector = Vector(x=0, y=-1)
case ActionType.INTERACT_DOWN:
61 changes: 33 additions & 28 deletions game/controllers/master_controller.py
Original file line number Diff line number Diff line change
@@ -5,31 +5,30 @@
from game.common.avatar import Avatar
from game.common.enums import *
from game.common.player import Player
# from game.common.stats import GameStats - don't need it because it is specifically for 1.7
# but keeping it for now in case it breaks something
import game.config as config
import game.config as config # this is for turns
from game.utils.thread import CommunicationThread
from game.controllers.movement_controller import MovementController
from game.controllers.controller import Controller
from game.controllers.interact_controller import InteractController
from game.common.map.game_board import GameBoard


class MasterController(Controller):
def __init__(self):
super().__init__()
self.game_over: bool = False
# self.event_timer = GameStats.event_timer
self.event_times: tuple[int, int] | None = None
# self.event_timer = GameStats.event_timer # anything related to events are commented it out until made
# self.event_times: tuple[int, int] | None = None
self.turn: int = 1
self.current_world_data: GameBoard | None = None
self.current_world_data: dict = None
self.movement_controller: MovementController = MovementController()
self.interact_controller: InteractController = InteractController()

# Receives all clients for the purpose of giving them the objects they will control
def give_clients_objects(self, clients: list(Player), world: GameBoard):
# starting_positions = [[3, 3], [3, 9]]
def give_clients_objects(self, clients: list[Player], world: dict):
# starting_positions = [[3, 3], [3, 9]] # would be done in generate game
for index, client in enumerate(clients):
client.avatar = Avatar(position=game_board[index])
client.avatar: Avatar = Avatar(position=world[index])

# Generator function. Given a key:value pair where the key is the identifier for the current world and the value is
# the state of the world, returns the key that will give the appropriate world information
@@ -44,51 +43,57 @@ def game_loop_logic(self, start=1):
self.turn += 1

# Receives world data from the generated game log and is responsible for interpreting it
def interpret_current_turn_data(self, clients, world, turn):
def interpret_current_turn_data(self, clients: list[Player], world: dict, turn):
self.current_world_data = world
if turn == 1:
random.seed(world["seed"])
self.event_times = random.randrange(162, 172), random.randrange(329, 339)
random.seed(world["game_board"].seed)
# self.event_times = random.randrange(162, 172), random.randrange(329, 339)

# Receive a specific client and send them what they get per turn. Also obfuscates necessary objects.
def client_turn_arguments(self, client, turn):
turn_action = Action()
client.action = turn_action
def client_turn_arguments(self, client: Player, turn):
# turn_action: Action = Action()
# client.action: Action = turn_action
# ^if you want to use action as an object instead of an enum

turn_actions: list[ActionType] | list = []
client.actions = turn_actions

# Create deep copies of all objects sent to the player
current_world = deepcopy(self.current_world_data["game_map"])
current_world = deepcopy(self.current_world_data["game_board"]) # what is current world and copy avatar
copy_avatar = deepcopy(client.avatar)
# Obfuscate data in objects that that player should not be able to see
# Currently world data isn't obfuscated at all
args = (self.turn, turn_action, self.current_world_data)
args = (self.turn, turn_actions, current_world, copy_avatar)
return args

# Perform the main logic that happens per turn
def turn_logic(self, clients, turn):
def turn_logic(self, clients: list[Player], turn):
for client in clients:
self.movement_controller.handle_actions(self.current_world_data["game_map"], client)
self.interact_controller.handle_actions(client, self.current_world_data["game_map"])
for action in client.actions:
self.movement_controller.handle_actions(action, client, self.current_world_data["game_board"])
self.interact_controller.handle_actions(action, client, self.current_world_data["game_board"])
# checks event logic at the end of round
self.handle_events(clients)
# self.handle_events(clients)

def handle_events(self, clients):
# comment out for now, nothing is in place for event types yet
# def handle_events(self, clients):
# If it is time to run an event, master controller picks an event to run
if self.turn == self.event_times[0] or self.turn == self.event_times[1]:
self.current_world_data["game_map"].generate_event(EventType.example, EventType.example)
# event type.example is just a placeholder for now
# if self.turn == self.event_times[0] or self.turn == self.event_times[1]:
# self.current_world_data["game_map"].generate_event(EventType.example, EventType.example)
# event type.example is just a placeholder for now

# Return serialized version of game
def create_turn_log(self, clients, turn):
def create_turn_log(self, clients: list[Player], turn):
data = dict()
data['tick'] = turn
data['clients'] = [client.to_json() for client in clients]
# Add things that should be thrown into the turn logs here
data['game_map'] = self.current_world_data["game_map"].to_json()
data['game_board'] = self.current_world_data["game_board"].to_json()

return data

# Gather necessary data together in results file
def return_final_results(self, clients, turn):
def return_final_results(self, clients: list[Player], turn):
JuliaCaesar marked this conversation as resolved.
Show resolved Hide resolved
data = dict()

data['players'] = list()
4 changes: 2 additions & 2 deletions game/controllers/movement_controller.py
Original file line number Diff line number Diff line change
@@ -11,11 +11,11 @@ class MovementController(Controller):
def __init__(self):
super().__init__()

def handle_actions(self, client: Player, world: GameBoard):
def handle_actions(self, action: ActionType, client: Player, world: GameBoard):
avatar_pos: Vector = Vector(client.avatar.position.x, client.avatar.position.y)

pos_mod: Vector
match client.action:
match action:
case ActionType.MOVE_UP:
pos_mod = Vector(x=0, y=-1)
case ActionType.MOVE_DOWN:
6 changes: 5 additions & 1 deletion game/engine.py
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
import sys
import traceback

from game.common.map.game_board import GameBoard
from game.common.player import Player
from game.config import *
from game.controllers.master_controller import MasterController
@@ -167,8 +168,10 @@ def load(self):
world = None
with open(GAME_MAP_FILE) as json_file:
world = json.load(json_file)
world['game_board'] = GameBoard().from_json(world['game_board'])
self.world = world


# Sits on top of all actions that need to happen before the player takes their turn
def pre_tick(self):
# Increment the tick
@@ -260,7 +263,8 @@ def post_tick(self):
else:
data = self.master_controller.create_turn_log(self.clients, self.tick_number)

self.game_logs[self.tick_number] = data
with open(os.path.join(LOGS_DIR, f"turn_{self.tick_number:04d}.json"), 'w+') as f:
json.dump(data, f)

# Perform a game over check
if self.master_controller.game_over: