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()