Material-only feature extraction, byte-for-byte matching the private training pipeline's features.py::material_features (with include_dice=False) — the model this feeds expects exactly these 7 features, in this exact order, computed the same way.
Deliberately does NOT reuse Evaluator.scoreBitboard's point values (100/300/300/500/900): that scale belongs to the engine's own hand-tuned heuristic, not to the externally-trained model, which was fit on the raw 1/3/3/5/9 values below. Reusing the bitboard-intersection idiom is fine and intentional; reusing the point values would silently retrain nothing while changing what every input means.
The 7 features in the exact order the training pipeline's MATERIAL_COLUMNS expects: p_diff, n_diff, b_diff, r_diff, q_diff, material_diff, total_material — all from color's own perspective (own minus opponent), not White's.
The 7 features in the exact order the training pipeline's MATERIAL_COLUMNS expects: p_diff, n_diff, b_diff, r_diff, q_diff, material_diff, total_material — all from color's own perspective (own minus opponent), not White's.
Column names in extract's order, the layout the private training pipeline calls MATERIAL_COLUMNS. Kept here, and asserted to be RichFeatures.columnNames' prefix, so the material block has one definition rather than a copy per feature set built on top of it.
Column names in extract's order, the layout the private training pipeline calls MATERIAL_COLUMNS. Kept here, and asserted to be RichFeatures.columnNames' prefix, so the material block has one definition rather than a copy per feature set built on top of it.
Stable id of this feature contract, as a model manifest's featureSchema field names it (the JVM-only dicechess.engine.model package resolves that id back to this extractor). Versioned because a column added here would change what every trained model's input means, and a model trained on the old layout must then fail to load rather than be fed a different vector under the same name.
Stable id of this feature contract, as a model manifest's featureSchema field names it (the JVM-only dicechess.engine.model package resolves that id back to this extractor). Versioned because a column added here would change what every trained model's input means, and a model trained on the old layout must then fail to load rather than be fed a different vector under the same name.