Teaser 3344: Wining and dying
From The Sunday Times, 25th October 2026 [link] [link]
You are a king with 15 wine barrels in your cellar. You know that exactly one of the barrels contains a lethal poison, but you do not know which. On day one you give some wine (or none) to each of your four wine tasters. Any who took poison will be dead by the next day. The trouble is that you cannot tell the tasters apart, so if any of them do die you cannot be certain what wine it was you gave them. On day two you observe how many are still alive and can repeat the process with them, carrying on until you have identified the poisoned barrel.
On which day can you be absolutely certain which is the poisoned wine?
[teaser3344]








Jim Randell 6:22 am on 25 October 2026 Permalink |
If we cannot distinguish the tasters, then the outcome of a tasting is how many of the tasters survive. So with 4 tasters the possible outcomes are that 0, 1, 2, 3, 4 of them survive, which is only 5 possible outcomes. And there are 15 barrels, so we are going to need more than 1 round of tastings.
Let B(k, r) be the maximum number of barrels that can be resolved given k tasters and r rounds of tasting.
In 0 rounds we can only determine the poison barrel if there is only 1 barrel to choose from.
In 1 round of tasting there can be (k + 1) different outcomes (i.e. 0 .. k of the tasters die). So, if we label the barrels 0 .. k, and have the same number of tasters taste each barrel as the number on the label, then we can determine the poison barrel directly from the number of tasters that die.
For r > 1 we can have groups of barrels that are tasted by 0 .. k of the tasters, and the number of barrels that we allocate to each group must be the number that can be resolved by the surviving tasters (who were not part of that group).
This gives a straightforward recursive implementation of B(k, r).
This Python program looks at increasing numbers of rounds with 4 tasters, until we are able to resolve 15 barrels.
from enigma import (irange, inf, cache, arg, printf) # B(k, r) = max number of barrels that can be resolved # with <r> rounds of tasting and <k> tasters (initially) @cache def B(k, r): # in 0 rounds we can resolve 1 barrel if r == 0: return 1 # [optional] in 1 round we can resolve (k + 1) barrels if r == 1: return k + 1 # otherwise allocate barrels to tasters according to how many # barrels can be resolved by j surviving tasters in (r - 1) rounds return sum(B(j, r - 1) for j in irange(0, k)) n = arg(15, 0, int, prompt="number of barrels") k = arg(4, 1, int, prompt="number of tasters") # find minimum number of rounds to resolve <n> barrels for r in irange(0, inf): b = B(k, r) printf("[B({k}, {r}) = {b}]") if b >= n: printf("{n} barrels, {k} tasters -> {r} rounds") breakSolution: [To Be Revealed]
[Details – To Be Revealed]
See also: Teaser 2784.
LikeLike
NigelR 2:16 pm on 27 October 2026 Permalink |
Try this!
#display pots for this round of tasting and return player's answer on consequent deaths def print_pots(s,t): print (f'It is now Day {day} and {t} taster(s) are still alive.') print ('How many tasters will die if each of the pots below is drunk by a different taster?') while len(s)%t:s.append([]) for i in range(t): print (f'Pot {i+1}: {[s[j] for j in range(i,len(s),t) if s[j]]}') while True: try: deaths = int(input(f'enter 0 - {t}: ' )) if 0 <= deaths <= t: return deaths print (f' Try again - entry must be between 0 and {t}') except ValueError: print (f' Entry must be a whole number between 0 and {t}') # identifies barrels that should be included in todays's sampling pots samples = lambda s: flatten( [[lookup[k]]*int(k[day-1]) for k in s]) flatten = lambda l : [i for item in l for i in item if i] print('\n\n\n There are 15 barrels of wine, just one of which contains a lethal poison. Up to 4 tasters will each sample a') print('different pot containing a mix of wines. Anyone sampling a pot with the poisoned wine in the mix will die.') print('You are now asked to choose which is the poisoned barrel and then, without disclosing your selection, answer') print('questions on the tasting outcomes based on your choice. \n') day = 1 tasters = 4 #generate 2 digit code for each barrel based on @Jim Randall's analysis seq=[str(5-b)+str(i) for b in range(5,0,-1) for i in range(b)] code = {i: seq[i-1] for i in range(1, 16)} #create inverse lookup to identify barrel from code lookup = {code[i]:i for i in code.keys()} samp = samples(seq) d1 = print_pots(samp,tasters) day = 2 tasters -= d1 seq2 = [s for s in seq if int(s[0]) == d1] d2 = print_pots(samples(seq2),tasters) ans = lookup[str(d1)+str(d2)] print(f'The poison was in barrel {ans}')LikeLike
Jim Randell 11:20 am on 28 October 2026 Permalink |
Nice. I am going to post a decision tree when I give the details of my solution on Saturday. (Although not a program to generate the tree, like I did for Teaser 1342).
(I had to rescue the
[code]tag in your comment, as thelanguage="python"parameter had non-ASCII quotation marks, so WordPress didn't understand it, but hopefully the rest of the code came through OK once that was fixed).Also, there is a problem if all 4 tasters are poisoned on the first tasting, then the program gives an error.
LikeLike
NigelR 3:45 pm on 28 October 2026 Permalink |
Schoolboy error, and thanks for spotting! I did originally test for 4 deaths on day 1 but that was before I tried to be cute and added a padding test of %t. Line 38 becomes:’ if not tasters: d2 = 0’. Next line is ‘else: d2 = print_pots(…etc)
LikeLike