Tutorial: Hex Worlds#
In this tutorial you build a world made of hexagons, step by step. Hexagonal worlds are used for board games like Catan: land hexes, settlements on corners, and roads on edges.
If you have never worked with worlds before, start with the chapter Die erste Welt in the interactive course.
Step 1: Create a hex world#
A HexWorld is created with (columns, rows) and a tile_size:
import miniworlds
world = miniworlds.HexWorld(7, 7, tile_size=40)
world.background.grid = True
world.run()
Hexes are addressed with (column, row) coordinates, like tiles in a
TiledWorld. Odd rows are shifted half a hex to the right.
Step 2: Place actors on hexes#
An actor placed on a hex position is drawn as a hexagon:
import miniworlds
world = miniworlds.HexWorld(7, 7, tile_size=40)
world.background.grid = True
forest = miniworlds.Actor((2, 3))
forest.fill_color = (0, 120, 0)
desert = miniworlds.Actor((4, 2))
desert.fill_color = (230, 210, 160)
world.run()
Step 3: Query neighbors#
world.get_tile(position) returns a single hex.
tile.get_neighbour_tiles() returns the up to six neighboring hexes.
The example colors the center hex and all its neighbors:
import miniworlds
world = miniworlds.HexWorld(7, 7, tile_size=40)
world.background.grid = True
center = world.get_tile((3, 3))
center_actor = miniworlds.Actor(center.position)
center_actor.fill_color = (200, 30, 30)
for tile in center.get_neighbour_tiles():
actor = miniworlds.Actor(tile.position)
actor.fill_color = (0, 120, 0)
world.run()
world.get_tiles_in_radius(position, radius) returns all hexes within
a given hex distance, ordered from the center outwards.
world.get_tiles_in_ring(position, radius) returns only the hexes at
exactly the given distance, in ring traversal order.
Step 4: Move actors from hex to hex#
Actors move from hex to hex with the six hex directions "no", "o",
"so", "sw", "w", and "nw":
import miniworlds
world = miniworlds.HexWorld(7, 7, tile_size=40)
world.background.grid = True
robot = miniworlds.Actor((3, 3))
robot.fill_color = (200, 30, 30)
@robot.register
def act(self):
if world.frame % 20 == 0:
self.move_in_direction("no")
world.run()
Every move_in_direction call moves the actor exactly one hex,
independent of the row parity. The directions can also be given as
degrees: "no" is 30, "o" is 90, "so" is 150, "sw" is 210,
"w" is 270, and "nw" is 330.
Step 5: React to clicks#
With @world.register you can react to mouse clicks.
world.get_tile_from_pixel(mouse_pos) returns the hex under the mouse,
or None if the click is outside the world:
import miniworlds
world = miniworlds.HexWorld(7, 7, tile_size=40)
world.background.grid = True
@world.register
def on_mouse_left(self, mouse_pos):
tile = world.get_tile_from_pixel(mouse_pos)
if tile is None:
return
actor = miniworlds.Actor(tile.position)
actor.fill_color = (0, 120, 0)
world.run()
Step 6: Settlements and roads#
Corners and edges are positions, too.
world.get_corner_from_pixel(mouse_pos) returns the nearest corner,
world.get_edge_from_pixel(mouse_pos) the nearest edge.
Both return None if the pixel is outside the world, for example when
a toolbar or console is attached to the world.
A left click places a settlement (small circle) on a corner, a right click places a road (small rectangle) on an edge:
import miniworlds
world = miniworlds.HexWorld(7, 7, tile_size=40)
world.background.grid = True
@world.register
def on_mouse_left(self, mouse_pos):
corner = world.get_corner_from_pixel(mouse_pos)
settlement = miniworlds.Actor(corner.position)
settlement.fill_color = (200, 30, 30)
settlement.size = (0.3, 0.3)
@world.register
def on_mouse_right(self, mouse_pos):
edge = world.get_edge_from_pixel(mouse_pos)
road = miniworlds.Actor(edge.position)
road.fill_color = (90, 60, 30)
road.size = (0.6, 0.15)
world.run()
Step 7: A mini-Catan board#
A Catan board has 19 land hexes, arranged in rows of 3, 4, 5, 4, and 3:
import miniworlds
world = miniworlds.HexWorld(7, 7, tile_size=40)
world.background.grid = True
land_positions = [
(2, 1), (3, 1), (4, 1),
(2, 2), (3, 2), (4, 2), (5, 2),
(1, 3), (2, 3), (3, 3), (4, 3), (5, 3),
(1, 4), (2, 4), (3, 4), (4, 4),
(2, 5), (3, 5), (4, 5),
]
terrain_colors = [
(0, 120, 0), # forest
(220, 180, 60), # field
(120, 200, 60), # meadow
(150, 150, 150), # mountain
(200, 90, 30), # hill
(230, 210, 160), # desert
]
for number, position in enumerate(land_positions):
actor = miniworlds.Actor(position)
actor.fill_color = terrain_colors[number % len(terrain_colors)]
actor.border = 1
world.run()
From here you can continue in the pattern Hex Worlds and a Mini-Catan Board and add your own game rules: dice, resources, and trading are not part of the framework — they belong in your game code.
The full interface is documented in the HexWorld API reference.