{
  "api_version": "v1",
  "documentation_url": "https://venue-seating-wizard.aviramk.com/docs/",
  "summary": "The solver minimizes a penalty. Every term is scaled by 1000 and truncated to an integer so penalty and happiness figures stay comparable between runs.",
  "scaling": { "factor": 1000, "rounding": "truncate" },
  "terms": [
    {
      "id": "group_cohesion",
      "weight": 10,
      "formula": "strength * sqrt(members)",
      "charged_per": "extra table a group is split across",
      "description": "Charged for every table beyond the first that a group occupies, with a credit back when a subgroup sits together with its parent group."
    },
    {
      "id": "seating_preferences",
      "weight": 1,
      "charged_per": "unmet preference",
      "description": "Scaled by the guest's strongest group and by the normalized distance between the two tables, or from the table to the target area rectangle."
    },
    {
      "id": "table_usage",
      "weight": 0.5,
      "charged_per": "table used",
      "description": "A small charge per occupied table, so the venue prefers fewer, fuller tables."
    }
  ],
  "search": {
    "construction": "greedy",
    "improvement": "simulated annealing",
    "neighbourhoods": ["relocate", "swap", "ejection-chain", "group-move"],
    "evaluation": "incremental",
    "capacity": "Intermediate candidates may pass through over-capacity states under a penalty, because in a tightly packed venue the capacity-feasible neighbourhood can be disconnected. Only capacity-feasible arrangements are ever reported.",
    "optimality": "The solver does not prove optimality. It returns a good arrangement quickly and better ones the longer it runs."
  },
  "hard_constraints": [
    "Every table's capacity is respected in any reported arrangement.",
    "Every active guest row is seated.",
    "Locked placements are never moved."
  ]
}
