Solving the water bucket riddle!
Python 3, 243 239 bytes

-4 bytes thanks to @JonathanFrech!



def f(a,t,k=1):
 while g(a,t,[0]*len(a),[],k):k+=1
def g(a,t,c,p,k):n=len(a);k,i=k//n,k%n;k,j=k//n,k%n;exec(["c[i]=0","c[i]=a[i]","x=min(a[j]-c[j],c[i]);c[i]-=x;c[j]+=x"][k%3]);p+=k%3,i,j;return g(a,t,c,p,k//3)if k&gt;2else{t}-{*c}or print(p)


Try it online!

Input: a list of bucket capacities a, and the target t.
Output: to stdout, a list of integers, where each triplet m,i,j denotes a move: m is the move type (0,1,2 corresponds to empty, fill, pour), and i,j are the bucket indices (0-index). For move types empty and fill, the 2nd bucket is ignored.

How: Each sequence of moves p can be encoded by an integer k using modular arithmetic. g is a recursive function that checks if the sequence p encoded by k will result in the target t. If so, that sequence is printed to stdout, and a Falsy value is returned.


Old solution using itertools.product

Python 3.8 (pre-release), 279 249 bytes

Whopping -30 thanks to @ovs's double product trick!



from itertools import*
P=product
a,t=eval(input())
for r in count():
 for p in P(*tee(P((0,1,2),R:=range(n:=len(a)),R),r)):
  c=[0]*n;[exec(["c[i]=0","c[i]=a[i]","x=min(a[j]-c[j],c[i]);c[i]-=x;c[j]+=x"][m])for m,i,j in p]
  if t in c:print(p);exit()


Try it online!

Slow, ugly and can probably be golfed more.

Input: from stdin, a,t where a is the list of bucket capacities, and t is the goal.
Output: to stdout, the optimal list of moves, each move has the form (m, i, j) where:


m is the move type 0,1,2 (empty, fill, pour)
i and j are the target buckets' indices (0-index).
The moves empty and fill only affects the 1st bucket i, and thus the irrelevant 2nd bucket j is set to an arbitrary value.
The move (2,i,j) pours the water from bucket i to bucket j.


How:
This program simply tries all possible sequence of move, in order of length.

To generate all sequence of r moves:


product((0,1,2), range(n), range(n)) generates a list of all possible moves by performing the Cartesian product between all move types 0,1,2, all values of i and all values of j.
tee(product(...), r) clones the move list into r lists.
product(*tee(...)) takes the Cartesian product ofrmove lists, which results in all possible sequence ofr` moves.


To perform a sequence of move p:


c[i]=0,c[i]=a[i], and x=min(a[j]-c[j],c[i]);c[i]-=x;c[j]+=x respectively handles emptying, filling, and pouring between bucket i and j. Note that pouring can handle i==j, which results in a no-op.
exec(["handle E", "handle F", "handle P"][m]) selects the correct handler for move type m.