From fffdc8aab1086eb60bc9537fafb725ba876c46f2 Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Sat, 4 Dec 2021 20:37:59 -0700 Subject: slightly improve ETA calculation --- index.html | 2 +- src/history.rs | 64 +++++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/index.html b/index.html index 4359092..cb5aa8c 100644 --- a/index.html +++ b/index.html @@ -31,7 +31,7 @@

For tracking your Login Queue (Extreme) raid progression.

Share your FFXIV window, and then this tool will try to calculate how much longer you probably have to wait. - (The actual calculation isn't very sophisticated, but it's better than nothing.) + (The actual calculation isn't very well-designed, but it's better than nothing.) (Also this is incredibly janky; if anything breaks or misbehaves try reloading the page.)

diff --git a/src/history.rs b/src/history.rs index f8ffec2..6031499 100644 --- a/src/history.rs +++ b/src/history.rs @@ -1,6 +1,7 @@ // god is dead and we have killed him and consequently std::time::Instant doesn't work on wasm use chrono::prelude::*; +use chrono::Duration; use wasm_bindgen::prelude::*; struct Reading { @@ -18,36 +19,57 @@ pub enum NoETA { Fuck, } +// Duration doesn't impl Mul so we have to do percents +const RECENCY_PERCENT: i32 = 20; + impl History { pub fn record(&mut self, queue_size: u32) { - self.data.push(Reading { - time: Local::now(), - queue_size, - }); + let redundant = match self.data.last() { + Some(last) => queue_size == last.queue_size, + None => false, + }; + if !redundant { + self.data.push(Reading { + time: Local::now(), + queue_size, + }); + } } pub fn completion_time(&self) -> Result { - // TODO make this not suck - let &Reading { - time: first_time, - queue_size: first_size, - .. - } = self.data.first().ok_or(NoETA::Pending)?; + let weighted_average_step_time = self + .data + .iter() + .zip(self.data.iter().skip(1)) + .map(|(one, two)| { + // queue velocity should ideally be measured in distance per time, + // but the units wind up fucky (reciprocal duration not implemented) + // so instead we use time per distance + if one.time > two.time { + return Err(NoETA::Fuck); + } + let delta_time = two.time - one.time; + let delta_queue = match one.queue_size.checked_sub(two.queue_size) { + Some(d) => d, + None => return Err(NoETA::Fuck), + }; + Ok(delta_time / delta_queue as i32) + }) + .reduce(|previous, current| match (previous, current) { + (Ok(previous), Ok(current)) => { + let previous_term = previous * (100 - RECENCY_PERCENT) / 100; + let current_term = current * RECENCY_PERCENT / 100; + Ok(previous_term + current_term) + } + (Err(e), _) => Err(e), + (_, Err(e)) => Err(e), + }) + .ok_or(NoETA::Pending)??; let &Reading { time: last_time, queue_size: last_size, } = self.data.last().ok_or(NoETA::Pending)?; - let overall_time_elapsed = last_time - first_time; - if overall_time_elapsed.is_zero() { - return Err(NoETA::Pending); - } - let overall_queue_motion = match first_size.checked_sub(last_size) { - Some(0) => return Err(NoETA::Pending), - Some(x) => x, - None => return Err(NoETA::Fuck), - }; - let duration_per_step = overall_time_elapsed / overall_queue_motion as i32; - let remaining_duration = duration_per_step * last_size as i32; + let remaining_duration = weighted_average_step_time * last_size as i32; let completion_time = last_time + remaining_duration; let completion_time_gmt = completion_time.timestamp_millis(); Ok(js_sys::Date::new(&JsValue::from( -- cgit v1.2.3