feat: sample from transformations
This commit is contained in:
parent
bd8b0fecf1
commit
b37386b9e8
@ -42,13 +42,13 @@ pub mod v4 {
|
||||
pub problems: BTreeSet<Problem>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Problem {
|
||||
pub pattern: Pattern,
|
||||
pub method: v2::Method,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Pattern {
|
||||
pub pattern: BTreeMap<v1::HoldPosition, v1::HoldRole>,
|
||||
}
|
||||
@ -160,7 +160,7 @@ pub mod v2 {
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, derive_more::FromStr, derive_more::Display)]
|
||||
pub struct ProblemUid(pub uuid::Uuid);
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Display, Copy)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Display, Copy, Hash)]
|
||||
pub enum Method {
|
||||
#[display("Feet follow hands")]
|
||||
FeetFollowHands,
|
||||
|
@ -2,6 +2,22 @@ use super::*;
|
||||
use chrono::DateTime;
|
||||
use chrono::Utc;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashSet;
|
||||
|
||||
impl Problem {
|
||||
/// Returns all possible transformations for the pattern. Not for the method.
|
||||
#[must_use]
|
||||
pub fn transformations(&self, wall_dimensions: WallDimensions) -> HashSet<Self> {
|
||||
self.pattern
|
||||
.transformations(wall_dimensions)
|
||||
.into_iter()
|
||||
.map(|pattern| Self {
|
||||
pattern,
|
||||
method: self.method,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Pattern {
|
||||
#[must_use]
|
||||
@ -88,6 +104,23 @@ impl Pattern {
|
||||
|
||||
pattern
|
||||
}
|
||||
|
||||
/// Returns all possible transformations for the pattern
|
||||
#[must_use]
|
||||
pub fn transformations(&self, wall_dimensions: WallDimensions) -> HashSet<Self> {
|
||||
let mut transformations = HashSet::new();
|
||||
|
||||
let pattern = self.canonicalize();
|
||||
for mut pat in [pattern.mirror(), pattern] {
|
||||
transformations.insert(pat.clone());
|
||||
while let Some(p) = pat.shift_right(wall_dimensions, 1) {
|
||||
transformations.insert(p.clone());
|
||||
pat = p;
|
||||
}
|
||||
}
|
||||
|
||||
transformations
|
||||
}
|
||||
}
|
||||
|
||||
impl UserInteraction {
|
||||
|
@ -13,6 +13,7 @@ use leptos::prelude::*;
|
||||
use leptos_router::params::Params;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Params, PartialEq, Clone)]
|
||||
@ -60,7 +61,7 @@ struct Context {
|
||||
wall: Signal<models::Wall>,
|
||||
user_interactions: Signal<BTreeMap<models::Problem, models::UserInteraction>>,
|
||||
problem: Signal<Option<models::Problem>>,
|
||||
filtered_problems: Signal<BTreeSet<models::Problem>>,
|
||||
filtered_problem_transformations: Signal<Vec<HashSet<models::Problem>>>,
|
||||
user_interaction: Signal<Option<models::UserInteraction>>,
|
||||
todays_attempt: Signal<Option<models::Attempt>>,
|
||||
latest_attempt: Signal<Option<models::DatedAttempt>>,
|
||||
@ -70,6 +71,9 @@ struct Context {
|
||||
cb_next_problem: Callback<()>,
|
||||
cb_set_problem: Callback<models::Problem>,
|
||||
cb_upsert_todays_attempt: Callback<server_functions::UpsertTodaysAttempt>,
|
||||
|
||||
#[expect(dead_code)]
|
||||
filtered_problems: Signal<BTreeSet<models::Problem>>,
|
||||
}
|
||||
|
||||
#[component]
|
||||
@ -93,7 +97,12 @@ fn Controller(
|
||||
|
||||
// Derive signals
|
||||
let user_interaction = signals::user_interaction(user_interactions.into(), problem.into());
|
||||
let problem_transformations = signals::problem_transformations(wall);
|
||||
|
||||
// TODO: still used?
|
||||
let filtered_problems = signals::filtered_problems(wall, filter_holds.into());
|
||||
|
||||
let filtered_problem_transformations = signals::filtered_problem_transformations(problem_transformations.into(), filter_holds.into());
|
||||
let todays_attempt = signals::todays_attempt(user_interaction);
|
||||
let latest_attempt = signals::latest_attempt(user_interaction);
|
||||
|
||||
@ -111,14 +120,23 @@ fn Controller(
|
||||
// Callback: Set next problem to a random problem
|
||||
let cb_set_random_problem: Callback<()> = Callback::new(move |_| {
|
||||
// TODO: remove current problem from population
|
||||
let population = filtered_problems.read();
|
||||
let population = filtered_problem_transformations.read();
|
||||
let population = population.deref();
|
||||
|
||||
use rand::seq::IteratorRandom;
|
||||
let mut rng = rand::rng();
|
||||
let problem = population.iter().choose(&mut rng);
|
||||
|
||||
set_problem.set(problem.cloned());
|
||||
// Pick pattern
|
||||
let Some(problem_set) = population.iter().choose(&mut rng) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Pick problem out of pattern transformations
|
||||
let Some(problem) = problem_set.iter().choose(&mut rng) else {
|
||||
return;
|
||||
};
|
||||
|
||||
set_problem.set(Some(problem.clone()));
|
||||
});
|
||||
|
||||
// Callback: On click hold, Add/Remove hold position to problem filter
|
||||
@ -161,6 +179,7 @@ fn Controller(
|
||||
todays_attempt,
|
||||
filter_holds: filter_holds.into(),
|
||||
filtered_problems: filtered_problems.into(),
|
||||
filtered_problem_transformations: filtered_problem_transformations.into(),
|
||||
user_interactions: user_interactions.into(),
|
||||
});
|
||||
|
||||
@ -315,9 +334,11 @@ fn Filter() -> impl IntoView {
|
||||
}
|
||||
}
|
||||
|
||||
let problems_count = ctx.filtered_problem_transformations.read().iter().map(|set| set.len()).sum::<usize>();
|
||||
|
||||
let problems_counter = {
|
||||
let name = view! { <p class="mr-4 font-light text-right text-orange-300">{"Problems:"}</p> };
|
||||
let value = view! { <p class="text-white">{ctx.filtered_problems.read().len()}</p> };
|
||||
let value = view! { <p class="text-white">{problems_count}</p> };
|
||||
view! {
|
||||
<div class="grid grid-rows-none gap-x-0.5 gap-y-1 grid-cols-[auto_1fr]">
|
||||
{name} {value}
|
||||
@ -336,7 +357,8 @@ fn Filter() -> impl IntoView {
|
||||
let mut interaction_counters = InteractionCounters::default();
|
||||
let interaction_counters_view = {
|
||||
let user_ints = ctx.user_interactions.read();
|
||||
for problem in ctx.filtered_problems.read().iter() {
|
||||
for problem_set in ctx.filtered_problem_transformations.read().iter() {
|
||||
for problem in problem_set {
|
||||
if let Some(user_int) = user_ints.get(problem) {
|
||||
match user_int.best_attempt().map(|da| da.attempt) {
|
||||
Some(models::Attempt::Flash) => interaction_counters.flash += 1,
|
||||
@ -346,6 +368,7 @@ fn Filter() -> impl IntoView {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let flash = (interaction_counters.flash > 0).then(|| {
|
||||
let class = Gradient::CyanBlue.class_text();
|
||||
view! {
|
||||
@ -575,6 +598,7 @@ mod signals {
|
||||
use leptos::prelude::*;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub fn latest_attempt(user_interaction: Signal<Option<models::UserInteraction>>) -> Signal<Option<models::DatedAttempt>> {
|
||||
Signal::derive(move || user_interaction.read().as_ref().and_then(models::UserInteraction::latest_attempt))
|
||||
@ -600,6 +624,17 @@ mod signals {
|
||||
})
|
||||
}
|
||||
|
||||
/// Maps each problem to a set of problems comprising all transformation of the problem pattern.
|
||||
pub(crate) fn problem_transformations(wall: Signal<models::Wall>) -> Memo<Vec<HashSet<models::Problem>>> {
|
||||
Memo::new(move |_prev_val| {
|
||||
let wall = wall.read();
|
||||
wall.problems
|
||||
.iter()
|
||||
.map(|problem| problem.transformations(wall.wall_dimensions))
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn filtered_problems(
|
||||
wall: Signal<models::Wall>,
|
||||
filter_holds: Signal<BTreeSet<models::HoldPosition>>,
|
||||
@ -616,6 +651,28 @@ mod signals {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn filtered_problem_transformations(
|
||||
problem_transformations: Signal<Vec<HashSet<models::Problem>>>,
|
||||
filter_holds: Signal<BTreeSet<models::HoldPosition>>,
|
||||
) -> Memo<Vec<HashSet<models::Problem>>> {
|
||||
Memo::new(move |_prev_val| {
|
||||
let filter_holds = filter_holds.read();
|
||||
let problem_transformations = problem_transformations.read();
|
||||
|
||||
problem_transformations
|
||||
.iter()
|
||||
.map(|problem_set| {
|
||||
problem_set
|
||||
.iter()
|
||||
.filter(|problem| filter_holds.iter().all(|hold_pos| problem.pattern.pattern.contains_key(hold_pos)))
|
||||
.map(|problem| problem.clone())
|
||||
.collect::<HashSet<models::Problem>>()
|
||||
})
|
||||
.filter(|set| !set.is_empty())
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn hold_role(problem: Signal<Option<models::Problem>>, hold_position: models::HoldPosition) -> Signal<Option<models::HoldRole>> {
|
||||
Signal::derive(move || problem.get().and_then(|p| p.pattern.pattern.get(&hold_position).copied()))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user