 Hey guys, welcome back skits own episode 8 topic today is randomness Random integers random floats and how we can use randomness in assembly And ultimately I don't see too much value to randomness in engineering Maybe for Monte Carlo simulations, maybe for that cryptographic mumbo-jumbo that people do Really though I see value for randomness in just games you guys may be familiar with this game show deal or no deal basically you had bunch of cases between like a penny and a million dollars and You could pick one and keep it, but you couldn't open it instead You could open up other cases and see what was in them kind of narrow down What's in your case based off the cases that you've opened and then every once in a while the banker would give you an appraisal of Your case And you could take it or leave it and that's why the show is called deal or no deal I bring that up because obviously it involves randomness and I implemented that randomness in Assembly here and this is no libraries nothing. So if I run this binary We get an implementation of two or no deal in assembly So I'll pick a case pick 17 and you can see it gives us a running Expected value of the total number of cases that have been unopened so far. So I Can begin to open cases case 0 case 1 case 2 Case 3 that was a bad case case 4 case 5. Anyway, you can see that these cases are Randomly shuffled and we'll talk about how that works in in today's video So you can see I've got a an appraisal of 157 k I'll take that. That's a decent amount of money for me. So let's sell That was a bad deal because our case apparently had 3 fourths of a million inside. So that kind of sucks. But anyway, who cares? I'm not gonna be too sad about that so The topic today is randomness and the question We'll begin with is how do we get your random number? So It's pretty straightforward. We have two instructions to use we have Rd seed and Rd Rand They both are pretty much interchangeable instructions and they both use the hardware noise on the computer in a different way This is how I understand it. I could be wrong about this by the way But the way I see it is that there's some kind of hardware noise. Maybe it's thermal. Maybe it's electrical noise and Rd seed takes that noise itself or maybe with some manipulation as the output value and That would be kind of like a true RNG because it's just querying or probing or measuring some quantity on the processor as noise So that's going to be slow But it's also going to be have having more entropy than the alternative being Rd Rand Which is a pseudo random number generator that is seeded once in a while by the hardware noise So this is faster, but also less random than Rd seed We're going to use Rd Rand in this series because I don't care about entropy. Honestly, I just want to random value If you cared about that stuff, you could use Rd seed as a drop-in replacement for what I'm going to show you in this video So just one more thing about pseudo RNG You guys may be familiar with like the the XOR shift algorithm and things like that Basically these just turn numbers into other numbers. Let's say your first number was I don't know like seven That would be your let's say that was your seed. You could use that seed for your next number Let's say it was 13 and then you can use that seed for your next number I don't know three right and you could basically Keep using that XOR shift or whatever algorithm you're using to generate new numbers from the previous number By using that as basically the previous number as the seed I guess the advantage to this is that you don't have to pick a seed You can use the hardware noise as a seed every once in a while So that's kind of the the cool thing about how the way Rd Rand works Okay, cool How do we use these instructions? Well, there's the complicated answer and then there's an easy answer. So complicated answer is well, you can pass in Put to both Rd seed and Rd Rand either a 16 32 or 64 bit register and it will drop a bunch of zeros and ones in there for you So you type Rd Rand Rx or Rd seed Rx and both those instructions will give you 64 Random zeros and ones in Rx So pretty straightforward There's one caveat. However, and if you read the documentation it says that the Takes some time for the Randomness to to work right either to to query the hardware You know noise or whatever it's doing behind the scenes And so we have to check if there is a random value available for us or not and to do that It says here the carry flag indicates whether a random value is available and So all you have to do is check the state of the carry flag When you're generating your number, so here's how you would do it you'd have a loop and You'd try attempts to fill Rx with zeros and ones and check the carry flag if it was not set You would try again over and over and over and over again in my experience this never happens like from what I could tell you could comment this out and It works just fine, but just to be safe Put the slope in there just in case it matters on your computer Okay How can we use those random zeros and ones to get a an integer of consequence like a random number that we care about? Let's say you wanted a dice roll. Let's say you wanted a value between one and six so I have this function here called rent int which returns a Assigned long integer value and it takes in two inputs one being the lower bound One being upper bound so if you wanted a dice roll between one and six you'd pass RDI is one and RSI is six and then it would put Rx somewhere between those two Inclusive of the boundaries So how does this algorithm? Work I call it an algorithm. It's not really not with them the first three instructions and the last three are just management of the I guess registers again our calling convention in this series is that no register values are allowed to change state During a function apart from the output register so again, we save and restore those register values and Then here you can see those three Instructions from before actually two instructions which just make sure that we've generated and You know an available number in Rx and Then the question is how can we turn those zeros and ones into a Integer between one and six because remember 64 zeros and ones is not gonna be between one and six I'll tell you that right now and so the way that works is You have to use division and so if I remember correctly the way division works is you basically have RDX and Rax as As like a hundred and twenty eight bit long Register and then you can divide it by something so here you can see I'm using this instruction div RSI It basically divides that hundred and twenty eight bits in there by the 64 bits in here And when you when you do that in RDX You'll have the remainder and then in Rax You'll have the result of the division So we can use that to our advantage by basically putting a range in RSI So let's say for example this random number was like a million and RSI was six Then this operation would yield some random garbage in Rax that we don't care about that would be dividing a million by six Whatever that happens to be and it will be putting the remainder of the division in RDX that remainder is always gonna be between I guess zero and five in that case and Then we can use that for our random number right our bounded number. That's what we're doing here We're basically doing exactly that We are X soaring RDX That sets those high 64 bits to zero and the low 64 bits those are just actually we don't have to even do that We could leave it the way it was to be honest because we don't care about the actual result But we may as well X or it just for completeness sake Then what's RSI RSI is our range so you can see it was originally our Upper bound we incremented it by one and it'd be subtracted off the lower bound So at the end of the day, it's it's the range Then we divide that Rax by the range and then RDX again contains the remainder of that division We can add that remainder to our lower bound Remember RDX was gonna be between zero and five so we can add Between zero and five to one and get between one and six and then return that value. So that's pretty much how this operation works so we can basically Adjust our random number between our boundaries and Again, this instruction it could be skipped. I think With no ill effects, but I really don't care. It's a very fast instruction anyway, so All right, cool. What about a random float? So again, 64 zeros and ones is great in all, but it's not a float Let's not a foot that you would want. Maybe you want a foot between Zero and a hundred you have to somehow bound the problem between those two and the way I've done this is kind of interesting I have this function called Rand float Basically again, it returns a value a double precision floating point value in X and M zero And it takes these two bounds lower bound also in X and M zero upper bound in X and M one and it Returns of value between those two bounds So again these first four instructions and the last four instructions here those just manage the registers and preserve them across function calls and And then these three lines that puts random zeros and ones in rex for us And the last was that six lines actually do the heavy lifting of converting that zeros and ones string into a float between our bounds and so Again, I have a a range in X and M one So I converted our upper and lower bound to be a range Then what I could do is I basically took the random zeros and ones in rex and first I shifted off The leading zero leading sign bit either zero or one So now it's just 63 bits of zeros and ones That means I'm gonna have a positive value basically in the register This instruction here I mentioned in a previous video This converts that integer value into a floating point value with the same value basically so this turns our random sixty three zeros and ones integer into basically a float of the same value in X and M two then I'm multiplying that quantity that more or less integer quantity, but floating point represented by tiny what is tiny tiny is a value That is one half to the neck to the sixty third. So it's very small quantity So basically I'm turning our random number into a very very very tiny quantity in X and M two Then I'm multiplying that tiny quantity by our range and Adding it To our start value. So basically we're we're we're scaling between basically zero and one our range So it pretty straightforward not too complicated. I would say it's even easier than before Because there's no remainders or division or stuff like that just straight up multiplication. So that's how that works With that out of the way, let's look at the code. So I have four Examples here plus deal or no deal. I have two examples that do Integers and two that do floats and then I have two options for single values and two options for an array of Random values. So we'll talk about how that looks right now So let me hop into the virtual machine So you can check out all the code in the soy hubs depository by the way link in the description Here are our five examples. Let's go through each one. So example a that was our random integer. How does this work? Well What are my includes I have an include for that random integer function I have a print integer function and I have a Print character function to print out a new line. I think so Here's how that works The first three lines. That's just me setting the boundaries. So RDI zero. That's our lower bound RSI a hundred. That's our upper bound and then we're calling rent into that generates a random integer value and we print it out and Exit the program. So I run this we should get a single value on the screen between zero and a hundred So let's just test that out 68 Looks good to me and we can generate more as well. Sorry like this thirty six twenty eight fifty five seventy eight eight Etc. Okay, cool example B How about this in this case? We are doing an array of random integers. So We have a function called random int array and we have our function from before called print Array int so we can basically do the same thing as before Just with an array. So I have a memory location here. That's sixteen quad words long It starts off being zeros, but we're gonna fill it with random values. So you can see this six lines Basically fill that array with values between zero and a thousand typo, let's change this to a thousand So when I run this we should be printing out sixteen values between zero and a thousand to the screen So let's do that What why am I why am I leaving them out of them? So here you can see I've got values between zero and a thousand and I can do that multiple times and get different values each time Cool, how about example C? This is floats. So if I open up the code, you can see how this works Again, I have that random float function and our print float function from the previous video And as you would expect we have those Lower and upper bound being set to values from memory. So zero and a hundred floating point values in Memory, then we call random float And we print it out. So very very straightforward. I run this we should get a value between zero and a hundred in floating point Which we do And I can run that multiple times and get different values each time cool Next example example D the same thing just going to be for an array of floats in this case I have a Function that generates an array of floats. Why would you want this? Maybe you want to have a bunch of random quantities at once Let's say you have a game where you have a bunch of enemies appear on the screen And you want their X and Y positions in floating point and you have 10 enemies so you generate 20 random floats, right? So that's one possible use case for this and it's as you would expect we have Again, we have an array of 16 quadroids. That's enough for 16 double precision floating point values And we're gonna fill that with random values between zero and a hundred and you can see if I run that We get 16 values between zero and a hundred Got pretty close there and you nine get pretty close there three So you can see it works successfully and I can do it multiple times and you can see we get different values each time Very nice Now our last example The dealer no deal example we already played this I'm not gonna play it again But how does it work? Well, I'll shoot how it works. Oh, actually before I do that Let me show you the file size so Yeah, so you see it's 3,000 bytes and file size so it's not not very large at all. I could be made much smaller if you'd like I didn't try to optimize it for size. We could a lot of that is just like dealing with printing to the screen and stuff like that so it's not all just the Logic and and randomness in there. So Anyway in the code What are our includes? We have a bunch of includes here. We have again a random interay This is how we're kind of shuffling our cases. We pick a random array of integers for our shuffle algorithm and then we have Functions for printing functions for reading from the screen reading from standard input I should say as well as parsing ints Like when I type 16 on the keyboard, we have to parse that string into an actual integer quantity, right? So we have functions for that as well as we have function for moving the cursor around the screen to help us print things a little bit nicer so we have Some helper functions that we created here one called print cases this prints all the case Stuff to the screen you can see clears the screen moves the cursor to the top left prints some of the brackets the numbers It also prints the colors you can see If it was a low value that you opened up like a dollar or five dollars It would print in green if it was a medium value case that would print in yellow If it was a high-value case like a million it would print in red So yep, that's all there that function would basically print our Cases of the screen and this would print our instructions like what to do what numbers to type what letters to type etc and Yeah, down here is where a program actually starts I Can explain that a little bit better by going above and showing you our elf header So the elf header you can see here has an entry for the start address. So everything before the start address is just kind of Code that we can use so all our includes are just Basically copy and pasted into memory right here the entirety of our random int array Function gets copy and pasted right here in code, but we don't execute all the code linearly We execute starting at the start address So all this stuff here is just front matter. We actually start at start So you can see our first thing is we basically generate an array of shuffles so How we're gonna shuffle our cases so make sure you are our memory arrays down here for What we're gonna use in this program. So our cases are here So we have zero through 25 so 26 cases and we have an array of statuses So this will basically contain the value of the case if we've opened it or it will contain a negative one If you've not opened it yet, so it starts off all negative ones and then down here you can see this value So case case number zero will always have well The case ID zero will always have zero dollars in it as well as case 25 will always have $25 in it. Of course, we're gonna shuffle the cases around so it's not gonna be linear like that But it will have all the values in that list. Okay So basically we just have a game loop way where we are constantly doing two things one We're printing out that case list and the instruction list, but we're also computing the expected value so you can see here normally for expected value you have to add up all the cases and Divide by the number of cases that you're dividing by right But you can be kind of clever and know that well, there's always gonna be a certain number of cases 26 and there's Always gonna be a sum of them in maximum and you kind of can do the opposite So you can basically subtract off from the maximum value So here you can see if you add up numbers zero through a million in that list you get 3.4 million and There's a total number of 26 cases so what this loop does is basically subtracts off from our running sum the cases we've opened so far and then it decrements off our case number r14 To basically count the number of cases we have left and so this will constantly be Recomputing our expected value and saving it in register RBP so we can print that out Every iteration so that's pretty much how this code works You can peruse it in more detail. Let me just show you one more example of how this this code works So you can see on the bottom right there. That's our expected value running Every iteration so as I open cases that value will change so yeah With that out of the way, we have basically covered the entire content of this video oh Wait one more thing I forgot to mention That was a bad game Is the shuffle algorithm? Excuse my cats So the shuffle algorithm we're basically using a Fisher-Gates type shuffle so How that works is we've generated our shuffle array. That's an array of kind of shuffling actions So we have 26 shuffling actions between 0 and 25 and the way the shuffling algorithm works is very simple You can look it up on Wikipedia. Basically, we just keep exchanging Elements with elements from that shuffle array. So we keep just swapping them around You know two at a time swapping them swapping that's helping them as we go through all the elements and that enables us to get a decently well shuffled Array of cases basically so you can check that out on on your own time figure out how Fisher-Gates shuffling works But with that out of the way, we're pretty much done with the video We covered all the basics now you guys know how to generate a random series of zeros and ones in a register And you know how to convert that zeros and one string into an integer or a float for your own purposes. So Thanks for watching guys. I'll see you in the next video