Jeśli dobrze rozumiem to nasze algorytmy są identyczne. U mnie też szukam sąsiadujących z pustą, też wybieram jedną z nich, a przesunięcie to poprostu zamiana pustej z wybraną. Więc chyba czegoś nie zrozumiałem
EDIT:
teraz rozumiem, chodzi o realizacje znajdowania sąsiadujących. U mnie jest niepotrzebna pętla, już sie biore za poprawe tego i zobacze jak to będzie wtedy śmigało.
EDIT2:
została już tylko jedna pętla w klasie Puzzle, do renderowania. Znajdowanie pustej komórki jest niepotrzebne, przecież można sobie zapisywać gdzie ona jest po każdym przesunięciu. A znajdywanie rozwiązałem tak jak powiedziałeś, zapisując sąsiadów tak jak w isMoveable(), z tym że z drobnym sprwadzeniem czy nie wychodze poza plansze (jednowarunkowe ify). I teraz w zasadzie przyglądając się procesowi przesuwania (render() + alert() - wygląda jak animacja

) zauważyłem że trzeba jeszcze zrobić zabezpieczenie przed cofaniem się przesunięć. To przez to nawet przy dużej ilości iteracji plansza potrafi być niewymieszana.
EDIT3:
W sumie to niech sie cofa, efekt jest ten sam

Tak czy siak trzeba zrobić pow(size, 4) powtórzeń

Ale teraz to już śmiga tak szybko, że nie ma sie o co martwić

Na FTP są ostateczne poprawki do metody shuffle(). Uff.