2

I'm trying to port a Go language chess engine ( https://github.com/easychessanimations/gobbit ) to Rust ( https://github.com/easychessanimations/rustengine ). Problem with Go is that it produces a WASM executable which is bloated in size because it contains the whole Go runtime ( > 2 MB ). Also the native executable could be a bit faster. So I decided to give Rust a try.

Chess engines need a lot of tables that are initialized in the beginning and serve practically as constants throughout the whole lifetime of the program.

Already with initializing attack bitboards I'm having problems.

I define an attack table:

/// AttackTable type records an attack bitboard for every square of a chess board
pub type AttackTable = [Bitboard; BOARD_AREA];

/// EMPTY_ATTACK_TABLE defines an empty attack table, useful for initializing attack tables
pub const EMPTY_ATTACK_TABLE: AttackTable = [
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];

Then I have global variables of this type:

/// KNIGHT_ATTACK is the attack table of knight
pub static mut KNIGHT_ATTACK: AttackTable = EMPTY_ATTACK_TABLE;
/// BISHOP_ATTACK is the attack table of bishop
pub static mut BISHOP_ATTACK: AttackTable = EMPTY_ATTACK_TABLE;
...

Already at initialization I have to do this in an unsafe block:

/// initializes attack tables, must be called before using the module
pub fn init_attack_tables() {
    let mut sq: Square = 0;
    loop {
        if sq < BOARD_AREA {
            unsafe {
                KNIGHT_ATTACK[sq] = jump_attack_8(sq, KNIGHT_DELTAS, 0);
                BISHOP_ATTACK[sq] = sliding_attack_4(sq, BISHOP_DELTAS, 0);
                ...
            }
            sq += 1;
        } else {
            break;
        }
    }
}

All later accesses to these tables also have to be in unsafe blocks, so I must be doing something wrong.

However I need these tables, they have to be globals and should be accessed easily and quickly. I don't know what to do.

  • [lazy_static](https://docs.rs/lazy_static/1.4.0/lazy_static/) will probably fit your needs. This is likely a duplicate question, but I can't find an exact duplicate to link to. – justinas May 12 '20 at 14:41
  • 1
    Does this answer your question? [How do I create a global, mutable singleton?](https://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton) I linked to the above question. If you do not need mutability, as indicated, just do not wrap your data into a `Mutex`. – justinas May 12 '20 at 14:43
  • I'm aware of this question: [How do I create a global, mutable singleton?](https://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton), but I don't need to modify these tables later, so they are not there to share and modify a common state, but to provide a constant lookup – easychessanimations May 12 '20 at 14:47
  • *but I don't need to modify these tables later* — From the linked duplicate: **If you remove the Mutex then you have a global singleton without any mutability.** – Shepmaster May 12 '20 at 14:51
  • 2
    You can still use lazy static without a mutex. It will be initialized the first time you access it. Though depending on how complex your table generation functions are, you might get by with a const fn. https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9f0e84a7046c3260d7bc74737cd4039b – justinas May 12 '20 at 14:51
  • You may also be interested in [How to create a static string at compile time](https://stackoverflow.com/q/32956050/155423), which has an answer that shows using a build script to create certain kinds of pre-populated data. – Shepmaster May 12 '20 at 14:59
  • @justinas, thanks for this comment, I will look into it, this very well may be the thing that I need if it works out the way I expect – easychessanimations May 12 '20 at 15:00
  • managed to solve it using lazy_static; const fn did not work becaue it does not allow for / loop / if, still it would be nice if it did, because it would simplify initializations – easychessanimations May 12 '20 at 18:19
  • [my solution](https://stackoverflow.com/a/61759268/13526453) – easychessanimations May 12 '20 at 18:30
  • I would usually prefer generated code, either with a `build.rs` script or even just Python / Perl / Vim macros / whatever you're comfortable with, over doing the same initialization of static data at every program load. YMMV depending on the size of the tables to create and the runtime cost of initializing them. – trent May 13 '20 at 14:42
  • @trentcl, thanks for this comment, I actually do this where it takes expensive calculations, for magic bitboards I just finished the code which calculates them and logs them iinto a file, then from this file a Javascript script generates the rust code [gen.js](https://github.com/easychessanimations/rustengine/blob/master/gen.js), but once_cell is a very clean and workable solution without macros for rust static generation – easychessanimations May 13 '20 at 14:51

0 Answers0