Paylines (lines pays)

TL;DR

Игрок «покупает» N паттернов линий через reels (классически 5 reels, 3 rows). Выплата за N подряд одинаковых символов слева направо на любой из активных линий. Wild подставляется. Простейшая и старейшая механика выплат, до сих пор основа большинства классических слотов.

Как работает

Конфиг

  • paytable = {(kind, symbol): payout} — выплата за kind подряд символа.
  • paylines = {line_id: [row_per_reel]} — описание каждой линии: индекс row на каждом reel-е.

Пример из Stake 0_0_lines:

paylines = {
  1:  [0,0,0,0,0],     # верхний горизонт
  2:  [1,1,1,1,1],     # центр
  3:  [2,2,2,2,2],     # нижний
  4:  [0,1,2,1,0],     # V
  5:  [2,1,0,1,2],     # ^
  ...
  20: [1,0,0,0,1],
}
 
paytable = {
  (5, "H1"): 50, (4, "H1"): 20, (3, "H1"): 10,
  (5, "L4"): 2,  (4, "L4"): 0.5, (3, "L4"): 0.2,
  (5, "W"):  50, (4, "W"):  20, (3, "W"):  10,
  ...
}

Алгоритм evaluate

для каждой линии line_id:
  symbols = [board[reel][line[reel]] for reel in 0..num_reels]
  base_sym = first non-wild from symbols
  если все wild — base_sym = "W"
  kind = длина prefix-а из {base_sym, wild}
  win = paytable[(kind, base_sym)] если ключ есть, иначе 0
  если есть wild-only prefix и (kind, "W") в paytable:
    sравнить с (kind, base_sym) и взять больший
  применить multiplier-strategy

Возвращает winData = {totalWin, wins: [{symbol, kind, win, positions, meta: {lineIndex, multiplier, ...}}]}. Каждая выигранная линия — отдельная запись в wins. См. events-as-stream.

Multiplier strategies

Из wins/multiplier_strategy.py:

  • global — общий множитель раунда.
  • symbol — суммируются множители всех wild-символов на линии.
  • combined — global × sum(symbol).

В lines-играх multiplier на wild аддитивный: на линии [W×3, W×2, L4, L4, L4] выигрыш L4 умножается на (3+2) = 5x. (Сравните с ways, где multiplier мультипликативный.)

Мат-эффект

  • RTP контролируется через paytable × probabilities из reelstrip-комбинаций. Стандартная PAR-методология (см. PAR sheet).
  • Hit frequency = доля раундов с хотя бы одной выигрышной линией. На 5×3 поле с 20 линиями типично 25–35% (с учётом LDW).
  • Volatility — функция от paytable hi/lo пирамиды и редкости wild.
  • Combinatorics: каждое из len(reelstrip[reel])^num_reels board-комбинаций оценивается по 20 линиям независимо.

Wild-override — типичная ловушка

Если (3,"W") есть в paytable и платит больше, чем (5,"L4"), то на линии [W,W,W,L4,L4] алгоритм возьмёт 3-kind wild, а не 5-kind L4. Это часто не то, что хочет дизайн.

Warning

Конвенция Stake (и большинства студий): wild платит только на полную линию (5-kind). Соответственно (3,"W") и (4,"W") либо отсутствуют в paytable, либо имеют payout=0. Это резко упрощает балансировку.

Если делаем wild с low-kind платой — нужно явно прописать в spec, как разрешать конфликт, и тестить property-based тестом «на любой board нет линии, где payout < max возможного payout этой линии».

Варианты и подвиды

  • Fixed paylines vs adjustable — игрок выбирает, сколько линий покупать. Сейчас почти не используется (всегда fixed all-lines).
  • Left-to-right vs both-ways (407 ways на 5×3) — обычно left-only.
  • Stacked symbols — на reelstrip-е символы идут блоками, что создаёт волатильные кадры.
  • Wild-расширения: см. wild-symbol (expanding/sticky/walking/stacked/multiplier wilds).

Реализационные заметки

Бэк (TS)

type Payline = number[];                 // index по reel
type Payouts = Map<string, number>;      // ключ "kind:sym" → payout
 
function evaluatePaylines(
  board: SymbolName[][],
  paylines: Record<number, Payline>,
  paytable: Payouts,
  wildSyms: SymbolName[],
  multStrategy: MultiplierStrategy,
): WinData {
  const wins: WinDetail[] = [];
  for (const [lineId, line] of Object.entries(paylines)) {
    const symbolsOnLine = line.map((row, reel) => board[reel][row]);
    const winData = evaluateOneLine(symbolsOnLine, paytable, wildSyms);
    if (winData.win > 0) {
      const total = applyMult(winData, multStrategy, ctx.globalMult);
      wins.push({...winData, lineId, win: total, positions: linePositions(line)});
    }
  }
  return {totalWin: sum(wins, w => w.win), wins};
}

Edge cases

  • Wild-only prefix — все 5 символов wild → (5, "W") payout.
  • Wild на reel 1 — нормально для lines (в отличие от ways).
  • Несколько символов в paytable matching — берём max payout (с учётом wild override).
  • Multiplier × max-win-cap — после применения multiplier проверяем runningBetWin >= maxWin (см. wallet manager).

Фронт

Эмитится winInfo с meta.lineIndex, meta.multiplier, meta.winWithoutMult. Фронт рисует:

  • highlight winning positions;
  • line-trace анимацию;
  • multiplier-badge на wild-символах.

Никакой математики — всё в payload.

Compliance / cert

  • В paytable / help должны быть все возможные комбинации, включая wild-override и multiplier-effects (UKGC RTS 3, см. RTP disclosure).
  • LDW (выплата меньше ставки на all-line bet) — раскрывать как «win less than bet» в UI; см. responsible product design.
  • Лаборатория проверит, что paytable × reelstrip = заявленная RTP (полный цикл PAR sheet).

Примеры реализаций

  • Большинство классических Pragmatic / NetEnt / Microgaming слотов на 5×3 с 10/15/20/25 fixed lines.
  • Stake math-sdk sample game: games/0_0_lines/.

Связанные