Arizona doesn’t still have a “he needed killin’” homicide defense, does it?
That raises the knottier question of which state’s law applies if you fire a bullet in one state but it kills someone in another.
And an interestig alibi.
“You say he was shot at 9pm. Couldn’t have been me. I didn’t shoot anything until 10pm.”
Both states could prosecute, as well as the feds. And since they’re all separate sovereigns, that doesn’t count as double jeopardy. Not the best plan.
They can only execute you once. But using 3 different methods at once might be … interesting.
Are those the correct dice rolls for that probability?
Let’s see: 5 choose 2 divided by 10 choose 2, 10/45 = 1/4.5 or 2/9 or 22.2… %.
Roll 15-18 on 3d6: (10 + 6 + 3 + 1) / 216 = 20 / 216 = 9.26%
Roll 14 on 3d6 and roll 2-4 on d4: (15 / 216) * .75 = 5.21%
Roll 13 on 3d6 and roll 3-4 on d4: (21 / 216) * .5 = 4.86%
Roll 12 on 3d6 and roll 4 on d4: (25 / 216) * .25 = 2.89%
Sum them up: 22.22%
Never doubt the Munroe.
It’s an interesting problem in general.
The only way to get an exact result is if the total number of dice possibilities is an integer multiple of the denominator of the target probability. In this case, there are 6*6*6*4=864 possible dice rolls, while the target is 2/9. The three sixes each have a factor of 3 in them; more than enough for the 3*3=9.
But you’re screwed if you have to pick a good arrow out of a quiver of 7. No D&D die has a factor of 7 in it. You’d have to do something like “roll a D10; on a 10 you succeed, on a 7-9 you re-roll, and on a 1-6 you fail”. But that’s cheating and makes the one in the comic trivial as well.
What rules are allowed, anyway? In D&D it’s almost always that you succeed on a number greater than some threshold, but it wouldn’t surprise me if there were some exceptions. I haven’t played all that much.
The comic’s situation could be made simpler if you allow non-monotonic win cases: roll 2d6. You succeed on a 5 or a 9 (that’s 4+4=8 combinations out of 36, or 2/9).
Well, I wrote a little program to brute-force things but it isn’t very satisfying. And I found another issue: as the combinatorial problems increase in complexity, so to the odds of creating a bad fraction. For instance, say you have a quiver with 20 arrows (10 cursed) and you want the odds of picking three good ones. That’s 2/19 due to that denominator. The odds of your combination “passing through” a large prime factor just gets too high as the numbers increase.
Sometimes the numerators will cancel them out, but usually not. One ok combination is 26 arrows, half cursed, and pick 3. That gets you 11/100. That actually exposed something funny in my program. It came up with 2d10 + d20, threshold 31. That’s correct I think, but I didn’t consider the percentile d10 (the one with 00, 10, etc.). Or a d100 for that matter.
Some arbitrary fractions have easy answers; others don’t seem to. 37/120 can be done with d10+d12+d20, >=27. 36/120 is d10+d12, >=15. But 35/120 doesn’t find an answer with up to 7 dice (I didn’t wait any longer than that).
Huh. Even a fraction as simple as 4/9 doesn’t seem to have a solution with a small number of ordinary D&D dice.
I would quibble with that being the right probability. It’s the right probability for the precise question that was asked, but it’s not for the question that’s actually relevant. If the player does manage to avoid all the cursed arrows, then well and good, but if they don’t, then it’s probably relevant how many cursed arrows they drew. And it might even be relevant which cursed arrows, if not all the curses are the same. The dice-and-threshold method doesn’t give either of those answers.
Although D&D does usually use a dice-and-threshold method, when it does so, it’s almost always just a single d20 plus some set of static modifiers (something like 1d20+7 versus a threshold of 18). There are a few examples of other dice-and-threshold (for instance, the 5th edition version of the Sleep spell rolls 5d8 and compares that to the targets’ HP total), but then, there are also a few examples of things like “roll 1d8, and on an 8, discard the result and re-roll”. If a real situation like in the comic were to come up, I would expect that they players would just simulate it directly: “Roll two d10s and if they’re the same, re-roll one of them. The cursed arrows are 1-5.”
Of course, one of the curses is that that arrow changes the odds of drawing it at random.
God dammit. I thought “I wonder how AI would do on this problem” and asked Claude to write this program:
I want a Python program to perform some die roll simulations. The goal is to specify a fraction, say 2/9, and then give me the smallest set of D&D dice (D4, D6, D8, D10, D12, and D20) and a threshold so that when added up the probability is the same. For instance, for 2/9 I can roll 3D6 and a D4 and require a 16 or better. That comes to the same value.
The program should work by considering all possible sets of a single die, then two dice, etc. and checking if there is any numeric threshold that gives the right specified probability. Keep going until a solution is found or there are too many sets to consider.
I want the probability to be exact. No approximations. This means that certain fractions like 1/7 are impossible to achieve. The user should be notified if this is the case.
The program is perfect. Exactly right on the first try, took 20 seconds to generate. It’s way faster than my program, found a better solution to one of my previous problems (meaning there’s a bug in mine), has way friendlier input/output, like:
Enter target probability as a fraction (e.g., 2/9): 37/120
Solution found for 37/120:
Roll D10 + D12 + D20 and get 27 or higher
Not to mention:
Enter target probability as a fraction (e.g., 2/9): 2/9
Solution found for 2/9:
Roll D4 + 3D6 and get 16 or higher
It’s also commented better and frankly is better written. AI gonna put all of us out of a job…
The code, in case anyone’s interested:
Summary
from fractions import Fraction
from itertools import combinations_with_replacement
from collections import Counter
def calculate_outcomes(dice_counts):
"""Calculate all possible outcomes and their frequencies for a given set of dice."""
# Initialize with a single outcome of 0 occurring once
outcomes = {0: 1}
# Process each die type
for die, count in dice_counts.items():
for _ in range(count):
new_outcomes = {}
# For each existing outcome, add all possible rolls of the current die
for current_sum, freq in outcomes.items():
for roll in range(1, die + 1):
new_sum = current_sum + roll
new_outcomes[new_sum] = new_outcomes.get(new_sum, 0) + freq
outcomes = new_outcomes
return outcomes
def find_threshold(outcomes, target_prob):
"""Find a threshold that gives exactly the target probability, if one exists."""
total_outcomes = sum(outcomes.values())
# Try each possible threshold
for threshold in range(min(outcomes.keys()), max(outcomes.keys()) + 2):
# Calculate probability of rolling >= threshold
success_count = sum(freq for roll, freq in outcomes.items() if roll >= threshold)
prob = Fraction(success_count, total_outcomes)
if prob == target_prob:
return threshold
return None
def find_dice_combination(target_prob):
"""Find the smallest set of dice that can achieve the target probability."""
dice = [4, 6, 8, 10, 12, 20]
max_dice = 6 # Maximum number of dice to consider
# Try combinations of increasing size
for num_dice in range(1, max_dice + 1):
# Generate all possible combinations of dice
for combo in combinations_with_replacement(dice, num_dice):
# Convert to Counter for easier handling
dice_counts = Counter(combo)
# Calculate all possible outcomes for this combination
outcomes = calculate_outcomes(dice_counts)
# Try to find a threshold that gives the target probability
threshold = find_threshold(outcomes, target_prob)
if threshold is not None:
# Format the dice combination string
dice_str = []
for die, count in sorted(dice_counts.items()):
if count > 0:
dice_str.append(f"{count}D{die}" if count > 1 else f"D{die}")
return dice_str, threshold
return None, None
def main():
# Get input from user
try:
num, denom = map(int, input("Enter target probability as a fraction (e.g., 2/9): ").split('/'))
target = Fraction(num, denom)
if not 0 <= target <= 1:
print("Probability must be between 0 and 1")
return
dice_combo, threshold = find_dice_combination(target)
if dice_combo is None:
print(f"No exact solution found for {num}/{denom}")
print("This probability may be impossible to achieve with standard D&D dice.")
else:
print(f"\nSolution found for {num}/{denom}:")
print(f"Roll {' + '.join(dice_combo)} and get {threshold} or higher")
except ValueError:
print("Please enter the probability in the format 'n/m' where n and m are integers")
if __name__ == "__main__":
main()
I would use two separate rolls:
First arrow would be a d10 with a DC of 6 to not grab a cursed one.
Second would be a d10 (ignoring any 10s) with the DC dependent of the previous roll.
But then I hated statistics and probability in school.
Shouldn’t that be Black Hat, not Cueball, sitting on the non-Arizona side?
Of note, that’s almost certainly not a problem that has been programmed before (except, possibly, when Munroe did his calculations), so it, or something like it, would almost certainly have not been in the AI training data. Which means that that really is definitely a new creation by the AI. This is in contrast to the usual test cases folks give for AI, like “Write an implementation of Quicksort”, for which there is oodles of prior art in the training data.
I was thinking the same thing. Probably a number of people have now done what I did, but that wouldn’t be in the training set. That said, I’ve used it for enough different coding problems now that I know it isn’t just replicating something it had seen before.
I was impressed that even with my somewhat underspecified description, it got everything exactly right, including understanding what I meant by 3D6, for example. And even included niceties like printing D4 instead of 1D4.