mirror of
https://github.com/csd4ni3l/connect-the-current.git
synced 2026-01-01 04:13:41 +01:00
make corners straight use straight lines instead of arcs, add randomized level generator finally, make default difficulties bigger, make main menu buttons smaller, other fixes and improvements
This commit is contained in:
@@ -15,9 +15,9 @@ def get_opposite(direction):
|
||||
|
||||
class Cell(arcade.gui.UITextureButton):
|
||||
def __init__(self, cell_type, left_neighbour, top_neighbour):
|
||||
super().__init__(texture=TEXTURE_MAP[cell_type, ROTATIONS[cell_type][0], cell_type == "power_source"])
|
||||
super().__init__(texture=TEXTURE_MAP[cell_type, ROTATIONS[cell_type][0] if cell_type in ROTATIONS else "cross", cell_type == "power_source"])
|
||||
|
||||
self.rotation = ROTATIONS[cell_type][0]
|
||||
self.rotation = ROTATIONS[cell_type][0] if cell_type in ROTATIONS else "cross"
|
||||
self.cell_type = cell_type
|
||||
self.powered = False
|
||||
self.left_neighbour, self.top_neighbour = left_neighbour, top_neighbour
|
||||
@@ -38,13 +38,11 @@ class Cell(arcade.gui.UITextureButton):
|
||||
self.get_neighbour(neighbour_direction) for neighbour_direction in NEIGHBOURS[self.rotation]
|
||||
if (
|
||||
self.get_neighbour(neighbour_direction) and
|
||||
self.get_neighbour(neighbour_direction).cell_type != "house" and
|
||||
get_opposite(neighbour_direction) in NEIGHBOURS[self.get_neighbour(neighbour_direction).rotation]
|
||||
)
|
||||
]
|
||||
|
||||
def update_value(self):
|
||||
self.powered = any([neighbour.powered for neighbour in self.get_connected_neighbours()])
|
||||
|
||||
def update_visual(self):
|
||||
self.texture = TEXTURE_MAP[(self.cell_type, self.rotation, self.powered)]
|
||||
self.texture_hovered = TEXTURE_MAP[(self.cell_type, self.rotation, self.powered)]
|
||||
|
||||
129
game/level_generator.py
Normal file
129
game/level_generator.py
Normal file
@@ -0,0 +1,129 @@
|
||||
import random
|
||||
from utils.constants import ROTATIONS, NEIGHBOURS, DIRECTIONS
|
||||
from collections import deque
|
||||
|
||||
def in_bounds(x, y, size):
|
||||
return 0 <= x < size and 0 <= y < size
|
||||
|
||||
def classify_tile(conns):
|
||||
for rotation, connections in NEIGHBOURS.items():
|
||||
if conns == connections:
|
||||
for cell_type, rotations in ROTATIONS.items():
|
||||
if rotation in rotations:
|
||||
return cell_type
|
||||
|
||||
print(f"Unknown: {conns}")
|
||||
return "cross"
|
||||
|
||||
def add_cycles(conns, num_cycles):
|
||||
size = len(conns)
|
||||
added = 0
|
||||
attempts = 0
|
||||
max_attempts = num_cycles * 20
|
||||
|
||||
while added < num_cycles and attempts < max_attempts:
|
||||
attempts += 1
|
||||
x, y = random.randint(0, size-1), random.randint(0, size-1)
|
||||
|
||||
dirs = list(DIRECTIONS.items())
|
||||
random.shuffle(dirs)
|
||||
|
||||
for d, (dx, dy, opposite) in dirs:
|
||||
nx, ny = x + dx, y + dy
|
||||
|
||||
if in_bounds(nx, ny, size) and d not in conns[y][x]:
|
||||
conns[y][x].add(d)
|
||||
conns[ny][nx].add(opposite)
|
||||
added += 1
|
||||
break
|
||||
|
||||
return conns
|
||||
|
||||
def pick_random_cells(size, count, avoid=None):
|
||||
all_cells = [(x, y) for y in range(size) for x in range(size)]
|
||||
if avoid:
|
||||
all_cells = [c for c in all_cells if c not in avoid]
|
||||
random.shuffle(all_cells)
|
||||
return all_cells[:count]
|
||||
|
||||
def generate_spanning_tree_with_dead_ends(size, num_dead_ends):
|
||||
if num_dead_ends > size * size - 1:
|
||||
num_dead_ends = size * size - 1
|
||||
|
||||
grid = [[set() for _ in range(size)] for _ in range(size)]
|
||||
all_cells = [(x, y) for y in range(size) for x in range(size)]
|
||||
random.shuffle(all_cells)
|
||||
|
||||
start = all_cells[0]
|
||||
stack = [start]
|
||||
visited = {start}
|
||||
leaf_candidates = []
|
||||
|
||||
while len(visited) < size * size:
|
||||
if not stack:
|
||||
unvisited = [c for c in all_cells if c not in visited]
|
||||
if unvisited:
|
||||
stack.append(unvisited[0])
|
||||
visited.add(unvisited[0])
|
||||
|
||||
x, y = stack[-1]
|
||||
dirs = list(DIRECTIONS.items())
|
||||
random.shuffle(dirs)
|
||||
|
||||
found = False
|
||||
for d, (dx, dy, opposite) in dirs:
|
||||
nx, ny = x + dx, y + dy
|
||||
if in_bounds(nx, ny, size) and (nx, ny) not in visited:
|
||||
grid[y][x].add(d)
|
||||
grid[ny][nx].add(opposite)
|
||||
visited.add((nx, ny))
|
||||
stack.append((nx, ny))
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
if len(grid[y][x]) == 1:
|
||||
leaf_candidates.append((x, y))
|
||||
stack.pop()
|
||||
|
||||
leaf_nodes = leaf_candidates[:num_dead_ends]
|
||||
|
||||
for y in range(size):
|
||||
for x in range(size):
|
||||
if (x, y) not in leaf_nodes and len(grid[y][x]) < 2:
|
||||
dirs = list(DIRECTIONS.items())
|
||||
random.shuffle(dirs)
|
||||
|
||||
for d, (dx, dy, opposite) in dirs:
|
||||
nx, ny = x + dx, y + dy
|
||||
|
||||
if in_bounds(nx, ny, size) and d not in grid[y][x]:
|
||||
grid[y][x].add(d)
|
||||
grid[ny][nx].add(opposite)
|
||||
if len(grid[y][x]) >= 2:
|
||||
break
|
||||
|
||||
return grid, leaf_nodes
|
||||
|
||||
def generate_map(size, source_count, house_count, cycles=15):
|
||||
conns, dead_ends = generate_spanning_tree_with_dead_ends(size, house_count)
|
||||
conns = add_cycles(conns, cycles)
|
||||
|
||||
houses = dead_ends[:house_count]
|
||||
available_cells = [(x, y) for y in range(size) for x in range(size)
|
||||
if (x, y) not in houses]
|
||||
random.shuffle(available_cells)
|
||||
sources = available_cells[:source_count]
|
||||
|
||||
grid = []
|
||||
for y in range(size):
|
||||
grid.append([])
|
||||
for x in range(size):
|
||||
if (x, y) in sources:
|
||||
grid[-1].append("power_source")
|
||||
elif (x, y) in houses:
|
||||
grid[-1].append("house")
|
||||
else:
|
||||
grid[-1].append(classify_tile(conns[y][x]))
|
||||
|
||||
return grid
|
||||
18
game/play.py
18
game/play.py
@@ -5,6 +5,7 @@ from utils.preload import button_texture, button_hovered_texture
|
||||
|
||||
from collections import deque
|
||||
|
||||
from game.level_generator import generate_map
|
||||
from game.cells import *
|
||||
|
||||
class Game(arcade.gui.UIView):
|
||||
@@ -20,8 +21,10 @@ class Game(arcade.gui.UIView):
|
||||
self.houses = []
|
||||
|
||||
self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
|
||||
self.grid_size = list(map(int, difficulty.split("x")))
|
||||
self.power_grid = self.anchor.add(arcade.gui.UIGridLayout(horizontal_spacing=0, vertical_spacing=0, row_count=self.grid_size[0], column_count=self.grid_size[1]))
|
||||
self.grid_size = int(difficulty.split("x")[0])
|
||||
self.grid = generate_map(self.grid_size, int((self.grid_size * self.grid_size) / 10), int((self.grid_size * self.grid_size) / 5))
|
||||
|
||||
self.power_grid = self.anchor.add(arcade.gui.UIGridLayout(horizontal_spacing=0, vertical_spacing=0, row_count=self.grid_size, column_count=self.grid_size))
|
||||
|
||||
def on_show_view(self):
|
||||
super().on_show_view()
|
||||
@@ -30,13 +33,13 @@ class Game(arcade.gui.UIView):
|
||||
self.back_button.on_click = lambda event: self.main_exit()
|
||||
self.anchor.add(self.back_button, anchor_x="left", anchor_y="top", align_x=5, align_y=-5)
|
||||
|
||||
for row in range(self.grid_size[0]):
|
||||
for row in range(self.grid_size):
|
||||
self.cells.append([])
|
||||
for col in range(self.grid_size[1]):
|
||||
for col in range(self.grid_size):
|
||||
left_neighbour = self.cells[row][col - 1] if col > 0 else None
|
||||
top_neighbour = self.cells[row - 1][col] if row > 0 else None
|
||||
|
||||
cell_type = random.choice(["line", "corner", "t_junction", "cross", "power_source", "house"])
|
||||
cell_type = self.grid[row][col]
|
||||
|
||||
if cell_type in ["line", "corner", "t_junction", "cross"]:
|
||||
cell = PowerLine(cell_type, left_neighbour, top_neighbour)
|
||||
@@ -81,8 +84,9 @@ class Game(arcade.gui.UIView):
|
||||
queue.append(connected_neighbour)
|
||||
|
||||
for row in self.cells:
|
||||
for power_line in row:
|
||||
power_line.update_visual()
|
||||
for cell in row:
|
||||
cell.update_visual()
|
||||
|
||||
|
||||
def main_exit(self):
|
||||
from menus.main import Main
|
||||
|
||||
Reference in New Issue
Block a user