Craps

November 4, 2011

We play a single game of craps using the (crap) function:

(define (crap)
  (define (dice) (+ (randint 1 7) (randint 1 7)))
  (let loop ((roll (dice)) (point 0) (rolls '()))
    (if (zero? point)
        (cond ((member roll  (2 3 12)) (list #f roll))
              ((member roll  (7 11)) (list #t roll))
              (else (loop (dice) roll (cons roll rolls))))
        (cond ((= roll point) (cons #t (reverse (cons roll rolls))))
              ((= roll 7) (cons #f (reverse (cons roll rolls))))
              (else (loop (dice) point (cons roll rolls)))))))

We use the (crap) function to generate a list of craps games, suitable for calculating statistics:

(define (craps n)
  (do ((n n (- n 1)) (cs '() (cons (crap) cs))) ((zero? n) cs)))

Now, with some help from the Standard Prelude, we can calculate some statistics. Here is a histogram of the rolls seen in a thousand games:

> (map cdr (sort (lambda (a b) (< (car a) (car b)))
    (uniq-c = (sort (length (filter car (craps 1000)))
(98 184 253 358 438 565 435 382 231 176 100)

About half of games are winners:

> (length (filter car (craps 1000)))
504

The average game is about three-and-a-half rolls of the dice. There is of course no theoretical maximum, but we found one game with 26 rolls:

> (- (/ (apply + (map length (craps 1000))) 1000) 1.0)
3.579
> (- (apply max (map length (craps 1000))) 1)
26

We used filter, flatten, uniq-c and randint from the Standard Prelude. You can run the program at http://programmingpraxis.codepad.org/J9Tg9jZP.

About these ads

Pages: 1 2

18 Responses to “Craps”

  1. Graham said

    Tried my hand at a Common Lisp solution.

  2. GDean said
    n<-50000		#number of games
    aroll<-NA		
    win<-0
    loss<-0
    groll<-NA
    ploss<-0
    pwin<-0
    
    for	(y in 1:n)	{
    	a<-sample(1:6,2,replace=T)
    	roll<-a[1]+a[2]
    	aroll<-c(aroll,a[1]+a[2])
    	numroll<-1
    	if	((roll==2)||(roll==3)||(roll==12))	{
    		loss<-loss+1
    	}
    	if	((roll==7)||(roll==11))	{
    		win<-win+1
    	}
    	z<-(roll==2)||(roll==3)||(roll==12)||(roll==7)||(roll==11)
    	if	(z==FALSE)	{
    		point<-roll
    		repeat	{
    			a<-sample(1:6,2,replace=T)
    			roll<-a[1]+a[2]
    			aroll<-c(aroll,a[1]+a[2])
    			numroll<-numroll+1
    			if	((roll==7))	{
    				loss<-loss+1
    				ploss<-ploss+1
    				break
    			}
    			if	((roll==point))	{
    				win<-win+1
    				pwin<-pwin+1
    				break
    			}
    		}
    	}
    	groll<-c(groll,numroll)
    }
    
    print("MOST COMMON ROLL")
    arollt <- table(aroll)
    as.numeric(names(arollt[arollt == max(arollt)])) 
    
    print("WIN PERCENTAGE")
    print(100*win/(win+loss))
    
    print("AVERAGE NUMBER OF ROLLS PER GAME")
    groll<-groll[-1]
    mean(groll)
    
    print("MAXIMUM NUMBER of ROLLS IN A GAME")
    max(groll)
    
    print("Probability of Winning a Game with More than 1 Roll")
    print(100*pwin/(pwin+ploss))
    
  3. Graham said

    Wow, that might be the first R solution I’ve seen posted here. Makes sense, though, if you’re working with random variates and statistics. If there’s a way to vectorize the code, I don’t see it; so far the only thing I came up with was roll <- sum(sample(1:6, 2, replace=T)) (which isn’t very impressive).

  4. GDean said

    Graham, I think it does help though.. That solution is approaching novel lengths..

  5. Jussi Piitulainen said

    Do you guys see GDean’s R code as properly indented? For me it’s flush left in both Chromium and Firefox.
    This happened to me previously even though I used the advertised sourcecode tags that used to work.

    The HTML source for the R code is indented but the whitespace is not preserved.

  6. Graham said

    Jussi: as far as indentation goes, I usually post longer solutions offsite (github, codepad, etc.) and for shorter ones follow the instructions in the HOWTO link at the top of this page.

  7. programmingpraxis said

    I fixed the comment formatting. I changed &lt; and &quot; to their ascii equivalents, added a [sourcecodx lang="css"] to the top of the code, and [/sourcecodx] at the bottom of the code (note that I purposely mangled the word sourcecodx to prevent WordPress from writing my comment incorrectly). Looks fine to me.

    Jussi: Only a small number of languages are directly supported by the sourcecodx tag. You may have said something like [sourcecodx lang="fortran"]. I just looked at the WordPress support page linked in the HOWTO, and it appears they have added more languages since I wrote the HOWTO, and also added a feature for “plain text” source code: just say [sourcecodx] with no language parameter. I just checked by editing GDean’s code — that works, too. It’s too bad WordPress doesn’t support the <pre> tag.

    Graham: Unless the code is extremely long, it is better to have it here than elsewhere. In the first place it is more easily seen (I checked the stats — 110 people saw your comment, but only 9 clicked the link), and in the second place you can interrupt the code with annotations, as I frequently do in my suggested solutions.

  8. Jussi Piitulainen said

    Thank you, praxis, GDean’s R code looks good now. My unindented Python code is in the McCarthy thread. I said [sourcecodx lang="python"] — [/sourcecodx] unless I mistyped something (clever “x”) and python is listed as supported in the “how topost source code” page.

    Here, let me just try to see if it works:

    def evaluate(exp, env):
        return ( exp if isconstant(exp)
                    else lookup(exp, env) if isidentifier(exp)
                    else error() if not isnonemptylist(exp)
                    else call(exp, env) if not isidentifier(exp[0])
                    else special(exp, env) if isspecial(exp[0], env)
                    else evaluate(expand(exp, env), env) if ismacroword(exp[0], env)
                    else call(exp, env) )
    

    (Sorry about the off-topic code.)

  9. Jussi Piitulainen said

    Ok, looks good. Probably I have mistyped something when I submitted the McCarthy entry. Sorry for the noise.

  10. programmingpraxis said

    I fixed your McCarthy entry.

    It doesn’t look like you mistyped anything. I changed “lang” to “language” and it worked fine.

    I’ve seen this problem before, and don’t know how to deal with it. Sometimes “lang” works, and sometimes “language” works. I have no idea why.

    The workaround is probably to just ignore the language parameter and bracket your code with [sourcecodx] … [/sourcecodx] tags. You will lose the clever language-based formatting, but that should work with anything.

    The other approach is to format your code for html using the “foo-to-html” translator on the HOWTO page. That’s what I do for all the code presented in the suggested solutions. It’s inconvenient, but makes the code more integral to the running text.

    Private emails to me have one overwhelming complaint: readers can’t edit their own comments. I’ve considered moving from the WordPress comment forums to DISQUS, but haven’t done it because managing one blog is hard enough, and managing two things would doubtless be harder. Does anyone have experience with DISQUS?

  11. Jussi Piitulainen said

    The HOWTO page seems to suggest that HTML code tags work in this comment box. I just experimented with HTML style attributes, where style="white-space: pre" should preserve all whitespace. It worked in my tests.

    If this comment box allows both the code tags and the attribute, the following test code appears properly indented without any special markup in the code itself:

    (define (gcd m n)
    (cond ((< m n) (gcd m (- n m)))
    ((< n m) (gcd (- m n) n))
    (else m)))

    If not, not.

  12. Jussi Piitulainen said

    I think it stripped the attribute out. Just one repeat to test that I did not forget to type it in. And why would it preserve linebreaks then? Also, I’m now replacing the less-than signs with the entity, which I did not do above.
    (define (gcd m n)
    (cond ((< m n) (gcd m (- n m)))
    ((< n m) (gcd (- m n) n))

    Without the style attribute but with the non-breaking spaces suggested in the HOWTO, and nothing about linebreaks:
    (define (gcd m n)
      (cond ((< m n) (gcd m (- n m)))
            ((< n m) (gcd (- m n) n))

  13. Jussi Piitulainen said

    Ok, it seems like specifying the white-space style as pre or pre-wrap for the code element should work since the software recognizes the code tags. It does not preserve the style attribute, however, and I failed to find out how it manages to obey line breaks in code while ignoring indentations.

    Wait. I did not fail to find out. It adds br-elements at line breaks inside code elements. It’s doing too much and too little at the same time. So we continue to indent the hard way. Ok.

  14. programmingpraxis said

    When I first started the blog, I spent a day experimenting, as you have done. I’m pretty sure the choices are what I listed in the HOWTO, except that now WordPress seems to handle a [sourcecodx] . . . [/sourcecodx] without a language designator as plain text, which it did not do back then. I’ll add that to the HOWTO.

    Thanks for trying,

    Phil

  15. Jussi Piitulainen said

    Here’s some on-topic code, indented with sed as in the HOWTO page.
    The rules of the game are encoded in a generator that produces one
    game.

    >>> seed(exp(1))
    >>> list(game())
    [(6, None), (9, None), (4, None), (4, None), (6, 'win')]

    The log odds (meant to be to the base that Turing and Good used)
    thing is sometimes positive, sometime negative, which seems to
    suggest that the game is more or less fair.

    >>> logodds(100)
    0.17374096069422723
    >>> logodds(100)
    -0.6963592814139442

    This is Python 3. The magic keyword is yield. A couple of the generator things below produce indefinitely long sequences. In theory, game could do so but in practice it of course never does. It is safe to ask for the next game, or the next grade, or the 100 next games as above.

    from random import seed, randrange
    from math import exp, log10
    from itertools import islice

    def die():
        return 1 + randrange(6)

    def game(): # graded shots of a single game
        shot = die() + die()
        grade = ( "loss" if shot in (2, 3, 12)
                  else "win" if shot in (7, 11)
                  else None )
        yield shot, grade
        point = shot
        while not grade:
            shot = die() + die()
            grade = ( "win" if shot == point
                      else "loss" if shot == 7
                      else None )
            yield shot, grade

    def games(): # graded games, indefinitely
        while True:
            shots = list(game())
            yield ( [ shot for shot, grade in shots ],
                    shots[-1][1] )

    def grades(): # grades, indefinitely
        for shots, grade in games():
            yield grade

    def logodds(n): # 10 log10 (wins / losses) in n games
        iswin = lambda g : g == "win"
        wins = list(filter(iswin, islice(grades(), n)))
        return 10 * log10(len(wins) / (n - len(wins)))

    This can be done in Scheme but it would be a much longer story. Python has these built-in.

  16. ardnew said

    Since we’re discussing it, and just to put us back off topic again (sorry), I would like to point out that perl is another supported language by whatever is handling the attribute.

    The HOWTO seems to leave it out, but I’ve posted several perl sources this way

  17. Graham said

    I agree with Jussi on the usefulness of yield and generators in Python; if I have time, I’ll post a Python version myself later.

  18. A Clojure solution:

    (defn dice []
      (+ (inc (rand-int 6)) (inc (rand-int 6))))
    
    (def win? #{7 11})
    (def lose? #{2 3 12})
    
    (defn game []
      (loop [rolls 1 result (dice)]
        (cond
          (win? result) (list true rolls)
          (lose? result) (list false rolls)
          :else (recur (inc rolls) (dice)))))
    
    (defn games [n]
      (loop [i 0 results ()]
        (if (= i n) results
          (recur (inc i) (conj results (game))))))
    
    (defn stats [results]
      (let [rolls (map #(second %1) results) ngames (count results)]
        (println "Wins:" (count (filter #(first %1) results)) "/" ngames)
        (println "Avg rolls:" (/ (double (apply + rolls)) ngames))
        (println "Max rolls:" (reduce max rolls))))
    
    (stats (games 1000))
    

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 612 other followers

%d bloggers like this: