From 9f3ac3f0b9856dddb7662deba3227f7573f360d6 Mon Sep 17 00:00:00 2001 From: WiseDev <83840010+wisedevik@users.noreply.github.com> Date: Sun, 23 Aug 2026 14:21:57 +0300 Subject: [PATCH] use the client's square root, wrong answers and all LogicMath::sqrt is a 256-entry table of floor(16*sqrt(i)) with a seed picked by magnitude and one or two newton steps on top, and it is not exact: above 2147441940 the seed overshoots and the single correction cannot pull it back, so it answers 46341 where the true root is 46340, and 65535 for INT_MAX. that matters because the checksum is computed over whatever it returns. an honest square root would be a permanent, invisible disagreement, so this one is transcribed branch for branch - including the early -1 for negatives and the INT_MAX special case - and our own converging root is gone. checked against the exact root across the low range and the boundaries, where the two agree, and at the top, where they must not. distances now go through the saturating helper before the root, the way the client does it, rather than being squared in i64 on the way in. --- crates/logic/src/battle/logic_simulation.rs | 20 +----- crates/logic/src/lib.rs | 2 + crates/logic/src/logic_math.rs | 74 +++++++++++++++++++++ 3 files changed, 79 insertions(+), 17 deletions(-) create mode 100644 crates/logic/src/logic_math.rs diff --git a/crates/logic/src/battle/logic_simulation.rs b/crates/logic/src/battle/logic_simulation.rs index fdad36e..59c6e15 100644 --- a/crates/logic/src/battle/logic_simulation.rs +++ b/crates/logic/src/battle/logic_simulation.rs @@ -96,18 +96,6 @@ impl LogicCharacterStats { self.speed == 0 } } -pub fn integer_sqrt(value: i64) -> i64 { - if value <= 0 { - return 0; - } - let mut root = value; - let mut next = (root + 1) / 2; - while next < root { - root = next; - next = (root + value / root) / 2; - } - root -} pub fn distance_squared(from: (i32, i32), to: (i32, i32)) -> i32 { let dx = to.0 - from.0; let dy = to.1 - from.1; @@ -353,16 +341,14 @@ impl LogicBattle { }; let from = positions[index]; let stand_off = stats.range; - let straight = integer_sqrt( - ((goal.0 - from.0) as i64).pow(2) + ((goal.1 - from.1) as i64).pow(2), - ); + let straight = crate::logic_sqrt(distance_squared(from, goal)) as i64; if straight <= stand_off as i64 || straight == 0 { continue; } let waypoint = self.next_waypoint(index, from, goal, stats.flying); let dx = (waypoint.0 - from.0) as i64; let dy = (waypoint.1 - from.1) as i64; - let leg = integer_sqrt(dx * dx + dy * dy); + let leg = crate::logic_sqrt(distance_squared(from, waypoint)) as i64; if leg == 0 { continue; } @@ -424,7 +410,7 @@ impl LogicBattle { if square > sum.saturating_mul(sum) { continue; } - let distance = integer_sqrt(square as i64).max(1); + let distance = crate::logic_sqrt(square).max(1) as i64; let other_mass = if other_stats.is_building() { MASS_FOR_STATIC } else { diff --git a/crates/logic/src/lib.rs b/crates/logic/src/lib.rs index 270eb6e..ad93664 100644 --- a/crates/logic/src/lib.rs +++ b/crates/logic/src/lib.rs @@ -6,7 +6,9 @@ pub mod factory; pub mod home; pub mod logic_random; pub mod messages; +pub mod logic_math; pub mod model; +pub use logic_math::{logic_sqrt, SQRT_TABLE}; pub use commands::{ chest_source, command_type, CommandMeta, CommandOutcome, Execute, LogicBuyCardCommand, LogicBuyChestCommand, LogicBuyResourcePackCommand, LogicClaimAchievementRewardCommand, diff --git a/crates/logic/src/logic_math.rs b/crates/logic/src/logic_math.rs new file mode 100644 index 0000000..d88d896 --- /dev/null +++ b/crates/logic/src/logic_math.rs @@ -0,0 +1,74 @@ +pub const SQRT_TABLE: [i32; 256] = [ + 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, + 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, 86, 87, 89, + 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109, + 110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, + 156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, + 169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180, + 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, + 192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201, + 202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, + 212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, + 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, + 230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, + 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, + 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255, +]; +pub fn logic_sqrt(value: i32) -> i32 { + if value < 0x10000 { + if value < 256 { + if value < 0 { + return -1; + } + return SQRT_TABLE[value as usize] >> 4; + } + let seed = if value < 4096 { + if value < 1024 { + SQRT_TABLE[(value >> 2) as usize] >> 3 + } else { + SQRT_TABLE[(value >> 4) as usize] >> 2 + } + } else if value < 0x4000 { + SQRT_TABLE[(value >> 6) as usize] >> 1 + } else { + SQRT_TABLE[(value >> 8) as usize] + }; + let next = seed + 1; + return if next.wrapping_mul(next) > value { seed } else { next }; + } + let refined = if value < 0x1000000 { + let seed = if value < 0x100000 { + if value < 0x40000 { + 2 * SQRT_TABLE[(value >> 10) as usize] + } else { + 4 * SQRT_TABLE[(value >> 12) as usize] + } + } else if value < 0x400000 { + 8 * SQRT_TABLE[(value >> 14) as usize] + } else { + 16 * SQRT_TABLE[(value >> 16) as usize] + }; + (seed | 1) + value / seed + } else { + let seed = if value < 0x10000000 { + if value < 0x4000000 { + 32 * SQRT_TABLE[(value >> 18) as usize] + } else { + SQRT_TABLE[(value >> 20) as usize] << 6 + } + } else if value < 0x40000000 { + SQRT_TABLE[(value >> 22) as usize] << 7 + } else { + if value == i32::MAX { + return 0xFFFF; + } + SQRT_TABLE[(value >> 24) as usize] << 8 + }; + let half = ((seed | 1) + value / seed) >> 1; + value / half + half + 1 + }; + let root = refined >> 1; + root - i32::from(root.wrapping_mul(root) > value) +}