"Each food item gives 10 food. How can I find the shortest path that will allow me to collect 30 food",
so you need to collect 3 food items.
This is small enough that you shouldn't need to give up optimality.
- Compute shortest paths and distances from the starting location to each food location,
between each pair of food locations, and from each food location to each bin location.
The Floyd-Warshall algorithm can do that, but using Dijkstra's algorithm once with the starting
location as the initial node and once with each food tile as the initial node will probably
be faster, since your graph is sparse and all other pairwise information is not important. - Use the algorithm described by the following pseudo-code.
.
maxdist = 0 for tile0 in tiles: for tile1 in tiles: maxdist = max(maddest,distance(tile0,tile1)) dtobin = maxdist+1 for bin in bintiles: if distance(startingtile,bin) < dtobin: dtobin = distance(startingtile,bin) closestbin = bin if dtobin == maxdist+1: print("There are no reachable bins!") return [] path = shortestpath(startingtile,closestbin) L = distance(startingtile,closestbin) midpoint = path[round(L/2)] dtofood = maxdist+1 for food in foodtiles: if distance(midpoint,food) < dtofood: dtofood = distance(waypoint,food) food2 = food if dtofood == maxdist+1: print("There is no reachable food!") return [] partofpath = shortestpath(startingtile,food2) L = distance(startingtile,food2) waypoint = path[round(L/2)] dtofood = maxdist+1 for food in foodtiles: if distance(waypoint,food) < dtofood and food != food2: dtofood = distance(waypoint,food) food1 = food if dtofood == maxdist+1: print("There is only one reachable food item!") return [] partofpath = shortestpath(food2,closestbin) L = distnace(food2,closestbin) waypoint = path[round(L/2)] dtofood = maxdist+1 for food in foodtiles: if distance(waypoint,food) < dtofood and food != food2 and food != food1: dtofood = distance(waypoint,food) food3 = food if dtofood == maxdist+1: print("There are only two reachable food items!") return [] bestsofar = distance(startingtile,food1)+distance(food1,food2)+distance(food2,food3)+distance(food3,closestbin) waypointlists = [] for bin in bintiles: if bestsofar < distance(startingtile,bin): continue for food2 in foodtiles: if bestsofar < distance(startingtile,food2)+distance(food2,bin): continue for food1 in foodtiles: partial = distance(startingtile,food1)+distance(food1,food2) if bestsofar < partial+distance(food2,bin) or food2 == food1: continue for food3 in foodtiles: if bestsofar < partial+distance(food2,food3)+distance(food3,bin) or food3 == food2 or food3 == food1: continue currentpathlength = partial+distance(food2,food3)+distance(food3,bin) if currentpathlength < bestsofar: bestsofar = currentpathlength waypointlists = [[food1,food2,food3,bin]] else: waypointlists.append([food1,food2,food3,bin]) return waypointlists
(The continue syntax is described here.)
If waypointlists has more than one list, then choose
one of them according to whatever preference you have.
String together shortest paths [between startingtile and the initial waypoint]
and between consecutive waypoints. (Those shortest paths were found in step 1.)
If memory use is not a problem, then that algorithm can almost certainly be sped-up by
making lists of what tiles could possibly help and only trying those. If memory use is not a
problem, then that algorithm can probably be sped-up by trying things in a better order,
so that it will continue more often in the four final for loops, especially the outer loops.
Also, depending on cache behavior (something I have no clue about) and how often it continues
two lines afterward, computing partial might actually be slower than not doing so.