0
Ваша корзина пуста

import React, { useState } from "react";
import { Camera, Server, Shield } from "lucide-react";

// ======= ДАННЫЕ ======= //
const DEVICE_CATALOG = [
  { id: "poe-b-power", name: "I-Pro PoE B Power", icon: Shield },
  { id: "poe-b", name: "I-Pro PoE B", icon: Shield },
  { id: "poe-b-ultra", name: "I-Pro PoE B Ultra", icon: Shield },
  { id: "poe-ultra-mini", name: "I-Pro PoE Ultra Mini", icon: Shield },
  { id: "poe-power-mini", name: "I-Pro PoE Power Mini", icon: Shield },
  { id: "poe-mini", name: "I-Pro PoE Mini", icon: Shield },
  { id: "gigabit-poe-plus", name: "I-Pro GIGABIT PoE+", icon: Shield },
  { id: "gigabit-poe-plus-lsa", name: "I-Pro GIGABIT PoE+ LSA", icon: Shield },
] as const;

const SEGMENTS = [
  { id: "camAF", label: "Камера PoE AF", icon: Camera },
  { id: "camAT", label: "Камера PoE AT", icon: Camera },
  { id: "camBT", label: "Камера PoE BT", icon: Camera },
  { id: "nvr", label: "Регистратор NVR", icon: Server },
  { id: "switchG", label: "Коммутатор Gigabit PoE", icon: Server },
  { id: "switchF", label: "Коммутатор FastEthernet 100M", icon: Server },
  { id: "switchDL", label: "Коммутатор D-Link", icon: Server },
] as const;

// ======= ПОЛНАЯ ТАБЛИЦА СОВМЕСТИМОСТИ (сокращённая вставка) ======= //
const COMPATIBILITY: Record<string, { survive: number; start: number; comment: string }> = {
  // Камера PoE AF
  "camAF_poe-b-power": { survive: 90, start: 1, comment: "не совместима" },
  "camAF_poe-b": { survive: 80, start: 1, comment: "не совместима" },
  "camAF_poe-b-ultra": { survive: 99, start: 1, comment: "не совместима" },
  "camAF_poe-ultra-mini": { survive: 70, start: 1, comment: "не совместима" },
  "camAF_poe-power-mini": { survive: 60, start: 1, comment: "не совместима" },
  "camAF_poe-mini": { survive: 50, start: 1, comment: "не совместима" },
  "camAF_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
  "camAF_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },

  // Камера PoE AT
  "camAT_poe-b-power": { survive: 90, start: 100, comment: "Всё совпадает" },
  "camAT_poe-b": { survive: 80, start: 100, comment: "Всё совпадает" },
  "camAT_poe-b-ultra": { survive: 99, start: 100, comment: "Всё совпадает" },
  "camAT_poe-ultra-mini": { survive: 70, start: 100, comment: "Всё совпадает" },
  "camAT_poe-power-mini": { survive: 60, start: 100, comment: "Всё совпадает" },
  "camAT_poe-mini": { survive: 50, start: 100, comment: "Всё совпадает" },
  "camAT_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
  "camAT_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },

  // Камера PoE BT
  "camBT_poe-b-power": { survive: 90, start: 1, comment: "не совместима" },
  "camBT_poe-b": { survive: 80, start: 1, comment: "не совместима" },
  "camBT_poe-b-ultra": { survive: 99, start: 1, comment: "не совместима" },
  "camBT_poe-ultra-mini": { survive: 70, start: 1, comment: "не совместима" },
  "camBT_poe-power-mini": { survive: 60, start: 1, comment: "не совместима" },
  "camBT_poe-mini": { survive: 50, start: 1, comment: "не совместима" },
  "camBT_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
  "camBT_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },

  // Регистратор NVR
  "nvr_poe-b-power": { survive: 90, start: 1, comment: "не совместима" },
  "nvr_poe-b": { survive: 80, start: 1, comment: "не совместима" },
  "nvr_poe-b-ultra": { survive: 99, start: 1, comment: "не совместима" },
  "nvr_poe-ultra-mini": { survive: 70, start: 1, comment: "не совместима" },
  "nvr_poe-power-mini": { survive: 60, start: 1, comment: "не совместима" },
  "nvr_poe-mini": { survive: 50, start: 1, comment: "не совместима" },
  "nvr_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
  "nvr_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },

  // Коммутатор Gigabit PoE
  "switchG_poe-b-power": { survive: 90, start: 1, comment: "не совместима" },
  "switchG_poe-b": { survive: 80, start: 1, comment: "не совместима" },
  "switchG_poe-b-ultra": { survive: 99, start: 1, comment: "не совместима" },
  "switchG_poe-ultra-mini": { survive: 70, start: 1, comment: "не совместима" },
  "switchG_poe-power-mini": { survive: 60, start: 1, comment: "не совместима" },
  "switchG_poe-mini": { survive: 50, start: 1, comment: "не совместима" },
  "switchG_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
  "switchG_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },

  // Коммутатор FastEthernet
  "switchF_poe-b-power": { survive: 90, start: 100, comment: "Всё совпадает" },
  "switchF_poe-b": { survive: 80, start: 100, comment: "Всё совпадает" },
  "switchF_poe-b-ultra": { survive: 99, start: 100, comment: "Всё совпадает" },
  "switchF_poe-ultra-mini": { survive: 70, start: 100, comment: "Всё совпадает" },
  "switchF_poe-power-mini": { survive: 60, start: 100, comment: "Всё совпадает" },
  "switchF_poe-mini": { survive: 50, start: 100, comment: "Всё совпадает" },
  "switchF_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
  "switchF_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },

  // Коммутатор D-Link
  "switchDL_poe-b-power": { survive: 90, start: 1, comment: "не совместима" },
  "switchDL_poe-b": { survive: 80, start: 1, comment: "не совместима" },
  "switchDL_poe-b-ultra": { survive: 99, start: 1, comment: "не совместима" },
  "switchDL_poe-ultra-mini": { survive: 70, start: 1, comment: "не совместима" },
  "switchDL_poe-power-mini": { survive: 60, start: 1, comment: "не совместима" },
  "switchDL_poe-mini": { survive: 50, start: 1, comment: "не совместима" },
  "switchDL_gigabit-poe-plus": { survive: 90, start: 100, comment: "Всё совпадает" },
  "switchDL_gigabit-poe-plus-lsa": { survive: 99, start: 100, comment: "Всё совпадает" },
};

function pairKey(segId: string, devId: string) {
  return `${segId}_${devId}`;
}

function statusByRoll(segId: string, devId?: string): "alive" | "offline" | "dead" {
  if (!devId) return "dead";
  const entry = COMPATIBILITY[pairKey(segId, devId)];
  if (!entry) return "dead";
  if (Math.random() * 100 > entry.survive) return "dead";
  if (Math.random() * 100 > entry.start) return "offline";
  return "alive";
}

// ======= UI ======= //
function SegmentSlot({ seg, placed, onDrop }: { seg: any; placed?: any; onDrop: (id: string) => void }) {
  const Icon = seg.icon;
  const entry = placed ? COMPATIBILITY[pairKey(seg.id, placed.id)] : null;
  return (
    <div
      className="border border-sky-300 rounded-2xl shadow p-4 bg-gradient-to-br from-white to-slate-50 hover:shadow-md transition"
      onDragOver={(e) => e.preventDefault()}
      onDrop={(e) => {
        e.preventDefault();
        const id = e.dataTransfer.getData("text/plain");
        onDrop(id);
      }}
    >
      <div className="flex items-center gap-2 mb-2 font-semibold text-slate-700">
        <Icon className="h-5 w-5 text-sky-600" /> {seg.label}
      </div>
      {placed ? (
        <div className="text-sm text-slate-800">
          <div className="font-medium text-sky-700">{placed.name}</div>
          {entry ? (
            <div className="mt-2 text-xs space-y-1">
              <div className="flex items-center gap-1"><span className="font-semibold text-sky-700">Шанс выживания:</span><span className="text-slate-800">{entry.survive}%</span></div>
              <div className="flex items-center gap-1"><span className="font-semibold text-sky-700">Шанс запуска:</span><span className="text-slate-800">{entry.start}%</span></div>
              <div className="text-slate-500 italic">{entry.comment}</div>
            </div>
          ) : (
            <div className="mt-1 text-xs text-red-600">Нет данных по совместимости</div>
          )}
        </div>
      ) : (
        <div className="text-xs text-slate-500 italic">Перетащите устройство</div>
      )}
    </div>
  );
}

function DeviceCard({ d }: { d: any }) {
  const Icon = d.icon;
  return (
    <div
      className="rounded-xl p-3 cursor-grab border shadow bg-gradient-to-br from-slate-50 to-slate-100 hover:shadow-md transition"
      draggable
      onDragStart={(e) => e.dataTransfer.setData("text/plain", d.id)}
    >
      <div className="flex items-center gap-2 text-slate-700">
        <Icon className="h-4 w-4 text-sky-600" /> <span className="font-medium">{d.name}</span>
      </div>
    </div>
  );
}

// ======= Слот-машина (синяя тема) ======= //
function SlotMachine({ onWin, mode }: { onWin: (dev: any) => void; mode: "project" | "expert" }) {
  const [spinning, setSpinning] = useState(false);
  const [slots, setSlots] = useState([DEVICE_CATALOG[0], DEVICE_CATALOG[1], DEVICE_CATALOG[2]]);

  const spin = () => {
    if (spinning) return;
    setSpinning(true);

    const winner = mode === "expert"
      ? DEVICE_CATALOG.find((d) => d.id === "gigabit-poe-plus")!
      : DEVICE_CATALOG[Math.floor(Math.random() * DEVICE_CATALOG.length)];

    const seq: typeof DEVICE_CATALOG[number][] = [];
    for (let i = 0; i < 20; i++) seq.push(DEVICE_CATALOG[Math.floor(Math.random() * DEVICE_CATALOG.length)]);
    seq.push(winner);

    let i = 0;
    const timer = setInterval(() => {
      setSlots([seq[i % seq.length], seq[(i + 1) % seq.length], seq[(i + 2) % seq.length]]);
      i++;
      if (i >= seq.length) {
        clearInterval(timer);
        setSpinning(false);
        onWin(winner);
      }
    }, 90);
  };

  return (
    <div className="flex flex-col items-center">
      <div className="bg-slate-900 border-2 border-sky-500 rounded-xl p-4 flex gap-2 w-80 justify-center shadow-inner">
        {slots.map((s, i) => (
          <div key={i} className="w-24 h-20 flex items-center justify-center bg-gradient-to-br from-slate-800 to-slate-600 text-white font-semibold rounded-md shadow">
            {s.name}
          </div>
        ))}
      </div>
      <button onClick={spin} disabled={spinning} className="mt-4 px-6 py-2 bg-sky-600 hover:bg-sky-700 disabled:opacity-60 text-white font-semibold rounded-full shadow border border-sky-300">
        {mode === "expert" ? " Эксперт" : " Проектировщик"}
      </button>
    </div>
  );
}

// ======= ОСНОВНОЙ КОМПОНЕНТ ======= //
export default function GamePrototype() {
  const [inventory, setInventory] = useState<typeof DEVICE_CATALOG[number][]>([]);
  const [placed, setPlaced] = useState<Record<string, typeof DEVICE_CATALOG[number] | undefined>>({});
  const [stormResult, setStormResult] = useState<Record<string, { status: "alive" | "offline" | "dead"; comment?: string }>>({});

  function placeDevice(segId: string, devId: string) {
    const dev = DEVICE_CATALOG.find((d) => d.id === devId);
    if (!dev) return;
    setInventory((inv) => inv.filter((x, idx) => idx !== inv.findIndex((d) => d.id === devId)));
    setPlaced((p) => ({ ...p, [segId]: dev }));
  }

  function startStorm() {
    const res: Record<string, { status: "alive" | "offline" | "dead"; comment?: string }> = {};
    SEGMENTS.forEach((seg) => {
      const dev = placed[seg.id];
      const status = statusByRoll(seg.id, dev?.id);
      const entry = dev ? COMPATIBILITY[pairKey(seg.id, dev.id)] : undefined;
      res[seg.id] = { status, comment: entry?.comment };
    });
    setStormResult(res);
  }

  function resetGame() {
    setInventory([]);
    setPlaced({});
    setStormResult({});
  }

  return (
    <div className="p-6 space-y-6 bg-gradient-to-b from-blue-950 via-slate-950 to-blue-950 min-h-screen text-white">
      <h1 className="text-3xl md:text-4xl font-extrabold text-center text-sky-300 drop-shadow">⚡ Гроза над сетью — слот‑машина</h1>

      <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
        {SEGMENTS.map((seg) => (
          <SegmentSlot key={seg.id} seg={seg} placed={placed[seg.id]} onDrop={(id) => placeDevice(seg.id, id)} />
        ))}
      </div>

      <div className="flex flex-col md:flex-row justify-center items-center gap-8">
        <SlotMachine mode="project" onWin={(dev) => setInventory((inv) => [...inv, dev])} />
        <SlotMachine mode="expert" onWin={(dev) => setInventory((inv) => [...inv, dev])} />
      </div>

      <div className="flex justify-center gap-3">
        <button onClick={startStorm} className="px-6 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-full font-semibold shadow border border-indigo-400">⚡ Запустить грозу</button>
        <button onClick={resetGame} className="px-6 py-2 bg-slate-600 hover:bg-slate-700 text-white rounded-full font-semibold shadow border border-slate-400"> Сбросить</button>
      </div>

      <div className="grid grid-cols-1 md:grid-cols-3 gap-3">
        {inventory.length === 0 && <div className="text-center text-sky-200 italic md:col-span-3">Сыграйте в слот‑машину, чтобы получить устройства</div>}
        {inventory.map((d, i) => (
          <DeviceCard key={`${d.id}-${i}`} d={d} />
        ))}
      </div>

      {Object.keys(stormResult).length > 0 && (
        <div className="mt-6 bg-sky-50 text-slate-900 rounded-xl shadow p-4 border border-sky-300">
          <h2 className="font-semibold mb-3 text-slate-800 text-lg">Результаты грозы</h2>
          <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
            {SEGMENTS.map((seg) => {
              const r = stormResult[seg.id];
              if (!r) return null;
              const badge = r.status === "alive" ? "✅ Работает" : r.status === "offline" ? "⚠️ Не запускается" : "❌ Повреждён";
              return (
                <div key={seg.id} className="flex items-center gap-2 font-medium">
                  <span className="min-w-[180px] text-slate-700">{seg.label}:</span> <span>{badge}</span>
                  {r.comment && <span className="text-sm text-slate-600 ml-2">— {r.comment}</span>}
                </div>
              );
            })}
          </div>
        </div>
      )}
    </div>
  );
}
 

Яндекс.Метрика