# Enigmatic Code

Programming Enigma Puzzles

## Tantalizer 435: Compleat idiots

From New Scientist #986, 5th February 1976 [link]

The landlord of the Compleat Idiot likes to add spice to the day’s angling. Each angler starts by predicting everyone’s catch and there is a double scotch for each correct prediction afterwards. Yesterday no one was right about anyone, each man having predicted too few for those who beat him and too many for those who did not (including himself). Everyone caught at least one fish and all caught a different number. If I tell you the predictions (predictors down the left, persons predicted for across the top), can you work out the actual catches? [tantalizer435]

### One response to “Tantalizer 435: Compleat idiots”

1. Jim Randell 20 February 2019 at 7:26 am

This Python 3 program reuses the [[ Interval() ]] class I wrote for Enigma 70 and Enigma 201.

The program considers the possible orders of the anglers (from smallest catch to largest catch), and sees if intervals can be created that satisfy all the predictions.

It runs in 89ms.

Run: [ @repl.it ]

```from itertools import permutations
from enigma import irange, sprintf, join, printf

# represent an interval by its min and max values
class Interval(object):

def __init__(self, min=None, max=None):
self.min = 0 # lower limit
self.max = 999 # upper limit
self.update(min, max)

def __repr__(self):
return sprintf("<{self.min},{self.max}>")

# update the min/max values
def update(self, min=None, max=None):
if min is not None and min > self.min: self.min = min
if max is not None and max < self.max: self.max = max
if self.min > self.max: raise ValueError(sprintf("Bad Interval {self}"))

# iterate through the values in an interval
def __iter__(self):
yield from irange(self.min, self.max)

# an interval that is the intersection of this interval and another
def intersect(self, other):
return Interval(min=max(self.min, other.min), max=min(self.max, other.max))

# label the anglers
anglers = irange(0, 5)

# the table
table = (
(9, 4, 8, 3, 1, 6),
(6, 7, 5, 1, 6, 7),
(6, 9, 16, 5, 10, 3),
(4, 2, 4, 8, 9, 5),
(9, 5, 7, 9, 12, 9),
(8, 5, 10, 7, 5, 11),
)

# generate intervals for a given order
def intervals(order):
# split the order
for (i, X) in enumerate(order):
I = Interval(min=1)
# everyone before X underestimates their catch
for Y in order[:i]:
I.update(min=table[Y][X] + 1)
# everyone after X overestimates their catch
for Y in order[i:]:
I.update(max=table[Y][X] - 1)
yield I

# generate strictly increasing sequences from a collection of intervals
def sequences(intervals, s=[]):
# are we done?
if not intervals:
yield s
else:
i = intervals
if s: i = i.intersect(Interval(min=s[-1] + 1))
for x in i:
yield from sequences(intervals[1:], s + [x])

# count the solutions
n = 0

# consider possible orders
for order in permutations(anglers):

try:
s = list(intervals(order))
except ValueError:
continue

printf("order = ({order}), intervals = {s}", order=join(("ABCDEF"[i] for i in order), sep=" "))

# try to find solutions
for r in sequences(s):
printf(" -> {r}")
n += 1

printf("[{n} solutions]")
```

Solution: The actual catches are: A=7, B=1, C=6, D=2, E=11, F=8.

There is only one order that gives non-empty intervals for each angler. That is (B, D, C, A, F, E), from smallest catch to largest catch. And in this case each of the intervals defines a single possible amount.

This site uses Akismet to reduce spam. Learn how your comment data is processed.