Merge pull request #138 from mbooth101/settlers

Initial version of a Catan game board generator
sammachin-gprs
Dave Arter 2018-09-07 21:05:18 +01:00 committed by GitHub
commit c4f6bc8549
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 187 additions and 0 deletions

187
settlers/main.py Normal file
View File

@ -0,0 +1,187 @@
"""Settlers of Catan game board generator"""
___name___ = "settlers"
___license___ = "MIT"
___dependencies___ = ["ugfx_helper", "sleep"]
___categories___ = ["Games"]
___bootstrapped___ = False
import random, ugfx, ugfx_helper, math, time, buttons
from app import App, restart_to_default
from tilda import Buttons
ugfx_helper.init()
ugfx.clear(ugfx.BLACK)
"""
This was an experiment in drawing hexagons. Some notes:
Screen coords are x,y values that locate pixels on the physical display:
0,0 240,0
0,320 240,320
Hex coords are x,y,z values that locate the relative positions of hexagons:
0,1,-1
-1,1,0 1,0,-1
0,0,0
-1,0,1 1,-1,0
0,-1,1
Converting between the two systems can be done by multiplying the x and y
coordinates against a matrix. When converting to hex coords, the z value
can be computed from the new x and y values because x + y + z must always
equal zero.
"""
class Hex:
# Constant matrix used to convert from hex coords to screen coords
matrix = [3.0 * 0.5, 0.0, math.sqrt(3.0) * 0.5, math.sqrt(3.0)]
# The screen coordinate to use as the origin for hex coordinates,
# the centre of hex 0,0,0 will be at this coordinate
origin = [math.ceil(ugfx.width() / 2), math.ceil(ugfx.height() / 2)]
# Size in pixels of the hex, from the centre point to each corner
size = 22
# Possible kinds of resource and the colour it should be rendered
kinds = {
0: ugfx.html_color(0xd4e157), # Sheep
1: ugfx.html_color(0xffc107), # Wheat
2: ugfx.html_color(0x993300), # Wood
3: ugfx.html_color(0xff0000), # Brick
4: ugfx.html_color(0x757575), # Ore
5: ugfx.html_color(0xffee55), # Desert (nothing)
}
# Transformations for how to get to the neighbouring hexes
directions = {
0: [-1, 1, 0], # South West
1: [0, 1, -1], # South
2: [1, 0, -1], # South East
3: [1, -1, 0], # North East
4: [0, -1, 1], # North
5: [-1, 0, 1], # North West
}
def __init__(self, coords, kind, number, robber):
"""Create a new hex at the given hex coordinates, of the given kind of resource"""
# Validate coords
assert len(coords) == 3, 'Invalid number of hexagon coordinates'
assert coords[0] + coords[1] + coords[2] == 0, 'Invalid hexagon coordinate values'
self.coords = coords
# The kind of resource hosted by this hex
self.kind = kind
# The dice roll required to win this resource
self.number = number
# Whether this hex contains the robber
self.robber = robber
# Compute the screen coordinates of the centre of the hex
self.centre = Hex.to_screen_coords(self.coords[0], self.coords[1])
# Generate screen coordinates for each of the corners of the hex
self.corners = []
for i in range(0, 6):
angle = 2.0 * math.pi * (0 - i) / 6
offset = [Hex.size * math.cos(angle), Hex.size * math.sin(angle)]
self.corners.append([round(self.centre[0] + offset[0]), round(self.centre[1] + offset[1])])
@staticmethod
def to_screen_coords(x, y):
"""Returns screen coordinates computed from the given hex coordinates"""
newX = (Hex.matrix[0] * x + Hex.matrix[1] * y) * Hex.size
newY = (Hex.matrix[2] * x + Hex.matrix[3] * y) * Hex.size
return [newX + Hex.origin[0], newY + Hex.origin[1]]
@staticmethod
def get_neighbouring_hex_coords(coords, direction):
return [a + b for a, b in zip(coords, Hex.directions[direction])]
def draw(self):
"""Draw the hexagon to the screen"""
ugfx.fill_polygon(0, 0, self.corners, Hex.kinds[self.kind])
text_offset = Hex.size * 0.5
if self.robber:
ugfx.text(round(self.centre[0] - text_offset), round(self.centre[1] - text_offset), "Rb ", ugfx.BLACK)
else:
if self.kind != 5:
ugfx.text(round(self.centre[0] - text_offset), round(self.centre[1] - text_offset), "{} ".format(self.number), ugfx.BLACK)
def clear(self):
ugfx.fill_polygon(0, 0, self.corners, ugfx.BLACK)
def board_setup(resources, numbers):
"""Generate a random game board"""
# Two rings of hexes around the centre
radius = 2
# Choose a starting hex on the outermost ring of hexes
choice = random.randrange(0, 6)
coords = [0, 0, 0]
for i in range(radius):
coords = [a + b for a, b in zip(coords, Hex.directions[choice])]
# Copy lists so we can edit them with impunity
r_copy = resources.copy()
n_copy = numbers.copy()
hexes = []
while radius > 0:
# From the starting hex, go radius hexes in each of the 6 directions
for i in list(range((choice + 2) % 6, 6)) + list(range(0, (choice + 2) % 6)):
for j in range(radius):
# The resources are picked at random from the list
resource = r_copy.pop(random.randrange(0, len(r_copy)))
# But the dice roll numbers are picked in order, unless it's
# the desert in which case that is always 7
if resource == 5:
number = 7
else:
number = n_copy.pop(0)
hexes.append(Hex(coords, resource, number, number == 7))
coords = Hex.get_neighbouring_hex_coords(coords, i)
# Go into the next ring of hexes (opposite direction of starting choice)
coords = Hex.get_neighbouring_hex_coords(coords, (choice + 3) % 6)
radius = radius - 1
resource = r_copy.pop()
if resource == 5:
number = 7
else:
number = n_copy.pop(0)
hexes.append(Hex(coords, resource, number, number == 7))
return hexes
# List of resources (pre-randomised to combat the not-very random number
# generator) and dice rolls (these have a strict order) for 2-4 player games
resources = [4, 0, 1, 4, 4, 2, 5, 3, 2, 1, 2, 2, 1, 0, 3, 0, 3, 1, 0]
numbers = [5, 2, 6, 3, 8, 10, 9, 12, 11, 4, 8, 10, 9, 4, 5, 6, 3, 11]
def draw():
hexes = board_setup(resources, numbers)
for h in hexes:
h.clear()
time.sleep_ms(100)
h.draw()
ugfx.text(5, 5, 'Press A to generate another ', ugfx.WHITE)
draw()
# Main Loop
while True:
if buttons.is_triggered(tilda.Buttons.BTN_A):
draw()
elif buttons.is_triggered(tilda.Buttons.BTN_Menu):
break
time.sleep_ms(5)
restart_to_default()