Акты Становления

851: Пакет/Покерный UI

Покерный UI

Чисто функциональная React UI-библиотека для Движка Покера. Она строго следует парадигме "контролируемого компонента", отображая предоставленное ей состояние игры и отправляя действия обратно потребителю, не содержа при этом никакой бизнес-логики.

Пакет Покерный UI демонстрирует идеальную архитектуру для игровых интерфейсов в современной экосистеме с общим бэкендом. Он предоставляет полный, настраиваемый интерфейс покерного стола, который делегирует всю доменную логику Движку Покера.

Философия

Этот проект обеспечивает строгое разделение между представлением и логикой:

  1. Чистое Представление: UI не содержит никакой игровой логики. Он не вычисляет победителей, не следит за соблюдением правил и не управляет очередностью ходов. Он просто отображает Состояние, предоставленное движком.
  2. Контролируемый Ввод: Следуя паттерну React "контролируемый компонент" (например, <input value={...} onChange={...} />), основной компонент <PokerGame /> принимает свойство state и вызывает onStateChange (или отправляет команды) при взаимодействии с пользователем.
  3. Регистрируемый Движок: Он полностью полагается на Движок Покера для доменной логики, гарантируя, что фронтенд и бэкенд используют один и тот же набор правил.
  4. Готовность к Сервису: Он сразу совместим с Игровым Сервисом, что обеспечивает бесшовную многопользовательскую интеграцию.

Архитектура

Архитектура основана на однонаправленном потоке данных, где бэкенд (или локальный экземпляр движка) является единственным источником истины.

Синхронизирует

Props

Действие пользователя

Применяет

Новое состояние

Игровой Сервис / Бэкенд

Состояние движка

Покерный UI

Команда движка

Такое разделение ответственности открывает ключевые возможности:

  • Оптимистичные Обновления: Действия могут применяться локально для мгновенной обратной связи в ожидании подтверждения от сервера.
  • Повторяемость: В UI можно передать историю состояний, чтобы в точности воспроизвести раздачу.
  • Тестируемость: UI можно тестировать со статическими JSON-фикстурами, не имитируя сложную игровую логику.

Использование

Основной абстракцией является компонент <PokerGame />. Ему требуется объект state (соответствующий интерфейсу State Движка) и обработчик событий.

import React, { useState } from 'react';
import * as Poker from '@idealic/poker-engine';
import { PokerUI } from '@idealic/poker-ui';
import '@idealic/poker-ui/src/style.scss';

const App = () => {
  // 1. Инициализация состояния (обычно с бэкенда)
  const [state, setState] = useState<Poker.State>(initialState);

  const handleStateChange = (newState: Poker.State) => {
    // 2. Оптимистичное обновление локального состояния
    setState(newState);

    // 3. Отправка действия на бэкенд (концептуально)
    // api.sendAction(newState.lastAction);
  };

  return (
    <PokerUI
      state={state}
      onStateChange={handleStateChange}
      author="Player1" // Перспектива для рендеринга (карманные карты и т. д.)
      options={{
        buyIn: 1000,
      }}
    />
  );
};

Интеграция с Игровым Сервисом

При совместном использовании с Игровым Сервисом поток делегирует обработку бэкенду:

// ... внутри вашего компонента
const handleStateChange = async (newState: Poker.State) => {
  // Сервис обрабатывает состояние (проверяя логику, переходя к следующим улицам)
  const processed = await Service.process(newState);
  setState(processed);
};

Кастомизация

UI разработан с учетом расширяемости. Отдельные подкомпоненты можно переопределить через свойство components, сохраняя при этом общую структуру и логику.

import { PokerUI, Card } from '@idealic/poker-ui';

// Пользовательский компонент карты
const MyCustomCard = props => <div className="my-fancy-card">{props.card}</div>;

<PokerUI
  state={state}
  components={{
    Card: MyCustomCard, // Переопределить компонент Card
    // Chips, Player, Board и др. также могут быть переопределены
  }}
/>;

Стилизация

Стили по умолчанию предоставляются в файле src/style.scss и используют BEM-нотацию (например, .poker-player, .poker-card) для упрощения переопределения с помощью стандартного CSS.