Brain-Teaser 934: Joustin’ time
From The Sunday Times, 15th June 1980 [link]
At the recent All-England Jousting Tournament, the final was between Sir Yorick and Sir Zebedee. Each knight had brought nine men to fight for him, and the final consisted of a fight between two men — one from each knight. No man fought in two or more rounds.
At the start of the final, the two knights tossed a coin to decide the order of choosing their men for the first round. For each round after the first, the knight whose man had won the previous round chose his man first. In each round, the knight choosing second had the advantage that he knew his opponent’s choice before he made his own choice.
Sir Yorick brought two horsemen and seven spearmen, and Sir Zebedee brought six bowmen and three footsoldiers.
Whenever they fight:
a horseman always beats a bowman or a footsoldier;
a bowman always beats a spearman;
a spearman always beats a footsoldier.As each knight knew the above facts beforehand, he was able to choose his man for each round so that he won as many rounds as possible.
How many rounds did Sir Yorick win?
[teaser934]








Jim Randell 11:39 am on 6 October 2026 Permalink |
The following Python program scores a scenario by determining the number of wins for Y minus the number of wins for Z. Y is trying to make the score as large as possible (more positive), and Z is trying to make the score as small as possible (more negative).
If one player has only one type of combatant at their disposal, then the outcome is easily determined. But if both players have both types of combatant available then the first player can choose either type, and the second player can also choose either type in response. Y will choose the type that maximises the score of the scenario, and Z will choose the type that minimises the score of the scenario.
We can calculate the score for the scenarios where each player makes the first choice.
The program runs in 78ms. (Internal runtime is 124µs).
from enigma import (ediv, cache, printf) # labels for Y and Z (Y, Z) = (0, 1) # return wins (Y - Z) @cache def play(H, S, B, F, player): # if a player has only one type remaining then the outcome is determined # (both players have the same total number of men) if H == 0: return (F - B) # Y wins against F, loses against B if S == 0: return (H - 0) # Y wins with all H if B == 0: return (F - 0) # Y wins against all F if F == 0: return (H - S) # Y wins with H, loses with S # otherwise choose the best outcome for the current player if player == Y: # Y chooses H; Z responds with B or F rH = min( play(H - 1, S, B - 1, F, Y) + 1, # H vs B -> Y wins play(H - 1, S, B, F - 1, Y) + 1, # H vs F -> Y wins ) # Y chooses S; Z responds with B or F rS = min( play(H, S - 1, B - 1, F, Z) - 1, # S vs B -> Z wins play(H, S - 1, B, F - 1, Y) + 1, # S vs F -> Y wins ) # best outcome for Y return max(rH, rS) elif player == Z: # Z chooses B; Y responds with H or S rB = max( play(H - 1, S, B - 1, F, Y) + 1, # B vs H -> Y wins play(H, S - 1, B - 1, F, Z) - 1, # B vs S -> Z wins ) # Z chooses F; Y responds with H or S rF = max( play(H - 1, S, B, F - 1, Y) + 1, # F vs H -> Y wins play(H, S - 1, B, F - 1, Y) + 1, # F vs S -> Y wins ) # best outcome for Z return min(rB, rF) # consider situations where each player chooses first for (player, name) in zip([Y, Z], "YZ"): # initial set-up r = play(2, 7, 6, 3, player) # extract final scores y = ediv(r + 9, 2) z = 9 - y # output solution printf("{name} first -> Y = {y}, Z = {z}")Solution: Sir Yorick wins 5 of the rounds.
It doesn’t matter which knight goes first, the result is Y=5, Z=4 in each case.
LikeLike
Jim Randell 9:36 pm on 6 October 2026 Permalink |
Or, more succinctly:
from enigma import (ediv, cache, printf) # labels for Y and Z (Y, Z) = (0, 1) # return wins (Y - Z) @cache def play(H, S, B, F, player): # if a player has only one type remaining then the outcome is determined if H == 0: return (F - B) # Y wins against F, loses against B if S == 0: return (H - 0) # Y wins with all H if B == 0: return (F - 0) # Y wins against all F if F == 0: return (H - S) # Y wins with H, loses with S # otherwise choose the best outcome for the current player HB = play(H - 1, S, B - 1, F, Y) + 1 # H vs B -> Y wins HF = play(H - 1, S, B, F - 1, Y) + 1 # H vs F -> Y wins SB = play(H, S - 1, B - 1, F, Z) - 1 # S vs B -> Z wins SF = play(H, S - 1, B, F - 1, Y) + 1 # S vs F -> Y wins if player == Y: # return best outcome for Y return max(min(HB, HF), min(SB, SF)) elif player == Z: # return best outcome for Z return min(max(HB, SB), max(HF, SF)) # consider situations where each player chooses first for (player, name) in zip([Y, Z], "YZ"): # initial set-up r = play(2, 7, 6, 3, player) # extract final scores y = ediv(r + 9, 2) z = 9 - y # output solution printf("{name} first -> Y = {y}, Z = {z}")LikeLike