Contents:
Write a program to model the following peculiar duel (the names have been changed to protect the parties involved). A and B have quarelled and are going to settle their differences by duelling with pistols. Not only are A and B touchy, neither is a very good shot. A hits what he shoots at on average once every two tries. B is even worse, hitting what he shoots at once every three tries. Being a gentleman A allows B to fire first. The duel continues until one of them is hit.
Your program should determine who is most likely to win the duel, by simulating many duels between A and B. It should output the percentage of duels won by A and the percentage won by B.
Approach: We'll write this program from the inside out. Starting from the most specific event and working outward to a full program.
The duel begins with B taking a shot at A. He can either hit or miss A. The chances that he hits A are 1 in 3. The chances that he misses are 2 in 3.
import random # B shoots at A if random.randint(0, 2) == 2: print "B hits A. The duel's over." else: print "B misses A. The duel continues."
If B hits A then the duel ends, but if he misses the duel will continue.
The duel continues by A taking a shot at B. This code is similar to B taking
a shot at A, but the odds are different because A is a better shot. We put
the code for A's shot into the else branch of the
if above, since A only gets to shoot if B misses.
import random
# B shoots at A
if random.randint(0, 2) == 2:
print "B hits A. The duel's over."
else:
print "B misses A. The duel continues."
if random.randint(0, 1) == 1:
print "A hits B. The duel's over."
else:
print "A misses B. The duel continues."
Now if A hits B the duel is over, but if A misses the duel continues with
B taking a shot. We could copy our B shooting code and add it in the second
else branch above, but we could have to do that a lot, since the
duel could, in principle, take an infinite number of rounds to complete. A
simpler approach is to place the code above inside a while
loop:
import random
while ?:
# B shoots at A
if random.randint(0, 2) == 2:
print "B hits A. The duel's over."
else:
print "B misses A. The duel continues."
if random.randint(0, 1) == 1:
print "A hits B. The duel's over."
else:
print "A misses B. The duel continues."
The question is what the while condition should be. We want
to say, "while the duel is not over, they keep shooting at each other" and
Python lets us come very close to writing this:
import random
over = False
while not over:
# B shoots at A
if random.randint(0, 2) == 2:
print "B hits A. The duel's over."
over = True
else:
print "B misses A. The duel continues."
if random.randint(0, 1) == 1:
print "A hits B. The duel's over."
over = True
else:
print "A misses B. The duel continues."
We declare a variable called over to hold the state of the
duel. It's value is False when the duel is not over, and
True when the duel is over. We initialize it to be
False (since before hey have shot at each other the duel can't
be over). We update its value to True when one antagonist hits
the other. The loop lets them keep taking turns shooting at each other until
one finally hits the other.
This code successfully models a duel: run it and see.
We are making progress but we are not done because the original problem
wanted us to simulate many duels and display the percentage of duels won by
each antagonist. To simulate many duels we pack all the code above (except
for the import) into a for loop, and record, using
counters, the number of duels each antagonist wins.
import random
awins = 0
bwins = 0
for duel in range(0, 100): # See Pythonic Details for an explanation of range.
over = False
while not over:
# B shoots at A
if random.randint( 0, 2 ) == 2:
print "B hit A. The duel's over."
bwins = bwins + 1
over = True
else:
print "B missed A. The duel continues."
if random.randint( 0, 1 ) == 1:
print "A hit B. The duel's over."
awins = awins + 1
over = True
else:
print "A missed B. The duel continues."
print 6*'-'
print "A won", awins, "duels."
print "B won", bwins, "duels."
Note that we are careful to initialize our counters above
the for loop, but to reset over to
False inside the for loop.
We haven't quite met the question's requirements: