• Log InLog In
  • Register
Liquid`
Team Liquid Liquipedia
EST 05:16
CET 11:16
KST 19:16
  • Home
  • Forum
  • Calendar
  • Streams
  • Liquipedia
  • Features
  • Store
  • EPT
  • TL+
  • StarCraft 2
  • Brood War
  • Smash
  • Heroes
  • Counter-Strike
  • Overwatch
  • Liquibet
  • Fantasy StarCraft
  • TLPD
  • StarCraft 2
  • Brood War
  • Blogs
Forum Sidebar
Events/Features
News
Featured News
RSL Revival - 2025 Season Finals Preview8RSL Season 3 - Playoffs Preview0RSL Season 3 - RO16 Groups C & D Preview0RSL Season 3 - RO16 Groups A & B Preview2TL.net Map Contest #21: Winners12
Community News
ComeBackTV's documentary on Byun's Career !8Weekly Cups (Dec 8-14): MaxPax, Clem, Cure win4Weekly Cups (Dec 1-7): Clem doubles, Solar gets over the hump1Weekly Cups (Nov 24-30): MaxPax, Clem, herO win2BGE Stara Zagora 2026 announced15
StarCraft 2
General
When will we find out if there are more tournament ComeBackTV's documentary on Byun's Career ! Weekly Cups (Dec 8-14): MaxPax, Clem, Cure win RSL Revival - 2025 Season Finals Preview Weekly Cups (Dec 1-7): Clem doubles, Solar gets over the hump
Tourneys
$5,000+ WardiTV 2025 Championship RSL Offline Finals Info - Dec 13 and 14! Master Swan Open (Global Bronze-Master 2) Winter Warp Gate Amateur Showdown #1: Sparkling Tuna Cup - Weekly Open Tournament
Strategy
Custom Maps
Map Editor closed ?
External Content
Mutation # 504 Retribution Mutation # 503 Fowl Play Mutation # 502 Negative Reinforcement Mutation # 501 Price of Progress
Brood War
General
FlaSh on: Biggest Problem With SnOw's Playstyle screp: Command line app to parse SC rep files How Rain Became ProGamer in Just 3 Months BGH Auto Balance -> http://bghmmr.eu/ [BSL21] RO8 Bracket & Prediction Contest
Tourneys
Small VOD Thread 2.0 [Megathread] Daily Proleagues [BSL21] WB SEMIFINALS - Saturday 21:00 CET [BSL21] RO8 - Day 2 - Sunday 21:00 CET
Strategy
Game Theory for Starcraft Current Meta Simple Questions, Simple Answers Fighting Spirit mining rates
Other Games
General Games
Stormgate/Frost Giant Megathread Path of Exile Nintendo Switch Thread General RTS Discussion Thread Dawn of War IV
Dota 2
Official 'what is Dota anymore' discussion
League of Legends
Heroes of the Storm
Simple Questions, Simple Answers Heroes of the Storm 2.0
Hearthstone
Deck construction bug Heroes of StarCraft mini-set
TL Mafia
Mafia Game Mode Feedback/Ideas Survivor II: The Amazon Sengoku Mafia TL Mafia Community Thread
Community
General
Russo-Ukrainian War Thread US Politics Mega-thread Things Aren’t Peaceful in Palestine The Games Industry And ATVI YouTube Thread
Fan Clubs
White-Ra Fan Club
Media & Entertainment
Anime Discussion Thread [Manga] One Piece Movie Discussion!
Sports
2024 - 2026 Football Thread Formula 1 Discussion
World Cup 2022
Tech Support
Computer Build, Upgrade & Buying Resource Thread
TL Community
TL+ Announced Where to ask questions and add stream?
Blogs
The (Hidden) Drug Problem in…
TrAiDoS
I decided to write a webnov…
DjKniteX
James Bond movies ranking - pa…
Topin
Thanks for the RSL
Hildegard
Customize Sidebar...

Website Feedback

Closed Threads



Active: 1528 users

miss chance, rng, and some code experiments

Forum Index > BW General
Post a Reply
quaristice
Profile Joined February 2021
114 Posts
January 29 2023 14:23 GMT
#1
TLDR: miss chance basically works correctly/as expected as far as i can tell on deeper investigation? just it's nice to have some confirmation

so i think many people here probably know of the 1/256 chance "airshot" miss chance, as well as the 120/256 miss chance against uphill/under cover (non-stacking), but if you're not familiar, please check out the liquipedia page. TL user StaticNine also tested those numbers ingame in the past on their blog here.

DISCLAIMER: I'M NOT A STATISTICIAN OR ANYTHING RESEMBLING ONE AND PROBABILITY IS HARD

i haven't really had a chance to play much in the past few months because i've been moving continents and my ergonomic situation has been awful (bad furniture). and i don't want to aggravate my RSI. so i guess i am doing this type of shit instead of getting better at the game. but i got a bit curious watching a stream since i saw one of those 1/256 airshots happen. i feel like it's so rare to see them during gameplay, rarer than 1/256. i wanted to investigate and see if i could find any evidence to back this up.

i did this research based on the OpenBW github, so thanks to them.

so the RNG in BW is a linear congruential generator
specifically, it's the same as the LCG RNG from Borland C++.

Here's the relevant bits of code:
+ Show Spoiler +

https://github.com/OpenBW/openbw/blob/master/bwgame.h#L13187

st.lcg_rand_state = st.lcg_rand_state * 22695477 + 1;
return (st.lcg_rand_state >> 16) & 0x7fff;


https://github.com/OpenBW/openbw/blob/master/bwgame.h#L14370

fp8 unit_dodge_chance(const unit_t* u) const {
if (u_flying(u)) return 0_fp8;
if (unit_is_under_dark_swarm(u)) return 255_fp8;
if (st.tiles[tile_index(u->sprite->position)].flags & tile_t::flag_provides_cover) return 119_fp8;
return 0_fp8;
}

fp8 unit_target_miss_chance(const unit_t* u, const unit_t* target) const {
fp8 r = unit_dodge_chance(target);
if (!u_flying(u) && !u_flying(target)) {
if (get_ground_height_at(target->sprite->position) > get_ground_height_at(u->sprite->position)) {
if (r < 119_fp8) r = 119_fp8;
}
}
return r;
}

https://github.com/OpenBW/openbw/blob/master/bwgame.h#L14438

fp8::from_raw(lcg_rand(1) & 0xff) <= unit_target_miss_chance(bullet_owner_unit, target_unit)



the key thing in that code is the <=, and 0 for just normal, and the 119 for shooting up a cliff. the way the RNG is used there, gives a value from [0 to 255]. because of the <= have the equal in there, when the RNG rolls 0, you get a miss no matter what.

i did a bunch of testing by writing some rust code to replicate the rng, with different starting seeds.

the code is in the spoiler below if you want it, or if you just want to fuck around with it online, here's a rust playground link
+ Show Spoiler +


use std::num::Wrapping;

fn main() {
/////////////////////////////////////////////////////////////////
// constants to tweak here
let reroll_count = 65536; // how much should we reroll off a starting seed

// 0 normally, 119 for shooting uphill/under trees
let target = 0;
//let target = 119;

// -trial_range to trial_range as seed
let trial_range = 10000;

// how many misses in a row is weird
let outlier_chain_length = 10;

// the LCG seems to behave very mildly worse centered around initial seed 0
// so this gets added to +-trial_range
let trial_offset = 0;
// no more constants
/////////////////////////////////////////////////////////////////



// finding outliers
let expected_to_find = (reroll_count * (target+1)) / 256;

let found_threshold = expected_to_find / 4;

let found_threshold_high = expected_to_find + found_threshold;
let found_threshold_low = expected_to_find - found_threshold;
//


let mut found_count = 0;
let mut trial_count = 0;
let mut sequential_found_count = 0;
let mut sequential_not_found_count = 0;

let mut unusual_chain_count = 0;
let mut longest_chain = 0;

let mut loop_body = |start_state| {
let prev_found = found_count;

let mut lcg_rand_state = Wrapping(start_state);

let mut previous_roll_was_found = false;
let mut chain_length = 0;

for _ in 0..reroll_count
{
let (new_state, result) = lcg_rand(lcg_rand_state);

lcg_rand_state = new_state;

if (result & Wrapping(0xFF)) <= Wrapping(target)
{
found_count += 1;

if previous_roll_was_found {
sequential_found_count += 1;
}

previous_roll_was_found = true;
chain_length += 1;
} else {
if !previous_roll_was_found {
sequential_not_found_count += 1;
}

previous_roll_was_found = false;

if chain_length > outlier_chain_length {
unusual_chain_count += 1;
}

if chain_length > longest_chain {
longest_chain = chain_length;
}
chain_length = 0;
}
}

// finding outliers
let delta_found = found_count - prev_found;
if delta_found > found_threshold_high || delta_found < found_threshold_low {
println!("unusual number {} found for start seed {} outside range {} to {} centered around {}", delta_found, start_state, found_threshold_low, found_threshold_high, expected_to_find);
}

trial_count += 1;
};

// uncomment this and comment out the loop to test a specific seed
//loop_body(7);

for start_state
in -trial_range+trial_offset..trial_range+trial_offset
{
loop_body(start_state);
}

let total_count = trial_count * reroll_count;
println!(
"{} instances found in {} trials of {} rolls ({} total rolls)",
found_count, trial_count, reroll_count, total_count);
println!("{} were sequential misses", sequential_found_count);
println!();

//let reroll_count = reroll_count as f64;
let found_count = found_count as f64;
let total_count = total_count as f64;
let miss_rate = (found_count / total_count) * 100.0;
let miss_in = total_count / found_count;
let expected_rate = ((target as f64 + 1.0)/256.0) * 100.0;
println!("\n\t{}% miss rate", miss_rate);
println!("or roughly 1 in {}", miss_in as usize);
println!("in contrast to\n\t{}% expected", expected_rate);
println!("off by\n\t{}%", miss_rate - expected_rate);

println!();

let sequential_found_count = sequential_found_count as f64;
let sequential_miss_rate = sequential_found_count/found_count * 100.0;
let sequential_miss_in = found_count / sequential_found_count;

println!("{}% sequential miss rate",sequential_miss_rate);
println!("or roughly 1 in {}", sequential_miss_in as usize);
println!("in contrast to\n\t{}% expected", expected_rate);
println!("off by\n\t{}%", sequential_miss_rate - expected_rate);

println!();
let sequential_not_found_count = sequential_not_found_count as f64;
let sequential_hit_rate = sequential_not_found_count/(total_count-found_count) * 100.0;
println!("{}% sequential hit rate",sequential_hit_rate);
println!("in contrast to\n\t{}% expected", 100.0-expected_rate);
println!("off by\n\t{}%", sequential_hit_rate - (100.0-expected_rate));

println!();
println!("found {} chains of {} or more misses in a row", unusual_chain_count, outlier_chain_length);
println!("longest chain was {} misses in a row", longest_chain);
}


fn lcg_rand(lcg_rand_state : Wrapping<i32>) -> (Wrapping<i32>, Wrapping<i32>) {
let lcg_rand_state = lcg_rand_state * Wrapping(22695477) + Wrapping(1);

(lcg_rand_state, (lcg_rand_state >> 16) & Wrapping(0x7fff))
}


so for just raw percentages, everything seems fine. the biggest unexpected results was the uphill miss results for starting seed 2 being off by 0.37~% in 65535 rolls, which, is really not much at all.

next i examined "probability of a miss if the last roll was a miss" and "probability of a hit if the last roll was a hit", and i had roughly similar results. this assumes that there aren't other RNG rolls in the meantime, which is a pretty significant assumption. again some random seeds are a tiny bit "off" here, for example starting seed 4 with 65535 rolls gives is "off" by 0.72~% repeated successful hits against uphill. pretty mild stuff.

so yeah. idk. seems like it works as described. 1/256 chance and everything. if there's something weird, it might be something more periodic like "once every 4th roll". idk. or something i missed. i just really feel instinctively that 1/256 should happen more often than it actually does, it's real weird. like it's 0.390625%, right? and i compare it to rolling two d20s in a row and getting a 20 on both and that's 0.25%, which, if you've played any of those d20-using tabletop rpgs, i swear is more common than the starcraft "airshot". so what's up with that. maybe it's just that they're more memorable in other contexts and the airshots are usually not super noticeable.
LUCKY_NOOB
Profile Blog Joined June 2013
Bulgaria1506 Posts
January 30 2023 16:08 GMT
#2
StaticNine: "Even during Misses the zergling still received splash damage." Rigged against Zerg!

If you ask most Ts and Ps would say misses happen too often in their opinion.

I hope ur tests confirm nothing is amiss. (pun intended) ^_._^
ko-fi.com/luckynoob
Mutaller
Profile Blog Joined July 2013
United States1051 Posts
January 30 2023 18:13 GMT
#3
When looking at the air shot there could be a possibility that it feels lower than the dnd two d20s in a row, because how often does a player play with a singular air unit? The single air unit is going to be hard to notice. I love microing mutas and I think I probably have blamed my self for my mutas not all attacking on my micro rather than the mischance
"To practice isn't for you to get better now in the present. Practice will never betray you and will always come back for you in the future." -Jaedong
quaristice
Profile Joined February 2021
114 Posts
January 30 2023 20:42 GMT
#4
On January 31 2023 03:13 Mutaller wrote:
When looking at the air shot there could be a possibility that it feels lower than the dnd two d20s in a row, because how often does a player play with a singular air unit? The single air unit is going to be hard to notice. I love microing mutas and I think I probably have blamed my self for my mutas not all attacking on my micro rather than the mischance

i should've been more clear, the 1/256 airshot miss only occurs on same-height no-tree ground vs ground fighting.
the name is kind of confusing, just the liquipedia page and old TL.net threads from 10+ years ago call it an airshot.
LML
Profile Blog Joined March 2007
Germany1774 Posts
January 30 2023 22:46 GMT
#5
The air shot happens every time an SCV has 10 hp left and your goon fires a shot and you already tell it to go somewhere else while its energy ball is still in the air.
LML
Please log in or register to reply.
Live Events Refresh
Next event in 44m
[ Submit Event ]
Live Streams
Refresh
StarCraft 2
SortOf 197
StarCraft: Brood War
GuemChi 3526
Rain 1659
actioN 745
Bisu 664
Calm 542
Pusan 337
Shuttle 301
Mini 233
Soma 231
Killer 183
[ Show more ]
Horang2 159
Rush 111
sorry 70
Dewaltoss 63
Bale 44
Mind 43
soO 39
Mong 37
ggaemo 36
JYJ 31
NaDa 25
Barracks 24
Sacsri 16
Noble 10
ajuk12(nOOB) 10
910 9
Terrorterran 8
SilentControl 6
Shinee 1
Dota 2
XcaliburYe179
League of Legends
JimRising 416
C9.Mang0402
Counter-Strike
summit1g6496
olofmeister816
shoxiejesuss726
allub198
Other Games
ceh9645
crisheroes312
XaKoH 152
Trikslyr20
Organizations
StarCraft: Brood War
lovetv 13
StarCraft 2
Blizzard YouTube
StarCraft: Brood War
BSLTrovo
sctven
[ Show 13 non-featured ]
StarCraft 2
• AfreecaTV YouTube
• intothetv
• Kozan
• IndyKCrew
• LaughNgamezSOOP
• Migwel
• sooper7s
StarCraft: Brood War
• iopq 5
• BSLYoutube
• STPLYoutube
• ZZZeroYoutube
Dota 2
• Noizen22
League of Legends
• Jankos2296
Upcoming Events
WardiTV 2025
44m
ByuN vs Creator
Clem vs Rogue
Scarlett vs Spirit
ShoWTimE vs Cure
OSC
3h 44m
Big Brain Bouts
6h 44m
YoungYakov vs Jumy
TriGGeR vs Spirit
The PiG Daily
10h 44m
SHIN vs ByuN
Reynor vs Classic
TBD vs herO
Maru vs SHIN
TBD vs Classic
CranKy Ducklings
23h 44m
WardiTV 2025
1d
Reynor vs MaxPax
SHIN vs TBD
Solar vs herO
Classic vs TBD
SC Evo League
1d 2h
Ladder Legends
1d 8h
BSL 21
1d 9h
Sziky vs Dewalt
eOnzErG vs Cross
Sparkling Tuna Cup
1d 23h
[ Show More ]
Ladder Legends
2 days
BSL 21
2 days
StRyKeR vs TBD
Bonyth vs TBD
Replay Cast
2 days
Wardi Open
3 days
Monday Night Weeklies
3 days
WardiTV Invitational
5 days
Replay Cast
5 days
WardiTV Invitational
6 days
ByuN vs Solar
Clem vs Classic
Cure vs herO
Reynor vs MaxPax
Liquipedia Results

Completed

Acropolis #4 - TS3
RSL Offline Finals
Kuram Kup

Ongoing

C-Race Season 1
IPSL Winter 2025-26
KCM Race Survival 2025 Season 4
YSL S2
BSL Season 21
Slon Tour Season 2
CSL Season 19: Qualifier 1
WardiTV 2025
META Madness #9
eXTREMESLAND 2025
SL Budapest Major 2025
ESL Impact League Season 8
BLAST Rivals Fall 2025
IEM Chengdu 2025
PGL Masters Bucharest 2025
Thunderpick World Champ.
CS Asia Championships 2025
ESL Pro League S22

Upcoming

CSL Season 19: Qualifier 2
CSL 2025 WINTER (S19)
BSL 21 Non-Korean Championship
Acropolis #4
IPSL Spring 2026
Bellum Gens Elite Stara Zagora 2026
HSC XXVIII
Big Gabe Cup #3
OSC Championship Season 13
ESL Pro League Season 23
PGL Cluj-Napoca 2026
IEM Kraków 2026
BLAST Bounty Winter 2026
BLAST Bounty Winter Qual
TLPD

1. ByuN
2. TY
3. Dark
4. Solar
5. Stats
6. Nerchio
7. sOs
8. soO
9. INnoVation
10. Elazer
1. Rain
2. Flash
3. EffOrt
4. Last
5. Bisu
6. Soulkey
7. Mini
8. Sharp
Sidebar Settings...

Advertising | Privacy Policy | Terms Of Use | Contact Us

Original banner artwork: Jim Warren
The contents of this webpage are copyright © 2025 TLnet. All Rights Reserved.