Сантехник

import React, { useState, useCallback, useEffect } from ‘react’;
import { RotateCw, Play, RotateCcw, Home, Droplets, Settings, Zap } from ‘lucide-react’;

const PipeMazeGame = () => {
const [currentLevel, setCurrentLevel] = useState(1);
const [gameState, setGameState] = useState(‘playing’); // playing, success, failed
const [grid, setGrid] = useState([]);
const [availablePieces, setAvailablePieces] = useState({});
const [selectedPiece, setSelectedPiece] = useState(null);
const [waterFlow, setWaterFlow] = useState([]);
const [showInstructions, setShowInstructions] = useState(true);

// Типы труб и элементов
const PIPE_TYPES = {
empty: { symbol: ”, connections: [] },
straight_h: { symbol: ‘━’, connections: [‘left’, ‘right’] },
straight_v: { symbol: ‘┃’, connections: [‘top’, ‘bottom’] },
corner_tl: { symbol: ‘┗’, connections: [‘top’, ‘right’] },
corner_tr: { symbol: ‘┛’, connections: [‘top’, ‘left’] },
corner_bl: { symbol: ‘┏’, connections: [‘bottom’, ‘right’] },
corner_br: { symbol: ‘┓’, connections: [‘bottom’, ‘left’] },
cross: { symbol: ‘┼’, connections: [‘top’, ‘bottom’, ‘left’, ‘right’] },
tee_up: { symbol: ‘┻’, connections: [‘top’, ‘left’, ‘right’] },
tee_down: { symbol: ‘┳’, connections: [‘bottom’, ‘left’, ‘right’] },
tee_left: { symbol: ‘┫’, connections: [‘top’, ‘bottom’, ‘left’] },
tee_right: { symbol: ‘┣’, connections: [‘top’, ‘bottom’, ‘right’] },
source: { symbol: ‘?’, connections: [‘bottom’], fixed: true },
sink: { symbol: ‘?’, connections: [‘top’], fixed: true },
pump: { symbol: ‘⚡’, connections: [‘top’, ‘bottom’] },
filter: { symbol: ‘?’, connections: [‘left’, ‘right’] }
};

// Уровни игры
const LEVELS = {
1: {
name: “Первые шаги”,
description: “Подведите воду от дома к душу”,
grid: [
[‘source’, ’empty’, ’empty’, ’empty’],
[’empty’, ’empty’, ’empty’, ’empty’],
[’empty’, ’empty’, ’empty’, ’empty’],
[’empty’, ’empty’, ’empty’, ‘sink’] ],
pieces: {
straight_h: 3,
straight_v: 3,
corner_tl: 1,
corner_tr: 1,
corner_bl: 1,
corner_br: 1
},
maxMoves: 10
},
2: {
name: “Поворот за поворотом”,
description: “Используйте углы для обхода препятствий”,
grid: [
[‘source’, ’empty’, ‘wall’, ’empty’],
[’empty’, ’empty’, ‘wall’, ’empty’],
[’empty’, ’empty’, ’empty’, ’empty’],
[’empty’, ‘wall’, ’empty’, ‘sink’] ],
pieces: {
straight_h: 2,
straight_v: 2,
corner_tl: 2,
corner_tr: 2,
corner_bl: 2,
corner_br: 2
},
maxMoves: 12
},
3: {
name: “Фильтрация”,
description: “Вода должна пройти через фильтр”,
grid: [
[‘source’, ’empty’, ’empty’, ’empty’, ’empty’],
[’empty’, ’empty’, ’empty’, ’empty’, ’empty’],
[’empty’, ’empty’, ‘filter’, ’empty’, ’empty’],
[’empty’, ’empty’, ’empty’, ’empty’, ’empty’],
[’empty’, ’empty’, ’empty’, ’empty’, ‘sink’] ],
pieces: {
straight_h: 4,
straight_v: 4,
corner_tl: 2,
corner_tr: 2,
corner_bl: 2,
corner_br: 2
},
maxMoves: 15
}
};

// Инициализация уровня
const initializeLevel = useCallback((levelNum) => {
const level = LEVELS[levelNum];
if (!level) return;

setGrid(level.grid.map(row => row.map(cell => ({ type: cell, rotation: 0 }))));
setAvailablePieces(level.pieces);
setSelectedPiece(null);
setWaterFlow([]);
setGameState(‘playing’);
}, []);

useEffect(() => {
initializeLevel(currentLevel);
}, [currentLevel, initializeLevel]);

// Проверка соединений
const getConnections = (piece, rotation = 0) => {
const connections = PIPE_TYPES[piece.type]?.connections || [];
const rotations = [‘top’, ‘right’, ‘bottom’, ‘left’];

return connections.map(conn => {
const index = rotations.indexOf(conn);
return rotations[(index + rotation) % 4];
});
};

// Проверка потока воды
const checkWaterFlow = useCallback(() => {
const rows = grid.length;
const cols = grid[0]?.length || 0;

// Найти источник
let sourcePos = null;
for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { if (grid[r][c].type === 'source') { sourcePos = [r, c]; break; } } if (sourcePos) break; } if (!sourcePos) return []; const visited = new Set(); const queue = [sourcePos]; const flow = [sourcePos]; while (queue.length > 0) {
const [r, c] = queue.shift();
const key = `${r},${c}`;

if (visited.has(key)) continue;
visited.add(key);

const currentPiece = grid[r][c];
const connections = getConnections(currentPiece, currentPiece.rotation);

const directions = {
top: [-1, 0],
right: [0, 1],
bottom: [1, 0],
left: [0, -1] };

connections.forEach(direction => {
const [dr, dc] = directions[direction];
const newR = r + dr;
const newC = c + dc;

if (newR >= 0 && newR < rows && newC >= 0 && newC < cols) { const neighborPiece = grid[newR][newC]; if (neighborPiece.type !== 'empty' && neighborPiece.type !== 'wall') { const neighborConnections = getConnections(neighborPiece, neighborPiece.rotation); const oppositeDirection = { top: 'bottom', right: 'left', bottom: 'top', left: 'right' }[direction]; if (neighborConnections.includes(oppositeDirection)) { const neighborKey = `${newR},${newC}`; if (!visited.has(neighborKey)) { queue.push([newR, newC]); flow.push([newR, newC]); } } } } }); } return flow; }, [grid]); // Размещение элемента на поле const placePiece = (row, col) => {
if (!selectedPiece || gameState !== ‘playing’) return;

const currentCell = grid[row][col];
if (currentCell.type !== ’empty’) return;

if (availablePieces[selectedPiece] <= 0) return; const newGrid = [...grid]; newGrid[row][col] = { type: selectedPiece, rotation: 0 }; setGrid(newGrid); setAvailablePieces(prev => ({
…prev,
[selectedPiece]: prev[selectedPiece] – 1
}));
};

// Поворот элемента
const rotatePiece = (row, col, direction = 1) => {
const piece = grid[row][col];
if (piece.type === ’empty’ || PIPE_TYPES[piece.type]?.fixed) return;

const newGrid = […grid];
newGrid[row][col] = {
…piece,
rotation: (piece.rotation + direction + 4) % 4
};
setGrid(newGrid);
};

// Запуск воды
const startWaterFlow = () => {
const flow = checkWaterFlow();
setWaterFlow(flow);

// Проверить, достигла ли вода всех потребителей
const sinks = [];
grid.forEach((row, r) => {
row.forEach((cell, c) => {
if (cell.type === ‘sink’) {
sinks.push([r, c]);
}
});
});

const allSinksReached = sinks.every(sink =>
flow.some(([fr, fc]) => fr === sink[0] && fc === sink[1])
);

if (allSinksReached && sinks.length > 0) {
setGameState(‘success’);
} else if (flow.length > 1) {
setGameState(‘failed’);
}
};

// Сброс уровня
const resetLevel = () => {
initializeLevel(currentLevel);
};

// Следующий уровень
const nextLevel = () => {
if (LEVELS[currentLevel + 1]) {
setCurrentLevel(currentLevel + 1);
}
};

// Рендер ячейки сетки
const renderCell = (cell, row, col) => {
const isWaterFlowing = waterFlow.some(([r, c]) => r === row && c === col);
const piece = PIPE_TYPES[cell.type];

let symbol = piece?.symbol || ”;
let bgColor = ‘bg-gray-100’;

if (cell.type === ‘wall’) {
bgColor = ‘bg-gray-800’;
symbol = ‘■’;
} else if (isWaterFlowing) {
bgColor = ‘bg-blue-200′;
} else if (cell.type !== ’empty’) {
bgColor = ‘bg-gray-200’;
}

return (

placePiece(row, col)}
onContextMenu={(e) => {
e.preventDefault();
rotatePiece(row, col);
}}
style={{
transform: `rotate(${cell.rotation * 90}deg)`
}}
>
{symbol}

);
};

if (showInstructions) {
return (

? Водопроводные лабиринты

Добро пожаловать в игру-головоломку про сантехника! Ваша задача – проложить трубы от источника воды до потребителей.

Как играть:

  • • Выберите деталь из панели инструментов
  • • Кликните на пустую ячейку, чтобы установить деталь
  • • Правый клик или кнопка поворота – поворот детали
  • • Нажмите “Пустить воду” для проверки
  • • Вода должна дойти до всех потребителей

Символы:

? – Источник воды
? – Потребитель (душ)
? – Фильтр
⚡ – Насос
━ ┃ – Трубы
■ – Стена

);
}

return (

{/* Заголовок */}

? Водопроводные лабиринты

Уровень {currentLevel}: {LEVELS[currentLevel]?.name}
{LEVELS[currentLevel]?.description}

{/* Игровое поле */}

{grid.map((row, rowIndex) =>
row.map((cell, colIndex) => renderCell(cell, rowIndex, colIndex))
)}

{/* Кнопки управления */}


{/* Статус игры */}
{gameState === ‘success’ && (

? Уровень пройден!

)}

{gameState === ‘failed’ && (

? Вода не достигла цели!
Проверьте соединения труб

)}

{/* Панель инструментов */}

Доступные детали

{Object.entries(availablePieces).map(([pieceType, count]) => (

))}

{selectedPiece && (

Выбрана деталь: {PIPE_TYPES[selectedPiece]?.symbol}
Кликните на пустую ячейку для установки

)}

{/* Дополнительные кнопки */}


);
};

export default PipeMazeGame;