# Enigmatic Code

Programming Enigma Puzzles

## Tantalizer 430: Hop, skip and jump

From New Scientist #981, 1st January 1976 [link]

To shake down the plum pud, the five adults held three post-prandial athletic events. Each competitor scored the number of the place gained in each event, with the aim of totalling as few points as possible overall. Thus Uncle Arthur came second in the hop and scored 2 points for it. There were no ties in any event or in the overall totals and no one took the same place in two or more events.

Aunt Barbara, although bottom in one event, was top at skipping, Mother having been forced down to third place by a fit of hiccoughs. Father did better than Uncle Charlie at hopping. Uncle Arthur did not win the jumping. Mother did better at jumping than at hopping. Aunt Barbara was not second overall. The overall winner did not win the hopping.

As your post-prandial exercise, would you care to list the order in each event?

The puzzle can be solved as presented, but has two solutions. To arrive at the published single solution we seem to need an extra fact — “Uncle Arthur finished in third place overall”.

#### News

This puzzle completes the archive of Tantalizer puzzles from 1976. There is a full archive from this puzzle to the final Tantalizer puzzle in May 1977 (when the Puzzle series started).

[tantalizer430]

### One response to “Tantalizer 430: Hop, skip and jump”

1. Jim Randell 1 May 2019 at 8:38 am

The puzzle as published appears to admit two solutions, one of which is the published answer. To arrive at the single required solution we can add a condition to the puzzle text:

[… Uncle Arthur did not win the jumping,] but finished in third place overall. […]

This MiniZinc model includes this extra condition. It executes in 118ms.

```%#! minizinc -a --solver chuffed

include "globals.mzn";

% label the participants
int: M = 1;
int: F = 2;
int: A = 3;
int: B = 4;
int: C = 5;
set of int: Participant = 1..5;

% label the events
int: H = 1;
int: S = 2;
int: J = 3;
set of int: Event = 1..3;

% the scores: x[participant, event] = score
array [Participant, Event] of var 1..5: x;

% total points for particpant k
function var int: total(var int: k) =
sum (j in Event) (x[k, j]);

% overall position for particpant k is n
predicate overall(var int: k, var int: n) =
sum (i in Participant where i != k) (total(i) < total(k)) = n - 1;

% each event has 5 positions / scores
constraint forall (j in Event) (all_different([x[i, j] | i in Participant]));

% no-one got the same score for two events
constraint forall (i in Participant) (all_different([x[i, j] | j in Event]));

% the total scores are all different
constraint all_different([total(i) | i in Participant]);

% A came second in the hop
constraint x[A, H] = 2;

% B was bottom in one event
constraint exists (j in Event) (x[B, j] = 5);

% B was top at skipping
constraint x[B, S] = 1;

% M was third at skipping
constraint x[M, S] = 3;

% F did better than C at hopping
constraint x[F, H] < x[C, H];

% A did not win the jumping
constraint x[A, J] > 1;

% M did better at jumping than at hopping
constraint x[M, J] < x[M, H];

% B was not second overall
constraint not overall(B, 2);

% the overall winner did not with the hopping
constraint forall (i in Participant) (not(overall(i, 1) /\ x[i, H] = 1));

% [additional] A was third overall
constraint overall(A, 3);

solve satisfy;
```

And here is a Python program to format the output (using the minizinc.py wrapper):

```from minizinc import MiniZinc
from enigma import irange, join, unpack, printf

# the model
p = MiniZinc("tantalizer430.mzn", use_shebang=1)

# map labels to participants and events, positions
P = [ '?', 'Mother', 'Father', 'Arthur', 'Barbara', 'Charlie' ]
E = [ '?', 'Hop', 'Skip', 'Jump' ]
K = [ '?', '1st', '2nd', '3rd', '4th', '5th' ]

# solve the model
for (x,) in p.solve(result="x"):
# t[i] = total points for participant i
# e[(j, k)] = participant at position k in event j
e = dict()
t = dict((i, 0) for i in irange(1, 5))
# output the solution
for (i, d) in x.items():
for (j, k) in d.items():
e[(j, k)] = i
t[i] += k
for j in irange(1, 3):
ps = list((k, e[(j, k)]) for k in irange(1, 5))
printf("{j}: {ps}", j=E[j], ps=join((f"{K[k]} = {P[i]}" for (k, i) in ps), sep="; "))
ts = sorted(t.items(), key=unpack(lambda i, x: x))
printf("Total: {ts}", ts=join((f"{K[k]} = {P[i]} ({x} pts)" for (k, (i, x)) in enumerate(ts, start=1)), sep="; "))
printf()
```

Solution: The positions and points were:

Hop: 1st = Father; 2nd = Arthur; 3rd = Charlie; 4th = Barbara; 5th = Mother
Skip: 1st = Barbara; 2nd = Charlie; 3rd = Mother; 4th = Arthur; 5th = Father
Jump: 1st = Charlie; 2nd = Father; 3rd = Arthur; 4th = Mother; 5th = Barbara
Overall: 1st = Charlie (6 pts); 2nd = Father (8 pts); 3rd = Arthur (9 pts); 4th = Barbara (10 pts); 5th = Mother (12 pts)

The explanation given with the answer is:

Of 45 points awarded no one scored less than 6 or more than 12. So possible scores were (6, 7, 9, 11, 12) or (6, 8, 9, 10, 12) or (7, 8, 9, 10, 11), all implying 9 for Arthur the overall 3rd. Barbara scored 8 or 10 and, as she was not second, 10. A and B scored a 4, as did whoever got 11 or 12. So the winner got 1+2+3, the loser 3+4+5 and the runner-up 1+2+5. Now prove that Mother was bottom overall and then fill in.

Which makes use of the additional condition, not given in the original puzzle text, i.e. A finished in 3rd place overall.

Hop: 1st = Father; 2nd = Arthur; 3rd = Barbara; 4th = Charlie; 5th = Mother
Skip: 1st = Barbara; 2nd = Charlie; 3rd = Mother; 4th = Father; 5th = Arthur
Jump: 1st = Charlie; 2nd = Mother; 3rd = Father; 4th = Arthur; 5th = Barbara
Overall: 1st = Charlie (7 pts); 2nd = Father (8 pts); 3rd = Barbara (9 pts); 4th = Mother (10 pts); 5th = Arthur (11 pts)

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