Ah, when trying to debug it, I found the problem (or at least, a problem).
In the move code, what is the "k" loop doing?
Inside the loop only i and j are used, and they are not changed (which is recommended btw, don't mess with loop variables if you want to keep your sanity).
So you run the same test and update 4 times. Why?
Maybe you should scale down first. Try a single row in a simple array int stones[4] first, and only move towards 0.
Figuring out the algorithm by hand is also a good way to understand if it works. Write the algorithm down on paper.
"Play" the algorithm by hand. Take another piece of paper, write a start situation at the top, and then mechanically follow what the algorithm says you should do.
Each time the "stones" array changes, write a new line with the new situation under the previous line. That way you can really see how it works.
It may seem like a stupid way to write an algorithm, but it really works, as you follow it every single step, so you can immediately see when it makes a wrong decision, and fix it by changing the algorithm, and trying again.
Computers are very fast, which is great when things work. However, they thus also make a big mess very fast. Untangling what in the zillion steps it did was not correct is a lot of work. By doing everything manually, you can check each step immediately, and immediately know what it does wrong.
A more computerized way to do this is by using a debugger, and step through your code, verifying it makes the right checks and makes the right changes. You should probably try both, and decide what works best for you.
Also, I'm not sure if I understand what you're saying by moving each stone individually all the way, Alberth.
Right now, you walk over each square, and locally decide for that square what the answer should be (by looking around it). That won't work in its current form. If you have a row "2 2 2 2", then the result should be "4 4", and not "8", right?. I don't see any code that prevents a "4" getting merged with another "4". In other words, you have to distinguish between values that have been merged before (like "4" in the example), and values that have not yet merged (like "2"). To me, that says your strategy of visiting each square and locally decide what to do may not the right solution.
Maybe you should instead decide what the result of a destination should be completely before you go to the next destination.
In the stones[] array, what would mean
first decide the new value of "stones[0]" completely, possibly updating stones[1], stones[2], and/or stones[3].
Next, decide the new value of stones[1], possibly updating stones[2], and/or stones[3].
Next, decide the new value of stones[2], possibly updating stones[3].
And then you're done (I think), no need to decide a new value for stones[3].
By doing each position completely, you avoid problems like double merging.