<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>DanAllan.com &#187; Projects &amp; Adventures</title>
	<atom:link href="http://www.danallan.com/category/projects/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.danallan.com</link>
	<description></description>
	<lastBuildDate>Wed, 21 Dec 2011 18:24:01 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Couples&#8217; Dreidel</title>
		<link>http://www.danallan.com/projects/2011/couples-dreidel/</link>
		<comments>http://www.danallan.com/projects/2011/couples-dreidel/#comments</comments>
		<pubDate>Fri, 16 Dec 2011 18:26:34 +0000</pubDate>
		<dc:creator>Dan Allan</dc:creator>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[Projects & Adventures]]></category>

		<guid isPermaLink="false">http://www.danallan.com/?p=1667</guid>
		<description><![CDATA[During my first-ever game of dreidel this week, we agreed on a friendly variation in the rules. When someone was about to run out of chocolate coins and be knocked out of the game, couples could rescue each other with a &#8220;loan.&#8221; Eventually we put a stop to that, thinking that all this rescuing was [...]]]></description>
			<content:encoded><![CDATA[<p>During my first-ever game of dreidel this week, we agreed on a friendly variation in the rules. When someone was about to run out of chocolate coins and be knocked out of the game, couples could rescue each other with a &#8220;loan.&#8221; Eventually we put a stop to that, thinking that all this rescuing was making the game drag on.</p>
<p>For a definitive answer, I wrote a short Python script that simulates thousands of dreidel games, tallying how many turns are played. As it turns out, we were wrong. If you allow couples to win a joint victory, then games that allow rescuing actually end much faster. Couples defeat other couples and other single players faster than single players defeat each other, even though all players &mdash; coupled or not &mdash; still take separate turns. If you insist on having only one winner, making the couple break their alliance in the end and play out the game, the games get a little longer but the effect it not large. (I originally published a different conclusion about this last scenario.)</p>
<pre>
Simulating 10,000 6-player games. Each player start with 5 coins.

No couples: 112 minutes
One couple: 101 minutes
Two couples: 84 minutes
Three couples: 68 minutes

Disallow joint victories:
One couple: 114 minutes
Two couples: 121 minutes
Three couples: 127 minutes
</pre>
<p>The statistical error of these results is about one minute.<br />
<a href="#" id="example-show" class="showLink" onclick="showHide('example');return false;">See the code.</a></p>
<div id="example" class="more">
<pre class="brush: python">#!/usr/bin/python

# How long does a game of dreidel take?
# How does that change when couples form &quot;alliances&quot; and help each other out?

import random
from numpy import average, round
from scipy.stats import stderr

def pay(i, coins, couples):
	&quot;&quot;&quot;Pay what is owed, or have your partner pay, or lose the game.&quot;&quot;&quot;
	if coins[i] &gt; 0:
		# Player can pay.
		coins[i] -= 1
		paid = True
	elif couples[i]:
		# Player has a partner.
		if coins[couples[i]] &gt; 0:
			# That partner can afford a loan.
			coins[couples[i]] -= 1
			paid = True
		else:
			coins[i] = None # Player is out of the game.
			paid = False
	else:
		coins[i] = None # Player is out of the game.
		paid = False
	return coins, paid

def play_round(coins, couples, turn_counter, next_player):
	&quot;&quot;&quot;Simulate one round of the game, and count turns.&quot;&quot;&quot;
	# Ante up.
	pot = 0
	for i in range(len(coins)):
		if coins[i] is not None:
			coins, paid = pay(i, coins, couples)
			if (paid): pot += 1

	# Take turns.
	while pot &gt; 0:
		# List players in order, starting with whoever&#039;s turn it is.
		for i in map(lambda x: x % len(coins), range(next_player, next_player+len(coins))):
			if coins[i] is None:
				continue # Player is already out of the game.
			roll = random.choice([&#039;nun&#039;, &#039;gimmel&#039;, &#039;hey&#039;, &#039;shin&#039;])
			if roll is &#039;gimmel&#039;:
				coins[i] += pot
				pot = 0
			elif roll is &#039;hey&#039;:
				coins[i] += pot/2
				pot = pot - pot/2
			elif roll is &#039;shin&#039;:
				coins, paid = pay(i, coins, couples)
				if paid:
					pot += 1
			turn_counter += 1
			if pot is 0:
				next_player = i+1
				break

	return coins, turn_counter, next_player

def game(number_of_players, starting_money, couples, allow_joint_victory=True):
	&quot;&quot;&quot;Simulate a game, and count turns.&quot;&quot;&quot;
	coins = number_of_players*[starting_money]
	turn_counter = 0
	next_player = 0

	# In the function call, couples may be specified as an explicit list with a seating arrangement.
	# Alternatively, the call may simply give the NUBMER of couples. They will be seated beside each other.
	if type(couples) is not list:
		number_of_couples = couples
		couples = number_of_players*[None]
		for i in range(number_of_couples):
			couples[2*i]=2*i+1
			couples[2*i+1]=2*i

	while True:
		# Check whether game has ended.
		winners = [i for i, c in enumerate(coins) if c is not None]
		if (len(winners) is 1): break # One winner.
		if (len(winners) is 2 and
			winners[0] == couples[winners[1]]):
				if (allow_joint_victory is True):
					break # Joint win.
				else:
					# End the alliance and play it out.
					couples = number_of_players*[None]

		coins, turn_counter, next_player = play_round(coins, couples, turn_counter, next_player)
	return turn_counter

def simulate_games(N, number_of_players, starting_money, number_of_couples, allow_joint_victory=True):
	&quot;&quot;&quot;Return average game duration of N simulated games.&quot;&quot;&quot;
	TURNS_PER_MIN = 5 # estimated turns per minute, to interpret result
	outcomes = [game(number_of_players, starting_money, number_of_couples, allow_joint_victory) for x in range(N)]
	value = int(round(average(outcomes)/TURNS_PER_MIN))
	error = int(round(stderr(outcomes)/TURNS_PER_MIN))
	return str(value) + &#039; +/- &#039; + str(error) + &#039; minutes&#039;

TRIALS = 10000
N_PLAYERS = 6
MONEY = 5

print &#039;Simulating 10,000 6-player games. Each player starts with 5 coins.&#039;
print &#039;No couples:&#039;, simulate_games(TRIALS, N_PLAYERS, MONEY, 0)
print &#039;One couple:&#039;, simulate_games(TRIALS, N_PLAYERS, MONEY, 1)
print &#039;Two couples:&#039;, simulate_games(TRIALS, N_PLAYERS, MONEY, 2)
print &#039;Three couples:&#039;, simulate_games(TRIALS, N_PLAYERS, MONEY, 3)

print &#039;Disallow joint victories:&#039;
print &#039;One couple:&#039;, simulate_games(TRIALS, N_PLAYERS, MONEY, 1, False)
print &#039;Two couples:&#039;, simulate_games(TRIALS, N_PLAYERS, MONEY, 2, False)
print &#039;Three couples:&#039;, simulate_games(TRIALS, N_PLAYERS, MONEY, 3, False)</pre>
<p><a href="#" id="example-hide" class="hideLink" onclick="showHide('example');return false;">Hide the code.</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.danallan.com/projects/2011/couples-dreidel/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

