225 points jheitmann 4 days ago 42 comments
pema99 4 days ago | parent
jvanderbot 17 hours ago | parent
TL;DR: Wired up an in-game predictor of RNG output and used it to only craft legendary items when RNG would line up to roll legendary.
From base to legendary at a suprisingly high rate. Look very closely at the video at the top of the post - what I was seeing didn't sink in until I had finished the article. Amazing.
lowbloodsugar 17 hours ago | parent
strstr 16 hours ago | parent
The prng was seeded with usec time at first call. I called the rng a bunch of times to harvest entropy, and scanned the plausible usec times to find the seed. Then I primed the prng so I would win ties.
Frankly, I assume I implemented this wrong, but the theory was there lol.
rogueaine 16 hours ago | parent
strstr 16 hours ago | parent
> Sampling the current RNG through observations,
> Computing the current internal RNG state,
> Predicting the future internal states,
> Calculating corresponding quality levels for each future call, and finally
> Making use of the predicted levels with some adapters.
The entropy->seeds (internal RNG state) step took more math of course. Frankly, I wouldn’t be surprised if they could have extracted the seeds without the math with a bit of RE and memory inspection.
The version I did wasn’t predicting quality of course, it was predicting tie breakers
3eb7988a1663 12 hours ago | parent
e28eta 5 hours ago | parent
google surfaces a couple of HN posts, but the source (cigital.com) seems to be a dead domain at this point:
https://news.ycombinator.com/item?id=288138
https://news.ycombinator.com/item?id=9914607
It's a bad shuffle implementation + using time of day as seed (reducing search space). Using the player's 2 cards and the 3 flop cards, it finds the RNG seed in real time, and then future hands (on the same server) are solved in "under one second!"
myhf 15 hours ago | parent
PennRobotics 15 hours ago | parent
Except! On the Switch, you can't easily access the save file AND the random number generator is different than on PC. There is a seed cracker that looks at your traveling cart listing and calculates the character seed. Maybe because it's less important and harder to observe, I haven't found any tool to crack the other seed and don't have time to attempt writing it myself.
By inspecting cracked geode contents, you should be able to isolate your multiplayer ID and then predict random events on Nintendo just as PC players have done for the last decade with access to the save file.
The C# code for the game is online and the Switch RNG is known, so you never have to work in the dark. It's three steps: ensure your Switch RNG implementation works by testing against the normal seed, ensure your geode RNG implementation works by testing against the PC RNG, and then apply the Switch RNG to the geode function enough times that only one seed could create your observed sequence.
-----
Two semi-related open questions: Are you able to solve as quickly while starting at ANY geode as you'd be solving from the first geode? Does the RNG eventually repeat, so it actually doesn't matter what your multiplayer ID is as long as you observe a unique sequence, since there will only be one continuation of that sequence?
bombcar 12 hours ago | parent
nomel 10 hours ago | parent
xoxxala 7 hours ago | parent
thaumasiotes 5 hours ago | parent
(It's then filtered through the list, so the list needs to have good pseudorandomness properties anyway, but still.)
eru 2 hours ago | parent
Not sure? Suppose your list only had two number 0 and 1, and you build your random numbers one bit at a time.
Or more realistically, you have 256 numbers on the list 0, 1, 2, ..., 255 in order. If the 'large numbers of players drawing from the same list' assumption holds, it doesn't matter much that the list is in order.
What's just a bit weird is why anyone would want to turn an embarrassingly parallel problem into something with a sequential bottleneck?
thaumasiotes 2 hours ago | parent
Why not? That should convert your random number generation into draws from a Poisson process. If you were looking to simulate a Poisson distribution, you're set. If not, you probably just ruined your RNG.
(If the idea is that the interval between any two samples is so large that the list will inevitably be cycled several times before any one person can sample a second byte, there's something to that. It's going to make asking for random numbers more than 8 bits long challenging though.)
eru 1 minute ago | parent
'Yield' to other players' threads or processes after each byte you draw.
PennRobotics 5 hours ago | parent
the closely related not-at-all-random fizzlefade from Wolfenstein: https://fabiensanglard.net/fizzlefade/
vladde 3 hours ago | parent
vlyan 15 hours ago | parent
I'm an upper intermediate at Factorio, resorting to someone else's blueprints only for belt balancers and rail intersections, and I can't even begin to figure out how it's done.
Aardwolf 15 hours ago | parent
That's an RNG from 1996. It seems neither recent C++ standards, nor boost, know anything about the modern PRNGs that are much faster yet better at passing test suites
EDIT: Ok the above quote was from 2014, and boost seems to know some now! https://www.boost.org/doc/libs/latest/doc/html/boost_random/...
Founderarcstone 14 hours ago | parent
jason_s 13 hours ago | parent
hbroom 13 hours ago | parent
torvin92 13 hours ago | parent
harlan_pdx 12 hours ago | parent
jatins 6 hours ago | parent
cmovq 11 hours ago | parent
typedef xor_combine_engine<
xor_combine_engine<
linear_feedback_shift_engine<uint32_t, 32, 31, 13, 12>, 0,
linear_feedback_shift_engine<uint32_t, 32, 29, 2, 4>, 0>, 0,
linear_feedback_shift_engine<uint32_t, 32, 28, 3, 17>, 0> taus88;mitxela 7 hours ago | parent
ainiriand 3 hours ago | parent
windenntw 3 hours ago | parent
To do it in almost-plain C, you'd need fairly complex macros that are more difficult to write correctly.
To do it in plain C without macros, or plain C++ without templates... you'd need to work out the combination of the template expansion yourself and write down a piece of code that is more likely to have bugs and more difficult to understand.
eru 2 hours ago | parent
EDIT: I just had an AI agent run the experiment. At least for my version of clang, they produce the same assembly for x86_64 (modulo using slightly different registers).
windenntw 3 hours ago | parent
By making 3 instances of linear_feedback_shift_engine class template ( and 2 of xor_combine_engine ), you are forcing the compiler to expand the code exactly as-is 3 times, each with different parameters.
The parameters to the template are constant, therefore the compiler can easily look at how they are used and you are guaranteed ( even in a 1999 c++ compiler ) that the compiler will look at the copies of the code and merge them as much as possible into a single piece of code... which is the one that you see when you decompile the code.
So in summary, it means that you get to write fairly readable code while the final binary is fully optimized as-if you had spent the time merging all the variants as needed for the specific constants.
eru 2 hours ago | parent
chaz6 5 hours ago | parent
https://devblogs.microsoft.com/cppblog/bringing-correctly-ro...
It mentions how they had to use a custom math library to differences in results on different platforms causing multiplayer sync issues.
FrustratedMonky 3 hours ago | parent
jtrn 2 hours ago | parent
renyicircle 1 hour ago | parent