Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 51 additions & 27 deletions src/engine/engine.py
Original file line number Diff line number Diff line change
@@ -1,78 +1,102 @@
from src.models.event import (
Event,
EngineEvent,
UpdateBidEvent,
BuyAssetEvent,
EndTurnEvent,
UpdateBidRequest,
BuyAssetRequest,
EndTurn,
UpdateBidResponse,
BuyAssetResponse, NewPhase
)
from src.models.game_state import GameState


class Engine:
@classmethod
def handle_event(cls, game_state: GameState, event: Event) -> tuple[GameState, list[EngineEvent]]:
def handle_event(cls, game_state: GameState, event: Event) -> tuple[GameState, list[Event]]:
"""
Events happen every time a player takes an action or a timer runs out.
Every time an event occurs, the engine is informed and it can then:
-Update the game state
-Send messages back to the player interface if required
-Send messages back to the players OR to itself to trigger a new phase
:param game_state: The current state of the game
:param event: The triggering event
:return: The new game state and a list of events to be sent to the player interface
:return: The new game state and a list of events to be sent
"""
# Handle the event based on its type
if isinstance(event, UpdateBidEvent):
if isinstance(event, NewPhase):
return cls.handle_new_phase_event(game_state, event)
elif isinstance(event, UpdateBidRequest):
return cls.handle_update_bid_event(game_state, event)
elif isinstance(event, BuyAssetEvent):
elif isinstance(event, BuyAssetRequest):
return cls.handle_buy_asset_event(game_state, event)
elif isinstance(event, EndTurnEvent):
elif isinstance(event, EndTurn):
return cls.handle_end_turn_event(game_state, event)
else:
raise NotImplementedError(f"Event type {type(event)} not implemented.")

@classmethod
def handle_new_phase_event(
cls,
game_state: GameState,
event: NewPhase,
) -> tuple[GameState, list[Event]]:
"""
Handle a new phase event.
:param game_state: The current state of the game
:param event: The triggering event
:return: The new game state and a list of events to be sent to the player interface
"""
# TODO Do something depending on what phase we are in
# TODO if we are in the da_auction phase, we need to run the market coupling algorithm
raise NotImplementedError()

@classmethod
def handle_update_bid_event(
cls,
game_state: GameState,
event: Event,
) -> tuple[GameState, list[EngineEvent]]:
cls,
game_state: GameState,
event: UpdateBidRequest,
) -> tuple[GameState, list[UpdateBidResponse]]:
"""
Handle an update bid event.
:param game_state: The current state of the game
:param event: The triggering event
:return: The new game state and a list of events to be sent to the player interface
"""
# TODO Update bids in the game state and then return updated game state and maybe engine events
# TODO Check if the bid is valid (including if the player can afford it).
# TODO Update bids in the game state
# TODO Return one UpdateBidResponseEvent for the player who made the bid
raise NotImplementedError()

@classmethod
def handle_buy_asset_event(
cls,
game_state: GameState,
event: Event,
) -> tuple[GameState, list[EngineEvent]]:
cls,
game_state: GameState,
event: BuyAssetRequest,
) -> tuple[GameState, list[BuyAssetResponse]]:
"""
Handle a buy asset event.
:param game_state: The current state of the game
:param event: The triggering event
:return: The new game state and a list of events to be sent to the player interface
"""
# TODO Update assets ownership in the game state and then return updated game state and maybe engine events
# TODO Check if the request is valid (including if the asset is for sale and the player can afford it).
# TODO Update asset ownership
# TODO Return a BuyAssetResponse
raise NotImplementedError()

@classmethod
def handle_end_turn_event(
cls,
game_state: GameState,
event: Event,
) -> tuple[GameState, list[EngineEvent]]:
cls,
game_state: GameState,
event: EndTurn,
) -> tuple[GameState, list[NewPhase]]:
"""
Handle an end turn event.
:param game_state: The current state of the game
:param event: The triggering event
:return: The new game state and a list of events to be sent to the player interface
"""
# TODO Update the player to indicate that their turn has ended.
# If in the current phase the players need to take turns (asynchronous), then go to the next player
# If all turns are over, then do some special action and go to the next phase?
# TODO Update the player to indicate that their turn has ended
# TODO If this phase requires players to play one by one (Do we need such a phase?) Then cycle to the next player
# TODO Check if all players have ended their turns and we need to move on to the next phase
# TODO If necessary, return an event to signal yourself to go to a different phase
raise NotImplementedError()
6 changes: 3 additions & 3 deletions src/engine/market_coupling.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from src.models.assets import AssetRepo
from src.models.game_state import GameState
from src.models.market_coupling_result import MarketCouplingResult
from src.models.transmission import TransmissionRepo


class MarketCouplingCalculator:
@classmethod
def run(cls, assets: AssetRepo, transmission: TransmissionRepo) -> MarketCouplingResult:
def run(cls, game_state: GameState) -> MarketCouplingResult:
"""
Run the market coupling algorithm.
:param assets: The asset repository
:param transmission: The transmission repository
:param game_state: A copy of the game state
:return: The market coupling result
"""
# TODO Implement the market coupling algorithm
Expand Down
51 changes: 25 additions & 26 deletions src/models/event.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
from abc import ABC
from dataclasses import dataclass
from typing import Self

from src.models.game_state import GameState
from src.models.game_state import GameState, Phase
from src.models.ids import PlayerId, AssetId
from src.tools.serialization import (
SerializableBase,
SimpleDict,
simplify_type,
un_simplify_type,
)


@dataclass(frozen=True)
class Event(SerializableBase, ABC):
class Event(ABC):
pass

@dataclass(frozen=True)
class NewPhase(Event):
phase: Phase

@dataclass(frozen=True)
class PlayerEvent(Event, ABC):
Expand Down Expand Up @@ -43,33 +39,36 @@ def __repr__(self) -> str:


@dataclass(frozen=True)
class GameUpdateEvent(EngineEvent):
# The basic message that gets sent to a player to let them know the game state has changed
def to_simple_dict(self) -> SimpleDict:
return {
"player_id": simplify_type(self.player_id),
"game_state": self.game_state.to_simple_dict(),
}

@classmethod
def from_simple_dict(cls, simple_dict: SimpleDict) -> Self:
return cls(
player_id=un_simplify_type(x=simple_dict["player_id"], t=PlayerId),
game_state=GameState.from_simple_dict(simple_dict["game_state"]),
)
class GameUpdate(EngineEvent):
# The basic message that gets sent to a player to let them know the game state has
pass


@dataclass(frozen=True)
class UpdateBidEvent(PlayerEvent):
class UpdateBidRequest(PlayerEvent):
asset_id: AssetId
bid_price: float


@dataclass(frozen=True)
class BuyAssetEvent(PlayerEvent):
class UpdateBidResponse(EngineEvent):
game_state: GameState
success: bool
asset_id: AssetId


@dataclass(frozen=True)
class BuyAssetRequest(PlayerEvent):
asset_id: AssetId


@dataclass(frozen=True)
class BuyAssetResponse(EngineEvent):
game_state: GameState
success: bool
asset_id: AssetId


@dataclass(frozen=True)
class EndTurnEvent(PlayerEvent):
class EndTurn(PlayerEvent):
asset_id: AssetId