mirror of
https://github.com/csd4ni3l/game-of-life.git
synced 2026-01-01 04:23:42 +01:00
Add file manager quitting with escape, rle and life 1.05 loading support
and saving support for all formats(currently only rle in file manager)
This commit is contained in:
@@ -1,20 +1,21 @@
|
|||||||
import arcade, arcade.gui, os, time
|
import arcade, arcade.gui, os, time
|
||||||
|
|
||||||
from utils.constants import button_style
|
from game.file_support import save_file
|
||||||
|
from utils.constants import button_style, dropdown_style
|
||||||
from utils.preload import button_texture, button_hovered_texture
|
from utils.preload import button_texture, button_hovered_texture
|
||||||
|
|
||||||
from arcade.gui.experimental.scroll_area import UIScrollArea, UIScrollBar
|
from arcade.gui.experimental.scroll_area import UIScrollArea, UIScrollBar
|
||||||
|
|
||||||
class FileManager(arcade.gui.UIView):
|
class FileManager(arcade.gui.UIView):
|
||||||
def __init__(self, start_directory, allowed_extensions, *args):
|
def __init__(self, start_directory, allowed_extensions, save=False, *args):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.current_directory = start_directory
|
self.current_directory = start_directory
|
||||||
self.allowed_extensions = allowed_extensions
|
self.allowed_extensions = allowed_extensions
|
||||||
self.file_buttons = []
|
self.file_buttons = []
|
||||||
self.submitted_content = ""
|
self.submitted_content = ""
|
||||||
self.done = False
|
|
||||||
self.args = args
|
self.args = args
|
||||||
|
self.save = save
|
||||||
|
|
||||||
self.anchor = self.ui.add(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
|
self.anchor = self.ui.add(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
|
||||||
self.box = self.anchor.add(arcade.gui.UIBoxLayout(size_hint=(0.7, 0.7)), anchor_x="center", anchor_y="center")
|
self.box = self.anchor.add(arcade.gui.UIBoxLayout(size_hint=(0.7, 0.7)), anchor_x="center", anchor_y="center")
|
||||||
@@ -41,16 +42,24 @@ class FileManager(arcade.gui.UIView):
|
|||||||
self.back_button = self.anchor.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50), anchor_x="left", anchor_y="top", align_x=5, align_y=-5)
|
self.back_button = self.anchor.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50), anchor_x="left", anchor_y="top", align_x=5, align_y=-5)
|
||||||
self.back_button.on_click = lambda event: self.change_directory(os.path.dirname(self.current_directory))
|
self.back_button.on_click = lambda event: self.change_directory(os.path.dirname(self.current_directory))
|
||||||
|
|
||||||
|
if self.save:
|
||||||
|
self.save_filename_input = self.anchor.add(arcade.gui.UIInputText(font_name="Roboto", font_size=24, width=self.window.width / 2, height=self.window.height / 15), anchor_x="center", anchor_y="bottom", align_y=20)
|
||||||
|
# self.save_file_type_dropdown = self.anchor.add(arcade.gui.UIDropdown(options=["life_5", "life_6", "rle"], default="rle", width=self.window.width / 5, height=self.window.height / 15, primary_style=dropdown_style, dropdown_style=dropdown_style, active_style=dropdown_style), anchor_x="center", anchor_y="bottom", align_y=20, align_x=self.window.width / 7)
|
||||||
|
self.save_button = self.anchor.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Save', style=button_style, width=200, height=100), anchor_x="right", anchor_y="bottom", align_x=-35, align_y=5)
|
||||||
|
self.save_button.on_click = lambda event: self.save_content()
|
||||||
|
|
||||||
self.show_directory()
|
self.show_directory()
|
||||||
|
|
||||||
def submit(self, content):
|
def submit(self, content):
|
||||||
self.submitted_content = content
|
self.submitted_content = content
|
||||||
self.done = True
|
|
||||||
|
|
||||||
if os.path.isfile(content):
|
if os.path.isfile(content):
|
||||||
from game.play import Game
|
from game.play import Game
|
||||||
self.window.show_view(Game(*self.args, load_from=self.submitted_content))
|
self.window.show_view(Game(*self.args, load_from=self.submitted_content))
|
||||||
|
|
||||||
|
def save_content(self):
|
||||||
|
save_file(self.args[-1], f"{self.current_directory}/{self.save_filename_input.text}", "rle")
|
||||||
|
|
||||||
def get_content(self, directory):
|
def get_content(self, directory):
|
||||||
if not directory in self.content_cache or time.perf_counter() - self.content_cache[directory][-1] >= 30:
|
if not directory in self.content_cache or time.perf_counter() - self.content_cache[directory][-1] >= 30:
|
||||||
try:
|
try:
|
||||||
@@ -109,6 +118,12 @@ class FileManager(arcade.gui.UIView):
|
|||||||
else:
|
else:
|
||||||
self.file_buttons[-1].on_click = lambda event, file=f"{self.current_directory}/{file}": self.submit(file)
|
self.file_buttons[-1].on_click = lambda event, file=f"{self.current_directory}/{file}": self.submit(file)
|
||||||
|
|
||||||
|
def on_key_press(self, symbol: int, modifiers: int) -> bool | None:
|
||||||
|
super().on_key_press(symbol, modifiers)
|
||||||
|
if symbol == arcade.key.ESCAPE:
|
||||||
|
from game.play import Game
|
||||||
|
self.window.show_view(Game(*self.args))
|
||||||
|
|
||||||
def change_directory(self, directory):
|
def change_directory(self, directory):
|
||||||
if directory.startswith("//"): # Fix / paths
|
if directory.startswith("//"): # Fix / paths
|
||||||
directory = directory[1:]
|
directory = directory[1:]
|
||||||
|
|||||||
148
game/file_support.py
Normal file
148
game/file_support.py
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
def load_life_6(offset_x, offset_y, data):
|
||||||
|
loaded_data = []
|
||||||
|
|
||||||
|
for line in data:
|
||||||
|
if line == "#Life 1.06":
|
||||||
|
continue
|
||||||
|
|
||||||
|
x, y = line.split(" ")
|
||||||
|
x = int(offset_x + int(x))
|
||||||
|
y = int(offset_y + int(y))
|
||||||
|
loaded_data.append((y, x))
|
||||||
|
|
||||||
|
return loaded_data
|
||||||
|
|
||||||
|
def save_life_6(cell_grid):
|
||||||
|
data = "#Life 1.06"
|
||||||
|
alive_cells = [(row, col) for row in range(len(cell_grid)) for col in range(len(cell_grid[row])) if cell_grid[row][col]]
|
||||||
|
|
||||||
|
for cell in alive_cells:
|
||||||
|
data += f"\n{cell[0]} {cell[1]}"
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def load_life_5(offset_x, offset_y, data):
|
||||||
|
loaded_data = []
|
||||||
|
|
||||||
|
y = int(offset_y)
|
||||||
|
for line in data:
|
||||||
|
if line == "#Life 1.05" or line.startswith("#D") or line.startswith("#R") or line.startswith("#N"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
y += 1
|
||||||
|
for x, cell in enumerate(line):
|
||||||
|
x += int(offset_x)
|
||||||
|
if cell == "*":
|
||||||
|
loaded_data.append((y, x))
|
||||||
|
|
||||||
|
return loaded_data
|
||||||
|
|
||||||
|
def save_life_5(cell_grid):
|
||||||
|
data = "#Life 1.05\n#D Exported from csd4ni3l's Game Of Life viewer.\n#N\n"
|
||||||
|
|
||||||
|
for row_list in cell_grid.values():
|
||||||
|
for cell in row_list.values():
|
||||||
|
data += "*" if cell else "."
|
||||||
|
|
||||||
|
data += "\n"
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def load_rle(offset_x, offset_y, data):
|
||||||
|
loaded_data = []
|
||||||
|
rle_data = ""
|
||||||
|
x_offset = int(offset_x)
|
||||||
|
y_offset = int(offset_y)
|
||||||
|
y = 0
|
||||||
|
x = 0
|
||||||
|
|
||||||
|
for line in data:
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("#") or line.startswith("x"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
rle_data += line
|
||||||
|
|
||||||
|
pattern = re.compile(r"(\d*)([bo$!])")
|
||||||
|
matches = pattern.findall(rle_data)
|
||||||
|
|
||||||
|
for count_str, symbol in matches:
|
||||||
|
count = int(count_str) if count_str else 1
|
||||||
|
if symbol == "b":
|
||||||
|
x += count
|
||||||
|
elif symbol == "o":
|
||||||
|
for _ in range(count):
|
||||||
|
loaded_data.append((y_offset + y, x_offset + x))
|
||||||
|
x += 1
|
||||||
|
elif symbol == "$":
|
||||||
|
y += count
|
||||||
|
x = 0
|
||||||
|
elif symbol == "!":
|
||||||
|
break
|
||||||
|
|
||||||
|
return loaded_data
|
||||||
|
|
||||||
|
def save_rle(cell_grid):
|
||||||
|
live_cells = [(row, col) for row in cell_grid for col in cell_grid[row] if cell_grid[row][col]]
|
||||||
|
|
||||||
|
if not live_cells:
|
||||||
|
return "#C Empty pattern\nx = 0, y = 0, rule = B3/S23\n!"
|
||||||
|
|
||||||
|
min_row = min(row for row, _ in live_cells)
|
||||||
|
max_row = max(row for row, _ in live_cells)
|
||||||
|
min_col = min(col for _, col in live_cells)
|
||||||
|
max_col = max(col for _, col in live_cells)
|
||||||
|
|
||||||
|
width = max_col - min_col + 1
|
||||||
|
height = max_row - min_row + 1
|
||||||
|
|
||||||
|
data = "#C Exported from csd4ni3l's Game Of Life viewer.\n"
|
||||||
|
data += f"x = {width}, y = {height}, rule = B3/S23\n"
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
for row in range(min_row, max_row + 1):
|
||||||
|
line = ""
|
||||||
|
run_char = None
|
||||||
|
run_length = 0
|
||||||
|
|
||||||
|
for col in range(min_col, max_col + 1):
|
||||||
|
alive = cell_grid.get(row, {}).get(col, False)
|
||||||
|
char = "o" if alive else "b"
|
||||||
|
|
||||||
|
if char == run_char:
|
||||||
|
run_length += 1
|
||||||
|
else:
|
||||||
|
if run_char is not None:
|
||||||
|
line += (str(run_length) if run_length > 1 else "") + run_char
|
||||||
|
run_char = char
|
||||||
|
run_length = 1
|
||||||
|
|
||||||
|
if run_char:
|
||||||
|
line += (str(run_length) if run_length > 1 else "") + run_char
|
||||||
|
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
|
data += "$".join(lines) + "!"
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def load_file(offset_x, offset_y, file_path):
|
||||||
|
with open(file_path, "r") as file:
|
||||||
|
data = file.read().splitlines()
|
||||||
|
if "#Life 1.06" in data:
|
||||||
|
return load_life_6(offset_x, offset_y, data)
|
||||||
|
elif "#Life 1.05" in data:
|
||||||
|
return load_life_5(offset_x, offset_y, data)
|
||||||
|
elif file_path.endswith(".rle"):
|
||||||
|
return load_rle(offset_x, offset_y, data)
|
||||||
|
|
||||||
|
def save_file(cell_grid, file_path, file_type):
|
||||||
|
with open(file_path, "w") as file:
|
||||||
|
if file_type == "life_6":
|
||||||
|
file.write(save_life_6(cell_grid))
|
||||||
|
elif file_type == "life_5":
|
||||||
|
file.write(save_life_5(cell_grid))
|
||||||
|
elif file_type == "rle":
|
||||||
|
file.write(save_rle(cell_grid))
|
||||||
37
game/play.py
37
game/play.py
@@ -1,4 +1,5 @@
|
|||||||
import arcade, arcade.gui, random, math, copy, time, json, os
|
import arcade, arcade.gui, random, math, copy, time, json, os
|
||||||
|
from game.file_support import load_file
|
||||||
from utils.constants import COLS, ROWS, CELL_SIZE, SPACING, NEIGHBORS, button_style
|
from utils.constants import COLS, ROWS, CELL_SIZE, SPACING, NEIGHBORS, button_style
|
||||||
from utils.preload import create_sound, destroy_sound, button_texture, button_hovered_texture
|
from utils.preload import create_sound, destroy_sound, button_texture, button_hovered_texture
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ class Game(arcade.gui.UIView):
|
|||||||
|
|
||||||
def on_show_view(self):
|
def on_show_view(self):
|
||||||
super().on_show_view()
|
super().on_show_view()
|
||||||
self.setup_grid()
|
self.setup_grid(load_existing=bool(self.cell_grid))
|
||||||
|
|
||||||
self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
|
self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
|
||||||
self.info_box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=5, vertical=False), anchor_x="center", anchor_y="top")
|
self.info_box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=5, vertical=False), anchor_x="center", anchor_y="top")
|
||||||
@@ -49,6 +50,10 @@ class Game(arcade.gui.UIView):
|
|||||||
self.load_button.on_click = lambda event: self.load()
|
self.load_button.on_click = lambda event: self.load()
|
||||||
self.anchor.add(self.load_button, anchor_x="left", anchor_y="bottom", align_x=5, align_y=5)
|
self.anchor.add(self.load_button, anchor_x="left", anchor_y="bottom", align_x=5, align_y=5)
|
||||||
|
|
||||||
|
self.save_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="Save", style=button_style, width=200, height=100)
|
||||||
|
self.save_button.on_click = lambda event: self.save()
|
||||||
|
self.anchor.add(self.save_button, anchor_x="right", anchor_y="bottom", align_x=-5, align_y=5)
|
||||||
|
|
||||||
arcade.schedule(self.update_generation, 1 / self.generation_fps)
|
arcade.schedule(self.update_generation, 1 / self.generation_fps)
|
||||||
|
|
||||||
def main_exit(self):
|
def main_exit(self):
|
||||||
@@ -58,24 +63,16 @@ class Game(arcade.gui.UIView):
|
|||||||
def setup_grid(self, load_existing=False, randomized=False):
|
def setup_grid(self, load_existing=False, randomized=False):
|
||||||
self.spritelist.clear()
|
self.spritelist.clear()
|
||||||
|
|
||||||
for row in range(ROWS):
|
|
||||||
self.cell_grid[row] = {}
|
|
||||||
self.sprite_grid[row] = {}
|
|
||||||
|
|
||||||
if self.load_from:
|
if self.load_from:
|
||||||
loaded_data = []
|
loaded_data = load_file(COLS / 2, ROWS / 2, self.load_from)
|
||||||
with open(self.load_from, "r") as file:
|
|
||||||
data = file.read().splitlines()
|
|
||||||
for line in data:
|
|
||||||
if line == "#Life 1.06":
|
|
||||||
continue
|
|
||||||
|
|
||||||
x, y = line.split(" ")
|
|
||||||
x = int(COLS / 2 + int(x))
|
|
||||||
y = int(ROWS / 2 + int(y))
|
|
||||||
loaded_data.append((y, x))
|
|
||||||
|
|
||||||
for row in range(ROWS):
|
for row in range(ROWS):
|
||||||
|
if not row in self.cell_grid:
|
||||||
|
self.cell_grid[row] = {}
|
||||||
|
|
||||||
|
if not row in self.sprite_grid:
|
||||||
|
self.sprite_grid[row] = {}
|
||||||
|
|
||||||
for col in range(COLS):
|
for col in range(COLS):
|
||||||
if self.load_from:
|
if self.load_from:
|
||||||
self.cell_grid[row][col] = 1 if (row, col) in loaded_data else 0
|
self.cell_grid[row][col] = 1 if (row, col) in loaded_data else 0
|
||||||
@@ -199,8 +196,14 @@ class Game(arcade.gui.UIView):
|
|||||||
self.cell_grid[grid_row][grid_col] = 0
|
self.cell_grid[grid_row][grid_col] = 0
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
|
arcade.unschedule(self.update_generation)
|
||||||
from game.file_manager import FileManager
|
from game.file_manager import FileManager
|
||||||
self.window.show_view(FileManager(os.path.expanduser("~"), [".txt"], self.pypresence_client, self.generation, self.running, self.cell_grid))
|
self.window.show_view(FileManager(os.path.expanduser("~"), [".txt", ".rle"], False, self.pypresence_client, self.generation, self.running, self.cell_grid))
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
arcade.unschedule(self.update_generation)
|
||||||
|
from game.file_manager import FileManager
|
||||||
|
self.window.show_view(FileManager(os.path.expanduser("~"), [".txt", ".rle"], True, self.pypresence_client, self.generation, self.running, self.cell_grid))
|
||||||
|
|
||||||
def on_draw(self):
|
def on_draw(self):
|
||||||
super().on_draw()
|
super().on_draw()
|
||||||
|
|||||||
Reference in New Issue
Block a user