Skip to content

Commit

Permalink
fix bugs and add possible_moves method (#45)
Browse files Browse the repository at this point in the history
Revert "Refactor conversion functions and action handling"

This reverts commit 3311035.

add posible moves method

Refactor conversion functions and action handling

Refactor action perform_unchecked methods

Add new methods for performing unchecked actions
  • Loading branch information
maxblan authored Feb 11, 2024
1 parent e751072 commit 644c142
Show file tree
Hide file tree
Showing 15 changed files with 658 additions and 738 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "_socha"
version = "2.1.0"
version = "2.1.1"
edition = "2021"

[lib]
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "maturin"

[project]
name = "socha"
version = "2.1.0"
version = "2.1.1"
authors = [{ name = "maxblan", email = "[email protected]" }]
description = "This is the package for the Software-Challenge Germany 2023. This Season the game will be 'Hey, danke für den Fisch' a.k.a. 'Penguins' in short."
readme = "README.md"
Expand Down
65 changes: 52 additions & 13 deletions python/socha/_socha.pyi
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, List, Optional, Set
from typing import Any, List, Optional, Set, Tuple


class PluginConstants:
Expand Down Expand Up @@ -120,10 +120,7 @@ class Accelerate:
acc: int

def __init__(self, acc: int) -> None: ...
def perform(self, state: Any) -> Ship | BaseException(
AccelerationProblem): ...

def accelerate(self, ship: Any) -> Ship: ...
def perform(self, state: Any) -> Ship | BaseException: ...
def __str__(self) -> str: ...


Expand All @@ -142,8 +139,7 @@ class Advance:
distance: int

def __init__(self, distance: int) -> None: ...
def perform(self, state: GameState) -> Ship | BaseException(
AdvanceProblem): ...
def perform(self, state: GameState) -> Ship | BaseException: ...


class PushProblem:
Expand All @@ -161,8 +157,8 @@ class Push:
direction: CubeDirection

def __init__(self, direction: CubeDirection) -> None: ...
def perform(self, state: GameState) -> (Ship, Ship) | BaseException(
PushProblem): ...
def perform(self, state: GameState) -> Tuple[Ship,
Ship] | BaseException: ...


class TurnProblem():
Expand All @@ -177,8 +173,7 @@ class Turn:
direction: CubeDirection

def __init__(self, direction: CubeDirection) -> None: ...
def perform(self, state: GameState) -> Ship | BaseException(
TurnProblem): ...
def perform(self, state: GameState) -> Ship | BaseException: ...

def coal_cost(self, ship: Ship) -> int: ...

Expand Down Expand Up @@ -267,10 +262,43 @@ class Board:
def pickup_passenger(self, state: GameState) -> GameState: ...

def pickup_passenger_at_position(
self, pos: CubeCoordinates) -> Optional[Field]: ...
self, pos: CubeCoordinates) -> Optional[Field]:
"""
Picks up a passenger at the specified position using the CubeCoordinates.
Args:
pos (CubeCoordinates): The CubeCoordinates representing the position to check.
Returns:
Optional[Field]: The Field containing a passenger with a passenger count greater than 0,
or None if no such Field is found in any adjacent direction.
"""

def find_nearest_field_types(self, start_coordinates: CubeCoordinates,
field_type: FieldType) -> Set[CubeCoordinates]: ...
field_type: FieldType) -> Set[CubeCoordinates]:
"""
A function to find the nearest field(s) of a specific type from a starting point in a hexagonal grid.
Args:
start_coordinates (CubeCoordinates): A CubeCoordinates object representing the starting point for the search.
field_type (FieldType): A FieldType object representing the type of field being searched for.
Returns:
list of CubeCoordinates: A list of CubeCoordinates corresponding to the location of the nearest field(s) of the specified type.
Note:
This function will always return the coordinates of the nearest field(s) of the specified type, if such a field(s) exist.
If multiple fields of the same type are at the same minimum distance, it returns all of them.
If there isn't a field of the specified type or path to it, it will return an empty list.
Examples:
```python
from plugin import Board, CubeCoordinates, FieldType
board = Board()
board.find_nearest_field_types(CubeCoordinates(0, 0), FieldType.Water)
```
"""


class TeamPoints:
Expand Down Expand Up @@ -302,7 +330,11 @@ class GameState:
def ship_advance_points(self, ship: Ship) -> int: ...
def calculate_points(self, ship: Ship) -> int: ...
def is_current_ship_on_current(self) -> bool: ...
def perform_action(self, action: Accelerate | Advance |
Push | Turn) -> GameState: ...

def perform_move(self, move: Move) -> GameState: ...
def perform_move_unchecked(self, move: Move) -> GameState: ...
def advance_turn(self) -> GameState: ...
def effective_speed(self, ship: Ship) -> int: ...
def remove_passenger_at(self, coords: CubeCoordinates) -> bool: ...
Expand All @@ -321,6 +353,13 @@ class GameState:
def possible_advances(self) -> List[Advance]: ...
def sandbank_advances_for(self, ship: Ship) -> Optional[List[Advance]]: ...

def possible_moves(self) -> List[Move]: ...

def possible_action_comb(self, current_state: GameState,
current_actions: List[Accelerate | Advance | Push | Turn],
depth: int,
max_depth: int) -> List[List[Accelerate | Advance | Push | Turn]]: ...

def possible_actions(self, rank: int) -> List[Accelerate |
Advance | Push | Turn]: ...

Expand Down
2 changes: 1 addition & 1 deletion python/socha/api/networking/network_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def receive(self) -> Union[bytes, None]:
If a timeout occurs or a connection reset error is encountered, the socket is closed and None is returned.
"""
regex = re.compile(
br"<((room[\s\S]+?</room>)|errorpacket[\s\S]+?</errorpacket>|prepared[\s\S]+?</prepared>)")
br"<((room[\s\S]+?</room>)|(errorpacket[\s\S]+?</errorpacket>)|(prepared[\s\S]+?</prepared>)|(joined|left|join|observe|pause|step|cancel|creatGame|authenticate)[\s\S]*?/>)")
while True:
try:
chunk = self.socket.recv(16129)
Expand Down
64 changes: 43 additions & 21 deletions python/socha/api/networking/utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from ast import List
import logging
from typing import List

from socha import _socha
from socha._socha import Field, FieldType, Move, TeamEnum, CubeCoordinates, GameState
from socha.api.protocol.protocol import Acceleration, Actions, Advance, Push, Turn, Board, Data, Water, Sandbank, Island, Passenger, Goal
from socha.api.protocol.protocol import Acceleration, Actions, Advance, Push, Ship, Turn, Board, Data, Water, Sandbank, Island, Passenger, Goal


def _convert_board(protocol_board: Board) -> _socha.Board:
Expand Down Expand Up @@ -106,7 +105,18 @@ def _merge_advances(actions):
return new_actions


def if_last_game_state(message, last_game_state):
def if_last_game_state(message, last_game_state) -> GameState:
"""
Constructs a GameState from the provided message, ensuring to reflect the
current state based on the ships' positions, teams, and other attributes.
Args:
message: The input message containing the current game state.
last_game_state: The last game state to be updated.
Returns:
GameState: The constructed game state from the message.
"""
try:
last_game_state.board = _convert_board(
message.data.class_binding.board)
Expand All @@ -119,28 +129,40 @@ def if_last_game_state(message, last_game_state):
last_move = Move(actions=new_actions)
return last_game_state.perform_move(last_move)
except Exception as e:
logging.warning(
f"An error occurred: {e}. Returning the last game state without changes.")
return last_game_state
return if_not_last_game_state(message)


def if_not_last_game_state(message) -> GameState:
first_position = CubeCoordinates(
q=message.data.class_binding.ship[0].position.q, r=message.data.class_binding.ship[0].position.r)
first_team = TeamEnum.One if message.data.class_binding.ship[
0].team == "ONE" else TeamEnum.Two
first_team = _socha.Ship(position=first_position, team=first_team)

second_position = CubeCoordinates(
q=message.data.class_binding.ship[1].position.q, r=message.data.class_binding.ship[1].position.r)
second_team = TeamEnum.One if message.data.class_binding.ship[
1].team == "ONE" else TeamEnum.Two
second_team = _socha.Ship(position=second_position, team=second_team)
"""
Constructs a GameState from the provided message, ensuring to reflect the
current state based on the ships' positions, teams, and other attributes.
Args:
message: The input message containing the current game state.
Returns:
GameState: The constructed game state from the message.
"""
def create_ship(ship_data, team_enum_value) -> _socha.Ship:
"""Helper function to create a ship from the ship data."""
position = CubeCoordinates(
q=ship_data.position.q, r=ship_data.position.r)
team = TeamEnum.One if team_enum_value == "ONE" else TeamEnum.Two
return _socha.Ship(position=position, team=team, coal=ship_data.coal,
passengers=ship_data.passengers, points=ship_data.points,
speed=ship_data.speed, free_turns=ship_data.free_turns,
direction=direction_from_string(ship_data.direction))

first_ship_data, second_ship_data = message.data.class_binding.ship
first_ship = create_ship(first_ship_data, first_ship_data.team)
second_ship = create_ship(second_ship_data, second_ship_data.team)

current_team_enum = TeamEnum.One if message.data.class_binding.current_team == "ONE" else TeamEnum.Two

return GameState(
board=_convert_board(message.data.class_binding.board),
turn=message.data.class_binding.turn,
current_ship=first_team,
other_ship=second_team,
last_move=None,
current_ship=first_ship if current_team_enum == TeamEnum.One else second_ship,
other_ship=second_ship if current_team_enum == TeamEnum.One else first_ship,
last_move=None
)
46 changes: 44 additions & 2 deletions src/plugin/actions.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use pyo3::{IntoPy, PyObject};
use pyo3::{ IntoPy, PyErr, PyObject };
use pyo3::FromPyObject;
use pyo3::Python;

use crate::plugin::actions::push::Push;
use crate::plugin::actions::turn::Turn;

use self::{accelerate::Accelerate, advance::Advance};
use self::{ accelerate::Accelerate, advance::Advance };

use super::game_state::GameState;
use super::ship::Ship;

pub mod accelerate;
pub mod advance;
Expand All @@ -31,3 +34,42 @@ impl IntoPy<PyObject> for Action {
}
}

impl Action {
pub fn perform(
&self,
game_state: &mut GameState
) -> Result<(Option<Ship>, Option<Ship>), PyErr> {
match self {
Action::Accelerate(accelerate) =>
accelerate.perform(game_state).map(|ship| (Some(ship), None)),
Action::Advance(advance) => advance.perform(game_state).map(|ship| (Some(ship), None)),
Action::Push(push) =>
push.perform(game_state).map(|(ship1, ship2)| (Some(ship1), Some(ship2))),
Action::Turn(turn) => turn.perform(game_state).map(|ship| (Some(ship), None)),
}
}
}

impl From<Accelerate> for Action {
fn from(acc: Accelerate) -> Self {
Action::Accelerate(acc)
}
}

impl From<Turn> for Action {
fn from(turn: Turn) -> Self {
Action::Turn(turn)
}
}

impl From<Advance> for Action {
fn from(advance: Advance) -> Self {
Action::Advance(advance)
}
}

impl From<Push> for Action {
fn from(push: Push) -> Self {
Action::Push(push)
}
}
12 changes: 12 additions & 0 deletions src/plugin/actions/accelerate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ impl Accelerate {
}
}
}

fn accelerate(&self, ship: &mut Ship) -> Ship {
debug!("accelerate() called with ship: {:?}", ship);
let used_coal: i32 = self.acc.abs() - ship.free_acc;
Expand All @@ -80,6 +81,17 @@ impl Accelerate {
debug!("Acceleration completed and ship status: {:?}", ship);
return ship.clone();
}

fn accelerate_unchecked(&self, ship: &mut Ship) -> Ship {
debug!("accelerate_unchecked() called with ship: {:?}", ship);
let used_coal: i32 = self.acc.abs() - ship.free_acc;
ship.coal -= used_coal.max(0);
ship.free_acc = (-used_coal).max(0);
ship.accelerate_by(self.acc);
debug!("Unchecked acceleration completed and ship status: {:?}", ship);
ship.clone()
}

fn __repr__(&self) -> PyResult<String> {
debug!("__repr__ method called for Accelerate with acc: {}", self.acc);
Ok(format!("Accelerate({})", self.acc))
Expand Down
Loading

0 comments on commit 644c142

Please sign in to comment.