Имиджборда

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

Треды

Обложка треда
/Help
Автор: pechenie Гл. Администратор Постов: 0 Публичный
/aaa
/aaa
Автор: pechenie Гл. Администратор Постов: 2 Админский

Работает

Аватар
pechenie ✓ Гл. Администратор
08.01.2026 12:58

((123)) test

Аватар
pechenie ✓ Гл. Администратор
23.03.2026 21:12
→ Перейти в тред
Обложка треда
/PRISM
Автор: pechenie Гл. Администратор Постов: 9 Подтвержденные
Аватар
pechenie ✓ Гл. Администратор
12.01.2026 21:54
Аватар
pechenie ✓ Гл. Администратор
21.01.2026 23:33

Имба

Аватар
oxi ✓ Мастер
29.01.2026 18:15
→ Перейти в тред
Обложка треда
/rules
Автор: pechenie Гл. Администратор Постов: 3 Только чтение

Праыила имиджборды и всего сайта

Аватар
pechenie ✓ Гл. Администратор
12.01.2026 22:27

Что незя выкладывать: порнографию и неприятный контент (трупы, смерти и тд). Ссылки на подобное тоже запрещены. Также на сайте запрещено заниматься спамом

Аватар
pechenie ✓ Гл. Администратор
27.01.2026 23:17

Также администрация может удалить ваш пост по другим причинам, с вопросами пишите в /Help

Аватар
pechenie ✓ Гл. Администратор
27.01.2026 23:21
→ Перейти в тред
Обложка треда
/a
Автор: pechenie Гл. Администратор Постов: 6 Публичный

print("Hello world")

Аватар
pechenie ✓ Гл. Администратор
13.01.2026 17:55
Аватар
pechenie ✓ Гл. Администратор
23.02.2026 22:47

Тест

Аватар
pechenie ✓ Гл. Администратор
23.02.2026 22:50
→ Перейти в тред
Обложка треда
/reports
Автор: pechenie Гл. Администратор Постов: 1 Публичный

Если кто-то нарушает правила и тд присылайте сюда id поста и имя юзера

Аватар
pechenie ✓ Гл. Администратор
15.01.2026 17:46
→ Перейти в тред
Обложка треда
/linux
Автор: oxi Мастер Постов: 6 Публичный

Что ?

Аватар
oxi ✓ Мастер
20.03.2026 17:00

Треды ?

Аватар
pechenie ✓ Гл. Администратор
20.03.2026 23:41

нет, просто захотелось создать тред)

Аватар
oxi ✓ Мастер
28.03.2026 13:59
→ Перейти в тред
Обложка треда
/PG2D
Автор: pechenie Гл. Администратор Постов: 0 Публичный
/цитаты
/цитаты
Автор: pechenie Гл. Администратор Постов: 3 Публичный
larpxidized
24.09.2026 15:13
larpxidized
24.09.2026 15:13
larpxidized
24.09.2026 15:14
→ Перейти в тред

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

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

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

Изображение

Ааааа
Ааааааа
Аааа
Ааа
А
Ааааайзек

ID: 121 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

Поздравляю

ID: 120 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

Ух падла, полюбас ВСУ спонсировал

ID: 118 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

<?php
function ensureJsonFileExists($filename, $initialData) {
if (!file_exists($filename)) {
$dir = dirname($filename);
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
}
file_put_contents($filename, json_encode($initialData, JSON_PRETTY_PRINT));
}
}

ensureJsonFileExists('data/users.json', [
[
'id' => 1,
'username' => 'pechenie',
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi'
]
]);
ensureJsonFileExists('data/projects.json', []);
ensureJsonFileExists('data/images.json', []);
ensureJsonFileExists('data/comments.json', []);

session_start();
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Главная - PRISM</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Добро пожаловать на сайт PRISM</h1>
<nav>
<ul>
<li><a href="index.php">Главная</a></li>
<li><a href="projects.php">Проекты</a></li>
<li><a href="my_projects.php">Мои проекты</a></li>
<li><a href="imageboard.php">Имеджборда</a></li>
<li><a href="about.php">О нас</a></li>
<li><a href="events.php">Ивенты</a></li>
<?php
if (isset($_SESSION['username'])) {
if ($_SESSION['username'] === 'pechenie') {
echo '<li><a href="admin.php">Админ-панель</a></li>';
echo '<li><a href="debug.php">Debug</a></li>';
}
echo '<li><a href="my_projects.php">Мои проекты</a></li>';
echo '<li><a href="logout.php">Выйти (' . htmlspecialchars($_SESSION['username']) . ')</a></li>';
} else {
echo '<li><a href="login.php">Войти</a></li>';
echo '<li><a href="register.php">Зарегистрироваться</a></li>';
}
?>
</ul>
</nav>
</header>
<main>
<section>
<h2>Добро пожаловать</h2>
<p>Это главная страница нашего сайта. Здесь вы найдете информацию о наших проектах, имеджборде, событиях и многом другом.</p>

<?php if (isset($_SESSION['username'])): ?>
<div class="welcome-message">
<p>Рады видеть вас, <strong><?php echo htmlspecialchars($_SESSION['username']); ?></strong>!</p>
</div>
<?php else: ?>
<div class="guest-message">
<p>Пожалуйста, <a href="login.php">войдите</a> или <a href="register.php">зарегистрируйтесь</a>, чтобы получить полный доступ к сайту.</p>
</div>
<?php endif; ?>
</section>
</main>
<footer>
<p>© 2024 PRISM. Все права защищены.</p>
</footer>
</body>
</html>

ID: 117 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

/* Общие стили для формы с поддержкой всех браузеров */
:root {
--purple-medium: #9370db;
--purple-light: #b19cd9;
--purple-lighter: #e6e6fa;
--purple-dark: #663399;
--text-light: #ffffff;
}

.form-group {
display: -webkit-box; /* Для старых Safari */
display: -ms-flexbox; /* Для старых IE */
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 1rem;
width: 100%;
}

.form-group label {
font-weight: 600;
color: var(--purple-medium);
font-family: Arial, sans-serif;
}

.form-group input[type="text"],
.form-group input[type="date"],
.form-group textarea {
padding: 0.75rem 1rem;
border: 1px solid var(--purple-lighter);
border-radius: 6px;
font-size: 1rem;
-webkit-transition: all 0.3s ease;
-o-transition: all 0.3s ease;
transition: all 0.3s ease;
font-family: Arial, sans-serif;
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
/* Добавлены фолбэки для старых браузеров */
background-color: #ffffff;
color: #333333;
}

/* Фолбэк для gap в flexbox */
.form-group > *:not(:last-child) {
margin-bottom: 0.5rem;
}

.form-group input[type="text"]:focus,
.form-group input[type="date"]:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--purple-light);
-webkit-box-shadow: 0 0 0 3px rgba(147, 112, 219, 0.2);
box-shadow: 0 0 0 3px rgba(147, 112, 219, 0.2);
}

button {
background-color: var(--purple-medium);
color: var(--text-light);
border: none;
padding: 0.75rem 1.5rem;
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
-webkit-transition: all 0.3s ease;
-o-transition: all 0.3s ease;
transition: all 0.3s ease;
font-family: Arial, sans-serif;
margin-top: 0.5rem;
/* Добавлены фолбэки для transform */
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}

button:hover {
background-color: var(--purple-dark);
-webkit-transform: translateY(-2px);
-ms-transform: translateY(-2px);
transform: translateY(-2px);
-webkit-box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

/* Добавим медиа-запросы для мобильных устройств */
@media (max-width: 768px) {
.form-group input[type="text"],
.form-group input[type="date"],
.form-group textarea {
padding: 0.5rem 0.75rem;
font-size: 0.9rem;
}

button {
padding: 0.5rem 1rem;
font-size: 0.9rem;
}
}

/* Фолбэки для старых браузеров, не поддерживающих CSS переменные */
.form-group label {
color: #9370db; /* фолбэк для var(--purple-medium) */
color: var(--purple-medium);
}

.form-group input[type="text"],
.form-group input[type="date"],
.form-group textarea {
border-color: #e6e6fa; /* фолбэк для var(--purple-lighter) */
border-color: var(--purple-lighter);
}

.form-group input[type="text"]:focus,
.form-group input[type="date"]:focus,
.form-group textarea:focus {
border-color: #b19cd9; /* фолбэк для var(--purple-light) */
border-color: var(--purple-light);
}

button {
background-color: #9370db; /* фолбэк для var(--purple-medium) */
background-color: var(--purple-medium);
color: #ffffff; /* фолбэк для var(--text-light) */
color: var(--text-light);
}

button:hover {
background-color: #663399; /* фолбэк для var(--purple-dark) */
background-color: var(--purple-dark);
}

ID: 116 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

<?php
function ensureJsonFileExists($filename, $initialData) {
if (!file_exists($filename)) {
$dir = dirname($filename);
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
}
file_put_contents($filename, json_encode($initialData, JSON_PRETTY_PRINT));
}
}

ensureJsonFileExists('data/users.json', [['id' => 1, 'username' => 'pechenie', 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi']]);
ensureJsonFileExists('data/projects.json', []);
ensureJsonFileExists('data/images.json', []);
ensureJsonFileExists('data/comments.json', []);
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<link rel="stylesheet" href="style.css">
<meta charset="UTF-8">
<title>Главная</title>
</head>
<body>
<header>
<h1>Добро пожаловать на сайт PRISM</h1>
<nav>
<ul>
<li><a href="index.php">Главная</a></li>
<li><a href="projects.php">Проекты</a></li>
<li><a href="my_projects.php">Мои проекты</a></li>
<li><a href="imageboard.php">Имеджборда</a></li>
<li><a href="about.php">О нас</a></li>
<li><a href="events.php">Ивенты</a></li>
<?php
session_start();
if (isset($_SESSION['username'])) {
if ($_SESSION['username'] === 'pechenie') {
echo '<li><a href="admin.php">Админ-панель</a></li>';
echo '<li><a href="debug.php">Debug</a></li>';
}
echo '<li><a href="my_projects.php">Мои проекты</a></li>';
echo '<li><a href="logout.php">Выйти</a></li>';
} else {
echo '<li><a href="login.php">Войти</a></li>';
echo '<li><a href="register.php">Зарегистрироваться</a></li>';
}
?>
</ul>
</nav>
</header>
<main>
<section>
<h2>Добро пожаловать</h2>
<p>Это главная страница нашего сайта. Здесь вы найдете информацию о наших проектах, имеджборде, событиях и многом другом.</p>
</section>
</main>
</body>
</html>
(index.php)

ID: 115 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

/* Общие стили для формы с поддержкой всех браузеров */
:root {
--purple-medium: #9370db; /* Пример значения */
--purple-light: #b19cd9;
--purple-lighter: #e6e6fa;
--purple-dark: #663399;
--text-light: #ffffff;
}
.form-group {
display: -webkit-box; /* Для старых Safari */
display: -ms-flexbox; /* Для старых IE */
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 1rem;
width: 100%; /* Добавлено для правильного отображения */
}

.form-group label {
font-weight: 600;
color: var(--purple-medium);
font-family: Arial, sans-serif; /* Добавлено для кросс-браузерности */
}

.form-group input[type="text"],
.form-group input[type="date"],
.form-group textarea {
padding: 0.75rem 1rem;
border: 1px solid var(--purple-lighter);
border-radius: 6px;
font-size: 1rem;
-webkit-transition: all 0.3s ease;
-o-transition: all 0.3s ease;
transition: all 0.3s ease;
font-family: Arial, sans-serif; /* Добавлено для кросс-браузерности */
width: 100%; /* Добавлено для правильного отображения */
box-sizing: border-box; /* Важно для правильного расчета ширины */
}

.form-group input[type="text"]:focus,
.form-group input[type="date"]:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--purple-light);
-webkit-box-shadow: 0 0 0 3px rgba(147, 112, 219, 0.2);
box-shadow: 0 0 0 3px rgba(147, 112, 219, 0.2);
}

button {
background-color: var(--purple-medium);
color: var(--text-light);
border: none;
padding: 0.75rem 1.5rem;
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
-webkit-transition: all 0.3s ease;
-o-transition: all 0.3s ease;
transition: all 0.3s ease;
font-family: Arial, sans-serif; /* Добавлено для кросс-браузерности */
margin-top: 0.5rem; /* Добавлено для отступа */
}

button:hover {
background-color: var(--purple-dark);
-webkit-transform: translateY(-2px);
-ms-transform: translateY(-2px);
transform: translateY(-2px);
-webkit-box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

/* Добавим медиа-запросы для мобильных устройств */
@media (max-width: 768px) {
.form-group input[type="text"],
.form-group input[type="date"],
.form-group textarea {
padding: 0.5rem 0.75rem;
}

button {
padding: 0.5rem 1rem;
}
}
(style.css)

ID: 114 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

Браузер не может найти 3дс в Азии, тем временем я в рандомном магазине в Италии

ID: 113 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

Италия ! Сейчас я на пересадке

ID: 112 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

Мощность не прошитого Nintendo Switch

ID: 111 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение
ID: 110 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

TIMUR moment

ID: 109 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

Я получил БАН но зато у меня есть это

ID: 108 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

Tf2 meme

ID: 107 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

Кое что

ID: 106 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

Test2-reg_test

ID: 105 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

BELK GOVNO

ID: 104 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

(Теория) губы и поверапы это тоады которые деградировали. А тоады произошли от грибов

ID: 103 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение

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
Изображение

Цитата дал которой надо задуматься

ID: 101 | [Открыть на весь экран]
Автор: Anonymous
0
+0 / -0
Изображение