commit 3c17bdb5a07ff3791c9a9b8624dde54e2be872b8 Author: Elīna Lukjanoviča Date: Mon Feb 26 17:20:12 2024 +0000 Upload files to "/" diff --git a/0001_sprites.png b/0001_sprites.png new file mode 100644 index 0000000..b529c4f Binary files /dev/null and b/0001_sprites.png differ diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..e9d1b55 Binary files /dev/null and b/icon.png differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..ca66407 --- /dev/null +++ b/main.py @@ -0,0 +1,866 @@ +import win32api +import win32con +import win32gui +import pygame +import ctypes +import json +from tkinter import Tk, filedialog +import numpy as np + +# INITIALIZATION +pygame.init() +FPS = 60 + +MAIN_PATH = "C:/Users/User/Documents/Coding/Picture Puzzle/" +#or MAIN_PATH = "C:/Users/RVKG/Documents/My Palettes/Picture Puzzle/" +IMAGES_PATH = "/images/albums/" + +DATA_FILE = "main_data.json" +PUZZLE_FILE = "main_puzzle.json" +SETTINGS_FILE = "main_settings.json" +STATS_FILE = "main_stats.json" + +# Data loading and saving +def load_data(data): + try: + with open(data, "r") as file: + current_data = json.load(file) + except (FileNotFoundError, json.JSONDecodeError): + print("Something went wrong with file loading") + return current_data + +storage = load_data(DATA_FILE) +puzzle_data = load_data(PUZZLE_FILE) +settings_data = load_data(SETTINGS_FILE) +stats_data = load_data(STATS_FILE) + +# Configuration +SIZE_CHOICES = ["small", "big"] +VERSION_CHOICES = ["4x4", "3x3"] +STYLE_CHOICES = ["classic", "original", "sci-fi"] +MUSIC_ON = [True, False] +MUSIC_CHOICES = ["funky-leap", "serenity", "sunny-day"] +SOUND_ON = [True, False] +SOUND_CHOICES = ["puzzle", "wood", "metal_pipe", "lego_breaking"] +DISPLAY_CHOICES = ["time", "moves"] +LANGUAGE_CHOICES = ["english", "russian", "latvian"] +FONTS = ["msreferencesansserif", "arial", "bahnschrift"] + +if settings_data["gui_style"] == STYLE_CHOICES[0]: + FONT = FONTS[0] + STYLE_PATH = "1_" + text_color = (1, 35, 61) + timer_color = (255, 255, 255) +elif settings_data["gui_style"] == STYLE_CHOICES[1]: + FONT = FONTS[1] + STYLE_PATH = "2_" + text_color = (255, 255, 255) + timer_color = (70, 35, 0) +else: + FONT = FONTS[2] + STYLE_PATH = "3_" + text_color = (255, 255, 255) + timer_color = text_color + +if settings_data["size"] == SIZE_CHOICES[0]: + WIDTH, HEIGHT = 144, 154 + SIZE_PATH = "images/assets/0" + if FONT == FONTS[0]: + FONT_SIZE = 11 + elif FONT == FONTS[1]: + FONT_SIZE = 12 + elif FONT == FONTS[2]: + FONT_SIZE = 11 + non_movable_area = pygame.Rect(8, 19, 128, 128) + timer_area = pygame.Rect(22, 1, 55, 12) + puzzle_size = (128, 128) + win_area = pygame.Rect(55, 70, 55, 12) + +else: + WIDTH, HEIGHT = 202, 216 + SIZE_PATH = "images/assets/1" + if FONT == FONTS[0]: + FONT_SIZE = 15 + elif FONT == FONTS[1]: + FONT_SIZE = 17 + elif FONT == FONTS[2]: + FONT_SIZE = 16 + puzzle_size = (180, 180) + win_area = pygame.Rect(71, 98, 55, 12) + non_movable_area = pygame.Rect(11, 26, 180, 180) + timer_area = pygame.Rect(27, 2, 86, 16) + +puzzle_version = settings_data["version"] + +# Seting screen and other stuff +programIcon = pygame.image.load('icon.png') +pygame.display.set_icon(programIcon) +pygame.display.set_caption('Picture Puzzle') +screen_info = pygame.display.Info() +window_coords = [(screen_info.current_w - WIDTH) // 2, (screen_info.current_h - HEIGHT) // 2] +screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.NOFRAME) +clock = pygame.time.Clock() +hwnd = pygame.display.get_wm_info()["window"] + +# Sprites generation +class SpriteSheet: + def __init__(self, sheet_path): + self.sheet = pygame.image.load(sheet_path).convert_alpha() + + def get_sprite(self, position, width, height, color_key=None): + image = pygame.Surface((width, height), pygame.SRCALPHA).convert_alpha() + image.blit(self.sheet, (0, 0), (position[0], position[1], width, height)) + + if color_key: + for x in range(width): + for y in range(height): + if image.get_at((x, y))[:3] == color_key: + image.set_at((x, y), (0, 0, 0, 0)) + + return image + +sprite_sheet = SpriteSheet(SIZE_PATH + STYLE_PATH + "sprites.png") + +class Image: + def __init__(self, sprite_position, window_position, size): + self.sprite_sheet = sprite_sheet + self.sprite_position = sprite_position + self.window_position = window_position + self.size = size + self.update_image() + + def update_image(self): + self.img = self.sprite_sheet.get_sprite(self.sprite_position, *self.size) + self.rect = self.img.get_rect(topleft=self.window_position) + + def draw(self, screen): + screen.blit(self.img, self.rect) + +class Button: + def __init__(self, sprite_position, window_position, size): + self.sprite_sheet = sprite_sheet + self.sprite_position = sprite_position + self.window_position = window_position + self.size = size + self.hovered = False + self.disabled = False + self.update_images() + + def update_images(self): + self.img = self.sprite_sheet.get_sprite(self.sprite_position, *self.size) + hover_position = ( + self.sprite_position[0], + self.sprite_position[1] + 3 + self.size[1] + ) + self.hover_image = self.sprite_sheet.get_sprite(hover_position, *self.size) + + self.rect = self.img.get_rect(topleft=self.window_position) + self.mask = pygame.mask.from_surface(self.img) + + def is_hovered(self, mouse_pos): + return self.rect.collidepoint(mouse_pos) and self.mask.get_at( + (mouse_pos[0] - self.rect.x, mouse_pos[1] - self.rect.y) + ) + + def disable(self): + self.disabled = True + + def enable(self): + self.disabled = False + + def update(self, mouse_pos): + self.hovered = self.is_hovered(mouse_pos) + + def draw(self, screen): + if not self.disabled: + screen.blit(self.hover_image if self.hovered else self.img, self.rect) + else: + screen.blit(self.img, self.rect) + +# Ability to move window +def move_win(coordinates): + hwnd = pygame.display.get_wm_info()["window"] + w, h = pygame.display.get_surface().get_size() + win32gui.MoveWindow(hwnd, coordinates[0], coordinates[1], w, h, True) + set_shadow_style(hwnd) + +# Generating shadow +def set_shadow_style(hwnd): + try: + DWMWA_NCRENDERING_POLICY = 2 + DWMNCRP_ENABLED = 2 + + ctypes.windll.dwmapi.DwmSetWindowAttribute( + hwnd, + DWMWA_NCRENDERING_POLICY, + ctypes.byref(ctypes.c_uint(DWMNCRP_ENABLED)), + ctypes.sizeof(ctypes.c_uint), + ) + MARGINS = (ctypes.c_int * 4)(-1, -1, -1, -1) + ctypes.windll.dwmapi.DwmExtendFrameIntoClientArea(hwnd, ctypes.byref(MARGINS)) + + except Exception as e: + print(e) + +# Text render +def render_text(text, position, screen, color=text_color): + font = pygame.font.SysFont(FONT, FONT_SIZE) + text_render = font.render(text, True, color) + screen.blit(text_render, position) + +# MENU MODE +def browse(): + root = Tk() + root.withdraw() + file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.png;*.jpg;*.jpeg;*.gif")]) + root.destroy() + return file_path + +quick_game_activated = False +selected_file = None + +def quick_game(): + global selected_file, puzzle_image, puzzle_pieces, shuffled_pieces, empty_position, quick_game_activated, original_image + quick_game_activated = True + try: + selected_file = browse() + if selected_file is not None: + puzzle_image = pygame.image.load(selected_file) + original_image = pygame.transform.scale(puzzle_image, puzzle_size) + puzzle_pieces = cut_image(original_image) + shuffled_pieces = shuffle_pieces(puzzle_pieces) + empty_position = (len(puzzle_matrix) - 1, len(puzzle_matrix[0]) - 1) + quick_game_activated = False + except FileNotFoundError: + print("No file selected") + +def render_albums(): + global current_mode, album_name + current_hovered_index = -1 + if settings_data["size"] == SIZE_CHOICES[0]: + y = 25 + size = [20, 25, 100, 15] + cons = [15, 195, 12, 3, 9] + else: + y = 35 + size = [30, 35, 110, 25] + cons = [25, 247, 18, 3, 13] + + for hover_index, album in enumerate(storage["albums"]): + album_rect = pygame.Rect(size[0], y, size[2], size[3]) + album_hover = album_rect.collidepoint(pygame.mouse.get_pos()) + + if album_hover: + current_hovered_index = hover_index + + render_text(album, (size[1], y), screen) + y += cons[0] + + if album_hover and event.type == pygame.MOUSEBUTTONDOWN: + album_name = album + if album_name != album: + album_name = None + + if album_name is not None: + current_mode = "select" + + return album + + if current_hovered_index == -1: + current_hovered_index = 0 + arrow_image = Image((cons[1], 0), (cons[2], size[1] + cons[0] * current_hovered_index + cons[3]), (cons[4], cons[4])) + arrow_image.draw(screen) + +def album_deletion(): + # Not finished yet + pass + +def album_creation(): + # Not finished yet + pass + +# SELECT MODE +class ImageRender: + def __init__(self, album_name, initial_index=0): + self.album_name = album_name + self.index = initial_index + self.image_load() + + def image_load(self): + global current_img, current_album + if settings_data["size"] == SIZE_CHOICES[0]: + self.scaled_size = (18, 21, 108, 108) + else: + self.scaled_size = (22, 26, 158, 158) + + image_list = storage["albums"][self.album_name] + image_names = list(image_list.keys()) + + if not image_names: + return + + self.current = image_names[self.index] + selected_image = pygame.image.load(MAIN_PATH + IMAGES_PATH + self.album_name + "/" + self.current) + self.scaled_image = pygame.transform.scale(selected_image, (self.scaled_size[2], self.scaled_size[3])) + current_img = self.current + current_album = self.album_name + + def render(self, screen): + screen.blit(self.scaled_image, (self.scaled_size[0], self.scaled_size[1])) + pygame.display.flip() + + def update_index(self, increment): + self.index += increment + num_images = len(storage["albums"][self.album_name]) + if self.index >= num_images: + self.index = 0 + elif self.index < 0: + self.index = num_images - 1 + self.image_load() + + @staticmethod + def image_save(selected_album, current_img): + if settings_data["size"] == SIZE_CHOICES[0]: + scaled_size = (18, 21, 108, 108) + else: + scaled_size = (22, 26, 158, 158) + + for album in storage["albums"].values(): + for image in album.values(): + image[0] = 0 + chosen = storage["albums"][selected_album][current_img] + chosen[0] = 1 + with open(DATA_FILE, 'w') as data_file: + data_file.write(json.dumps(storage, indent=3)) + image_rect = pygame.Rect(scaled_size) + hover = image_rect.collidepoint(pygame.mouse.get_pos()) + if hover and pygame.mouse.get_pressed()[0]: + puzzle_data[f'current {puzzle_version}']["selected_image"] = IMAGES_PATH + selected_album + "/" + current_img + with open(PUZZLE_FILE, 'w') as puzzle_file: + puzzle_file.write(json.dumps(puzzle_data)) + load_puzzle() + return image_rect + + def image_deletion(self): + # Not finished yet + pass + + def image_creation(self): + # Not finished yet + pass + +def reload_chosen(): + for album in storage["albums"].values(): + for image in album.values(): + image[0] = 0 + with open(DATA_FILE, 'w') as data_file: + data_file.write(json.dumps(storage, indent=3)) + +# GAME MODE +def timer(elapsed_time): + hours = elapsed_time // 3600 + minutes = (elapsed_time % 3600) // 60 + seconds = elapsed_time % 60 + timer_text = f"{hours:02d}:{minutes:02d}:{seconds:02d}" + return timer_text + +# Puzzle loading and running logic +def cut_image(image): + piece_width = puzzle_size[0] // len(puzzle_matrix[0]) + piece_height = puzzle_size[1] // len(puzzle_matrix) + pieces = [] + for row in range(len(puzzle_matrix)): + for col in range(len(puzzle_matrix[0])): + left, top = col * piece_width, row * piece_height + piece_rect = pygame.Rect(left, top, piece_width, piece_height) + piece_image = image.subsurface(piece_rect) + + piece_layer = pygame.Surface((piece_width, piece_height), pygame.SRCALPHA) + + if puzzle_version == VERSION_CHOICES[0]: + layer_path = pygame.image.load("images/assets/piece32.png").convert_alpha() + else: + layer_path = pygame.image.load("images/assets/piece45.png" if settings_data["size"] == SIZE_CHOICES[0] else "images/assets/piece60.png").convert_alpha() + layer_path = pygame.transform.scale(layer_path, (piece_width, piece_height)) + layer_path.set_colorkey((0, 0, 0)) + + piece_layer.blit(piece_image, (0, 0)) + piece_layer.blit(layer_path, (0, 0)) + + pieces.append((piece_layer, (left, top))) + + return pieces + +def load_puzzle(): + global puzzle_matrix, original_matrix, empty_position, puzzle_pieces, shuffled_pieces, puzzle_size, directions, cell_size, number_of_rows, original_image + + puzzle_version = settings_data["version"] + image_path = puzzle_data[f'current {puzzle_version}']['selected_image'] + puzzle_image = pygame.image.load(MAIN_PATH + image_path) + + directions = {"up": [4, 8, 12], + "down": [], + "left": [13, 14, 15], + "right": []} + + puzzle_matrix = np.array(puzzle_data[f'current {puzzle_version}']['matrix']) + original_matrix = np.array(puzzle_data[f'puzzle {puzzle_version}']) + empty_position = np.nonzero(puzzle_matrix == 0) + number_of_rows = int(puzzle_version[0]) + cell_size = puzzle_size[0] // number_of_rows + original_image = pygame.transform.scale(puzzle_image, puzzle_size) + + puzzle_pieces = cut_image(original_image) + if np.max(puzzle_matrix) == 0: + shuffled_pieces = shuffle_pieces(puzzle_pieces) + empty_position = (len(puzzle_matrix) - 1, len(puzzle_matrix[0]) - 1) + +load_puzzle() + +def shuffle_pieces(puzzle_pieces): + global puzzle_matrix + numbers = list(range(len(puzzle_pieces))) + np.random.shuffle(numbers) + puzzle_matrix = np.array(numbers).reshape(puzzle_matrix.shape) + save_puzzle_state() + update_directions() + + for i, (_, (left, top)) in enumerate(puzzle_pieces): + row, col = divmod(numbers[i], len(puzzle_matrix[0])) + puzzle_pieces[i] = (puzzle_pieces[i][0], (col * cell_size, row * cell_size)) + + return puzzle_pieces + +def move_pieces(clicked_row, clicked_col): + global puzzle_matrix + + zero_positions = np.nonzero(puzzle_matrix == 0) + zero_row, zero_col = zero_positions[0][0], zero_positions[1][0] + + if clicked_row == zero_row: + if clicked_col < zero_col: + direction = "right" + else: + direction = "left" + elif clicked_col == zero_col: + if clicked_row < zero_row: + direction = "down" + else: + direction = "up" + else: + return + + if direction in ["up", "down"]: + pieces_to_move = puzzle_matrix[min(clicked_row, zero_row):max(clicked_row, zero_row) + 1, clicked_col] + else: + pieces_to_move = puzzle_matrix[clicked_row, min(clicked_col, zero_col):max(clicked_col, zero_col) + 1] + + if direction == "right": + puzzle_matrix[clicked_row, clicked_col:zero_col + 1] = np.roll(puzzle_matrix[clicked_row, clicked_col:zero_col + 1], 1) + elif direction == "left": + puzzle_matrix[clicked_row, zero_col:clicked_col + 1] = np.roll(puzzle_matrix[clicked_row, zero_col:clicked_col + 1], -1) + elif direction == "down": + puzzle_matrix[min(clicked_row, zero_row):max(clicked_row, zero_row) + 1, clicked_col] = np.roll(pieces_to_move, 1) + elif direction == "up": + puzzle_matrix[min(clicked_row, zero_row):max(clicked_row, zero_row) + 1, clicked_col] = np.roll(pieces_to_move, -1)[:pieces_to_move.shape[0]] + + save_puzzle_state() + update_directions() + +def update_directions(): + global directions, puzzle_matrix + zero_positions = np.nonzero(puzzle_matrix == 0) + zero_row, zero_col = zero_positions[0][0], zero_positions[1][0] + + directions = { + "up": puzzle_matrix[zero_row + 1:, zero_col].flatten().tolist() if zero_row < number_of_rows - 1 else [], + "down": puzzle_matrix[:zero_row, zero_col].flatten().tolist() if zero_row > 0 else [], + "left": puzzle_matrix[zero_row, zero_col + 1:].flatten().tolist() if zero_col < number_of_rows - 1 else [], + "right": puzzle_matrix[zero_row, :zero_col].flatten().tolist() if zero_col > 0 else [], + } + +update_directions() + +def save_puzzle_state(): + puzzle_matrix_list = puzzle_matrix.tolist() + puzzle_data[f'current {puzzle_version}']['matrix'] = puzzle_matrix_list + with open(PUZZLE_FILE, 'w') as puzzle_file: + json.dump(puzzle_data, puzzle_file) + +def update_puzzle_data(completed, sorted, current_time, current_moves): + print("saving2") + path_components = puzzle_data[f'current {puzzle_version}']['selected_image'].split('/')[2:] + current_data = storage[path_components[0]] + for component in path_components[1:]: + if component in current_data: + current_data = current_data[component] + else: + print("Something went wrong while updating puzzle data") + return + + print("current_data:", current_data) + initial_chosen, initial_completed, initial_sorted, initial_moves, initial_time, _, initial_current_moves = current_data + initial_completed = int(completed) + initial_sorted = int(sorted) + initial_moves += current_moves + + initial_time_seconds = sum(int(x) * 60**i for i, x in enumerate(reversed(initial_time.split(":")))) + current_time_seconds = sum(int(x) * 60**i for i, x in enumerate(reversed(current_time.split(":")))) + total_time_seconds = initial_time_seconds + current_time_seconds + hours, remainder = divmod(total_time_seconds, 3600) + minutes, seconds = divmod(remainder, 60) + formatted_time = f"{hours:02d}:{minutes:02d}:{seconds:02d}" + + current_data = [initial_chosen, initial_completed, initial_sorted, initial_moves, formatted_time, current_time, initial_current_moves] + print(current_data) + storage[path_components[0]][path_components[1]][path_components[2]] = current_data + with open(DATA_FILE, 'w') as data_file: + data_file.write(json.dumps(storage, indent=3)) + +def fade_in_out(rect, duration): + fade_surface = pygame.Surface((rect.width, rect.height)) + fade_surface.fill((0, 0, 0)) + fade_surface.set_alpha(0) + +# SETTINGS MODE +def play_music(): + music_file = settings_data["selected_music"] + volume = settings_data["music_volume"] + pygame.mixer.music.load("misc/" + music_file + ".mp3") + pygame.mixer.music.set_volume(volume) + pygame.mixer.music.play(-1) + +if settings_data["music"] == MUSIC_ON[0]: + play_music() + +def play_sound(): + sound_file = settings_data["selected_sound"] + volume = settings_data["sound_volume"] + sound = pygame.mixer.Sound("misc/" + sound_file + ".ogg") + sound.set_volume(volume) + pygame.mixer.Sound.play(sound) + +def set_hue(): + if settings_data["gui_style"] == STYLE_CHOICES[0]: + hue = 204 + elif settings_data["gui_style"] == STYLE_CHOICES[2]: + hue = 255 # ??? + + keys = pygame.key.get_pressed() + if keys[pygame.K_LEFT]: + hue = (hue - 1) % 360 + if keys[pygame.K_RIGHT]: + hue = (hue + 1) % 360 + +def export(): + # Export data function, not finished yet + pass + +def reset(): + # Reset all data function, not finished yet + pass + +def info(): + # Not finished yet + pass + +# Stats +def stats(): + # Not finished yet + pass + +# Buttons +if settings_data["size"] == SIZE_CHOICES[0]: + px12 = (12, 12) + px14 = (14, 14) + timer_button = Button((0, 0), (7, 3), px12) + menu_button = Button((15, 0), (80, 3), px12) + hint_button = Button((30, 0), (95, 3), px12) + shuffle_button = Button((45, 0), (110, 3), px12) + close_button = Button((60, 0), (125, 3), px12) + finished_button = Button((75, 0), (7, 3), px12) + not_finished_button = Button((90, 0), (7, 3), px12) + delete_button = Button((105, 0), (95, 3), px12) + continue_button = Button((120, 0), (110, 3), px12) + settings_button = Button((135, 0), (7, 3), px12) + quick_add_button = Button((34, 30), (22, 3), (55, 12)) + new_button = Button((150, 0), (80, 3), px12) + info_button = Button((165, 0), (7, 3), px12) + stats_button = Button((180, 0), (95, 3), px12) + to_left_button = Button((0, 30), (44, 136), px14) + to_right_button = Button((17, 30), (86, 136), px14) + BACKGROUND_IMAGE = Image((0, 64), (0, 0), (WIDTH, HEIGHT)) + ver1_layer = sprite_sheet.get_sprite((147, 64), WIDTH, HEIGHT, color_key=(255, 0, 128)) + ver2_layer = sprite_sheet.get_sprite((294, 64), WIDTH, HEIGHT, color_key=(255, 0, 128)) +else: + px16 = (16, 16) + px20 = (20, 20) + timer_button = Button((0, 0), (8, 4), px16) + menu_button = Button((19, 0), (118, 4), px16) + hint_button = Button((38, 0), (138, 4), px16) + shuffle_button = Button((57, 0), (158, 4), px16) + close_button = Button((76, 0), (178, 4), px16) + finished_button = Button((95, 0), (8, 4), px16) + not_finished_button = Button((114, 0), (8, 4), px16) + delete_button = Button((133, 0), (138, 4), px16) + continue_button = Button((152, 0), (158, 4), px16) + settings_button = Button((171, 0), (8, 4), px16) + quick_add_button = Button((46, 38), (28, 4), (86, 16)) + new_button = Button((190, 0), (118, 4), px16) + info_button = Button((209, 0), (8, 4), px16) + stats_button = Button((228, 0), (138, 4), px16) + to_left_button = Button((0, 38), (66, 191), px20) + to_right_button = Button((23, 38), (116, 191), px20) + BACKGROUND_IMAGE = Image((0, 84), (0, 0), (WIDTH, HEIGHT)) + ver1_layer = sprite_sheet.get_sprite((205, 84), WIDTH, HEIGHT, color_key=(255, 0, 128)) + ver2_layer = sprite_sheet.get_sprite((410, 84), WIDTH, HEIGHT, color_key=(255, 0, 128)) + +path_components = puzzle_data[f'current {puzzle_version}']['selected_image'].split('/')[2:] +current_data = storage[path_components[0]] +for component in path_components[1:]: + current_data = current_data[component] + +completed = current_data[1] +not_shuffled = current_data[2] +timer_text = current_data[5] +current_moves = current_data[6] + +if completed: + state_button = finished_button +else: + state_button = not_finished_button + +game_buttons = [timer_button, menu_button, hint_button, shuffle_button, close_button] +menu_buttons = [settings_button, quick_add_button, new_button, delete_button, continue_button, close_button] +select_buttons = [state_button, menu_button, delete_button, continue_button, close_button, to_left_button, to_right_button] +settings_buttons = [info_button, menu_button, stats_button, continue_button, close_button] + +do_not_save = False + +def button_check(pressed_button, run, current_mode, timer_running): + global completed, not_shuffled, timer_text, current_moves + not_shuffled = current_data[2] + timer_text = current_data[5] + current_moves = current_data[6] + do_not_save = False + if pressed_button == close_button: + run = False + save_puzzle_state() + update_puzzle_data(completed, not_shuffled, timer_text, current_moves) + reload_chosen() + elif pressed_button == settings_button: + current_mode = "settings" + elif pressed_button == menu_button: + current_mode = "menu" + timer_running = False + elif pressed_button == continue_button: + current_mode = "game" + elif pressed_button == delete_button: + if current_mode == "menu": + album_deletion() + else: + # Not finished yet + pass + elif pressed_button == new_button: + album_creation() + elif pressed_button == quick_add_button: + quick_game() + current_mode = "game" + not_shuffled = 1 + timer_text = "00:00:00" + current_moves = 0 + do_not_save = True + elif pressed_button in [not_finished_button, finished_button]: + # Not finished yet + pass + elif pressed_button == info_button: + info() + elif pressed_button == stats_button: + stats() + + return run, current_mode, timer_running, not_shuffled, timer_text, current_moves, do_not_save + +win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, + win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE) | win32con.WS_EX_LAYERED) + +win32gui.SetLayeredWindowAttributes(hwnd, win32api.RGB(*(255, 0, 128)), 0, win32con.LWA_COLORKEY) +set_shadow_style(hwnd) + +def data_check(current_mode): + if current_mode == "select": + background_image = ver2_layer + current_buttons = select_buttons + + elif current_mode == "game": + current_buttons = game_buttons + background_image = ver1_layer + + elif current_mode == "menu": + current_buttons = menu_buttons + background_image = ver1_layer + + elif current_mode == "settings": + current_buttons = settings_buttons + background_image = ver1_layer + + return background_image, current_buttons + +# Game state +run = True +current_mode = "menu" +# Dragging and mouse interaction +dragging = False +can_move = False +start_pos = (0, 0) +pressed_button = None +clicked_piece = None +# Select mode +clicked_image = False +image_display = None +# Puzzle-related stuff +last_shuffle_time = 0 +# Timer-related stuff +timer_running = False +timer_start_time = 0 +elapsed_time = 0 +total_paused_time = 0 +timer_button.disable() + +while run: + background_image, current_buttons = data_check(current_mode) + current_time = pygame.time.get_ticks() + + screen.fill((255,0,128)) + screen.blit(BACKGROUND_IMAGE.img, (0, 0)) + screen.blit(background_image, (0, 0)) + + mouse_pos = pygame.mouse.get_pos() + + for button in current_buttons: + button.update(mouse_pos) + + for button in current_buttons: + button.draw(screen) + + clock.tick(FPS) + + if current_mode == "menu": + selected_album = render_albums() + + if current_mode == "select": + if image_display is None or current_album != selected_album: + selected_image = ImageRender(selected_album) + image_display = True + + selected_image.render(screen) + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + if current_mode == "game": + save_puzzle_state() + update_puzzle_data(completed, not_shuffled, timer_text, current_moves) + reload_chosen() + run = False + elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: + if current_mode == "game": + save_puzzle_state() + update_puzzle_data(completed, not_shuffled, timer_text, current_moves) + reload_chosen() + run = False + elif event.type == pygame.MOUSEBUTTONDOWN: + if not non_movable_area.collidepoint(pygame.mouse.get_pos()): + can_move = True + if current_mode == "select" and event.button == 1: + clicked_image_rect = ImageRender.image_save(selected_album, current_img) + clicked_image = clicked_image_rect.collidepoint(event.pos) + if clicked_image: + current_mode = "game" + shuffle_pieces(puzzle_pieces) + last_click_time = pygame.time.get_ticks() + + if to_left_button.is_hovered(event.pos): + selected_image.update_index(-1) + last_image_switch_time = current_time + + if to_right_button.is_hovered(event.pos): + selected_image.update_index(1) + last_image_switch_time = current_time + + elif current_mode == "game": + if non_movable_area.collidepoint(event.pos) and not timer_running: + timer_running = True + timer_button.enable() + timer_start_time = pygame.time.get_ticks() + elif timer_button.rect.collidepoint(event.pos) and timer_running: + timer_running = False + timer_button.disable() + total_paused_time += pygame.time.get_ticks() - timer_start_time + + if event.button == 1: + dragging = True + start_pos = pygame.mouse.get_pos() + for button in current_buttons: + if button.rect.collidepoint(start_pos): + pressed_button = button + run, current_mode, timer_running, not_shuffled, timer_text, current_moves, do_not_save = button_check(pressed_button, run, current_mode, timer_running) + if settings_data["sound"] == SOUND_ON[0] and current_mode == "game" and non_movable_area.collidepoint(event.pos): + play_sound() + + elif event.type == pygame.MOUSEMOTION and can_move: + new_pos = pygame.mouse.get_pos() + offset_x = new_pos[0] - start_pos[0] + offset_y = new_pos[1] - start_pos[1] + window_coords[0] += offset_x + window_coords[1] += offset_y + move_win(window_coords) + + elif event.type == pygame.MOUSEBUTTONUP: + dragging = False + pressed_button = None + can_move = False + + if current_mode == "game": + if timer_running: + current_time = pygame.time.get_ticks() - timer_start_time + elapsed_time = (current_time + total_paused_time) // 1000 + timer_text = timer(elapsed_time) + render_text(timer_text, (timer_area.x, timer_area.y), screen, timer_color) + + # Puzzle + if pressed_button == shuffle_button and current_time - last_shuffle_time > 500: + if not_shuffled: + np.copyto(puzzle_matrix, original_matrix) + save_puzzle_state() + update_directions() + else: + shuffle_pieces(puzzle_pieces) + not_shuffled = 0 + last_shuffle_time = current_time + + for row in range(len(puzzle_matrix)): + for col in range(len(puzzle_matrix[0])): + if puzzle_matrix[row][col] != 0: + + piece_x = col * puzzle_size[0] // len(puzzle_matrix[0]) + non_movable_area.x + piece_y = row * puzzle_size[1] // len(puzzle_matrix) + non_movable_area.y + screen.blit(puzzle_pieces[puzzle_matrix[row][col] - 1][0], (piece_x, piece_y)) + + if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + mouse_x, mouse_y = pygame.mouse.get_pos() + + clicked_row, clicked_col = (mouse_y - non_movable_area.y) // cell_size, (mouse_x - non_movable_area.x) // cell_size + + if 0 <= clicked_row < len(puzzle_matrix) and 0 <= clicked_col < len(puzzle_matrix[0]): + if puzzle_matrix[clicked_row, clicked_col] != 0: + move_pieces(clicked_row, clicked_col) + + if pressed_button == hint_button: + screen.blit(original_image, non_movable_area) + if number_of_rows == 4 and puzzle_data[f'current {puzzle_version}']['matrix'] == puzzle_data[f'puzzle {puzzle_version}']: + render_text("You win !!", (win_area.x, win_area.y), screen, timer_color) + completed = 1 + not_shuffled = 1 + + pygame.display.flip() + +pygame.quit() \ No newline at end of file diff --git a/main_data.json b/main_data.json new file mode 100644 index 0000000..ccfdcdd --- /dev/null +++ b/main_data.json @@ -0,0 +1,107 @@ +{ + "albums": { + "Original images": { + "1.png": [ + 0, + 0, + 0, + 0, + "00:00:00", + "00:00:00", + 0 + ], + "2.png": [ + 0, + 0, + 0, + 0, + "00:00:00", + "00:00:00", + 0 + ], + "3.png": [ + 0, + 0, + 0, + 0, + "00:00:00", + "00:00:00", + 0 + ], + "4.png": [ + 0, + 0, + 0, + 0, + "00:00:00", + "00:00:00", + 0 + ], + "5.png": [ + 0, + 0, + 0, + 0, + "00:00:00", + "00:00:00", + 0 + ], + "6.png": [ + 0, + 0, + 0, + 0, + "00:00:00", + "00:00:00", + 0 + ], + "7.png": [ + 0, + 0, + 0, + 0, + "00:00:00", + "00:00:00", + 0 + ], + "8.png": [ + 0, + 0, + 0, + 0, + "00:00:00", + "00:00:00", + 0 + ], + "9.png": [ + 0, + 0, + 0, + 0, + "00:00:00", + "00:00:00", + 0 + ], + "10.png": [ + 0, + 0, + 0, + 0, + "00:00:34", + "00:00:04", + 0 + ] + }, + "Birds": { + "1.png": [ + 0, + 0, + 0, + 0, + "00:00:00", + "00:00:00", + 0 + ] + } + } +} \ No newline at end of file diff --git a/main_puzzle.json b/main_puzzle.json new file mode 100644 index 0000000..eea5db6 --- /dev/null +++ b/main_puzzle.json @@ -0,0 +1 @@ +{"puzzle 4x4": [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 0]], "puzzle 3x3": [[1, 2, 3], [4, 5, 6], [7, 8, 0]], "current 4x4": {"matrix": [[6, 8, 0, 12], [1, 11, 13, 15], [5, 7, 14, 4], [2, 9, 10, 3]], "selected_image": "/images/albums/Original images/10.png"}, "current 3x3": {"matrix": [[3, 6, 7], [5, 4, 1], [0, 8, 2]], "selected_image": "/images/albums/Birds/1.png"}} \ No newline at end of file