kratsg Posted September 4, 2008 Share Posted September 4, 2008 So, this at first glance seemed a bit easy to work, but maybe I'm thinking too much E_E Let's say we have a random number that is generated every time a page loads (most likely using the mt_rand() function). How can I translate that number into random events with certain probabilities? And not only that, but also be able to add more events in the future. The chances of each random event can vary, but must be as small as .0001% (the admin is allowed to set it that low, where .0001% = Rarest, .001% = Ultra Rare, .01% = Super Rare, etc...). This tells me that I need to have mt_rand(1,1000000). (Range = One to One-Million, since 1/1,000,000 is .000001 = .0001%). So, here's where it gets a little tricky. Sure, I can easily create a RE with a chance of .0001%: $random_number = mt_rand(1,1000000); if($random_number == "5348"){//do this RE } That's easy. It's a no-brainer. But how would you create RE's with a chance of say, .1395% for instance? This means creating a range of numbers, which could be hard-coded into the script, but this seems unfeasible when you want to be able to: 1) Add an event via a User Interface in the Administrative Panel with certain actions [easily done] and a certain chance of happening 2) Edit that event via the same User Interface to change it's action or it's chance of happening. 3) Delete that event altogether. I'm kind of stumped at this point. Also, as well, is there a way to create events that only happen on a specific day of the month, or during a specific month/season? I can figure out how to dynamically insert user information into the event (by using RegEx to search for {username} and etc...) and how to handle different types of events (by looking up the event type and running a specific function within the class to handle the event). And yes, these events are going to be stored in a database table (as that's what databases are there for, helping you make your life easier). Quote Link to comment Share on other sites More sharing options...
kratsg Posted September 4, 2008 Author Share Posted September 4, 2008 I've been thinking of a database which would among other fields, contain "min" and "max" columns. Imagine a table like the following: id|min|max|type|message|chance 1|5901|6000|take_points|"Oh no! You tripped and fell. When you got up, you realized you had lost {points} points."|.01 The range of numbers (100 exactly, including the min/max) will give a chance of (100/1,000,000)*100 = .01%. Now, a query could be set-up such as: $random_number = mt_rand(1,1000000); $query = "SELECT type,message FROM random_events WHERE `min` <= '$random_number' AND `max` >= '$random_number' LIMIT 1"; if(!mysql_num_rows(mysql_query($query))){//0 rows returned, no random event } else {//1 row returned, random event } And it would be pretty straightforward in handling it. But then we have some problems: 1) How will we add another RE and still make sure that the range of numbers DOES NOT conflict with any other random event? 2) By using a range of consecutive numbers, it's not as "more random" as it would be if we had randomly selected 100 (assumingly non-consecutive) numbers between 1 and 1,000,000 to be "event numbers" that trigger the event. Quote Link to comment Share on other sites More sharing options...
Mchl Posted September 4, 2008 Share Posted September 4, 2008 If we assume, that all random events with probability of 0,1 are equally probable (well... they are) but only one can happen at a time then I'd go somewhere like this. database table id|chance|type|message 1|0.1|take_points|"Oh no! You tripped and fell. When you got up, you realized you had lost {points} points." Store 'chance' as decimal or string. Float might yield unexpected results. $random_number = mt_rand(1,1000000); if($random_number = 1) {$chance = 0.000001} elseif($random_number <= 11) {$chance = 0.00001} elseif($random_number <= 111) {$chance = 0.0001} elseif($random_number <= 1111) {$chance = 0.001} elseif($random_number <= 11111) {$chance = 0.01} elseif($random_number <= 111111) {$chance = 0.1} $query = "SELECT * FROM random_events WHERE chance = $chance"; //get all possible random events with $chance $result = mysql_query($query); while ($row = mysql_fetch_array($result)) { $events[] = $row; } //So we have all events with given probability in $events array, now pick one (randomly) $event = $events[mt_rand(0,count($events)-1)] It has the drawback of being limited only to probabilities being powers of 10... I'm sure something better can be devised... Quote Link to comment Share on other sites More sharing options...
Mchl Posted September 4, 2008 Share Posted September 4, 2008 What was here made no sense Quote Link to comment Share on other sites More sharing options...
corbin Posted September 4, 2008 Share Posted September 4, 2008 If you thought of the chances as 1 out of y, you could just do: if(rand(1,y) == 1) For example, if(rand(1,1) == 1) //always true if(rand(1,2) == 1) //50% So on... But that could become cumbersome to store/figure. Quote Link to comment Share on other sites More sharing options...
kratsg Posted September 5, 2008 Author Share Posted September 5, 2008 If you thought of the chances as 1 out of y, you could just do: if(rand(1,y) == 1) For example, if(rand(1,1) == 1) //always true if(rand(1,2) == 1) //50% So on... But that could become cumbersome to store/figure. That's just basic probability. I understand that part. It's mainly trying to figure out how to assign numbers to a row that has a chance value, (especially one with ranges) that do not conflict with other ranges. I want these events to be disjoint. This means that only one of these random events can occur. If we assume, that all random events with probability of 0,1 are equally probable (well... they are) but only one can happen at a time then I'd go somewhere like this. database table id|chance|type|message 1|0.1|take_points|"Oh no! You tripped and fell. When you got up, you realized you had lost {points} points." Store 'chance' as decimal or string. Float might yield unexpected results. $random_number = mt_rand(1,1000000); if($random_number = 1) {$chance = 0.000001} elseif($random_number <= 11) {$chance = 0.00001} elseif($random_number <= 111) {$chance = 0.0001} elseif($random_number <= 1111) {$chance = 0.001} elseif($random_number <= 11111) {$chance = 0.01} elseif($random_number <= 111111) {$chance = 0.1} $query = "SELECT * FROM random_events WHERE chance = $chance"; //get all possible random events with $chance $result = mysql_query($query); while ($row = mysql_fetch_array($result)) { $events[] = $row; } //So we have all events with given probability in $events array, now pick one (randomly) $event = $events[mt_rand(0,count($events)-1)] It has the drawback of being limited only to probabilities being powers of 10... I'm sure something better can be devised... I like this idea, but it's limited in a sense. First of all, if you get $random_number = 1; that means $chance = 0.1 (since it will test true for all conditionals). However, it succeeds in the idea of setting the ranges such that, if the random number is in a range of 10 numbers you set for this certain chance value, then you randomly pick one of the numerous events with that chance value and display it. Yet, this almost dictates that we would need "even" chance numbers (such as 1%,2%, etc...). And yet, you would have to determine a range that would have no random event. Which brings up another idea... What if instead of a "chance" value, we have something called a rarity value (this almost sounds like chance, but slightly different). Let's say our rarity of event value ranges from 0-100 (where 0 = never happening, 100 = always happening). So, let's say we do mt_rand(0,100) in order to pick our numbers. Then, let's say for instance it's 34. (I've taken stat before, but I'm not entirely good at probabilities). Let's have that table again, but this time with these rarity values. We'll have a query that gives us the sum of these values: <?php $random_number = mt_rand(0,100); $query = "SELECT * FROM `random_events` WHERE `rarity` = '$random_number'"; $result = mysql_query($query); if(!mysql_num_rows($result)){return;}//no event while ($row = mysql_fetch_array($result)) { $events[] = $row; } $query = "SELECT sum(rarity) FROM `random_events` WHERE `rarity` = '$random_number'"; $sum = mysql_query($query); $chance = round($random_number/$sum,4)*1000; //gives a number of the following format #### if(mt_rand(1,10000) <= $chance){//that means a success! //So we have all events with given probability in $events array, now pick one (randomly) $event = $events[mt_rand(0,count($events)-1)] } else {return;}//no event ?> What you do is determine what rarity value we have based on the value you get (but this is the drawback, honestly since all values have an equal chance of being selected E_E). Anyhow, once we determine the rarity of the event, then we grab the sum of all the events with that rarity, divide the rarity by the sum of all events there (rarity/#events*rarity) leaving (1/#events). So each event of that rarity has an equal chance of 1/#events existing to occur. Then we see if we even get that chance, if it's true, randomly pick any one of the events (since they will all have the same rarity and therefore same "weight"), otherwise, no event. So, in this case, we literally have a dynamic chance based on how many events exist with the same probability of happening. Although, it seems to get confusing o_o Does anyone see where I'm trying to head at? Cause I don't E_E But again, a drawback is you can't randomly select the rarity based on it's weight. (not unless you create an array that has something like this: $weighted_rarities = (1,2,2,3,3,3,4,4,4,4,5,5,5,5,5,etc...,100,100,100); Where rarity 1 is one time, rarity 2 is repeated twice, rarity 3 is repeated 3 times, rarity "n" is repeated "n" times which would give each rarity weight according to it's true rarity... right?) Quote Link to comment Share on other sites More sharing options...
Mchl Posted September 5, 2008 Share Posted September 5, 2008 First of all, if you get $random_number = 1; that means $chance = 0.1 (since it will test true for all conditionals). It won't. Notice 'elseif's. If $random_number == 1 then it will test true on the first conditional, and the rest of conditionals will not be performed at all. Now to read the rest Quote Link to comment Share on other sites More sharing options...
kratsg Posted September 5, 2008 Author Share Posted September 5, 2008 Oh, I didn't see the elseifs o_o But see here's where it gets odd.. The chances of getting 2 or 3 or 4 are the same as the chances of getting 1 or 59 or 139 or etc... So there has to be a way to give them a different chance of occuring. Quote Link to comment Share on other sites More sharing options...
kratsg Posted September 5, 2008 Author Share Posted September 5, 2008 Hey, I found one that may be what I'm looking for in terms of logic.. and this is a VERY nice solution: <?php $query = "SELECT id,chance FROM random_events ORDER BY `chance` ASC"; //get all possible random events into an array $result = mysql_query($query); while ($row = mysql_fetch_array($result)) { $events[] = $row['id']; $weights[] = $row['chance']; } $event = $events[w_rand($weights)];//returns an index function w_rand($weights) { $r = mt_rand(1,1000000); $offset = 0; foreach ($weights as $k => $w) { $offset += $w*1000000; if ($r <= $offset) { return $k; } } } ?> Now, the database would be set up with (probably) starting with a 90% chance of no event happening (since this is the largest chance, it would be the last thing in the array created). This script does require that the sum of all chances equals 1, in order to function properly. If you imagine a simple example: ($weights = array(.15,.35,.20,.30)) Let's pick a random number like above: 429411. What the function will do is multiple each value in the array by 1 million: $weights = array(150000,350000,200000,300000); First loop: $offset = 150000; (less than the random value, keep going) Second Loop: $offset = 150000+350000 = 500000; (greater than the random value, we've found our event, return the index of the array and stop the loop) Here's the downfall: what about areas of no-event? This assumes that we're going to fill every possible space with a specified event or specified take no action. Otherwise, if we have something like this: $weights = array(.15,.20,.30) where the total is 65% (.65), then it shall fail. Here's how: First Loop: $offset = 150000; Second Loop: $offset = 150000+200000 = 350000; Third Loop: $offset = 350000+300000 = 650000; Return the Third Index. It shall fail in the sense that if anything is bigger than 650000, it won't return anything, which leads me to think up the following solutions. 1) Set a row in the database to be a specified "no-event" with a chance of (100%). 2) Add other rows in the database to represent events as much as you'd like with a chance of at least .000001 (.0001%). (these other rows don't have to fill in those gaps for the needed sum of 100%). 3) When you run the loop, you can generate a REALLY SMALL NUMBER in which the event shall be triggered. The gap that will exist (if any) exist after the last item in the array (in this case, the no-event). So what we do is have the no-event chance set to 100%, so if the random number does not trigger any of the random events before, it will trigger the no-event. Does anyone see any downfalls here in my solution? Lemme know if I've got a good idea before I start implementing it. Quote Link to comment Share on other sites More sharing options...
Mchl Posted September 5, 2008 Share Posted September 5, 2008 I had something similar on mind this morning, but didn't come up with anything usable... Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.