Skip to content

Commit

Permalink
Merge pull request #47 from bcollazo/fix/bugs
Browse files Browse the repository at this point in the history
FIX: Bugs
  • Loading branch information
bcollazo committed Oct 31, 2020
2 parents 9170521 + eb462c2 commit e15a43b
Show file tree
Hide file tree
Showing 6 changed files with 415 additions and 329 deletions.
29 changes: 29 additions & 0 deletions catanatron/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from catanatron.models.map import Port, Water
from catanatron.models.board import Board, Graph
from catanatron.models.player import Player, Color
from catanatron.models.enums import DevelopmentCard


def longest_road(board: Board, players: Iterable[Player], actions: Iterable[Action]):
Expand Down Expand Up @@ -78,3 +79,31 @@ def longest_acyclic_path(subgraph: Graph):
paths.extend(paths_from_this_node)

return max(paths, key=len)


def largest_army(players: Iterable[Player], actions: Iterable[Action]):
num_knights_to_players = defaultdict(set)
for player in players:
num_knight_played = player.played_development_cards.count(
DevelopmentCard.KNIGHT
)
num_knights_to_players[num_knight_played].add(player.color)

max_count = max(num_knights_to_players.keys())
if max_count < 3:
return (None, None)

candidates = num_knights_to_players[max_count]
knight_actions = list(
filter(
lambda a: a.action_type == ActionType.PLAY_KNIGHT_CARD
and a.player.color in candidates,
actions,
)
)
while len(candidates) > 1:
action = knight_actions.pop()
if action.player.color in candidates:
candidates.remove(action.player.color)

return candidates.pop(), max_count
8 changes: 4 additions & 4 deletions catanatron/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ def execute(self, action, action_callback=None):
resource = player_to_steal_from.resource_deck.random_draw()
action.player.resource_deck.replenish(1, resource)
action.player.development_deck.draw(1, DevelopmentCard.KNIGHT)
action.player.mark_played_dev_card()
action.player.mark_played_dev_card(DevelopmentCard.KNIGHT)
elif action.action_type == ActionType.PLAY_YEAR_OF_PLENTY:
cards_selected = action.value # Assuming action.value is a resource deck
if not action.player.can_play_year_of_plenty():
Expand All @@ -318,7 +318,7 @@ def execute(self, action, action_callback=None):
action.player.resource_deck += cards_selected
action.player.development_deck.draw(1, DevelopmentCard.YEAR_OF_PLENTY)
self.resource_deck -= cards_selected
action.player.mark_played_dev_card()
action.player.mark_played_dev_card(DevelopmentCard.YEAR_OF_PLENTY)
elif action.action_type == ActionType.PLAY_MONOPOLY:
card_type_to_steal = action.value
cards_stolen = ResourceDeck()
Expand All @@ -335,15 +335,15 @@ def execute(self, action, action_callback=None):
)
action.player.resource_deck += cards_stolen
action.player.development_deck.draw(1, DevelopmentCard.MONOPOLY)
action.player.mark_played_dev_card()
action.player.mark_played_dev_card(DevelopmentCard.MONOPOLY)
elif action.action_type == ActionType.PLAY_ROAD_BUILDING:
if not action.player.can_play_road_building():
raise ValueError("Player cant play road building now")
first_edge, second_edge = action.value
self.board.build_road(action.player.color, first_edge)
self.board.build_road(action.player.color, second_edge)
action.player.development_deck.draw(1, DevelopmentCard.ROAD_BUILDING)
action.player.mark_played_dev_card()
action.player.mark_played_dev_card(DevelopmentCard.ROAD_BUILDING)
elif action.action_type == ActionType.MARITIME_TRADE:
trade_offer = action.value
offering = ResourceDeck.from_array(trade_offer.offering)
Expand Down
5 changes: 4 additions & 1 deletion catanatron/models/player.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import random
from enum import Enum
from collections import defaultdict

from catanatron.models.decks import ResourceDeck, DevelopmentDeck
from catanatron.models.enums import DevelopmentCard
Expand All @@ -20,15 +21,17 @@ def __init__(self, color, name=None):
self.actual_victory_points = 0
self.resource_deck = ResourceDeck()
self.development_deck = DevelopmentDeck()
self.played_development_cards = DevelopmentDeck()

self.clean_turn_state()

def clean_turn_state(self):
self.playable_development_cards = self.development_deck.to_array()
self.has_rolled = False

def mark_played_dev_card(self):
def mark_played_dev_card(self, card_type):
self.playable_development_cards = []
self.played_development_cards.replenish(1, card_type)

def decide(self, game, playable_actions):
"""Should return one of the playable_actions.
Expand Down
158 changes: 158 additions & 0 deletions tests/models/test_board_initializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
from catanatron.algorithms import longest_road
from catanatron.models.board import Board
from catanatron.models.board_initializer import EdgeRef, NodeRef
from catanatron.models.player import Color

# ===== Buildable nodes
def test_buildable_nodes():
board = Board()
nodes = board.buildable_nodes(Color.RED)
assert len(nodes) == 0
nodes = board.buildable_nodes(Color.RED, initial_build_phase=True)
assert len(nodes) == 54


def test_placing_settlement_removes_four_buildable_nodes():
board = Board()
board.build_settlement(
Color.RED, board.nodes[((0, 0, 0), NodeRef.SOUTH)], initial_build_phase=True
)
nodes = board.buildable_nodes(Color.RED)
assert len(nodes) == 0
nodes = board.buildable_nodes(Color.RED, initial_build_phase=True)
assert len(nodes) == 50
nodes = board.buildable_nodes(Color.BLUE, initial_build_phase=True)
assert len(nodes) == 50


def test_buildable_nodes_respects_distance_two():
board = Board()
board.build_settlement(
Color.RED, board.nodes[((0, 0, 0), NodeRef.SOUTH)], initial_build_phase=True
)

board.build_road(Color.RED, board.edges[((0, 0, 0), EdgeRef.SOUTHWEST)])
nodes = board.buildable_nodes(Color.RED)
assert len(nodes) == 0

board.build_road(Color.RED, board.edges[((0, 0, 0), EdgeRef.WEST)])
nodes = board.buildable_nodes(Color.RED)
assert len(nodes) == 1
assert nodes.pop() == board.nodes[((0, 0, 0), NodeRef.NORTHWEST)]


def test_cant_use_enemy_roads_to_connect():
board = Board()
board.build_settlement(
Color.RED, board.nodes[((0, 0, 0), NodeRef.SOUTH)], initial_build_phase=True
)
board.build_road(Color.RED, board.edges[((0, 0, 0), EdgeRef.SOUTHEAST)])

board.build_settlement(
Color.BLUE,
board.nodes[((0, 0, 0), NodeRef.NORTHEAST)],
initial_build_phase=True,
)
board.build_road(Color.BLUE, board.edges[((0, 0, 0), EdgeRef.EAST)])
board.build_road(Color.BLUE, board.edges[((0, 0, 0), EdgeRef.NORTHEAST)])
board.build_road(Color.BLUE, board.edges[((1, 0, -1), EdgeRef.WEST)])

nodes = board.buildable_nodes(Color.RED)
assert len(nodes) == 0

nodes = board.buildable_nodes(Color.BLUE)
assert len(nodes) == 1


# ===== Buildable edges
def test_buildable_edges_simple():
board = Board()
board.build_settlement(
Color.RED, board.nodes[((0, 0, 0), NodeRef.SOUTH)], initial_build_phase=True
)
buildable = board.buildable_edges(Color.RED)
assert len(buildable) == 3


def test_buildable_edges():
board = Board()
board.build_settlement(
Color.RED, board.nodes[((0, 0, 0), NodeRef.SOUTH)], initial_build_phase=True
)
board.build_road(Color.RED, board.edges[((0, 0, 0), EdgeRef.SOUTHWEST)])
buildable = board.buildable_edges(Color.RED)
assert len(buildable) == 4


def test_water_edge_is_not_buildable():
board = Board()
board.build_settlement(
Color.RED, board.nodes[((0, 2, -2), NodeRef.NORTH)], initial_build_phase=True
)
buildable = board.buildable_edges(Color.RED)
assert len(buildable) == 2


# ===== Find connected components
def test_connected_components_empty_board():
board = Board()
components = board.find_connected_components(Color.RED)
assert len(components) == 0


def test_one_connected_component():
board = Board()
board.build_settlement(
Color.RED, board.nodes[((0, 0, 0), NodeRef.SOUTH)], initial_build_phase=True
)
board.build_road(Color.RED, board.edges[((0, 0, 0), EdgeRef.SOUTHEAST)])
board.build_settlement(
Color.RED, board.nodes[((0, 0, 0), NodeRef.NORTHEAST)], initial_build_phase=True
)
board.build_road(Color.RED, board.edges[((0, 0, 0), EdgeRef.EAST)])
components = board.find_connected_components(Color.RED)
assert len(components) == 1

board.build_road(Color.RED, board.edges[((0, 0, 0), EdgeRef.NORTHEAST)])
components = board.find_connected_components(Color.RED)
assert len(components) == 1


def test_two_connected_components():
board = Board()
board.build_settlement(
Color.RED, board.nodes[((0, 0, 0), NodeRef.SOUTH)], initial_build_phase=True
)
board.build_road(Color.RED, board.edges[((0, 0, 0), EdgeRef.SOUTHWEST)])
components = board.find_connected_components(Color.RED)
assert len(components) == 1

board.build_settlement(
Color.RED, board.nodes[((0, 0, 0), NodeRef.NORTHEAST)], initial_build_phase=True
)
board.build_road(Color.RED, board.edges[((0, 0, 0), EdgeRef.NORTHEAST)])
components = board.find_connected_components(Color.RED)
assert len(components) == 2


def test_three_connected_components_bc_enemy_cut_road():
board = Board()
board.build_settlement(
Color.RED, board.nodes[((0, 0, 0), NodeRef.SOUTH)], initial_build_phase=True
)
board.build_road(Color.RED, board.edges[((0, 0, 0), EdgeRef.SOUTHWEST)])

board.build_settlement(
Color.RED, board.nodes[((0, 0, 0), NodeRef.NORTHEAST)], initial_build_phase=True
)
board.build_road(Color.RED, board.edges[((0, 0, 0), EdgeRef.NORTHEAST)])
board.build_road(Color.RED, board.edges[((0, 0, 0), EdgeRef.NORTHWEST)])
board.build_road(Color.RED, board.edges[((-1, 1, 0), EdgeRef.NORTHEAST)])

board.build_settlement(
Color.BLUE,
board.nodes[((0, 0, 0), NodeRef.NORTHWEST)],
initial_build_phase=True,
)
components = board.find_connected_components(Color.RED)
assert len(components) == 3
Loading

0 comments on commit e15a43b

Please sign in to comment.