Sunday Times Teaser 3129 – Bounce Count
by Mark Valentine
Published Sunday September 11 2022 (link)
At the local arcade, Claire and David played an air hockey game, consisting of a square table with small pockets at each corner, on which a very small puck can travel 1m leftright and 1m updown between the perimeter walls. Projecting the puck from a corner, players earn a token for each bounce off a wall, until the puck drops into a pocket.
In their game, one puck travelled 1m farther overall than its leftright distance (for the other, the extra travel was 2m). Claire’s threedigit number of tokens was a cube, larger than David’s number which was trianglular (1+2+3+…). Picking up a spare token, they could then arrange all their tokens into a cube and a square combined.
How many tokens did they end up with?

Brian Gladman permalink123456789101112131415161718192021222324252627282930313233343536373839404142434445from itertools import productfrom math import isqrt, gcd# perfect cubes and triangular numbers less than 1000cubes = set(n ** 3 for n in range(1, 10))tris = set(n * (n + 1) // 2 for n in range(1, 45))# with x  1 left/right bounces, the total left/right distance travelled is x;# with y  1 up/down bounces, the total up/down distance travelled is y; the# total distance travelled is then z such that z^2 = x^2 + y^2, giving a token# of x + y  2; when x = z  d, find y such that y^2 = z^2  (z  d)^2, giving# the token (z  d) + y  2 for d in (1, 2) and tokens < 1000; note that when# gcd(z  d, y) > 1 the puck will leave the table before reaching (z  d, y).def generate(d):for z in range(1, 1004):y2 = d * (2 * z  d)if (y:= isqrt(y2)) ** 2 == y2:# make sure that the puck reaches the point (z  d, y)if gcd(z  d, y) == 1:yield z  d + y  2# accumulate cubic and triangluar number tokens for Claire# and David for distance differences <d> equal to 1 and 2c1, c2, d1, d2 = [], [], [], []for d in (1, 2):for token in generate(d):if token >= 100 and token in cubes:(c1, c2)[d  1].append(token)if token in tris:(d1, d2)[d  1].append(token)# consider all combinations of tokens for Claire and David# with different distance differencesfor p in ((c1, d2), (c2, d1)):for c, d in product(*p):if c > d:# can their combined tokens plus one spare be# formed from the sum of a cube and a square?tv = c + d + 1for cb in cubes:if (rem := tv  cb) >= 0:if isqrt(rem) ** 2 == rem:print(f"Tokens of {c} and {d}.")

John Z. permalink12345678910111213141516171819202122232425262728293031# cubescs = tuple(i * i * i for i in range(1, 12))# triangular numbersts = tuple(j * (j+1) // 2 for j in range(1, 38))# tokens = horizontal distance  1 + vertical distance  1# tokens if overall distance (hypotenuse) == horizontal + 1t1s = tuple(i + int(i1)  2 for i in range(1, 300)if (i1 := ((i + 1) ** 2  i ** 2) ** (1 / 2)) == int(i1))# tokens if overall distance == horizontal + 2t2s = tuple(i + int(i2)  2 for i in range(1, 300)if (i2 := ((i + 2) ** 2  i ** 2) ** (1 / 2)) == int(i2))# consider tokens where "one puck travelled 1m farther overall..."for t1 in t1s:# consider tokens where "one puck travelled 2m farther overall..."for t2 in t2s:if ((t2 > t1 and t1 in ts and t2 in cs[4:9]) or(t1 > t2 and t1 in cs[4:9] and t2 in ts)):# sum Claire and David's tokens; add 1tokens = t1 + t2 + 1for k in cs:if tokens  k > 0 and (tksr := (tokens  k) ** 0.5) == int(tksr):print('Tokens:', t1, t2, 'cube:', k, 'square:', tokens  k)