Search code examples
parsingprologdcg

prolog, robot move with grammar + parser


I'm stuck with some grammar exercise in Prolog. This is my first time with grammar (in any language). I need to create a parser that will be counting robot's destination coordinates as well as the direction (makred as one of: n,s,w,e) it is currently pointed to.

Possible moves are: [go,10] (or any number), [turn, left], [turn, right]. It was easy (I think..) to write parser that recognizes the correct sentence, like:[go,10,turn,right,go,10]:

robot_p-->pol.

pol-->[go],num.
pol-->[go],num,pol.
pol-->[turn],[left].
pol-->[turn],[left],pol.
pol-->[turn],[right].
pol-->[turn],[right],pol.
num-->[D],{number(D)}.
robot(L):-robot_p(L,[]).

Yet now, I have no idea, how could I accumulate every single step, so for the sentence I've given as an example, the result would be [10,10,e]. I think I should write something like:

robot_p(result)-->pol(result).

But if so, how would it look then? I could change num to:

num(D)-->[D],{number(D)}.

Next, this would start:

pol([X,Y,N])-->[go],num(D),{N==n;//here I don't know what next}.

Yet, I'm not sure if it's good approach and if so, what to do now.

EDIT:

I've forgotten to say, that I need to pass starting coordinates. So for [0,0,n] and moves [go,10,turn,right,go,10] the result is [10,10,e].

EDIT2:

Big thanks to @BretC for help. I've followed your tips and created the version that was satisfying by me. Maybe it will help someone:)

pol([X])-->[go],num(X).
pol([X| T])-->[go],num(X),pol(T).
pol([left])-->[turn],[left].
pol([left | T])-->[turn],[left],pol(T).
pol([right])-->[turn],[right].
pol([right | T])-->[turn],[right],pol(T).

commands(L,V):-pol(V,L,[]).
move([X,Y,D],V,[X1,Y,D]):-D==n,X1 is X+V.
move([X,Y,D],V,[X1,Y,D]):-D==s,X1 is X-V.
move([X,Y,D],V,[X,Y1,D]):-D==w,Y1 is Y-V.
move([X,Y,D],V,[X,Y1,D]):-D==e,Y1 is Y+V.
rotate(D,P,D1):-P==left,D==n,D1=w.
rotate(D,P,D1):-P==left,D==s,D1=e.
rotate(D,P,D1):-P==left,D==w,D1=s.
rotate(D,P,D1):-P==left,D==e,D1=n.
rotate(D,P,D1):-P==right,D==n,D1=e.
rotate(D,P,D1):-P==right,D==s,D1=w.
rotate(D,P,D1):-P==right,D==w,D1=n.
rotate(D,P,D1):-P==right,D==e,D1=s.
robot_H(D,[],D).
robot_H([X,Y,D],[H|T],K):-number(H),move([X,Y,D],H,ND),robot_H(ND,T,K).
robot_H([X,Y,D],[H|T],K):- \+number(H),rotate(D,H,D1),robot_H([X,Y,D1],T,K).

robot(S,P,K):-commands(P,P1),robot_H(S,P1,K).
num(X)-->[X],{number(X)}.

So, for instance:

?- robot([0,0,n],[go, 10, turn, left,turn,left, go, 20],K).
K = [-10, 0, s]


Solution

  • Not sure if this helps, but I would use the DGC to build a list of clauses representing the natural language and then process those clauses.

    For example, if you add the following arguments to your grammar...

    pol([move(X)])-->[go],num(X).
    pol([move(X) | T])-->[go],num(X),pol(T).
    pol([turn(left)])-->[turn],[left].
    pol([turn(left) | T])-->[turn],[left],pol(T).
    pol([turn(right)])-->[turn],[right].
    pol([turn(right) | T])-->[turn],[right],pol(T).
    num(D)-->[D],{number(D)}.
    

    ...now you can get a list of 'commands', for example...

    4 ?- pol(X, [go,10,turn,left,go,10], []).
    X = [move(10), turn(left), move(10)] .
    

    So that's the parsing bit, now you need to write clauses to process the simulation, for example...

    % Parses grammar and processes commands
    robot(SENTANCE, XOut, YOut) :-
        % Parse the sentance
        pol(COMMANDS, SENTANCE, []),
        % Process the commands
        process(COMMANDS, north, 0, 0, XOut, YOut).
    
    % process(COMMAND_LIST, HEADING, XSTART, YSTART, XOUT, YOUT)
    
    % Case 1: No commands to process, return current position
    process([], _, X, Y, X, Y).
    % Case 2: Process a single command then process the rest of the commands
    process([COMMAND | T], Heading, XIn, YIn, XOut, YOut) :-
        do_command(COMMAND, Heading, XIn, YIn, HeadingTmp, XTmp, YTmp),
        process(T, HeadingTmp, XTmp, YTmp, XOut, YOut).
    
    % do_command(COMMAND, HEADING_START, XSTART, YSTART, HEADING_OUT, XOUT, YOUT).
    % Move in current direction command
    do_command(move(N), Heading, X, Y, Heading, XOut, YOut) :-
        x_delta(Heading, N, XDelta),
        y_delta(Heading, N, YDelta),
        XOut is X + XDelta,
        YOut is Y + YDelta.
    % Turn command  
    do_command(turn(Dir), HeadingIn, X, Y, HeadingOut, X, Y) :-
        turn(HeadingIn, Dir, HeadingOut).
    
    % Utility clauses to help with deltas + turns
    x_delta(north, _, 0).
    x_delta(south, _, 0).
    x_delta(east, X, X).
    x_delta(west, X, Y) :- Y is -X.
    
    y_delta(north, X, X).
    y_delta(south, X, Y) :- Y is -X.
    y_delta(east, _, 0).
    y_delta(west, _, 0).
    
    turn(north, right, east).
    turn(east, right, south).
    turn(south, right, west).
    turn(west, right, north).
    
    turn(north, left, west).
    turn(east, left, north).
    turn(south, left, east).
    turn(west, left, south).
    

    Example output...

    2 ?- robot([go, 10, turn, left, go, 11], X, Y).
    X = -11,
    Y = 10