use std::str::FromStr;
use cubing::alg::Move;
use cubing::kpuzzle::{KPattern, KPatternData, KPuzzle};
use serde::{Deserialize, Serialize};
use twips::_internal::cli::args::{CustomGenerators, Generators};
use twips::_internal::search::iterative_deepening::individual_search::IndividualSearchOptions;
use twips::_internal::search::iterative_deepening::iterative_deepening_search::{
ImmutableSearchData, IterativeDeepeningSearch,
};
use twips::scramble::scramble_finder::free_memory_for_all_scramble_finders;
use wasm_bindgen::prelude::*;
use twips::scramble::{
derive_scramble_for_event_seeded, random_scramble_for_event, DerivationSalt, DerivationSeed,
Event,
};
pub fn internal_init() {
console_error_panic_hook::set_once();
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WasmTwipsOptions {
target_pattern: Option<KPatternData>,
generator_moves: Option<Vec<Move>>,
#[serde(flatten)]
inidividual_search_options: IndividualSearchOptions,
}
#[wasm_bindgen]
#[allow(non_snake_case)]
pub fn wasmTwips(
kpuzzle_json: String,
search_pattern_json: String,
options_json: String, ) -> Result<String, String> {
internal_init();
let kpuzzle = KPuzzle::try_from_json(kpuzzle_json.as_bytes());
let kpuzzle = kpuzzle.map_err(|e| e.to_string())?;
let search_pattern = KPattern::try_from_json(&kpuzzle, search_pattern_json.as_bytes());
let search_pattern = search_pattern.map_err(|e| e.to_string())?;
let options: WasmTwipsOptions = match serde_json::from_slice(options_json.as_bytes()) {
Ok(options) => options,
Err(e) => return Err(e.to_string()),
};
if options
.inidividual_search_options
.min_num_solutions
.is_some()
{
return Err("`minNumSolutions` is not implemented yet".to_owned());
}
let target_pattern = match options.target_pattern {
Some(target_pattern_data) => {
let target_pattern = KPattern::try_from_data(&kpuzzle, &target_pattern_data);
target_pattern.map_err(|e| e.to_string())?
}
None => kpuzzle.default_pattern(),
};
let generators = match options.generator_moves {
Some(generator_moves) => Generators::Custom(CustomGenerators {
moves: generator_moves,
algs: vec![],
}),
None => Generators::Default,
};
let mut iterative_deepening_search =
<IterativeDeepeningSearch<KPuzzle>>::new_with_hash_prune_table(
ImmutableSearchData::try_from_common_options_with_auto_search_generators(
kpuzzle.clone(),
generators.enumerate_moves_for_kpuzzle(&kpuzzle),
vec![target_pattern], Default::default(),
)
.map_err(|e| e.description)?,
Default::default(), Default::default(), );
match iterative_deepening_search
.search(
&search_pattern,
options.inidividual_search_options,
Default::default(),
)
.next()
{
Some(alg) => Ok(alg.to_string().to_owned()),
None => Err("No solution found!".to_owned()),
}
}
#[wasm_bindgen]
#[allow(non_snake_case)]
pub fn wasmRandomScrambleForEvent(event_str: String) -> Result<String, String> {
internal_init();
let event = Event::try_from(event_str.as_str()).map_err(|e| e.description)?;
match random_scramble_for_event(event) {
Ok(scramble) => Ok(scramble.to_string()),
Err(e) => Err(e.description),
}
}
#[wasm_bindgen]
#[allow(non_snake_case)]
pub fn wasmDeriveScrambleForEvent(
hex_derivation_seed_str: String,
derivation_salt_hierarchy_str: String,
subevent_str: String,
) -> Result<String, String> {
internal_init();
let derivation_seed = DerivationSeed::from_str(&hex_derivation_seed_str)?;
let hierarchy = if derivation_salt_hierarchy_str.is_empty() {
vec![]
} else {
derivation_salt_hierarchy_str
.split("/")
.map(DerivationSalt::from_str)
.collect::<Result<Vec<DerivationSalt>, String>>()?
};
let subevent = Event::try_from(subevent_str.as_str()).map_err(|e| e.description)?;
match derive_scramble_for_event_seeded(&derivation_seed, &hierarchy, subevent) {
Ok(scramble) => Ok(scramble.to_string()),
Err(e) => Err(e),
}
}
#[wasm_bindgen]
#[allow(non_snake_case)]
pub extern "C" fn wasmFreeMemoryForAllScrambleFinders() -> u32 {
free_memory_for_all_scramble_finders() as u32
}