831 lines
24 KiB
HTML
831 lines
24 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru" data-theme="auto">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>Кубик Рубика - Игра онлайн</title>
|
||
<link rel="stylesheet" href="design-system/kt-ai-fonts.css">
|
||
<link rel="stylesheet" href="design-system/kt-ai-tokens.css">
|
||
<link rel="stylesheet" href="design-system/kt-ai-components.css">
|
||
<link rel="stylesheet" href="design-system/kt-ai-page.css">
|
||
<link rel="icon" href="design-system/brand/kt-ai-logo.png">
|
||
<style>
|
||
.kt-ai-btn { text-decoration: none; }
|
||
|
||
.game-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: var(--kt-ai-space-6);
|
||
padding: var(--kt-ai-space-6);
|
||
}
|
||
|
||
.scene {
|
||
width: 300px;
|
||
height: 300px;
|
||
perspective: 1200px;
|
||
margin: var(--kt-ai-space-4) 0;
|
||
}
|
||
|
||
.cube {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
transform-style: preserve-3d;
|
||
transform: rotateX(-25deg) rotateY(-35deg);
|
||
transition: transform 0.1s ease-out;
|
||
}
|
||
|
||
.cubie {
|
||
position: absolute;
|
||
width: 96px;
|
||
height: 96px;
|
||
transform-style: preserve-3d;
|
||
}
|
||
|
||
.face {
|
||
position: absolute;
|
||
width: 96px;
|
||
height: 96px;
|
||
border: 3px solid var(--kt-ai-fg-faint);
|
||
border-radius: 8px;
|
||
background: var(--kt-ai-bg);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
backface-visibility: hidden;
|
||
}
|
||
|
||
.face-white { background: #f5f5f5; }
|
||
.face-yellow { background: #fbbf24; }
|
||
.face-red { background: #ef4444; }
|
||
.face-orange { background: #f97316; }
|
||
.face-blue { background: #3b82f6; }
|
||
.face-green { background: #22c55e; }
|
||
|
||
.face-front { transform: rotateY(0deg) translateZ(48px); }
|
||
.face-back { transform: rotateY(180deg) translateZ(48px); }
|
||
.face-right { transform: rotateY(90deg) translateZ(48px); }
|
||
.face-left { transform: rotateY(-90deg) translateZ(48px); }
|
||
.face-top { transform: rotateX(90deg) translateZ(48px); }
|
||
.face-bottom { transform: rotateX(-90deg) translateZ(48px); }
|
||
|
||
.controls {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--kt-ai-space-3);
|
||
justify-content: center;
|
||
max-width: 600px;
|
||
}
|
||
|
||
.control-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: var(--kt-ai-space-2);
|
||
padding: var(--kt-ai-space-3);
|
||
background: var(--kt-ai-bg-soft);
|
||
border-radius: var(--kt-ai-radius-lg);
|
||
}
|
||
|
||
.control-group-title {
|
||
font-size: var(--kt-ai-text-sm);
|
||
color: var(--kt-ai-fg-muted);
|
||
font-weight: var(--kt-ai-weight-medium);
|
||
}
|
||
|
||
.btn-row {
|
||
display: flex;
|
||
gap: var(--kt-ai-space-2);
|
||
}
|
||
|
||
.stats {
|
||
display: flex;
|
||
gap: var(--kt-ai-space-6);
|
||
margin-top: var(--kt-ai-space-4);
|
||
}
|
||
|
||
.stat-item {
|
||
text-align: center;
|
||
}
|
||
|
||
.stat-value {
|
||
font-size: var(--kt-ai-text-2xl);
|
||
font-weight: var(--kt-ai-weight-bold);
|
||
color: var(--kt-ai-primary);
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: var(--kt-ai-text-sm);
|
||
color: var(--kt-ai-fg-muted);
|
||
}
|
||
|
||
.instructions {
|
||
max-width: 600px;
|
||
margin-top: var(--kt-ai-space-6);
|
||
padding: var(--kt-ai-space-4);
|
||
background: var(--kt-ai-bg-soft);
|
||
border-radius: var(--kt-ai-radius-lg);
|
||
}
|
||
|
||
.instructions h3 {
|
||
margin-bottom: var(--kt-ai-space-3);
|
||
color: var(--kt-ai-fg);
|
||
}
|
||
|
||
.instructions ul {
|
||
list-style: none;
|
||
padding: 0;
|
||
}
|
||
|
||
.instructions li {
|
||
padding: var(--kt-ai-space-2) 0;
|
||
color: var(--kt-ai-fg-muted);
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.instructions li strong {
|
||
color: var(--kt-ai-fg);
|
||
}
|
||
|
||
.win-message {
|
||
position: fixed;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
background: var(--kt-ai-card-bg);
|
||
border: 2px solid var(--kt-ai-primary);
|
||
border-radius: var(--kt-ai-radius-2xl);
|
||
padding: var(--kt-ai-space-8);
|
||
text-align: center;
|
||
z-index: 1000;
|
||
display: none;
|
||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||
}
|
||
|
||
.win-message.show {
|
||
display: block;
|
||
animation: popIn 0.3s ease-out;
|
||
}
|
||
|
||
@keyframes popIn {
|
||
from { transform: translate(-50%, -50%) scale(0.8); opacity: 0; }
|
||
to { transform: translate(-50%, -50%) scale(1); opacity: 1; }
|
||
}
|
||
|
||
.win-message h2 {
|
||
color: var(--kt-ai-primary);
|
||
margin-bottom: var(--kt-ai-space-4);
|
||
}
|
||
|
||
.overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0,0,0,0.5);
|
||
z-index: 999;
|
||
display: none;
|
||
}
|
||
|
||
.overlay.show {
|
||
display: block;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="kt-ai-pagehead">
|
||
<div class="kt-ai-wrap kt-ai-pagehead-in">
|
||
<a class="kt-ai-brand" href="#">
|
||
<img src="design-system/brand/kt-ai-logo.png" alt="KT AI" height="22">
|
||
<span>Кубик Рубика</span>
|
||
</a>
|
||
<span class="kt-ai-spacer"></span>
|
||
<a class="kt-ai-btn" data-variant="ghost" href="#instructions">Как играть</a>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="kt-ai-wrap" style="padding-top: var(--kt-ai-space-8); padding-bottom: var(--kt-ai-space-8);">
|
||
<section class="kt-ai-hero" data-align="center" style="padding: var(--kt-ai-space-4) 0;">
|
||
<h1>Собери Кубик Рубика</h1>
|
||
<p class="kt-ai-hero-sub">Классическая 3D головоломка в твоём браузере</p>
|
||
</section>
|
||
|
||
<div class="game-container">
|
||
<div class="scene">
|
||
<div class="cube" id="cube"></div>
|
||
</div>
|
||
|
||
<div class="stats">
|
||
<div class="stat-item">
|
||
<div class="stat-value" id="moves">0</div>
|
||
<div class="stat-label">Ходов</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-value" id="time">00:00</div>
|
||
<div class="stat-label">Время</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="controls">
|
||
<div class="control-group">
|
||
<div class="control-group-title">Горизонтальные</div>
|
||
<div class="btn-row">
|
||
<button class="kt-ai-btn" data-size="sm" onclick="rotateLayer('U', -1)">U</button>
|
||
<button class="kt-ai-btn" data-size="sm" onclick="rotateLayer('D', 1)">D</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-group">
|
||
<div class="control-group-title">Вертикальные</div>
|
||
<div class="btn-row">
|
||
<button class="kt-ai-btn" data-size="sm" onclick="rotateLayer('L', -1)">L</button>
|
||
<button class="kt-ai-btn" data-size="sm" onclick="rotateLayer('R', 1)">R</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-group">
|
||
<div class="control-group-title">Передние/Задние</div>
|
||
<div class="btn-row">
|
||
<button class="kt-ai-btn" data-size="sm" onclick="rotateLayer('F', 1)">F</button>
|
||
<button class="kt-ai-btn" data-size="sm" onclick="rotateLayer('B', -1)">B</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-group">
|
||
<div class="control-group-title">Действия</div>
|
||
<div class="btn-row">
|
||
<button class="kt-ai-btn" data-variant="primary" data-size="sm" onclick="scramble()">Перемешать</button>
|
||
<button class="kt-ai-btn" data-size="sm" onclick="showHint()">Подсказать</button>
|
||
<button class="kt-ai-btn" data-variant="danger" data-size="sm" onclick="resetGame()">Заново</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="instructions" id="instructions">
|
||
<h3>Как играть</h3>
|
||
<ul>
|
||
<li><strong>Вращение кубика:</strong> зажмите левую кнопку мыши на кубике и перетаскивайте</li>
|
||
<li><strong>Вращение слоёв:</strong> используйте кнопки U, D, L, R, F, B</li>
|
||
<li><strong>Цель:</strong> собрать кубик так, чтобы каждая грань была одного цвета</li>
|
||
<li><strong>Обозначения:</strong> U (верх), D (низ), L (лево), R (право), F (перед), B (зад)</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<footer class="kt-ai-wrap kt-ai-pagefoot">
|
||
<span>© 2026 Кубик Рубика</span>
|
||
<span class="kt-ai-spacer"></span>
|
||
<span>Сделано с использованием KT AI Design System</span>
|
||
</footer>
|
||
|
||
<div class="overlay" id="overlay"></div>
|
||
<div class="win-message" id="winMessage">
|
||
<h2>Поздравляем!</h2>
|
||
<p style="margin-bottom: var(--kt-ai-space-4); color: var(--kt-ai-fg-muted);">Вы собрали кубик Рубика!</p>
|
||
<div class="stats" style="justify-content: center; margin-bottom: var(--kt-ai-space-4);">
|
||
<div class="stat-item">
|
||
<div class="stat-value" id="finalMoves">0</div>
|
||
<div class="stat-label">Ходов</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-value" id="finalTime">00:00</div>
|
||
<div class="stat-label">Время</div>
|
||
</div>
|
||
</div>
|
||
<button class="kt-ai-btn" data-variant="primary" onclick="resetGame()">Играть снова</button>
|
||
</div>
|
||
|
||
<script>
|
||
const COLORS = {
|
||
WHITE: 'white',
|
||
YELLOW: 'yellow',
|
||
RED: 'red',
|
||
ORANGE: 'orange',
|
||
BLUE: 'blue',
|
||
GREEN: 'green'
|
||
};
|
||
|
||
const FACES = ['front', 'back', 'right', 'left', 'top', 'bottom'];
|
||
const FACE_COLORS = {
|
||
front: COLORS.RED,
|
||
back: COLORS.ORANGE,
|
||
right: COLORS.BLUE,
|
||
left: COLORS.GREEN,
|
||
top: COLORS.WHITE,
|
||
bottom: COLORS.YELLOW
|
||
};
|
||
|
||
let cube = [];
|
||
let isAnimating = false;
|
||
let moveCount = 0;
|
||
let startTime = null;
|
||
let timerInterval = null;
|
||
let cubeRotationX = -25;
|
||
let cubeRotationY = -35;
|
||
let isDragging = false;
|
||
let lastMouseX = 0;
|
||
let lastMouseY = 0;
|
||
|
||
function createCubie(x, y, z) {
|
||
const cubie = document.createElement('div');
|
||
cubie.className = 'cubie';
|
||
cubie.dataset.x = x;
|
||
cubie.dataset.y = y;
|
||
cubie.dataset.z = z;
|
||
|
||
FACES.forEach(face => {
|
||
const faceEl = document.createElement('div');
|
||
faceEl.className = `face face-${face} face-${FACE_COLORS[face]}`;
|
||
if (face === 'front') faceEl.style.transform = 'rotateY(0deg) translateZ(48px)';
|
||
if (face === 'back') faceEl.style.transform = 'rotateY(180deg) translateZ(48px)';
|
||
if (face === 'right') faceEl.style.transform = 'rotateY(90deg) translateZ(48px)';
|
||
if (face === 'left') faceEl.style.transform = 'rotateY(-90deg) translateZ(48px)';
|
||
if (face === 'top') faceEl.style.transform = 'rotateX(90deg) translateZ(48px)';
|
||
if (face === 'bottom') faceEl.style.transform = 'rotateX(-90deg) translateZ(48px)';
|
||
cubie.appendChild(faceEl);
|
||
});
|
||
|
||
updateCubiePosition(cubie, x, y, z);
|
||
return cubie;
|
||
}
|
||
|
||
function updateCubiePosition(cubie, x, y, z) {
|
||
const offsetX = x * 100;
|
||
const offsetY = -y * 100;
|
||
const offsetZ = z * 100;
|
||
cubie.style.transform = `translate3d(${offsetX}px, ${offsetY}px, ${offsetZ}px)`;
|
||
}
|
||
|
||
function initCube() {
|
||
const cubeEl = document.getElementById('cube');
|
||
cubeEl.innerHTML = '';
|
||
cube = [];
|
||
|
||
for (let x = -1; x <= 1; x++) {
|
||
cube[x + 1] = [];
|
||
for (let y = -1; y <= 1; y++) {
|
||
cube[x + 1][y + 1] = [];
|
||
for (let z = -1; z <= 1; z++) {
|
||
const cubie = createCubie(x, y, z);
|
||
cube[x + 1][y + 1][z + 1] = {
|
||
element: cubie,
|
||
x, y, z,
|
||
colors: { ...FACE_COLORS }
|
||
};
|
||
cubeEl.appendChild(cubie);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function rotateLayer(layer, direction, isScramble = false) {
|
||
if (isAnimating) return;
|
||
isAnimating = true;
|
||
|
||
if (!startTime && !isScramble) startTimer();
|
||
|
||
const affectedCubies = [];
|
||
|
||
for (let x = 0; x < 3; x++) {
|
||
for (let y = 0; y < 3; y++) {
|
||
for (let z = 0; z < 3; z++) {
|
||
let include = false;
|
||
if (layer === 'U' && y === 1) include = true;
|
||
if (layer === 'D' && y === -1) include = true;
|
||
if (layer === 'L' && x === -1) include = true;
|
||
if (layer === 'R' && x === 1) include = true;
|
||
if (layer === 'F' && z === 1) include = true;
|
||
if (layer === 'B' && z === -1) include = true;
|
||
|
||
if (include) {
|
||
affectedCubies.push(cube[x + 1][y + 1][z + 1]);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
const angle = 90 * direction;
|
||
const duration = 300;
|
||
const startTimeAnim = Date.now();
|
||
|
||
function animate() {
|
||
const elapsed = Date.now() - startTimeAnim;
|
||
const progress = Math.min(elapsed / duration, 1);
|
||
const easeProgress = 1 - Math.pow(1 - progress, 3);
|
||
const currentAngle = angle * easeProgress;
|
||
|
||
affectedCubies.forEach(cubie => {
|
||
let newX = cubie.x;
|
||
let newY = cubie.y;
|
||
let newZ = cubie.z;
|
||
|
||
if (layer === 'U' || layer === 'D') {
|
||
const cos = Math.cos(currentAngle * Math.PI / 180);
|
||
const sin = Math.sin(currentAngle * Math.PI / 180);
|
||
newX = Math.round(cubie.x * cos - cubie.z * sin);
|
||
newZ = Math.round(cubie.x * sin + cubie.z * cos);
|
||
} else if (layer === 'L' || layer === 'R') {
|
||
const cos = Math.cos(currentAngle * Math.PI / 180);
|
||
const sin = Math.sin(currentAngle * Math.PI / 180);
|
||
newY = Math.round(cubie.y * cos - cubie.z * sin);
|
||
newZ = Math.round(cubie.y * sin + cubie.z * cos);
|
||
} else if (layer === 'F' || layer === 'B') {
|
||
const cos = Math.cos(currentAngle * Math.PI / 180);
|
||
const sin = Math.sin(currentAngle * Math.PI / 180);
|
||
newX = Math.round(cubie.x * cos - cubie.y * sin);
|
||
newY = Math.round(cubie.x * sin + cubie.y * cos);
|
||
}
|
||
|
||
updateCubiePosition(cubie.element, newX, newY, newZ);
|
||
|
||
const faceElements = cubie.element.querySelectorAll('.face');
|
||
faceElements.forEach(face => {
|
||
face.style.transform = face.style.transform + ` rotate${getRotationAxis(layer)}(${currentAngle}deg)`;
|
||
});
|
||
});
|
||
|
||
if (progress < 1) {
|
||
requestAnimationFrame(animate);
|
||
} else {
|
||
affectedCubies.forEach(cubie => {
|
||
let newX = cubie.x;
|
||
let newY = cubie.y;
|
||
let newZ = cubie.z;
|
||
|
||
if (layer === 'U' || layer === 'D') {
|
||
const temp = newX;
|
||
if (direction > 0) {
|
||
newX = -newZ;
|
||
newZ = temp;
|
||
} else {
|
||
newX = newZ;
|
||
newZ = -temp;
|
||
}
|
||
} else if (layer === 'L' || layer === 'R') {
|
||
const temp = newY;
|
||
if (direction > 0) {
|
||
newY = -newZ;
|
||
newZ = temp;
|
||
} else {
|
||
newY = newZ;
|
||
newZ = -temp;
|
||
}
|
||
} else if (layer === 'F' || layer === 'B') {
|
||
const temp = newX;
|
||
if (direction > 0) {
|
||
newX = -newY;
|
||
newY = temp;
|
||
} else {
|
||
newX = newY;
|
||
newY = -temp;
|
||
}
|
||
}
|
||
|
||
cubie.x = newX;
|
||
cubie.y = newY;
|
||
cubie.z = newZ;
|
||
|
||
const tempColors = { ...cubie.colors };
|
||
if (layer === 'U' || layer === 'D') {
|
||
if (direction > 0) {
|
||
cubie.colors.front = tempColors.right;
|
||
cubie.colors.right = tempColors.back;
|
||
cubie.colors.back = tempColors.left;
|
||
cubie.colors.left = tempColors.front;
|
||
} else {
|
||
cubie.colors.front = tempColors.left;
|
||
cubie.colors.left = tempColors.back;
|
||
cubie.colors.back = tempColors.right;
|
||
cubie.colors.right = tempColors.front;
|
||
}
|
||
}
|
||
|
||
const newFaceElements = cubie.element.querySelectorAll('.face');
|
||
newFaceElements.forEach(face => {
|
||
face.style.transform = '';
|
||
const faceClass = Array.from(face.classList).find(c => c.startsWith('face-'));
|
||
if (faceClass) {
|
||
const faceName = faceClass.replace('face-', '');
|
||
face.className = `face face-${faceName} face-${cubie.colors[faceName]}`;
|
||
if (faceName === 'front') face.style.transform = 'rotateY(0deg) translateZ(48px)';
|
||
if (faceName === 'back') face.style.transform = 'rotateY(180deg) translateZ(48px)';
|
||
if (faceName === 'right') face.style.transform = 'rotateY(90deg) translateZ(48px)';
|
||
if (faceName === 'left') face.style.transform = 'rotateY(-90deg) translateZ(48px)';
|
||
if (faceName === 'top') face.style.transform = 'rotateX(90deg) translateZ(48px)';
|
||
if (faceName === 'bottom') face.style.transform = 'rotateX(-90deg) translateZ(48px)';
|
||
}
|
||
});
|
||
|
||
updateCubiePosition(cubie.element, cubie.x, cubie.y, cubie.z);
|
||
|
||
const oldCubie = cube[cubie.x + 1][cubie.y + 1][cubie.z + 1];
|
||
if (oldCubie && oldCubie !== cubie) {
|
||
const temp = { ...oldCubie };
|
||
cube[cubie.x + 1][cubie.y + 1][cubie.z + 1] = cubie;
|
||
} else {
|
||
cube[cubie.x + 1][cubie.y + 1][cubie.z + 1] = cubie;
|
||
}
|
||
});
|
||
|
||
for (let x = 0; x < 3; x++) {
|
||
for (let y = 0; y < 3; y++) {
|
||
for (let z = 0; z < 3; z++) {
|
||
const cubie = cube[x][y][z];
|
||
if (cubie.x !== x - 1 || cubie.y !== y - 1 || cubie.z !== z - 1) {
|
||
const targetCubie = cube[cubie.x + 1][cubie.y + 1][cubie.z + 1];
|
||
cube[x][y][z] = targetCubie;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
isAnimating = false;
|
||
moveCount++;
|
||
document.getElementById('moves').textContent = moveCount;
|
||
checkWin();
|
||
}
|
||
}
|
||
|
||
animate();
|
||
}
|
||
|
||
function getRotationAxis(layer) {
|
||
if (layer === 'U' || layer === 'D') return 'Y';
|
||
if (layer === 'L' || layer === 'R') return 'X';
|
||
return 'Z';
|
||
}
|
||
|
||
function scramble() {
|
||
if (isAnimating) return;
|
||
|
||
const layers = ['U', 'D', 'L', 'R', 'F', 'B'];
|
||
const moves = 20;
|
||
let currentMove = 0;
|
||
|
||
resetGame();
|
||
moveHistory = [];
|
||
|
||
function nextMove() {
|
||
if (currentMove >= moves) return;
|
||
|
||
const layer = layers[Math.floor(Math.random() * layers.length)];
|
||
const direction = Math.random() > 0.5 ? 1 : -1;
|
||
|
||
moveHistory.push({ layer, direction });
|
||
rotateLayer(layer, direction, true);
|
||
currentMove++;
|
||
|
||
setTimeout(nextMove, 350);
|
||
}
|
||
|
||
nextMove();
|
||
}
|
||
|
||
function startTimer() {
|
||
startTime = Date.now();
|
||
timerInterval = setInterval(() => {
|
||
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
||
const minutes = Math.floor(elapsed / 60).toString().padStart(2, '0');
|
||
const seconds = (elapsed % 60).toString().padStart(2, '0');
|
||
document.getElementById('time').textContent = `${minutes}:${seconds}`;
|
||
}, 1000);
|
||
}
|
||
|
||
function checkWin() {
|
||
let isWin = true;
|
||
|
||
for (let x = 0; x < 3; x++) {
|
||
for (let y = 0; y < 3; y++) {
|
||
for (let z = 0; z < 3; z++) {
|
||
const cubie = cube[x][y][z];
|
||
|
||
if (cubie.x === -1 && cubie.colors.left !== COLORS.GREEN) isWin = false;
|
||
if (cubie.x === 1 && cubie.colors.right !== COLORS.BLUE) isWin = false;
|
||
if (cubie.y === 1 && cubie.colors.top !== COLORS.WHITE) isWin = false;
|
||
if (cubie.y === -1 && cubie.colors.bottom !== COLORS.YELLOW) isWin = false;
|
||
if (cubie.z === 1 && cubie.colors.front !== COLORS.RED) isWin = false;
|
||
if (cubie.z === -1 && cubie.colors.back !== COLORS.ORANGE) isWin = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (isWin && moveCount > 0) {
|
||
if (timerInterval) {
|
||
clearInterval(timerInterval);
|
||
timerInterval = null;
|
||
}
|
||
|
||
document.getElementById('finalMoves').textContent = moveCount;
|
||
document.getElementById('finalTime').textContent = document.getElementById('time').textContent;
|
||
document.getElementById('winMessage').classList.add('show');
|
||
document.getElementById('overlay').classList.add('show');
|
||
}
|
||
}
|
||
|
||
function updateCubeRotation() {
|
||
const cubeEl = document.getElementById('cube');
|
||
cubeEl.style.transform = `rotateX(${cubeRotationX}deg) rotateY(${cubeRotationY}deg)`;
|
||
}
|
||
|
||
const scene = document.querySelector('.scene');
|
||
|
||
scene.addEventListener('mousedown', (e) => {
|
||
if (e.target.closest('.cubie')) {
|
||
isDragging = true;
|
||
lastMouseX = e.clientX;
|
||
lastMouseY = e.clientY;
|
||
scene.style.cursor = 'grabbing';
|
||
}
|
||
});
|
||
|
||
document.addEventListener('mousemove', (e) => {
|
||
if (isDragging) {
|
||
const deltaX = e.clientX - lastMouseX;
|
||
const deltaY = e.clientY - lastMouseY;
|
||
|
||
cubeRotationY += deltaX * 0.5;
|
||
cubeRotationX -= deltaY * 0.5;
|
||
|
||
cubeRotationX = Math.max(-90, Math.min(90, cubeRotationX));
|
||
|
||
updateCubeRotation();
|
||
|
||
lastMouseX = e.clientX;
|
||
lastMouseY = e.clientY;
|
||
}
|
||
});
|
||
|
||
document.addEventListener('mouseup', () => {
|
||
isDragging = false;
|
||
scene.style.cursor = 'grab';
|
||
});
|
||
|
||
scene.addEventListener('mouseleave', () => {
|
||
isDragging = false;
|
||
scene.style.cursor = 'grab';
|
||
});
|
||
|
||
scene.style.cursor = 'grab';
|
||
|
||
document.addEventListener('touchstart', (e) => {
|
||
if (e.target.closest('.cubie')) {
|
||
isDragging = true;
|
||
lastMouseX = e.touches[0].clientX;
|
||
lastMouseY = e.touches[0].clientY;
|
||
}
|
||
}, { passive: true });
|
||
|
||
document.addEventListener('touchmove', (e) => {
|
||
if (isDragging) {
|
||
e.preventDefault();
|
||
const deltaX = e.touches[0].clientX - lastMouseX;
|
||
const deltaY = e.touches[0].clientY - lastMouseY;
|
||
|
||
cubeRotationY += deltaX * 0.5;
|
||
cubeRotationX -= deltaY * 0.5;
|
||
|
||
cubeRotationX = Math.max(-90, Math.min(90, cubeRotationX));
|
||
|
||
updateCubeRotation();
|
||
|
||
lastMouseX = e.touches[0].clientX;
|
||
lastMouseY = e.touches[0].clientY;
|
||
}
|
||
}, { passive: false });
|
||
|
||
document.addEventListener('touchend', () => {
|
||
isDragging = false;
|
||
});
|
||
|
||
initCube();
|
||
|
||
let moveHistory = [];
|
||
let hintTimeout = null;
|
||
|
||
function showHint() {
|
||
const moveNames = {
|
||
'U': { name: 'Верх (U)', axis: 'Y' },
|
||
'D': { name: 'Низ (D)', axis: 'Y' },
|
||
'L': { name: 'Лево (L)', axis: 'X' },
|
||
'R': { name: 'Право (R)', axis: 'X' },
|
||
'F': { name: 'Перед (F)', axis: 'Z' },
|
||
'B': { name: 'Зад (B)', axis: 'Z' }
|
||
};
|
||
|
||
if (moveHistory.length === 0) {
|
||
showHintMessage('Нажмите «Перемешать» и начните решать!', 'info');
|
||
return;
|
||
}
|
||
|
||
const lastMove = moveHistory[moveHistory.length - 1];
|
||
const moveInfo = moveNames[lastMove.layer];
|
||
const directionText = lastMove.direction > 0 ? '→' : '←';
|
||
const message = `Последний ход: ${moveInfo.name} ${directionText}`;
|
||
|
||
showHintMessage(message, 'hint');
|
||
|
||
const cubies = getCubiesForLayer(lastMove.layer);
|
||
highlightCubies(cubies);
|
||
}
|
||
|
||
function getCubiesForLayer(layer) {
|
||
const cubies = [];
|
||
for (let x = 0; x < 3; x++) {
|
||
for (let y = 0; y < 3; y++) {
|
||
for (let z = 0; z < 3; z++) {
|
||
let include = false;
|
||
if (layer === 'U' && cube[x][y][z].y === 1) include = true;
|
||
if (layer === 'D' && cube[x][y][z].y === -1) include = true;
|
||
if (layer === 'L' && cube[x][y][z].x === -1) include = true;
|
||
if (layer === 'R' && cube[x][y][z].x === 1) include = true;
|
||
if (layer === 'F' && cube[x][y][z].z === 1) include = true;
|
||
if (layer === 'B' && cube[x][y][z].z === -1) include = true;
|
||
|
||
if (include) {
|
||
cubies.push(cube[x][y][z]);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return cubies;
|
||
}
|
||
|
||
function highlightCubies(cubies) {
|
||
cubies.forEach(cubie => {
|
||
cubie.element.style.filter = 'brightness(1.3) drop-shadow(0 0 8px var(--kt-ai-primary))';
|
||
});
|
||
|
||
if (hintTimeout) clearTimeout(hintTimeout);
|
||
|
||
hintTimeout = setTimeout(() => {
|
||
cubies.forEach(cubie => {
|
||
cubie.element.style.filter = '';
|
||
});
|
||
}, 2000);
|
||
}
|
||
|
||
function showHintMessage(text, type) {
|
||
let messageEl = document.getElementById('hintMessage');
|
||
|
||
if (!messageEl) {
|
||
messageEl = document.createElement('div');
|
||
messageEl.id = 'hintMessage';
|
||
messageEl.style.cssText = `
|
||
position: fixed;
|
||
top: 100px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
background: var(--kt-ai-card-bg);
|
||
border: 2px solid var(--kt-ai-primary);
|
||
border-radius: var(--kt-ai-radius-lg);
|
||
padding: var(--kt-ai-space-4) var(--kt-ai-space-6);
|
||
z-index: 1001;
|
||
display: none;
|
||
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
||
max-width: 400px;
|
||
text-align: center;
|
||
font-weight: var(--kt-ai-weight-medium);
|
||
`;
|
||
document.body.appendChild(messageEl);
|
||
}
|
||
|
||
messageEl.textContent = text;
|
||
messageEl.style.display = 'block';
|
||
|
||
setTimeout(() => {
|
||
messageEl.style.display = 'none';
|
||
}, 2500);
|
||
}
|
||
|
||
function resetGame() {
|
||
document.getElementById('winMessage').classList.remove('show');
|
||
document.getElementById('overlay').classList.remove('show');
|
||
|
||
moveCount = 0;
|
||
document.getElementById('moves').textContent = '0';
|
||
moveHistory = [];
|
||
|
||
if (timerInterval) {
|
||
clearInterval(timerInterval);
|
||
timerInterval = null;
|
||
}
|
||
startTime = null;
|
||
document.getElementById('time').textContent = '00:00';
|
||
|
||
cubeRotationX = -25;
|
||
cubeRotationY = -35;
|
||
updateCubeRotation();
|
||
|
||
initCube();
|
||
}
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|