tanks/Sand_2.py

606 lines
17 KiB
Python
Raw Normal View History

2026-08-04 13:19:11 +02:00
import arcade
import arcade.gui
import random
# =====================================================
# USTAWIENIA
# =====================================================
CELL_SIZE = 5
GRID_WIDTH = 240
GRID_HEIGHT = 160
SCREEN_WIDTH = GRID_WIDTH * CELL_SIZE
SCREEN_HEIGHT = GRID_HEIGHT * CELL_SIZE
TITLE = "Silnik Piaskownicy"
FPS = 60
# =====================================================
# MATERIAŁY
# =====================================================
PUSTE = 0
PIASEK = 1
ZWIR = 2
WODA = 3
KAMIEN = 4
MOKRY_PIASEK = 5
AIR=6
LAWA=7
KOLORY = {
PUSTE: arcade.color.BLACK,
PIASEK: (235, 210, 70),
ZWIR: (140, 140, 140),
WODA: (50, 120, 255),
KAMIEN: (80, 80, 80),
MOKRY_PIASEK: (205,180,40),
AIR:(248,248,248),
LAWA: (200,40,40)
}
# =====================================================
# KLASA ŚWIATA
# =====================================================
class World:
def __init__(self):
self.width = GRID_WIDTH
self.height = GRID_HEIGHT
# Szybka tablica 1D
self.grid = [PUSTE] * (self.width * self.height)
def index(self, x, y):
x = int(x)
y = int(y)
return y * self.width + x
def inside(self, x, y):
return (
0 <= x < self.width and
0 <= y < self.height
)
def get(self, x, y):
if not self.inside(x, y):
return PUSTE
return self.grid[self.index(x, y)]
def set(self, x, y, value):
if not self.inside(x, y):
return
self.grid[self.index(x, y)] = value
def clear(self):
self.grid[:] = [PUSTE] * (self.width * self.height)
def swap(self, x1, y1, x2, y2):
i1 = self.index(x1, y1)
i2 = self.index(x2, y2)
self.grid[i1], self.grid[i2] = (
self.grid[i2],
self.grid[i1]
)
# =====================================================
# GŁÓWNE OKNO
# =====================================================
class Game(arcade.Window):
def __init__(self):
super().__init__(
SCREEN_WIDTH,
SCREEN_HEIGHT,
TITLE,
update_rate=1 / FPS
)
arcade.set_background_color(arcade.color.BLACK)
self.world_old = World()
self.world_new = World()
# Wybrany materiał
self.current_material = PIASEK
# Rozmiar pędzla
self.brush_size = 3
# Czy mysz jest wciśnięta
self.mouse_down = False
# Pozycja myszy
self.mouse_x = 0
self.mouse_y = 0
# Stan gry
self.paused = False
# Tekst interfejsu
self.info_text = arcade.Text(
"",
10,
SCREEN_HEIGHT - 25,
arcade.color.WHITE,
14
)
self.manager = arcade.gui.UIManager()
self.manager.enable()
self.manager = arcade.gui.UIManager()
self.manager.enable()
# Główny układ pozycjonujący (zastępuje UIAnchorWidget)
self.anchor_layout = arcade.gui.UIAnchorLayout()
# Układ pionowy dla elementów wewnątrz menu
self.v_box = arcade.gui.UIBoxLayout()
# Poprawione: definicja opcji pisana małą literą
opcje = ["Piasek", "Żwir", "Woda","Kamień","Mokry piasek","Powietrze","Lawa"]
# Tworzenie widgetu listy wybieranej
self.dropdown = arcade.gui.UIDropdown(
default=opcje[0], # Wartość domyślna (pierwszy element)
options=opcje, # Lista dostępnych opcji
width=200
)
# Przypisanie akcji po zmianie wyboru
self.dropdown.on_change = self.on_dropdown_change
# Dodanie listy wybieranej do pionowego kontenera
self.v_box.add(self.dropdown)
self.anchor_layout.add(
child=self.v_box,
anchor_x="right",
anchor_y="top"
)
# Dodanie całego układu do menedżera UI
self.manager.add(self.anchor_layout)
def can_move(self, x, y):
return self.world_old.get(x, y) == PUSTE and self.world_new.get(x, y) == PUSTE
def on_dropdown_change(self, event: arcade.gui.UIOnChangeEvent):
print(f"Zmieniono z: {event.old_value} na: {event.new_value}")
if event.new_value=="Piasek":
self.current_material = PIASEK
elif event.new_value=="Żwir":
self.current_material = ZWIR
elif event.new_value=="Woda":
self.current_material = WODA
elif event.new_value=="Kamień":
self.current_material = KAMIEN
elif event.new_value=="Mokry piasek":
self.current_material = MOKRY_PIASEK
elif event.new_value=="Powietrze":
self.current_material = AIR
elif event.new_value=="Lawa":
self.current_material = LAWA
def update_info(self):
self.info_text.text = (
f"Materiał: {self.material_name()} | "
f"Pędzel: {self.brush_size} | "
f"Pauza: {self.paused}"
)
def material_name(self):
nazwy = {
PUSTE: "Puste",
PIASEK: "Piasek",
ZWIR: "Żwir",
WODA: "Woda",
KAMIEN: "Kamień",
MOKRY_PIASEK: "Mokry piasek",
AIR:"Powietrze",
LAWA:"Lawa"
}
return nazwy[self.current_material]
# ==============================================
def on_update(self, delta_time):
if self.paused:
return
world_old = self.world_old
world_new = self.world_new
# Najpierw kopiujemy cały stan starego świata
world_new.grid[:] = world_old.grid[:]
for y in range(1, world_old.height):
for x in range(1, world_old.width):
material = world_old.get(x, y)
if material == PIASEK:
self.update_sand(x, y)
elif material == ZWIR:
self.update_zwir(x, y)
elif material == KAMIEN:
self.update_rock(x, y)
elif material == WODA:
self.update_woda(x, y)
elif material == MOKRY_PIASEK:
self.update_wet_sand(x,y)
elif material == AIR:
self.update_air(x,y)
elif material == LAWA:
self.update_lawa(x,y)
# Zamiana światów po zakończeniu całej klatki
self.world_old, self.world_new = (
self.world_new,
self.world_old
)
if self.mouse_down:
gx = int(self.mouse_x // CELL_SIZE)
gy = int(self.mouse_y // CELL_SIZE)
self.paint(gx, gy)
# ==============================================
def on_draw(self):
self.clear()
world = self.world_old
for y in range(world.height):
for x in range(world.width):
material = world.get(x, y)
if material == PUSTE:
continue
arcade.draw_rect_filled(
arcade.LBWH(
x * CELL_SIZE,
y * CELL_SIZE,
CELL_SIZE,
CELL_SIZE
),
KOLORY[material]
)
# reszta rysowania...
gx = self.mouse_x // CELL_SIZE
gy = self.mouse_y // CELL_SIZE
brush_pixels = self.brush_size * CELL_SIZE
center_x = gx * CELL_SIZE + CELL_SIZE / 2
center_y = gy * CELL_SIZE + CELL_SIZE / 2
rect = arcade.XYWH(
center_x,
center_y,
brush_pixels,
brush_pixels,
)
arcade.draw_rect_outline(
rect,
arcade.color.WHITE,
border_width=2,
)
self.update_info()
self.info_text.draw()
self.manager.draw()
self.world_old=self.world_new
def czy_dotyka(self,x,y,czego,na_co):
world_old = self.world_old
world_new = self.world_new
if world_old.get(x, y - 1) == czego:
world_new.set(x, y, na_co)
elif world_old.get(x - 1, y - 1) == czego:
world_new.set(x, y, na_co)
elif world_old.get(x + 1, y - 1) == czego:
world_new.set(x, y, na_co)
elif world_old.get(x + 1, y) == czego:
world_new.set(x, y, na_co)
elif world_old.get(x - 1, y) == czego:
world_new.set(x, y, na_co)
elif world_old.get(x - 1, y + 1) == czego:
world_new.set(x, y, na_co)
elif world_old.get(x, y + 1) == czego:
world_new.set(x, y, na_co)
elif world_old.get(x + 1, y + 1) == czego:
world_new.set(x, y, na_co)
def update_sand(self, x, y):
world_old = self.world_old
world_new = self.world_new
kierunek = random.choice([-1, 1])
szansa_w_wodzie=random.choice([True, False])
# dół
if world_old.get(x, y - 1) == PUSTE:
world_new.swap(x,y,x,y - 1)
# dół-lewo / dół-prawo
elif PUSTE == world_old.get(x + kierunek, y - 1):
if self.can_move(x + kierunek, y - 1):
world_new.swap(x, y, x + kierunek, y - 1)
elif world_old.get( x - kierunek, y - 1) == PUSTE:
if self.can_move(x - kierunek, y - 1):
world_new.swap(x, y, x - kierunek, y - 1)
# W wodzie
elif world_old.get(x, y - 1) == WODA and szansa_w_wodzie:
world_new.swap(x,y,x,y - 1)
world_new.set(x,y-1,MOKRY_PIASEK)
elif world_old.get( x + kierunek, y - 1) == WODA and szansa_w_wodzie:
world_new.swap(x, y, x + kierunek, y - 1)
world_new.set(x+kierunek, y - 1, MOKRY_PIASEK)
elif world_old.get( x - kierunek, y - 1) == WODA and szansa_w_wodzie:
world_new.swap(x, y, x - kierunek, y - 1)
world_new.set(x - kierunek, y - 1, MOKRY_PIASEK)
elif world_old.get( x +1, y-1) == WODA or world_old.get( x , y - 1) == WODA or world_old.get( x-1, y-1) == WODA or world_old.get( x - 1, y) == WODA or world_old.get( x - 1, y + 1) == WODA or world_old.get( x, y+1) == WODA or world_old.get( x +1, y +1) == WODA or world_old.get( x +1, y) == WODA:
world_new.set(x, y, MOKRY_PIASEK)
szansa_na_znikniencie_wody=random.choice([True, False])
if szansa_na_znikniencie_wody:
self.czy_dotyka(x,y,WODA,MOKRY_PIASEK)
def update_zwir(self,x,y):
world_old = self.world_old
world_new = self.world_new
if world_old.get(x, y - 1) == PUSTE:
world_new.swap(x,y,x, y - 1)
for kierunek in random.choice([-1, 1]), random.choice([1, -1]):
skos_x = x + kierunek
skos_y = y - 1
dol_x = x + kierunek
dol_y = y - 2
if world_old.get(skos_x, skos_y) == PUSTE and world_old.get(dol_x, dol_y) == PUSTE:
if self.can_move(skos_x, skos_y):
world_new.swap( x, y, skos_x, skos_y)
break
def update_rock(self,x,y):
world_new = self.world_new
world_old = self.world_old
if world_old.get(x, y - 1) == PUSTE and self.can_move(x,y-1):
world_new.swap(x, y, x, y - 1)
def update_woda(self, x, y):
world_new = self.world_new
world_old = self.world_old
kierunek = random.choice([-1, 1])
# dół
if world_old.get(x, y - 1) == PUSTE:
world_new.swap(x,y,x,y - 1)
# dół-lewo / dół-prawo
elif world_old.get( x +1, y - 1) == PUSTE:
world_new.swap(x, y, x + 1, y - 1)
elif world_old.get( x - 1, y - 1) == PUSTE:
world_new.swap(x, y, x - 1, y - 1)
# lewo/prawo
elif (
world_old.get(x - 1, y) == PUSTE
and world_old.get(x + 1, y) == PUSTE
):
world_new.swap(x, y, x + 1, y)
elif world_old.get(x + kierunek, y) == PUSTE:
world_new.swap(x, y, x + kierunek, y)
elif world_old.get(x - kierunek, y) == PUSTE:
world_new.swap(x, y, x - kierunek, y)
self.czy_dotyka(x,y,LAWA,AIR)
def update_wet_sand(self, x, y):
world_old = self.world_old
world_new = self.world_new
kierunek = random.choice([-1, 1])
szansa_w_wodzie=random.choice([True, False])
if world_old.get(x, y - 1) == PUSTE:
world_new.swap(x,y,x,y - 1)
# dół-lewo / dół-prawo
elif PUSTE == world_old.get(x + kierunek, y - 1):
if self.can_move(x + kierunek, y - 1):
world_new.swap(x, y, x + kierunek, y - 1)
elif world_old.get( x + kierunek, y - 1) == PUSTE:
if self.can_move(x + kierunek, y - 1):
world_new.swap(x, y, x + kierunek, y - 1)
# W wodzie
elif world_old.get(x, y - 1) == WODA and szansa_w_wodzie:
world_new.swap(x,y,x,y - 1)
elif world_old.get( x + kierunek, y - 1) == WODA and szansa_w_wodzie:
world_new.swap(x, y, x + kierunek, y - 1)
elif world_old.get( x + kierunek, y - 1) == WODA and szansa_w_wodzie:
world_new.swap(x, y, x + kierunek, y - 1)
def update_air(self,x,y):
world_old = self.world_old
world_new = self.world_new
kierunekx = random.choice([-1,0, 1])
kieruneky = random.choice([-1,0, 1])
lista = [False] * 10 + [True]
czy_ruszyc = random.choice(lista)
if world_old.get(x, y + 1) != PUSTE:
world_new.swap(x, y, x, y + 1)
elif self.can_move(x + kierunekx, y +kieruneky) and czy_ruszyc:
world_new.swap(x, y, x + kierunekx, y+kieruneky)
def update_lawa(self,x,y):
world_new = self.world_new
world_old = self.world_old
kierunek = random.choice([-1, 1])
if random.random() < 0.9:
return
# dół
if world_old.get(x, y - 1) == PUSTE:
world_new.swap(x, y, x, y - 1)
# dół-lewo / dół-prawo
elif world_old.get(x + 1, y - 1) == PUSTE:
world_new.swap(x, y, x + 1, y - 1)
elif world_old.get(x - 1, y - 1) == PUSTE:
world_new.swap(x, y, x - 1, y - 1)
# lewo/prawo
elif (
world_old.get(x - 1, y) == PUSTE
and world_old.get(x + 1, y) == PUSTE
):
world_new.swap(x, y, x + 1, y)
elif world_old.get(x + kierunek, y) == PUSTE:
world_new.swap(x, y, x + kierunek, y)
elif world_old.get(x - kierunek, y) == PUSTE:
world_new.swap(x, y, x - kierunek, y)
self.czy_dotyka(x,y,WODA,KAMIEN)
def on_key_press(self,key, modifiers):
if key == arcade.key.KEY_1:
self.current_material = PIASEK
elif key == arcade.key.KEY_2:
self.current_material = ZWIR
elif key == arcade.key.KEY_3:
self.current_material = WODA
elif key == arcade.key.KEY_4:
self.current_material = KAMIEN
elif key == arcade.key.KEY_5:
self.current_material = MOKRY_PIASEK
elif key == arcade.key.KEY_6:
self.current_material = AIR
elif key == arcade.key.KEY_7:
self.current_material = LAWA
elif key == arcade.key.C:
self.world_old.clear()
elif key == arcade.key.SPACE:
self.paused = not self.paused
# ==============================================
def on_mouse_press(self, x, y, button, modifiers):
self.mouse_down = True
self.mouse_x = x
self.mouse_y = y
gx = x // CELL_SIZE
gy = y // CELL_SIZE
self.paint(gx, gy)
def on_mouse_release(self, x, y, button, modifiers):
self.mouse_down = False
def on_mouse_motion(self, x, y, dx, dy):
self.mouse_x = x
self.mouse_y = y
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
self.mouse_x = x
self.mouse_y = y
def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
self.brush_size += scroll_y
if self.brush_size < 1:
self.brush_size = 1
if self.brush_size > 20:
self.brush_size = 20
def paint(self, grid_x, grid_y):
grid_x = int(grid_x)
grid_y = int(grid_y)
r = int(self.brush_size)
for dy in range(-r, r + 1):
for dx in range(-r, r + 1):
if dx * dx + dy * dy > r * r:
continue
x = grid_x + dx
y = grid_y + dy
self.world_old.set(
x,
y,
self.current_material
)
# =====================================================
# START PROGRAMU
# =====================================================
def main():
Game()
arcade.run()
if __name__ == "__main__":
main()