Given these facts
connection(philly,nyc,no).
connection(nyc,philly,no).
connection(philly,harrisburg,no).
connection(harrisburg,philly,no).
connection(paris,nyc,yes).
connection(nyc,paris,yes).
passport(harrisburg).
where a connection
has arguments from
, to
, passport needed
and these test cases
:- begin_tests(travel).
travel_test_case_generator( harrisburg ,harrisburg ,no ,[harrisburg] ).
travel_test_case_generator( harrisburg ,harrisburg ,yes ,[harrisburg] ).
travel_test_case_generator( harrisburg ,philly ,no ,[harrisburg,philly] ).
travel_test_case_generator( harrisburg ,philly ,yes ,[harrisburg,philly] ).
travel_test_case_generator( harrisburg ,nyc ,no ,[harrisburg,philly,nyc] ).
travel_test_case_generator( harrisburg ,nyc ,yes ,[harrisburg,philly,nyc] ).
travel_test_case_generator( harrisburg ,paris ,yes ,[harrisburg,philly,nyc,paris] ).
travel_test_case_generator( harrisburg ,paris ,no ,[harrisburg,philly,nyc,philly,harrisburg,passport,philly,nyc,paris] ).
travel_test_case_generator( philly ,philly ,no ,[philly] ).
travel_test_case_generator( philly ,philly ,yes ,[philly] ).
travel_test_case_generator( philly ,nyc ,no ,[philly,nyc] ).
travel_test_case_generator( philly ,nyc ,yes ,[philly,nyc] ).
travel_test_case_generator( philly ,paris ,yes ,[philly,nyc,paris] ).
travel_test_case_generator( philly ,paris ,no ,[philly,nyc,philly,harrisburg,passport,philly,nyc,paris] ).
travel_test_case_generator( nyc ,paris ,yes ,[nyc,paris] ).
travel_test_case_generator( nyc ,paris ,no ,[nyc,philly,harrisburg,passport,philly,nyc,paris] ).
test(001,[forall(travel_test_case_generator(Start,End,Passport,Expected_route))]) :-
route(Start,End,Passport,Route),
assertion( Route == Expected_route ).
:- end_tests(travel).
Here is the solution using prolog. This code was written as a proof of concept to see how to answer the question. It was not written to the specs of the question so if you know Prolog you will find the obvious places where it can be improved or doesn't implement an algorithm as expected.
route(Start,End,Passport,Route) :-
route(Start,End,Passport,[],Route_reversed),
reverse(Route_reversed,Route), !.
route(City,City,_,Route0,Route) :-
visit(City,Route0,Route).
route(A,C,yes,Route0,Route) :-
connection(A,B,_),
\+ member(B,Route0),
visit(A,Route0,Route1),
route(B,C,yes,Route1,Route).
route(A,C,no,Route0,Route) :-
connection(A,B,Need_passport),
\+ member(B,Route0),
(
(
Need_passport == yes,
\+ member(passport,Route0)
)
->
(
get_passport_shortest(A,Route1),
route(B,C,yes,[],Route2),
reverse(Route0,Route0_reversed),
append([Route0_reversed,[A],Route1,Route2],Route_reversed),
reverse(Route_reversed,Route)
)
;
(
visit(A,Route0,Route1),
route(B,C,no,Route1,Route)
)
).
route_no(A,A,no,Route,Route).
route_no(A,C,no,Route0,Route) :-
connection(A,B,no),
\+ member(B,Route0),
visit(B,Route0,Route1),
route_no(B,C,no,Route1,Route).
get_passport(A,Route) :-
passport(B),
route_no(A,B,no,[],Route1),
route_no(B,A,no,[],Route2),
reverse(Route1,Route1_reversed),
reverse(Route2,Route2_reversed),
append([Route1_reversed,[passport],Route2_reversed],Route).
visit(City,Route0,Route) :-
(
Route0 = [City|_]
->
Route = Route0
;
Route = [City|Route0]
).
get_passport_shortest(A,Shortest_route) :-
findall(Route,get_passport(A,Route),Routes),
select_shortest(Routes,Shortest_route).
select_shortest([H|T],Result) :-
length(H,Length),
select_shortest(T,Length,H,Result).
select_shortest([],_Current_length,Result,Result).
select_shortest([Item|T],Current_length0,Current_shortest0,Result) :-
length(Item,Item_length),
(
Item_length < Current_length0
->
(
Current_length = Item_length,
Current_shortest = Item
)
;
(
Current_length = Current_length0,
Current_shortest = Current_shortest0
)
),
select_shortest(T,Current_length,Current_shortest,Result).
When the test case are run
?- make.
% c:/so_question_159 (posted) compiled 0.00 sec, 0 clauses
% PL-Unit: travel ................ done
% All 16 tests passed
true.
All the test pass.
The reason the passport is in Harrisburg instead of Philly is that in testing the code, the code worked when the passport was in Philly. Then by adding Harrisburg and testing again a problem was uncovered in the code and fixed. If one changes passport(harrisburg).
to passport(philly).
the code will work but requires additional test cases.
Further questions posted in comments and moved here.
From grodzi
In your tests, the line (third from the end) philly, paris, no
, why do philly,nyc,philly, harrisbug...
when you can just do philly,harrisburg,philly...
to get the passport? Is it intended or some minor bug?
Nice to see someone is paying attention. That is no bug and that was one of the test that exposed the bug when the passport was in Harrisburg. The way I interpret the problem as stated by the OP, the travel case is just an easier to understand version of his real problem related to an dynamic FSA with login and logout. So knowing that you need the passport is not known until you try to do the travel from nyc
to paris
. At this point you need the passport if it is not in hand and so need travel back to harrisbug
to get it.
So yes that does look odd from a normal trip solver problem and we as humans can easily see the optimization, either because of experience, or superior reason ability, or peeking ahead and knowing that we need a passport to get to paris
, but the system does not know it needs a passport until it is needed. I can add more rules and more conditions to this, but at present there is only the passport. If however the OP adds more conditions then I will ask for a new question because this question should have been more specific.
From OP
Regarding conditions being several layers deep, how does your example show that?
It doesn't at the moment because there were no rules needing to do that. It was posed as a question for others who have or plan to answer this as it will be a choice they would have to make when writing the code.
From OP
Does your example with the password window attempt to see how FSM handles user error?
No, I only looked at your basic ideas in the question posted.
This question is referring to the OPs code posted at GitHub
References of value
Attribute Grammars (Wikipedia)
Automated planning and scheduling (Wikipedia) (Prolog example)
RosettaCode Dijkstra's algorithm
SLD Resolution
Tabled execution (SLG resolution)
Declarative Programming - 3: logic programming and Prolog