{
  "schemaVersion": "2.0.0",
  "scriptFile": "durak52.js",
  "gameInfo": {
    "id": "durak52",
    "name": "Durak (52-Card)",
    "description": "Standard Russian Durak on a full 52-card deck (2-A) instead of 36. Hand size 6, trump turned up from the stock. Attack, defend, throw in, transfer. Last player with cards is the Durak (fool)!",
    "version": 2,
    "author": "GameTwo",
    "category": "shedding",
    "minPlayers": 2,
    "maxPlayers": 6,
    "tags": [
      "shedding",
      "russian",
      "trump",
      "attack-defend",
      "throw-in",
      "transfer"
    ]
  },
  "playerConfig": {
    "min": 2,
    "max": 6,
    "startingHandSize": 0,
    "perPlayerZones": [
      "hand"
    ],
    "initialState": {
      "score": 0,
      "durakCount": 0
    },
    "allowBots": true
  },
  "zones": {
    "stock": {
      "visibility": "hidden",
      "shared": true,
      "shuffle": true,
      "initialCards": "all",
      "label": "Deck"
    },
    "discard": {
      "visibility": "visible",
      "shared": true,
      "stackOrder": "top",
      "label": "Discard"
    },
    "playArea": {
      "visibility": "visible",
      "shared": true,
      "label": "Table"
    },
    "trumpCard": {
      "visibility": "visible",
      "shared": true,
      "label": "Trump Card"
    },
    "hand": {
      "visibility": "ownerOnly",
      "shared": false,
      "label": "Hand"
    }
  },
  "cards": [
    {
      "id": "S2",
      "name": "2 of Spades",
      "suit": "spades",
      "rank": "2",
      "value": 2,
      "count": 1
    },
    {
      "id": "S3",
      "name": "3 of Spades",
      "suit": "spades",
      "rank": "3",
      "value": 3,
      "count": 1
    },
    {
      "id": "S4",
      "name": "4 of Spades",
      "suit": "spades",
      "rank": "4",
      "value": 4,
      "count": 1
    },
    {
      "id": "S5",
      "name": "5 of Spades",
      "suit": "spades",
      "rank": "5",
      "value": 5,
      "count": 1
    },
    {
      "id": "S6",
      "name": "6 of Spades",
      "suit": "spades",
      "rank": "6",
      "value": 6,
      "count": 1
    },
    {
      "id": "S7",
      "name": "7 of Spades",
      "suit": "spades",
      "rank": "7",
      "value": 7,
      "count": 1
    },
    {
      "id": "S8",
      "name": "8 of Spades",
      "suit": "spades",
      "rank": "8",
      "value": 8,
      "count": 1
    },
    {
      "id": "S9",
      "name": "9 of Spades",
      "suit": "spades",
      "rank": "9",
      "value": 9,
      "count": 1
    },
    {
      "id": "S10",
      "name": "10 of Spades",
      "suit": "spades",
      "rank": "10",
      "value": 10,
      "count": 1
    },
    {
      "id": "SJ",
      "name": "Jack of Spades",
      "suit": "spades",
      "rank": "J",
      "value": 11,
      "count": 1
    },
    {
      "id": "SQ",
      "name": "Queen of Spades",
      "suit": "spades",
      "rank": "Q",
      "value": 12,
      "count": 1
    },
    {
      "id": "SK",
      "name": "King of Spades",
      "suit": "spades",
      "rank": "K",
      "value": 13,
      "count": 1
    },
    {
      "id": "SA",
      "name": "Ace of Spades",
      "suit": "spades",
      "rank": "A",
      "value": 14,
      "count": 1
    },
    {
      "id": "C2",
      "name": "2 of Clubs",
      "suit": "clubs",
      "rank": "2",
      "value": 2,
      "count": 1
    },
    {
      "id": "C3",
      "name": "3 of Clubs",
      "suit": "clubs",
      "rank": "3",
      "value": 3,
      "count": 1
    },
    {
      "id": "C4",
      "name": "4 of Clubs",
      "suit": "clubs",
      "rank": "4",
      "value": 4,
      "count": 1
    },
    {
      "id": "C5",
      "name": "5 of Clubs",
      "suit": "clubs",
      "rank": "5",
      "value": 5,
      "count": 1
    },
    {
      "id": "C6",
      "name": "6 of Clubs",
      "suit": "clubs",
      "rank": "6",
      "value": 6,
      "count": 1
    },
    {
      "id": "C7",
      "name": "7 of Clubs",
      "suit": "clubs",
      "rank": "7",
      "value": 7,
      "count": 1
    },
    {
      "id": "C8",
      "name": "8 of Clubs",
      "suit": "clubs",
      "rank": "8",
      "value": 8,
      "count": 1
    },
    {
      "id": "C9",
      "name": "9 of Clubs",
      "suit": "clubs",
      "rank": "9",
      "value": 9,
      "count": 1
    },
    {
      "id": "C10",
      "name": "10 of Clubs",
      "suit": "clubs",
      "rank": "10",
      "value": 10,
      "count": 1
    },
    {
      "id": "CJ",
      "name": "Jack of Clubs",
      "suit": "clubs",
      "rank": "J",
      "value": 11,
      "count": 1
    },
    {
      "id": "CQ",
      "name": "Queen of Clubs",
      "suit": "clubs",
      "rank": "Q",
      "value": 12,
      "count": 1
    },
    {
      "id": "CK",
      "name": "King of Clubs",
      "suit": "clubs",
      "rank": "K",
      "value": 13,
      "count": 1
    },
    {
      "id": "CA",
      "name": "Ace of Clubs",
      "suit": "clubs",
      "rank": "A",
      "value": 14,
      "count": 1
    },
    {
      "id": "D2",
      "name": "2 of Diamonds",
      "suit": "diamonds",
      "rank": "2",
      "value": 2,
      "count": 1
    },
    {
      "id": "D3",
      "name": "3 of Diamonds",
      "suit": "diamonds",
      "rank": "3",
      "value": 3,
      "count": 1
    },
    {
      "id": "D4",
      "name": "4 of Diamonds",
      "suit": "diamonds",
      "rank": "4",
      "value": 4,
      "count": 1
    },
    {
      "id": "D5",
      "name": "5 of Diamonds",
      "suit": "diamonds",
      "rank": "5",
      "value": 5,
      "count": 1
    },
    {
      "id": "D6",
      "name": "6 of Diamonds",
      "suit": "diamonds",
      "rank": "6",
      "value": 6,
      "count": 1
    },
    {
      "id": "D7",
      "name": "7 of Diamonds",
      "suit": "diamonds",
      "rank": "7",
      "value": 7,
      "count": 1
    },
    {
      "id": "D8",
      "name": "8 of Diamonds",
      "suit": "diamonds",
      "rank": "8",
      "value": 8,
      "count": 1
    },
    {
      "id": "D9",
      "name": "9 of Diamonds",
      "suit": "diamonds",
      "rank": "9",
      "value": 9,
      "count": 1
    },
    {
      "id": "D10",
      "name": "10 of Diamonds",
      "suit": "diamonds",
      "rank": "10",
      "value": 10,
      "count": 1
    },
    {
      "id": "DJ",
      "name": "Jack of Diamonds",
      "suit": "diamonds",
      "rank": "J",
      "value": 11,
      "count": 1
    },
    {
      "id": "DQ",
      "name": "Queen of Diamonds",
      "suit": "diamonds",
      "rank": "Q",
      "value": 12,
      "count": 1
    },
    {
      "id": "DK",
      "name": "King of Diamonds",
      "suit": "diamonds",
      "rank": "K",
      "value": 13,
      "count": 1
    },
    {
      "id": "DA",
      "name": "Ace of Diamonds",
      "suit": "diamonds",
      "rank": "A",
      "value": 14,
      "count": 1
    },
    {
      "id": "H2",
      "name": "2 of Hearts",
      "suit": "hearts",
      "rank": "2",
      "value": 2,
      "count": 1
    },
    {
      "id": "H3",
      "name": "3 of Hearts",
      "suit": "hearts",
      "rank": "3",
      "value": 3,
      "count": 1
    },
    {
      "id": "H4",
      "name": "4 of Hearts",
      "suit": "hearts",
      "rank": "4",
      "value": 4,
      "count": 1
    },
    {
      "id": "H5",
      "name": "5 of Hearts",
      "suit": "hearts",
      "rank": "5",
      "value": 5,
      "count": 1
    },
    {
      "id": "H6",
      "name": "6 of Hearts",
      "suit": "hearts",
      "rank": "6",
      "value": 6,
      "count": 1
    },
    {
      "id": "H7",
      "name": "7 of Hearts",
      "suit": "hearts",
      "rank": "7",
      "value": 7,
      "count": 1
    },
    {
      "id": "H8",
      "name": "8 of Hearts",
      "suit": "hearts",
      "rank": "8",
      "value": 8,
      "count": 1
    },
    {
      "id": "H9",
      "name": "9 of Hearts",
      "suit": "hearts",
      "rank": "9",
      "value": 9,
      "count": 1
    },
    {
      "id": "H10",
      "name": "10 of Hearts",
      "suit": "hearts",
      "rank": "10",
      "value": 10,
      "count": 1
    },
    {
      "id": "HJ",
      "name": "Jack of Hearts",
      "suit": "hearts",
      "rank": "J",
      "value": 11,
      "count": 1
    },
    {
      "id": "HQ",
      "name": "Queen of Hearts",
      "suit": "hearts",
      "rank": "Q",
      "value": 12,
      "count": 1
    },
    {
      "id": "HK",
      "name": "King of Hearts",
      "suit": "hearts",
      "rank": "K",
      "value": 13,
      "count": 1
    },
    {
      "id": "HA",
      "name": "Ace of Hearts",
      "suit": "hearts",
      "rank": "A",
      "value": 14,
      "count": 1
    }
  ],
  "initialState": {
    "turnNumber": 1,
    "roundNumber": 1,
    "_phase": "deal",
    "_trumpSuit": null,
    "_attackerId": null,
    "_defenderId": null,
    "_originalAttacker": null,
    "_attacks": [],
    "_tableRanks": {},
    "_maxAttacks": 6,
    "_boutOver": false,
    "_defenderTook": false,
    "_transferMode": true,
    "_deckEmpty": false,
    "_playersOut": [],
    "_throwInOrder": [],
    "_throwInPos": 0,
    "gameOver": false,
    "winner": null,
    "durak": null,
    "botConfig": {
      "enabled": true,
      "thinkDelayMs": 350,
      "actionDelayMs": 500,
      "mcts": {
        "minDifficulty": 4,
        "iterations": {
          "4": 150,
          "5": 300,
          "6": 500,
          "7": 1000
        },
        "explorationConstant": 1.414,
        "maxTimeMs": 5000
      }
    },
    "_throwInReopen": true,
    "_throwInPasses": 0
  },
  "globalEvents": {
    "onGameStart": [
      {
        "action": "log",
        "message": "Durak (Полный Дурак) — Classic Russian card game. Attack, defend, throw in, transfer. Last with cards is the fool!"
      },
      {
        "action": "jsAction",
        "function": "setupDurak"
      }
    ],
    "onTurnStart": [
      {
        "action": "jsAction",
        "function": "durakTurnStart"
      }
    ],
    "onBotTurn": [
      {
        "action": "jsAction",
        "function": "durakBotTurn"
      }
    ],
    "onCardPlay": [
      {
        "action": "jsAction",
        "function": "durakOnCardPlay",
        "cardId": "{{trigger.cardInstanceId}}",
        "playerId": "{{trigger.playerId}}"
      }
    ],
    "take": [
      {
        "action": "jsAction",
        "function": "durakTake",
        "playerId": "{{trigger.playerId}}"
      }
    ],
    "end_attack": [
      {
        "action": "jsAction",
        "function": "durakEndAttack",
        "playerId": "{{trigger.playerId}}"
      }
    ],
    "pass_throw": [
      {
        "action": "jsAction",
        "function": "durakPassThrow",
        "playerId": "{{trigger.playerId}}"
      }
    ],
    "onTurnEnd": [
      {
        "action": "jsAction",
        "function": "durakTurnEnd"
      }
    ]
  },
  "playValidation": {
    "type": "jsCondition",
    "function": "durakCanPlay"
  },
  "turnOrderRules": {
    "direction": "clockwise",
    "allowReverse": false,
    "allowSkip": false,
    "allowExtraTurns": true
  },
  "winConditions": [
    {
      "id": "durak_found",
      "type": "stateCondition",
      "priority": 100,
      "condition": {
        "type": "gameState",
        "key": "gameOver",
        "equals": true
      },
      "winner": "{{game.winner}}"
    }
  ],
  "ui": {
    "theme": {
      "backgroundColor": "#1a0a0a",
      "accentColor": "#c0392b",
      "textColor": "#ecf0f1",
      "cardBackAsset": "assets/card_background.png"
    },
    "regions": [
      {
        "id": "stock",
        "zone": "stock",
        "piece": "pile",
        "anchor": "overlayTopLeft",
        "faceUp": false,
        "showCount": true,
        "userMovable": true,
        "userResizable": true,
        "userRotatable": false,
        "priority": 20
      },
      {
        "id": "trump",
        "zone": "trumpCard",
        "piece": "single",
        "anchor": "overlayTopLeft",
        "offset": {
          "x": 0.045,
          "y": 0.09
        },
        "faceUp": true,
        "userMovable": true,
        "userResizable": true,
        "userRotatable": true,
        "priority": 5
      },
      {
        "id": "discard",
        "zone": "discard",
        "piece": "pile",
        "anchor": "overlayTopRight",
        "faceUp": true
      },
      {
        "id": "attack",
        "zone": "playArea",
        "piece": "pairedRow",
        "anchor": "centre",
        "pairsWith": "defense",
        "growth": "horizontal",
        "scrollable": true,
        "autoScrollToEnd": true,
        "priority": 90
      },
      {
        "id": "defense",
        "zone": "playArea",
        "piece": "row",
        "anchor": "centre",
        "hidden": true
      },
      {
        "id": "hand",
        "zone": "{player}_hand",
        "piece": "fan",
        "anchor": "perPlayer"
      }
    ],
    "hand": {
      "piece": "fan",
      "orientation": "horizontal",
      "curvature": 0.14,
      "allowUserOrientationToggle": true,
      "sortable": true
    },
    "interaction": {
      "modes": [
        "dragDrop",
        "tapToPlace"
      ],
      "defaultMode": "tapToPlace",
      "ghostPreview": true,
      "autoScrollOnPlace": true,
      "placeHints": {
        "attack": "Tap the attack row to lead or add",
        "defend": "Tap an uncovered attack to cover",
        "throw_in": "Throw in a matching rank, or Pass"
      },
      "playLabels": {
        "attack": "Attack",
        "defend": "Cover",
        "throw_in": "Throw in",
        "transfer": "Counterattack",
        "cover": "Cover",
        "cover_or_transfer": "Cover / Counterattack"
      },
      "ghostSlots": {
        "attack": "rowAppend",
        "defend": "pairCover",
        "throw_in": "rowAppend",
        "transfer": "rowAppend",
        "cover": "pairCover",
        "cover_or_transfer": [
          "pairCover",
          "rowAppend"
        ]
      }
    },
    "cards": {
      "cardDefaults": {
        "width": 60,
        "height": 90,
        "borderRadius": 6
      }
    },
    "layout": {
      "mode": "cardsOnly",
      "showInfoBar": true,
      "showPlayerBar": true
    }
  },
  "cardSorting": {
    "options": [
      {
        "id": "trumpHigh",
        "label": "Order cards",
        "property": "trumpThenRank",
        "direction": "asc"
      }
    ],
    "compoundSorts": []
  },
  "invariants": [
    {
      "id": "taker-never-covers",
      "description": "A defender who chose to take must never be seated in defend phase",
      "severity": "critical",
      "appliesWhen": {
        "_phase": "defend",
        "_takePending": true
      },
      "condition": {
        "type": "not",
        "operand": {
          "type": "currentPlayerIs",
          "equals": "$_defenderId"
        }
      },
      "failMessage": "The taker was seated to cover cards they already agreed to pick up"
    },
    {
      "id": "no-cover-after-take",
      "description": "In defend phase, a pending take means the defender seat is vacated for the attacker",
      "severity": "critical",
      "appliesWhen": {
        "_phase": "defend"
      },
      "condition": {
        "type": "implies",
        "if": {
          "type": "gameState",
          "key": "_takePending",
          "equals": true
        },
        "then": {
          "type": "currentPlayerIs",
          "equals": "$_attackerId"
        }
      },
      "failMessage": "Defend phase with a pending take must seat the attacker"
    },
    {
      "id": "taker-owns-thrown-cards",
      "description": "While a take is pending, the pile must still sit on the table — throw-ins count toward what the taker receives when the bout resolves",
      "severity": "critical",
      "appliesWhen": {
        "_takePending": true
      },
      "condition": {
        "type": "zoneHasCard",
        "zone": "playArea",
        "count": 1
      },
      "failMessage": "Take pending but the table pile is gone — thrown cards must reach the taker through resolveTake"
    },
    {
      "id": "defender-always-answers",
      "description": "Defend phase never opens with an empty table — the defender always has attacks to cover or take",
      "severity": "critical",
      "appliesWhen": {
        "_phase": "defend"
      },
      "condition": {
        "type": "zoneHasCard",
        "zone": "playArea",
        "count": 1
      },
      "failMessage": "Defend phase with an empty table — no dead defend states"
    },
    {
      "id": "throw-in-non-defender",
      "description": "The throw-in cursor never seats the defender — the pile they already agreed to take is not theirs to beat",
      "severity": "critical",
      "appliesWhen": {
        "_phase": "throw_in"
      },
      "condition": {
        "type": "not",
        "operand": {
          "type": "currentPlayerIs",
          "equals": "$_defenderId"
        }
      },
      "failMessage": "The defender was seated in the throw-in window"
    },
    {
      "id": "attack-limit-defender-hand",
      "description": "A bout never carries more uncovered attacks than the defender holds cards to answer them, and never more than six attacks total",
      "severity": "critical",
      "appliesWhen": {
        "_phase": "defend"
      },
      "condition": {
        "type": "jsExpression",
        "expression": "dkInvAttackLimit"
      },
      "failMessage": "More cards were thrown at the defender than the defender can possibly cover"
    }
  ]
}
