Skip to content

Improve strength limiting with logistic noise and root depth caps - #7036

Open
yashahuja31 wants to merge 1 commit into
official-stockfish:masterfrom
yashahuja31:fix-issue-3635
Open

Improve strength limiting with logistic noise and root depth caps#7036
yashahuja31 wants to merge 1 commit into
official-stockfish:masterfrom
yashahuja31:fix-issue-3635

Conversation

@yashahuja31

Copy link
Copy Markdown

Description

This PR refactors the Skill implementation for strength-limited play, addressing issues discussed in #3635.

Instead of selecting moves semi-randomly at the root from MultiPV scores (which can cause blunder-like single-move drops despite deep search), strength is limited in two complementary ways:

  1. Root Depth Cap: A level-dependent root depth limit (depth_limit() = 1 + level) prevents the search from finding deep tactical lines and forced mates at lower skill levels.
  2. Logistic Noise Perturbation: Standard logistic distribution quantile noise (NoiseQuantile) is added to leaf evaluations during evaluate(), scaled by skill level and game phase (non-pawn material). This simulates human-like misjudgments of positions rather than sudden gross blunders.

Key Details & Stability

  • Per-Game Seed Consistency: Evaluation perturbation is deterministic for a given position key and game seed. The seed is drawn once per game (ThreadPool::clear(), called on ucinewgame), ensuring consistent evaluations within a game while varying engine play across different games.
  • Tablebase & Mate Protection: Perturbed evaluation values are clamped to prevent fabricating artificial mate or tablebase scores (std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1)).
  • No Functional Change at Default: At full strength (Skill Level 20), skill.enabled() is false, maintaining exact master behavior and benchmark signature.

Testing & Verification

  • Bench Signature: 2829394 (Unchanged, signature OK).
  • Unit Tests: Added tests/skill_unit_test.py covering UCI options (Skill Level, UCI_LimitStrength, UCI_Elo), depth limits, search execution, and seed re-randomization across games.
  • Sanity Test: Confirmed smooth agreement curve with full-strength moves across levels (from 40.9% at Level 0 to 90.9% at Level 20).
  • Head-to-Head: Match tests vs master show progressive Elo scaling across skill levels without single-move catastrophic blunders.

Closes #3635

No functional change

This refactors the strength limited search implementation to address issue official-stockfish#3635.

Instead of selecting moves randomly at the root from multiPV scores:
- Evaluation noise (sampled from standard logistic distribution quantiles) is added to leaf evaluations, scaled by level and non-pawn material phase.
- Evaluation perturbation is deterministic per position key and game seed (drawn once per game on ucinewgame in ThreadPool::clear).
- A level-dependent depth limit is enforced to prevent deep tactical lines while maintaining human-like mistakes at lower skill levels.
- Added tests/skill_unit_test.py automated unit test suite for skill options, depth limits, and game re-seeding.

Bench: 2829394

closes official-stockfish#3635

No functional change
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

clang-format 20 needs to be run on this PR.
If you do not have clang-format installed, the maintainer will run it when merging.
For the exact version please see https://packages.ubuntu.com/questing/clang-format-20.
An easier way to install it might be through https://apt.llvm.org/#llvmsh.

(execution 31201262293 / attempt 1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[RFC] Considerations for "Skill Level"

1 participant