Skip to content

rust port - #989

Draft
Disservin wants to merge 155 commits into
masterfrom
port-rust
Draft

rust port#989
Disservin wants to merge 155 commits into
masterfrom
port-rust

Conversation

@Disservin

@Disservin Disservin commented Feb 14, 2026

Copy link
Copy Markdown
Owner

How to test this branch, check it out via git and run
make this should give you a ./fastchess executable in the same dir.
This requires cargo/rust
https://rust-lang.org/tools/install/

  • curious about things which dont work yet

changes:

  • has experimental shogi support with usi
  • logging, uses a separate lightweight thread to allow for very fast time controls with realtime engine logging enabled
  • ctrl c uses a separate lightweight thread too
  • massive cleanup on game assignments and color usage
  • easier tests
  • crc32fast dependency which uses simd
  • better future variant support
  • faster engine shutdown time

breaking changes:

  • non intended

issues (known):

  • none known

fixed issues (done):

perf:

./fastchess -engine name=sf-dev1 cmd=./stockfish \
    -engine name=sf-dev2 cmd=./stockfish \
    -rounds 50 -concurrency -1 -openings file=UHO_Lichess_4852_v1.epd format=epd \
    -each tc=1+0.01 dir=/home/me/Github/Stockfish/src/ -check-mate-pvs;
Language Memory Time
Rust 216M 18s
C++ 217M 20s

@Disservin
Disservin force-pushed the port-rust branch 2 times, most recently from e56d463 to 201cd7b Compare February 22, 2026 14:14
@Disservin Disservin mentioned this pull request Feb 22, 2026
@Disservin

Copy link
Copy Markdown
Owner Author

todo: drop illegal enpassant squares when parsing fen

@KazApps

KazApps commented Mar 4, 2026

Copy link
Copy Markdown

works on my end too - no time losses
even with 192 concurrency at 1.00+0.01
huge thanks!

@Disservin

Copy link
Copy Markdown
Owner Author

cool very nice

@KazApps

KazApps commented Mar 5, 2026

Copy link
Copy Markdown

assert!(!startpos.is_in_check());

is this assert really necessary?
the book I use for stoat tests includes some positions where the side to move is in check, so fastchess crashes because of it

@Disservin

Disservin commented Mar 5, 2026

Copy link
Copy Markdown
Owner Author

hm that came straight from @87flowers https://github.com/87flowers/shogitest/blob/f620e5b69911ff4d37807f11bcf4da93820e7fd7/src/shogi.rs#L1002 maybe they can clarify the intention?

I know very little about shogi

@87flowers

87flowers commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

I hadn't thought too hard about the last_not_in_check_ply logic when the opening is in check, and had put that in so I could think about it at a later date.
Removing the assert is fine.

I suggest the following change:


diff --git a/app/src/variants/shogi.rs b/app/src/variants/shogi.rs
index cc874d8..0cd6769 100644
--- a/app/src/variants/shogi.rs
+++ b/app/src/variants/shogi.rs
@@ -930,12 +930,15 @@ pub(crate) struct Game {
 
 impl Game {
     pub fn new(startpos: Position) -> Game {
-        assert!(!startpos.is_in_check());
+        let mut last_not_in_check_ply = [-1; 2];
+        if !startpos.is_in_check() {
+            last_not_in_check_ply[startpos.stm.to_index()] = 0;
+        }
         Game {
             current_position: startpos,
             moves: vec![],
             history: vec![startpos],
-            last_not_in_check_ply: [-1, -1],
+            last_not_in_check_ply,
         }
     }

@KazApps

KazApps commented Mar 5, 2026

Copy link
Copy Markdown

here you're assigning ply - 1:

self.last_not_in_check_ply[stm.to_index()] = (self.history.len() - 1) as isize

but here you're assigning the ply:
last_not_in_check_ply[startpos.stm.to_index()] = 0;

won't that cause any issues?

@87flowers

Copy link
Copy Markdown
Contributor
             history: vec![startpos],

history starts with history.len() == 1

@KazApps

KazApps commented Mar 5, 2026

Copy link
Copy Markdown

ohhh, you're right

@Disservin

Copy link
Copy Markdown
Owner Author

okay thanks you two :) pushed a patch

@Disservin

Copy link
Copy Markdown
Owner Author

if there are more shogi things to support you can list them, though I'll (myself) will probably only implement the sane ones and not delve into weird cutechess things.. unless someone else takes that up

@KazApps

KazApps commented Jul 15, 2026

Copy link
Copy Markdown

@Disservin

fn is_threefold_repetition(&self) -> bool {
// Shogi uses fourfold repetition (sennichite), not threefold
// For now, return false as we don't track position history
false
}

could you update this?

@KazApps

KazApps commented Jul 15, 2026

Copy link
Copy Markdown

fn status(&self) -> GameStatus {
// Check for game over conditions
// For now, we return ongoing - the actual outcome is tracked by do_move
GameStatus::ONGOING
}

this part as well

@Disservin

Copy link
Copy Markdown
Owner Author

wdym

@KazApps

KazApps commented Jul 15, 2026

Copy link
Copy Markdown

I mean I think it should be something like this:

--- a/app/src/variants/shogi.rs
+++ b/app/src/variants/shogi.rs
@@ -1211,7 +1211,7 @@
 
 // ── Game Trait Implementation ────────────────────────────────────────────────
 
-use crate::game::{GameStatus, Side as GameColor};
+use crate::game::{GameOverReason, GameStatus, Side as GameColor};
 use crate::types::VariantType;
 use crate::variants::GameMove;
 
@@ -1262,6 +1262,7 @@
 pub struct ShogiGame {
     game: Game,
     ply_count: u32,
+    last_outcome: GameOutcome,
 }
 
 impl ShogiGame {
@@ -1270,6 +1271,7 @@
         Self {
             game: Game::new(Position::default()),
             ply_count: 0,
+            last_outcome: GameOutcome::Undetermined,
         }
     }
 
@@ -1279,6 +1281,7 @@
         Some(Self {
             game: Game::new(position),
             ply_count: 0,
+            last_outcome: GameOutcome::Undetermined,
         })
     }
 
@@ -1290,6 +1293,7 @@
     /// Makes a move and returns true if successful.
     fn make_shogi_move(&mut self, mv: &ShogiMove) -> bool {
         let outcome = self.game.do_move(mv.inner());
+        self.last_outcome = outcome;
         if outcome != GameOutcome::LossByIllegal(self.game.stm()) {
             self.ply_count += 1;
             true
@@ -1351,9 +1355,12 @@
     }
 
     fn status(&self) -> GameStatus {
-        // Check for game over conditions
-        // For now, we return ongoing - the actual outcome is tracked by do_move
-        GameStatus::ONGOING
+        let reason = match self.last_outcome {
+            GameOutcome::Checkmated(_) => GameOverReason::Checkmate,
+            GameOutcome::DrawBySennichite => GameOverReason::Repetition,
+            _ => GameOverReason::None,
+        };
+        GameStatus { reason }
     }
 
     fn parse_move(&self, notation: &str) -> Option<Box<dyn GameMove>> {
@@ -1414,8 +1421,6 @@
     }
 
     fn is_threefold_repetition(&self) -> bool {
-        // Shogi uses fourfold repetition (sennichite), not threefold
-        // For now, return false as we don't track position history
-        false
+        self.last_outcome == GameOutcome::DrawBySennichite
     }
 }

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.

3 participants