Where is the plan in planning.domain solver? - artificial-intelligence

(define (domain dungeon_hero)
(:requirements :strips :adl :typing :negative-preconditions) ;; There is use of negative-preconditions in the domain
(:types hero room sword monster trap clear) ;; Various types used
(:predicates (connected ?r1 -room ?r2 -room) ;; defines that two rooms are connected and will be defined twice for bi-directional connection
(at-hero ?h -hero ?r -room) ;; defines what is there in which room i.e.hero,trap,monster or room is clear
(monster-in ?m -monster ?r1 -room) ;; defines a monster in a room
(trap-in ?t -trap ?r -room) ;; defines a trap in a room
(sword-in ?s -sword ?r -room) ;; defines a sword in a room
(clear-room ?r -room) ;; defined a room is clear
(handempty ?h -hero ) ;; TRUE if the Hero's hand is empty
(haveSword ?s -sword ) ;; if the hero is holding the sword
(alive ?h -hero) ;; If the hero is alive
(exist ?r -room) ;; If the room is not destroyed i.e. after the visit
)
(:action visit2sword
:parameters (?h -hero ?from -room ?to -room ?s -sword ?t -trap ?m -monster)
:precondition (and (at-hero ?h ?from)(alive ?h) (exist ?to)(sword-in ?s ?to)(connected ?from ?to) )
:effect
(and (not (at-hero ?h ?from))
(not(exist ?from))
(at-hero ?h ?to)
(not(monster-in ?m ?to))
(not(trap-in ?t ?to))
)
)
(:action pick-up-sword
:parameters (?h -hero ?r -room ?s -sword )
:precondition (and (at-hero ?h ?r)(alive ?h) (sword-in ?s ?r) (handempty ?h))
:effect
(and (not (handempty ?h))
(haveSword ?s)
(not(sword-in ?s ?r))
)
)
(:action visit2monster
:parameters (?h -hero ?from -room ?to -room ?s -sword ?m -monster ?t -trap)
:precondition (and (at-hero ?h ?from)(alive ?h)(not(handempty ?h))(exist ?to)(haveSword ?s)(monster-in ?m ?to) (connected ?from ?to) )
:effect
(and (not (at-hero ?h ?from))
(not(exist ?from))
(at-hero ?h ?to)
(not(trap-in ?t ?to))
(not(sword-in ?s ?to))
)
)
(:action visit2trap
:parameters (?h -hero ?from -room ?to -room ?s -sword ?t -trap)
:precondition (and (at-hero ?h ?from)(alive ?h)(not(haveSword ?s))(handempty ?h)(trap-in ?t ?to) (exist ?to)(connected ?from ?to) )
:effect
(and (not (at-hero ?h ?from))
(not(exist ?from))
(at-hero ?h ?to)
)
)
(:action visit2clear
:parameters (?h -hero ?from -room ?to -room ?s -sword ?t -trap ?m -monster)
:precondition (and (at-hero ?h ?from)(alive ?h)(clear-room ?to)(exist ?to)(connected ?from ?to) )
:effect
(and (not (at-hero ?h ?from))
(not(exist ?from))
(at-hero ?h ?to)
(not(monster-in ?m ?to))
(not(trap-in ?t ?to))
;; (not(sword-in ?s ?to))
)
)
(:action destroy-sword
:parameters (?h -hero ?t -trap ?s -sword ?m -monster ?r -room )
:precondition
((at-hero ?h ?r)(alive ?h) (haveSword ?s)(not(monster-in ?m ?r)) (not(trap-in ?t ?r)
;;(and(at-hero ?h ?r)(alive ?h) (haveSword ?s)(not(monster-in ?m ?r)) (not(trap-in ?t ?r))
)
:effect
(and (handempty ?h)
(not(haveSword ?s))
)
)
(:action disarm ;; Disarm the trap to
:parameters (?h -hero ?t -trap ?r -room )
:precondition (and(at-hero ?h ?r)(alive ?h)(handempty ?h)(trap-in ?t ?r) )
:effect
(and (not (trap-in ?t ?r))
)
)
(:action getkilled ;; Disarm the trap to
:parameters (?h -hero ?m -monster ?s -sword ?r -room )
:precondition (and(at-hero ?h ?r)(alive ?h)(handempty ?h)(monster-in ?m ?r)(not(haveSword ?s))) ;;
:effect
(and (not (alive ?h))
)
)
)
(define (problem T2PROBLEM-1)
(:domain dungeon_hero)
(:objects d0-1 d1-1 d1-2 d2-1 d2-2 d3-1 d3-2 d4-1 - room
sword1 - sword
mon1 mon2 - monster
clear1 clear2 - clear
trap1 - trap
h1 - hero
;;h1 -hand
)
(:INIT
;; Initial status of the hero h1
(at-hero h1 d0-1)
(alive h1)
(handempty h1)
;; content of each room
(sword-in sword1 d1-1)
(monster-in mon1 d1-2)
(monster-in mon2 d2-1)
(trap-in trap1 d1-1)
(clear-room d2-2)
(clear-room d3-2)
(clear-room d4-1)
;; All rooms exist at the initial state and hence exist should be TRUE
(exist d0-1) (exist d1-1) (exist d1-2) (exist d2-1) (exist d2-2)
(exist d3-1) (exist d3-2) (exist d4-1)
;; Rooms are connected in both direction being undirected graph
(connected d0-1 d1-1)(connected d1-1 d0-1)
(connected d0-1 d1-2)(connected d1-2 d0-1)
(connected d1-1 d1-2)(connected d1-2 d1-1)
(connected d1-1 d2-1)(connected d2-1 d1-1)
(connected d1-2 d2-2)(connected d2-2 d1-2)
(connected d2-1 d2-2)(connected d2-2 d2-1)
(connected d2-1 d3-1)(connected d3-1 d2-1)
(connected d2-2 d3-2)(connected d3-2 d2-2)
(connected d3-1 d3-2)(connected d3-2 d3-1)
(connected d3-1 d4-1)(connected d4-1 d3-1)
)
(:goal (AND (ALIVE h1) (AT-hero h1 d4-1)))
)

Your domain model is not valid PDDL.
Action "destroy-sword" has an "incorrect" precondition specification.
:precondition
((at-hero ?h ?r)(alive ?h) (haveSword ?s)(not(monster-in ?m ?r)) (not(trap-in ?t ?r)
;;(and(at-hero ?h ?r)(alive ?h) (haveSword ?s)(not(monster-in ?m ?r)) (not(trap-in ?t ?r))
)
Should this precondition be as follows?
:precondition
(and (at-hero ?h ?r)(alive ?h) (haveSword ?s)(not(monster-in ?m ?r)) (not(trap-in ?t ?r)
;;(and(at-hero ?h ?r)(alive ?h) (haveSword ?s)(not(monster-in ?m ?r)) (not(trap-in ?t ?r))
))
If you specify it as described above, the output of the planner is " ff: goal can be simplified to FALSE. No plan will solve it". So the specified planning problem has no plan.

Related

How to change the use of Common Lisp `with-open-file` so that elements are appended inside a list in a file?

I am using Common Lisp (SBCL). Currently, I can write a file appending lists. In order to illustrate, let's first define some variables to make it easier to understand:
(defvar example-1 '((d-1 u-1) (i-1) (i-2)))
(defvar example-2 '((d-2 u-2) (i-1) (i-2)))
Now, in the REPL:
CL-USER> (with-open-file (str "/home/pedro/miscellaneous/misc/tests-output/stack-overflow.lisp"
:direction :output
:if-exists :append
:if-does-not-exist :create)
(format str " ~S ~%" example-1))
CL-USER> (with-open-file (str "/home/pedro/miscellaneous/misc/tests-output/stack-overflow.lisp"
:direction :output
:if-exists :append
:if-does-not-exist :create)
(format str " ~S ~%" example-2))
If I go to checkout the file "stack-overflow.lisp", I can see:
((D-1 U-1) (I-1) (I-2))
((D-2 U-2) (I-1) (I-2))
Ok. This is close to what I want.
However, I would like to have everything wrapped in a list:
(
((D-1 U-1) (I-1) (I-2))
((D-2 U-2) (I-1) (I-2))
)
Thus, every time something is "appended" to the file, it should be inside this list. I am changing this because it will make this file easier to read and work on. I will need to filter the elements added.
What do I need to change in with-open-file function to have this output?
Use a single call to WITH-OPEN-FILE and combine everything into a single list.
(with-open-file (str "/home/pedro/miscellaneous/misc/tests-output/stack-overflow.lisp"
:direction :output
:if-exists :overwrite
:if-does-not-exist :create)
(pprint (list example-1 example-2) str))
If you want to append to the list every time you write, you need to read the list, append to the list, then overwrite the file with the updated list.
(defun append-to-list-in-file (filename new-item &aux contents) ;;;;
(setq contents (list)) ;; default in case reading fails
(ignore-errors
(with-open-file (str filename :direction :input)
(setq contents (read str))))
(setq contents (nconc contents (list new-item)))
(with-open-file (str filename :direction :output :if-exists :overwrite)
(write contents :stream str)))

Creating inferred relation with multiple restrictions in OWL

I have the following Turtle syntax file (see end of question), and I would like to be able to infer :hasSibling, but only full siblings, not half. i.e. I only want those children that share the same mother and father.
I have reviewed How to infer isBrotherOf property between two individuals, which solves half of the problem. The following SPARQL query:
PREFIX : <http://example.com/Test#>
SELECT DISTINCT *
WHERE {
?child :isSiblingOf ?sibling .
FILTER ( ?child = :jennySmith )
}
returns:
:jimJones - half-sibling, don't want
:joeSmith
:jennySmith - self, not ideal, but I can live with it
I've tried to use owl:intersectionOf and provide 2 sets, one using :hasFather in an owl:propertyChainAxiom and the other using :hasMother but that intersection is empty (it's possible -- likely -- I've got the syntax wrong, or that the 2 property chains are in fact returning different "things" -- I don't have a strong grasp of OWL yet):
:x1
a owl:Restriction ;
owl:intersectionOf (
[
owl:propertyChainAxiom(
:hasFather
:isParentOf
) ] [
owl:propertyChainAxiom(
:hasMother
:isParentOf
)
]
)
.
Another possibility is that such a thing is just not possible with OWL.
I have the repository in GraphDB set up with OWL2-RL. Please ignore that there are existing ontologies to define family trees, and that OWL might not be the best way to represent them. My goal is not to create a family tree, but to learn OWL restrictions and inferences. This was the MVCE I came up with to illustrate my problem.
#prefix owl: <http://www.w3.org/2002/07/owl#> .
#prefix owl2: <http://www.w3.org/2006/12/owl2#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
#prefix xml: <http://www.w3.org/XML/1998/namespace> .
#prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
#prefix : <http://example.com/Test#> .
<file:test.ttl>
a owl:Ontology ;
.
:Person
a owl:Class ;
rdfs:subClassOf owl:Thing ;
.
:hasGender
a owl:ObjectProperty ;
rdfs:domain :Person ;
.
:Man
a owl:Class ;
rdfs:subClassOf :Person ;
owl:equivalentClass [
a owl:Restriction ;
owl:onProperty :hasGender ;
owl:hasValue :male ;
]
.
:Woman
a owl:Class ;
rdfs:subClassOf :Person ;
owl:equivalentClass [
a owl:Restriction ;
owl:onProperty :hasGender ;
owl:hasValue :female ;
]
.
:Parent
a owl:Restriction ;
rdfs:subClassOf :Person ;
owl:onProperty :isParentOf ;
owl:someValuesFrom :Person ;
.
:Child
a owl:Restriction ;
rdfs:subClassOf :Person ;
owl:onProperty :hasParent ;
owl:someValuesFrom :Person ;
.
:Father
a owl:Class ;
rdfs:subClassOf :Parent ;
owl:equivalentClass [
a owl:Restriction ;
owl:intersectionOf ( :Parent :Man ) ;
] ;
.
:Mother
a owl:Class ;
rdfs:subClassOf :Parent ;
owl:equivalentClass [
a owl:Restriction ;
owl:intersectionOf ( :Parent :Woman ) ;
] ;
owl2:disjointObjectProperties :Father ;
.
:hasFather
owl:inverseOf :isFatherOf ;
.
:hasMother
owl:inverseOf :isMotherOf ;
.
:hasParent
a owl:ObjectProperty ;
.
:isMotherOf
a owl:ObjectProperty ;
rdfs:domain :Woman ;
rdfs:range: :Child ;
rdfs:subPropertyOf :isParentOf ;
.
:isFatherOf
a owl:ObjectProperty ;
rdfs:domain :Man ;
rdfs:range: :Child ;
rdfs:subPropertyOf :isParentOf ;
.
:isParentOf
a owl:ObjectProperty ;
rdfs:domain :Person ;
rdfs:range :Person ;
owl:inverseOf :hasParent ;
.
:isSiblingOf
a owl:ObjectProperty ;
owl:propertyChainAxiom(
:hasParent
:isParentOf
)
.
:janeSmith
a :Person ;
:hasGender :female ;
:isMotherOf :jimJones ;
:isMotherOf :joeSmith ;
:isMotherOf :jennySmith ;
.
:johnSmith
a :Person ;
:hasGender :male ;
:isFatherOf :joeSmith ;
:isFatherOf :jennySmith ;
.
:tomJones
a :Person ;
:hasGender :male ;
:isFatherOf :jimJones ;
.
:jimJones
a :Person ;
:hasGender :male ;
.
:joeSmith
a :Person ;
:hasGender :male ;
.
:jennySmith
a :Person ;
:hasGender :female ;
:isMotherOf :harrySmith ;
.
:harrySmith
a :Person ;
:hasGender :male ;
.
You can define :isFullSiblingOf with a custom rule like this. Read about Constraints and Cut at http://graphdb.ontotext.com/documentation/enterprise/reasoning.html
id: isFullSiblingOf
x <:hasFather> f
x <:hasMother> m
y <:hasFather> f [Constraint x != y] [Cut]
y <:hasMother> m
----------------
x <:isSiblingOf> y
PS1: I assume you already have inverse reasoning. The best is to pick the axioms you need, then add your own: eg you don't have to include all RL axioms if you don't need them. See more advice: http://graphdb.ontotext.com/documentation/standard/rules-optimisations.html
PS2: You should also declare isFullSiblingOf as owl:SymmetricProperty. BTW I would use hasFullSibling since the inverse name is harder to grasp
PS3: owl2:disjointObjectProperties :Father above is a mistake, you can't use that on classes.
PS4: <file:test.ttl> uses a relative URL without base, which is not very good.

OWL Object Property domain/range restrictions to the same level classes

I want to design an object property which is always linked only between the same level of classes. For example,
I want to limit the property isCounterPartOf to be an arc of the sibling nodes which belong to the same upper class, such as
house isCounterPartOf cars
bad isCounterPartOf good
slow isCounterPartOf fast
and the property should NOT link between classes of different levels (those having different ancestors), like
cars isCounterPartOf bad
cars isCounterPartOf object
cars isCounterPartOf Entity
Is there a way to do this with defining only one property?
Assuming your objective is when :isCounterPartOf links two individuals, and one is a member of e.g. :Bad, then the other should be classified as :Good, you don't need to define domain and range of :isCounterPartOf, just that it is owl:SymmetricProperty. You only need to define your classes, :Bad to be equivalent of :isCounterPartOf some :Good and :Good to be equivalent of :isCounterPartOf some :Bad, and for all "pairs" of classes respectively.
Then if:
:A :isCounterPartOf :B
:C :isCounterPartOf :B
:A a :Slow
:C a :Bad
then :B will be classified as :Fast and :Good.
Clarification (based on the comments)
In the example above,
1. :isCouterPartOf is a symetric object property:
:isCounterPartOf rdf:type owl:ObjectProperty ,
owl:SymmetricProperty .
:Good, :Bad, :Slow and :Fast are OWL classes, for which:
(no idea why the code formatting doesn't work)
:Bad rdf:type owl:Class ;
owl:equivalentClass [ rdf:type owl:Restriction ;
owl:onProperty :isCounterPartOf ;
owl:someValuesFrom :Good
] .
:Fast rdf:type owl:Class ;
owl:equivalentClass [ rdf:type owl:Restriction ;
owl:onProperty :isCounterPartOf ;
owl:someValuesFrom :Slow
] .
:Good rdf:type owl:Class ;
owl:equivalentClass [ rdf:type owl:Restriction ;
owl:onProperty :isCounterPartOf ;
owl:someValuesFrom :Bad
] .
:Slow rdf:type owl:Class ;
owl:equivalentClass [ rdf:type owl:Restriction ;
owl:onProperty :isCounterPartOf ;
owl:someValuesFrom :Fast
] .
:A, :B, and :C are individuals, for which it is asserted that:
(again, no idea why the code formatting doesn't work)
:A rdf:type owl:NamedIndividual ,
:Slow ;
:isCounterPartOf :B .
:B rdf:type owl:NamedIndividual ,
owl:Thing .
:C rdf:type owl:NamedIndividual ,
:Bad ;
:isCounterPartOf :B .
Based on these assertions, when you run the reasoner, you'll have the following situation:
:A rdf:type owl:NamedIndividual ,
:Bad , #inferred
:Slow ;
:isCounterPartOf :B .
:B rdf:type owl:NamedIndividual ,
:Fast , #inferred
:Good , #inferred
owl:Thing ;
:isCounterPartOf :A , #inferred
:C . #inferred
:C rdf:type owl:NamedIndividual ,
:Bad ,
:Slow ; #inferred
:isCounterPartOf :B .

Open World Assumption in Protégé Ontology

I want to model the concept of a movie series in Protege.
Here's my movie trilogy class:
Here is the definition of an individual in the class 'Film Series':
The individual has three 'hasEpisode' properties.
But the open world assumption (OWA) prevents this individual to be inferred under the class 'Trilogy'.
Possible solution:
I could give each individual in the Film Series class a data property that specifies the amount of movies in the series.
However, I would rather like to use the number of 'hasEpisode' object properties (because they are already available and it would mean less maintenance).
All you need to do is declare that those three films are not the same, and assert that those are the only three films that the series has. You can do that by saying that the episodes are all different:
Episode1 ≠ Episode2
Episode1 ≠ Episode3
Episode2 ≠ Episode3
and that the triology has only those episodes:
{theTriology} &sqsubseteq; ∀ hasEpisode.{Episode1, Episode2, Episode3}
You need both types of axioms. The universal axiom says that every episode of the series must be either episode1, episode2, or episode3. That means that the series has at most three episodes, but it could have fewer, if any of those individuals are actually the same. Then the inequalities say that those individuals are all distinct, which means that the series actually has at least three episodes. Since it has at least three and at most three, it must have exactly three.
Here's what it looks like in Protege (note that TheMatrix is inferred to be a Trilogy):
Here's the ontology, in case you want to take a look:
#prefix : <http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
#prefix untitled-ontology-38: <http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#> .
#prefix owl: <http://www.w3.org/2002/07/owl#> .
#prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
untitled-ontology-38:Triology
a owl:Class ;
owl:equivalentClass [ a owl:Class ;
owl:intersectionOf ( untitled-ontology-38:FilmSeries _:b0 )
] .
untitled-ontology-38:FilmSeries
a owl:Class .
_:b0 a owl:Restriction ;
owl:cardinality "3"^^xsd:nonNegativeInteger ;
owl:onProperty untitled-ontology-38:hasEpisode .
untitled-ontology-38:hasEpisode
a owl:ObjectProperty .
<http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38>
a owl:Ontology .
untitled-ontology-38:TheMatrixRevolution
a owl:Thing , owl:NamedIndividual .
untitled-ontology-38:TheMatrixReloaded
a owl:Thing , owl:NamedIndividual .
untitled-ontology-38:TheMatrix
a owl:Thing , owl:NamedIndividual .
[ a owl:AllDifferent ;
owl:distinctMembers ( untitled-ontology-38:TheMatrix untitled-ontology-38:TheMatrixReloaded untitled-ontology-38:TheMatrixRevolution )
] .
untitled-ontology-38:Matrix
a owl:NamedIndividual , untitled-ontology-38:FilmSeries ;
a [ a owl:Restriction ;
owl:allValuesFrom [ a owl:Class ;
owl:oneOf ( untitled-ontology-38:TheMatrixReloaded untitled-ontology-38:TheMatrix untitled-ontology-38:TheMatrixRevolution )
] ;
owl:onProperty untitled-ontology-38:hasEpisode
] ;
untitled-ontology-38:hasEpisode
untitled-ontology-38:TheMatrix , untitled-ontology-38:TheMatrixReloaded , untitled-ontology-38:TheMatrixRevolution .
<rdf:RDF
xmlns:untitled-ontology-38="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<owl:Ontology rdf:about="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38"/>
<owl:Class rdf:about="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#FilmSeries"/>
<owl:Class rdf:about="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#Triology">
<owl:equivalentClass>
<owl:Class>
<owl:intersectionOf rdf:parseType="Collection">
<owl:Class rdf:about="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#FilmSeries"/>
<owl:Restriction>
<owl:onProperty>
<owl:ObjectProperty rdf:about="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#hasEpisode"/>
</owl:onProperty>
<owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger"
>3</owl:cardinality>
</owl:Restriction>
</owl:intersectionOf>
</owl:Class>
</owl:equivalentClass>
</owl:Class>
<owl:NamedIndividual rdf:about="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#Matrix">
<rdf:type rdf:resource="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#FilmSeries"/>
<rdf:type>
<owl:Restriction>
<owl:onProperty rdf:resource="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#hasEpisode"/>
<owl:allValuesFrom>
<owl:Class>
<owl:oneOf rdf:parseType="Collection">
<owl:Thing rdf:about="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#TheMatrixReloaded">
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#NamedIndividual"/>
</owl:Thing>
<owl:Thing rdf:about="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#TheMatrix">
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#NamedIndividual"/>
</owl:Thing>
<owl:Thing rdf:about="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#TheMatrixRevolution">
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#NamedIndividual"/>
</owl:Thing>
</owl:oneOf>
</owl:Class>
</owl:allValuesFrom>
</owl:Restriction>
</rdf:type>
<untitled-ontology-38:hasEpisode rdf:resource="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#TheMatrix"/>
<untitled-ontology-38:hasEpisode rdf:resource="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#TheMatrixReloaded"/>
<untitled-ontology-38:hasEpisode rdf:resource="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#TheMatrixRevolution"/>
</owl:NamedIndividual>
<owl:AllDifferent>
<owl:distinctMembers rdf:parseType="Collection">
<owl:Thing rdf:about="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#TheMatrix"/>
<owl:Thing rdf:about="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#TheMatrixReloaded"/>
<owl:Thing rdf:about="http://www.semanticweb.org/taylorj/ontologies/2015/2/untitled-ontology-38#TheMatrixRevolution"/>
</owl:distinctMembers>
</owl:AllDifferent>
</rdf:RDF>

Reading a matrix from a file with Lisp

I'm starting with Lisp and I have quite a problem trying to read a matrix from a file to program the A-star algorithm.
The file I should read has at the top of the file the number of rows and the number of columns, and I must save both in a couple of global variables (N and M). The file format should be something like this:
ROWS
5
COLUMNS
5
MATRIX
1 1 1 1 1
1 1 0 1 1
1 1 0 0 1
1 1 0 0 1
1 1 1 0 1
START
4 2
GOAL
4 4
And my code at the moment is like this (It's probably quite lame, by the way)
(defvar *N*)
(defvar *M*)
(defvar *Goal*)
(defvar *Start*)
(defvar *Matrix*)
(defun read-matrix (file)
(let ((in (OPEN file :DIRECTION :INPUT)))
(read-line in nil)
(SETQ *N* (parse-integer (read-char in)))
(read-line in nil)
(read-line in nil)
(SETQ *M* (parse-integer (read-line in)))
(read-line in nil)
(read-line in nil)
(SETQ *Matrix* (MAKE-ARRAY '(*N* *M*) :initial-element 1))
(loop for i from 0 to *N*
do (loop for j from 0 to *M*
do (SETF (AREF *Matrix* i j)
(read-char in))))
(read-line in nil)
(SETQ *Start* (read-line in))
(read-line in nil)
(SETQ *Goal* (read-line in))))
I'd be really grateful if anybody could help me.
There are a bunch of errors.
you open a stream, but never close it
you call PARSE-INTEGER on a character, when it expects a string
MAKE-ARRAY is called with a list of symbols, but it should be a list of numbers.
Also: using global variables is not a good idea.
First problem: you open a file, but never close it. When you use open, you need to remember to close afterwards. Since this is so common, there is with-open-file, which closes automatically (see the CLHS for documentation).
(defvar *N*)
(defvar *M*)
(defvar *Goal*)
(defvar *Start*)
(defvar *Matrix*)
(defun read-matrix (file)
(with-open-file (in file
:direction :input)
(read-line in nil)
(setq *N* (parse-integer (read-char in)))
(read-line in nil)
(read-line in nil)
(setq *M* (parse-integer (read-line in)))
(read-line in nil)
(read-line in nil)
(setq *Matrix* (make-array '(*N* *M*) :initial-element 1))
(loop for i from 0 to *N*
do (loop for j from 0 to *M*
do (setf (aref *Matrix* i j)
(read-char in))))
(read-line in nil)
(setq *Start* (read-line in))
(read-line in nil)
(setq *Goal* (read-line in))))
Second problem: read-char returns a character, and parse-integer is not defined for a character. It seems that you can simply read the entire line.
(defvar *N*)
(defvar *M*)
(defvar *Goal*)
(defvar *Start*)
(defvar *Matrix*)
(defun read-matrix (file)
(with-open-file (in file
:direction :input)
(read-line in nil)
(setq *N* (parse-integer (read-line in)))
(read-line in nil)
(read-line in nil)
(setq *M* (parse-integer (read-line in)))
(read-line in nil)
(read-line in nil)
(setq *Matrix* (make-array '(*N* *M*) :initial-element 1))
(loop for i from 0 to *N*
do (loop for j from 0 to *M*
do (setf (aref *Matrix* i j)
(read-char in))))
(read-line in nil)
(setq *Start* (read-line in))
(read-line in nil)
(setq *Goal* (read-line in))))
Third problem: it seems that you discard too many lines between those that you need.
(defvar *N*)
(defvar *M*)
(defvar *Goal*)
(defvar *Start*)
(defvar *Matrix*)
(defun read-matrix (file)
(with-open-file (in file
:direction :input)
(read-line in nil) ; "ROWS"
(setq *N* (parse-integer (read-line in)))
(read-line in nil) ; "COLUMNS"
(setq *M* (parse-integer (read-line in)))
(read-line in nil) ; "MATRIX"
(setq *Matrix* (make-array '(*N* *M*) :initial-element 1))
(loop for i from 0 to *N*
do (loop for j from 0 to *M*
do (setf (aref *Matrix* i j)
(read-char in))))
(read-line in nil) ; "START"
(setq *Start* (read-line in))
(read-line in nil) ; "GOAL"
(setq *Goal* (read-line in))))
Fourth problem: that make-array form needs numbers to set its dimensions, but you give it two symbols instead. You need to evaluate those symbols to get the values you want:
(defvar *N*)
(defvar *M*)
(defvar *Goal*)
(defvar *Start*)
(defvar *Matrix*)
(defun read-matrix (file)
(with-open-file (in file
:direction :input)
(read-line in nil) ; "ROWS"
(setq *N* (parse-integer (read-line in)))
(read-line in nil) ; "COLUMNS"
(setq *M* (parse-integer (read-line in)))
(read-line in nil) ; "MATRIX"
(setq *Matrix* (make-array (list *N* *M*) :initial-element 1))
(loop for i from 0 to *N*
do (loop for j from 0 to *M*
do (setf (aref *Matrix* i j)
(read-char in))))
(read-line in nil) ; "START"
(setq *Start* (read-line in))
(read-line in nil) ; "GOAL"
(setq *Goal* (read-line in))))
Fifth problem: It seems that you want your matrix to contain bits, but you only put characters into it. On top of that, your matrix will not even contain a #\1 (the character) where you expect a 1 (the number) and a #\0 where you expect a 0, but instead the following (assuming Unix-style newlines):
#2A((#\1 #\Space #\1 #\Space #\1)
(#\Space #\1 #\Space #\1 #\Newline)
(#\1 #\Space #\1 #\Space #\0)
(#\Space #\1 #\Space #\1 #\Newline)
(#\1 #\Space #\1 #\Space #\0))
To accomplish what you want, I propose to read the matrix lines, then split them on spaces and parse the fields. A handy library for that is split-sequence.
(defvar *N*)
(defvar *M*)
(defvar *Goal*)
(defvar *Start*)
(defvar *Matrix*)
(defun read-matrix (file)
(with-open-file (in file
:direction :input)
(read-line in nil) ; "ROWS"
(setq *N* (parse-integer (read-line in)))
(read-line in nil) ; "COLUMNS"
(setq *M* (parse-integer (read-line in)))
(read-line in nil) ; "MATRIX"
(setq *Matrix* (make-array (list *N* *M*) :initial-element 1))
(loop :for i :from 0 :to *N*
:do (let* ((line (read-line in))
(fields (split-sequence:split-sequence #\Space line))))
(loop :for field :in fields
:for j :upfrom 0
:do (setf (aref *matrix* i j)
(parse-integer field))))
(read-line in nil) ; "START"
(setq *Start* (read-line in))
(read-line in nil) ; "GOAL"
(setq *Goal* (read-line in))))
At this point, this should "work", for some value of "work" (I have not tested it, though).
However, the use of global variables for transfer of information has been established as uncouth about 30 years ago. The first step of bringing this to a style more suitable for a robust program would be to return the values from the function instead.
(defun read-matrix (file)
(let (n m goal start matrix)
(with-open-file (in file
:direction :input)
(read-line in nil) ; "ROWS"
(setf n (parse-integer (read-line in)))
(read-line in nil) ; "COLUMNS"
(setf m (parse-integer (read-line in)))
(read-line in nil) ; "MATRIX"
(setf matrix (make-array (list n m) :initial-element 1))
(loop :for i :from 0 :to n
:do (let* ((line (read-line in))
(fields (split-sequence:split-sequence #\Space line))))
(loop :for field :in fields
:for j :upfrom 0
:do (setf (aref matrix i j)
(parse-integer field))))
(read-line in nil) ; "START"
(setf start (read-line in))
(read-line in nil) ; "GOAL"
(setf goal (read-line in)))
(values n m goal start matrix)))
I think that what you really need is a struct or class for what you are reading in here. It might be called game-state.
(defclass game-state ()
((rows :accessor rows :initarg :rows)
(columns :accessor columns :initarg :columns)
(goal :accessor goal :initarg :goal)
(start :accessor start :initarg :start)
(board :accessor board :initarg board)))
Your function should then be named read-game-state and return an object of this class.
Something to get you started:
(defvar *rows*)
(defvar *columns*)
(defvar *goal*)
(defvar *start*)
(defvar *matrix*)
(defun parse-sequence (tokens parser &key (delimiter #\Space))
(do ((start 0)
(end (position delimiter tokens :start 0)
(position delimiter tokens :start start))
result)
;; you could also read from end to avoid reversing
;; I just find this to be more natural
((null end)
(reverse
(cons
(funcall parser
(subseq tokens start (length tokens)))
result)))
(setf result
(cons (funcall parser
(subseq tokens start end)) result))
(setf start (1+ end))))
(defun parse-matrix-source (file)
(with-open-file (stream file :direction :input)
(do ((line (read-line stream nil :eof)
(read-line stream nil :eof))
(matrix-row 0)
(matrix-column 0 0)
state)
((eq line :eof))
(cond
((string= line "ROWS") (setf state '*rows*))
((string= line "COLUMNS") (setf state '*columns*))
((string= line "MATRIX") (setf state '*matrix*))
((string= line "START") (setf state '*start*))
((string= line "GOAL") (setf state '*goal*))
((eq state '*rows*) (setf *rows* (parse-integer line)))
((eq state '*columns*)
(setf *columns* (parse-integer line)
*matrix* (make-array (list *rows* *columns*)
:initial-element 0)))
((eq state '*matrix*)
(dolist (i (parse-sequence line 'parse-integer))
(setf (aref *matrix* matrix-column matrix-row) i
matrix-column (1+ matrix-column)))
(incf matrix-row))
((eq state '*start*)
(setf *start* (parse-sequence line 'parse-integer)))
((eq state '*goal*)
(setf *goal* (parse-sequence line 'parse-integer)))))))
(defun test-parse-matrix ()
(parse-matrix-source #P"~/Projects/lisp-doodles/matrix.txt")
(format t "rows: ~d, columns: ~d, goal: ~s, start: ~s~& matrix: ~s~&"
*rows* *columns* *goal* *start* *matrix*))
(test-parse-matrix)
But I would think about a better format - in which case you have a good chance of finding a library that is already able to parse it + will allow for more flexibility in the future. Even CSV or INI would be better then what you have now.

Resources