Started out with just a simple BFS search, which was taking up to 10s of seconds for some of the cases. I added heuristics, but these didn't immediately improve the runtime (overhead of heap, etc.). I added some checks for invalid states to return large values in these cases, and noticed that this improved and sometimes trumped the heuristic value. Simple heuristic only prunes out about 0.5 of the nodes. Added the abstraction heuristic. This started performing better when using max of the number of moves for an individual piece. Invalid states or other heuristic wasn't helping here. Then added the condensed states, so you don't include the moves that aren't pushing a box. This with abstract heuristic was performing best (in terms of runtime). Using abstractions with multiple pieces would expand a few less nodes, but was much slower. After changing the unique id from a vector of ints that was converted to a string, to a single number, the runtime improved about 2.5-3x and was soon under 2s. For cases where we would overflow a 32-bit integer, we still have to fall back to the other implementation. Seems 64-bit integers are not supported in javascript--which is lame. Some observations. The abstract heuristic was typically about 10x less nodes expanded than a bfs, and the condensed version was about 10x less than this. This tended to hold regardless of the heuristic. On the way home, was playing around with a better mapping of invalid states. This didn't seem to give huge gains in the abstract case, but does help out the other simple heuristics and when added into the search directly gives a huge boost over the BFS. Some of the BFS would not complete on cases with lots of blocks. (e.g., "microa level:7"). However, these are still the cases where the abstraction still performs best (but the margin is smaller): "microa level: 7" name elapsed time expanded sol.length abs+cond 0.534 2517 27 abs 0.973 11700 27 simple+cond 0.551 3213 27 simple 0.97 16518 27 bfs+conde 0.594 3963 27 bfs 0.939 20264 27 "microc level: 19" name elapsed time expanded sol.length abs+cond 2.553 9335 39 abs 5.269 50426 39 simple+cond 1.821 9730 39 simple 3.963 56553 39 bfs+conde 2.154 12324 39 bfs 4.399 75512 39 The reason that the invalid states didn't help the abstracted heuristic, and the reason that the abstracted heuristic was performing well was because it was basically already finding the invalid states through its single block searches which would end up in a state where no solution was possible and terminate and cache a large heuristic value (solution length < 0). The level which took me several attempts to solve manually, the one that got me thinking about doing this at all, was "microc level:8". After adding the pruning of invalid nodes (bfs with condensed moves is now the fastest, and the heuristic only expands a few less nodes): name elapsed time expanded abstract+condense 1.524 28494 abstract 3.921 156623 simpleheur+condense 0.937 28686 simpleheur 2.535 157862 bfs+condense 0.769 28706 bfs 1.993 158013 Before adding pruning everywhere: name elapsed time expanded sol.length abstract+condense 1.494 28505 165 abstract 3.133 156721 165 simpleheur+condense 1.255 44239 165 simpleheur 3.105 235409 165 bfs+condense 3.51 183370 192 bfs 9.593 983861 165 And using full pruning in the heuristic only name elapsed time expanded sol.length abstract+condense 1.408 28505 165 abstract 3.029 156721 165 simpleheur+condense 0.758 28688 165 simpleheur 2.127 157870 165 bfs+condense 3.607 183370 192 bfs 9.402 983861 165 Some other interesting timings: microc level:12 name elapsed time expanded sol.length abs+cond 0.341 9638 65 abs 1.124 52603 65 simple+cond 0.276 10845 65 simple 0.786 57751 65 bfs+conde 0.266 11642 65 bfs 0.596 61080 65 microc level:14 name elapsed time expanded sol.length abs+cond 0.351 3000 26 abs 0.47 13495 26 simple+cond 0.213 3043 26 simple 0.352 14833 26 bfs+conde 0.66 8990 26 bfs 1.108 48850 26 microc level:20 name elapsed time expanded sol.length abs+cond 0.05 1612 52 abs 0.162 9425 52 simple+cond 0.043 2108 52 simple 0.139 11958 52 bfs+conde 0.058 2475 52 bfs 0.14 15449 52 Conclusion. The heuristics all kind of suck, or are too expensive to compute and don't give any benefit. Adding condensed moves and avoiding terminal states early (which is something you do when you are playing) is good. Most of the problems are not that hard.