10
\$\begingroup\$

Implement a program or function which simulates common dice for role playing games. It should handle at least the d6 and d20, the two most common dice.

However, it should work as stereotypical gamers expect them to work, and not as real dice work.

It is an in-joke between gamers, that one can have a specially lucky die for a very very important roll, by previously throwing a lot of dice, selecting those which resulted in a "1", then throwing them again, until you get a few which rolled a "1" multiple times. You then carefully preserve them, because they rolled a 1 multiple times in sequence, so the probability to roll a 1 next time should be extremely low.

Of course, this is not how dice work in real life, because the rolls are statistically independent.

Your simulated dice have to take previous rolls into account, and work similarly to how the gambler in the gambler's fallacy expects it to work. For example, if a lot of low numbers were rolled, the probability of rolling a higher number should be increased.

However, as this is cheating, you have to hide it well. This means, a casual glance upon the program should not reveal that you cheated. This means, explicitly saving the previous results and reading them on every throw would be too suspicious. You have to hide this "property" of your dice, and bonus points if you make it plausible deniable and disguise it as an honest mistake. (for example, you make your own RNG with an "unintentional" flaw)

Voters, please take into account how well hidden this "flaw" is.

The programs should be clear, and not obfuscated. It's too easy to hide evil code in an obfuscated program.

\$\endgroup\$

closed as off-topic by Mego, Sriotchilism O'Zaic, Stewie Griffin, pajonk, J. Sallé Dec 4 '17 at 12:18

  • This question does not appear to be about programming puzzles or code golf within the scope defined in the help center.
If this question can be reworded to fit the rules in the help center, please edit the question.

  • 3
    \$\begingroup\$ How well hidden are we talking about? IMO, anything beyond the language's equivalent of getRandomBetween(1,d) would cause me to look deeper into it. \$\endgroup\$ – Geobits Mar 8 '14 at 20:54
  • \$\begingroup\$ @Geobits : you can find a very nice example for how to solve underhanded problems here: codegolf.stackexchange.com/questions/19569/… I mean you can do anything if you justify it well enough, of course, the justification can be a big lie. \$\endgroup\$ – vsz Mar 8 '14 at 20:57
  • \$\begingroup\$ Godmaydamnit, java doesnt has enough quirks for underhanded stuff... \$\endgroup\$ – masterX244 Mar 8 '14 at 21:03
  • \$\begingroup\$ Related: thedailywtf.com/articles/introducing-the-lucky-deuce \$\endgroup\$ – Neil Jan 16 '16 at 16:45
  • 4
    \$\begingroup\$ I'm voting to close this question as off-topic because underhanded challenges are off-topic now, and somehow this one slipped under the radar. \$\endgroup\$ – Mego Dec 4 '17 at 5:46
3
\$\begingroup\$

Java

public class GamerDie {
    private final java.util.Random rnd;
    private final int sides;

    public GamerDie(int sides) {
        this.sides = sides;
        this.rnd = new java.util.Random();
    }

    public int throw() {
        return rnd.nextInt(sides) + 1;
    }
}

It's so simple that it's obviously not hiding anything: but java.util.Random is a straightforward linear congruential generator, and it uses a discard technique to ensure uniformity, so it guarantees that in any run of the largest multiple of size smaller than 2^48 samples it will distribute the numbers evenly, satisfying the requirement.

\$\endgroup\$
  • \$\begingroup\$ cant get behind the explain how java.util.random works \$\endgroup\$ – masterX244 Mar 8 '14 at 21:56
  • \$\begingroup\$ The discarding that java.util.Random performs has very little to do with the behavior of this answer. Really, what this answer relies on is the fact that like any RNG, java.util.Random has a period, and if you generate a number of numbers on the order of the period, its statistical properties break down. That's not very interesting; the same would happen with even a cryptographically-secure RNG like Blum Blum Shub if you ran it long enough. \$\endgroup\$ – user2357112 Mar 17 '14 at 6:41
  • \$\begingroup\$ @user2357112, the discarding is relevant because the question requires uniformity, not a small bias to smaller numbers. In my opinion this answer epitomises underhandedness: the deliberate use of a standard library in a way which at first glance seems transparently correct but actually takes it outside of its design parameters. \$\endgroup\$ – Peter Taylor Mar 17 '14 at 8:37
  • \$\begingroup\$ Pretty much every RNG does the discarding thing, though. It's nothing special. You could have used this answer with literally any pseudorandom number generator, because if an RNG 1) has a period, and 2) can produce more than 1 different number, then within the scope of a single period, the more a number has shown up relative to other numbers, the less it will show up until the next period by a simple counting argument. \$\endgroup\$ – user2357112 Mar 17 '14 at 9:02
  • \$\begingroup\$ The analysis in this answer requires on the order of 2^48 rolls for an effect to show up. Maybe if you had used a more sophisticated analysis, showing that the use of an LCG causes measurable statistical anomalies to appear within a number of rolls that would plausibly appear in a tabletop game, this might be an okay answer. When you're talking about trillions of rolls, though, it's just not very underhanded. \$\endgroup\$ – user2357112 Mar 17 '14 at 9:03
0
\$\begingroup\$

Ruby

Currently only supports d6, will add d20 support later on...

Lo and behold - those dices are nasty!

# first idea was to create 6 super cool dices just by copy&paste
# -> each dice holds its number at the beginning of the array
# -> we don't need all of them now, so we comment them out
dice0 = %w[[[[[[[[[ 0 . : :. :: ::. ::: ]]]]]]]]
#dice1 = %w[[[[[[[ 1 : . :. ::. :: ::: ]]]]]]]
#dice2 = %w[[[[[[ 2 . : :. :: ::. ::: ]]]]]]
#dice3 = %w[[[[[[ 3 : . :. ::. :: ::: ]]]]]]]
#dice4 = %w[[[[[[[ 4 . : :. :: ::: ::. ]]]]]]]
#dice5 = %w[[[[[[[[ 5 . : :. :: ::. ::: ]]]]]]]]]

# and hey, those dices are almost ascii art ;)

# well, let's just create a standard dice
# -> get rid of the number at the beginning
# -> then sort (maybe we need that later due to the
#    currently unused dices being unsorted)
dice = dice0.select!{|e| /[:.]+/ === e}.sort

def roll(d)
  # rolling is easy
  # -> use size instead of hardcoded number,
  #   maybe we'll have other dices later
  d.slice!(rand(d.size - 1))
end

# and here you have 8 very underhanded dices!
dices = [dice]*8

# roll like a champion
roll(dices[0])
...
\$\endgroup\$
  • \$\begingroup\$ I'd add an "abort "requires ruby 2" if RUBY_VERSION < "2"' in there somewhere, as if you run it on earlier versions it spoils the trick \$\endgroup\$ – bazzargh Mar 17 '14 at 16:45
0
\$\begingroup\$

Haskell

Use one random thing to make another random thing: in this case, shuffle cards to generate dice throws.

import System.Environment
import System.Random
import Data.Array.IO
import Control.Monad
-- make random dice from random cards
suit c=map (\(a,b)->[a,b])$zip "A23456789TJQK" (repeat c)
deck=concatMap(\s->suit s) "♠♥♦♣"
-- just like casinos, use more decks for extra randomness
decks=concat$take 8$repeat deck
-- shuffle the cards
shuffle :: [a] -> IO [a]
shuffle xs = do
        ar <- newArray n xs
        forM [1..n] $ \i -> do
            j <- randomRIO (i,n)
            vi <- readArray ar i
            vj <- readArray ar j
            writeArray ar j vi
            return vj
  where
    n = length xs
    newArray :: Int -> [a] -> IO (IOArray Int a)
    newArray n xs =  newListArray (1,n) xs
-- convert a card to a die, by counting along the original deck
-- then taking mod (faces). If we don't have enough cards to make
-- a full set of faces, assign the 'extra' cards a value of 0
card2die faces card=
  let index=(head[i|(i,c)<-zip[0..]deck,c==card]) in
  if (index > (length deck-(length deck`mod`faces)))
  then 0
  else (index`mod`faces)+1
main=
  do
    args <- getArgs
    let faces = read (args!!0)
    -- throw away cards we can't map to die faces
    cards<-shuffle$filter (\card->card2die faces card/=0) decks
    mapM_ (\card->putStrLn (card++" -> "++(show (card2die faces card)))) cards

Takes one argument, the number of faces on the die. Output is like this:

./cards 20|head
2♦ -> 8
7♥ -> 20
J♦ -> 17
6♥ -> 19
9♥ -> 2
8♥ -> 1
5♥ -> 18
4♠ -> 4
Q♥ -> 5
2♣ -> 1

... and so on for all cards (discards are not printed). Too obvious?

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.