fix(benchmark): rank by sheet cost so engines can't game the score
The benchmark is about to be used as the objective for LLM-designed engines, and several gaps would have rewarded the wrong behavior: - Ranking was utilization-first, so dropping awkward parts raised the score. Rank valid > fully placed > cost > plates, where cost is salvage-credited sheet area plus a largest-sheet penalty per unplaced part; placing a part is never scored worse than omitting it. - Salvage rate was ignored in scoring; cost now uses EstimateNetArea, recomputed from job geometry rather than trusted from the engine. - Rotation constraints were never validated. Add RotationPolicy.Allows (shared with NestJobPlacementValidator) and check every placement. - Returned sheets were trusted, so an engine could loosen spacing or invent a size. Sheets must now match offered stock. - Part-in-part placements were flagged as overlaps; spacing now accounts for cutouts, with an X-sorted sweep to prune distant pairs. - Summary averaged per-job percentages; it now sums areas and cost. - --spacing and sheet sizes parsed with the current culture. - Warn when .nest jobs offer only their original sheet sizes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,12 @@ namespace OpenNest.Benchmark
|
||||
|
||||
public int TotalRequestedQuantity => Requests.Sum(r => r.Quantity);
|
||||
|
||||
/// <summary>Sheet area charged per unplaced part: the largest candidate
|
||||
/// sheet. Any single part that fits the stock at all fits on one such
|
||||
/// sheet, so placing a part is never scored worse than leaving it out.</summary>
|
||||
public double UnplacedPartPenalty =>
|
||||
CandidateSizes.Count == 0 ? 0 : CandidateSizes.Max(s => s.Width * s.Length);
|
||||
|
||||
/// <summary>
|
||||
/// Builds the whole-job request this job represents: one NestJobPart per
|
||||
/// requested drawing, and one NestPlateStock per candidate sheet size
|
||||
|
||||
Reference in New Issue
Block a user