The problem consists of two parts: (1) location 1, (2) the location of the first 0 after finding 1, so that 1 can be placed in a new position. The Prolog solution will reflect this. Your initial attempt is trying to process everything in one predicate, which, in my opinion, makes the task harder.
The base case is simple. It represents the smallest allowable move.
move([1,0], [0,1]).
Then recurrence cases. They provide at least 3 positions in the list, since a trivial case of two positions is already handled by the base case, and it sets a mutual exception in the rules to avoid redundant decisions.
% If we're at a 0 (space), keep going move([0,X1,X2|T], [0|R]) :- move([X1,X2|T], R). % If we see a 1, we can move it, or we can leave it alone and move on move([1,X1,X2|T], [0|R]) :- place([X1,X2|T], R). move([1,X1,X2|T], [1|R]) :- move([X1,X2|T], R). % Place the 1 at the first located 0 (space) place([0|T], [1|T]). place([1|T], [1|R]) :- place(T, R).
So, to determine the valid following positions from the starting position:
| ?- move([1,0,0,1,1,1,0],R). R = [0,1,0,1,1,1,0] ? a R = [1,0,0,0,1,1,1] R = [1,0,0,1,0,1,1] R = [1,0,0,1,1,0,1] (1 ms) no | ?-
You can also determine which starting positions will lead to a certain next position:
| ?- move(S, [1,0,0,1,0,1,1]). S = [1,0,0,1,1,0,1] ? a S = [1,0,0,1,1,1,0] S = [1,0,1,0,0,1,1] no
<h / "> This can also be done using DCG:
move([0, 1]) --> [1, 0]. move([0|R]) --> see(0), move(R). move([0|R]) --> see(1), place(R). move([1|R]) --> see(1), move(R). see(N), [X1, X2] --> [N, X1, X2]. place([1|T]) --> [0], seq(T). place([1|R]) --> [1], place(R). seq([]) --> []. seq([X|Xs]) --> [X], seq(Xs). | ?- phrase(move(R), [1,0,0,1,1,1,0]). R = [0,1,0,1,1,1,0] ? a R = [1,0,0,0,1,1,1] R = [1,0,0,1,0,1,1] R = [1,0,0,1,1,0,1] no | ?- phrase(move([1,0,0,1,0,1,1]), S). S = [1,0,0,1,1,0,1] ? a S = [1,0,0,1,1,1,0] S = [1,0,1,0,0,1,1] no