1
Fork 0

Back up everything before history rewrite

Signed-off-by: prescientmoon <git@moonythm.dev>
This commit is contained in:
prescientmoon 2024-08-16 15:38:00 +02:00
parent c035ecbb52
commit 3f922fcf6c
Signed by: prescientmoon
SSH key fingerprint: SHA256:UUF9JT2s8Xfyv76b8ZuVL7XrmimH4o49p4b+iexbVH4
6 changed files with 175 additions and 83 deletions

View file

@ -1,4 +1,4 @@
use std::{fs, path::PathBuf}; use std::{fs, io::Cursor, path::PathBuf};
use image::{imageops::FilterType, GenericImageView, Rgba}; use image::{imageops::FilterType, GenericImageView, Rgba};
use num::Integer; use num::Integer;
@ -153,9 +153,17 @@ impl JacketCache {
image.resize(BITMAP_IMAGE_SIZE, BITMAP_IMAGE_SIZE, FilterType::Nearest); image.resize(BITMAP_IMAGE_SIZE, BITMAP_IMAGE_SIZE, FilterType::Nearest);
if should_blur_jacket_art() { if should_blur_jacket_art() {
image = image.blur(20.0); image = image.blur(40.0);
} }
let encoded_pic = {
let mut processed_pic = Vec::new();
image.write_to(
&mut Cursor::new(&mut processed_pic),
image::ImageFormat::Jpeg,
)?;
processed_pic.leak()
};
let bitmap: &'static _ = Box::leak(Box::new(image.into_rgb8())); let bitmap: &'static _ = Box::leak(Box::new(image.into_rgb8()));
if name == "base" { if name == "base" {
@ -163,7 +171,7 @@ impl JacketCache {
for chart in song_cache.charts_mut() { for chart in song_cache.charts_mut() {
if chart.song_id == song_id && chart.cached_jacket.is_none() { if chart.song_id == song_id && chart.cached_jacket.is_none() {
chart.cached_jacket = Some(Jacket { chart.cached_jacket = Some(Jacket {
raw: contents, raw: encoded_pic,
bitmap, bitmap,
}); });
} }
@ -171,7 +179,7 @@ impl JacketCache {
} else if difficulty.is_some() { } else if difficulty.is_some() {
let chart = song_cache.lookup_chart_mut(chart_id).unwrap(); let chart = song_cache.lookup_chart_mut(chart_id).unwrap();
chart.cached_jacket = Some(Jacket { chart.cached_jacket = Some(Jacket {
raw: contents, raw: encoded_pic,
bitmap, bitmap,
}); });
} }

View file

@ -11,7 +11,7 @@ use crate::context::{Error, UserContext};
use crate::user::User; use crate::user::User;
use super::chart::SongCache; use super::chart::SongCache;
use super::score::Score; use super::score::{Score, ScoringSystem};
// {{{ Create play // {{{ Create play
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -127,7 +127,7 @@ pub struct DbPlay {
impl DbPlay { impl DbPlay {
#[inline] #[inline]
pub fn to_play(self) -> Play { pub fn into_play(self) -> Play {
Play { Play {
id: self.id as u32, id: self.id as u32,
chart_id: self.chart_id as u32, chart_id: self.chart_id as u32,
@ -175,6 +175,20 @@ pub struct Play {
} }
impl Play { impl Play {
// {{{ Query the underlying score
#[inline]
pub fn score(&self, system: ScoringSystem) -> Score {
match system {
ScoringSystem::Standard => self.score,
ScoringSystem::EX => self.zeta_score,
}
}
#[inline]
pub fn play_rating(&self, system: ScoringSystem, chart_constant: u32) -> i32 {
self.score(system).play_rating(chart_constant)
}
// }}}
// {{{ Play => distribution // {{{ Play => distribution
pub fn distribution(&self, note_count: u32) -> Option<(u32, u32, u32, u32)> { pub fn distribution(&self, note_count: u32) -> Option<(u32, u32, u32, u32)> {
if let Some(fars) = self.far_notes { if let Some(fars) = self.far_notes {
@ -278,7 +292,8 @@ impl Play {
song.title, chart.difficulty song.title, chart.difficulty
) )
})? })?
.map(|p| p.to_play()); .map(|p| p.into_play());
let prev_score = prev_play.as_ref().map(|p| p.score); let prev_score = prev_play.as_ref().map(|p| p.score);
let prev_zeta_score = prev_play.as_ref().map(|p| p.zeta_score); let prev_zeta_score = prev_play.as_ref().map(|p| p.zeta_score);
// }}} // }}}
@ -360,9 +375,10 @@ pub async fn get_best_plays<'a>(
db: &SqlitePool, db: &SqlitePool,
song_cache: &'a SongCache, song_cache: &'a SongCache,
user: &User, user: &User,
scoring_system: ScoringSystem,
min_amount: usize, min_amount: usize,
max_amount: usize, max_amount: usize,
) -> Result<Result<PlayCollection<'a>, &'static str>, Error> { ) -> Result<Result<PlayCollection<'a>, String>, Error> {
// {{{ DB data fetching // {{{ DB data fetching
let plays: Vec<DbPlay> = query_as( let plays: Vec<DbPlay> = query_as(
" "
@ -381,7 +397,10 @@ pub async fn get_best_plays<'a>(
// }}} // }}}
if plays.len() < min_amount { if plays.len() < min_amount {
return Ok(Err("Not enough plays found")); return Ok(Err(format!(
"Not enough plays found ({} out of a minimum of {min_amount})",
plays.len()
)));
} }
// {{{ B30 computation // {{{ B30 computation
@ -390,13 +409,13 @@ pub async fn get_best_plays<'a>(
let mut plays: Vec<(Play, &Song, &Chart)> = plays let mut plays: Vec<(Play, &Song, &Chart)> = plays
.into_iter() .into_iter()
.map(|play| { .map(|play| {
let play = play.to_play(); let play = play.into_play();
let (song, chart) = song_cache.lookup_chart(play.chart_id)?; let (song, chart) = song_cache.lookup_chart(play.chart_id)?;
Ok((play, song, chart)) Ok((play, song, chart))
}) })
.collect::<Result<Vec<_>, Error>>()?; .collect::<Result<Vec<_>, Error>>()?;
plays.sort_by_key(|(play, _, chart)| -play.score.play_rating(chart.chart_constant)); plays.sort_by_key(|(play, _, chart)| -play.play_rating(scoring_system, chart.chart_constant));
plays.truncate(max_amount); plays.truncate(max_amount);
// }}} // }}}
@ -404,11 +423,12 @@ pub async fn get_best_plays<'a>(
} }
#[inline] #[inline]
pub fn compute_b30_ptt(plays: &PlayCollection<'_>) -> i32 { pub fn compute_b30_ptt(scoring_system: ScoringSystem, plays: &PlayCollection<'_>) -> i32 {
plays plays
.iter() .iter()
.map(|(play, _, chart)| play.score.play_rating(chart.chart_constant)) .map(|(play, _, chart)| play.play_rating(scoring_system, chart.chart_constant))
.sum::<i32>() .sum::<i32>()
/ plays.len() as i32 .checked_div(plays.len() as i32)
.unwrap_or(0)
} }
// }}} // }}}

View file

@ -6,6 +6,21 @@ use crate::context::Error;
use super::chart::Chart; use super::chart::Chart;
// {{{ Scoring system
#[derive(Debug, Clone, Copy, poise::ChoiceParameter)]
pub enum ScoringSystem {
Standard,
// Inspired by sdvx's EX-scoring
EX,
}
impl Default for ScoringSystem {
fn default() -> Self {
Self::Standard
}
}
// }}}
// {{{ Grade // {{{ Grade
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Grade { pub enum Grade {
@ -140,7 +155,7 @@ impl Score {
if play_rating >= prev_play_rating { if play_rating >= prev_play_rating {
write!(buffer, " (+{:.2})", play_rating - prev_play_rating)?; write!(buffer, " (+{:.2})", play_rating - prev_play_rating)?;
} else { } else {
write!(buffer, " (-{:.2})", play_rating - prev_play_rating)?; write!(buffer, " ({:.2})", play_rating - prev_play_rating)?;
} }
} }

View file

@ -1,7 +1,7 @@
use std::{cell::RefCell, env::var, path::PathBuf, str::FromStr, sync::OnceLock, thread::LocalKey}; use std::{cell::RefCell, env::var, path::PathBuf, str::FromStr, sync::OnceLock, thread::LocalKey};
use freetype::{Face, Library}; use freetype::{Face, Library};
use image::{imageops::FilterType, ImageBuffer, Rgb, Rgba}; use image::{ImageBuffer, Rgb, Rgba};
use crate::{arcaea::chart::Difficulty, timed}; use crate::{arcaea::chart::Difficulty, timed};
@ -55,93 +55,100 @@ pub fn with_font<T>(
#[inline] #[inline]
pub fn should_skip_jacket_art() -> bool { pub fn should_skip_jacket_art() -> bool {
static CELL: OnceLock<bool> = OnceLock::new(); var("SHIMMERING_NO_JACKETS").unwrap_or_default() == "1"
*CELL.get_or_init(|| var("SHIMMERING_NO_JACKETS").unwrap_or_default() == "1")
} }
#[inline] #[inline]
pub fn should_blur_jacket_art() -> bool { pub fn should_blur_jacket_art() -> bool {
static CELL: OnceLock<bool> = OnceLock::new(); var("SHIMMERING_BLUR_JACKETS").unwrap_or_default() == "1"
*CELL.get_or_init(|| var("SHIMMERING_BLUR_JACKETS").unwrap_or_default() == "1")
} }
pub fn get_b30_background() -> &'static ImageBuffer<Rgb<u8>, Vec<u8>> { pub fn get_b30_background() -> &'static ImageBuffer<Rgb<u8>, Vec<u8>> {
static CELL: OnceLock<ImageBuffer<Rgb<u8>, Vec<u8>>> = OnceLock::new(); static CELL: OnceLock<ImageBuffer<Rgb<u8>, Vec<u8>>> = OnceLock::new();
CELL.get_or_init(|| { CELL.get_or_init(|| {
let raw_b30_background = image::open(get_assets_dir().join("b30_background.jpg")) timed!("load_b30_background", {
.expect("Could not open b30 background"); let raw_b30_background = image::open(get_assets_dir().join("b30_background.jpg"))
.expect("Could not open b30 background");
raw_b30_background raw_b30_background.blur(7.0).into_rgb8()
.resize( })
8 * raw_b30_background.width(),
8 * raw_b30_background.height(),
FilterType::Lanczos3,
)
.blur(7.0)
.into_rgb8()
}) })
} }
pub fn get_count_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> { pub fn get_count_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new(); static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
CELL.get_or_init(|| { CELL.get_or_init(|| {
image::open(get_assets_dir().join("count_background.png")) timed!("load_count_backound", {
.expect("Could not open count background") image::open(get_assets_dir().join("count_background.png"))
.into_rgba8() .expect("Could not open count background")
.into_rgba8()
})
}) })
} }
pub fn get_score_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> { pub fn get_score_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new(); static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
CELL.get_or_init(|| { CELL.get_or_init(|| {
image::open(get_assets_dir().join("score_background.png")) timed!("load_score_background", {
.expect("Could not open score background") image::open(get_assets_dir().join("score_background.png"))
.into_rgba8() .expect("Could not open score background")
.into_rgba8()
})
}) })
} }
pub fn get_status_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> { pub fn get_status_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new(); static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
CELL.get_or_init(|| { CELL.get_or_init(|| {
image::open(get_assets_dir().join("status_background.png")) timed!("load_status_background", {
.expect("Could not open status background") image::open(get_assets_dir().join("status_background.png"))
.into_rgba8() .expect("Could not open status background")
.into_rgba8()
})
}) })
} }
pub fn get_grade_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> { pub fn get_grade_background() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new(); static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
CELL.get_or_init(|| { CELL.get_or_init(|| {
image::open(get_assets_dir().join("grade_background.png")) timed!("load_grade_background", {
.expect("Could not open grade background") image::open(get_assets_dir().join("grade_background.png"))
.into_rgba8() .expect("Could not open grade background")
.into_rgba8()
})
}) })
} }
pub fn get_top_backgound() -> &'static ImageBuffer<Rgb<u8>, Vec<u8>> { pub fn get_top_backgound() -> &'static ImageBuffer<Rgb<u8>, Vec<u8>> {
static CELL: OnceLock<ImageBuffer<Rgb<u8>, Vec<u8>>> = OnceLock::new(); static CELL: OnceLock<ImageBuffer<Rgb<u8>, Vec<u8>>> = OnceLock::new();
CELL.get_or_init(|| { CELL.get_or_init(|| {
image::open(get_assets_dir().join("top_background.png")) timed!("load_top_background", {
.expect("Could not open top background") image::open(get_assets_dir().join("top_background.png"))
.into_rgb8() .expect("Could not open top background")
.into_rgb8()
})
}) })
} }
pub fn get_name_backgound() -> &'static ImageBuffer<Rgb<u8>, Vec<u8>> { pub fn get_name_backgound() -> &'static ImageBuffer<Rgb<u8>, Vec<u8>> {
static CELL: OnceLock<ImageBuffer<Rgb<u8>, Vec<u8>>> = OnceLock::new(); static CELL: OnceLock<ImageBuffer<Rgb<u8>, Vec<u8>>> = OnceLock::new();
CELL.get_or_init(|| { CELL.get_or_init(|| {
image::open(get_assets_dir().join("name_background.png")) timed!("load_name_background", {
.expect("Could not open name background") image::open(get_assets_dir().join("name_background.png"))
.into_rgb8() .expect("Could not open name background")
.into_rgb8()
})
}) })
} }
pub fn get_ptt_emblem() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> { pub fn get_ptt_emblem() -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new(); static CELL: OnceLock<ImageBuffer<Rgba<u8>, Vec<u8>>> = OnceLock::new();
CELL.get_or_init(|| { CELL.get_or_init(|| {
image::open(get_assets_dir().join("ptt_emblem.png")) timed!("load_ptt_emblem", {
.expect("Could not open ptt emblem") image::open(get_assets_dir().join("ptt_emblem.png"))
.into_rgba8() .expect("Could not open ptt emblem")
.into_rgba8()
})
}) })
} }
@ -150,14 +157,16 @@ pub fn get_difficulty_background(
) -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> { ) -> &'static ImageBuffer<Rgba<u8>, Vec<u8>> {
static CELL: OnceLock<[ImageBuffer<Rgba<u8>, Vec<u8>>; 5]> = OnceLock::new(); static CELL: OnceLock<[ImageBuffer<Rgba<u8>, Vec<u8>>; 5]> = OnceLock::new();
&CELL.get_or_init(|| { &CELL.get_or_init(|| {
let assets_dir = get_assets_dir(); timed!("load_difficulty_background", {
Difficulty::DIFFICULTY_SHORTHANDS.map(|shorthand| { let assets_dir = get_assets_dir();
image::open(assets_dir.join(format!("diff_{}.png", shorthand.to_lowercase()))) Difficulty::DIFFICULTY_SHORTHANDS.map(|shorthand| {
.expect(&format!( image::open(assets_dir.join(format!("diff_{}.png", shorthand.to_lowercase())))
"Could not get background for difficulty {:?}", .expect(&format!(
shorthand "Could not get background for difficulty {:?}",
)) shorthand
.into_rgba8() ))
.into_rgba8()
})
}) })
})[difficulty.to_index()] })[difficulty.to_index()]
} }

View file

@ -20,7 +20,7 @@ use crate::{
arcaea::{ arcaea::{
jacket::BITMAP_IMAGE_SIZE, jacket::BITMAP_IMAGE_SIZE,
play::{compute_b30_ptt, get_best_plays, DbPlay}, play::{compute_b30_ptt, get_best_plays, DbPlay},
score::Score, score::{Score, ScoringSystem},
}, },
assert_is_pookie, assert_is_pookie,
assets::{ assets::{
@ -92,7 +92,7 @@ pub async fn best(
song.title, chart.difficulty song.title, chart.difficulty
) )
})? })?
.to_play(); .into_play();
let (embed, attachment) = play let (embed, attachment) = play
.to_embed( .to_embed(
@ -117,11 +117,13 @@ pub async fn best(
#[poise::command(prefix_command, slash_command)] #[poise::command(prefix_command, slash_command)]
pub async fn plot( pub async fn plot(
ctx: Context<'_>, ctx: Context<'_>,
scoring_system: Option<ScoringSystem>,
#[rest] #[rest]
#[description = "Name of chart to show (difficulty at the end)"] #[description = "Name of chart to show (difficulty at the end)"]
name: String, name: String,
) -> Result<(), Error> { ) -> Result<(), Error> {
let user = get_user!(&ctx); let user = get_user!(&ctx);
let scoring_system = scoring_system.unwrap_or_default();
let (song, chart) = guess_song_and_chart(&ctx.data(), &name)?; let (song, chart) = guess_song_and_chart(&ctx.data(), &name)?;
@ -152,7 +154,12 @@ pub async fn plot(
let min_time = plays.iter().map(|p| p.created_at).min().unwrap(); let min_time = plays.iter().map(|p| p.created_at).min().unwrap();
let max_time = plays.iter().map(|p| p.created_at).max().unwrap(); let max_time = plays.iter().map(|p| p.created_at).max().unwrap();
let mut min_score = plays.iter().map(|p| p.score).min().unwrap(); let mut min_score = plays
.iter()
.map(|p| p.clone().into_play().score(scoring_system))
.min()
.unwrap()
.0 as i64;
if min_score > 9_900_000 { if min_score > 9_900_000 {
min_score = 9_800_000; min_score = 9_800_000;
@ -202,20 +209,26 @@ pub async fn plot(
.draw()?; .draw()?;
let mut points: Vec<_> = plays let mut points: Vec<_> = plays
.iter() .into_iter()
.map(|play| (play.created_at.and_utc().timestamp_millis(), play.score)) .map(|play| {
(
play.created_at.and_utc().timestamp_millis(),
play.into_play().score(scoring_system),
)
})
.collect(); .collect();
points.sort(); points.sort();
points.dedup(); points.dedup();
chart.draw_series(LineSeries::new(points.iter().map(|(t, s)| (*t, *s)), &BLUE))?; chart.draw_series(LineSeries::new(
points.iter().map(|(t, s)| (*t, s.0 as i64)),
&BLUE,
))?;
chart.draw_series( chart.draw_series(points.iter().map(|(t, s)| {
points Circle::new((*t, s.0 as i64), 3, plotters::style::Color::filled(&BLUE))
.iter() }))?;
.map(|(t, s)| Circle::new((*t, *s), 3, plotters::style::Color::filled(&BLUE))),
)?;
root.present()?; root.present()?;
} }
@ -235,6 +248,7 @@ pub async fn plot(
async fn best_plays( async fn best_plays(
ctx: &Context<'_>, ctx: &Context<'_>,
user: &User, user: &User,
scoring_system: ScoringSystem,
grid_size: (u32, u32), grid_size: (u32, u32),
require_full: bool, require_full: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
@ -245,10 +259,11 @@ async fn best_plays(
&user_ctx.db, &user_ctx.db,
&user_ctx.song_cache, &user_ctx.song_cache,
&user, &user,
scoring_system,
if require_full { if require_full {
grid_size.0 * grid_size.1 grid_size.0 * grid_size.1
} else { } else {
grid_size.0 * (grid_size.1.max(1) - 1) grid_size.0 * (grid_size.1.max(1) - 1) + 1
} as usize, } as usize,
(grid_size.0 * grid_size.1) as usize (grid_size.0 * grid_size.1) as usize
) )
@ -471,7 +486,7 @@ async fn best_plays(
stroke: Some((Color::BLACK, 1.5)), stroke: Some((Color::BLACK, 1.5)),
drop_shadow: None, drop_shadow: None,
}, },
&format!("{:0>10}", format!("{}", play.score)), &format!("{:0>10}", format!("{}", play.score(scoring_system))),
) )
})?; })?;
// }}} // }}}
@ -494,9 +509,12 @@ async fn best_plays(
// }}} // }}}
// {{{ Display status text // {{{ Display status text
with_font(&EXO_FONT, |faces| { with_font(&EXO_FONT, |faces| {
let status = play let status = play.short_status(chart).ok_or_else(|| {
.short_status(chart) format!(
.ok_or_else(|| format!("Could not get status for score {}", play.score))?; "Could not get status for score {}",
play.score(scoring_system)
)
})?;
let x_offset = match status { let x_offset = match status {
'P' => 2, 'P' => 2,
@ -540,7 +558,7 @@ async fn best_plays(
// }}} // }}}
// {{{ Display grade text // {{{ Display grade text
with_font(&EXO_FONT, |faces| { with_font(&EXO_FONT, |faces| {
let grade = play.score.grade(); let grade = play.score(scoring_system).grade();
let center = grade_bg_area.center(); let center = grade_bg_area.center();
drawer.text( drawer.text(
@ -586,7 +604,10 @@ async fn best_plays(
(top_left_center, 94), (top_left_center, 94),
faces, faces,
style, style,
&format!("{:.2}", play.score.play_rating_f32(chart.chart_constant)), &format!(
"{:.2}",
play.play_rating(scoring_system, chart.chart_constant) as f32 / 100.0
),
)?; )?;
Ok(()) Ok(())
@ -622,7 +643,7 @@ async fn best_plays(
.attachment(CreateAttachment::bytes(out_buffer, "b30.png")) .attachment(CreateAttachment::bytes(out_buffer, "b30.png"))
.content(format!( .content(format!(
"Your ptt is {:.2}", "Your ptt is {:.2}",
compute_b30_ptt(&plays) as f32 / 100.0 compute_b30_ptt(scoring_system, &plays) as f32 / 100.0
)); ));
ctx.send(reply).await?; ctx.send(reply).await?;
@ -632,15 +653,34 @@ async fn best_plays(
// {{{ B30 // {{{ B30
/// Show the 30 best scores /// Show the 30 best scores
#[poise::command(prefix_command, slash_command, user_cooldown = 30)] #[poise::command(prefix_command, slash_command, user_cooldown = 30)]
pub async fn b30(ctx: Context<'_>) -> Result<(), Error> { pub async fn b30(ctx: Context<'_>, scoring_system: Option<ScoringSystem>) -> Result<(), Error> {
let user = get_user!(&ctx); let user = get_user!(&ctx);
best_plays(&ctx, &user, (5, 6), true).await best_plays(
&ctx,
&user,
scoring_system.unwrap_or_default(),
(5, 6),
true,
)
.await
} }
#[poise::command(prefix_command, slash_command, hide_in_help, global_cooldown = 5)] #[poise::command(prefix_command, slash_command, hide_in_help, global_cooldown = 5)]
pub async fn bany(ctx: Context<'_>, width: u32, height: u32) -> Result<(), Error> { pub async fn bany(
ctx: Context<'_>,
scoring_system: Option<ScoringSystem>,
width: u32,
height: u32,
) -> Result<(), Error> {
let user = get_user!(&ctx); let user = get_user!(&ctx);
assert_is_pookie!(ctx, user); assert_is_pookie!(ctx, user);
best_plays(&ctx, &user, (width, height), false).await best_plays(
&ctx,
&user,
scoring_system.unwrap_or_default(),
(width, height),
false,
)
.await
} }
// }}} // }}}

View file

@ -333,7 +333,7 @@ impl CharMeasurements {
.ok_or_else(|| "No chars in cache")?; .ok_or_else(|| "No chars in cache")?;
println!("char '{}', distance {}", best_match.1, best_match.0); println!("char '{}', distance {}", best_match.1, best_match.0);
if best_match.0 <= 1.0 { if best_match.0 <= 0.75 {
result.push(best_match.1); result.push(best_match.1);
} }
} }