Brief Summary
I developed a tool for extracting entropy from dice roll results. It aggregates rolls, using various group sizes, to achieve a high extraction efficiency.
Contents
- Brief Summary
- Summary
- Downloadable Assets
- How To Use The Tools
- Project Log
Summary
I developed a tool for extracting entropy from dice roll results. It aggregates rolls, using various group sizes, to achieve a high extraction efficiency.
To produce a Bitcoin private key, i.e. 32 bytes (256 bits) of entropy, it requires an estimated 103 dice rolls, from which it will extract entropy at ~97% efficiency. Please note that the required number of rolls and the efficiency are only mathematical estimates, not guarantees.
For small numbers of rolls, less aggregation is possible, so the efficiency is lower. For 1 bit, the efficiency is ~52%. For 1 byte, ~66%. For 2 bytes, ~80%. For 8 bytes, ~93%.
Please see the Downloadable Assets section for the tool and a corresponding test suite, and the How To Use The Tool section for instructions and examples.
Downloadable Assets
Assets of this article:
List:
- entropy_from_dice.py
- test_entropy_from_dice.py
- test_entropy_from_dice.py
Asset: A tool that extracts entropy from dice roll results. Python 2.7.12.
entropy_from_dice.py
Asset: A test suite for the extraction tool. Pytest 4.6.11.
test_entropy_from_dice.py
How To Use The Tools
Help:
python entropy_from_dice.py --help
Main:
python entropy_from_dice.py -f dice_rolls.txt
Note: By default, the tool looks for a file named 'dice_rolls.txt' in its local directory, so in the above command the filepath argument is optional.
python entropy_from_dice.py --diceRollsString 664532 --outputType=bits --entropyAmount=2 --keepExtraEntropy
Estimate:
python entropy_from_dice.py -n 32 --estimate --debug
Note: I prefer to increase an estimation result by about 5-10%, so that I can be fairly sure that the desired amount of entropy will be produced on the first run.
Test:
pytest -q
Note: The test suite file needs to be in the same directory as the tool file.
Examples of use:
stjohn@judgement:work$ python entropy_from_dice.py -f dice_rolls.txt
089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8
[64 hex characters = 32 bytes]
stjohn@judgement:work$ echo -n '089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8' | wc -c
64
[By default, the tool looks for a file named 'dice_rolls.txt' in its local directory.]
stjohn@judgement:work$ python entropy_from_dice.py
089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8
stjohn@judgement:work$ python entropy_from_dice.py --diceRollsString 664532 --outputType=bits --entropyAmount=2 --keepExtraEntropy
1011001101
stjohn@judgement:work$ python entropy_from_dice.py --diceRollsString 4532123123123 --outputType=bits --entropyAmount=0 --keepExtraEntropy --logLevel=info
- diceRollsString: 4532123123123666
- Number of dice rolls: 13
- Extracted bits: '101000010011000001110010010000110'
- Number of extracted bits: 33
- Extraction efficiency: 98.20%
101000010011000001110010010000110
stjohn@judgement:work$ python entropy_from_dice.py -n 32 --estimate
103
stjohn@judgement:work$ python entropy_from_dice.py -n 32 --estimate --debug
DEBUG [263: estimateRequiredDiceRolls] Estimating required dice rolls for 256 bits of entropy:
DEBUG [288: estimateRequiredDiceRolls] dice group size 53 (137 bits), 1 groups, 137 bits handled, 119 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 53 (137 bits), 0 groups, 0 bits handled, 119 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 12 (31 bits), 3 groups, 93 bits handled, 26 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 12 (31 bits), 0 groups, 0 bits handled, 26 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 7 (18 bits), 1 groups, 18 bits handled, 8 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 7 (18 bits), 0 groups, 0 bits handled, 8 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 2 (5 bits), 1 groups, 5 bits handled, 3 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 2 (5 bits), 0 groups, 0 bits handled, 3 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 1 (2 bits), 1 groups, 2 bits handled, 1 bits to go.
DEBUG [279: estimateRequiredDiceRolls] No smaller group sizes to use. Add 1 more group of the current size.
DEBUG [288: estimateRequiredDiceRolls] dice group size 1 (2 bits), 1 groups, 1 bits handled, 0 bits to go.
INFO [126: main] Estimated dice rolls required for 256 bits of entropy (with efficiency=0.9716):
103
stjohn@judgement:work$ python entropy_from_dice.py --diceRollsString 43 --outputType=bits --entropyAmount=0 --keepExtraEntropy --debug
INFO [189: processDiceRolls] diceRollsString: 43
INFO [191: processDiceRolls] Number of dice rolls: 2
DEBUG [193: processDiceRolls] Processing dice rolls:
DEBUG [331: convertDiceRollsToBitsUsingLargestPossibleGroups] diceRollsString: 43
DEBUG [332: convertDiceRollsToBitsUsingLargestPossibleGroups] Converting diceRollsString to bits using groupSizeList [53, 12, 7, 2, 1]
DEBUG [339: convertDiceRollsToBitsUsingLargestPossibleGroups] Can't use groupSize=53 - it's larger than the remaining number of dice rolls (2).
DEBUG [339: convertDiceRollsToBitsUsingLargestPossibleGroups] Can't use groupSize=12 - it's larger than the remaining number of dice rolls (2).
DEBUG [339: convertDiceRollsToBitsUsingLargestPossibleGroups] Can't use groupSize=7 - it's larger than the remaining number of dice rolls (2).
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 2. Using groupSize=2. Converting dice rolls section '43' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 43
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=2, groupSize=2, a=6**groupSize=6**2=36
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=36. Result: 2**5 = 32.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=36, nextLowestIntegerPowerOf2 = 32 (=2**5)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 32-1 = 31
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 62
DEBUG [423: convertDiceRollsToBits] - rolls section '43' ('32' in base6), value=20, bits='10100'
DEBUG [424: convertDiceRollsToBits] 0 groups of 2 dice roll values ignored because they were greater than the maxDiceRollValue 62 (51 in base6, 31 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '10100'
INFO [195: processDiceRolls] Extracted bits: '10100'
INFO [197: processDiceRolls] Number of extracted bits: 5
INFO [200: processDiceRolls] Extraction efficiency: 96.71%
10100
stjohn@judgement:work$ pytest test_entropy_from_dice.py -q
....................................................................... [ 78%]
................... [100%]
90 passed in 0.24 seconds
Project Log
In this project, I'm going to work on extracting entropy bytes from dice roll results. I'll aggregate the dice rolls to increase the efficiency of the extraction process.
In the previous project Generating entropy with dice, the entropy extraction efficiency was 66%.
1 dice roll has 6 possible outcomes: 1,2,3,4,5,6.
A single roll result from 1 dice is a number in base 6 (with a 1-indexed numeral system).
Let's calculate the Shannon entropy of a dice.
Entropy(S) = - SUM_i (p_i log_2(p_i))
For p_i = 6:
Entropy(S) = - SUM_i=1_6 ((1/6) log_2(1/6))
= - log_2(1/6)
= log_2(6)
~= 2.5850
So: 1 dice roll has 2.5850 bits of entropy, as a theoretical maximum. One way to think about this is that it takes ~2.5850 bits to transmit the dice roll result over a communication channel that can only handle binary data.
2 dice rolls have 2 * 2.5850 = 5.1700 bits of entropy.
Also: 2 dice rolls have 6 * 6 = 36 possible results.
Table of possible results:
(1,1) (1,2) (1,3) (1,4) (1,5) (1,6)
(2,1) (2,2) (2,3) (2,4) (2,5) (2,6)
(3,1) (3,2) (3,3) (3,4) (3,5) (3,6)
(4,1) (4,2) (4,3) (4,4) (4,5) (4,6)
(5,1) (5,2) (5,3) (5,4) (5,5) (5,6)
(6,1) (6,2) (6,3) (6,4) (6,5) (6,6)
(2,1) (2,2) (2,3) (2,4) (2,5) (2,6)
(3,1) (3,2) (3,3) (3,4) (3,5) (3,6)
(4,1) (4,2) (4,3) (4,4) (4,5) (4,6)
(5,1) (5,2) (5,3) (5,4) (5,5) (5,6)
(6,1) (6,2) (6,3) (6,4) (6,5) (6,6)
32 is an integer power of 2.
32 = 2 ** 5
So the base 10 numbers in [0,31] can be mapped exactly to the base 2 numbers in [0, 11111].
36 - 32 = 4. I won't attempt to extract the entropy in the last 4 possible results. I'll just recurse i.e. move on to the next dice roll value.
2^5 = 5 bits. So, in 32/36 cases of rolling 2 dice, I'll be able to extract 5 bits of entropy. In 4/36 cases: 0 bits of entropy.
So, on average: 4/36 * 0 + 32/36 * 5 ~= 4.4444 bits of entropy per 2 dice rolls.
This has an efficiency of 4.4444 / 5.1700 ~= 0.8600 = 86.00%.
I can generalise this efficiency calculation for different numbers of aggregated dice.
Note: In this approach, I've chosen to always multiply the first fraction in the equation by 0, so it can be ignored.
Algorithm:
let b = bits per dice roll = log_2(6) ~= 2.5850.
let n = number of aggregated dice (dice group size).
let a = 6^n = size of possibility space of dice group outcome.
let m = greatest integer power of 2 that is less than or equal to a.
let x = integer exponent of m.
let efficiency = ef = ((m/a) * x) / (n * b)
= (m * x) / (a * n * b)
let n = number of aggregated dice (dice group size).
let a = 6^n = size of possibility space of dice group outcome.
let m = greatest integer power of 2 that is less than or equal to a.
let x = integer exponent of m.
let efficiency = ef = ((m/a) * x) / (n * b)
= (m * x) / (a * n * b)
Let's test this algorithm with n=1.
b = 2.5850
n = 1
a = 6^n = 6^1 = 6
m = 4 (= 2^2)
x = 2
ef = (m * x) / (a * n * b)
= (4 * 2) / (6 * 1 * 2.5850)
= 8 / (6 * 2.5850)
~= 0.5158
~= 51.58%
n = 1
a = 6^n = 6^1 = 6
m = 4 (= 2^2)
x = 2
ef = (m * x) / (a * n * b)
= (4 * 2) / (6 * 1 * 2.5850)
= 8 / (6 * 2.5850)
~= 0.5158
~= 51.58%
Hm.
It looks like my original calculation in the previous project was skewed.
I simply ignored dice roll results in [5,6] and assumed that, because I was processing results in [1,2,3,4] that comprised 66% of the possibility space, I was extracting 66% of the available entropy.
I extracted 1 base16 hex byte per 2 dice rolls, i.e. 4 bits per 2 dice rolls, or 2 bits per dice roll. If 2 bits of entropy was the theoretical maximum entropy available in each dice roll, then, yes, the efficiency would have 66%. However, there is an additional fractional part of the entropy per dice roll, 2.5850 - 2 = 0.5850, which I didn't take account of. So, the actual efficiency of my previous approach was 51.58%.
From earlier, the efficiency of a new process that aggregated the dice properly into groups of 2 would be 86.00%, which is a significant gain.
Let's test this algorithm with n=2, to confirm that the result is as expected.
b = 2.5850
n = 2
a = 6^n = 6^2 = 36
m = 32 (= 2^5)
x = 5
ef = (m * x) / (a * n * b)
= (32 * 5) / (36 * 2 * 2.5850)
= 160 / (72 * 2.5850)
~= 0.8600
= 86.00%
n = 2
a = 6^n = 6^2 = 36
m = 32 (= 2^5)
x = 5
ef = (m * x) / (a * n * b)
= (32 * 5) / (36 * 2 * 2.5850)
= 160 / (72 * 2.5850)
~= 0.8600
= 86.00%
Good. This matches the previous manually-calculated result.
Let's automate this calculation, and perform it for more numbers.
calc_ef.py
def main(): | |
results = {} | |
b = 2.5850 | |
for n in xrange(1,101): | |
a = 6**n | |
m, x = nextLowestIntegerPowerOf2(a) | |
ef = (m * x) / (a * n * b) | |
ef *= 100 | |
print "{n} dice aggregated: efficiency = {ef:.2f}%".format(n=n, ef=ef) | |
results[n] = ef | |
max_n = max(results, key=lambda k: results[k]) | |
print "- Greatest efficiency ({ef:.2f}%) found at {n} dice.".format(n=max_n, ef=results[max_n]) | |
print "- Next: Sort by efficiency." | |
for n in sorted(results, key=lambda k: results[k], reverse=True): | |
print "{n} dice ({ef:.2f}%)".format(n=n, ef=results[n]) | |
def nextLowestIntegerPowerOf2(n): | |
i = 0 | |
m = 0 | |
# increment power of 2 until we surpass the given value. | |
while m < n: | |
m = 2**i | |
i += 1 | |
# go back down one power. | |
i -= 2 | |
m = 2**i | |
return m, i | |
if __name__ == '__main__': | |
main() |
stjohn@judgement:work$ python calc_ef.py
1 dice aggregated: efficiency = 51.58%
2 dice aggregated: efficiency = 85.97%
3 dice aggregated: efficiency = 53.49%
4 dice aggregated: efficiency = 76.41%
5 dice aggregated: efficiency = 48.91%
6 dice aggregated: efficiency = 67.92%
7 dice aggregated: efficiency = 93.15%
8 dice aggregated: efficiency = 60.38%
9 dice aggregated: efficiency = 82.29%
10 dice aggregated: efficiency = 53.67%
11 dice aggregated: efficiency = 72.86%
12 dice aggregated: efficiency = 98.59%
13 dice aggregated: efficiency = 64.59%
14 dice aggregated: efficiency = 87.23%
15 dice aggregated: efficiency = 57.29%
16 dice aggregated: efficiency = 77.27%
17 dice aggregated: efficiency = 50.85%
18 dice aggregated: efficiency = 68.50%
19 dice aggregated: efficiency = 92.17%
20 dice aggregated: efficiency = 60.76%
21 dice aggregated: efficiency = 81.69%
22 dice aggregated: efficiency = 53.91%
23 dice aggregated: efficiency = 72.44%
24 dice aggregated: efficiency = 97.26%
25 dice aggregated: efficiency = 64.26%
26 dice aggregated: efficiency = 86.24%
27 dice aggregated: efficiency = 57.02%
28 dice aggregated: efficiency = 76.50%
29 dice aggregated: efficiency = 50.61%
30 dice aggregated: efficiency = 67.87%
31 dice aggregated: efficiency = 90.99%
32 dice aggregated: efficiency = 60.23%
33 dice aggregated: efficiency = 80.72%
34 dice aggregated: efficiency = 53.46%
35 dice aggregated: efficiency = 71.63%
36 dice aggregated: efficiency = 95.95%
37 dice aggregated: efficiency = 63.58%
38 dice aggregated: efficiency = 85.15%
39 dice aggregated: efficiency = 56.44%
40 dice aggregated: efficiency = 75.57%
41 dice aggregated: efficiency = 50.11%
42 dice aggregated: efficiency = 67.08%
43 dice aggregated: efficiency = 89.79%
44 dice aggregated: efficiency = 59.55%
45 dice aggregated: efficiency = 79.70%
46 dice aggregated: efficiency = 52.87%
47 dice aggregated: efficiency = 70.75%
48 dice aggregated: efficiency = 94.66%
49 dice aggregated: efficiency = 62.82%
50 dice aggregated: efficiency = 84.04%
51 dice aggregated: efficiency = 55.78%
52 dice aggregated: efficiency = 74.61%
53 dice aggregated: efficiency = 99.79%
54 dice aggregated: efficiency = 66.25%
55 dice aggregated: efficiency = 88.59%
56 dice aggregated: efficiency = 58.83%
57 dice aggregated: efficiency = 78.66%
58 dice aggregated: efficiency = 52.24%
59 dice aggregated: efficiency = 69.85%
60 dice aggregated: efficiency = 93.39%
61 dice aggregated: efficiency = 62.03%
62 dice aggregated: efficiency = 82.93%
63 dice aggregated: efficiency = 55.09%
64 dice aggregated: efficiency = 73.64%
65 dice aggregated: efficiency = 98.43%
66 dice aggregated: efficiency = 65.40%
67 dice aggregated: efficiency = 87.41%
68 dice aggregated: efficiency = 58.08%
69 dice aggregated: efficiency = 77.63%
70 dice aggregated: efficiency = 51.59%
71 dice aggregated: efficiency = 68.94%
72 dice aggregated: efficiency = 92.13%
73 dice aggregated: efficiency = 61.23%
74 dice aggregated: efficiency = 81.82%
75 dice aggregated: efficiency = 54.39%
76 dice aggregated: efficiency = 72.67%
77 dice aggregated: efficiency = 97.10%
78 dice aggregated: efficiency = 64.55%
79 dice aggregated: efficiency = 86.24%
80 dice aggregated: efficiency = 57.33%
81 dice aggregated: efficiency = 76.60%
82 dice aggregated: efficiency = 50.93%
83 dice aggregated: efficiency = 68.04%
84 dice aggregated: efficiency = 90.89%
85 dice aggregated: efficiency = 60.43%
86 dice aggregated: efficiency = 80.73%
87 dice aggregated: efficiency = 53.68%
88 dice aggregated: efficiency = 71.71%
89 dice aggregated: efficiency = 95.79%
90 dice aggregated: efficiency = 63.70%
91 dice aggregated: efficiency = 85.08%
92 dice aggregated: efficiency = 56.58%
93 dice aggregated: efficiency = 75.58%
94 dice aggregated: efficiency = 50.27%
95 dice aggregated: efficiency = 67.14%
96 dice aggregated: efficiency = 89.67%
97 dice aggregated: efficiency = 59.64%
98 dice aggregated: efficiency = 79.65%
99 dice aggregated: efficiency = 52.98%
100 dice aggregated: efficiency = 70.76%
- Greatest efficiency (99.79%) found at 53 dice.
- Next: Sort by efficiency.
53 dice (99.79%)
12 dice (98.59%)
65 dice (98.43%)
24 dice (97.26%)
77 dice (97.10%)
36 dice (95.95%)
89 dice (95.79%)
48 dice (94.66%)
60 dice (93.39%)
7 dice (93.15%)
19 dice (92.17%)
72 dice (92.13%)
31 dice (90.99%)
84 dice (90.89%)
43 dice (89.79%)
96 dice (89.67%)
55 dice (88.59%)
67 dice (87.41%)
14 dice (87.23%)
26 dice (86.24%)
79 dice (86.24%)
2 dice (85.97%)
38 dice (85.15%)
91 dice (85.08%)
50 dice (84.04%)
62 dice (82.93%)
9 dice (82.29%)
74 dice (81.82%)
21 dice (81.69%)
86 dice (80.73%)
33 dice (80.72%)
45 dice (79.70%)
98 dice (79.65%)
57 dice (78.66%)
69 dice (77.63%)
16 dice (77.27%)
81 dice (76.60%)
28 dice (76.50%)
4 dice (76.41%)
93 dice (75.58%)
40 dice (75.57%)
52 dice (74.61%)
64 dice (73.64%)
11 dice (72.86%)
76 dice (72.67%)
23 dice (72.44%)
88 dice (71.71%)
35 dice (71.63%)
100 dice (70.76%)
47 dice (70.75%)
59 dice (69.85%)
71 dice (68.94%)
18 dice (68.50%)
83 dice (68.04%)
6 dice (67.92%)
30 dice (67.87%)
95 dice (67.14%)
42 dice (67.08%)
54 dice (66.25%)
66 dice (65.40%)
13 dice (64.59%)
78 dice (64.55%)
25 dice (64.26%)
90 dice (63.70%)
37 dice (63.58%)
49 dice (62.82%)
61 dice (62.03%)
73 dice (61.23%)
20 dice (60.76%)
85 dice (60.43%)
8 dice (60.38%)
32 dice (60.23%)
97 dice (59.64%)
44 dice (59.55%)
56 dice (58.83%)
68 dice (58.08%)
80 dice (57.33%)
15 dice (57.29%)
27 dice (57.02%)
92 dice (56.58%)
39 dice (56.44%)
51 dice (55.78%)
63 dice (55.09%)
75 dice (54.39%)
22 dice (53.91%)
87 dice (53.68%)
10 dice (53.67%)
3 dice (53.49%)
34 dice (53.46%)
99 dice (52.98%)
46 dice (52.87%)
58 dice (52.24%)
70 dice (51.59%)
1 dice (51.58%)
82 dice (50.93%)
17 dice (50.85%)
29 dice (50.61%)
94 dice (50.27%)
41 dice (50.11%)
5 dice (48.91%)
Let's pick out the most pragmatic group sizes.
We take the most efficient group size, which is 53, and then always pick a smaller size as we go further down the list of sizes sorted by efficiency.
So: 53, 12, 7, 2, 1.
53 dice (99.79%)
12 dice (98.59%)
7 dice (93.15%)
2 dice (85.97%)
1 dice (51.58%)
I'll write an implementation that uses groups of 2 dice first. Later, I'll extend it to 7 and 12 dice, and see how inconvenient it is in practice to work with groups of those sizes.
Hm. Instead of refusing to use a final group of 11, could aggregate by 12 until we run out of groups of 12, then aggregate by 7, then by 2, then by 1.
So we can use multiple group sizes to aggregate rolls, in order of decreasing size.
Let's get some base conversion data.
6*6 = 36.
I'll go up to 50.
base_values.py
def main(): | |
print 'base10 | base6 | dice roll | base2' | |
for i in xrange(51): | |
value_base10 = i | |
value_base2 = bin(i) | |
# Remove initial '0b'. | |
value_base2 = value_base2[2:] | |
value_base6 = base10_to_base6(value_base10) | |
# Increment each dice roll character by 1. | |
dice_roll = ''.join([str(int(x)+1) for x in value_base6]) | |
output = "{a:<{b}} | {c:<{d}} | {e:<{f}} | {g:<{h}}".format( | |
a=value_base10, b=len('base10'), | |
c=value_base6, d=len('base6'), | |
e=dice_roll, f=len('dice_roll'), | |
g=value_base2, h=len('value_base2') | |
) | |
print output | |
def base10_to_base6(v): # v = 'value' | |
if v == 0: return '0' | |
r = '' # r = 'result' | |
while v > 0: | |
v, remainder = divmod(v, 6) | |
# Add new base6 digits to the front of r. | |
# - This is because on each iteration we're dividing by a higher power of 6. | |
r = str(remainder) + r | |
return r | |
if __name__ == '__main__': | |
main() |
stjohn@judgement:work$ python base_values.py
base10 | base6 | dice roll | base2
0 | 0 | 1 | 0
1 | 1 | 2 | 1
2 | 2 | 3 | 10
3 | 3 | 4 | 11
4 | 4 | 5 | 100
5 | 5 | 6 | 101
6 | 10 | 21 | 110
7 | 11 | 22 | 111
8 | 12 | 23 | 1000
9 | 13 | 24 | 1001
10 | 14 | 25 | 1010
11 | 15 | 26 | 1011
12 | 20 | 31 | 1100
13 | 21 | 32 | 1101
14 | 22 | 33 | 1110
15 | 23 | 34 | 1111
16 | 24 | 35 | 10000
17 | 25 | 36 | 10001
18 | 30 | 41 | 10010
19 | 31 | 42 | 10011
20 | 32 | 43 | 10100
21 | 33 | 44 | 10101
22 | 34 | 45 | 10110
23 | 35 | 46 | 10111
24 | 40 | 51 | 11000
25 | 41 | 52 | 11001
26 | 42 | 53 | 11010
27 | 43 | 54 | 11011
28 | 44 | 55 | 11100
29 | 45 | 56 | 11101
30 | 50 | 61 | 11110
31 | 51 | 62 | 11111
32 | 52 | 63 | 100000
33 | 53 | 64 | 100001
34 | 54 | 65 | 100010
35 | 55 | 66 | 100011
36 | 100 | 211 | 100100
37 | 101 | 212 | 100101
38 | 102 | 213 | 100110
39 | 103 | 214 | 100111
40 | 104 | 215 | 101000
41 | 105 | 216 | 101001
42 | 110 | 221 | 101010
43 | 111 | 222 | 101011
44 | 112 | 223 | 101100
45 | 113 | 224 | 101101
46 | 114 | 225 | 101110
47 | 115 | 226 | 101111
48 | 120 | 231 | 110000
49 | 121 | 232 | 110001
50 | 122 | 233 | 110010
Reading through the output, I can see that the base conversions have worked properly. I can reuse the conversion code in other programs.
Although: I'll need some code that converts dice rolls to the other bases.
convert_dice_rolls_to_other_bases.py
def main(): | |
print 'dice roll | base6 | base10 | base2' | |
for i in xrange(51): | |
value_base10 = i | |
value_base6 = base10_to_base6(value_base10) | |
# Increment each dice roll character by 1. | |
dice_roll = ''.join([str(int(x)+1) for x in value_base6]) | |
# Now, convert from the dice roll value back into the various bases. | |
value_base6_2 = ''.join([str(int(x)-1) for x in dice_roll]) | |
value_base10_2 = int(value_base6_2, 6) | |
value_base2_2 = bin(value_base10_2) | |
# Remove initial '0b'. | |
value_base2_2 = value_base2_2[2:] | |
output = "{e:<{f}} | {c:<{d}} | {a:<{b}} | {g:<{h}}".format( | |
a=value_base10_2, b=len('base10'), | |
c=value_base6_2, d=len('base6'), | |
e=dice_roll, f=len('dice_roll'), | |
g=value_base2_2, h=len('value_base2') | |
) | |
print output | |
def base10_to_base6(v): # v = 'value' | |
if v == 0: return '0' | |
r = '' # r = 'result' | |
while v > 0: | |
v, remainder = divmod(v, 6) | |
# Add new base6 digits to the front of r. | |
# - This is because on each iteration we're dividing by a higher power of 6. | |
r = str(remainder) + r | |
return r | |
if __name__ == '__main__': | |
main() |
stjohn@judgement:work$ python convert_dice_rolls_to_other_bases.py
dice roll | base6 | base10 | base2
1 | 0 | 0 | 0
2 | 1 | 1 | 1
3 | 2 | 2 | 10
4 | 3 | 3 | 11
5 | 4 | 4 | 100
6 | 5 | 5 | 101
21 | 10 | 6 | 110
22 | 11 | 7 | 111
23 | 12 | 8 | 1000
24 | 13 | 9 | 1001
25 | 14 | 10 | 1010
26 | 15 | 11 | 1011
31 | 20 | 12 | 1100
32 | 21 | 13 | 1101
33 | 22 | 14 | 1110
34 | 23 | 15 | 1111
35 | 24 | 16 | 10000
36 | 25 | 17 | 10001
41 | 30 | 18 | 10010
42 | 31 | 19 | 10011
43 | 32 | 20 | 10100
44 | 33 | 21 | 10101
45 | 34 | 22 | 10110
46 | 35 | 23 | 10111
51 | 40 | 24 | 11000
52 | 41 | 25 | 11001
53 | 42 | 26 | 11010
54 | 43 | 27 | 11011
55 | 44 | 28 | 11100
56 | 45 | 29 | 11101
61 | 50 | 30 | 11110
62 | 51 | 31 | 11111
63 | 52 | 32 | 100000
64 | 53 | 33 | 100001
65 | 54 | 34 | 100010
66 | 55 | 35 | 100011
211 | 100 | 36 | 100100
212 | 101 | 37 | 100101
213 | 102 | 38 | 100110
214 | 103 | 39 | 100111
215 | 104 | 40 | 101000
216 | 105 | 41 | 101001
221 | 110 | 42 | 101010
222 | 111 | 43 | 101011
223 | 112 | 44 | 101100
224 | 113 | 45 | 101101
225 | 114 | 46 | 101110
226 | 115 | 47 | 101111
231 | 120 | 48 | 110000
232 | 121 | 49 | 110001
233 | 122 | 50 | 110010
Now, for each of the group sizes that we chose earlier, let's find out the maximum dice roll value that we'll process. (Because, recall, we ignore the values that don't fit into the [0, nextLowestPowerOf2] domain.)
Group size list: 1,2,7,12,53
Also, find the number of bits (i.e. the length of one axis of the possibility space in base 2) for each of these group sizes.
max_dice_roll_values.py
def main(): | |
for i in [1,2,7,12,53]: | |
nDice = i | |
maxDiceRoll = '6' * nDice | |
# Convert this dice roll into a base10 value. | |
max_base6 = ''.join([str(int(x)-1) for x in maxDiceRoll]) | |
max_base10 = int(max_base6, 6) | |
# Find the maxUsableValue and various details about it. | |
maxUsableValue, exponent = nextLowestIntegerPowerOf2(max_base10) | |
maxUsableValue -= 1 # 0-index the domain. | |
max_base6 = base10_to_base6(maxUsableValue) | |
maxUsableDiceRoll = ''.join([str(int(x)+1) for x in max_base6]) | |
max_base2 = bin(maxUsableValue)[2:] | |
output = '\nNumber of dice: {}'.format(nDice) | |
output += '\n- maxUsableValue (base10): {}'.format(maxUsableValue) | |
output += '\n- maxUsableValue (base2): {}'.format(max_base2) | |
output += '\n- number of bits: {}'.format(exponent) | |
output += '\n- maxUsableValue (base6): {}'.format(max_base6) | |
output += '\n- maxUsableValue (dice roll): {}'.format(maxUsableDiceRoll) | |
print output | |
def nextLowestIntegerPowerOf2(n): | |
i = 0 | |
m = 0 | |
# increment power of 2 until we surpass the given value. | |
while m < n: | |
m = 2**i | |
i += 1 | |
# go back down one power. | |
i -= 2 | |
m = 2**i | |
return m, i | |
def base10_to_base6(v): # v = 'value' | |
if v == 0: return '0' | |
r = '' # r = 'result' | |
while v > 0: | |
v, remainder = divmod(v, 6) | |
# Add new base6 digits to the front of r. | |
# - This is because on each iteration we're dividing by a higher power of 6. | |
r = str(remainder) + r | |
return r | |
if __name__ == '__main__': | |
main() |
stjohn@judgement:work$ python max_dice_roll_values.py
Number of dice: 1
- maxUsableValue (base10): 3
- maxUsableValue (base2): 11
- number of bits: 2
- maxUsableValue (base6): 3
- maxUsableValue (dice roll): 4
Number of dice: 2
- maxUsableValue (base10): 31
- maxUsableValue (base2): 11111
- number of bits: 5
- maxUsableValue (base6): 51
- maxUsableValue (dice roll): 62
Number of dice: 7
- maxUsableValue (base10): 262143
- maxUsableValue (base2): 111111111111111111
- number of bits: 18
- maxUsableValue (base6): 5341343
- maxUsableValue (dice roll): 6452454
Number of dice: 12
- maxUsableValue (base10): 2147483647
- maxUsableValue (base2): 1111111111111111111111111111111
- number of bits: 31
- maxUsableValue (base6): 553032005531
- maxUsableValue (dice roll): 664143116642
Number of dice: 53
- maxUsableValue (base10): 174224571863520493293247799005065324265471
- maxUsableValue (base2): 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
- number of bits: 137
- maxUsableValue (base6): 55531440222042340011252522140533220014350441204441251
- maxUsableValue (dice roll): 66642551333153451122363633251644331125461552315552362
So, combining this with the earlier efficiency calculations:
- From 1 dice, we can extract 2 bits of entropy with 51.58% efficiency.
- From a group of 2 dice, we can extract 5 bits of entropy with 85.97% efficiency.
- From a group of 7 dice, we can extract 18 bits of entropy with 93.15% efficiency.
- From a group of 12 dice, we can extract 31 bits of entropy with 98.59% efficiency.
- From a group of 53 dice, we can extract 137 bits of entropy with 99.79% efficiency.
Note: In all of these cases, if the dice roll result is greater than the maxUsableValue, we will extract 0 bits of entropy with 0% efficiency.
[A lot of development work occurs here.]
I've written a tool that performs the entropy extraction from dice roll results. It's called:
entropy_from_dice.py
My Python version:
stjohn@judgement:work$ python --version
Python 2.7.12
Work machine:
- Name: Judgement
- Windows 10
- Windows Subsystem for Linux (WSL): Ubuntu 16.04
- I'm working in the WSL Ubuntu terminal.
The previous project Generating entropy with dice has an asset called dice_rolls.txt, which contains a set of 200 dice roll results. In the linked project's log, the entropy output extracted from these results is recorded:
15e7e195332b0aa8a684dc3be1f29dd04daa0a5434c11a8b9e6ad180df076ae47c62
I've tested that, with diceGroupSize=1, my new tool produces the same result.
I've also done this for:
- The project Using a transaction to validate a Bitcoin address, which has an asset called dice_rolls_2.txt.
- The project Storing bitcoin on an offline Raspberry Pi, which has assets called:
-- dice_rolls_3.txt.
-- dice_rolls_4.txt.
-- dice_rolls_5.txt.
-- dice_rolls_6.txt.
-- dice_rolls_7.txt.
-- Exception: There was a mistake in this project, in which the entropy for dice_rolls_6.txt was extracted from dice_rolls_5.txt instead, so in this case my result didn't match the result in the project.
Entropy values:
- dice_rolls.txt:
15e7e195332b0aa8a684dc3be1f29dd04daa0a5434c11a8b9e6ad180df076ae47c62
- dice_rolls_2.txt:
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9fd83
- dice_rolls_3.txt: c592e1dad5e9871fdeffb551b4544b0a1cf0378c6371d7a397ff5faf04c934ca30
- dice_rolls_4.txt: 91fbb8a38082b09e6ed984fda95866dfab95dc66b0cb6b5133dd87d07315364e279d9258
- dice_rolls_5.txt: 644dc86dd0cb4518d9d7f68db2d6e9455e34ceeb32c897df2616418c4c9bb04006
- dice_rolls_6.txt:
[accidentally the same as the entropy for dice_rolls_5.txt]
- my output:
340cd7ed66c4fee0a7570d28eafef25f60db58bcbf78a4dc60c6e617a42cd7e7eec3
- dice_rolls_7.txt: f78c5ebf0efac89d60aedb38c30906ad5b22ae522a3e29819248c21d59e8397f9b315219aa
15e7e195332b0aa8a684dc3be1f29dd04daa0a5434c11a8b9e6ad180df076ae47c62
- dice_rolls_2.txt:
a26e15954d2dafcee70eeaaa084eab8a4c1a30b0f71a42be4d8da20123bff121258f67ebadfc5c460f0f939ba600d49c3eef672241d21f164897d19fb6fe64c9fd83
- dice_rolls_3.txt: c592e1dad5e9871fdeffb551b4544b0a1cf0378c6371d7a397ff5faf04c934ca30
- dice_rolls_4.txt: 91fbb8a38082b09e6ed984fda95866dfab95dc66b0cb6b5133dd87d07315364e279d9258
- dice_rolls_5.txt: 644dc86dd0cb4518d9d7f68db2d6e9455e34ceeb32c897df2616418c4c9bb04006
- dice_rolls_6.txt:
[accidentally the same as the entropy for dice_rolls_5.txt]
- my output:
340cd7ed66c4fee0a7570d28eafef25f60db58bcbf78a4dc60c6e617a42cd7e7eec3
- dice_rolls_7.txt: f78c5ebf0efac89d60aedb38c30906ad5b22ae522a3e29819248c21d59e8397f9b315219aa
I've also written a pytest test suite for the new tool, called:
test_entropy_from_dice.py
I installed pytest during this project. Some details:
stjohn@judgement:work$ pip --version
pip 20.2.4 from /home/stjohn/.local/lib/python2.7/site-packages/pip (python 2.7)
stjohn@judgement:work$ pip install pytest
[...]
Installing collected packages: atomicwrites, contextlib2, six, scandir, pathlib2, zipp, configparser, importlib-metadata, pluggy, pyparsing, packaging, more-itertools, py, attrs, backports.functools-lru-cache, wcwidth, funcsigs, pytest
Successfully installed atomicwrites-1.4.0 attrs-20.2.0 backports.functools-lru-cache-1.6.1 configparser-4.0.2 contextlib2-0.6.0.post1 funcsigs-1.0.2 importlib-metadata-2.0.0 more-itertools-5.0.0 packaging-20.4 pathlib2-2.3.5 pluggy-0.13.1 py-1.9.0 pyparsing-2.4.7 pytest-4.6.11 scandir-1.10.0 six-1.15.0 wcwidth-0.2.5 zipp-1.2.0
Let's record some examples of using
entropy_from_dice.py
Note: If it uses a large groupSize, and the dice roll results value is greater than the maxUsableValue, it tries again with a smaller groupSize.
Example: The diceRollString '64', when processed with groupSize=2, is greater than the maxUsableValue of '62', so the result is empty (''). Try again with groupSize=1. For groupSize=1, '6' is greater than than the maxUsableValue of '4', but '4' is converted to '11' in binary. So: We get '11' instead of nothing.
stjohn@judgement:work$ python entropy_from_dice.py -h
usage: entropy_from_dice.py [-h] [-f DICEROLLSFILEPATH] [-r DICEROLLSSTRING]
[-n ENTROPYAMOUNT]
[-g DICEGROUPSIZES [DICEGROUPSIZES ...]]
[-o {bits,bytes}] [-l {debug,info,warning,error}]
[-d] [-e] [-k]
Extract entropy (as bits or bytes) from dice roll values.
optional arguments:
-h, --help show this help message and exit
-f DICEROLLSFILEPATH, --diceRollsFilePath DICEROLLSFILEPATH
path to file containing the dice rolls (default:
'dice_rolls.txt').
-r DICEROLLSSTRING, --diceRollsString DICEROLLSSTRING
sequence of dice rolls (e.g. '451236'). This overrides
--diceRollsFilePath.
-n ENTROPYAMOUNT, --entropyAmount ENTROPYAMOUNT
desired number of entropy bytes (default: '32'). If
--outputType is set to 'bits', then this value will be
treated as the desired number of entropy bits.
-g DICEGROUPSIZES [DICEGROUPSIZES ...], --diceGroupSizes DICEGROUPSIZES [DICEGROUPSIZES ...]
ordered list of integer sizes to use when aggregating
the dice rolls into groups (default: '[53, 12, 7, 2,
1]'). This argument is used for testing.
-o {bits,bytes}, --outputType {bits,bytes}
Choose format of output (default: 'bytes').
-l {debug,info,warning,error}, --logLevel {debug,info,warning,error}
Choose logging level (default: 'error').
-d, --debug Sets logLevel to 'debug'. This overrides --logLevel.
-e, --estimate Estimate required number of dice rolls for the desired
amount of entropy.
-k, --keepExtraEntropy
If there is extra entropy than the desired amount,
don't remove it.
Supplementary Notes:
- Dice roll values must consist only of characters in the list "123456".
- Whitespace characters (newlines, tabs, and spaces) are permitted in the dice roll results file, and are ignored during processing.
- Dice rolls are aggregated into groups to more efficiently extract entropy.
- Expected entropy per dice roll = log_2(6) ~= 2.5850 bits.
- Bits per byte = 8.
- Bits per 32 bytes = 32 * 8 = 256.
stjohn@judgement:work$ python entropy_from_dice.py --diceRollsString 1 --outputType=bits --entropyAmount=2
00
[same, but in shorthand]
stjohn@judgement:work$ python entropy_from_dice.py -r 1 --outputType=bits -n 2
00
stjohn@judgement:work$ python entropy_from_dice.py --outputType=bits -n 2 -r 2
01
stjohn@judgement:work$ python entropy_from_dice.py --outputType=bits -n 2 -r 3
10
stjohn@judgement:work$ python entropy_from_dice.py --outputType=bits -n 2 -r 4
11
stjohn@judgement:work$ python entropy_from_dice.py --outputType=bits -n 1 -r 5
Traceback (most recent call last):
File "entropy_from_dice.py", line 534, in <module>
main()
File "entropy_from_dice.py", line 136, in main
entropy = processDiceRolls(args)
File "entropy_from_dice.py", line 210, in processDiceRolls
stop(report)
File "entropy_from_dice.py", line 528, in stop
raise Exception(msg)
Exception: Problem: Desired amount of entropy = 1 bits, but only 0 bits were produced.
Estimated new dice rolls required: 2
stjohn@judgement:work$ python entropy_from_dice.py --outputType=bits -n 0 -r 5 --keepExtraEntropy
stjohn@judgement:work$ python entropy_from_dice.py --outputType=bits -n=0 --keepExtraEntropy -r 561234
111010000101111
stjohn@judgement:work$ python entropy_from_dice.py --outputType=bits -n=0 --keepExtraEntropy -r 561234 --diceGroupSizes 1
00011011
stjohn@judgement:work$ python entropy_from_dice.py --outputType=bits -n=0 --keepExtraEntropy -r 561234 --diceGroupSizes 2
111010000101111
stjohn@judgement:work$ python entropy_from_dice.py -n 0 -r 561234 -k --diceGroupSizes 2 --outputType=bytes
e8
stjohn@judgement:work$ python entropy_from_dice.py -n 0 -r 561234 -k
e8
[demonstrate use of --debug option]
stjohn@judgement:work$ python entropy_from_dice.py -r 12341 -k -n 2
Traceback (most recent call last):
File "entropy_from_dice.py", line 538, in <module>
main()
File "entropy_from_dice.py", line 136, in main
entropy = processDiceRolls(args)
File "entropy_from_dice.py", line 240, in processDiceRolls
stop(report)
File "entropy_from_dice.py", line 532, in stop
raise Exception(msg)
Exception: Problem: Desired amount of entropy = 2 bytes, but only 1 bytes were produced.
Estimated new dice rolls required: 4
stjohn@judgement:work$ python entropy_from_dice.py -r 12341 -k -n 2 --debug
INFO [189: processDiceRolls] diceRollsString: 12341
INFO [191: processDiceRolls] Number of dice rolls: 5
DEBUG [193: processDiceRolls] Processing dice rolls:
DEBUG [331: convertDiceRollsToBitsUsingLargestPossibleGroups] diceRollsString: 12341
DEBUG [332: convertDiceRollsToBitsUsingLargestPossibleGroups] Converting diceRollsString to bits using groupSizeList [53, 12, 7, 2, 1]
DEBUG [339: convertDiceRollsToBitsUsingLargestPossibleGroups] Can't use groupSize=53 - it's larger than the remaining number of dice rolls (5).
DEBUG [339: convertDiceRollsToBitsUsingLargestPossibleGroups] Can't use groupSize=12 - it's larger than the remaining number of dice rolls (5).
DEBUG [339: convertDiceRollsToBitsUsingLargestPossibleGroups] Can't use groupSize=7 - it's larger than the remaining number of dice rolls (5).
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 5. Using groupSize=2. Converting dice rolls section '12' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 12
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=2, groupSize=2, a=6**groupSize=6**2=36
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=36. Result: 2**5 = 32.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=36, nextLowestIntegerPowerOf2 = 32 (=2**5)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 32-1 = 31
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 62
DEBUG [423: convertDiceRollsToBits] - rolls section '12' ('01' in base6), value=1, bits='00001'
DEBUG [424: convertDiceRollsToBits] 0 groups of 2 dice roll values ignored because they were greater than the maxDiceRollValue 62 (51 in base6, 31 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '00001'
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 3. Using groupSize=2. Converting dice rolls section '34' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 34
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=2, groupSize=2, a=6**groupSize=6**2=36
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=36. Result: 2**5 = 32.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=36, nextLowestIntegerPowerOf2 = 32 (=2**5)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 32-1 = 31
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 62
DEBUG [423: convertDiceRollsToBits] - rolls section '34' ('23' in base6), value=15, bits='01111'
DEBUG [424: convertDiceRollsToBits] 0 groups of 2 dice roll values ignored because they were greater than the maxDiceRollValue 62 (51 in base6, 31 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '01111'
DEBUG [339: convertDiceRollsToBitsUsingLargestPossibleGroups] Can't use groupSize=2 - it's larger than the remaining number of dice rolls (1).
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 1. Using groupSize=1. Converting dice rolls section '1' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 1
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=1, groupSize=1, a=6**groupSize=6**1=6
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=6. Result: 2**2 = 4.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=6, nextLowestIntegerPowerOf2 = 4 (=2**2)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 4-1 = 3
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 4
DEBUG [423: convertDiceRollsToBits] - rolls section '1' ('0' in base6), value=0, bits='00'
DEBUG [424: convertDiceRollsToBits] 0 groups of 1 dice roll values ignored because they were greater than the maxDiceRollValue 4 (3 in base6, 3 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '00'
INFO [195: processDiceRolls] Extracted bits: '000010111100'
INFO [197: processDiceRolls] Number of extracted bits: 12
INFO [200: processDiceRolls] Extraction efficiency: 92.84%
DEBUG [216: processDiceRolls] Converting extracted bits to bytes:
DEBUG [221: processDiceRolls] Partial byte: 4 bits not used.
INFO [229: processDiceRolls] Extracted bytes (base 16): '0b'
INFO [231: processDiceRolls] Number of extracted bytes: 1
INFO [234: processDiceRolls] Extraction efficiency: 61.90%
DEBUG [237: processDiceRolls] Problem: Desired amount of entropy = 2 bytes, but only 1 bytes were produced.
DEBUG [263: estimateRequiredDiceRolls] Estimating required dice rolls for 4 bits of entropy:
DEBUG [288: estimateRequiredDiceRolls] dice group size 53 (137 bits), 0 groups, 0 bits handled, 4 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 12 (31 bits), 0 groups, 0 bits handled, 4 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 7 (18 bits), 0 groups, 0 bits handled, 4 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 2 (5 bits), 0 groups, 0 bits handled, 4 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 1 (2 bits), 2 groups, 4 bits handled, 0 bits to go.
DEBUG [241: processDiceRolls] Normalised efficiency: 0.5158
Traceback (most recent call last):
File "entropy_from_dice.py", line 541, in <module>
main()
File "entropy_from_dice.py", line 136, in main
entropy = processDiceRolls(args)
File "entropy_from_dice.py", line 243, in processDiceRolls
stop(report)
File "entropy_from_dice.py", line 535, in stop
raise Exception(msg)
Exception: Problem: Desired amount of entropy = 2 bytes, but only 1 bytes were produced.
Estimated new dice rolls required: 4
[demonstrate use of the estimation capability]
stjohn@judgement:work$ python entropy_from_dice.py -n 0 --estimate
0
stjohn@judgement:work$ python entropy_from_dice.py -n 0 --estimate --debug
INFO [126: main] Estimated dice rolls required for 0 bits of entropy (with efficiency=1.0000):
0
stjohn@judgement:work$ python entropy_from_dice.py -n 1 --estimate --debug
DEBUG [261: estimateRequiredDiceRolls] Estimating required dice rolls for 8 bits of entropy:
DEBUG [286: estimateRequiredDiceRolls] dice group size 53 (137 bits), 0 groups, 0 bits handled, 8 bits to go.
DEBUG [286: estimateRequiredDiceRolls] dice group size 12 (31 bits), 0 groups, 0 bits handled, 8 bits to go.
DEBUG [286: estimateRequiredDiceRolls] dice group size 7 (18 bits), 0 groups, 0 bits handled, 8 bits to go.
DEBUG [286: estimateRequiredDiceRolls] dice group size 2 (5 bits), 1 groups, 5 bits handled, 3 bits to go.
DEBUG [286: estimateRequiredDiceRolls] dice group size 2 (5 bits), 0 groups, 0 bits handled, 3 bits to go.
DEBUG [286: estimateRequiredDiceRolls] dice group size 1 (2 bits), 1 groups, 2 bits handled, 1 bits to go.
DEBUG [277: estimateRequiredDiceRolls] No smaller group sizes to use. Add 1 more group of the current size.
DEBUG [286: estimateRequiredDiceRolls] dice group size 1 (2 bits), 1 groups, 1 bits handled, 0 bits to go.
INFO [126: main] Estimated dice rolls required for 8 bits of entropy (with efficiency=0.6632):
6
stjohn@judgement:work$ python entropy_from_dice.py -n 1 --estimate
6
stjohn@judgement:work$ python entropy_from_dice.py -n 1 --estimate --outputType=bits
2
stjohn@judgement:work$ python entropy_from_dice.py -n 32 --estimate
103
stjohn@judgement:work$ python entropy_from_dice.py -n 32 --estimate --diceGroupSizes 1
193
stjohn@judgement:work$ python entropy_from_dice.py -n 64 --estimate
202
stjohn@judgement:work$ python entropy_from_dice.py -n 64 --estimate --diceGroupSizes 1
385
stjohn@judgement:work$ python entropy_from_dice.py -n 64 --estimate --diceGroupSizes 2 1
232
stjohn@judgement:work$ python entropy_from_dice.py -n 64 --estimate --diceGroupSizes 5 2 1
Traceback (most recent call last):
File "entropy_from_dice.py", line 541, in <module>
main()
File "entropy_from_dice.py", line 112, in main
raise ValueError(msg)
ValueError: groupSize 5 is not in list of permitted dice group sizes: [53, 12, 7, 2, 1]
stjohn@judgement:work$ python entropy_from_dice.py -n 64 --estimate --diceGroupSizes 7 2 1
215
stjohn@judgement:work$ python entropy_from_dice.py -n 64 --estimate --diceGroupSizes 12 7 2 1
203
stjohn@judgement:work$ python entropy_from_dice.py -n 64 --estimate --diceGroupSizes 53 12 7 2 1
202
stjohn@judgement:work$ python entropy_from_dice.py -n 20 --estimate
63
stjohn@judgement:work$ python entropy_from_dice.py -n 32 --estimate --debug
DEBUG [263: estimateRequiredDiceRolls] Estimating required dice rolls for 256 bits of entropy:
DEBUG [288: estimateRequiredDiceRolls] dice group size 53 (137 bits), 1 groups, 137 bits handled, 119 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 53 (137 bits), 0 groups, 0 bits handled, 119 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 12 (31 bits), 3 groups, 93 bits handled, 26 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 12 (31 bits), 0 groups, 0 bits handled, 26 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 7 (18 bits), 1 groups, 18 bits handled, 8 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 7 (18 bits), 0 groups, 0 bits handled, 8 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 2 (5 bits), 1 groups, 5 bits handled, 3 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 2 (5 bits), 0 groups, 0 bits handled, 3 bits to go.
DEBUG [288: estimateRequiredDiceRolls] dice group size 1 (2 bits), 1 groups, 2 bits handled, 1 bits to go.
DEBUG [279: estimateRequiredDiceRolls] No smaller group sizes to use. Add 1 more group of the current size.
DEBUG [288: estimateRequiredDiceRolls] dice group size 1 (2 bits), 1 groups, 1 bits handled, 0 bits to go.
INFO [126: main] Estimated dice rolls required for 256 bits of entropy (with efficiency=0.9716):
103
stjohn@judgement:work$ cat dice_rolls.txt
12224
32455
43512
63552
22164
14133
41133
33313
32336
61214
24161
64534
43126
65454
13563
25466
26462
61651
25156
45236
33631
13365
22211
42146
15162
61523
56355
65313
43624
32555
33364
26155
52361
11545
66244
15162
64235
63343
21244
12313
stjohn@judgement:work$ python entropy_from_dice.py -f dice_rolls.txt
089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8
[Note: By default, the tool looks for a file named 'dice_rolls.txt' in its local directory.]
stjohn@judgement:work$ python entropy_from_dice.py
089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8
stjohn@judgement:work$ python entropy_from_dice.py -f dice_rolls.txt --keepExtraEntropy
089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8c0a81aa21f5fe0948993141fff83cfad37ff44197315da78eb8277bce94fc90b
stjohn@judgement:work$ python entropy_from_dice.py -f dice_rolls.txt -n 0
stjohn@judgement:work$ python entropy_from_dice.py -f dice_rolls.txt -n 1
08
stjohn@judgement:work$ python entropy_from_dice.py -f dice_rolls.txt -n 2
089f
stjohn@judgement:work$ python entropy_from_dice.py -f dice_rolls.txt -n 5
089f7ffa20
stjohn@judgement:work$ python entropy_from_dice.py -f dice_rolls.txt --logLevel=info
- diceRollsString: 12224324554351263552221641413341133333133233661214241616453443126654541356325466264626165125156452363363113365222114214615162615235635565313436243255533364261555236111545662441516264235633432124412313
- Number of dice rolls: 200
- Extracted bits: '000010001001111101111111111110100010000001110110101011101011001101101101000010011010000011010000100101010010100101100001000101101100111101101110001110101001001110100001111111001100111000001100101000110001111001110110001010001100010000110000000000011101100011000000101010000001101010100010000111110101111111100000100101001000100110010011000101000001111111111111100000111100111110101101001101111111111101000100000110010111001100010101110110100111100011101011100000100111011110111100111010010100111111001001000010110010'
- Number of extracted bits: 516
- Extraction efficiency: 99.81%
- Extracted bytes (base 16): '089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8c0a81aa21f5fe0948993141fff83cfad37ff44197315da78eb8277bce94fc90b'
- Number of extracted bytes: 64
- Extraction efficiency: 99.03%
- Entropy truncated down from 128 bytes to 32 bytes.
089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8
stjohn@judgement:work$ python entropy_from_dice.py -n 64 -e --logLevel=info
- Estimated dice rolls required for 512 bits of entropy (with efficiency=0.9870):
202
stjohn@judgement:work$ python entropy_from_dice.py -f dice_rolls.txt --debug
INFO [189: processDiceRolls] diceRollsString: 12224324554351263552221641413341133333133233661214241616453443126654541356325466264626165125156452363363113365222114214615162615235635565313436243255533364261555236111545662441516264235633432124412313
INFO [191: processDiceRolls] Number of dice rolls: 200
DEBUG [193: processDiceRolls] Processing dice rolls:
DEBUG [331: convertDiceRollsToBitsUsingLargestPossibleGroups] diceRollsString: 12224324554351263552221641413341133333133233661214241616453443126654541356325466264626165125156452363363113365222114214615162615235635565313436243255533364261555236111545662441516264235633432124412313
DEBUG [332: convertDiceRollsToBitsUsingLargestPossibleGroups] Converting diceRollsString to bits using groupSizeList [53, 12, 7, 2, 1]
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 200. Using groupSize=53. Converting dice rolls section '12224324554351263552221641413341133333133233661214241' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 12224324554351263552221641413341133333133233661214241
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=53, groupSize=53, a=6**groupSize=6**53=174588755932389037098918153698611839369216
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=174588755932389037098918153698611839369216. Result: 2**137 = 174224571863520493293247799005065324265472.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=174588755932389037098918153698611839369216, nextLowestIntegerPowerOf2 = 174224571863520493293247799005065324265472 (=2**137)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 174224571863520493293247799005065324265472-1 = 174224571863520493293247799005065324265471
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 66642551333153451122363633251644331125461552315552362
DEBUG [423: convertDiceRollsToBits] - rolls section '12224324554351263552221641413341133333133233661214241' ('01113213443240152441110530302230022222022122550103130' in base6), value=5868541363145551599096222659694389505438, bits='00001000100111110111111111111010001000000111011010101110101100110110110100001001101000001101000010010101001010010110000100010110110011110'
DEBUG [424: convertDiceRollsToBits] 0 groups of 53 dice roll values ignored because they were greater than the maxDiceRollValue 66642551333153451122363633251644331125461552315552362 (55531440222042340011252522140533220014350441204441251 in base6, 174224571863520493293247799005065324265471 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '00001000100111110111111111111010001000000111011010101110101100110110110100001001101000001101000010010101001010010110000100010110110011110'
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 147. Using groupSize=53. Converting dice rolls section '61645344312665454135632546626462616512515645236336311' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 61645344312665454135632546626462616512515645236336311
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=53, groupSize=53, a=6**groupSize=6**53=174588755932389037098918153698611839369216
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=174588755932389037098918153698611839369216. Result: 2**137 = 174224571863520493293247799005065324265472.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=174588755932389037098918153698611839369216, nextLowestIntegerPowerOf2 = 174224571863520493293247799005065324265472 (=2**137)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 174224571863520493293247799005065324265472-1 = 174224571863520493293247799005065324265471
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 66642551333153451122363633251644331125461552315552362
DEBUG [423: convertDiceRollsToBits] - rolls section '61645344312665454135632546626462616512515645236336311' ('50534233201554343024521435515351505401404534125225200' in base6), value=150035688552776692208861034798034245059232, bits='11011100011101010010011101000011111110011001110000011001010001100011110011101100010100011000100001100000000000111011000110000001010100000'
DEBUG [424: convertDiceRollsToBits] 0 groups of 53 dice roll values ignored because they were greater than the maxDiceRollValue 66642551333153451122363633251644331125461552315552362 (55531440222042340011252522140533220014350441204441251 in base6, 174224571863520493293247799005065324265471 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '11011100011101010010011101000011111110011001110000011001010001100011110011101100010100011000100001100000000000111011000110000001010100000'
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 94. Using groupSize=53. Converting dice rolls section '33652221142146151626152356355653134362432555333642615' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 33652221142146151626152356355653134362432555333642615
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=53, groupSize=53, a=6**groupSize=6**53=174588755932389037098918153698611839369216
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=174588755932389037098918153698611839369216. Result: 2**137 = 174224571863520493293247799005065324265472.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=174588755932389037098918153698611839369216, nextLowestIntegerPowerOf2 = 174224571863520493293247799005065324265472 (=2**137)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 174224571863520493293247799005065324265472-1 = 174224571863520493293247799005065324265471
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 66642551333153451122363633251644331125461552315552362
DEBUG [423: convertDiceRollsToBits] - rolls section '33652221142146151626152356355653134362432555333642615' ('22541110031035040515041245244542023251321444222531504' in base6), value=72502715048689294800548021779702212459040, bits='01101010100010000111110101111111100000100101001000100110010011000101000001111111111111100000111100111110101101001101111111111101000100000'
DEBUG [424: convertDiceRollsToBits] 0 groups of 53 dice roll values ignored because they were greater than the maxDiceRollValue 66642551333153451122363633251644331125461552315552362 (55531440222042340011252522140533220014350441204441251 in base6, 174224571863520493293247799005065324265471 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '01101010100010000111110101111111100000100101001000100110010011000101000001111111111111100000111100111110101101001101111111111101000100000'
DEBUG [339: convertDiceRollsToBitsUsingLargestPossibleGroups] Can't use groupSize=53 - it's larger than the remaining number of dice rolls (41).
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 41. Using groupSize=12. Converting dice rolls section '552361115456' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 552361115456
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=12, groupSize=12, a=6**groupSize=6**12=2176782336
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=2176782336. Result: 2**31 = 2147483648.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=2176782336, nextLowestIntegerPowerOf2 = 2147483648 (=2**31)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 2147483648-1 = 2147483647
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 664143116642
DEBUG [423: convertDiceRollsToBits] - rolls section '552361115456' ('441250004345' in base6), value=1707890537, bits='1100101110011000101011101101001'
DEBUG [424: convertDiceRollsToBits] 0 groups of 12 dice roll values ignored because they were greater than the maxDiceRollValue 664143116642 (553032005531 in base6, 2147483647 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '1100101110011000101011101101001'
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 29. Using groupSize=12. Converting dice rolls section '624415162642' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 624415162642
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=12, groupSize=12, a=6**groupSize=6**12=2176782336
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=2176782336. Result: 2**31 = 2147483648.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=2176782336, nextLowestIntegerPowerOf2 = 2147483648 (=2**31)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 2147483648-1 = 2147483647
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 664143116642
DEBUG [423: convertDiceRollsToBits] - rolls section '624415162642' ('513304051531' in base6), value=1909916911, bits='1110001110101110000010011101111'
DEBUG [424: convertDiceRollsToBits] 0 groups of 12 dice roll values ignored because they were greater than the maxDiceRollValue 664143116642 (553032005531 in base6, 2147483647 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '1110001110101110000010011101111'
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 17. Using groupSize=12. Converting dice rolls section '356334321244' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 356334321244
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=12, groupSize=12, a=6**groupSize=6**12=2176782336
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=2176782336. Result: 2**31 = 2147483648.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=2176782336, nextLowestIntegerPowerOf2 = 2147483648 (=2**31)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 2147483648-1 = 2147483647
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 664143116642
DEBUG [423: convertDiceRollsToBits] - rolls section '356334321244' ('245223210133' in base6), value=1021923273, bits='0111100111010010100111111001001'
DEBUG [424: convertDiceRollsToBits] 0 groups of 12 dice roll values ignored because they were greater than the maxDiceRollValue 664143116642 (553032005531 in base6, 2147483647 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '0111100111010010100111111001001'
DEBUG [339: convertDiceRollsToBitsUsingLargestPossibleGroups] Can't use groupSize=12 - it's larger than the remaining number of dice rolls (5).
DEBUG [339: convertDiceRollsToBitsUsingLargestPossibleGroups] Can't use groupSize=7 - it's larger than the remaining number of dice rolls (5).
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 5. Using groupSize=2. Converting dice rolls section '12' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 12
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=2, groupSize=2, a=6**groupSize=6**2=36
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=36. Result: 2**5 = 32.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=36, nextLowestIntegerPowerOf2 = 32 (=2**5)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 32-1 = 31
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 62
DEBUG [423: convertDiceRollsToBits] - rolls section '12' ('01' in base6), value=1, bits='00001'
DEBUG [424: convertDiceRollsToBits] 0 groups of 2 dice roll values ignored because they were greater than the maxDiceRollValue 62 (51 in base6, 31 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '00001'
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 3. Using groupSize=2. Converting dice rolls section '31' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 31
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=2, groupSize=2, a=6**groupSize=6**2=36
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=36. Result: 2**5 = 32.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=36, nextLowestIntegerPowerOf2 = 32 (=2**5)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 32-1 = 31
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 62
DEBUG [423: convertDiceRollsToBits] - rolls section '31' ('20' in base6), value=12, bits='01100'
DEBUG [424: convertDiceRollsToBits] 0 groups of 2 dice roll values ignored because they were greater than the maxDiceRollValue 62 (51 in base6, 31 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '01100'
DEBUG [339: convertDiceRollsToBitsUsingLargestPossibleGroups] Can't use groupSize=2 - it's larger than the remaining number of dice rolls (1).
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 1. Using groupSize=1. Converting dice rolls section '3' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 3
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=1, groupSize=1, a=6**groupSize=6**1=6
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=6. Result: 2**2 = 4.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=6, nextLowestIntegerPowerOf2 = 4 (=2**2)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 4-1 = 3
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 4
DEBUG [423: convertDiceRollsToBits] - rolls section '3' ('2' in base6), value=2, bits='10'
DEBUG [424: convertDiceRollsToBits] 0 groups of 1 dice roll values ignored because they were greater than the maxDiceRollValue 4 (3 in base6, 3 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '10'
INFO [195: processDiceRolls] Extracted bits: '000010001001111101111111111110100010000001110110101011101011001101101101000010011010000011010000100101010010100101100001000101101100111101101110001110101001001110100001111111001100111000001100101000110001111001110110001010001100010000110000000000011101100011000000101010000001101010100010000111110101111111100000100101001000100110010011000101000001111111111111100000111100111110101101001101111111111101000100000110010111001100010101110110100111100011101011100000100111011110111100111010010100111111001001000010110010'
INFO [197: processDiceRolls] Number of extracted bits: 516
INFO [200: processDiceRolls] Extraction efficiency: 99.81%
DEBUG [216: processDiceRolls] Converting extracted bits to bytes:
DEBUG [221: processDiceRolls] Partial byte: 4 bits not used.
INFO [229: processDiceRolls] Extracted bytes (base 16): '089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8c0a81aa21f5fe0948993141fff83cfad37ff44197315da78eb8277bce94fc90b'
INFO [231: processDiceRolls] Number of extracted bytes: 64
INFO [234: processDiceRolls] Extraction efficiency: 99.03%
INFO [147: main] Entropy truncated down from 128 bytes to 32 bytes.
089f7ffa2076aeb36d09a0d095296116cf6e3a93a1fcce0ca31e7628c43001d8
And some examples of testing the tool.
stjohn@judgement:work$ pytest test_entropy_from_dice.py
============================= test session starts =============================
platform linux2 -- Python 2.7.12, pytest-4.6.11, py-1.9.0, pluggy-0.13.1
rootdir: /mnt/c/Users/User/Desktop/stuff/articles/generating_entropy_from_dice_2/work
collected 90 items
test_entropy_from_dice.py ............................................. [ 50%]
............................................. [100%]
========================== 90 passed in 0.26 seconds ==========================
stjohn@judgement:work$ pytest test_entropy_from_dice.py -q
....................................................................... [ 78%]
................... [100%]
90 passed in 0.24 seconds
stjohn@judgement:work$ pytest test_entropy_from_dice.py::test_f1_one -s
============================= test session starts =============================
platform linux2 -- Python 2.7.12, pytest-4.6.11, py-1.9.0, pluggy-0.13.1
rootdir: /mnt/c/Users/User/Desktop/stuff/articles/generating_entropy_from_dice_2/work
collected 1 item
test_entropy_from_dice.py .
========================== 1 passed in 0.05 seconds ===========================
stjohn@judgement:work$ pytest test_entropy_from_dice.py::test_f1_one -q
. [100%]
1 passed in 0.05 seconds
stjohn@judgement:work$ pytest -o log_cli=true --log-cli-level=DEBUG --log-format="%(levelname)s [%(lineno)s: %(funcName)s] %(message)s" test_entropy_from_dice.py::test_f1_one
============================= test session starts =============================
platform linux2 -- Python 2.7.12, pytest-4.6.11, py-1.9.0, pluggy-0.13.1
rootdir: /mnt/c/Users/User/Desktop/stuff/articles/generating_entropy_from_dice_2/work
collected 1 item
test_entropy_from_dice.py::test_f1_one
-------------------------------- live log call --------------------------------
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=1. Result: 2**0 = 1.
PASSED [100%]
========================== 1 passed in 0.06 seconds ===========================
stjohn@judgement:work$ pytest -o log_cli=true --log-cli-level=DEBUG --log-format="%(levelname)s [%(lineno)s: %(funcName)s] %(message)s" test_entropy_from_dice.py::test_f6_6262_to_bytes
============================= test session starts =============================
platform linux2 -- Python 2.7.12, pytest-4.6.11, py-1.9.0, pluggy-0.13.1
rootdir: /mnt/c/Users/User/Desktop/stuff/articles/generating_entropy_from_dice_2/work
collected 1 item
test_entropy_from_dice.py::test_f6_6262_to_bytes
-------------------------------- live log call --------------------------------
INFO [189: processDiceRolls] diceRollsString: 6262
INFO [191: processDiceRolls] Number of dice rolls: 4
DEBUG [193: processDiceRolls] Processing dice rolls:
DEBUG [331: convertDiceRollsToBitsUsingLargestPossibleGroups] diceRollsString: 6262
DEBUG [332: convertDiceRollsToBitsUsingLargestPossibleGroups] Converting diceRollsString to bits using groupSizeList [2, 1]
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 4. Using groupSize=2. Converting dice rolls section '62' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 62
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=2, groupSize=2, a=6**groupSize=6**2=36
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=36. Result: 2**5 = 32.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=36, nextLowestIntegerPowerOf2 = 32 (=2**5)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 32-1 = 31
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 62
DEBUG [423: convertDiceRollsToBits] - rolls section '62' ('51' in base6), value=31, bits='11111'
DEBUG [424: convertDiceRollsToBits] 0 groups of 2 dice roll values ignored because they were greater than the maxDiceRollValue 62 (51 in base6, 31 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '11111'
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 2. Using groupSize=2. Converting dice rolls section '62' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 62
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=2, groupSize=2, a=6**groupSize=6**2=36
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=36. Result: 2**5 = 32.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=36, nextLowestIntegerPowerOf2 = 32 (=2**5)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 32-1 = 31
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 62
DEBUG [423: convertDiceRollsToBits] - rolls section '62' ('51' in base6), value=31, bits='11111'
DEBUG [424: convertDiceRollsToBits] 0 groups of 2 dice roll values ignored because they were greater than the maxDiceRollValue 62 (51 in base6, 31 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '11111'
INFO [195: processDiceRolls] Extracted bits: '1111111111'
INFO [197: processDiceRolls] Number of extracted bits: 10
INFO [200: processDiceRolls] Extraction efficiency: 96.71%
DEBUG [216: processDiceRolls] Converting extracted bits to bytes:
DEBUG [221: processDiceRolls] Partial byte: 2 bits not used.
INFO [229: processDiceRolls] Extracted bytes (base 16): 'ff'
INFO [231: processDiceRolls] Number of extracted bytes: 1
INFO [234: processDiceRolls] Extraction efficiency: 77.37%
PASSED [100%]
========================== 1 passed in 0.09 seconds ===========================
[test recursion functionality]
stjohn@judgement:work$ pytest -o log_cli=true --log-cli-level=DEBUG --log-format="%(levelname)s [%(lineno)s: %(funcName)s] %(message)s" test_entropy_from_dice.py::test_f4_recurse_64
============================= test session starts =============================
platform linux2 -- Python 2.7.12, pytest-4.6.11, py-1.9.0, pluggy-0.13.1
rootdir: /mnt/c/Users/User/Desktop/stuff/articles/generating_entropy_from_dice_2/work
collected 1 item
test_entropy_from_dice.py::test_f4_recurse_64
-------------------------------- live log call --------------------------------
DEBUG [331: convertDiceRollsToBitsUsingLargestPossibleGroups] diceRollsString: 64
DEBUG [332: convertDiceRollsToBitsUsingLargestPossibleGroups] Converting diceRollsString to bits using groupSizeList [2, 1]
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 2. Using groupSize=2. Converting dice rolls section '64' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 64
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=2, groupSize=2, a=6**groupSize=6**2=36
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=36. Result: 2**5 = 32.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=36, nextLowestIntegerPowerOf2 = 32 (=2**5)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 32-1 = 31
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 62
DEBUG [415: convertDiceRollsToBits] - rolls section '64' ('53' in base6) ignored.
DEBUG [424: convertDiceRollsToBits] 1 groups of 2 dice roll values ignored because they were greater than the maxDiceRollValue 62 (51 in base6, 31 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = ''
DEBUG [353: convertDiceRollsToBitsUsingLargestPossibleGroups] No bits extracted. Try to process this section again with a smaller groupSize.
DEBUG [331: convertDiceRollsToBitsUsingLargestPossibleGroups] diceRollsString: 64
DEBUG [332: convertDiceRollsToBitsUsingLargestPossibleGroups] Converting diceRollsString to bits using groupSizeList [1]
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 2. Using groupSize=1. Converting dice rolls section '6' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 6
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=1, groupSize=1, a=6**groupSize=6**1=6
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=6. Result: 2**2 = 4.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=6, nextLowestIntegerPowerOf2 = 4 (=2**2)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 4-1 = 3
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 4
DEBUG [415: convertDiceRollsToBits] - rolls section '6' ('5' in base6) ignored.
DEBUG [424: convertDiceRollsToBits] 1 groups of 1 dice roll values ignored because they were greater than the maxDiceRollValue 4 (3 in base6, 3 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = ''
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 1. Using groupSize=1. Converting dice rolls section '4' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 4
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=1, groupSize=1, a=6**groupSize=6**1=6
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=6. Result: 2**2 = 4.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=6, nextLowestIntegerPowerOf2 = 4 (=2**2)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 4-1 = 3
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 4
DEBUG [423: convertDiceRollsToBits] - rolls section '4' ('3' in base6), value=3, bits='11'
DEBUG [424: convertDiceRollsToBits] 0 groups of 1 dice roll values ignored because they were greater than the maxDiceRollValue 4 (3 in base6, 3 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '11'
PASSED [100%]
========================== 1 passed in 0.09 seconds ===========================
stjohn@judgement:work$ pytest -o log_cli=true --log-cli-level=DEBUG --log-format="%(levelname)s [%(lineno)s: %(funcName)s] %(message)s" test_entropy_from_dice.py::test_f4_recurse_group_7
============================= test session starts =============================
platform linux2 -- Python 2.7.12, pytest-4.6.11, py-1.9.0, pluggy-0.13.1
rootdir: /mnt/c/Users/User/Desktop/stuff/articles/generating_entropy_from_dice_2/work
collected 1 item
test_entropy_from_dice.py::test_f4_recurse_group_7
-------------------------------- live log call --------------------------------
DEBUG [331: convertDiceRollsToBitsUsingLargestPossibleGroups] diceRollsString: 6452463
DEBUG [332: convertDiceRollsToBitsUsingLargestPossibleGroups] Converting diceRollsString to bits using groupSizeList [7, 2, 1]
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 7. Using groupSize=7. Converting dice rolls section '6452463' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 6452463
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=7, groupSize=7, a=6**groupSize=6**7=279936
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=279936. Result: 2**18 = 262144.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=279936, nextLowestIntegerPowerOf2 = 262144 (=2**18)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 262144-1 = 262143
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 6452454
DEBUG [415: convertDiceRollsToBits] - rolls section '6452463' ('5341352' in base6) ignored.
DEBUG [424: convertDiceRollsToBits] 1 groups of 7 dice roll values ignored because they were greater than the maxDiceRollValue 6452454 (5341343 in base6, 262143 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = ''
DEBUG [353: convertDiceRollsToBitsUsingLargestPossibleGroups] No bits extracted. Try to process this section again with a smaller groupSize.
DEBUG [331: convertDiceRollsToBitsUsingLargestPossibleGroups] diceRollsString: 6452463
DEBUG [332: convertDiceRollsToBitsUsingLargestPossibleGroups] Converting diceRollsString to bits using groupSizeList [2, 1]
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 7. Using groupSize=2. Converting dice rolls section '64' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 64
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=2, groupSize=2, a=6**groupSize=6**2=36
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=36. Result: 2**5 = 32.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=36, nextLowestIntegerPowerOf2 = 32 (=2**5)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 32-1 = 31
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 62
DEBUG [415: convertDiceRollsToBits] - rolls section '64' ('53' in base6) ignored.
DEBUG [424: convertDiceRollsToBits] 1 groups of 2 dice roll values ignored because they were greater than the maxDiceRollValue 62 (51 in base6, 31 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = ''
DEBUG [353: convertDiceRollsToBitsUsingLargestPossibleGroups] No bits extracted. Try to process this section again with a smaller groupSize.
DEBUG [331: convertDiceRollsToBitsUsingLargestPossibleGroups] diceRollsString: 64
DEBUG [332: convertDiceRollsToBitsUsingLargestPossibleGroups] Converting diceRollsString to bits using groupSizeList [1]
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 2. Using groupSize=1. Converting dice rolls section '6' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 6
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=1, groupSize=1, a=6**groupSize=6**1=6
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=6. Result: 2**2 = 4.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=6, nextLowestIntegerPowerOf2 = 4 (=2**2)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 4-1 = 3
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 4
DEBUG [415: convertDiceRollsToBits] - rolls section '6' ('5' in base6) ignored.
DEBUG [424: convertDiceRollsToBits] 1 groups of 1 dice roll values ignored because they were greater than the maxDiceRollValue 4 (3 in base6, 3 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = ''
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 1. Using groupSize=1. Converting dice rolls section '4' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 4
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=1, groupSize=1, a=6**groupSize=6**1=6
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=6. Result: 2**2 = 4.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=6, nextLowestIntegerPowerOf2 = 4 (=2**2)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 4-1 = 3
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 4
DEBUG [423: convertDiceRollsToBits] - rolls section '4' ('3' in base6), value=3, bits='11'
DEBUG [424: convertDiceRollsToBits] 0 groups of 1 dice roll values ignored because they were greater than the maxDiceRollValue 4 (3 in base6, 3 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '11'
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 5. Using groupSize=2. Converting dice rolls section '52' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 52
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=2, groupSize=2, a=6**groupSize=6**2=36
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=36. Result: 2**5 = 32.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=36, nextLowestIntegerPowerOf2 = 32 (=2**5)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 32-1 = 31
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 62
DEBUG [423: convertDiceRollsToBits] - rolls section '52' ('41' in base6), value=25, bits='11001'
DEBUG [424: convertDiceRollsToBits] 0 groups of 2 dice roll values ignored because they were greater than the maxDiceRollValue 62 (51 in base6, 31 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '11001'
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 3. Using groupSize=2. Converting dice rolls section '46' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 46
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=2, groupSize=2, a=6**groupSize=6**2=36
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=36. Result: 2**5 = 32.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=36, nextLowestIntegerPowerOf2 = 32 (=2**5)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 32-1 = 31
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 62
DEBUG [423: convertDiceRollsToBits] - rolls section '46' ('35' in base6), value=23, bits='10111'
DEBUG [424: convertDiceRollsToBits] 0 groups of 2 dice roll values ignored because they were greater than the maxDiceRollValue 62 (51 in base6, 31 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '10111'
DEBUG [339: convertDiceRollsToBitsUsingLargestPossibleGroups] Can't use groupSize=2 - it's larger than the remaining number of dice rolls (1).
DEBUG [346: convertDiceRollsToBitsUsingLargestPossibleGroups] Number of dice rolls remaining = 1. Using groupSize=1. Converting dice rolls section '3' to bits.
DEBUG [387: convertDiceRollsToBits] diceRollsString: 3
DEBUG [388: convertDiceRollsToBits] len(diceRollsString)=1, groupSize=1, a=6**groupSize=6**1=6
DEBUG [486: nextLowestIntegerPowerOf2] Find greatest power of 2 that is less than or equal to n=6. Result: 2**2 = 4.
DEBUG [391: convertDiceRollsToBits] For a=6**groupSize=6, nextLowestIntegerPowerOf2 = 4 (=2**2)
DEBUG [399: convertDiceRollsToBits] maxUsableValue = powerOf2 - 1 = 4-1 = 3
DEBUG [403: convertDiceRollsToBits] maxDiceRollValue (maxUsableValue converted back into a dice roll value): 4
DEBUG [423: convertDiceRollsToBits] - rolls section '3' ('2' in base6), value=2, bits='10'
DEBUG [424: convertDiceRollsToBits] 0 groups of 1 dice roll values ignored because they were greater than the maxDiceRollValue 4 (3 in base6, 3 in base10).
DEBUG [425: convertDiceRollsToBits] Result: bits = '10'
PASSED [100%]
========================== 1 passed in 0.15 seconds ===========================
Great. That's the end of this project.
[start of notes]
I found these links helpful during this project:
crypto.stackexchange.com/questions/6175/how-to-best-obtain-bit-sequences-from-throwing-normal-dice
math.stackexchange.com/questions/2916887/shannon-entropy-of-a-fair-dice
www.math.hawaii.edu/~ramsey/Probability/TwoDice.html
[end of notes]
In the error message, "does" should be "does not".