Inveteratus Posted March 24 Posted March 24 (edited) I've seen some stock markets in MCCodes-based systems, and sadly most fail for a number of reasons. A classic example being one I came across a year or so back. From what I can ascertain from playing it; it had a cron running every 5 or 10 minutes changing the price to a random value with a set range. This meant that any smart player would monitor the market for a few days to determine the lower and upper bounds, then buy when within lowest 10-20% of the price range, and sell in the upper 80-90% of the range. Oddly, I had a mail from the admin/owner asking how I was managing to make so much money so quickly... Despite telling them, they failed to understand the underlying problem, and banned me a day or so later. My fault ? Possibly, I guess it is abusing a bug, but it's a bug that despite explaining it, the owner failed to take steps to address it in a timely manner. There's actually a number of problems here : Random values do not a stock market make. Using crons to update the prices. Failure to log actions on the market. And critically, not play-testing prior to release. So who to we address these ? 1. Use a noise generator There's a number of decent noise generators out there, though I find that Simplex is probably the best for mimicking a stock market. Remember, in a real market, prices are driven by "market forces", which essentially means the amount of stocks that are being bought and sold. As a game simply won't have the number of players to drive this, then using a noise function seems to produce the best result. 2. Crons are unnecessary when you can generate the values via your function in real time. Assume your noise functions is defined as : function noise( float $x ): float then you can pass in a UNIX timestamp scaled by (say) 1/255 (i.e. multiply by 0.003921568627451), then may get a result in the range -0.7888 to 0.6373. Multiple this by (say) 128 and add 127 you would see a range of 26.023 to 208.583. So while the main input into your noise function is essentially a time value you would actually be using it like: $timeScaleFactor = 1 / 255; $rangeFactor = 128; $rangeOffset = 127; $result = noise($time * $timeScaleFactor) * $rangeFactor + $rangeOffset; This gives you plenty of scope to vary the stock prices by changing the input variables, and the best bit is, being deterministic, the $result will always be the same for the same input values - so no need to use crons, you can generate the price at any time on the fly. 3. Logging actions There are multiple options here, probably the simplest is just to store the player-id, the transaction type (buy or sell), the stock-id, the number of stocks, and the price they were bought or sold at. Of course, you may not really need to log transactions assume you follow the next step closely. 4. Play testing This is extremely important, and sadly something that is often missed by owner/admins. Ideally unit and feature tests should be present to ensure the site actually works using either PHPUnit or PestPHP. After that, you can consider acceptance testing or simple just make sure you manually test EVERY page in your project. And that's not just a case of seeing if the page loads - drop in some bad data; i.e. try and buy -1 stocks, or putting string data into numeric fields. There's a number of techniques that people favor here - I tend to rely heavily on feature tests; but they may not pick up everything if you are lazy when writing your tests. Going back to the original example what would these points have addressed? Well for one, using a non-random system makes it more difficult to predict the outcome of the stocks despite being deterministic. Time is one factor, but you still may have to consider limiting the number of stocks available, or perhaps ensure that you cannot sell stocks for maybe 14 days. Without crons, we've got rid of a hidden problem and extra load which simply is no longer needed, though you would need to to do more work to present a stock graph rather than just pulling data out of the db. Logging is of course a simple one to address, and may not be needed if you trust your code and understand any potential pitfalls. Finally testing is probably the biggest one here. Without testing your code, you run the risk of missing something that may seem stable and fits in nicely in your project but suddenly introduces massive amounts of funds making your game highly unstable. If anybody has suggestions in improving the buy/sell experience, let us know - it's a potentially useful module for many games as it can tie up money for long periods of time. Finally, for reference, here's an example but of code you might want to play with: function noise(float $x): float { $permute = [ 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180, ]; $i0 = $x > 0 ? (int)$x : (int)$x - 1; $i1 = $i0 + 1; $x0 = $x - $i0; $x1 = $x0 - 1.0; $t0 = 1.0 - $x0 * $x0; $t0 *= $t0; $n0 = $t0 * $t0 * $noiseGradient($permute[$i0 & 0xFF], $x0); $t1 = 1.0 - $x1 * $x1; $t1 *= $t1; $n1 = $t1 * $t1 * $noiseGradient($permute[$i1 & 0xFF], $x1); // The maximum value of this noise is 8 * (3 / 4) ^ 4 = 2.53125 // A factor of 0.395 scales to fit exactly within [-1,1] return 0.395 * ($n0 + $n1); } function noiseGradient(int $hash, float $x): float { $h = $hash & 15; $grad = 1.0 + ($h & 7); if (($h & 8) != 0) { $grad = -$grad; } return $grad * $x; } And just for giggle ... something I was experimenting with -- apologies for the un-formatted dates Edited March 24 by Inveteratus Added comment about 0.395 scaling factor 3 1 Quote
CrazyElk Posted March 25 Posted March 25 What MNG said above, both educational and inspirational ... thanks @Inveteratus. Quote
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.