Skip to content

Commit

Permalink
Add the Initiate Situation action
Browse files Browse the repository at this point in the history
Add the Initiate Situation action, which can be performed by the user in it's correct place in the sequence of play. Also add test coverage. The new action doesn't do anything yet. There isn't even an action log for it.
  • Loading branch information
iamlogand committed Feb 2, 2024
1 parent 3ed0bce commit dbb99ed
Show file tree
Hide file tree
Showing 16 changed files with 281 additions and 106 deletions.
1 change: 1 addition & 0 deletions backend/rorapp/functions/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# The functions module provides a collection of public functions that are intended to be used by other parts of the application.
from .faction_leader_helper import select_faction_leader, set_faction_leader # noqa: F401
from .forum_phase_helper import initiate_situation # noqa: F401
from .forum_phase_starter import start_forum_phase # noqa: F401
from .game_deleter import delete_all_games # noqa: F401
from .game_generator import generate_game # noqa: F401
Expand Down
31 changes: 5 additions & 26 deletions backend/rorapp/functions/faction_leader_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import Optional
from rorapp.functions.action_helper import delete_old_actions
from rorapp.functions.forum_phase_helper import (
generate_initiate_situation_action,
get_next_faction_in_forum_phase,
)
from rorapp.functions.mortality_phase_starter import setup_mortality_phase
Expand All @@ -23,7 +24,6 @@
from rorapp.serializers import (
ActionLogSerializer,
ActionSerializer,
StepSerializer,
TitleSerializer,
SenatorActionLogSerializer,
)
Expand Down Expand Up @@ -87,7 +87,9 @@ def set_faction_leader(senator_id: int) -> (Response, dict):
messages_to_send.extend(proceed_to_next_step_if_forum_phase(game.id, step, faction))
messages_to_send.extend(delete_old_actions(game.id))

return Response({"message": "Faction leader selected"}, status=200), messages_to_send
return Response(
{"message": "Faction leader selected"}, status=200
), messages_to_send


def get_previous_title(faction) -> Optional[Title]:
Expand Down Expand Up @@ -182,30 +184,7 @@ def proceed_to_next_step_if_forum_phase(game_id, step, faction) -> [dict]:
next_faction = get_next_faction_in_forum_phase(faction)

if next_faction is not None:
new_step = Step(index=step.index + 1, phase=step.phase)
new_step.save()

messages_to_send.append(
generate_select_faction_leader_action(next_faction, new_step)
)

messages_to_send.append(
create_websocket_message("step", StepSerializer(new_step).data)
)
messages_to_send.extend(generate_initiate_situation_action(next_faction))
else:
messages_to_send.extend(start_next_turn(game_id, step))
return messages_to_send


def generate_select_faction_leader_action(faction: Faction, step: Step) -> dict:
senators = Senator.objects.filter(faction=faction, death_step__isnull=True)
senator_id_list = [senator.id for senator in senators]
action = Action(
step=step,
faction=faction,
type="select_faction_leader",
required=True,
parameters=senator_id_list,
)
action.save()
return create_websocket_message("action", ActionSerializer(action).data)
94 changes: 92 additions & 2 deletions backend/rorapp/functions/forum_phase_helper.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
from typing import Optional
from rorapp.models import Faction
from rest_framework.response import Response
from typing import List, Optional
from rorapp.functions.websocket_message_helper import (
create_websocket_message,
destroy_websocket_message,
)
from rorapp.models import (
Action,
Faction,
Phase,
Senator,
Step,
)
from rorapp.serializers import ActionSerializer, StepSerializer


def get_next_faction_in_forum_phase(
Expand All @@ -23,3 +35,81 @@ def get_next_faction_in_forum_phase(
if next_faction_index >= len(factions):
return None
return factions[next_faction_index]


def generate_select_faction_leader_action(faction: Faction, step: Step) -> dict:
senators = Senator.objects.filter(faction=faction, death_step__isnull=True)
senator_id_list = [senator.id for senator in senators]
action = Action(
step=step,
faction=faction,
type="select_faction_leader",
required=True,
parameters=senator_id_list,
)
action.save()
return create_websocket_message("action", ActionSerializer(action).data)


def generate_initiate_situation_action(faction: Faction) -> List[dict]:
messages_to_send = []

# Create new step
latest_step = Step.objects.filter(phase__turn__game=faction.game.id).order_by(
"-index"
)[0]
# Need to get latest phase because the latest step might not be from the current forum phase
latest_phase = Phase.objects.filter(turn__game=faction.game.id).order_by("-index")[
0
]
new_step = Step(index=latest_step.index + 1, phase=latest_phase)
new_step.save()
messages_to_send.append(
create_websocket_message("step", StepSerializer(new_step).data)
)

# Create new action
action = Action(
step=new_step,
faction=faction,
type="initiate_situation",
required=True,
)
action.save()
messages_to_send.append(
create_websocket_message("action", ActionSerializer(action).data)
)
return messages_to_send


def initiate_situation(action_id: int) -> dict:
"""
Initiate a random situation.
This function is called when a player initiates a situation during the forum phase.
Args:
action_id (int): The action ID.
Returns:
dict: The response with a message and a status code.
"""
messages_to_send = []

# Mark the action as complete
action = Action.objects.get(id=action_id)
action.completed = True
action.save()
messages_to_send.append(destroy_websocket_message("action", action_id))

# Create new step
new_step = Step(index=action.step.index + 1, phase=action.step.phase)
new_step.save()
messages_to_send.append(
create_websocket_message("step", StepSerializer(new_step).data)
)

messages_to_send.append(
generate_select_faction_leader_action(action.faction, new_step)
)
return Response({"message": "Situation initiated"}, status=200), messages_to_send
29 changes: 14 additions & 15 deletions backend/rorapp/functions/forum_phase_starter.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
from typing import List
from rorapp.functions.action_helper import delete_old_actions
from rorapp.functions.faction_leader_helper import generate_select_faction_leader_action
from rorapp.functions.forum_phase_helper import generate_initiate_situation_action
from rorapp.functions.websocket_message_helper import create_websocket_message
from rorapp.models import (
Faction,
Phase,
Step,
)
from rorapp.serializers import (
StepSerializer,
PhaseSerializer,
)
from rorapp.serializers import PhaseSerializer


def start_forum_phase(game_id: int) -> List[dict]:
"""
Start the forum phase.
def start_forum_phase(game_id) -> List[dict]:
Args:
game_id (int): The game ID.
Returns:
List[dict]: The WebSocket messages to send.
"""
messages_to_send = []
latest_step = Step.objects.filter(phase__turn__game=game_id).order_by("-index")[0]

Expand All @@ -25,16 +31,9 @@ def start_forum_phase(game_id) -> List[dict]:
messages_to_send.append(
create_websocket_message("phase", PhaseSerializer(new_phase).data)
)
new_step = Step(index=latest_step.index + 1, phase=new_phase)
new_step.save()
messages_to_send.append(
create_websocket_message("step", StepSerializer(new_step).data)
)

# Create actions
# Create action and new step
first_faction = Faction.objects.filter(game__id=game_id).order_by("rank").first()
messages_to_send.append(
generate_select_faction_leader_action(first_faction, new_step)
)
messages_to_send.extend(generate_initiate_situation_action(first_faction))
messages_to_send.extend(delete_old_actions(game_id))
return messages_to_send
2 changes: 1 addition & 1 deletion backend/rorapp/functions/game_starter.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from django.contrib.auth.models import User
from rest_framework.response import Response
from rest_framework.exceptions import NotFound, PermissionDenied
from rorapp.functions.faction_leader_helper import generate_select_faction_leader_action
from rorapp.functions.forum_phase_helper import generate_select_faction_leader_action
from rorapp.functions.rank_helper import rank_senators_and_factions
from rorapp.functions.websocket_message_helper import (
send_websocket_messages,
Expand Down
54 changes: 37 additions & 17 deletions backend/rorapp/tests/forum_phase_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def test_forum_phase(self) -> None:
for player_count in range(3, 7):
self.do_forum_phase_test(player_count)

def action_processor(self, action: Action) -> dict:
def faction_leader_action_processor(self, action: Action) -> dict:
faction = Faction.objects.filter(player=action.faction.player.id).get(
game=action.faction.game.id
)
Expand All @@ -35,32 +35,48 @@ def action_processor(self, action: Action) -> dict:

def do_forum_phase_test(self, player_count: int) -> None:
random.seed(1)
game_id, faction_ids_with_leadership = self.setup_game_in_forum_phase(player_count)
game_id, faction_ids_with_leadership = self.setup_game_in_forum_phase(
player_count
)
for _ in range(0, player_count):
check_latest_phase(self, game_id, "Forum")
potential_actions = get_and_check_actions(
situation_potential_actions = get_and_check_actions(
self, game_id, False, "initiate_situation", 1
)
submit_actions(
self,
game_id,
situation_potential_actions,
self.faction_leader_action_processor,
)
check_latest_phase(self, game_id, "Forum")
faction_leader_potential_actions = get_and_check_actions(
self, game_id, False, "select_faction_leader", 1
)
self.assertEqual(len(potential_actions), 1)
faction_leader_titles = Title.objects.filter(
name="Faction Leader",
senator__faction=potential_actions[0].faction,
end_step=None
senator__faction=faction_leader_potential_actions[0].faction,
end_step=None,
)

# If the faction already has a leader, then there should be no existing faction leader title.
self.assertEqual(len(faction_leader_titles), 1 if potential_actions[0].faction.id in faction_ids_with_leadership else 0)
self.assertEqual(
len(faction_leader_titles),
1
if faction_leader_potential_actions[0].faction.id
in faction_ids_with_leadership
else 0,
)
submit_actions(
self,
game_id,
potential_actions,
self.action_processor,
faction_leader_potential_actions,
self.faction_leader_action_processor,
)
self.assertEqual(len(potential_actions), 1)
faction_leader_titles = Title.objects.filter(
name="Faction Leader",
senator__faction=potential_actions[0].faction,
end_step=None
senator__faction=faction_leader_potential_actions[0].faction,
end_step=None,
)
self.assertEqual(len(faction_leader_titles), 1)
check_latest_phase(self, game_id, "Mortality")
Expand All @@ -72,20 +88,24 @@ def setup_game_in_forum_phase(self, player_count: int) -> (int, List[int]):
faction_ids_with_leadership = set_some_faction_leaders(game_id)
start_forum_phase(game_id)
return (game_id, faction_ids_with_leadership)



def set_some_faction_leaders(game_id: int) -> List[int]:
"""
Assigns faction leader titles to 2 senators then returns their faction IDs.
"""
factions = Faction.objects.filter(game=game_id)
first_faction = factions.first()
second_faction = factions.last()
senator_in_faction_1 = Senator.objects.filter(game=game_id, faction=first_faction).first()
senator_in_faction_2 = Senator.objects.filter(game=game_id, faction=second_faction).first()
senator_in_faction_1 = Senator.objects.filter(
game=game_id, faction=first_faction
).first()
senator_in_faction_2 = Senator.objects.filter(
game=game_id, faction=second_faction
).first()
set_faction_leader(senator_in_faction_1.id)
set_faction_leader(senator_in_faction_2.id)
return [
senator_in_faction_1.faction.id,
senator_in_faction_2.faction.id,
]

13 changes: 9 additions & 4 deletions backend/rorapp/tests/test_helper.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from typing import List
from django.db.models.query import QuerySet
from django.test import TestCase
from rorapp.models import Action, Phase, Senator, Step
from rorapp.models import Action, Phase, Step, Turn
from django.contrib.auth.models import User
from typing import Callable
from rorapp.functions import set_faction_leader


def check_all_actions(
Expand Down Expand Up @@ -118,8 +117,14 @@ def check_latest_phase(
expected_latest_phase_name: str,
expected_phase_count: int | None = None,
) -> None:
phases = Phase.objects.filter(turn__game=game_id)
"""
Check that the latest phase has the expected name, and matches the latest step and latest turn.
"""
latest_turn = Turn.objects.filter(game=game_id).order_by("-index").first()
phases = Phase.objects.filter(turn=latest_turn).order_by("-index")
if expected_phase_count:
test_case.assertEqual(phases.count(), expected_phase_count)
latest_phase = phases[len(phases) - 1]
latest_phase = phases.first()
latest_step = Step.objects.filter(phase__turn=latest_turn).order_by("-index").first()
test_case.assertEqual(latest_phase.id, latest_step.phase.id)
test_case.assertEqual(latest_phase.name, expected_latest_phase_name)
Loading

0 comments on commit dbb99ed

Please sign in to comment.