PyCatan-AI / pycatan /core /board.py
EZTIME2025
organise the project
69373e6
from .harbor import Harbor, HarborType
from .player import Player
from .statuses import Statuses
from .building import Building
from .tile_type import TileType
from .card import ResCard, DevCard
from .tile import Tile
from .point import Point
# used to shuffle the deck of tiles
import random
import abc
# used for debugging
import pprint
# Base class for different Catan boards
# Should not be instantiated, otherwise the board will be empty
class Board(object):
__metaclass__ = abc.ABCMeta
def __init__(self, game):
# The game the board is in
self.game = game
# The tiles on the board
# Should be set in a subclass
self.tiles = ()
# The points on the board
# Where the players can place settlements/cities
# Will be set at the end of __init__
self.points = ()
# The roads
self.roads = []
# The locations of the harbors
self.harbors = []
# The location of the robber
# going r, i
self.robber = None
# gives the players cards for a certain roll
def add_yield(self, roll):
# Track resources distributed: {player_name: [resource_names]}
distribution = {}
for r in self.points:
for p in r:
# Check there is a building on the point
if p.building != None:
building = p.building
tiles = p.tiles
# checks if any tiles have the right number
for current_tile in tiles:
# makes sure the robber isn't there
if self.robber == current_tile.position:
# skips this tile
continue
if current_tile.token_num == roll:
# adds the card to the player's inventory
owner = building.owner
# gets the card type
card_type = Board.get_card_from_tile(current_tile.type)
if card_type:
cards_to_add = []
# adds two if it is a city
if building.type == Building.BUILDING_CITY:
cards_to_add = [card_type, card_type]
else:
cards_to_add = [card_type]
self.game.players[owner].add_cards(cards_to_add)
# Record distribution
player_name = f"Player {owner + 1}"
if player_name not in distribution:
distribution[player_name] = []
for card in cards_to_add:
distribution[player_name].append(card.name.split('.')[-1] if hasattr(card, 'name') else str(card))
return distribution
# adds a Building object to the board
def add_building(self, building, point):
point.building = building
# adds a Building object, which must be a road
# since roads record their own position and are not in self.points
def add_road(self, road):
self.roads.append(road)
# upgrades an existing settlement to a city
def upgrade_settlement(self, player, point):
# Get building at point
building = point.building
# checks there is a settlement at r, i
if building == None:
return Statuses.ERR_NOT_EXIST
# checks the settlement is controlled by the correct player
# if no player is specified, uses the current controlling player
if building.owner != player:
return Statuses.ERR_BAD_OWNER
# checks it is a settlement and not a city
if building.type != Building.BUILDING_SETTLEMENT:
return Statuses.ERR_UPGRADE_CITY
# checks the player has the cards
needed_cards = [
ResCard.Wheat,
ResCard.Wheat,
ResCard.Ore,
ResCard.Ore,
ResCard.Ore
]
if not self.game.players[player].has_cards(needed_cards):
return Statuses.ERR_CARDS
# removes the cards
self.game.players[player].remove_cards(needed_cards)
# changes the settlement to a city
building.type = Building.BUILDING_CITY
# adds another victory point
self.game.players[player].victory_points += 1
return Statuses.ALL_GOOD
# gets all the buildings on the board
def get_buildings(self):
buildings = []
for r in self.points:
for p in r:
if p.building != None:
buildings.append(p.building)
return buildings
# moves the robber to a givne coord
def move_robber(self, tile_pos):
self.robber = tile_pos
def __repr__(self):
return ("Board Object")
# Get a shuffled deck of the correct number of each type of tile in a board
@staticmethod
def get_shuffled_tile_deck():
deck = []
# sets up all_tiles
for i in range(4):
# adds four fields, forests and pastures
deck.append(TileType.Fields)
deck.append(TileType.Forest)
deck.append(TileType.Pasture)
# adds three mountains and hills
if i < 3:
deck.append(TileType.Mountains)
deck.append(TileType.Hills)
# adds one desert
if i == 0:
deck.append(TileType.Desert)
# shuffles the deck
random.shuffle(deck)
return deck
@staticmethod
def get_shuffled_tile_nums():
nums = []
# Get 2 of each number, most of the time
for i in range(2):
# Go through each type
for x in range(2, 13):
# Does not add a number token with 7
if x != 7:
# Only adds one 2 and one 12
if x == 2 or x == 12:
if i == 0:
nums.append(x)
# Adds two of everything else
else:
nums.append(x)
random.shuffle(nums)
return nums
# returns the card associated with the tile
# for example, Brick for Hills, Wood for forests, etc
@staticmethod
def get_card_from_tile(tile):
# returns the appropriete card
if tile == TileType.Forest:
return ResCard.Wood
elif tile == TileType.Hills:
return ResCard.Brick
elif tile == TileType.Pasture:
return ResCard.Sheep
elif tile == TileType.Fields:
return ResCard.Wheat
elif tile == TileType.Mountains:
return ResCard.Ore
else:
return None