Controlling your Avatar¶
Actions List¶
Your Client.take_turn() method must return a list of ActionType s.
Note that only the FIRST TWO actions will be actually be used by the engine.
See Enums for a list of all possible ActionType s.
Note
You may see additional ActionTypes s in your editor’s autocomplete, but those ActionType s will not do anything.
Walking Around And Touching Stuff¶
To collect coins, batteries, and scrap, all you have to do is walk onto them. No interaction required.
Once you have a direction you want to move or interact, you can use convert_vector_to_move() or convert_vector_to_interact() to get a corresponding ActionType. These functions (and other useful stuff) can be imported from game.constants.
Caution
If a conversion fails, these functions will return None. This might happen if you convert “diagonal” vectors because you can only move and interact straight up/down/left/right:
convert_vector_to_move(Vector(6, 7)) # None
convert_vector_to_interact(Vector(-9, -9)) # None
convert_vector_to_move(Vector(1, 0)) # ActionType.MOVE_RIGHT
convert_vector_to_interact(Vector(0, 0)) # ActionType.INTERACT_CENTER
Additionally, you cannot convert a zero vector to a move because… well… you can figure that out.
You can, however, convert a zero vector to an interact because you can interact with the tile you’re standing on. :^)
Note
Moving “up” is actually moving in a negative y direction. Strange.
Activating Generators¶
If you have enough scrap, you may activate a generator by interacting with it.
Since you must use ActionType s, you must also know which direction to interact:
# x.direction_to(y) returns (y - x).as_direction()
direction_to_generator: Vector = avatar.position.direction_to(generator_position)
interact_action: ActionType | None = convert_vector_to_interact(direction_to_generator)
if interact_action is None:
# the generator is not directly up/down/left/right
else:
actions.append(interact_action)
Caution
Note that the vector conversion may return None.
See the caution message in Walking Around And Touching Stuff for more details.
Pathfinding¶
For your convenience:
import heapq
from typing import Dict, List, Tuple, Optional
from game.common.enums import ActionType, ObjectType
from game.common.game_object import GameObject
from game.common.map.occupiable import Occupiable
from game.constants import DIRECTION_TO_MOVE
from game.utils.vector import Vector
Position = Tuple[int, int]
DIRECTIONS = [(1,0), (-1,0), (0,1), (0,-1)]
def a_star_move(start: Vector, goal: Vector, world, allow_vents: bool = True, game_object: GameObject | None = None) -> ActionType | None:
path = a_star_path(
start=start,
goal=goal,
world=world,
allow_vents=allow_vents,
game_object=game_object
)
if not path or len(path) < 2:
return None
next_step: Vector = path[1]
direction = next_step - start
action = DIRECTION_TO_MOVE.get(direction)
return action
def a_star_path(start: Vector, goal: Vector, world, allow_vents = True, game_object: GameObject | None = None) -> Optional[List[Vector]]:
start_p = (start.x, start.y)
goal_p = (goal.x, goal.y)
frontier = [(0, start_p)]
came_from = {start_p: None}
cost = {start_p: 0}
while frontier:
_, current = heapq.heappop(frontier)
if current == goal_p:
path = []
while current is not None:
x, y = current
path.insert(0, Vector(x, y))
current = came_from[current]
return path
for dx, dy in DIRECTIONS:
nxt = (current[0] + dx, current[1] + dy)
vec = Vector(nxt[0], nxt[1])
if game_object is not None and not world.can_object_occupy(vec, game_object):
continue
if not world.is_valid_coords(vec):
continue
top = world.get_top(vec)
if top and top.object_type != ObjectType.AVATAR:
# walls block
if top.object_type == ObjectType.WALL:
continue
# vents block unless allowed
if top.object_type == ObjectType.VENT and not allow_vents:
continue
# can't pass through non-occupiable
if not isinstance(top, Occupiable):
continue
new_cost = cost[current] + 1
if nxt not in cost or new_cost < cost[nxt]:
cost[nxt] = new_cost
priority = new_cost + vec.distance(goal)
heapq.heappush(frontier, (priority, nxt))
came_from[nxt] = current
return None