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
74 changes: 25 additions & 49 deletions socha/api/plugin/penguins/board.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import _pickle as pickle
import logging
import warnings
from itertools import chain, takewhile
from operator import attrgetter
from typing import List, Union, Optional

from socha.api.plugin.penguins.coordinate import HexCoordinate, Vector, CartesianCoordinate
Expand Down Expand Up @@ -110,12 +112,7 @@ def get_empty_fields(self) -> List[Field]:
"""
:return: A list of all empty fields.
"""
fields: List[Field] = []
for row in self.board:
for field in row:
if field.is_empty():
fields.append(field)
return fields
return list(filter(lambda field: field.is_empty(), chain(*self.board)))

def is_occupied(self, coordinates: HexCoordinate) -> bool:
"""
Expand Down Expand Up @@ -179,9 +176,7 @@ def get_field_or_none(self, position: HexCoordinate) -> Union[Field, None]:
:return: The field at the given position, or None if the position is not valid.
"""
cartesian = position.to_cartesian()
if self.is_valid(position):
return self._get_field(cartesian.x, cartesian.y)
return None
return self._get_field(cartesian.x, cartesian.y) if self.is_valid(position) else None

def get_field_by_index(self, index: int) -> Field:
"""
Expand Down Expand Up @@ -227,10 +222,7 @@ def contains(self, field: Field) -> bool:
:param field: The field to check for.
:return: True if the board contains the field, False otherwise.
"""
for row in self.board:
if field in row:
return True
return False
return any(field in row for row in self.board)

def contains_all(self, fields: List[Field]) -> bool:
"""
Expand All @@ -242,37 +234,31 @@ def contains_all(self, fields: List[Field]) -> bool:
if not fields:
return False

for field in fields:
if not self.contains(field):
return False
return True
return all(self.contains(field) for field in fields)

def get_moves_in_direction(self, origin: HexCoordinate, direction: Vector, team_enum: Optional[TeamEnum] = None) \
-> List[Move]:
"""
Gets all moves in the given direction from the given origin.

Args:
origin: The origin of the move.
direction: The direction of the move.
team_enum: Team to make moves for.

Returns:
List[Move]: List of moves that can be made in the given direction from the given index,
for the given team_enum
"""
if team_enum is None:
team_enum = self.get_field(origin).penguin.team_enum
team_enum = team_enum or self.get_field(origin).penguin.team_enum
if not self.get_field(origin).penguin or self.get_field(origin).penguin.team_enum != team_enum:
return []

moves = []
for i in range(1, self.width()):
def valid_destination(i):
destination = origin.add_vector(direction.scalar_product(i))
if self._is_destination_valid(destination):
moves.append(Move(team_enum=team_enum, from_value=origin, to_value=destination))
else:
break
return self._is_destination_valid(destination)

moves = [Move(team_enum=team_enum, from_value=origin, to_value=origin.add_vector(direction.scalar_product(i)))
for i in takewhile(valid_destination, range(1, self.width()))]

return moves

def _is_destination_valid(self, field: HexCoordinate) -> bool:
Expand Down Expand Up @@ -316,26 +302,19 @@ def get_teams_penguins(self, team: TeamEnum) -> List[Penguin]:
:param team: The team_enum to search for.
:return: A list of all coordinates that are occupied by a penguin of the given team_enum.
"""
penguins = []
for row in self.board:
for field in row:
if field.penguin and field.penguin.team_enum == team:
penguins.append(field.penguin)
return penguins
is_team_penguin = lambda field: field.penguin and field.penguin.team_enum == team
penguins = filter(is_team_penguin, (field for row in self.board for field in row))
return list(map(attrgetter('penguin'), penguins))

def get_most_fish(self) -> List[Field]:
"""
Returns a list of all fields with the most fish.

:return: A list of Fields.
"""

fields = list(filter(lambda field_x: not field_x.is_occupied(), self.get_all_fields()))
fields.sort(key=lambda field_x: field_x.get_fish(), reverse=True)
for i, field in enumerate(fields):
if field.get_fish() < fields[0].get_fish():
fields = fields[:i]
return fields
fields = [field for field in self.get_all_fields() if not field.is_occupied()]
max_fish = max(fields, key=lambda field: field.get_fish()).get_fish()
return list(filter(lambda field: field.get_fish() == max_fish, fields))

def get_board_intersection(self, other: 'Board') -> List[Field]:
"""
Expand Down Expand Up @@ -389,16 +368,13 @@ def move(self, move: Move) -> 'Board':
def pretty_print(self):
print()
for i, row in enumerate(self.board):
row_str = ""
if (i + 1) % 2 == 0:
print(" ", end="")
for field in row:
if field.is_empty():
print("~", end=" ")
elif field.is_occupied():
print(field.get_team().value[0], end=" ")
else:
print(field.get_fish(), end=" ")
print()
row_str += " "
row_str += " ".join(["~" if field.is_empty() else field.get_team().value[0] if field.is_occupied() else str(
field.get_fish())
for field in row])
print(row_str)
print()

def __eq__(self, other):
Expand Down
31 changes: 13 additions & 18 deletions socha/api/plugin/penguins/game_state.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import _pickle as pickle
import logging
from dataclasses import dataclass
from typing import List, Optional

from socha.api.plugin.penguins.board import Board
Expand Down Expand Up @@ -47,6 +46,7 @@ def __init__(self, board: Board, turn: int, first_team: Team, second_team: Team,
self.first_team = first_team
self.second_team = second_team
self.last_move = last_move
self.possible_moves = self._get_possible_moves(self.current_team)

@property
def round(self):
Expand All @@ -64,10 +64,6 @@ def other_team(self):
def current_pieces(self):
return self.current_team.get_penguins()

@property
def possible_moves(self):
return self._get_possible_moves(self.current_team)

def _get_possible_moves(self, current_team: Optional[Team]) -> List[Move]:
"""
Gets all possible moves for the current team.
Expand All @@ -77,23 +73,22 @@ def _get_possible_moves(self, current_team: Optional[Team]) -> List[Move]:
:return: A list of all possible moves from the current player's turn.
"""
current_team = current_team or self.current_team
moves = []

if not current_team:
return moves
return []

if len(self.board.get_teams_penguins(current_team.name)) < 4:
for x in range(self.board.width()):
for y in range(self.board.height()):
field = self.board.get_field(CartesianCoordinate(x, y).to_hex())
if not field.is_occupied() and field.get_fish() == 1:
moves.append(
Move(team_enum=current_team.name, from_value=None,
to_value=CartesianCoordinate(x, y).to_hex()))
moves = [(x, y) for x in range(self.board.width()) for y in range(self.board.height())]
moves = filter(lambda pos: not self.board.get_field(
CartesianCoordinate(*pos).to_hex()).is_occupied() and self.board.get_field(
CartesianCoordinate(*pos).to_hex()).get_fish() == 1, moves)
moves = map(lambda pos: Move(team_enum=current_team.name, from_value=None,
to_value=CartesianCoordinate(*pos).to_hex()), moves)
return list(moves)
else:
for piece in self.board.get_teams_penguins(current_team.name):
moves.extend(self.board.possible_moves_from(piece.coordinate, current_team.name))
return moves
pieces = self.board.get_teams_penguins(current_team.name)
moves = map(lambda piece: self.board.possible_moves_from(piece.coordinate, current_team.name), pieces)
moves = [item for sublist in moves for item in sublist]
return moves

def current_team_from_turn(self, turn: int) -> Team:
"""
Expand Down