diff --git a/game_collection/__pycache__/game_2048.cpython-314.pyc b/game_collection/__pycache__/game_2048.cpython-314.pyc new file mode 100644 index 0000000..b7533b4 Binary files /dev/null and b/game_collection/__pycache__/game_2048.cpython-314.pyc differ diff --git a/game_collection/__pycache__/game_snake.cpython-314.pyc b/game_collection/__pycache__/game_snake.cpython-314.pyc new file mode 100644 index 0000000..2226c1c Binary files /dev/null and b/game_collection/__pycache__/game_snake.cpython-314.pyc differ diff --git a/game_collection/game_2048.py b/game_collection/game_2048.py new file mode 100644 index 0000000..f77862c --- /dev/null +++ b/game_collection/game_2048.py @@ -0,0 +1,190 @@ +import pygame +import numpy as np +import copy +import os + +CELL_SIZE = 100 +GRID_SIZE = 4 +MARGIN = 10 +HEADER_HEIGHT = 80 +WINDOW_SIZE = GRID_SIZE * CELL_SIZE + (GRID_SIZE + 1) * MARGIN +TOTAL_HEIGHT = WINDOW_SIZE + HEADER_HEIGHT + +COLORS = { + 0: (205, 193, 180), + 2: (238, 228, 218), + 4: (237, 224, 200), + 8: (242, 177, 121), + 16: (245, 149, 99), + 32: (246, 124, 95), + 64: (246, 94, 59), + 128: (237, 207, 114), + 256: (237, 204, 97), + 512: (237, 200, 80), + 1024: (237, 197, 63), + 2048: (237, 194, 46), +} + +TEXT_COLORS = { + 2: (119, 110, 101), + 4: (119, 110, 101), +} + +class Game2048: + def __init__(self, screen): + self.screen = screen + self.font_large = pygame.font.SysFont('Arial', 40, bold=True) + self.font_small = pygame.font.SysFont('Arial', 24) + self.font_score = pygame.font.SysFont('Arial', 28, bold=True) + self.reset() + + def reset(self): + self.board = np.zeros((GRID_SIZE, GRID_SIZE), dtype=int) + self.score = 0 + self.win = False + self.game_over = False + self.add_random_tile() + self.add_random_tile() + + def add_random_tile(self): + empty_cells = [] + for i in range(GRID_SIZE): + for j in range(GRID_SIZE): + if self.board[i, j] == 0: + empty_cells.append((i, j)) + if empty_cells: + i, j = empty_cells[np.random.randint(len(empty_cells))] + self.board[i, j] = np.random.choice([2, 2, 2, 4]) + + def move_left(self): + changed = False + for i in range(GRID_SIZE): + row = self.board[i, :] + new_row = [] + merged = [] + for val in row: + if val != 0: + if new_row and new_row[-1] == val and not merged[-1]: + new_row[-1] *= 2 + self.score += new_row[-1] + merged[-1] = True + if new_row[-1] == 2048: + self.win = True + else: + new_row.append(val) + merged.append(False) + new_row += [0] * (GRID_SIZE - len(new_row)) + if not np.array_equal(row, new_row): + changed = True + self.board[i, :] = new_row + return changed + + def move_right(self): + self.board = self.board[:, ::-1] + changed = self.move_left() + self.board = self.board[:, ::-1] + return changed + + def move_up(self): + self.board = self.board.T + changed = self.move_left() + self.board = self.board.T + return changed + + def move_down(self): + self.board = self.board.T + changed = self.move_right() + self.board = self.board.T + return changed + + def can_move(self): + if (self.board == 0).any(): + return True + for i in range(GRID_SIZE): + for j in range(GRID_SIZE - 1): + if self.board[i, j] == self.board[i, j + 1]: + return True + for i in range(GRID_SIZE - 1): + for j in range(GRID_SIZE): + if self.board[i, j] == self.board[i + 1, j]: + return True + return False + + def handle_input(self, key): + if self.game_over or self.win: + if key == pygame.K_r: + self.reset() + return + + changed = False + if key == pygame.K_LEFT or key == pygame.K_a: + changed = self.move_left() + elif key == pygame.K_RIGHT or key == pygame.K_d: + changed = self.move_right() + elif key == pygame.K_UP or key == pygame.K_w: + changed = self.move_up() + elif key == pygame.K_DOWN or key == pygame.K_s: + changed = self.move_down() + + if changed: + self.add_random_tile() + if not self.can_move(): + self.game_over = True + + def draw(self): + self.screen.fill((250, 248, 239)) + + title = self.font_large.render('2048', True, (119, 110, 101)) + self.screen.blit(title, (MARGIN, 15)) + + score_text = self.font_score.render(f'Score: {self.score}', True, (119, 110, 101)) + self.screen.blit(score_text, (WINDOW_SIZE - score_text.get_width() - MARGIN, 20)) + + hint = self.font_small.render('Arrow Keys/WASD: Move | R: Reset | ESC: Switch Game', True, (150, 150, 150)) + self.screen.blit(hint, (MARGIN, 55)) + + grid_x = MARGIN + grid_y = HEADER_HEIGHT + + pygame.draw.rect(self.screen, (187, 173, 160), + (grid_x, grid_y, WINDOW_SIZE - MARGIN, WINDOW_SIZE - MARGIN), + border_radius=6) + + for i in range(GRID_SIZE): + for j in range(GRID_SIZE): + x = grid_x + MARGIN + j * (CELL_SIZE + MARGIN) + y = grid_y + MARGIN + i * (CELL_SIZE + MARGIN) + value = self.board[i, j] + color = COLORS.get(value, (60, 58, 50)) + pygame.draw.rect(self.screen, color, (x, y, CELL_SIZE, CELL_SIZE), border_radius=6) + + if value != 0: + text_color = TEXT_COLORS.get(value, (255, 255, 255)) + if value >= 1024: + font = pygame.font.SysFont('Arial', 28, bold=True) + elif value >= 128: + font = pygame.font.SysFont('Arial', 32, bold=True) + else: + font = self.font_large + text = font.render(str(value), True, text_color) + text_rect = text.get_rect(center=(x + CELL_SIZE // 2, y + CELL_SIZE // 2)) + self.screen.blit(text, text_rect) + + if self.win: + self.draw_overlay('You Win!', 'Press R to restart') + elif self.game_over: + self.draw_overlay('Game Over!', 'Press R to restart') + + def draw_overlay(self, title, subtitle): + overlay = pygame.Surface((WINDOW_SIZE - MARGIN, WINDOW_SIZE - MARGIN)) + overlay.fill((255, 255, 255)) + overlay.set_alpha(180) + self.screen.blit(overlay, (MARGIN, HEADER_HEIGHT)) + + title_text = self.font_large.render(title, True, (119, 110, 101)) + title_rect = title_text.get_rect(center=(WINDOW_SIZE // 2, HEADER_HEIGHT + (WINDOW_SIZE - HEADER_HEIGHT) // 2 - 20)) + self.screen.blit(title_text, title_rect) + + sub_text = self.font_small.render(subtitle, True, (150, 150, 150)) + sub_rect = sub_text.get_rect(center=(WINDOW_SIZE // 2, HEADER_HEIGHT + (WINDOW_SIZE - HEADER_HEIGHT) // 2 + 20)) + self.screen.blit(sub_text, sub_rect) diff --git a/game_collection/game_snake.py b/game_collection/game_snake.py new file mode 100644 index 0000000..aa9e13d --- /dev/null +++ b/game_collection/game_snake.py @@ -0,0 +1,156 @@ +import pygame +import time +import numpy as np +from pygame.locals import * + +BOARDWIDTH = 48 +BOARDHEIGHT = 28 + +class Food: + def __init__(self): + self.item = (4, 5) + + def _draw(self, screen, i, j): + color = 255, 0, 255 + radius = 10 + width = 10 + position = 10 + 20 * i, 10 + 20 * j + pygame.draw.circle(screen, color, position, radius, width) + + def update(self, screen, enlarge, snack): + if enlarge: + self.item = np.random.randint(1, BOARDWIDTH - 2), np.random.randint(1, BOARDHEIGHT - 2) + while self.item in snack.item: + self.item = np.random.randint(1, BOARDWIDTH - 2), np.random.randint(1, BOARDHEIGHT - 2) + self._draw(screen, self.item[0], self.item[1]) + +class Snack: + def __init__(self): + self.item = [(3, 25), (2, 25), (1, 25), (1, 24)] + self.x = 0 + self.y = -1 + + def move(self, enlarge): + if not enlarge: + self.item.pop() + head = (self.item[0][0] + self.x, self.item[0][1] + self.y) + self.item.insert(0, head) + + def eat_food(self, food): + snack_x, snack_y = self.item[0] + food_x, food_y = food.item + if (food_x == snack_x) and (food_y == snack_y): + return 1 + else: + return 0 + + def toward(self, x, y): + if self.x * x >= 0 and self.y * y >= 0: + self.x = x + self.y = y + + def get_head(self): + return self.item[0] + + def draw(self, screen): + radius = 15 + width = 15 + color = 255, 0, 0 + position = 10 + 20 * self.item[0][0], 10 + 20 * self.item[0][1] + pygame.draw.circle(screen, color, position, radius, width) + radius = 10 + width = 10 + color = 255, 255, 0 + for i, j in self.item[1:]: + position = 10 + 20 * i, 10 + 20 * j + pygame.draw.circle(screen, color, position, radius, width) + +class GameSnake: + def __init__(self, screen): + self.screen = screen + self.font = pygame.font.SysFont('Arial', 20) + self.font_large = pygame.font.SysFont('Arial', 40) + self.reset() + + def reset(self): + self.snack = Snack() + self.food = Food() + self.score = 0 + self.is_fail = False + self.last_move_time = time.time() + self.move_delay = 0.1 + + def init_board(self): + board_width = BOARDWIDTH + board_height = BOARDHEIGHT + color = 10, 255, 255 + width = 0 + for i in range(board_width): + pos = i * 20, 0, 20, 20 + pygame.draw.rect(self.screen, color, pos, width) + pos = i * 20, (board_height - 1) * 20, 20, 20 + pygame.draw.rect(self.screen, color, pos, width) + for i in range(board_height - 1): + pos = 0, 20 + i * 20, 20, 20 + pygame.draw.rect(self.screen, color, pos, width) + pos = (board_width - 1) * 20, 20 + i * 20, 20, 20 + pygame.draw.rect(self.screen, color, pos, width) + + def game_over(self): + broad_x, broad_y = self.snack.get_head() + flag = 0 + old = len(self.snack.item) + new = len(set(self.snack.item)) + if new < old: + flag = 1 + if broad_x == 0 or broad_x == BOARDWIDTH - 1: + flag = 1 + if broad_y == 0 or broad_y == BOARDHEIGHT - 1: + flag = 1 + return flag == 1 + + def print_text(self, x, y, text, color=(255, 0, 0)): + imgText = self.font.render(text, True, color) + self.screen.blit(imgText, (x, y)) + + def handle_input(self, keys): + if keys[K_w] or keys[K_UP]: + self.snack.toward(0, -1) + elif keys[K_s] or keys[K_DOWN]: + self.snack.toward(0, 1) + elif keys[K_a] or keys[K_LEFT]: + self.snack.toward(-1, 0) + elif keys[K_d] or keys[K_RIGHT]: + self.snack.toward(1, 0) + elif keys[K_r]: + self.reset() + + def update(self): + current_time = time.time() + if current_time - self.last_move_time >= self.move_delay: + self.last_move_time = current_time + if not self.is_fail: + enlarge = self.snack.eat_food(self.food) + if enlarge: + self.score += 100 + self.food.update(self.screen, enlarge, self.snack) + self.snack.move(enlarge) + self.is_fail = self.game_over() + + def draw(self): + self.screen.fill((0, 0, 100)) + self.init_board() + + text = f"Score: {self.score} | WASD/Arrows: Move | R: Reset | ESC: Switch Game" + self.print_text(0, 0, text) + + if self.is_fail: + game_over_text = self.font_large.render("GAME OVER", True, (255, 255, 255)) + text_rect = game_over_text.get_rect(center=(BOARDWIDTH * 10, BOARDHEIGHT * 10)) + self.screen.blit(game_over_text, text_rect) + hint_text = self.font.render("Press R to restart", True, (200, 200, 200)) + hint_rect = hint_text.get_rect(center=(BOARDWIDTH * 10, BOARDHEIGHT * 10 + 40)) + self.screen.blit(hint_text, hint_rect) + else: + self.food.update(self.screen, False, self.snack) + self.snack.draw(self.screen) diff --git a/game_collection/main.py b/game_collection/main.py new file mode 100644 index 0000000..19b2ee5 --- /dev/null +++ b/game_collection/main.py @@ -0,0 +1,109 @@ +import pygame +import sys +from pygame.locals import * + +from game_2048 import Game2048 +from game_snake import GameSnake, BOARDWIDTH, BOARDHEIGHT + +SCREEN_WIDTH_2048 = 440 +SCREEN_HEIGHT_2048 = 520 + +SCREEN_WIDTH_SNAKE = BOARDWIDTH * 20 +SCREEN_HEIGHT_SNAKE = BOARDHEIGHT * 20 + +class GameCollection: + def __init__(self): + pygame.init() + self.current_game = 'menu' + self.screen = None + self.game_2048 = None + self.game_snake = None + self.font_large = pygame.font.SysFont('Arial', 48, bold=True) + self.font_medium = pygame.font.SysFont('Arial', 32) + self.font_small = pygame.font.SysFont('Arial', 24) + + def create_screen(self, width, height, title): + screen = pygame.display.set_mode((width, height)) + pygame.display.set_caption(title) + return screen + + def draw_menu(self): + self.screen.fill((250, 248, 239)) + + title = self.font_large.render('Game Collection', True, (119, 110, 101)) + title_rect = title.get_rect(center=(SCREEN_WIDTH_2048 // 2, 100)) + self.screen.blit(title, title_rect) + + games = [ + ('1. 2048', 'Press 1 to play'), + ('2. Snake', 'Press 2 to play'), + ] + + y_pos = 200 + for game_name, hint in games: + name_text = self.font_medium.render(game_name, True, (119, 110, 101)) + name_rect = name_text.get_rect(center=(SCREEN_WIDTH_2048 // 2, y_pos)) + self.screen.blit(name_text, name_rect) + + hint_text = self.font_small.render(hint, True, (150, 150, 150)) + hint_rect = hint_text.get_rect(center=(SCREEN_WIDTH_2048 // 2, y_pos + 35)) + self.screen.blit(hint_text, hint_rect) + + y_pos += 100 + + exit_text = self.font_small.render('Press ESC to exit', True, (150, 150, 150)) + exit_rect = exit_text.get_rect(center=(SCREEN_WIDTH_2048 // 2, y_pos + 50)) + self.screen.blit(exit_text, exit_rect) + + def run(self): + clock = pygame.time.Clock() + + while True: + for event in pygame.event.get(): + if event.type == QUIT: + pygame.quit() + sys.exit() + + if event.type == KEYDOWN: + if event.key == K_ESCAPE: + if self.current_game == 'menu': + pygame.quit() + sys.exit() + else: + self.current_game = 'menu' + self.screen = self.create_screen(SCREEN_WIDTH_2048, SCREEN_HEIGHT_2048, 'Game Collection') + + elif self.current_game == 'menu': + if event.key == K_1: + self.current_game = '2048' + self.screen = self.create_screen(SCREEN_WIDTH_2048, SCREEN_HEIGHT_2048, '2048') + self.game_2048 = Game2048(self.screen) + elif event.key == K_2: + self.current_game = 'snake' + self.screen = self.create_screen(SCREEN_WIDTH_SNAKE, SCREEN_HEIGHT_SNAKE, 'Snake') + self.game_snake = GameSnake(self.screen) + + elif self.current_game == '2048': + self.game_2048.handle_input(event.key) + + elif self.current_game == 'snake': + if event.key == K_r: + self.game_snake.reset() + + if self.current_game == 'menu': + self.screen = self.screen or self.create_screen(SCREEN_WIDTH_2048, SCREEN_HEIGHT_2048, 'Game Collection') + self.draw_menu() + elif self.current_game == '2048': + self.game_2048.draw() + elif self.current_game == 'snake': + keys = pygame.key.get_pressed() + self.game_snake.handle_input(keys) + self.game_snake.update() + self.game_snake.draw() + + pygame.display.update() + clock.tick(60) + +if __name__ == '__main__': + collection = GameCollection() + collection.run()