Форум

Новое Популярное Изображения Видео Треды

Загрузить изображение, видео, мир или создать тред

Для загрузки необходимо войти.

Лента (Новое)

Изображение
import pygame
import sys
import random
import time
import math
from pygame import gfxdraw
from typing import List, Tuple, Optional, Dict, Any
import json
import os

# Инициализация pygame
pygame.init()
pygame.mixer.init()

# Константы
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 700
BOARD_SIZE = 8
CELL_SIZE = 70
BOARD_OFFSET_X = (SCREEN_WIDTH - BOARD_SIZE * CELL_SIZE) // 2
BOARD_OFFSET_Y = 100
ANIMATION_SPEED = 0.2
FPS = 60

# Цвета
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (34, 139, 34)
DARK_GREEN = (0, 100, 0)
GOLD = (255, 215, 0)
SILVER = (192, 192, 192)
BLUE = (30, 144, 255)
RED = (255, 69, 0)
GRAY = (128, 128, 128)
LIGHT_GRAY = (200, 200, 200)

# Состояния игры
MENU = 0
PLAYING = 1
GAME_OVER = 2
SETTINGS = 3

class Particle:
def __init__(self, x: float, y: float, color: Tuple[int, int, int]):
self.x = x
self.y = y
self.color = color
self.size = random.randint(2, 5)
self.speed = random.uniform(1, 3)
self.angle = random.uniform(0, math.pi * 2)
self.life = random.uniform(0.5, 1.5)
self.age = 0

def update(self, dt: float) -> bool:
self.x += math.cos(self.angle) * self.speed * dt * 60
self.y += math.sin(self.angle) * self.speed * dt * 60
self.age += dt
return self.age < self.life

def draw(self, screen: pygame.Surface):
alpha = max(0, int(255 * (1 - self.age / self.life)))
color = (*self.color[:3], alpha)
pygame.gfxdraw.filled_circle(
screen,
int(self.x),
int(self.y),
int(self.size),
color
)

class Animation:
def __init__(self):
self.particles: List[Particle] = []
self.flip_animations: Dict[Tuple[int, int], float] = {}
self.last_flip_time: float = 0
self.move_indicator_pos: Optional[Tuple[int, int]] = None
self.move_indicator_alpha: int = 0
self.move_indicator_dir: int = 1

def add_flip_animation(self, row: int, col: int):
self.flip_animations[(row, col)] = 0
self.last_flip_time = time.time()

def add_particles(self, x: float, y: float, color: Tuple[int, int, int], count: int = 10):
for _ in range(count):
self.particles.append(Particle(x, y, color))

def update(self, dt: float):
# Обновление частиц
self.particles = [p for p in self.particles if p.update(dt)]

# Обновление анимации переворота
current_time = time.time()
if current_time - self.last_flip_time > ANIMATION_SPEED:
self.flip_animations = {}

# Обновление индикатора хода
if self.move_indicator_pos:
self.move_indicator_alpha += self.move_indicator_dir * 150 * dt
if self.move_indicator_alpha > 255:
self.move_indicator_alpha = 255
self.move_indicator_dir = -1
elif self.move_indicator_alpha < 0:
self.move_indicator_alpha = 0
self.move_indicator_dir = 1

def draw(self, screen: pygame.Surface):
# Рисование частиц
for particle in self.particles:
particle.draw(screen)

# Рисование индикатора хода
if self.move_indicator_pos:
row, col = self.move_indicator_pos
x = BOARD_OFFSET_X + col * CELL_SIZE + CELL_SIZE // 2
y = BOARD_OFFSET_Y + row * CELL_SIZE + CELL_SIZE // 2
radius = int(CELL_SIZE * 0.3)
color = (*BLUE, self.move_indicator_alpha)
pygame.gfxdraw.filled_circle(screen, x, y, radius, color)

class SoundManager:
def __init__(self):
self.sounds: Dict[str, pygame.mixer.Sound] = {}
self.enabled = True
self.load_sounds()

def load_sounds(self):
try:
self.sounds["place"] = pygame.mixer.Sound(os.path.join("sounds", "place.wav"))
self.sounds["flip"] = pygame.mixer.Sound(os.path.join("sounds", "flip.wav"))
self.sounds["win"] = pygame.mixer.Sound(os.path.join("sounds", "win.wav"))
self.sounds["lose"] = pygame.mixer.Sound(os.path.join("sounds", "lose.wav"))
self.sounds["draw"] = pygame.mixer.Sound(os.path.join("sounds", "draw.wav"))
self.sounds["click"] = pygame.mixer.Sound(os.path.join("sounds", "click.wav"))
except:
# Создаем простые звуки программно, если файлы не найдены
self.create_default_sounds()

def create_default_sounds(self):
# Простые звуки для демонстрации
def make_beep(sound: pygame.mixer.Sound, frequency: int, duration: float):
sample_rate = 44100
samples = int(sample_rate * duration)
buffer = numpy.zeros((samples, 2), dtype=numpy.int16)
for i in range(samples):
t = float(i) / sample_rate
val = int(32767 * math.sin(2 * math.pi * frequency * t))
buffer[i][0] = val
buffer[i][1] = val
sound = pygame.mixer.Sound(buffer)
sound.set_volume(0.3)
return sound

try:
import numpy
self.sounds["place"] = make_beep("place", 440, 0.1)
self.sounds["flip"] = make_beep("flip", 220, 0.05)
self.sounds["win"] = make_beep("win", 880, 0.5)
self.sounds["lose"] = make_beep("lose", 110, 0.5)
self.sounds["draw"] = make_beep("draw", 440, 0.3)
self.sounds["click"] = make_beep("click", 660, 0.05)
except:
self.enabled = False

def play(self, sound_name: str):
if self.enabled and sound_name in self.sounds:
self.sounds[sound_name].play()

class ReversiGame:
def __init__(self):
self.board: List[List[Optional[int]]] = [[None for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]
self.current_player: int = 1 # 1 - черные, 2 - белые
self.game_state: int = MENU
self.animation = Animation()
self.sound = SoundManager()
self.player_names = ["Черные", "Белые"]
self.scores = [2, 2]
self.valid_moves: List[Tuple[int, int]] = []
self.game_mode = "pvp" # 'pvp', 'pve', 'eve'
self.ai_difficulty = "medium" # 'easy', 'medium', 'hard'
self.theme = "classic" # 'classic', 'wood', 'modern'
self.board_colors = {
"classic": (GREEN, DARK_GREEN),
"wood": ((160, 82, 45), (139, 69, 19)),
"modern": ((70, 130, 180), (100, 149, 237))
}
self.piece_colors = {
"classic": (BLACK, WHITE),
"wood": ((50, 50, 50), (220, 220, 220)),
"modern": ((30, 30, 30), (240, 240, 240))
}
self.hint_enabled = True
self.undo_stack: List[Dict[str, Any]] = []
self.redo_stack: List[Dict[str, Any]] = []
self.move_history: List[str] = []
self.game_start_time: float = 0
self.game_duration: float = 0
self.player_times = [0, 0]
self.last_move_time: float = 0
self.winner: Optional[int] = None
self.consecutive_passes = 0
self.font_small = pygame.font.SysFont("Arial", 16)
self.font_medium = pygame.font.SysFont("Arial", 24)
self.font_large = pygame.font.SysFont("Arial", 48)
self.load_settings()

def load_settings(self):
try:
with open("reversi_settings.json", "r") as f:
settings = json.load(f)
self.player_names = settings.get("player_names", self.player_names)
self.game_mode = settings.get("game_mode", self.game_mode)
self.ai_difficulty = settings.get("ai_difficulty", self.ai_difficulty)
self.theme = settings.get("theme", self.theme)
self.hint_enabled = settings.get("hint_enabled", self.hint_enabled)
self.sound.enabled = settings.get("sound_enabled", self.sound.enabled)
except:
pass

def save_settings(self):
settings = {
"player_names": self.player_names,
"game_mode": self.game_mode,
"ai_difficulty": self.ai_difficulty,
"theme": self.theme,
"hint_enabled": self.hint_enabled,
"sound_enabled": self.sound.enabled
}
with open("reversi_settings.json", "w") as f:
json.dump(settings, f)

def reset_game(self):
self.board = [[None for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]
self.board[3][3] = 2
self.board[3][4] = 1
self.board[4][3] = 1
self.board[4][4] = 2
self.current_player = 1
self.scores = [2, 2]
self.valid_moves = self.get_valid_moves(self.current_player)
self.undo_stack = []
self.redo_stack = []
self.move_history = []
self.game_start_time = time.time()
self.game_duration = 0
self.player_times = [0, 0]
self.last_move_time = time.time()
self.winner = None
self.consecutive_passes = 0
self.animation = Animation()
self.game_state = PLAYING

def get_valid_moves(self, player: int) -> List[Tuple[int, int]]:
valid_moves = []
for row in range(BOARD_SIZE):
for col in range(BOARD_SIZE):
if self.board[row][col] is None and self.is_valid_move(row, col, player):
valid_moves.append((row, col))
return valid_moves

def is_valid_move(self, row: int, col: int, player: int) -> bool:
if self.board[row][col] is not None:
return False

opponent = 3 - player
directions = [(-1, -1), (-1, 0), (-1, 1),
(0, -1), (0, 1),
(1, -1), (1, 0), (1, 1)]

for dr, dc in directions:
r, c = row + dr, col + dc
if 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and self.board[r][c] == opponent:
r += dr
c += dc
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and self.board[r][c] == opponent:
r += dr
c += dc
if 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and self.board[r][c] == player:
return True
return False

def make_move(self, row: int, col: int) -> bool:
if not self.is_valid_move(row, col, self.current_player):
return False

# Сохраняем состояние для возможного отмены
self.save_state()

self.board[row][col] = self.current_player
self.sound.play("place")
self.animation.add_particles(
BOARD_OFFSET_X + col * CELL_SIZE + CELL_SIZE // 2,
BOARD_OFFSET_Y + row * CELL_SIZE + CELL_SIZE // 2,
self.piece_colors[self.theme][self.current_player - 1],
20
)

opponent = 3 - self.current_player
directions = [(-1, -1), (-1, 0), (-1, 1),
(0, -1), (0, 1),
(1, -1), (1, 0), (1, 1)]
flipped = 0

for dr, dc in directions:
r, c = row + dr, col + dc
to_flip = []
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and self.board[r][c] == opponent:
to_flip.append((r, c))
r += dr
c += dc

if 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and self.board[r][c] == self.current_player and to_flip:
for (fr, fc) in to_flip:
self.board[fr][fc] = self.current_player
self.animation.add_flip_animation(fr, fc)
self.sound.play("flip")
flipped += len(to_flip)

self.scores[self.current_player - 1] += flipped + 1
self.scores[opponent - 1] -= flipped

# Обновляем историю ходов
move_str = f"{'Черные' if self.current_player == 1 else 'Белые'} -> {chr(ord('A') + col)}{row + 1}"
self.move_history.append(move_str)

# Обновляем время игрока
current_time = time.time()
self.player_times[self.current_player - 1] += current_time - self.last_move_time
self.last_move_time = current_time

# Переключаем игрока
self.current_player = opponent
self.valid_moves = self.get_valid_moves(self.current_player)

# Проверяем условия окончания игры
if not self.valid_moves:
self.consecutive_passes += 1
if self.consecutive_passes >= 2:
self.end_game()
else:
# Пропуск хода
self.current_player = opponent
self.valid_moves = self.get_valid_moves(self.current_player)
if not self.valid_moves:
self.end_game()
else:
self.consecutive_passes = 0

# Если следующий ход ИИ, делаем его
if self.game_state == PLAYING and self.game_mode != "pvp":
if (self.game_mode == "pve" and self.current_player == 2) or self.game_mode == "eve":
self.ai_move()

return True

def ai_move(self):
if not self.valid_moves:
return

# Простое ИИ
if self.ai_difficulty == "easy":
row, col = random.choice(self.valid_moves)
elif self.ai_difficulty == "medium":
# Предпочитаем углы и края
best_score = -1
best_moves = []

for row, col in self.valid_moves:
score = 0
# Углы самые ценные
if (row == 0 or row == BOARD_SIZE - 1) and (col == 0 or col == BOARD_SIZE - 1):
score += 10
# Края тоже хороши
elif row == 0 or row == BOARD_SIZE - 1 or col == 0 or col == BOARD_SIZE - 1:
score += 2
# Избегаем клеток рядом с углами (если угол не занят)
for corner_row, corner_col in [(0, 0), (0, BOARD_SIZE-1), (BOARD_SIZE-1, 0), (BOARD_SIZE-1, BOARD_SIZE-1)]:
if self.board[corner_row][corner_col] is None:
if abs(row - corner_row) <= 1 and abs(col - corner_col) <= 1:
score -= 5

if score > best_score:
best_score = score
best_moves = [(row, col)]
elif score == best_score:
best_moves.append((row, col))

row, col = random.choice(best_moves)
else: # hard
# Минимакс с альфа-бета отсечением
def evaluate_board():
# Простая оценка - разница в количестве фишек
return self.scores[1] - self.scores[0] # Белые - Черные

def minimax(depth: int, alpha: float, beta: float, maximizing_player: bool) -> float:
if depth == 0 or not self.valid_moves:
return evaluate_board()

if maximizing_player:
max_eval = -float('inf')
for row, col in self.valid_moves:
# Сохраняем состояние
old_board = [r[:] for r in self.board]
old_scores = self.scores[:]
old_player = self.current_player

# Делаем ход
self.make_move(row, col)
eval = minimax(depth - 1, alpha, beta, False)

# Восстанавливаем состояние
self.board = old_board
self.scores = old_scores
self.current_player = old_player
self.valid_moves = self.get_valid_moves(self.current_player)

max_eval = max(max_eval, eval)
alpha = max(alpha, eval)
if beta <= alpha:
break
return max_eval
else:
min_eval = float('inf')
for row, col in self.valid_moves:
# Сохраняем состояние
old_board = [r[:] for r in self.board]
old_scores = self.scores[:]
old_player = self.current_player

# Делаем ход
self.make_move(row, col)
eval = minimax(depth - 1, alpha, beta, True)

# Восстанавливаем состояние
self.board = old_board
self.scores = old_scores
self.current_player = old_player
self.valid_moves = self.get_valid_moves(self.current_player)

min_eval = min(min_eval, eval)
beta = min(beta, eval)
if beta <= alpha:
break
return min_eval

best_score = -float('inf')
best_moves = []

for row, col in self.valid_moves:
# Сохраняем состояние
old_board = [r[:] for r in self.board]
old_scores = self.scores[:]
old_player = self.current_player

# Делаем ход
self.make_move(row, col)
score = minimax(3, -float('inf'), float('inf'), False)

# Восстанавливаем состояние
self.board = old_board
self.scores = old_scores
self.current_player = old_player
self.valid_moves = self.get_valid_moves(self.current_player)

if score > best_score:
best_score = score
best_moves = [(row, col)]
elif score == best_score:
best_moves.append((row, col))

row, col = random.choice(best_moves)

# Добавляем небольшую задержку для реалистичности
pygame.time.delay(500)
self.make_move(row, col)

def end_game(self):
self.game_state = GAME_OVER
self.game_duration = time.time() - self.game_start_time

if self.scores[0] > self.scores[1]:
self.winner = 1
self.sound.play("win")
elif self.scores[1] > self.scores[0]:
self.winner = 2
self.sound.play("lose")
else:
self.winner = 0
self.sound.play("draw")

def save_state(self):
state = {
"board": [row[:] for row in self.board],
"current_player": self.current_player,
"scores": self.scores[:],
"valid_moves": self.valid_moves[:],
"move_history": self.move_history[:],
"consecutive_passes": self.consecutive_passes
}
self.undo_stack.append(state)
self.redo_stack = []

def undo(self):
if not self.undo_stack:
return

# Сохраняем текущее состояние в стек redo
current_state = {
"board": [row[:] for row in self.board],
"current_player": self.current_player,
"scores": self.scores[:],
"valid_moves": self.valid_moves[:],
"move_history": self.move_history[:],
"consecutive_passes": self.consecutive_passes
}
self.redo_stack.append(current_state)

# Восстанавливаем предыдущее состояние
state = self.undo_stack.pop()
self.board = [row[:] for row in state["board"]]
self.current_player = state["current_player"]
self.scores = state["scores"][:]
self.valid_moves = state["valid_moves"][:]
self.move_history = state["move_history"][:]
self.consecutive_passes = state["consecutive_passes"]

# Обновляем время последнего хода
self.last_move_time = time.time()

# Если игра была окончена, возвращаемся в состояние игры
if self.game_state == GAME_OVER:
self.game_state = PLAYING
self.winner = None

def redo(self):
if not self.redo_stack:
return

# Сохраняем текущее состояние в стек undo
current_state = {
"board": [row[:] for row in self.board],
"current_player": self.current_player,
"scores": self.scores[:],
"valid_moves": self.valid_moves[:],
"move_history": self.move_history[:],
"consecutive_passes": self.consecutive_passes
}
self.undo_stack.append(current_state)

# Восстанавливаем следующее состояние
state = self.redo_stack.pop()
self.board = [row[:] for row in state["board"]]
self.current_player = state["current_player"]
self.scores = state["scores"][:]
self.valid_moves = state["valid_moves"][:]
self.move_history = state["move_history"][:]
self.consecutive_passes = state["consecutive_passes"]

# Обновляем время последнего хода
self.last_move_time = time.time()

# Если игра была окончена, возвращаемся в состояние игры
if self.game_state == GAME_OVER:
self.game_state = PLAYING
self.winner = None

def draw(self, screen: pygame.Surface):
screen.fill(BLACK)

if self.game_state == MENU:
self.draw_menu(screen)
elif self.game_state == PLAYING or self.game_state == GAME_OVER:
self.draw_board(screen)
self.draw_info_panel(screen)

if self.game_state == GAME_OVER:
self.draw_game_over(screen)
elif self.game_state == SETTINGS:
self.draw_settings(screen)

self.animation.draw(screen)

def draw_menu(self, screen: pygame.Surface):
title = self.font_large.render("REVERSI DELUXE", True, GOLD)
screen.blit(title, (SCREEN_WIDTH // 2 - title.get_width() // 2, 100))

# Кнопки
button_y = 250
button_height = 50
button_width = 300
button_margin = 20

# Игрок vs Игрок
pygame.draw.rect(screen, GREEN,
(SCREEN_WIDTH // 2 - button_width // 2, button_y, button_width, button_height))
text = self.font_medium.render("Игрок vs Игрок", True, WHITE)
screen.blit(text, (SCREEN_WIDTH // 2 - text.get_width() // 2, button_y + button_height // 2 - text.get_height() // 2))

button_y += button_height + button_margin

# Игрок vs Компьютер
pygame.draw.rect(screen, GREEN,
(SCREEN_WIDTH // 2 - button_width // 2, button_y, button_width, button_height))
text = self.font_medium.render("Игрок vs Компьютер", True, WHITE)
screen.blit(text, (SCREEN_WIDTH // 2 - text.get_width() // 2, button_y + button_height // 2 - text.get_height() // 2))

button_y += button_height + button_margin

# Компьютер vs Компьютер
pygame.draw.rect(screen, GREEN,
(SCREEN_WIDTH // 2 - button_width // 2, button_y, button_width, button_height))
text = self.font_medium.render("Компьютер vs Компьютер", True, WHITE)
screen.blit(text, (SCREEN_WIDTH // 2 - text.get_width() // 2, button_y + button_height // 2 - text.get_height() // 2))

button_y += button_height + button_margin

# Настройки
pygame.draw.rect(screen, BLUE,
(SCREEN_WIDTH // 2 - button_width // 2, button_y, button_width, button_height))
text = self.font_medium.render("Настройки", True, WHITE)
screen.blit(text, (SCREEN_WIDTH // 2 - text.get_width() // 2, button_y + button_height // 2 - text.get_height() // 2))

button_y += button_height + button_margin

# Выход
pygame.draw.rect(screen, RED,
(SCREEN_WIDTH // 2 - button_width // 2, button_y, button_width, button_height))
text = self.font_medium.render("Выход", True, WHITE)
screen.blit(text, (SCREEN_WIDTH // 2 - text.get_width() // 2, button_y + button_height // 2 - text.get_height() // 2))

def draw_board(self, screen: pygame.Surface):
# Рисуем доску
light, dark = self.board_colors[self.theme]
for row in range(BOARD_SIZE):
for col in range(BOARD_SIZE):
color = light if (row + col) % 2 == 0 else dark
pygame.draw.rect(screen, color,
(BOARD_OFFSET_X + col * CELL_SIZE,
BOARD_OFFSET_Y + row * CELL_SIZE,
CELL_SIZE, CELL_SIZE))

# Рисуем подсказки для допустимых ходов
if self.hint_enabled and self.game_state == PLAYING:
for row, col in self.valid_moves:
x = BOARD_OFFSET_X + col * CELL_SIZE + CELL_SIZE // 2
y = BOARD_OFFSET_Y + row * CELL_SIZE + CELL_SIZE // 2
radius = int(CELL_SIZE * 0.2)
pygame.gfxdraw.filled_circle(screen, x, y, radius, (*BLUE, 100))

# Рисуем фишки
for row in range(BOARD_SIZE):
for col in range(BOARD_SIZE):
if self.board[row][col] is not None:
x = BOARD_OFFSET_X + col * CELL_SIZE + CELL_SIZE // 2
y = BOARD_OFFSET_Y + row * CELL_SIZE + CELL_SIZE // 2
radius = int(CELL_SIZE * 0.4)

# Анимация переворота
if (row, col) in self.animation.flip_animations:
progress = self.animation.flip_animations[(row, col)]
progress = min(1.0, progress + 0.1)
self.animation.flip_animations[(row, col)] = progress

if progress < 1.0:
# Рисуем частично перевернутую фишку
player = self.board[row][col]
opponent = 3 - player

# Определяем, какая часть фишки видна
if progress < 0.5:
# Показываем больше старого цвета
main_color = self.piece_colors[self.theme][opponent - 1]
secondary_color = self.piece_colors[self.theme][player - 1]
ratio = progress / 0.5
else:
# Показываем больше нового цвета
main_color = self.piece_colors[self.theme][player - 1]
secondary_color = self.piece_colors[self.theme][opponent - 1]
ratio = (progress - 0.5) / 0.5

# Рисуем основную часть
pygame.gfxdraw.filled_circle(screen, x, y, radius, main_color)
pygame.gfxdraw.aacircle(screen, x, y, radius, main_color)

# Рисуем переворачивающуюся часть
if progress < 0.5:
# Старый цвет сверху
start_angle = math.pi * (0.5 - ratio)
end_angle = math.pi * (0.5 + ratio)
else:
# Новый цвет сверху
start_angle = math.pi * (1.5 - ratio)
end_angle = math.pi * (1.5 + ratio)

pygame.gfxdraw.pie(screen, x, y, radius,
int(math.degrees(start_angle)),
int(math.degrees(end_angle)),
secondary_color)
continue

# Обычная фишка
player = self.board[row][col]
color = self.piece_colors[self.theme][player - 1]
pygame.gfxdraw.filled_circle(screen, x, y, radius, color)
pygame.gfxdraw.aacircle(screen, x, y, radius, color)

# Эффект "блика" на фишке
highlight_radius = int(radius * 0.6)
highlight_offset = int(radius * 0.3)
highlight_color = (min(color[0] + 50, 255),
min(color[1] + 50, 255),
min(color[2] + 50, 255))
pygame.gfxdraw.filled_circle(screen,
x - highlight_offset,
y - highlight_offset,
highlight_radius,
highlight_color)

# Рисуем сетку
for i in range(BOARD_SIZE + 1):
# Горизонтальные линии
pygame.draw.line(screen, BLACK,
(BOARD_OFFSET_X, BOARD_OFFSET_Y + i * CELL_SIZE),
(BOARD_OFFSET_X + BOARD_SIZE * CELL_SIZE, BOARD_OFFSET_Y + i * CELL_SIZE), 2)
# Вертикальные линии
pygame.draw.line(screen, BLACK,
(BOARD_OFFSET_X + i * CELL_SIZE, BOARD_OFFSET_Y),
(BOARD_OFFSET_X + i * CELL_SIZE, BOARD_OFFSET_Y + BOARD_SIZE * CELL_SIZE), 2)

# Буквы и цифры для обозначения клеток
for i in range(BOARD_SIZE):
# Буквы (A-H)
letter = chr(ord('A') + i)
text = self.font_small.render(letter, True, WHITE)
screen.blit(text, (BOARD_OFFSET_X + i * CELL_SIZE + CELL_SIZE // 2 - text.get_width() // 2,
BOARD_OFFSET_Y + BOARD_SIZE * CELL_SIZE + 5))
# Цифры (1-8)
number = str(i + 1)
text = self.font_small.render(number, True, WHITE)
screen.blit(text, (BOARD_OFFSET_X - 20,
BOARD_OFFSET_Y + i * CELL_SIZE + CELL_SIZE // 2 - text.get_height() // 2))

def draw_info_panel(self, screen: pygame.Surface):
# Панель информации сверху
pygame.draw.rect(screen, DARK_GREEN, (0, 0, SCREEN_WIDTH, BOARD_OFFSET_Y - 10))

# Имена игроков и счет
black_name = self.font_medium.render(f"{self.player_names[0]}: {self.scores[0]}", True, WHITE)
white_name = self.font_medium.render(f"{self.player_names[1]}: {self.scores[1]}", True, WHITE)

screen.blit(black_name, (50, 20))
screen.blit(white_name, (SCREEN_WIDTH - 50 - white_name.get_width(), 20))

# Индикатор текущего игрока
if self.game_state == PLAYING:
indicator_text = self.font_medium.render("Ход:", True, WHITE)
screen.blit(indicator_text, (SCREEN_WIDTH // 2 - indicator_text.get_width() // 2 - 40, 20))

indicator_color = self.piece_colors[self.theme][self.current_player - 1]
pygame.gfxdraw.filled_circle(screen,
SCREEN_WIDTH // 2 + 20,
20 + indicator_text.get_height() // 2,
15,
indicator_color)
pygame.gfxdraw.aacircle(screen,
SCREEN_WIDTH // 2 + 20,
20 + indicator_text.get_height() // 2,
15,
indicator_color)

# Время игры
if self.game_state == PLAYING:
game_time = time.time() - self.game_start_time
else:
game_time = self.game_duration

minutes = int(game_time // 60)
seconds = int(game_time % 60)
time_text = self.font_small.render(f"Время: {minutes:02d}:{seconds:02d}", True, WHITE)
screen.blit(time_text, (SCREEN_WIDTH // 2 - time_text.get_width() // 2, 50))

# Время игроков
if self.game_state == PLAYING:
current_time = time.time()
if self.last_move_time:
self.player_times[self.current_player - 1] += current_time - self.last_move_time
self.last_move_time = current_time

for i in range(2):
minutes = int(self.player_times[i] // 60)
seconds = int(self.player_times[i] % 60)
player_time = self.font_small.render(f"{minutes:02d}:{seconds:02d}", True, WHITE)

if i == 0:
screen.blit(player_time, (50, 50))
else:
screen.blit(player_time, (SCREEN_WIDTH - 50 - player_time.get_width(), 50))

# Кнопки управления
button_width = 100
button_height = 30
button_margin = 10

# Кнопка "Меню"
pygame.draw.rect(screen, BLUE,
(SCREEN_WIDTH - button_width - 20, BOARD_OFFSET_Y + BOARD_SIZE * CELL_SIZE + 20,
button_width, button_height))
menu_text = self.font_small.render("Меню", True, WHITE)
screen.blit(menu_text,
(SCREEN_WIDTH - button_width - 20 + button_width // 2 - menu_text.get_width() // 2,
BOARD_OFFSET_Y + BOARD_SIZE * CELL_SIZE + 20 + button_height // 2 - menu_text.get_height() // 2))

# Кнопка "Отменить"
if self.undo_stack:
pygame.draw.rect(screen, BLUE,
(SCREEN_WIDTH - 2 * button_width - 20 - button_margin,
BOARD_OFFSET_Y + BOARD_SIZE * CELL_SIZE + 20,
button_width, button_height))
undo_text = self.font_small.render("Отменить", True, WHITE)
screen.blit(undo_text,
(SCREEN_WIDTH - 2 * button_width - 20 - button_margin + button_width // 2 - undo_text.get_width() // 2,
BOARD_OFFSET_Y + BOARD_SIZE * CELL_SIZE + 20 + button_height // 2 - undo_text.get_height() // 2))

# Кнопка "Повторить"
if self.redo_stack:
pygame.draw.rect(screen, BLUE,
(SCREEN_WIDTH - 3 * button_width - 20 - 2 * button_margin,
BOARD_OFFSET_Y + BOARD_SIZE * CELL_SIZE + 20,
button_width, button_height))
redo_text = self.font_small.render("Повторить", True, WHITE)
screen.blit(redo_text,
(SCREEN_WIDTH - 3 * button_width - 20 - 2 * button_margin + button_width // 2 - redo_text.get_width() // 2,
BOARD_OFFSET_Y + BOARD_SIZE * CELL_SIZE + 20 + button_height // 2 - redo_text.get_height() // 2))

def draw_game_over(self, screen: pygame.Surface):
# Затемнение экрана
overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 180))
screen.blit(overlay, (0, 0))

# Окно с результатом
result_width = 400
result_height = 200
pygame.draw.rect(screen, DARK_GREEN,
(SCREEN_WIDTH // 2 - result_width // 2,
SCREEN_HEIGHT // 2 - result_height // 2,
result_width, result_height))
pygame.draw.rect(screen, GOLD,
(SCREEN_WIDTH // 2 - result_width // 2,
SCREEN_HEIGHT // 2 - result_height // 2,
result_width, result_height), 3)

# Текст результата
if self.winner == 0:
result_text = self.font_large.render("Ничья!", True, GOLD)
else:
winner_name = self.player_names[self.winner - 1]
result_text = self.font_large.render(f"Победитель: {winner_name}", True, GOLD)

screen.blit(result_text,
(SCREEN_WIDTH // 2 - result_text.get_width() // 2,
SCREEN_HEIGHT // 2 - result_height // 2 + 30))

# Счет
score_text = self.font_medium.render(f"{self.scores[0]} - {self.scores[1]}", True, WHITE)
screen.blit(score_text,
(SCREEN_WIDTH // 2 - score_text.get_width() // 2,
SCREEN_HEIGHT // 2 - score_text.get_height() // 2 + 10))

# Кнопки
button_width = 120
button_height = 40
button_margin = 20

# Кнопка "Новая игра"
pygame.draw.rect(screen, GREEN,
(SCREEN_WIDTH // 2 - button_width - button_margin // 2,
SCREEN_HEIGHT // 2 + result_height // 2 - button_height - 20,
button_width, button_height))
new_game_text = self.font_medium.render("Новая игра", True, WHITE)
screen.blit(new_game_text,
(SCREEN_WIDTH // 2 - button_width - button_margin // 2 + button_width // 2 - new_game_text.get_width() // 2,
SCREEN_HEIGHT // 2 + result_height // 2 - button_height - 20 + button_height // 2 - new_game_text.get_height() // 2))

# Кнопка "Меню"
pygame.draw.rect(screen, BLUE,
(SCREEN_WIDTH // 2 + button_margin // 2,
SCREEN_HEIGHT // 2 + result_height // 2 - button_height - 20,
button_width, button_height))
menu_text = self.font_medium.render("Меню", True, WHITE)
screen.blit(menu_text,
(SCREEN_WIDTH // 2 + button_margin // 2 + button_width // 2 - menu_text.get_width() // 2,
SCREEN_HEIGHT // 2 + result_height // 2 - button_height - 20 + button_height // 2 - menu_text.get_height() // 2))

def draw_settings(self, screen: pygame.Surface):
screen.fill(DARK_GREEN)

title = self.font_large.render("Настройки", True, GOLD)
screen.blit(title, (SCREEN_WIDTH // 2 - title.get_width() // 2, 50))

# Настройки игроков
player_settings_y = 150
setting_margin = 50

# Имя игрока 1
player1_text = self.font_medium.render("Имя игрока 1 (Черные):", True, WHITE)
screen.blit(player1_text, (100, player_settings_y))

pygame.draw.rect(screen, WHITE, (400, player_settings_y, 200, 30), 2)
player1_name = self.font_medium.render(self.player_names[0], True, WHITE)
screen.blit(player1_name, (410, player_settings_y + 2))

# Имя игрока 2
player2_text = self.font_medium.render("Имя игрока 2 (Белые):", True, WHITE)
screen.blit(player2_text, (100, player_settings_y + setting_margin))

pygame.draw.rect(screen, WHITE, (400, player_settings_y + setting_margin, 200, 30), 2)
player2_name = self.font_medium.render(self.player_names[1], True, WHITE)
screen.blit(player2_name, (410, player_settings_y + setting_margin + 2))

# Сложность ИИ
difficulty_text = self.font_medium.render("Сложность ИИ:", True, WHITE)
screen.blit(difficulty_text, (100, player_settings_y + 2 * setting_margin))

difficulty_options = ["Легкая", "Средняя", "Сложная"]
for i, option in enumerate(difficulty_options):
color = GREEN if (i == 0 and self.ai_difficulty == "easy") or \
(i == 1 and self.ai_difficulty == "medium") or \
(i == 2 and self.ai_difficulty == "hard") else BLUE
pygame.draw.rect(screen, color, (400 + i * 70, player_settings_y + 2 * setting_margin, 65, 30))
option_text = self.font_small.render(option, True, WHITE)
screen.blit(option_text, (400 + i * 70 + 32 - option_text.get_width() // 2,
player_settings_y + 2 * setting_margin + 15 - option_text.get_height() // 2))

# Тема
theme_text = self.font_medium.render("Тема:", True, WHITE)
screen.blit(theme_text, (100, player_settings_y + 3 * setting_margin))

theme_options = ["Классика", "Дерево", "Модерн"]
for i, option in enumerate(theme_options):
color = GREEN if (i == 0 and self.theme == "classic") or \
(i == 1 and self.theme == "wood") or \
(i == 2 and self.theme == "modern") else BLUE
pygame.draw.rect(screen, color, (400 + i * 70, player_settings_y + 3 * setting_margin, 65, 30))
option_text = self.font_small.render(option, True, WHITE)
screen.blit(option_text, (400 + i * 70 + 32 - option_text.get_width() // 2,
player_settings_y + 3 * setting_margin + 15 - option_text.get_height() // 2))

# Другие настройки
other_settings_y = player_settings_y + 4 * setting_margin

# Подсказки
hints_text = self.font_medium.render("Показывать подсказки:", True, WHITE)
screen.blit(hints_text, (100, other_settings_y))

hints_color = GREEN if self.hint_enabled else RED
hints_status = "Вкл" if self.hint_enabled else "Выкл"
pygame.draw.rect(screen, hints_color, (400, other_settings_y, 65, 30))
status_text = self.font_small.render(hints_status, True, WHITE)
screen.blit(status_text, (400 + 32 - status_text.get_width() // 2,
other_settings_y + 15 - status_text.get_height() // 2))

# Звук
sound_text = self.font_medium.render("Звук:", True, WHITE)
screen.blit(sound_text, (100, other_settings_y + setting_margin))

sound_color = GREEN if self.sound.enabled else RED
sound_status = "Вкл" if self.sound.enabled else "Выкл"
pygame.draw.rect(screen, sound_color, (400, other_settings_y + setting_margin, 65, 30))
sound_status_text = self.font_small.render(sound_status, True, WHITE)
screen.blit(sound_status_text, (400 + 32 - sound_status_text.get_width() // 2,
other_settings_y + setting_margin + 15 - sound_status_text.get_height() // 2))

# Кнопки
button_width = 150
button_height = 40
button_margin = 20

# Кнопка "Сохранить"
pygame.draw.rect(screen, GREEN,
(SCREEN_WIDTH // 2 - button_width - button_margin // 2,
SCREEN_HEIGHT - 100,
button_width, button_height))
save_text = self.font_medium.render("Сохранить", True, WHITE)
screen.blit(save_text,
(SCREEN_WIDTH // 2 - button_width - button_margin // 2 + button_width // 2 - save_text.get_width() // 2,
SCREEN_HEIGHT - 100 + button_height // 2 - save_text.get_height() // 2))

# Кнопка "Отмена"
pygame.draw.rect(screen, RED,
(SCREEN_WIDTH // 2 + button_margin // 2,
SCREEN_HEIGHT - 100,
button_width, button_height))
cancel_text = self.font_medium.render("Отмена", True, WHITE)
screen.blit(cancel_text,
(SCREEN_WIDTH // 2 + button_margin // 2 + button_width // 2 - cancel_text.get_width() // 2,
SCREEN_HEIGHT - 100 + button_height // 2 - cancel_text.get_height() // 2))

def handle_click(self, pos: Tuple[int, int]):
if self.game_state == MENU:
self.handle_menu_click(pos)
elif self.game_state == PLAYING:
self.handle_game_click(pos)
elif self.game_state == GAME_OVER:
self.handle_game_over_click(pos)
elif self.game_state == SETTINGS:
self.handle_settings_click(pos)

def handle_menu_click(self, pos: Tuple[int, int]):
button_y = 250
button_height = 50
button_width = 300
button_margin = 20

# Проверка кнопок
for i in range(4):
rect = pygame.Rect(SCREEN_WIDTH // 2 - button_width // 2,
button_y + i * (button_height + button_margin),
button_width, button_height)
if rect.collidepoint(pos):
self.sound.play("click")
if i == 0: # PvP
self.game_mode = "pvp"
self.reset_game()
elif i == 1: # PvE
self.game_mode = "pve"
self.reset_game()
elif i == 2: # EvE
self.game_mode = "eve"
self.reset_game()
elif i == 3: # Настройки
self.game_state = SETTINGS

# Кнопка выхода (5-я кнопка)
rect = pygame.Rect(SCREEN_WIDTH // 2 - button_width // 2,
button_y + 4 * (button_height + button_margin),
button_width, button_height)
if rect.collidepoint(pos):
self.sound.play("click")
pygame.quit()
sys.exit()

def handle_game_click(self, pos: Tuple[int, int]):
# Проверка хода на доске
if (BOARD_OFFSET_X <= pos[0] <= BOARD_OFFSET_X + BOARD_SIZE * CELL_SIZE and
BOARD_OFFSET_Y <= pos[1] <= BOARD_OFFSET_Y + BOARD_SIZE * CELL_SIZE):

col = (pos[0] - BOARD_OFFSET_X) // CELL_SIZE
row = (pos[1] - BOARD_OFFSET_Y) // CELL_SIZE

if (row, col) in self.valid_moves:
self.make_move(row, col)

# Проверка кнопок управления
button_width = 100
button_height = 30

# Кнопка "Меню"
menu_rect = pygame.Rect(SCREEN_WIDTH - button_width - 20,
BOARD_OFFSET_Y + BOARD_SIZE * CELL_SIZE + 20,
button_width, button_height)
if menu_rect.collidepoint(pos):
self.sound.play("click")
self.game_state = MENU

# Кнопка "Отменить"
if self.undo_stack:
undo_rect = pygame.Rect(SCREEN_WIDTH - 2 * button_width - 20 - 10,
BOARD_OFFSET_Y + BOARD_SIZE * CELL_SIZE + 20,
button_width, button_height)
if undo_rect.collidepoint(pos):
self.sound.play("click")
self.undo()

# Кнопка "Повторить"
if self.redo_stack:
redo_rect = pygame.Rect(SCREEN_WIDTH - 3 * button_width - 20 - 20,
BOARD_OFFSET_Y + BOARD_SIZE * CELL_SIZE + 20,
button_width, button_height)
if redo_rect.collidepoint(pos):
self.sound.play("click")
self.redo()

def handle_game_over_click(self, pos: Tuple[int, int]):
button_width = 120
button_height = 40
button_margin = 20

# Кнопка "Новая игра"
new_game_rect = pygame.Rect(SCREEN_WIDTH // 2 - button_width - button_margin // 2,
SCREEN_HEIGHT // 2 + 200 // 2 - button_height - 20,
button_width, button_height)
if new_game_rect.collidepoint(pos):
self.sound.play("click")
self.reset_game()

# Кнопка "Меню"
menu_rect = pygame.Rect(SCREEN_WIDTH // 2 + button_margin // 2,
SCREEN_HEIGHT // 2 + 200 // 2 - button_height - 20,
button_width, button_height)
if menu_rect.collidepoint(pos):
self.sound.play("click")
self.game_state = MENU

def handle_settings_click(self, pos: Tuple[int, int]):
player_settings_y = 150
setting_margin = 50
button_width = 65
button_height = 30

# Сложность ИИ
for i in range(3):
difficulty_rect = pygame.Rect(400 + i * 70, player_settings_y + 2 * setting_margin,
button_width, button_height)
if difficulty_rect.collidepoint(pos):
self.sound.play("click")
if i == 0:
self.ai_difficulty = "easy"
elif i == 1:
self.ai_difficulty = "medium"
else:
self.ai_difficulty = "hard"

# Тема
for i in range(3):
theme_rect = pygame.Rect(400 + i * 70, player_settings_y + 3 * setting_margin,
button_width, button_height)
if theme_rect.collidepoint(pos):
self.sound.play("click")
if i == 0:
self.theme = "classic"
elif i == 1:
self.theme = "wood"
else:
self.theme = "modern"

# Подсказки
hints_rect = pygame.Rect(400, player_settings_y + 4 * setting_margin,
button_width, button_height)
if hints_rect.collidepoint(pos):
self.sound.play("click")
self.hint_enabled = not self.hint_enabled

# Звук
sound_rect = pygame.Rect(400, player_settings_y + 5 * setting_margin,
button_width, button_height)
if sound_rect.collidepoint(pos):
self.sound.play("click")
self.sound.enabled = not self.sound.enabled

# Кнопки сохранения/отмены
button_width = 150
button_height = 40
button_margin = 20

# Кнопка "Сохранить"
save_rect = pygame.Rect(SCREEN_WIDTH // 2 - button_width - button_margin // 2,
SCREEN_HEIGHT - 100,
button_width, button_height)
if save_rect.collidepoint(pos):
self.sound.play("click")
self.save_settings()
self.game_state = MENU

# Кнопка "Отмена"
cancel_rect = pygame.Rect(SCREEN_WIDTH // 2 + button_margin // 2,
SCREEN_HEIGHT - 100,
button_width, button_height)
if cancel_rect.collidepoint(pos):
self.sound.play("click")
self.load_settings() # Восстанавливаем старые настройки
self.game_state = MENU

def main():
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Reversi Deluxe")
clock = pygame.time.Clock()

game = ReversiGame()

running = True
while running:
dt = clock.tick(FPS) / 1000.0 # Delta time в секундах

for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # Левая кнопка мыши
game.handle_click(event.pos)

game.animation.update(dt)
game.draw(screen)
pygame.display.flip()

pygame.quit()
sys.exit()

if __name__ == "__main__":
main()
ID: 102 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Добавь это в проекты
Anonymous 22.07.2025 12:53
Что это ?
Anonymous 22.07.2025 12:52
Изображение
Цитата дал которой надо задуматься
ID: 101 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
Есть у вас идеи какую игру нам (команде PRISM) сделать. Для соревнования с друзьями и сингл плеером
ID: 100 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
Сходка Нинтендо боев (старое фото)
ID: 99 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
И dsi ll
Anonymous 21.07.2025 17:41
Не ньюшка xl (на этой фотогрии её нет)
Anonymous 21.07.2025 17:40
у тебыя 2 дс?
Anonymous 21.07.2025 17:21
Изображение
ID: 98 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
Special for TIMUR
ID: 97 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
Сток
ID: 95 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
ИНШАЛАХ
ID: 94 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
ЗДРАСТВУЙТЕ, Я ИСЛАМСКИЙ ТЕРРОРИСТ И Я ################
Anonymous 20.07.2025 20:43
Изображение
Столько весят ВСЕ треды и проекты
ID: 93 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
Доброе утро
ID: 92 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
Imba
ID: 91 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
Ну как так то
ID: 90 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
Киса 😁
ID: 88 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
Tf2 mge
ID: 87 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
Крутой мужик
ID: 86 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
Зря
ID: 85 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
Tf2 mge
ID: 84 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
КТО УДАЛИЛ МОЙ ПОСТ — ГЕЙ
ID: 83 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Сам гей
Anonymous 20.07.2025 13:21
Сам такой
Anonymous 20.07.2025 08:41
Изображение
СРОЧНО НУЖЕН ТЕСТОСТЕРОН
ID: 82 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
Изображение
Да
ID: 81 | [Перейти к посту]
Автор: Anonymous
0
+0 / -0
что бл
Anonymous 24.07.2025 15:55
Изображение
ID скопирован!