Skip to main content

OpeningBookDatabase

Struct OpeningBookDatabase 

Source
pub struct OpeningBookDatabase { /* private fields */ }

Implementations§

Source§

impl OpeningBookDatabase

Source

pub fn open(config: &OpeningBookConfig) -> SqlResult<Self>

Examples found in repository?
examples/depth4_survey.rs (lines 127-130)
100fn main() {
101    let args = parse_args();
102
103    let started = Instant::now();
104    let mut states = enumerate_depth4();
105    println!(
106        "enumerated {} canonical nonterminal depth-4 states in {:.2}s",
107        states.len(),
108        started.elapsed().as_secs_f64()
109    );
110    if args.sample > 0 && args.sample < states.len() {
111        use rand::prelude::*;
112        let mut rng = StdRng::seed_from_u64(args.seed);
113        states.shuffle(&mut rng);
114        states.truncate(args.sample);
115        states.sort_by_key(|(bb, _)| bb.to_le_bytes());
116        println!(
117            "sampled {} of {} states (seed={})",
118            states.len(),
119            10946,
120            args.seed
121        );
122    } else if args.limit > 0 {
123        states.truncate(args.limit);
124        println!("limited to {} states for this run", states.len());
125    }
126
127    let book = OpeningBookDatabase::open(&OpeningBookConfig {
128        database_path: args.db.clone(),
129        ..Default::default()
130    })
131    .expect("open book");
132
133    let mut rows: Vec<serde_json::Value> = Vec::with_capacity(states.len());
134    let mut solved = 0usize;
135    let mut cutoff = 0usize;
136    let solve_started = Instant::now();
137
138    for (i, (bb, mult)) in states.iter().enumerate() {
139        let t0 = Instant::now();
140        let reference = solve_position_with_book(bb, args.budget_s, Some(&book));
141        let elapsed = t0.elapsed().as_secs_f64();
142        println!(
143            "  [{}/{}] {} solved={} elapsed={:.3}s",
144            i + 1,
145            states.len(),
146            State::new(*bb).to_qfen(),
147            reference.is_some(),
148            elapsed
149        );
150
151        match &reference {
152            Some(r) => {
153                solved += 1;
154                rows.push(serde_json::json!({
155                    "qfen": State::new(*bb).to_qfen(),
156                    "multiplicity": mult,
157                    "value": r["value"],
158                    "nodes": r["nodes"],
159                    "solve_time_s": elapsed,
160                    "solver": r["solver"],
161                }));
162            }
163            None => {
164                cutoff += 1;
165                rows.push(serde_json::json!({
166                    "qfen": State::new(*bb).to_qfen(),
167                    "multiplicity": mult,
168                    "value": null,
169                    "nodes": null,
170                    "solve_time_s": elapsed,
171                    "solver": null,
172                }));
173            }
174        }
175
176        if (i + 1) % 25 == 0 || i + 1 == states.len() {
177            println!(
178                "progress: {}/{} solved={} cutoff={} elapsed_total={:.1}s",
179                i + 1,
180                states.len(),
181                solved,
182                cutoff,
183                solve_started.elapsed().as_secs_f64()
184            );
185            // Flush partial results after every progress line so a killed
186            // run still leaves a readable, if incomplete, artifact — the
187            // SQLite book itself is already durable per-row regardless.
188            let partial = serde_json::json!({
189                "budget_s": args.budget_s,
190                "total_canonical_depth4_states": states.len(),
191                "processed": i + 1,
192                "solved": solved,
193                "cutoff": cutoff,
194                "positions": rows,
195            });
196            std::fs::write(&args.out, serde_json::to_string_pretty(&partial).unwrap())
197                .expect("write partial output");
198        }
199    }
200
201    println!(
202        "done: {} solved, {} cutoff, total solve wall time {:.1}s -> {}",
203        solved,
204        cutoff,
205        solve_started.elapsed().as_secs_f64(),
206        args.out
207    );
208}
Source

pub fn add_position( &self, state: &State, evaluation: f64, visit_count: i64, win_count_p0: i64, win_count_p1: i64, draw_count: i64, best_moves: &[(i32, i32)], depth: i32, is_terminal: TerminalStatus, symmetry_count: i32, ) -> SqlResult<()>

Source

pub fn add_solved_position( &self, state: &State, game_value: i32, optimal_moves: &[(i32, i32)], ) -> SqlResult<bool>

Upsert an exactly-solved position: evaluation is game_value as f64, is_terminal stays Interior (the position itself is not terminal — its game value is exactly known), depth is pieces placed (derived from state.bb), and best_moves records every optimal move (not just a top-5 slice, unlike Self::add_position) as (shape, position) pairs in the given order.

Only canonical representatives are stored. The optimal moves are expressed in state’s own board orientation, but the row is keyed by the canonical key shared by up to eight symmetric orientations; storing a non-representative orientation would let a later lookup on the representative serve moves that are wrong (possibly illegal) for that board. If state is not its own canonical representative (canonical_payload() != bb.to_le_bytes()), nothing is written and Ok(false) is returned; Ok(true) means the row was upserted. Translating moves across orientations via the symmetry transform is a documented follow-up that would lift this restriction.

The position row and its best-move rows are written in one transaction, so a mid-way failure can never leave a solved row with partial best_moves.

Visit/win/draw counters and symmetry_count are not meaningful for a solved reference and are stored as 0.

Source

pub fn get_position(&self, state: &State) -> SqlResult<Option<OpeningBookEntry>>

Source

pub fn query_by_depth( &self, depth: i32, limit: i64, ) -> SqlResult<Vec<OpeningBookEntry>>

Source

pub fn add_edges(&self, edges: &[(&[u8], &[u8])]) -> SqlResult<()>

Source

pub fn get_children(&self, canonical_key: &[u8]) -> SqlResult<Vec<Vec<u8>>>

Source

pub fn get_parents(&self, canonical_key: &[u8]) -> SqlResult<Vec<Vec<u8>>>

Source

pub fn get_edge_count(&self) -> SqlResult<i64>

Source

pub fn total_positions(&self) -> SqlResult<i64>

Source

pub fn max_depth(&self) -> SqlResult<Option<i32>>

Source

pub fn positions_by_depth(&self) -> SqlResult<Vec<(i32, i64)>>

Source

pub fn db_path(&self) -> &str

Source

pub fn file_size(&self) -> u64

Trait Implementations§

Source§

impl Drop for OpeningBookDatabase

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V