//! module for displaying current game state
use std::collections::HashMap;
use crate::app::GameReader;
use heck::ToTitleCase;
use leptos::{html::Canvas, *};
use magister::color::Color;
use plotters::{
chart::{ChartBuilder, LabelAreaPosition},
coord::ranged1d::{IntoSegmentedCoord, SegmentValue},
drawing::IntoDrawingArea,
series::Histogram,
style::{Color as _, RED, WHITE},
};
use plotters_canvas::CanvasBackend;
use web_sys::HtmlCanvasElement;
#[component]
pub fn Player(#[prop(into)] index: MaybeSignal<usize>) -> impl IntoView {
// Display the manapool and balance
const COLORS: &[Color] = &Color::all();
let game = expect_context::<GameReader>();
let player_data = move || {
game.0.with(|gs| {
let gs = gs.as_ref().ok()?;
Some(gs.players()[index.get()].clone())
})
};
view! {
{move || {
player_data()
.map(|pd| {
let id = pd.id();
let balance = pd.balance;
let pool = pd.mana_pool;
let (gen_count, set_gen_count) = create_signal(1usize);
let (has_error, set_has_error) = create_signal(false);
let (gen_mana, set_gen_mana) = create_signal(None);
let (all_gen_mana, set_all_gen_mana) = create_signal(HashMap::new());
let plot_ref = create_node_ref::<Canvas>();
let plotted = move |plot_ref: &HtmlElement<Canvas>, data: &[(Color, usize)]| {
let backend = CanvasBackend::with_canvas_object(
Clone::clone(HtmlCanvasElement::as_ref(plot_ref)),
)
.expect("plotters canvas failed to initialize")
.into_drawing_area();
backend.fill(&WHITE.mix(1.0)).expect("failed to clear canvas");
let mut chart = ChartBuilder::on(&backend)
.set_label_area_size(LabelAreaPosition::Left, 40)
.set_label_area_size(LabelAreaPosition::Bottom, 40)
.build_cartesian_2d(
(0usize..6).into_segmented(),
0..all_gen_mana
.get()
.values()
.copied()
.max()
.map(|x| x + 3)
.unwrap_or(3),
)
.expect("Failed to create chart");
const COLORS: &[Color] = &Color::all();
chart
.configure_mesh()
.disable_x_mesh()
.x_desc("Color")
.y_desc("Count")
.y_labels(5)
.x_labels(7)
.x_label_formatter(
&|idx| {
match idx {
SegmentValue::Exact(idx) => format!("{}?", COLORS[*idx]),
SegmentValue::CenterOf(idx) => {
COLORS
.get(*idx)
.map(ToString::to_string)
.unwrap_or(String::new())
}
SegmentValue::Last => String::new(),
}
},
)
.draw()
.expect("Failed to draw axises");
chart
.draw_series(
Histogram::vertical(&chart)
.style(RED.mix(0.5).filled())
.data(
data
.iter()
.map(|(c, i)| (
COLORS.iter().position(|oc| oc == c).unwrap(),
*i,
)),
),
)
.expect("Failed to draw data");
backend.present().expect("failed to present chart");
};
create_effect(move |_| {
let data = all_gen_mana.get();
if let Some(plot_ref) = plot_ref.get().as_ref() {
plotted(plot_ref, &data.into_iter().collect::<Vec<_>>())
}
});
view! {
<div>
<h2 class="italic text-xl">
"Player " {move || index.get() + 1} " : "
<span class="font-mono not-italic bg-base-200 rounded p-1">
{move || format!("{id}")}
</span>
</h2>
<ul>
{COLORS
.iter()
.map(|c| {
view! {
<li>
{c.color().to_title_case()} " Mana: "
{pool.get(c).copied().unwrap_or(0)} " / (" {balance.get(*c)}
")"
</li>
}
})
.collect_view()}
</ul>
<div class="join">
<input
class="input input-bordered join-item"
type="number"
prop:value=move || gen_count.get()
class:has-error=has_error
on:input=move |ev| {
set_gen_count
.set(
match event_target_value(&ev).parse() {
Ok(val) => {
set_has_error.set(false);
val
}
Err(_e) => {
set_has_error.set(true);
return;
}
},
);
}
/>
<button
class="btn join-item"
on:click=move |_| {
let generated_mana: Vec<_> = balance
.gen_mana(&mut rand::thread_rng())
.take(gen_count.get())
.collect();
let counts = Color::all()
.into_iter()
.map(|c| (
c,
generated_mana.iter().filter(|col| col == &&c).count(),
))
.collect::<HashMap<_, _>>();
set_all_gen_mana
.update(|all_gen_mana| {
for color in Color::all() {
*all_gen_mana.entry(color).or_insert(0)
+= counts.get(&color).copied().unwrap_or(0);
}
});
set_gen_mana.set(Some(generated_mana));
}
>
"Generate Mana"
</button>
</div>
<div class="font-mono">
{move || {
if let Some(gen_mana) = gen_mana.get() {
let mapped = Color::all()
.into_iter()
.map(|c| (
c,
gen_mana.iter().filter(|col| col == &&c).count(),
))
.collect::<HashMap<_, _>>();
format!("{mapped:?}")
} else {
"".to_string()
}
}}
</div>
<Show when=move || {
all_gen_mana.with(|agm| agm.values().copied().sum::<usize>() > 0)
}>
<div>
"Total Mana: "
{move || all_gen_mana.with(|agm| agm.values().sum::<usize>())}
</div>
<canvas width="600" height="200" _ref=plot_ref></canvas>
</Show>
</div>
}
})
}}
}
}