Search code examples
prolog

How to tell prolog if the function returns false then skip it?(15 puzzle game)


I have made a function which in it I call several functions, but when one of them returns false the main function stops and returns false. So is there a way to tell Prolog if the function returns false then skip it and check the rest?

In detail: I am trying to make the 15 puzzle game as a project, and I want to make function that gives me all the possible next moves.

I end up with calling all the previous functions that controls the blank tile.

next_move(Board, Moves):-
  swapup(Board,Result),
  swapdown(Board,Result),
  swapright(Board,Result),
  swapleft(Board,Result).

I want this function to return all the next possible moves

Here is the full code:

position([Tile|_], Tile, 0).
position([_|Tail], Tile, Index):-
  position(Tail, Tile, Index1),
  Index is Index1+1.

swap(Board,I,J,R) :-
   same_length(Board,R),
   append(BeforeI,[AtI|PastI],Board),
   append(BeforeI,[AtJ|PastI],Bs),
   append(BeforeJ,[AtJ|PastJ],Bs),
   append(BeforeJ,[AtI|PastJ],R),
   length(BeforeI,I),
   length(BeforeJ,J).

swapup(Board,Result):-
    position(Board,0,Index),
    Index \=0,
    Index \=1,
    Index \=2,
    Index \=3,
    Up is Index-4,
    swap(Board,Up,Index,Result).

swapdown(Board,Result):-
    position([],0,Index),
    Index \=12,
    Index \=13,
    Index \=14,
    Index \=15,
    Down is Index+4,
    swap(Board, Down, Index, Result).

swapright(Board,Result):-
    position([],0,Index),
    Index \=3,
    Index \=7,
    Index \=11,
    Index \=15,
    Right is Index+1,
    swap(Board, Right, Index, Result).

swapleft(Board,Result):-
    position([],0,Index),
    Index \=0,
    Index \=4,
    Index \=8,
    Index \=12,
    Left is Index-1,
    swap(Board, Left, Index, Result).

swap(Board,Result) :- swapup(Board,Result).
swap(Board,Result) :- swapdown(Board,Result).
swap(Board,Result) :- swapright(Board,Result).
swap(Board,Result) :- swapleft(Board,Result).

next_move(Board,Moves) :- findall(Result,swap(Board,Result),Moves).

Solution

  • Here's a simple program:

    a.
    b :- fail.
    c.
    
    g :- a, b, c.
    

    I can ask if g succeeds:

    ?- g.
    false.
    

    It does not.

    I can solve this two ways.

    (1)

    g :- a, b, c.
    g :- a, c.
    

    (2)

    g :- a, (b; true), c.
    

    Either way, g succeeds now:

    ?- g.
    true.
    

    Update

    This is the code you've posted:

    next_move(Board, Moves) :-
        swapup(Board,Result),
        swapdown(Board,Result),
        swapright(Board,Result),
        swapleft(Board,Result).
    

    On the LHS you have a variable Moves that is not on the RHS. Moves can never be unified and will always remain a variable.

    Let's ignore that for a minute and look at the swap predicates.

    If I assume that Result is a valid move given the current state of the Board then what your code is saying next_move(Board, Moves) succeeds when each of the swap predicates succeed and that can't happen unless the move you get from each of the swap predicates is the same.

    Once swapup(Board,Result) succeeds it will have unified Result with the "swap up" move. Then you ask if swapdown(Board,Result) succeeds. Well, Result isn't a variable anymore so you're asking if the Result you get from swapdown is the same as the Result you get from swapup. I'm guessing it's not.

    It's possible that you need something like this:

    next_move(Board,[U,D,R,L]):-
        swapup(Board,U),
        swapdown(Board,D),
        swapright(Board,R),
        swapleft(Board,L).
    

    But that's not clear because I don't know what value you get when your open space on your board is already at an edge.

    It's probably more likely that you need this:

    swap(Board,Result) :- swapup(Board,Result).
    swap(Board,Result) :- swadown(Board,Result).
    swap(Board,Result) :- swapright(Board,Result).
    swap(Board,Result) :- swapleft(Board,Result).
    
    next_move(Board,Moves) :- findall(Result,swap(Board,Result),Moves).
    

    But all of this is guessing without seeing your full code.


    Here's your code working now. There were a number of issues.

    position/3 just simply didn't work.

    Here's a version that does:

    position(List,Element,Index) :-
        position(List,Element,0,Index).
    
    position([Element|_],Element,Index,Index).
    position([_|Tail],Element,Counter,Index) :-
        Next is Counter + 1,
        position(Tail,Element,Next,Index).
    

    You were always calling swap/4 with the same order of the two indices. You have to make sure they go in in ascending order. swap/4 didn't work in any case. Here's the new version:

    swap(BoardIn,I,J,BoardOut) :-
        position(BoardIn,X,I), % find the value `X` at position `I`
        position(BoardIn,Y,J), % find the value `Y` at position `J`
        append(Left,[X|MiddleEnd1],BoardIn), % find the list that is `Left` of `X`
        append(Middle,[Y|End],MiddleEnd1), % find the `Middle` that is left of `Y` and the list `End` that is to the right of `Y`
        append(Middle,[X|End],MiddleEnd2), % put `X` between `Middle` and `End`.
        append(Left,[Y|MiddleEnd2],BoardOut). % put the `Y` between `Left` & `Middle`.
    

    I cleaned up the swap*/2 predicates.

    swapup(BoardIn,BoardOut):-
        position(BoardIn,0,Index),
        Up is Index - 4,
        Up >= 0,
        swap(BoardIn,Up,Index,BoardOut).
    
    swapdown(BoardIn,BoardOut):-
        position(BoardIn,0,Index),
        Down is Index + 4,
        Down =< 15,
        swap(BoardIn,Index,Down,BoardOut).
    
    swapright(BoardIn,BoardOut):-
        position(BoardIn,0,Index),
        Index mod 4 =\= 3,
        Right is Index + 1,
        swap(BoardIn,Index,Right,BoardOut).
    
    swapleft(BoardIn,BoardOut):-
        position(BoardIn,0,Index),
        Index mod 4 =\= 0,
        Left is Index - 1,
        swap(BoardIn,Left,Index,BoardOut).
    
    swap(BoardIn,BoardOut) :- swapup(BoardIn,BoardOut).
    swap(BoardIn,BoardOut) :- swapdown(BoardIn,BoardOut).
    swap(BoardIn,BoardOut) :- swapright(BoardIn,BoardOut).
    swap(BoardIn,BoardOut) :- swapleft(BoardIn,BoardOut).
    

    And finally, here's the next_move/2 predicate:

    next_move(BoardIn,Moves) :-
        length(BoardIn,16),
        position(BoardIn,0,_),
        findall(BoardOut,swap(BoardIn,BoardOut),Moves).
    

    Now I can call this query:

    ?- next_move([1,0,2,3,4,5,6,7,8,9,10,11,12,13,14,15],Result),write(Result).
    

    I get this result:

    [[1,5,2,3,4,0,6,7,8,9,10,11,12,13,14,15],[1,2,0,3,4,5,6,7,8,9,10,11,12,13,14,15],[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]]