forked from StrafesNET/roblox-bot-player
Compare commits
2 Commits
xeno-debug
...
ai-ratio2
| Author | SHA1 | Date | |
|---|---|---|---|
|
2ffce9d9ca
|
|||
|
8a55fbffd9
|
1
.rustfmt.toml
Normal file
1
.rustfmt.toml
Normal file
@@ -0,0 +1 @@
|
||||
hard_tabs=true
|
||||
9
Cargo.lock
generated
9
Cargo.lock
generated
@@ -1407,6 +1407,13 @@ version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde"
|
||||
|
||||
[[package]]
|
||||
name = "ratio_from_float"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"strafesnet_common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ratio_ops"
|
||||
version = "0.1.1"
|
||||
@@ -1687,6 +1694,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"pollster",
|
||||
"ratio_from_float",
|
||||
"strafesnet_common",
|
||||
"strafesnet_graphics",
|
||||
"strafesnet_roblox_bot_file",
|
||||
@@ -1700,6 +1708,7 @@ dependencies = [
|
||||
name = "strafesnet_roblox_bot_player_wasm_module"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ratio_from_float",
|
||||
"strafesnet_common",
|
||||
"strafesnet_graphics",
|
||||
"strafesnet_roblox_bot_file",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
members = [
|
||||
"lib",
|
||||
"native-player",
|
||||
"ratio_from_float",
|
||||
"wasm-module"
|
||||
]
|
||||
resolver = "3"
|
||||
|
||||
@@ -64,6 +64,9 @@ impl PlaybackHead{
|
||||
state.set_offset(offset);
|
||||
self.timer=Timer::from_state(state,paused);
|
||||
}
|
||||
pub fn set_scale(&mut self,time:SessionTime,new_scale:strafesnet_common::integer::Ratio64){
|
||||
self.timer.set_scale(time,new_scale);
|
||||
}
|
||||
pub fn advance_time(&mut self,bot:&CompleteBot,time:SessionTime){
|
||||
let mut simulation_time=self.time(bot,time);
|
||||
let mut time_float=simulation_time.get() as f64/PhysicsTime::ONE_SECOND.get() as f64;
|
||||
|
||||
@@ -4,8 +4,9 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
pollster = "0.4.0"
|
||||
ratio_from_float = { version = "0.1.0", path = "../ratio_from_float" }
|
||||
strafesnet_roblox_bot_player = { version = "0.1.0", path = "../lib" }
|
||||
pollster = "0.4.0"
|
||||
wgpu = "28.0.0"
|
||||
winit = "0.30.12"
|
||||
strafesnet_common.workspace = true
|
||||
|
||||
@@ -23,6 +23,7 @@ pub struct PlayerWorker<'a>{
|
||||
graphics_thread:Graphics,
|
||||
bot:Option<CompleteBot>,
|
||||
playback_head:PlaybackHead,
|
||||
playback_speed:i8,
|
||||
}
|
||||
impl<'a> PlayerWorker<'a>{
|
||||
pub fn new(
|
||||
@@ -35,6 +36,7 @@ impl<'a> PlayerWorker<'a>{
|
||||
graphics_thread,
|
||||
bot:None,
|
||||
playback_head,
|
||||
playback_speed:0,
|
||||
}
|
||||
}
|
||||
pub fn send(&mut self,ins:TimedInstruction<Instruction,SessionTime>){
|
||||
@@ -48,7 +50,16 @@ impl<'a> PlayerWorker<'a>{
|
||||
Instruction::SessionControl(SessionControlInstruction::SkipBack)=>{
|
||||
self.playback_head.seek_backward(SessionTime::from_secs(5));
|
||||
},
|
||||
Instruction::SessionControl(session_control_instruction)=>{},
|
||||
Instruction::SessionControl(SessionControlInstruction::DecreaseTimescale)=>{
|
||||
self.playback_speed=self.playback_speed.saturating_sub(1).max(-48);
|
||||
let speed=2.0f64.powf(self.playback_speed as f64/3.0);
|
||||
self.playback_head.set_scale(ins.time,ratio_from_float::ratio_from_f64(speed).unwrap());
|
||||
},
|
||||
Instruction::SessionControl(SessionControlInstruction::IncreaseTimescale)=>{
|
||||
self.playback_speed=self.playback_speed.saturating_add(1).min(48);
|
||||
let speed=2.0f64.powf(self.playback_speed as f64/3.0);
|
||||
self.playback_head.set_scale(ins.time,ratio_from_float::ratio_from_f64(speed).unwrap());
|
||||
},
|
||||
Instruction::Render=>if let Some(bot)=&self.bot{
|
||||
self.playback_head.advance_time(bot,ins.time);
|
||||
let (pos,angles)=self.playback_head.get_position_angles(bot,ins.time);
|
||||
|
||||
7
ratio_from_float/Cargo.toml
Normal file
7
ratio_from_float/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "ratio_from_float"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
strafesnet_common.workspace = true
|
||||
128
ratio_from_float/src/lib.rs
Normal file
128
ratio_from_float/src/lib.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
use strafesnet_common::integer::Ratio64;
|
||||
|
||||
/// Convert an `f64` to a `Ratio64`.
|
||||
///
|
||||
/// Returns `None` for NaN, infinities, or when the exact fraction would overflow `i64`/`u64`.
|
||||
/// The result is always reduced to lowest terms.
|
||||
pub fn ratio_from_f64(x: f64) -> Option<Ratio64> {
|
||||
// Handle special values first
|
||||
match x.classify() {
|
||||
core::num::FpCategory::Nan | core::num::FpCategory::Infinite => return None,
|
||||
core::num::FpCategory::Zero => return Ratio64::new(0, 1),
|
||||
core::num::FpCategory::Subnormal | core::num::FpCategory::Normal => {
|
||||
if x < i64::MIN as f64 {
|
||||
return None;
|
||||
}
|
||||
if (i64::MAX as f64) < x {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2️⃣ Pull out the raw bits
|
||||
let bits: u64 = x.to_bits();
|
||||
let sign: i64 = if (bits >> 63) != 0 { -1 } else { 1 };
|
||||
let exp_raw: u32 = ((bits >> 52) & 0x7FF) as u32;
|
||||
let mant: u64 = bits & 0xFFFFFFFFFFFFF; // 52 bits
|
||||
|
||||
// 3️⃣ Normalise exponent and mantissa
|
||||
let (exp, mant) = if exp_raw == 0 {
|
||||
// subnormal
|
||||
(1 - 1023, mant) // unbiased exponent = -1022
|
||||
} else {
|
||||
// normal
|
||||
((exp_raw as i32) - 1023, mant | (1 << 52)) // implicit leading 1
|
||||
};
|
||||
|
||||
// value = sign * mant * 2^(exp-52)
|
||||
let shift = exp - 52; // may be negative
|
||||
|
||||
// 4️⃣ Build numerator / denominator as 64‑bit values
|
||||
// ────────────────────────────────────────
|
||||
// If shift is positive → numerator = mant << shift
|
||||
// If shift is negative → denominator = 1 << (-shift)
|
||||
// We use the checked arithmetic helpers to catch overflow.
|
||||
let (mut num, den) = if shift >= 0 {
|
||||
// shift <= 63 because 53‑bit mantissa * 2^shift must fit in i64
|
||||
let s = shift as u32;
|
||||
let n = (mant as i64).checked_shl(s)?;
|
||||
(n, 1)
|
||||
} else {
|
||||
// shift is negative
|
||||
let s = (-shift) as u32;
|
||||
if s > 63 {
|
||||
// 2^s would not fit in a u64 → underflow
|
||||
return Ratio64::new(0, 1);
|
||||
}
|
||||
(mant as i64, 1u64 << s)
|
||||
};
|
||||
|
||||
// 5️⃣ Apply the sign
|
||||
num *= sign;
|
||||
|
||||
Ratio64::new(num, den)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let r = ratio_from_f64(1.5).unwrap();
|
||||
assert_eq!(r.num(), 3);
|
||||
assert_eq!(r.den(), 2);
|
||||
|
||||
let r = ratio_from_f64(0.1).unwrap();
|
||||
// 0.1 = 3602879701896397 / 36028797018963968
|
||||
assert_eq!(r.num(), 3602879701896397);
|
||||
assert_eq!(r.den(), 36028797018963968);
|
||||
|
||||
let r = ratio_from_f64(-3.141592653589793).unwrap();
|
||||
assert_eq!(r.num(), -884279719003555);
|
||||
assert_eq!(r.den(), 281474976710656);
|
||||
|
||||
// NaN / Infinity → None
|
||||
assert!(ratio_from_f64(f64::NAN).is_none());
|
||||
assert!(ratio_from_f64(f64::INFINITY).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflow() {
|
||||
// value that would need > 64‑bit numerator
|
||||
let f = (i64::MAX as f64) * 2.0; // just above i64::MAX
|
||||
assert!(ratio_from_f64(f).is_none());
|
||||
|
||||
// subnormal: denominator would need 2^1074 > u64::MAX
|
||||
let sub = f64::MIN_POSITIVE / 2.0; // 2.22507e‑308 / 2 = 1.1125e‑308
|
||||
assert_eq!(ratio_from_f64(sub).unwrap().num(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let numbers = [
|
||||
0.0,
|
||||
-0.0,
|
||||
1.0,
|
||||
-1.0,
|
||||
3.141592653589793,
|
||||
1.5,
|
||||
0.1,
|
||||
2.225073858507201e-308, // subnormal
|
||||
1.7976931348623157e308, // max normal
|
||||
];
|
||||
|
||||
for f in numbers {
|
||||
match ratio_from_f64(f) {
|
||||
Some(r) => println!(
|
||||
"{:>15} → {:>15} / {:>15} (≈ {:.20})",
|
||||
f,
|
||||
r.num(),
|
||||
r.den(),
|
||||
f
|
||||
),
|
||||
None => println!("{:>15} → overflow / NaN / infinite", f),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ edition = "2024"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
ratio_from_float = { version = "0.1.0", path = "../ratio_from_float" }
|
||||
strafesnet_roblox_bot_player = { version = "0.1.0", path = "../lib" }
|
||||
strafesnet_common.workspace = true
|
||||
strafesnet_graphics.workspace = true
|
||||
|
||||
Reference in New Issue
Block a user