Skip to content

Implement own MoveVec - #445

Closed
bdmendes wants to merge 5 commits into
masterfrom
movelist
Closed

bdmendes wants to merge 5 commits into
masterfrom
movelist

Conversation

@bdmendes

@bdmendes bdmendes commented Jul 18, 2026 •

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • Improvements
    • Improved chess move generation and ordering for more consistent search behavior.
    • Updated move listing and move selection to use the same legal-move results throughout the application.
    • Improved handling when no principal variation is available by selecting a legal fallback move.
    • Enhanced support for prioritizing promotions, captures, tactical moves, and quiet moves during analysis.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This refactor introduces MoveVec as an in-place move buffer, updates move generation and consumers to use it, and replaces MovePicker search ordering with MoveEvaluator scoring.

Changes

Move ordering refactor

Layer / File(s) Summary
MoveVec and generation API
src/moves/vec.rs, src/moves/generate/*, src/moves/mod.rs
Adds fixed-capacity scored move storage, updates generate_moves to populate a mutable buffer, and adapts generators and tests.
Position API and move consumers
src/position/mod.rs, src/engine/mod.rs, src/main.rs, src/search/perft.rs, src/search/see.rs
Routes move lookup, perft, UCI commands, and fallback selection through Position::moves.
Search move evaluation
src/search/move_evaluator.rs, src/search/mod.rs
Adds heuristic move scoring and changes quiescence and PVS to evaluate and iterate MoveVec entries instead of using MovePicker.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Poem

I’m a bunny with moves in a row,
Scored high or low as they go.
Picker hopped away,
Evaluators now play—
And buffers keep chess in a flow!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: introducing a custom MoveVec and switching move generation/search code to use it.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.38554% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 92%. Comparing base (073f049) to head (9365584).

Additional details and impacted files
@@          Coverage Diff           @@
##           master   #445    +/-   ##
======================================
+ Coverage      91%    92%    +1%     
======================================
  Files          33     34     +1     
  Lines        3739   3874   +135     
  Branches     3739   3874   +135     
======================================
+ Hits         3401   3534   +133     
  Misses        319    319            
- Partials       19     21     +2     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9cf5def1-9301-4087-8a5f-cc02228d116c

📥 Commits

Reviewing files that changed from the base of the PR and between 073f049 and 9365584.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • src/engine/mod.rs
  • src/main.rs
  • src/moves/generate/castle.rs
  • src/moves/generate/leapers.rs
  • src/moves/generate/mod.rs
  • src/moves/generate/pawns.rs
  • src/moves/mod.rs
  • src/moves/vec.rs
  • src/position/mod.rs
  • src/search/mod.rs
  • src/search/move_evaluator.rs
  • src/search/perft.rs
  • src/search/picker.rs
  • src/search/see.rs
💤 Files with no reviewable changes (1)
  • src/search/picker.rs

Comment thread src/moves/generate/mod.rs
Comment on lines +86 to +91
fn assert_eq_vec_move(moves: &mut MoveVec, expected: &[&str]) {
assert_eq!(moves.len(), expected.len());
let mov_strs = moves.iter().map(|m| m.to_string()).collect::<Vec<String>>();
moves
let move_vec = moves.into_iter().map(|m| m.to_string()).collect::<Vec<String>>();
move_vec
.iter()
.map(|m| m.to_string())
.for_each(|m| assert!(expected.contains(&m.as_str()), "got: {:?}, expected: {:?}", mov_strs, expected));
.for_each(|m| assert!(expected.contains(&m.as_str()), "got: {:?}, expected: {:?}", move_vec, expected));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Compare the complete move multisets.

The current check accepts duplicates such as actual [a, a] against expected [a, b]. Sort both collections and assert equality.

Proposed fix
 fn assert_eq_vec_move(moves: &mut MoveVec, expected: &[&str]) {
-    assert_eq!(moves.len(), expected.len());
-    let move_vec = moves.into_iter().map(|m| m.to_string()).collect::<Vec<String>>();
-    move_vec
-        .iter()
-        .for_each(|m| assert!(expected.contains(&m.as_str()), "got: {:?}, expected: {:?}", move_vec, expected));
+    let mut actual = moves.into_iter().map(|m| m.to_string()).collect::<Vec<_>>();
+    let mut expected = expected.iter().map(|m| (*m).to_owned()).collect::<Vec<_>>();
+    actual.sort_unstable();
+    expected.sort_unstable();
+    assert_eq!(actual, expected);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn assert_eq_vec_move(moves: &mut MoveVec, expected: &[&str]) {
assert_eq!(moves.len(), expected.len());
let mov_strs = moves.iter().map(|m| m.to_string()).collect::<Vec<String>>();
moves
let move_vec = moves.into_iter().map(|m| m.to_string()).collect::<Vec<String>>();
move_vec
.iter()
.map(|m| m.to_string())
.for_each(|m| assert!(expected.contains(&m.as_str()), "got: {:?}, expected: {:?}", mov_strs, expected));
.for_each(|m| assert!(expected.contains(&m.as_str()), "got: {:?}, expected: {:?}", move_vec, expected));
fn assert_eq_vec_move(moves: &mut MoveVec, expected: &[&str]) {
let mut actual = moves.into_iter().map(|m| m.to_string()).collect::<Vec<_>>();
let mut expected = expected.iter().map(|m| (*m).to_owned()).collect::<Vec<_>>();
actual.sort_unstable();
expected.sort_unstable();
assert_eq!(actual, expected);
}

Comment thread src/moves/vec.rs

use crate::moves::Move;

const MAX_SIZE: usize = 128;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not silently discard moves after 128 entries.

Legal positions can exceed this capacity, causing search and perft to omit valid branches. Use a proven upper bound and make overflow explicit.

Proposed safeguard
 pub fn push_scored(&mut self, mov: Move, score: i8) {
-    if self.raw_size < MAX_SIZE {
-        self.data[self.raw_size].write((Some(mov), score));
-        self.raw_size += 1;
-        self.length += 1;
-    }
+    assert!(self.raw_size < MAX_SIZE, "MoveVec capacity exceeded");
+    self.data[self.raw_size].write((Some(mov), score));
+    self.raw_size += 1;
+    self.length += 1;
 }

Also increase MAX_SIZE to a documented bound covering every generated pre-filter move set.

Also applies to: 98-103

@bdmendes bdmendes closed this Jul 18, 2026
@github-actions

Copy link
Copy Markdown

Against master [hash=64]: 🆗 Elo: -13.90 +/- 17.41, nElo: -17.23 +/- 21.53

@github-actions

Copy link
Copy Markdown

Against v1.6.0 [hash=64]: ✅ Elo: 55.71 +/- 19.08, nElo: 64.03 +/- 21.53

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.

1 participant