Skip to main content

wowlab_wasm/
permutations.rs

1use wasm_bindgen::prelude::*;
2use wowlab_common::sim::permutations::PermutationSpace;
3use wowlab_types::{
4    game::GearSlot,
5    sim::FastMap,
6    wasm::{WasmCommonError, from_js, to_js},
7};
8
9const MAX_PERMUTATION_ROWS: usize = 10_000;
10
11fn permutation_config_error(error: impl std::fmt::Display) -> WasmCommonError {
12    WasmCommonError::Config(error.to_string())
13}
14
15#[wasm_bindgen(js_name = buildPermutationSpace)]
16pub fn wasm_build_permutation_space(profile: JsValue) -> Result<JsValue, WasmCommonError> {
17    let profile: wowlab_parsers::Profile = from_js(profile)?;
18    let space = wowlab_common::sim::permutations::build_space(&profile)
19        .map_err(permutation_config_error)?;
20
21    Ok(to_js(&space)?)
22}
23
24#[wasm_bindgen(js_name = buildSpaceForSelection)]
25pub fn wasm_build_space_for_selection(
26    profile: JsValue,
27    selections: JsValue,
28) -> Result<JsValue, WasmCommonError> {
29    let profile: wowlab_parsers::Profile = from_js(profile)?;
30    let selections: FastMap<GearSlot, Vec<u32>> = from_js(selections)?;
31    let space = wowlab_common::sim::permutations::build_space_for_selection(&profile, &selections)
32        .map_err(permutation_config_error)?;
33
34    Ok(to_js(&space)?)
35}
36
37#[wasm_bindgen(js_name = tournamentPayload)]
38pub fn wasm_tournament_payload(space: JsValue) -> Result<JsValue, WasmCommonError> {
39    let space: PermutationSpace = from_js(space)?;
40
41    space.validate().map_err(permutation_config_error)?;
42    let payload = wowlab_common::sim::permutations::tournament_payload(&space);
43
44    Ok(to_js(&payload)?)
45}
46
47#[wasm_bindgen(js_name = computeCostAnalysis)]
48pub fn wasm_compute_cost_analysis(space: JsValue) -> Result<JsValue, WasmCommonError> {
49    let space: PermutationSpace = from_js(space)?;
50
51    space.validate().map_err(permutation_config_error)?;
52    let analysis = wowlab_common::sim::permutations::compute_cost_analysis(&space)
53        .map_err(permutation_config_error)?;
54
55    Ok(to_js(&analysis)?)
56}
57
58/// Generate permutation rows (item IDs per contested slot) for indices 0..limit.
59#[wasm_bindgen(js_name = generatePermutationRows)]
60pub fn wasm_generate_permutation_rows(
61    space: JsValue,
62    limit: u32,
63) -> Result<JsValue, WasmCommonError> {
64    let space: PermutationSpace = from_js(space)?;
65
66    space.validate().map_err(permutation_config_error)?;
67    let contested = space.contested_slots();
68    let n =
69        usize::try_from(u64::from(limit).min(space.total())).map_err(permutation_config_error)?;
70
71    if n > MAX_PERMUTATION_ROWS {
72        return Err(WasmCommonError::Config(format!(
73            "requested {n} permutation rows; maximum supported is {MAX_PERMUTATION_ROWS}"
74        )));
75    }
76
77    let mut rows: Vec<Vec<u32>> = Vec::with_capacity(n);
78
79    for idx in 0..n {
80        let choices = space.decode(u64::try_from(idx).unwrap_or(u64::MAX));
81        let mut row = Vec::with_capacity(contested.len());
82
83        // #t(block: rust_unchecked_indexing) choices bounded by decode
84        for (ci, sc) in contested.iter().enumerate() {
85            let pick = choices[ci] as usize;
86
87            row.push(sc.candidates.get(pick).map_or(0, |c| c.item.gear.id));
88        }
89
90        rows.push(row);
91    }
92
93    Ok(to_js(&rows)?)
94}