├── .gitignore ├── LICENSE ├── README.md ├── elm.json └── src └── Random.elm /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-present, Evan Czaplicki 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Evan Czaplicki nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Randomness 2 | 3 | Need to generate random numbers? How about random game boards? Or random positions in 3D space? This is the package for you! 4 | 5 | 6 | ## Example 7 | 8 | This package will help you build a random `Generator` like this: 9 | 10 | ```elm 11 | import Random 12 | 13 | probability : Random.Generator Float 14 | probability = 15 | Random.float 0 1 16 | 17 | roll : Random.Generator Int 18 | roll = 19 | Random.int 1 6 20 | 21 | usuallyTrue : Random.Generator Bool 22 | usuallyTrue = 23 | Random.weighted (80, True) [ (20, False) ] 24 | ``` 25 | 26 | In each of these defines _how_ to generate random values. The most interesting case is `usuallyTrue` which generates `True` 80% of the time and `False` 20% of the time! 27 | 28 | Now look at this [working example](https://guide.elm-lang.org/effects/random.html) to see a `Generator` used in an application. 29 | 30 | 31 | ## Mindset Shift 32 | 33 | If you are coming from JavaScript, this package is usually quite surprising at first. Why not just call `Math.random()` and get random floats whenever you want? Well, all Elm functions have this “same input, same output” guarantee. That is part of what makes Elm so reliable and easy to test! But if we could generate random values anytime we want, we would have to throw that guarantee out. 34 | 35 | So instead, we create a `Generator` and hand it to the Elm runtime system to do the dirty work of generating values. We get to keep our guarantees _and_ we get random values. Great! And once people become familiar with generators, they often report that it is _easier_ than the traditional imperative APIs for most cases. For example, jump to the docs for [`Random.map4`](Random#map4) for an example of generating random [quadtrees](https://en.wikipedia.org/wiki/Quadtree) and think about what it would look like to do that in JavaScript! 36 | 37 | Point is, this library takes some learning, but we really think it is worth it. So hang in there, and do not hesitate to ask for help on [Slack](https://elmlang.herokuapp.com/) or [Discourse](https://discourse.elm-lang.org/)! 38 | 39 | 40 | ## Future Plans 41 | 42 | There are a ton of useful helper functions in the [`elm-community/random-extra`][extra] package. Do you need random `String` values? Random dictionaries? Etc. 43 | 44 | We will probably do an API review and merge the results into this package someday. Not sure when, but it would be kind of nice to have it all in one place. But in the meantime, just do `elm install elm-community/random-extra` if you need stuff from there! 45 | 46 | [extra]: https://package.elm-lang.org/packages/elm-community/random-extra/latest 47 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "elm/random", 4 | "summary": "Generate random numbers and values (RNG)", 5 | "license": "BSD-3-Clause", 6 | "version": "1.0.0", 7 | "exposed-modules": [ 8 | "Random" 9 | ], 10 | "elm-version": "0.19.0 <= v < 0.20.0", 11 | "dependencies": { 12 | "elm/core": "1.0.0 <= v < 2.0.0", 13 | "elm/time": "1.0.0 <= v < 2.0.0" 14 | }, 15 | "test-dependencies": {} 16 | } -------------------------------------------------------------------------------- /src/Random.elm: -------------------------------------------------------------------------------- 1 | effect module Random where { command = MyCmd } exposing 2 | ( Generator, Seed 3 | , int, float, uniform, weighted, constant 4 | , list, pair 5 | , map, map2, map3, map4, map5 6 | , andThen, lazy 7 | , minInt, maxInt 8 | , generate 9 | , step, initialSeed, independentSeed 10 | ) 11 | 12 | {-| This library helps you generate pseudo-random values. 13 | 14 | It is an implementation of [Permuted Congruential Generators][pcg] 15 | by M. E. O'Neil. It is not cryptographically secure. 16 | 17 | [extra]: /packages/elm-community/random-extra/latest 18 | [pcg]: http://www.pcg-random.org/ 19 | 20 | 21 | # Generators 22 | @docs Generator, generate 23 | 24 | # Primitives 25 | @docs int, float, uniform, weighted, constant 26 | 27 | # Data Structures 28 | @docs pair, list 29 | 30 | # Mapping 31 | @docs map, map2, map3, map4, map5 32 | 33 | # Fancy Stuff 34 | @docs andThen, lazy 35 | 36 | # Constants 37 | @docs maxInt, minInt 38 | 39 | # Generate Values Manually 40 | @docs Seed, step, initialSeed, independentSeed 41 | 42 | -} 43 | 44 | import Basics exposing (..) 45 | import Bitwise 46 | import List exposing ((::)) 47 | import Platform 48 | import Platform.Cmd exposing (Cmd) 49 | import Task exposing (Task) 50 | import Time 51 | 52 | 53 | 54 | -- PRIMITIVE GENERATORS 55 | 56 | 57 | {-| Generate 32-bit integers in a given range. 58 | 59 | import Random 60 | 61 | singleDigit : Random.Generator Int 62 | singleDigit = 63 | Random.int 0 9 64 | 65 | closeToZero : Random.Generator Int 66 | closeToZero = 67 | Random.int -5 5 68 | 69 | anyInt : Random.Generator Int 70 | anyInt = 71 | Random.int Random.minInt Random.maxInt 72 | 73 | This generator *can* produce values outside of the range [[`minInt`](#minInt), 74 | [`maxInt`](#maxInt)] but sufficient randomness is not guaranteed. 75 | -} 76 | int : Int -> Int -> Generator Int 77 | int a b = 78 | Generator 79 | (\seed0 -> 80 | let 81 | ( lo, hi ) = 82 | if a < b then 83 | ( a, b ) 84 | else 85 | ( b, a ) 86 | 87 | range = 88 | hi - lo + 1 89 | in 90 | -- fast path for power of 2 91 | if (Bitwise.and (range - 1) range) == 0 then 92 | ( (Bitwise.shiftRightZfBy 0 (Bitwise.and (range - 1) (peel seed0))) + lo, next seed0 ) 93 | else 94 | let 95 | threshhold = 96 | -- essentially: period % max 97 | Bitwise.shiftRightZfBy 0 (remainderBy range (Bitwise.shiftRightZfBy 0 -range)) 98 | 99 | accountForBias : Seed -> ( Int, Seed ) 100 | accountForBias seed = 101 | let 102 | x = 103 | peel seed 104 | 105 | seedN = 106 | next seed 107 | in 108 | if x < threshhold then 109 | -- in practice this recurses almost never 110 | accountForBias seedN 111 | else 112 | ( remainderBy range x + lo, seedN ) 113 | in 114 | accountForBias seed0 115 | ) 116 | 117 | 118 | {-| The underlying algorithm works well in a specific range of integers. 119 | It can generate values outside of that range, but they are “not as random”. 120 | 121 | The `maxInt` that works well is `2147483647`. 122 | -} 123 | maxInt : Int 124 | maxInt = 125 | 2147483647 126 | 127 | 128 | {-| The underlying algorithm works well in a specific range of integers. 129 | It can generate values outside of that range, but they are “not as random”. 130 | 131 | The `minInt` that works well is `-2147483648`. 132 | -} 133 | minInt : Int 134 | minInt = 135 | -2147483648 136 | 137 | 138 | {-| Generate floats in a given range. 139 | 140 | import Random 141 | 142 | probability : Random.Generator Float 143 | probability = 144 | Random.float 0 1 145 | 146 | The `probability` generator will produce values between zero and one with 147 | a uniform distribution. Say it produces a value `p`. We can then check if 148 | `p < 0.4` if we want something to happen 40% of the time. 149 | 150 | This becomes very powerful when paired with functions like [`map`](#map) and 151 | [`andThen`](#andThen). Rather than dealing with twenty random float messages 152 | in your `update`, you can build up sophisticated logic in the `Generator` 153 | itself! 154 | -} 155 | float : Float -> Float -> Generator Float 156 | float a b = 157 | Generator (\seed0 -> 158 | let 159 | -- Get 64 bits of randomness 160 | seed1 = 161 | next seed0 162 | 163 | n0 = 164 | peel seed0 165 | 166 | n1 = 167 | peel seed1 168 | 169 | -- Get a uniformly distributed IEEE-754 double between 0.0 and 1.0 170 | hi = 171 | toFloat (Bitwise.and 0x03FFFFFF n0) * 1.0 172 | 173 | lo = 174 | toFloat (Bitwise.and 0x07FFFFFF n1) * 1.0 175 | 176 | val = 177 | -- These magic constants are 2^27 and 2^53 178 | ((hi * 134217728.0) + lo) / 9007199254740992.0 179 | 180 | -- Scale it into our range 181 | range = 182 | abs (b - a) 183 | 184 | scaled = 185 | val * range + a 186 | in 187 | ( scaled, next seed1 ) 188 | ) 189 | 190 | 191 | {-| Generate the same value every time. 192 | 193 | import Random 194 | 195 | alwaysFour : Random.Generator Int 196 | alwaysFour = 197 | Random.constant 4 198 | 199 | Think of it as picking from a hat with only one thing in it. It is weird, 200 | but it can be useful with [`elm-community/random-extra`][extra] which has 201 | tons of nice helpers. 202 | 203 | [extra]: /packages/elm-community/random-extra/latest 204 | -} 205 | constant : a -> Generator a 206 | constant value = 207 | Generator (\seed -> (value, seed)) 208 | 209 | 210 | 211 | -- DATA STRUCTURES 212 | 213 | 214 | {-| Generate a pair of random values. A common use of this might be to generate 215 | a point in a certain 2D space: 216 | 217 | import Random 218 | 219 | randomPoint : Random.Generator (Float, Float) 220 | randomPoint = 221 | Random.pair (Random.float -200 200) (Random.float -100 100) 222 | 223 | Maybe you are doing an animation with SVG and want to randomly generate some 224 | entities? 225 | -} 226 | pair : Generator a -> Generator b -> Generator (a,b) 227 | pair genA genB = 228 | map2 (\a b -> (a,b)) genA genB 229 | 230 | 231 | {-| Generate a list of random values. 232 | 233 | import Random 234 | 235 | tenFractions : Random.Generator (List Float) 236 | tenFractions = 237 | Random.list 10 (Random.float 0 1) 238 | 239 | fiveGrades : Random.Generator (List Int) 240 | fiveGrades = 241 | Random.list 5 (int 0 100) 242 | 243 | If you want to generate a list with a random length, you need to use 244 | [`andThen`](#andThen) like this: 245 | 246 | fiveToTenDigits : Random.Generator (List Int) 247 | fiveToTenDigits = 248 | Random.int 5 10 249 | |> Random.andThen (\len -> Random.list len (Random.int 0 9)) 250 | 251 | This generator gets a random integer between five and ten **and then** 252 | uses that to generate a random list of digits. 253 | -} 254 | list : Int -> Generator a -> Generator (List a) 255 | list n (Generator gen) = 256 | Generator (\seed -> 257 | listHelp [] n gen seed 258 | ) 259 | 260 | 261 | listHelp : List a -> Int -> (Seed -> (a,Seed)) -> Seed -> (List a, Seed) 262 | listHelp revList n gen seed = 263 | if n < 1 then 264 | (revList, seed) 265 | 266 | else 267 | let 268 | (value, newSeed) = 269 | gen seed 270 | in 271 | listHelp (value :: revList) (n-1) gen newSeed 272 | 273 | 274 | 275 | -- ENUMERATIONS 276 | 277 | 278 | {-| Generate values with equal probability. Say we want a random suit for some 279 | cards: 280 | 281 | import Random 282 | 283 | type Suit = Diamond | Club | Heart | Spade 284 | 285 | suit : Random.Generator Suit 286 | suit = 287 | Random.uniform Diamond [ Club, Heart, Spade ] 288 | 289 | That generator produces all `Suit` values with equal probability, 25% each. 290 | 291 | **Note:** Why not have `uniform : List a -> Generator a` as the API? It looks 292 | a little prettier in code, but it leads to an awkward question. What do you do 293 | with `uniform []`? How can it produce an `Int` or `Float`? The current API 294 | guarantees that we always have *at least* one value, so we never run into that 295 | question! 296 | -} 297 | uniform : a -> List a -> Generator a 298 | uniform value valueList = 299 | weighted (addOne value) (List.map addOne valueList) 300 | 301 | 302 | addOne : a -> (Float, a) 303 | addOne value = 304 | ( 1, value ) 305 | 306 | 307 | {-| Generate values with a _weighted_ probability. Say we want to simulate a 308 | [loaded die](https://en.wikipedia.org/wiki/Dice#Loaded_dice) that lands 309 | on ⚄ and ⚅ more often than the other faces: 310 | 311 | import Random 312 | 313 | type Face = One | Two | Three | Four | Five | Six 314 | 315 | roll : Random.Generator Face 316 | roll = 317 | Random.weighted 318 | (10, One) 319 | [ (10, Two) 320 | , (10, Three) 321 | , (10, Four) 322 | , (20, Five) 323 | , (40, Six) 324 | ] 325 | 326 | So there is a 40% chance of getting `Six`, a 20% chance of getting `Five`, and 327 | then a 10% chance for each of the remaining faces. 328 | 329 | **Note:** I made the weights add up to 100, but that is not necessary. I always 330 | add up your weights into a `total`, and from there, the probablity of each case 331 | is `weight / total`. Negative weights do not really make sense, so I just flip 332 | them to be positive. 333 | -} 334 | weighted : (Float, a) -> List (Float, a) -> Generator a 335 | weighted first others = 336 | let 337 | normalize (weight, _) = 338 | abs weight 339 | 340 | total = 341 | normalize first + List.sum (List.map normalize others) 342 | in 343 | map (getByWeight first others) (float 0 total) 344 | 345 | 346 | getByWeight : (Float, a) -> List (Float, a) -> Float -> a 347 | getByWeight (weight, value) others countdown = 348 | case others of 349 | [] -> 350 | value 351 | 352 | second :: otherOthers -> 353 | if countdown <= abs weight then 354 | value 355 | else 356 | getByWeight second otherOthers (countdown - abs weight) 357 | 358 | 359 | 360 | -- CUSTOM GENERATORS 361 | 362 | 363 | {-| Transform the values produced by a generator. For example, we can 364 | generate random boolean values: 365 | 366 | import Random 367 | 368 | bool : Random.Generator Bool 369 | bool = 370 | Random.map (\n -> n < 20) (Random.int 1 100) 371 | 372 | The `bool` generator first picks a number between 1 and 100. From there 373 | it checks if the number is less than twenty. So the resulting `Bool` will 374 | be `True` about 20% of the time. 375 | 376 | You could also do this for lower case ASCII letters: 377 | 378 | letter : Random.Generator Char 379 | letter = 380 | Random.map (\n -> Char.fromCode (n + 97)) (Random.int 0 25) 381 | 382 | The `letter` generator first picks a number between 0 and 25 inclusive. 383 | It then uses `Char.fromCode` to turn [ASCII codes][ascii] into `Char` values. 384 | 385 | **Note:** Instead of making these yourself, always check if the 386 | [`random-extra`][extra] package has what you need! 387 | 388 | [ascii]: https://en.wikipedia.org/wiki/ASCII#Printable_characters 389 | [extra]: /packages/elm-community/random-extra/latest 390 | -} 391 | map : (a -> b) -> Generator a -> Generator b 392 | map func (Generator genA) = 393 | Generator (\seed0 -> 394 | let 395 | (a, seed1) = genA seed0 396 | in 397 | (func a, seed1) 398 | ) 399 | 400 | 401 | {-| Combine two generators. Maybe we have a space invaders game and want to 402 | generate enemy ships with a random location: 403 | 404 | import Random 405 | 406 | type alias Enemy 407 | { health : Float 408 | , rotation : Float 409 | , x : Float 410 | , y : Float 411 | } 412 | 413 | enemy : Random.Generator Enemy 414 | enemy = 415 | Random.map2 416 | (\x y -> Enemy 100 0 x y) 417 | (Random.float 0 100) 418 | (Random.float 0 100) 419 | 420 | Now whenever we run the `enemy` generator we get an enemy with full health, 421 | no rotation, and a random position! Now say we want to start with between 422 | five and ten enemies on screen: 423 | 424 | initialEnemies : Random.Generator (List Enemy) 425 | initialEnemies = 426 | Random.int 5 10 427 | |> Random.andThen (\num -> Random.list num enemy) 428 | 429 | We will generate a number between five and ten, **and then** generate 430 | that number of enemies! 431 | 432 | **Note:** Snapping generators together like this is very important! Always 433 | start by with generators for each `type` you need, and then snap them 434 | together. 435 | -} 436 | map2 : (a -> b -> c) -> Generator a -> Generator b -> Generator c 437 | map2 func (Generator genA) (Generator genB) = 438 | Generator (\seed0 -> 439 | let 440 | (a, seed1) = genA seed0 441 | (b, seed2) = genB seed1 442 | in 443 | (func a b, seed2) 444 | ) 445 | 446 | 447 | {-| Combine three generators. Maybe you want to make a simple slot machine? 448 | 449 | import Random 450 | 451 | type alias Spin = 452 | { one : Symbol 453 | , two : Symbol 454 | , three : Symbol 455 | } 456 | 457 | type Symbol = Cherry | Seven | Bar | Grapes 458 | 459 | spin : Random.Generator Spin 460 | spin = 461 | Random.map3 Spin symbol symbol symbol 462 | 463 | symbol : Random.Generator Symbol 464 | symbol = 465 | Random.uniform Cherry [ Seven, Bar, Grapes ] 466 | 467 | **Note:** Always start with the types. Make a generator for each thing you need 468 | and then put them all together with one of these `map` functions. 469 | -} 470 | map3 : (a -> b -> c -> d) -> Generator a -> Generator b -> Generator c -> Generator d 471 | map3 func (Generator genA) (Generator genB) (Generator genC) = 472 | Generator (\seed0 -> 473 | let 474 | (a, seed1) = genA seed0 475 | (b, seed2) = genB seed1 476 | (c, seed3) = genC seed2 477 | in 478 | (func a b c, seed3) 479 | ) 480 | 481 | 482 | {-| Combine four generators. 483 | 484 | Say you are making game and want to place enemies or terrain randomly. You 485 | _could_ generate a [quadtree](https://en.wikipedia.org/wiki/Quadtree)! 486 | 487 | import Random 488 | 489 | type QuadTree a 490 | = Empty 491 | | Leaf a 492 | | Node (QuadTree a) (QuadTree a) (QuadTree a) (QuadTree a) 493 | 494 | quadTree : Random.Generator a -> Random.Generator (QuadTree a) 495 | quadTree leafGen = 496 | let 497 | subQuads = 498 | Random.lazy (\_ -> quadTree leafGen) 499 | in 500 | Random.andThen identity <| 501 | Random.uniform 502 | (Random.constant Empty) 503 | [ Random.map Leaf leafGen 504 | , Random.map4 Node subQuad subQuad subQuad subQuad 505 | ] 506 | 507 | We start by creating a `QuadTree` type where each quadrant is either `Empty`, a 508 | `Leaf` containing something interesting, or a `Node` with four sub-quadrants. 509 | 510 | Next the `quadTree` definition describes how to generate these values. A third 511 | of a time you get an `Empty` tree. A third of the time you get a `Leaf` with 512 | some interesting value. And a third of the time you get a `Node` full of more 513 | `QuadTree` values. How are those subtrees generated though? Well, we use our 514 | `quadTree` generator! 515 | 516 | **Exercises:** Can `quadTree` generate infinite `QuadTree` values? Is there 517 | some way to limit the depth of the `QuadTree`? Can you render the `QuadTree` 518 | to HTML using absolute positions and fractional dimensions? Can you render 519 | the `QuadTree` to SVG? 520 | 521 | **Note:** Check out the docs for [`lazy`](#lazy) to learn why that is needed 522 | to define a recursive `Generator` like this one. 523 | -} 524 | map4 : (a -> b -> c -> d -> e) -> Generator a -> Generator b -> Generator c -> Generator d -> Generator e 525 | map4 func (Generator genA) (Generator genB) (Generator genC) (Generator genD) = 526 | Generator (\seed0 -> 527 | let 528 | (a, seed1) = genA seed0 529 | (b, seed2) = genB seed1 530 | (c, seed3) = genC seed2 531 | (d, seed4) = genD seed3 532 | in 533 | (func a b c d, seed4) 534 | ) 535 | 536 | 537 | {-| Combine five generators. 538 | 539 | If you need to combine more things, you can always do it by chaining 540 | [`andThen`](#andThen). There are also some additional helpers for this 541 | in [`elm-community/random-extra`][extra]. 542 | 543 | [extra]: /packages/elm-community/random-extra/latest 544 | -} 545 | map5 : (a -> b -> c -> d -> e -> f) -> Generator a -> Generator b -> Generator c -> Generator d -> Generator e -> Generator f 546 | map5 func (Generator genA) (Generator genB) (Generator genC) (Generator genD) (Generator genE) = 547 | Generator (\seed0 -> 548 | let 549 | (a, seed1) = genA seed0 550 | (b, seed2) = genB seed1 551 | (c, seed3) = genC seed2 552 | (d, seed4) = genD seed3 553 | (e, seed5) = genE seed4 554 | in 555 | (func a b c d e, seed5) 556 | ) 557 | 558 | 559 | {-| Generate fancy random values. 560 | 561 | We have seen examples of how `andThen` can be used to generate variable length 562 | lists in the [`list`](#list) and [`map2`](#map2) docs. We saw how it could help 563 | generate a quadtree in the [`map4`](#map4) docs. 564 | 565 | Anything you could ever want can be defined using this operator! As one last 566 | example, here is how you can define `map` using `andThen`: 567 | 568 | import Random 569 | 570 | map : (a -> b) -> Random.Generator a -> Random.Generator b 571 | map func generator = 572 | generator 573 | |> Random.andThen (\value -> Random.constant (func value)) 574 | 575 | The `andThen` function gets used a lot in [`elm-community/random-extra`][extra], 576 | so it may be helpful to look through the implementation there for more examples. 577 | 578 | [extra]: /packages/elm-community/random-extra/latest 579 | -} 580 | andThen : (a -> Generator b) -> Generator a -> Generator b 581 | andThen callback (Generator genA) = 582 | Generator (\seed -> 583 | let 584 | (result, newSeed) = genA seed 585 | (Generator genB) = callback result 586 | in 587 | genB newSeed 588 | ) 589 | 590 | 591 | {-| Helper for defining self-recursive generators. Say we want to generate a 592 | random number of probabilities: 593 | 594 | import Random 595 | 596 | probabilities : Random.Generator (List Float) 597 | probabilities = 598 | Random.andThen identity <| 599 | Random.uniform 600 | (Random.constant []) 601 | [ Random.map2 (::) 602 | (Random.float 0 1) 603 | (Random.lazy (\_ -> probabilities)) 604 | ] 605 | 606 | In 50% of cases we end the list. In 50% of cases we generate a probability and 607 | add it onto a random number of probabilities. The `lazy` call is crucial 608 | because it means we do not unroll the generator unless we need to. 609 | 610 | This is a pretty subtle issue, so I recommend reading more about it 611 | [here](https://elm-lang.org/0.19.0/bad-recursion)! 612 | 613 | **Note:** You can delay evaluation with `andThen` too. The thing that matters 614 | is that you have a function call that delays the creation of the generator! 615 | -} 616 | lazy : (() -> Generator a) -> Generator a 617 | lazy callback = 618 | Generator (\seed -> 619 | let 620 | (Generator gen) = callback () 621 | in 622 | gen seed 623 | ) 624 | 625 | 626 | -- IMPLEMENTATION 627 | 628 | {- Explanation of the PCG algorithm 629 | 630 | This is a special variation (dubbed RXS-M-SH) that produces 32 631 | bits of output by keeping 32 bits of state. There is one function 632 | (next) to derive the following state and another (peel) to obtain 32 633 | psuedo-random bits from the current state. 634 | 635 | Getting the next state is easy: multiply by a magic factor, modulus by 2^32, 636 | and then add an increment. If two seeds have different increments, 637 | their random numbers from the two seeds will never match up; they are 638 | completely independent. This is very helpful for isolated components or 639 | multithreading, and elm-test relies on this feature. 640 | 641 | Transforming a seed into 32 random bits is more complicated, but 642 | essentially you use the "most random" bits to pick some way of scrambling 643 | the remaining bits. Beyond that, see section 6.3.4 of the [paper]. 644 | 645 | [paper](http://www.pcg-random.org/paper.html) 646 | 647 | Once we have 32 random bits, we have to turn it into a number. For integers, 648 | we first check if the range is a power of two. If it is, we can mask part of 649 | the value and be done. If not, we need to account for bias. 650 | 651 | Let's say you want a random number between 1 and 7 but I can only generate 652 | random numbers between 1 and 32. If I modulus by result by 7, I'm biased, 653 | because there are more random numbers that lead to 1 than 7. So instead, I 654 | check to see if my random number exceeds 28 (the largest multiple of 7 less 655 | than 32). If it does, I reroll, otherwise I mod by seven. This sounds 656 | wasteful, except that instead of 32 it's 2^32, so in practice it's hard to 657 | notice. So that's how we get random ints. There's another process from 658 | floats, but I don't understand it very well. 659 | -} 660 | 661 | 662 | {-| Maybe you do not want to use [`generate`](#generate) for some reason? Maybe 663 | you need to be able to exactly reproduce a sequence of random values? 664 | 665 | In that case, you can work with a `Seed` of randomness and [`step`](#step) it 666 | forward by hand. 667 | -} 668 | type Seed 669 | = Seed Int Int 670 | -- the first Int is the state of the RNG and stepped with each random generation 671 | -- the second state is the increment which corresponds to an independent RNG 672 | 673 | 674 | -- step the RNG to produce the next seed 675 | -- this is incredibly simple: multiply the state by a constant factor, modulus it 676 | -- by 2^32, and add a magic addend. The addend can be varied to produce independent 677 | -- RNGs, so it is stored as part of the seed. It is given to the new seed unchanged. 678 | next : Seed -> Seed 679 | next (Seed state0 incr) = 680 | -- The magic constant is from Numerical Recipes and is inlined for perf. 681 | Seed (Bitwise.shiftRightZfBy 0 ((state0 * 1664525) + incr)) incr 682 | 683 | 684 | -- obtain a psuedorandom 32-bit integer from a seed 685 | peel : Seed -> Int 686 | peel (Seed state _) = 687 | -- This is the RXS-M-SH version of PCG, see section 6.3.4 of the paper 688 | -- and line 184 of pcg_variants.h in the 0.94 (non-minimal) C implementation, 689 | -- the latter of which is the source of the magic constant. 690 | let 691 | word = 692 | (Bitwise.xor state (Bitwise.shiftRightZfBy ((Bitwise.shiftRightZfBy 28 state) + 4) state)) * 277803737 693 | in 694 | Bitwise.shiftRightZfBy 0 (Bitwise.xor (Bitwise.shiftRightZfBy 22 word) word) 695 | 696 | 697 | {-| A `Generator` is a **recipe** for generating random values. For example, 698 | here is a generator for numbers between 1 and 10 inclusive: 699 | 700 | import Random 701 | 702 | oneToTen : Random.Generator Int 703 | oneToTen = 704 | Random.int 1 10 705 | 706 | Notice that we are not actually generating any numbers yet! We are describing 707 | what kind of values we want. To actually get random values, you create a 708 | command with the [`generate`](#generate) function: 709 | 710 | type Msg = NewNumber Int 711 | 712 | newNumber : Cmd Msg 713 | newNumber = 714 | Random.generate NewNumber oneToTen 715 | 716 | Each time you run this command, it runs the `oneToTen` generator and produces 717 | random integers between one and ten. 718 | 719 | **Note 1:** If you are not familiar with commands yet, start working through 720 | [guide.elm-lang.org][guide]. It builds up to an example that generates 721 | random numbers. Commands are one of the core concepts in Elm, and it will 722 | be faster overall to invest in understanding them _now_ rather than copy/pasting 723 | your way to frustration! And if you feel stuck on something, definitely ask 724 | about it in [the Elm slack][slack]. Folks are happy to help! 725 | 726 | **Note 2:** The random `Generator` API is quite similar to the JSON `Decoder` API. 727 | Both are building blocks that snap together with `map`, `map2`, etc. You can read 728 | more about JSON decoders [here][json] to see the similarity. 729 | 730 | [guide]: https://guide.elm-lang.org/ 731 | [slack]: https://elmlang.herokuapp.com/ 732 | [json]: https://guide.elm-lang.org/interop/json.html 733 | -} 734 | type Generator a = 735 | Generator (Seed -> (a, Seed)) 736 | 737 | 738 | {-| So you need _reproducable_ randomness for some reason. 739 | 740 | This `step` function lets you use a `Generator` without commands. It is a 741 | normal Elm function. Same input, same output! So to get a 3D point you could 742 | say: 743 | 744 | import Random 745 | 746 | type alias Point3D = { x : Float, y : Float, z : Float } 747 | 748 | point3D : Random.Seed -> (Point3D, Random.Seed) 749 | point3D seed0 = 750 | let 751 | (x, seed1) = Random.step (Random.int 0 100) seed0 752 | (y, seed2) = Random.step (Random.int 0 100) seed1 753 | (z, seed3) = Random.step (Random.int 0 100) seed2 754 | in 755 | ( Point3D x y z, seed3 ) 756 | 757 | Notice that we use different seeds on each line! If we instead used `seed0` 758 | for everything, the `x`, `y`, and `z` values would always be exactly the same! 759 | Same input, same output! 760 | 761 | Threading seeds around is not super fun, so if you really need this, it is 762 | best to build your `Generator` like normal and then just `step` it all at 763 | once at the top of your program. 764 | -} 765 | step : Generator a -> Seed -> (a, Seed) 766 | step (Generator generator) seed = 767 | generator seed 768 | 769 | 770 | {-| Create a `Seed` for _reproducable_ randomness. 771 | 772 | import Random 773 | 774 | seed0 : Random.Seed 775 | seed0 = 776 | Random.initialSeed 42 777 | 778 | If you hard-code your `Seed` like this, every run will be the same. This can 779 | be useful if you are testing a game with randomness and want it to be easy to 780 | reproduce past games. 781 | 782 | In practice, you may want to get the initial seed by (1) sending it to Elm 783 | through flags or (2) using `Time.now` to get a number that the user has not 784 | seen before. (Flags are described on [this page][flags].) 785 | 786 | [flags]: https://guide.elm-lang.org/interop/flags.html 787 | -} 788 | initialSeed : Int -> Seed 789 | initialSeed x = 790 | let 791 | -- the default increment magic constant is taken from Numerical Recipes 792 | (Seed state1 incr) = 793 | next (Seed 0 1013904223) 794 | 795 | state2 = 796 | Bitwise.shiftRightZfBy 0 (state1 + x) 797 | in 798 | next (Seed state2 incr) 799 | 800 | 801 | {-| A generator that produces a seed that is independent of any other seed in 802 | the program. These seeds will generate their own unique sequences of random 803 | values. They are useful when you need an unknown amount of randomness *later* 804 | but can request only a fixed amount of randomness *now*. 805 | 806 | The independent seeds are extremely likely to be distinct for all practical 807 | purposes. However, it is not proven that there are no pathological cases. 808 | -} 809 | independentSeed : Generator Seed 810 | independentSeed = 811 | Generator <| 812 | \seed0 -> 813 | let 814 | gen = 815 | int 0 0xFFFFFFFF 816 | 817 | {-- 818 | Although it probably doesn't hold water theoretically, xor two 819 | random numbers to make an increment less likely to be 820 | pathological. Then make sure that it's odd, which is required. 821 | Next make sure it is positive. Finally step it once before use. 822 | --} 823 | makeIndependentSeed state b c = 824 | next <| Seed state <| 825 | Bitwise.shiftRightZfBy 0 (Bitwise.or 1 (Bitwise.xor b c)) 826 | in 827 | step (map3 makeIndependentSeed gen gen gen) seed0 828 | 829 | 830 | 831 | -- MANAGER 832 | 833 | 834 | {-| Create a command that produces random values. Say you want to generate 835 | random points: 836 | 837 | import Random 838 | 839 | point : Random.Generator (Int, Int) 840 | point = 841 | Random.pair (Random.int -100 100) (Random.int -100 100) 842 | 843 | type Msg = NewPoint (Int, Int) 844 | 845 | newPoint : Cmd Msg 846 | newPoint = 847 | Random.generate NewPoint point 848 | 849 | Each time you run the `newPoint` command, it will produce a new 2D point like 850 | `(57, 18)` or `(-82, 6)`. 851 | 852 | **Note:** Read through [guide.elm-lang.org][guide] to learn how commands work. 853 | If you are coming from JS it can be hopelessly frustrating if you just try to 854 | wing it. And definitely ask around on Slack if you feel stuck! Investing in 855 | understanding generators is really worth it, and once it clicks, folks often 856 | dread going back to `Math.random()` in JavaScript. 857 | 858 | [guide]: https://guide.elm-lang.org/ 859 | -} 860 | generate : (a -> msg) -> Generator a -> Cmd msg 861 | generate tagger generator = 862 | command (Generate (map tagger generator)) 863 | 864 | 865 | type MyCmd msg = Generate (Generator msg) 866 | 867 | 868 | cmdMap : (a -> b) -> MyCmd a -> MyCmd b 869 | cmdMap func (Generate generator) = 870 | Generate (map func generator) 871 | 872 | 873 | init : Task Never Seed 874 | init = 875 | Task.andThen (\time -> Task.succeed (initialSeed (Time.posixToMillis time))) Time.now 876 | 877 | 878 | onEffects : Platform.Router msg Never -> List (MyCmd msg) -> Seed -> Task Never Seed 879 | onEffects router commands seed = 880 | case commands of 881 | [] -> 882 | Task.succeed seed 883 | 884 | Generate generator :: rest -> 885 | let 886 | (value, newSeed) = 887 | step generator seed 888 | in 889 | Task.andThen 890 | (\_ -> onEffects router rest newSeed) 891 | (Platform.sendToApp router value) 892 | 893 | 894 | onSelfMsg : Platform.Router msg Never -> Never -> Seed -> Task Never Seed 895 | onSelfMsg _ _ seed = 896 | Task.succeed seed 897 | --------------------------------------------------------------------------------