Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

Commit

Permalink
feat: add DropAction (#1656)
Browse files Browse the repository at this point in the history
* feat: add DropAction

* delete unused file

* update test pip packages

* add tests

* revert back Pipfile and lock

* revert pytest.ini

* add and update log
  • Loading branch information
dionizh committed May 17, 2022
1 parent d79a354 commit ede244b
Show file tree
Hide file tree
Showing 11 changed files with 325 additions and 354 deletions.
8 changes: 8 additions & 0 deletions aimmo-game-worker/simulation/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ def serialise(self):
return {"action_type": "pickup"}


class DropAction(Action):
def __init__(self, index):
self.index = index

def serialise(self):
return {"action_type": "drop", "options": {"index": self.index}}


class MoveAction(Action):
def __init__(self, direction):
self.direction = direction
Expand Down
19 changes: 0 additions & 19 deletions aimmo-game-worker/simulation/event.py

This file was deleted.

2 changes: 1 addition & 1 deletion aimmo-game-worker/simulation/world_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# how many nearby artefacts to return
SCAN_LIMIT = 3
SCAN_RADIUS = 12
ARTEFACT_TYPES = ["chest", "key", "yellow_orb", "phone", "keyboard"]
ARTEFACT_TYPES = ["chest", "key", "yellow_orb", "phone", "keyboard", "coins"]
PICKUP_TYPES = ["damage_boost", "invulnerability", "health"] + ARTEFACT_TYPES


Expand Down
2 changes: 1 addition & 1 deletion aimmo-game/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "pypi"
pytest = "~=6.2"
pytest-asyncio = "==0.14.0"
pytest-pythonpath = "*"
black = "==20.8b1"
black = "*"

[packages]
aimmo-game = {editable = true,path = "."}
Expand Down
503 changes: 231 additions & 272 deletions aimmo-game/Pipfile.lock

Large diffs are not rendered by default.

46 changes: 27 additions & 19 deletions aimmo-game/simulation/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

from simulation.direction import Direction
from simulation.event import (
DroppedEvent,
FailedAttackEvent,
FailedDropEvent,
FailedMoveEvent,
FailedPickupEvent,
MovedEvent,
Expand Down Expand Up @@ -60,9 +62,6 @@ def _reject(self):


class WaitAction(Action):
def __init__(self, avatar):
super(WaitAction, self).__init__(avatar)

def _is_legal(self, world_map):
return True

Expand All @@ -71,9 +70,6 @@ def _apply(self, world_map):


class PickupAction(Action):
def __init__(self, avatar):
super(PickupAction, self).__init__(avatar)

def _is_legal(self, world_map):
current_cell = world_map.get_cell(self.avatar.location)
cell_has_artefact = issubclass(type(current_cell.interactable), _Artefact)
Expand All @@ -88,9 +84,28 @@ def _apply(self, world_map):
def _reject(self):
self.avatar.add_event(FailedPickupEvent())
self.avatar.clear_action()
self.avatar.logs.append(
"Uh oh! Your avatar was unable to pick up the artefact. Your backpack is full! 🎒 "
)
self.avatar.logs.append("Uh oh! Your avatar was unable to pick up the artefact. Your backpack is full! 🎒 ")


class DropAction(Action):
def __init__(self, avatar, index):
super(DropAction, self).__init__(avatar)
self.index = index

def _is_legal(self, world_map):
return self.index < len(self.avatar.backpack)

def _apply(self, world_map):
artefact_type = str(self.avatar.backpack[self.index])
del self.avatar.backpack[self.index]
self.avatar.add_event(DroppedEvent(self.index))
self.avatar.clear_action()
self.avatar.logs.append(f"{artefact_type.capitalize()} dropped!")

def _reject(self):
self.avatar.add_event(FailedDropEvent())
self.avatar.clear_action()
self.avatar.logs.append("Uh oh! Your avatar was unable to drop the artefact at that position!")


class MoveAction(Action):
Expand Down Expand Up @@ -151,9 +166,7 @@ def _is_legal(self, world_map):
def _apply(self, world_map):
attacked_avatar = world_map.attackable_avatar(self.target_location)
damage_dealt = 1
self.avatar.add_event(
PerformedAttackEvent(attacked_avatar, self.target_location, damage_dealt)
)
self.avatar.add_event(PerformedAttackEvent(attacked_avatar, self.target_location, damage_dealt))
attacked_avatar.add_event(ReceivedAttackEvent(self.avatar, damage_dealt))
attacked_avatar.damage(damage_dealt)

Expand All @@ -171,11 +184,6 @@ def _reject(self):
self.avatar.clear_action()


ACTIONS = {
"attack": AttackAction,
"move": MoveAction,
"wait": WaitAction,
"pickup": PickupAction,
}
ACTIONS = {"attack": AttackAction, "move": MoveAction, "wait": WaitAction, "pickup": PickupAction, "drop": DropAction}

PRIORITIES = {WaitAction: 0, PickupAction: 0, AttackAction: 1, MoveAction: 2}
PRIORITIES = {WaitAction: 0, PickupAction: 0, DropAction: 0, AttackAction: 1, MoveAction: 2}
12 changes: 6 additions & 6 deletions aimmo-game/simulation/event.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
from collections import namedtuple

ReceivedAttackEvent = namedtuple(
"ReceivedAttackEvent", ["attacking_avatar", "damage_dealt"]
)
ReceivedAttackEvent = namedtuple("ReceivedAttackEvent", ["attacking_avatar", "damage_dealt"])

PerformedAttackEvent = namedtuple(
"PerformedAttackEvent", ["attacked_avatar", "target_location", "damage_dealt"]
)
PerformedAttackEvent = namedtuple("PerformedAttackEvent", ["attacked_avatar", "target_location", "damage_dealt"])

FailedAttackEvent = namedtuple("FailedAttackEvent", ["target_location"])

Expand All @@ -17,3 +13,7 @@
PickedUpEvent = namedtuple("PickedUpEvent", ["interactable"])

FailedPickupEvent = namedtuple("FailedPickupEvent", [])

DroppedEvent = namedtuple("DroppedEvent", ["index"])

FailedDropEvent = namedtuple("FailedDropEvent", [])
3 changes: 3 additions & 0 deletions aimmo-game/simulation/interactables/pickups/artefacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ def __init__(self, cell):
def get_targets(self):
return [self.cell.avatar]

def __str__(self):
return self._type

def __repr__(self):
return f"{type(self).__name__}(Location={self.cell.location})"

Expand Down
33 changes: 22 additions & 11 deletions aimmo-game/tests/test_interactables/test_artefacts.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import pytest

from simulation.action import PickupAction
from simulation.action import PickupAction, DropAction
from simulation.interactables.pickups import (
ChestArtefact,
KeyArtefact,
YellowOrbArtefact,
PhoneArtefact,
KeyboardArtefact,
CoinsArtefact,
)
from simulation.location import Location
from tests.test_simulation.dummy_avatar import CustomLiveDummy
Expand All @@ -15,6 +18,9 @@
(ChestArtefact, "chest"),
(KeyArtefact, "key"),
(YellowOrbArtefact, "yellow_orb"),
(PhoneArtefact, "phone"),
(KeyboardArtefact, "keyboard"),
(CoinsArtefact, "coins"),
]


Expand Down Expand Up @@ -50,20 +56,12 @@ async def test_artefact_applies_correctly(game, cell, artefact_class, artefact_t
cell.interactable = artefact

# Move to the cell with the artefact

await game.simulation_runner.run_single_turn(
game.turn_collector.collected_turn_actions
)

await game.simulation_runner.run_single_turn(game.turn_collector.collected_turn_actions)
assert cell.interactable is not None

# Pickup the artefact

avatar.set_next_action(PickupAction(avatar))

await game.simulation_runner.run_single_turn(
game.turn_collector.collected_turn_actions
)
await game.simulation_runner.run_single_turn(game.turn_collector.collected_turn_actions)

assert cell.avatar == avatar
assert cell.interactable is None
Expand All @@ -75,3 +73,16 @@ async def test_artefact_applies_correctly(game, cell, artefact_class, artefact_t
def test_artefact_repr(cell, artefact_class, artefact_type):
artefact = artefact_class(cell)
assert repr(artefact) == f"{type(artefact).__name__}(Location={cell.location})"


@pytest.mark.asyncio
@pytest.mark.parametrize("artefact_class, artefact_type", testdata)
async def test_drop_artefact(game, cell, artefact_class, artefact_type):
avatar: "CustomLiveDummy" = game.avatar_manager.get_avatar(1)
artefact = artefact_class(cell)
avatar.backpack = [artefact]

# Drop the artefact
avatar.set_next_action(DropAction(avatar, 0))
await game.simulation_runner.run_single_turn(game.turn_collector.collected_turn_actions)
assert len(avatar.backpack) == 0
34 changes: 19 additions & 15 deletions aimmo-game/tests/test_simulation/test_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,8 @@ def test_successful_attack_action(self):
assert self.other_avatar.times_died == 0
assert self.other_avatar.health == 4

assert self.avatar.events == [
event.PerformedAttackEvent(self.other_avatar, target_location, damage_dealt)
]
assert self.other_avatar.events == [
event.ReceivedAttackEvent(self.avatar, damage_dealt)
]
assert self.avatar.events == [event.PerformedAttackEvent(self.other_avatar, target_location, damage_dealt)]
assert self.other_avatar.events == [event.ReceivedAttackEvent(self.avatar, damage_dealt)]

def test_successful_multiple_attack_actions(self):
game_state = GameState(AvatarMap(self.other_avatar), self.avatar_manager)
Expand Down Expand Up @@ -111,12 +107,8 @@ def test_avatar_dies(self):

target_location = NORTH_OF_ORIGIN
damage_dealt = 1
assert self.avatar.events == [
event.PerformedAttackEvent(self.other_avatar, target_location, damage_dealt)
]
assert self.other_avatar.events == [
event.ReceivedAttackEvent(self.avatar, damage_dealt)
]
assert self.avatar.events == [event.PerformedAttackEvent(self.other_avatar, target_location, damage_dealt)]
assert self.other_avatar.events == [event.ReceivedAttackEvent(self.avatar, damage_dealt)]

assert self.avatar.location == ORIGIN
assert self.other_avatar.health == 0
Expand Down Expand Up @@ -152,11 +144,23 @@ def test_failed_pickup_action_if_backpack_full(self):
game_state.world_map.setup_cell(self.avatar.location)
artefact = game_state.world_map.get_cell(self.avatar.location).interactable

self.avatar.backpack = [
YellowOrbArtefact for _ in range(self.avatar.BACKPACK_SIZE)
]
self.avatar.backpack = [YellowOrbArtefact for _ in range(self.avatar.BACKPACK_SIZE)]

action.PickupAction(self.avatar).process(game_state.world_map)

assert self.avatar.events == [event.FailedPickupEvent()]
assert artefact.in_backpack == False

def test_successful_drop_action(self):
game_state = GameState(PickupMap(YellowOrbArtefact), self.avatar_manager)
game_state.world_map.setup_cell(self.avatar.location)
artefact = game_state.world_map.get_cell(self.avatar.location).interactable
self.avatar.backpack = [artefact]

action.DropAction(self.avatar, 0).process(game_state.world_map)
assert self.avatar.events == [event.DroppedEvent(index=0)]

def test_failed_drop_action(self):
game_state = GameState(InfiniteMap(), self.avatar_manager)
action.DropAction(self.avatar, 0).process(game_state.world_map)
assert self.avatar.events == [event.FailedDropEvent()]
17 changes: 7 additions & 10 deletions game_frontend/src/pyodide/webWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import ComputedTurnResult from './computedTurnResult'
let pyodide: Pyodide

function getAvatarStateFromGameState(gameState: any, playerAvatarID: number): object {
return gameState.players.find(player => player.id === playerAvatarID)
return gameState.players.find((player) => player.id === playerAvatarID)
}

async function initializePyodide() {
Expand All @@ -27,7 +27,7 @@ from io import StringIO
from pyodide import to_js
from simulation import direction, location
from simulation.action import MoveAction, PickupAction, WaitAction, MoveTowardsAction
from simulation.action import MoveAction, PickupAction, WaitAction, MoveTowardsAction, DropAction
from simulation.avatar_state import create_avatar_state
from simulation.world_map import WorldMapCreator
Expand Down Expand Up @@ -72,7 +72,7 @@ to_js({"action": serialized_action, "log": logs, "turnCount": game_state["turnCo
return Promise.resolve({
action: { action_type: 'wait' },
log: simplifyErrorMessageInLog(error.toString()),
turnCount: gameState.turnCount + 1
turnCount: gameState.turnCount + 1,
})
}
}
Expand All @@ -86,10 +86,7 @@ export function simplifyErrorMessageInLog(log: string): string {
return `Uh oh! Something isn't correct on line ${matches[1]}. Here's the error we got:\n${simpleError}`
}
// error not in next_turn function
return log
.split('\n')
.slice(-2)
.join('\n')
return log.split('\n').slice(-2).join('\n')
}

export async function updateAvatarCode(
Expand All @@ -110,14 +107,14 @@ export async function updateAvatarCode(
return Promise.resolve({
action: { action_type: 'wait' },
log: '',
turnCount: turnCount
turnCount: turnCount,
})
} catch (error) {
await setAvatarCodeToWaitActionOnError()
return Promise.resolve({
action: { action_type: 'wait' },
log: simplifyErrorMessageInLog(error.toString()),
turnCount: turnCount
turnCount: turnCount,
})
}
}
Expand All @@ -132,7 +129,7 @@ async function setAvatarCodeToWaitActionOnError() {
const pyodideWorker = {
initializePyodide,
computeNextAction,
updateAvatarCode
updateAvatarCode,
}

export type PyodideWorker = typeof pyodideWorker
Expand Down

0 comments on commit ede244b

Please sign in to comment.