Prolog trace output to search tree - debugging

I'm trying to translate the trace output from prolog to a search tree, but I'm not sure if it's correct.
Any help on how to make a search tree from the trace information is appreciated.
Database/Input:
rara([X|L],A,M):-
rara(L,[Y|A],M).
rara([M|L],L,[M]).
rara(L,M):-
rara(L,[],M).
Query:
rara([a,b,c,d],X).
Trace output:
Call: (8) rara([a, b, c, d], _G922) ? creep
Call: (9) rara([a, b, c, d], [], _G922) ? creep
Call: (10) rara([b, c, d], [_G1043], _G922) ? creep
Call: (11) rara([c, d], [_G1046, _G1043], _G922) ? creep
Call: (12) rara([d], [_G1049, _G1046, _G1043], _G922) ? creep
Call: (13) rara([], [_G1052, _G1049, _G1046, _G1043], _G922) ? creep
Fail: (13) rara([], [_G1052, _G1049, _G1046, _G1043], _G922) ? creep
Redo: (12) rara([d], [_G1049, _G1046, _G1043], _G922) ? creep
Fail: (12) rara([d], [_G1049, _G1046, _G1043], _G922) ? creep
Redo: (11) rara([c, d], [_G1046, _G1043], _G922) ? creep
Fail: (11) rara([c, d], [_G1046, _G1043], _G922) ? creep
Redo: (10) rara([b, c, d], [_G1043], _G922) ? creep
Fail: (10) rara([b, c, d], [_G1043], _G922) ? creep
Redo: (9) rara([a, b, c, d], [], _G922) ? creep
Fail: (9) rara([a, b, c, d], [], _G922) ? creep
Fail: (8) rara([a, b, c, d], _G922) ? creep
Current search tree:
rara([a,b,c,d],X).
| |
rara([a, b, c, d], _G922) rara([b, c, d], [_G1043], _G922)
X = _G922 |
rara([c, d], [_G1046, _G1043], _G922)
|
rara([d], [_G1049, _G1046, _G1043], _G922)
|
rara([], [_G1052, _G1049, _G1046, _G1043], _G922)

Related

Why prolog fails to find a route to a vertices (in graph)?

I have a question to you:
I have an exercise that says:
Let there be given edges in the directed graph arc (a, b), arc (b, c), arc (a, d), arc (d, e), arc (d, f), arc (f, a) and arc (f, g). Test go / 2, go1 / 3 predicates.
I need to know why Prolog answers false for the following query:
? - go1 (a, b, X).
I have a graph illustrated below and a code pasted below.
The code is here:
arc(a,b).%these are the edges.
arc(b,c).
arc(a,d).
arc(d,e).
arc(d,f).
arc(f,a).
arc(f,g).
go(X,X).
go(X,Y):-arc(X,Z),go(Z,Y).
% without arc(f,a):
% yes ?- go(a,c).
% no ?- go(d,c).
% no ?- go(f,a).
% yes ?- go(a,g).
%
% with arc(f,a):
% yes ?- go(a,c).
% out of local stack ?- go(a,g).
member1(H,[H|_]).
member1(H,[_|T]):-member1(H,T).
go1(X,X,_).
go1(X,Y,T):-arc(X,Z),not(member1(Z,[X|T])),go1(Z,Y,[X|T]).
% with arc(f,a):
% yes ?- go1(a,c,[]).
% yes ?- go1(a,g,[]).
So guys, please help me to figure out why it happens? Why does Prolog answer false for the following query:
? - go1 (a, b, X).
I've tried to trace the query and I've gotten the answer that is below:
[trace] ?- go1(a,b,X).
Call: (8) go1(a, b, _982) ? creep
Call: (9) arc(a, _1202) ? creep
Exit: (9) arc(a, b) ? creep
^ Call: (9) not(member1(b, [a|_982])) ? creep
Call: (10) member1(b, [a|_982]) ? creep
Call: (11) member1(b, _982) ? creep
Exit: (11) member1(b, [b|_1206]) ? creep
Exit: (10) member1(b, [a, b|_1206]) ? creep
^ Fail: (9) not(user:member1(b, [a|_982])) ? creep
Redo: (9) arc(a, _1202) ? creep
Exit: (9) arc(a, d) ? creep
^ Call: (9) not(member1(d, [a|_982])) ? creep
Call: (10) member1(d, [a|_982]) ? creep
Call: (11) member1(d, _982) ? creep
Exit: (11) member1(d, [d|_1206]) ? creep
Exit: (10) member1(d, [a, d|_1206]) ? creep
^ Fail: (9) not(user:member1(d, [a|_982])) ? creep
Fail: (8) go1(a, b, _982) ? creep
false.
Well, what I don't understand is why we have here the answer below:
Exit: (11) member1(b, [b|_1206]) ? creep
Exit: (10) member1(b, [a, b|_1206]) ? creep
^ Fail: (9) not(user:member1(b, [a|_982])) ? creep
Why does the Prolog not recognize that there is an edge from a to b? Why does it say that b is in the list that there is only [a] after all. Why does this call fail there? What's the reason of the query failure?
You pass it a free variable X, so that means that the:
member(b, [a|X]).
call will succeed, indeed:
?- member(b, [a|X]).
X = [b|_2142] ;
X = [_2140, b|_2148] ;
X = [_2140, _2146, b|_2154]
Now not(member(Z, [X|T])) will succeed, given that the search of member(Z, [X|T]) fails, but here it will always succeed, since a free variable can always be grounded to a list that contains Z, hence it fails.
If you call it with:
go1 (a, b, [])
it will succeed, since then member(b, [a]) will indeed fail.
You can define a predicate that will reconstruct the path with:
go1(A, B, P) :-
go1(A, B, P, []).
go1(A, A, [A], _).
go1(A, C, [A|T], V) :-
arc(A, B),
\+ member(B, [A|V]),
go1(B, C, T, [A|V]).
and thus call it with go1(A, B, P) to obtain the path P.

confusion in how backtracking works given query with member predicate

Suppose i invoke the query member(a, [c,b,a,y]), the output is -
true ;
false.
How come we have "another solution"? Why we dont finish at true.?
Here's the trace:
%% [trace] ?- member(a,[c,b,a,y]).
%% Call: (6) member(a, [c, b, a, y]) ?
%% Call: (7) member(a, [b, a, y]) ?
%% Call: (8) member(a, [a, y]) ?
%% Exit: (8) member(a, [a, y]) ?
%% Exit: (7) member(a, [b, a, y]) ?
%% Exit: (6) member(a, [c, b, a, y]) ?
%% true ;
%% Redo: (8) member(a, [a, y]) ?
%% Call: (9) member(a, [y]) ?
%% Call: (10) member(a, []) ?
%% Fail: (10) member(a, []) ?
%% Fail: (9) member(a, [y]) ?
%% Fail: (8) member(a, [a, y]) ?
%% Fail: (7) member(a, [b, a, y]) ?
%% Fail: (6) member(a, [c, b, a, y]) ?
%% false.
I don't understand how can we backtrack to (8) and why?
member(X,[X|_]).
member(X,[Y|T]) :- member(X,T).
Let's re-write it as the nearly-equivalent one-liner,
membr(X,[A|B]):- ( X = A ; membr(X,B) ).
Tracing it can be a bit clearer:
[trace] 5 ?- trace, membr(a, [c,b,a,y]). X=a
Call: (8) membr(a, [c, b, a, y]) ? creep [A8|B8]=[c,b,a,y]
Call: (9) a=c ? creep X=A8 ?
Fail: (9) a=c ? creep false
Redo: (8) membr(a, [c, b, a, y]) ? creep membr(X,B8) ?
Call: (9) membr(a, [b, a, y]) ? creep [A9|B9]=B8
Call: (10) a=b ? creep X=A9 ?
Fail: (10) a=b ? creep false
Redo: (9) membr(a, [b, a, y]) ? creep membr(X,B9) ?
Call: (10) membr(a, [a, y]) ? creep [A10|B10]=B9
Call: (11) a=a ? creep X=A10 ?
Exit: (11) a=a ? creep true
Exit: (10) membr(a, [a, y]) ? creep success!
Exit: (9) membr(a, [b, a, y]) ? creep success!
Exit: (8) membr(a, [c, b, a, y]) ? creep success!
true ;
Redo: (10) membr(a, [a, y]) ? creep membr(X,B10) ?
Call: (11) membr(a, [y]) ? creep [A11|B11]=B10
Call: (12) a=y ? creep X=A11 ?
Fail: (12) a=y ? creep .........
Redo: (11) membr(a, [y]) ? creep .........
Call: (12) membr(a, []) ? creep
Fail: (12) membr(a, []) ? creep
Fail: (11) membr(a, [y]) ? creep
Fail: (10) membr(a, [a, y]) ? creep
Fail: (9) membr(a, [b, a, y]) ? creep
Fail: (8) membr(a, [c, b, a, y]) ? creep
false.
There's clearly a choice point waiting, after the first success is reached:
(10) membr(X,[A|B]):- ( X = A ; membr(X,B) ).
and after the success is reported, with all the frames still there on the stack, the top -- 10th -- frame is retried from the point where it has stopped, having succeeded:
(10) membr(X,[A|B]):- ( X = A ; membr(X,B) ).
The chain of exits is provisional: we've only traced back up the stack in order to be able to report the found value, as its bits and pieces are stored in the frames leading to the successful 11th frame (with X=a, A10=a, X=A10). The frames were not eliminated, for if they were, we'd only ever could report only the one first found value. But Prolog wants to report all of them.

Prolog list recursion

The goal of my predicate is:
?- line_terminal_stations(east_london, StartsAt, EndsAt).
StartsAt = shoreditch
EndsAt = new_cross
Below is what I have so far, the recursion works as expected and progressively creates a list of stations on the line.
line_terminal_stations(LineName, StationX, StationY):-
next_station(LineName, StationX, StationY, []).
next_station(LineName, StationX, StationY, V) :-
link(StationX, StationY, LineName),
next_station(LineName, StationY, _, [StationX | V]).
However once the final station has been found the predicate fails and begins to 'undo' the list. Whereas when link/3 fails, i want to end the recursion so i can extract the first and last station of the list.
Examples of link/3:
link(shoreditch, whitechapel, east_london).
link(whitechapel, shadwell, east_london).
Example of run-through:
line_terminal_stations(east_london, StartsAt, EndsAt).
Redo: (9) link(_G3031, _G3032, east_london) ? creep
Exit: (9) link(whitechapel, shadwell, east_london) ? creep
Call: (9) next_station(east_london, shadwell, _G3128, [whitechapel]) ? creep
Call: (10) link(shadwell, _G3127, east_london) ? creep
Exit: (10) link(shadwell, wapping, east_london) ? creep
Call: (10) next_station(east_london, wapping, _G3131, [shadwell, whitechapel]) ? creep
Call: (11) link(wapping, _G3130, east_london) ? creep
Exit: (11) link(wapping, rotherhithe, east_london) ? creep
Call: (11) next_station(east_london, rotherhithe, _G3134, [wapping, shadwell, whitechapel]) ? creep
Call: (12) link(rotherhithe, _G3133, east_london) ? creep
Exit: (12) link(rotherhithe, surrey_docks, east_london) ? creep
Call: (12) next_station(east_london, surrey_docks, _G3137, [rotherhithe, wapping, shadwell, whitechapel]) ? creep
Call: (13) link(surrey_docks, _G3136, east_london) ? creep
Exit: (13) link(surrey_docks, new_cross_gate, east_london) ? creep
Call: (13) next_station(east_london, new_cross_gate, _G3140, [surrey_docks, rotherhithe, wapping, shadwell, whitechapel]) ? creep
Call: (14) link(new_cross_gate, _G3139, east_london) ? creep
Fail: (14) link(new_cross_gate, _G3139, east_london) ? creep
Fail: (13) next_station(east_london, new_cross_gate, _G3140, [surrey_docks, rotherhithe, wapping, shadwell, whitechapel]) ? creep
Redo: (13) link(surrey_docks, _G3136, east_london) ? creep
Exit: (13) link(surrey_docks, new_cross, east_london) ? creep
Call: (13) next_station(east_london, new_cross, _G3140, [surrey_docks, rotherhithe, wapping, shadwell, whitechapel]) ? creep
Call: (14) link(new_cross, _G3139, east_london) ? creep
Fail: (14) link(new_cross, _G3139, east_london) ? creep
Fail: (13) next_station(east_london, new_cross, _G3140, [surrey_docks, rotherhithe, wapping, shadwell, whitechapel]) ? creep
Fail: (12) next_station(east_london, surrey_docks, _G3137, [rotherhithe, wapping, shadwell, whitechapel]) ? creep
Fail: (11) next_station(east_london, rotherhithe, _G3134, [wapping, shadwell, whitechapel]) ? creep
Fail: (10) next_station(east_london, wapping, _G3131, [shadwell, whitechapel]) ? creep
Fail: (9) next_station(east_london, shadwell, _G3128, [whitechapel]) ? creep
One approach with minimum code changes would be:
link(shoreditch, whitechapel, east_london).
link(whitechapel, shadwell, east_london).
line_terminal_stations(LineName, First, Last):-
next_station(LineName, _, _, [], Stations),
Stations = [Last|_],
last(Stations, First).
next_station(LineName, StationX, StationY, V, [StationX|V]) :-
\+ link(StationX, StationY, LineName).
next_station(LineName, StationX, StationY, V, Stations) :-
link(StationX, StationY, LineName),
next_station(LineName, StationY, _, [StationX | V], Stations).
Test run:
[debug] ?- line_terminal_stations(east_london, StartsAt, EndsAt).
StartsAt = shoreditch,
EndsAt = shadwell
But as it stands the link/3 requires to be in the right order to first find the true startstation. I.e you can backtrack and find different start-station:
[debug] ?- line_terminal_stations(east_london, StartsAt, EndsAt).
StartsAt = shoreditch,
EndsAt = shadwell ;
StartsAt = whitechapel,
EndsAt = shadwell
But you need recursion?
If the line isn't a ring, you can simply impose that StartAt is a starting point, but not an ending point, and that EndAt is a ending point, but not a starting point.
I mean
line_terminal_stations(Line, StartsAt, EndsAt) :-
link(StartsAt, _, Line),
\+ link(_, StartsAt, Line),
link(_, EndsAt, Line),
\+ link(EndsAt, _, Line).

Definition of a path/trail/walk

Many predicates define some kind of an acyclic path built from edges defined via a binary relation, quite similarly to defining transitive closure. A generic definition is thus called for.
Note that the notions defined in graph theory do not readily match what is commonly expected. Most notably, we are not interested in the edges' names.
Worse, also graph theory has changed a bit, introducing the notion of walk, noting
Traditionally, a path referred to what is now usually known as an open walk. Nowadays, when stated without any qualification, a path is usually understood to be simple, meaning that no vertices (and thus no edges) are repeated. (The term chain has also been used to refer to a walk in which all vertices and edges are distinct.)
So my question is: How to name and define this functionality?
What I have done so far is to define:
path(Rel_2, Path, X0,X)
The first argument has to be the continuation of the relation which is an incomplete goal that lacks two further arguments. Then comes either the Path or the pair of vertices.
Example usage
n(a, b).
n(b, c).
n(b, a).
?- path(n,Xs, a,X).
Xs = [a], X = a
; Xs = [a, b], X = b
; Xs = [a, b, c], X = c
; false.
Implementation
:- meta_predicate(path(2,?,?,?)).
:- meta_predicate(path(2,?,?,?,+)).
path(R_2, [X0|Ys], X0,X) :-
path(R_2, Ys, X0,X, [X0]).
path(_R_2, [], X,X, _).
path(R_2, [X1|Ys], X0,X, Xs) :-
call(R_2, X0,X1),
non_member(X1, Xs),
path(R_2, Ys, X1,X, [X1|Xs]).
non_member(_E, []).
non_member(E, [X|Xs]) :-
dif(E,X),
non_member(E, Xs).
How about defining path/4 like this?
path(R_2, Xs, A,Z) :- % A path `Xs` from `A` to `Z` is ...
walk(R_2, Xs, A,Z), % ... a walk `Xs` from `A` to `Z` ...
all_dif(Xs). % ... with no duplicates in `Xs`.
To aid universal termination, we swap the two goals in above conjunction ...
path(R_2, Xs, A,Z) :-
all_dif(Xs), % enforce disequality ASAP
walk(R_2, Xs, A,Z).
... and use the following lazy implementation of all_dif/1:
all_dif(Xs) :- % enforce pairwise term inequality
freeze(Xs, all_dif_aux(Xs,[])). % (may be delayed)
all_dif_aux([], _).
all_dif_aux([E|Es], Vs) :-
maplist(dif(E), Vs), % is never delayed
freeze(Es, all_dif_aux(Es,[E|Vs])). % (may be delayed)
walk/4 is defined like path/4 and path/5 given by the OP:
:- meta_predicate walk(2, ?, ?, ?).
walk(R_2, [X0|Xs], X0,X) :-
walk_from_to_step(Xs, X0,X, R_2).
:- meta_predicate walk_from_to_step(?, ?, ?, 2).
walk_from_to_step([], X,X, _).
walk_from_to_step([X1|Xs], X0,X, R_2) :-
call(R_2, X0,X1),
walk_from_to_step(Xs, X1,X, R_2).
IMO above path/4 is simpler and more approachable, particularly for novices. Would you concur?
I want to focus on naming the predicate.
Unlike maplist/2,
the argument order isn't of primary importance here.
The predicate name should make the meaning of the respective arguments clear.
So far, I like path_from_to_edges best, but it has its pros and cons, too.
path_from_to_edges(Path,From,To,Edges_2) :-
path(Edges_2,Path,From,To).
Let's pick it apart:
pro: path is a noun, it cannot be mis-read a verb. To me, a list of vertices is implied.
pro: from stands for a vertex, and so does to.
con: edges is somewhat vague, but using lambdas here is the most versatile choice.
con: According to Wikipedia, a path is a trail in which all vertices (except possibly the first and last) are distinct. So that would need to be clarified in the description.
Using lambdas for a lists of neighbor vertices Ess:
?- Ess = [a-[b],b-[c,a]],
From = a,
path_from_to_edges(Path,From,To,\X^Y^(member(X-X_neibs,Ess),member(Y,X_neibs))).
Ess = [a-[b],b-[c,a]], From = a, To = a, Path = [a]
; Ess = [a-[b],b-[c,a]], From = a, To = b, Path = [a,b]
; Ess = [a-[b],b-[c,a]], From = a, To = c, Path = [a,b,c]
; false.
Edit 2015-06-02
Another shot at better naming! This leans more on the side of maplist/2...
graph_path_from_to(P_2,Path,From,To) :-
path(P_2,Path,From,To).
Here, graph, of course, is a noun, not a verb.
Regarding the meaning of "path": paths definitely should allow From=To and not exclude that by default (with pairwise term inequalities). It is easy to exclude this with an additional dif(From,To) goal, but not the other way round.
I do not see the reason to define in path/4 the arguments "start node" and "end node". It seems that a simple path/2 with the rule and the list of nodes must be enough.
If the user wants a list starting with some node (by example, 'a'), he can query the statement as: path( some_rule, ['a'|Q] ).
A user could, by example, request for path that have length 10 in the way: length(P,10), path( some_rule, P).
* Addendum 1 *
Some utility goals can be easily added, but they are not the main subject. Example, path/3 with start node is:
path( some_rule, [start|Q], start ) :-
path ( some_rule, [start|Q ] ).
* Addendum 2 *
Addition of last node as argument could give the false idea that this argument drives the algorithm, but it doesn't. Assume by example:
n(a, b).
n(a, c).
n(a, d).
and trace algorithm execution for the query:
[trace] ?- path( n, P, X, d ).
Call: (6) path(n, _G1025, _G1026, d) ? creep
Call: (7) path(n, _G1107, _G1026, d, [_G1026]) ? creep
Exit: (7) path(n, [], d, d, [d]) ? creep
Exit: (6) path(n, [d], d, d) ? creep
P = [d],
X = d ;
Redo: (7) path(n, _G1107, _G1026, d, [_G1026]) ? creep
Call: (8) n(_G1026, _G1112) ? creep
Exit: (8) n(a, b) ? creep
Call: (8) non_member(b, [a]) ? creep
Call: (9) dif:dif(b, a) ? creep
Exit: (9) dif:dif(b, a) ? creep
Call: (9) non_member(b, []) ? creep
Exit: (9) non_member(b, []) ? creep
Exit: (8) non_member(b, [a]) ? creep
Call: (8) path(n, _G1113, b, d, [b, a]) ? creep
Call: (9) n(b, _G1118) ? creep
Fail: (9) n(b, _G1118) ? creep
Fail: (8) path(n, _G1113, b, d, [b, a]) ? creep
Redo: (9) non_member(b, []) ? creep
Fail: (9) non_member(b, []) ? creep
Fail: (8) non_member(b, [a]) ? creep
Redo: (8) n(_G1026, _G1112) ? creep
Exit: (8) n(a, c) ? creep
Call: (8) non_member(c, [a]) ? creep
Call: (9) dif:dif(c, a) ? creep
Exit: (9) dif:dif(c, a) ? creep
Call: (9) non_member(c, []) ? creep
Exit: (9) non_member(c, []) ? creep
Exit: (8) non_member(c, [a]) ? creep
Call: (8) path(n, _G1113, c, d, [c, a]) ? creep
Call: (9) n(c, _G1118) ? creep
Fail: (9) n(c, _G1118) ? creep
Fail: (8) path(n, _G1113, c, d, [c, a]) ? creep
Redo: (9) non_member(c, []) ? creep
Fail: (9) non_member(c, []) ? creep
Fail: (8) non_member(c, [a]) ? creep
Redo: (8) n(_G1026, _G1112) ? creep
Exit: (8) n(a, d) ? creep
Call: (8) non_member(d, [a]) ? creep
Call: (9) dif:dif(d, a) ? creep
Exit: (9) dif:dif(d, a) ? creep
Call: (9) non_member(d, []) ? creep
Exit: (9) non_member(d, []) ? creep
Exit: (8) non_member(d, [a]) ? creep
Call: (8) path(n, _G1113, d, d, [d, a]) ? creep
Exit: (8) path(n, [], d, d, [d, a]) ? creep
Exit: (7) path(n, [d], a, d, [a]) ? creep
Exit: (6) path(n, [a, d], a, d) ? creep
P = [a, d],
X = a .
as you can see, in this case algorithm fails to brute force.
For this reason, if algorithm is not improved, I suggest do not add "end node" as "path" argument.

Not sufficiently instantiated for maplist(all_distinct, list)

I'm not able to run this code, what exactly do I have to say about the list to allow maplist/2 to run all_distinct/1?
Solution = [A, B, C, D, E, F, G, H, I],
Solution ins 1..9,
maplist(all_distinct, Solution).
I get ERROR: Arguments are not sufficiently instantiated. I understand that I'm not telling it enough about the list of numbers, but I don't know what I need to tell it. I want a list of 9 different numbers 1 through 9.
Here's a trace when I try to execute:
Call: (7) puzzle(_G548) ? creep
Call: (8) _G548=[_G656, _G659, _G662, _G665, _G668, _G671, _G674, _G677|...] ? creep
Exit: (8) [_G656, _G659, _G662, _G665, _G668, _G671, _G674, _G677|...]=[_G656, _G659, _G662, _G665, _G668, _G671, _G674, _G677|...] ? creep
Call: (8) clpfd: ([_G656, _G659, _G662, _G665, _G668, _G671, _G674|...]ins 1..9) ? creep
Call: (9) error:must_be(list, [_G656, _G659, _G662, _G665, _G668, _G671, _G674|...]) ? creep
Exit: (9) error:must_be(list, [_G656, _G659, _G662, _G665, _G668, _G671, _G674|...]) ? creep
Call: (9) clpfd:'__aux_maplist/2_fd_variable+0'([_G656, _G659, _G662, _G665, _G668, _G671, _G674|...]) ? creep
Call: (10) clpfd:fd_variable(_G656) ? creep
Call: (11) var(_G656) ? creep
Exit: (11) var(_G656) ? creep
Call: (11) true ? creep
Exit: (11) true ? creep
Exit: (10) clpfd:fd_variable(_G656) ? creep
Call: (10) clpfd:'__aux_maplist/2_fd_variable+0'([_G659, _G662, _G665, _G668, _G671, _G674, _G677|...]) ? creep
It looks like ins/2 might be not working and then still passing off to maplist/2? I've got no idea what is happening.
What you are doing is that you are making a list of variables, Solutions, and then Solutions ins 1..9 makes each variable an integer between 1 and 9.
all_distinct/1 expects a list, not an integer.
So, if you want a list of 9 distinct integers:
?- Solutions = [A,B,C,D,E,F,G,H,I],
Solutions ins 1..9,
all_distinct(Solutions).
L = [A, B, C, D, E, F, G, H, I],
A in 1..9,
all_distinct([A, B, C, D, E, F, G, H|...]),
B in 1..9,
C in 1..9,
D in 1..9,
E in 1..9,
F in 1..9,
G in 1..9,
H in 1..9,
I in 1..9.

Resources