Archived

This topic is now archived and is closed to further replies.

bishop_pass

Using Lisp (or another language) to generate fictional characters

Recommended Posts

quote:
Original post by bishop_pass
I myself think the subject at hand here dovetails nicely with my other thread, and in fact, the efforts here could serve as inputs to narratives generated by an application such as that envisioned in the other thread.

I concur. Is what I loosely outlined at all what you had in mind? I realize it's not perfect but at least I can say I'm putting forth some effort. (And I realized that it was from you that the e-mail originated. Thanks for the motivation.)

EDIT: I see that you've already remarked. It's pretty much exactly what I've outlined above upon rereading it though I'll post my humble Lisp rendering of the same if you'd like.

[edited by - kordova on October 31, 2003 12:25:41 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by kordova
EDIT: I see that you''ve already remarked. It''s pretty much exactly what I''ve outlined above upon rereading it though I''ll post my humble Lisp rendering of the same if you''d like.
Yes, I would like that, though I may not see it or comment on it until tomorrow night, as I''m likely going to bed here shortly...

Share this post


Link to post
Share on other sites
I think my biggest setback at present is not knowing where to store the info regarding the parents etc. Presently I''m trying to avoid throwing it into some sort of defined structure, at the cost of globals and just testing the various capabilities, as that seems to defy the nature of the various functions I''ve written. Then again I''m stumbling about the whole thing it seems (not that that is necessarily a bad thing).

Share this post


Link to post
Share on other sites
I actually have about four versions of this going at once so I''ll post the most basic as it works and the other ones definitely feel sloppy. For the most part they just veer from this sort of structure so I''ll just wait for comments etc. Also, I''m not sure whether a division between male and female is necessary as I had here, though I can see where that may very well depend on the system/time period etc at hand. (And the only "branch" that treats them together is very, very messy at this point).


(setf possible_mother
''((female english annie-may) (female english dora) (female spanish zenada)(female russian sveta)))
(setf possible_father
''((male english john) (male english james) (male spanish carlos)(male russian boris)))
(setf last_names
''((english smith) (english williams) (english berry)(spanish sanchez)(russian orkovstka)))
(setf occupations
''((doctor)(marshall)(general-store-owner)(bartender)(outlaw)))


(defun get_first_name (names ethnicity)
(progn
(let* ((rand (make-random (length names))) (rnd (funcall (rand-func rand))))
(cond
((equal (list(cadr(elt names rnd))) ethnicity) (elt names rnd))
(t (get_first_name names ethnicity))))))

(defun get_last_name (names)
(let ((rand (make-random (length names))))
(elt names (funcall (rand-func rand)))))

(defun get_occupation (occupations)
(let ((rand (make-random (length occupations))))
(elt occupations (funcall (rand-func rand)))))

(defun get_father (first_names last_names occupations)
(let ((last_name (get_last_name last_names)))
(append
(list last_name)
(list (get_first_name first_names (list(car last_name))))
(list (get_occupation occupations)))))

(defun get_mother (first_names last_names occupations)
(let ((last_name (get_last_name last_names)))
(append
(list last_name)
(list (get_first_name first_names (list(car last_name))))
(list (get_occupation occupations)))))

(defun get_parents (m_first_names f_first_names last_names occupations)
(append
(list(get_father m_first_names last_names occupations))
(list(get_mother f_first_names last_names occupations))))


From which the following came about:

(get_parents possible_father possible_mother last_names occupations)

(((RUSSIAN ORKOVSTKA) (MALE RUSSIAN BORIS) (MARSHALL)) ((ENGLISH WILLIAMS) (FEMALE ENGLISH DORA) (BARTENDER)))

(get_parents possible_father possible_mother last_names occupations)

(((SPANISH SANCHEZ) (MALE SPANISH CARLOS) (MARSHALL)) ((ENGLISH WILLIAMS) (FEMALE ENGLISH DORA) (OUTLAW)))

(not "mind blowin''" I know, but it''s a start I suppose, albeit an inefficient one)

Share this post


Link to post
Share on other sites
Dora Williams, the outlaw! Interesting.

What you'd want, I'd think, is a function called 'assert' which allows you to add new knowledge. For example:

(assert '(spelling "William" william)) [ENTER]
(assert '(nickname "Willy" william)) [ENTER]
(assert '(nickname "Will" william)) [ENTER]
(assert '(nickname "Billy" william)) [ENTER]
(assert '(nickname "Bill" william)) [ENTER]
(assert '(ethnicity english william)) [ENTER]
(assert '(instance-of male-first-names william)) [ENTER]


Now, each time assert is called, it appends this data into a list which might or might not exist yet. In the end, the above assertions would have created a list that looks like this:

william:
((spelling ("William"))
(nickname ("Willy" "Will" "Billy" "Bill"))
(ethnicity (english))
(instance-of (male-first-names)))


That whole list is assigned to the symbol/variable called william. Notice that each entry is its own list, and that each entry can have multiple entries. Also, notice that there is no requirement for a particular entry to necessarily exist, unless we wish to enforce it.

So, assert takes a list as its input, finds the last element in the list (in this case william), sees if there is a list which already exists that is called william, and then sees if that particular slot exists in the list william, for example, nickname and does the necessary appending or complete addition of the element.

Here's how we enforce semantics. Let's look at the slot nickname as an example. The slot nickname makes sense for things which are first-names. Therefore, the slot nickname should only reside in structures which are instances of first-names. Let's actually create a structure which has the same format as william, and call it nickname. The point is to describe the meaning of nickname to enforce the semantics of it.

nickname:
((makes-sense-for (first-names))
(number-of-entries (any))
(instance-of (slot)))


Notice that it takes on the same format as the william structure. In this regard, we can build information about nickname also using the assert function!

Now, since we have said that nickname is an instance-of slot, it can exist as a slot on other structures. For example, in the william structure, its slots are nickname, ethnicity, spelling and instance-of.

Now, when assert is called, the first thing it should do is check to make sure that the slot being asserted makes sense for that type of thing. In this case, nickname should make sense for names. But is william a first-name? How does the system know?

Here's how it knows: Actually, there are two ways.

Let's say we have already asserted that william is an instance-of male-first-names, and we have also already asserted that male-first-names is a sub-category of first-names and we have asserted that first-names is a sub-category of names. The rule of determining if something is an instance-of something else is to follow the chain upwards. But what if we haven't asserted those things yet?

Aha! That's where the knowledge implicit in the makes-sense-for piece of knowledge comes in handy! If we haven't asserted it yet, then assert should go ahead and place that entry on william precisely because it knows that when we place the slot nickname on a structure, it is then an instance-of whatever nickname makes sense for!

[edited by - bishop_pass on November 1, 2003 11:14:10 AM]

Share this post


Link to post
Share on other sites
How do we do quick data retrieval from both ends? Easy!

When we say assert that william is an instance-of male-first-names, the inverse is automatically asserted. The inverse is that william is a member of male-first-names. We have a structure like this before the assertion:

male-first-names:
(((instances (robert james andrew george carlos enrique))
((sub-category-of (first-names)))


And afterwards, we have:
male-first-names:
(((instances (robert james andrew george carlos enrique william))
((sub-category-of (first-names)))


It goes without saying that the slot instance-of has some knowledge encoded about it as well, namely:

instance-of:
(((makes-sense-for (things))
((inverse (instances)))


Notice that we have encoded the inverse of instance-of. Because of that, we also have knowledge of the instances slot. It looks like this:

instances:
(((makes-sense-for (things))
((inverse (instance-of)))


All slots have an inverse. The inverse of makes-sense-for is can-have-the-slot. So for example, while we know that ethnicity makes-sense-for names, we know that names can-have-the-slot ethnicity.

Share this post


Link to post
Share on other sites
quote:
Original post by kordova
I think my biggest setback at present is not knowing where to store the info regarding the parents etc. Presently I'm trying to avoid throwing it into some sort of defined structure, at the cost of globals and just testing the various capabilities, as that seems to defy the nature of the various functions I've written. Then again I'm stumbling about the whole thing it seems (not that that is necessarily a bad thing).
Where do we store info for the parents?

Aha! We follow the same format that I outlined above. We have a structure for a person. (please, don't confuse my use of the word 'structure' with that used in C or C++. Our structures here are freeform extensible and non rigid. You might want to call them frames, borrowing from the AI community).

some-person-it-doesnt-really-matter-what-the-symbol-name-is:
(((father (some-other-person-again-it-doesnt-really-matter))
((mother (again-some-other-person))
((instance-of (male-human))
((ethnicity (spanish))
((month-born (january))
((date-born (12))
((day-born (monday))
((year-born (1853))
((first-name ("enrique"))
((last-name ("sanchez")))


Note, because of our freeform structure (frame) and how we can enforce semantics, there is no limit to the type of things we can say about enrique, or his parents.

[edited by - bishop_pass on November 1, 2003 11:55:30 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by kordova
Could you give an example of instance/instance-of in use? I think I understand what they are, just not so much the mechanics you have in mind. Cheers.
Secretariat-the-horse is an instance of a thoroughbred. He is also an instance of a horse, a race horse, a living thing, a physical thing, a beloved creature, an animal, an animal-no-longer-living, etc.

The concept of horse, or thoroughbred, however, is not something which is an instance of something. Rather, those concepts are categories.

Instances are particular individuals, such as the particular mouse that you are using with your computer. But computer-mouses in general is a category, or a class, if you will.

margarita-sanchez is an instance of a female human, a human, an animal, a living thing, a physical thing, a thing, and possibly, she is an instance of a land-owner, a rancher, etc. But all of those things of which she is an instance of are categories, and so those things are sub-categories of other things.

Back to secretariat-the-horse. I said what he is an instance of. A race horse (the group of all race horses) is a sub-category (not an instance-of) horse. A thoroughbred is a sub-category of horse. A horse is a sub-category of animal. Animal is a sub-category of living-thing.

Now, some particular horse is an instance-of horse, but, since we only use the symbol horse in one context, we specify it as a category. If we want to express knowledge about some particular horse, then we give it another name, perhaps the-horse-that-was-white-and-was-owned-by-margarita-sanchez, and in that case, it is an instance-of horse.

Share this post


Link to post
Share on other sites
I'm still pondering the idea of attributes such as the given health maladies, alcoholism etc though I can get to that later. (It certainly is neat how I can whip up quick tests for any ideas in Lisp as opposed to the tedious design constraints with C++.) Anyway, I have output that appears as such:

(((SPANISH "sanchez") (MALE SPANISH "carlos") (DOCTOR)) ((RUSSIAN "orkovstka") (FEMALE RUSSIAN "sveta") (BARTENDER)))

taking the names as strings, and also taking gender into consideration when selecting occupations (and perhaps other such attributes later on as most of the code in the file is commented as broken or in testing so I hope to have some more interesting developments at some point soon.)

I'm also probably going to end up rewriting some of/all of this so that the output/lists are actually organized (be rid of multiple instances of ethnicity etc)

I'm still shaky about the "instance/instance-of" concept. I mean, I understand it to some extent, but to use your own example, if a an animal is a race-horse, is it's instance-of member (instance-of (horse)), (instance-of (race-horse)), (instance-of (horse race-horse)) ... ?

[edited by - kordova on November 1, 2003 12:46:29 PM]

Share this post


Link to post
Share on other sites
Ok, I''ve been working with this further today, along the lines as you set forth.

In your above example of

some-person-it-doesnt-really-matter-what-the-symbol-name-is: 

I have already pretty much worked on generating the varius aspects of the mother/father. I don''t see the other attributes of "some-person..." as a problem, being somewhat randomly generated, being sure to place limits due to various factors such as parental ethnicity and so forth. However, this one still hangs in my mind:
((instance-of (male-human)) 

Upon creating this "some-person" does the user provide this or how else does the structure gain this attribute/information about itself?

Share this post


Link to post
Share on other sites
quote:
Original post by kordova
I'm still shaky about the "instance/instance-of" concept. I mean, I understand it to some extent, but to use your own example, if a an animal is a race-horse, is it's instance-of member (instance-of (horse)), (instance-of (race-horse)), (instance-of (horse race-horse)) ... ?


Ok, look closely. I'm going to refer to our structures as frames from here on out. The title of a frame is the name of the symbol which is set to the list of elements which follow it. We might have the following frame:

bugs-bunny:
((instance-of (cartoon-character))
(friends (sylvester tweety foghorn-leghorn yosemite-sam)))


The symbol bugs-bunny is the name of the symbol which is set equal to (use setf) the list which contains two sub lists. Each sublist in the bugs-bunny list contains what is called a slot/value pair. The first slot/value pair is (instance-of (cartoon-character)). The slot is instance-of and the value is (cartoon-character). We put cartoon-character in a list, because some slots allow multiple entries, and we can see that in action in the next slot/value pair, where we have four values for the slot friends. Ok, so we now know how to structure our frames, and what a slot is, and what a value is. Now, forget about bugs-bunny, and lets move on to understanding instances.

Look at the following frames:

horse:
((sub-category-of (mammal))
(categories (race-horse draft-horse thoroughbred quarter-horse)))

mammal:
((sub-category-of (animal))
(categories (horse dog cat human gorilla tiger)))

animal:
((sub-category-of (living-thing))
(categories (mammals reptiles fish insects circus-animals)))

race-horse:
((sub-category-of (horse))
(instances (secretariat seabiscuit spectacular-bid ruffian)))

secretariat:
((instance-of (race-horse thoroughbred)))


Now, notice that secretariat is an instance-of both a race-horse and a thoroughbred. That's perfectly valid. Also, it is also true that secretariat is also an (instance-of (horse mammal animal living-thing)) among others. We could cache these values on the secretariat frame if we wanted to. We would get this:

secretariat:
((instance-of (race-horse thoroughbred horse mammal animal living-thing)))


For high speed retrieval, caching is better. But we can write code to make these determinations even if we don't cache it. The rule goes like this:

If something is an instance-of a category, then it is also an instance-of everything that the named category is a sub-category of.

An semi ideal solution is to have a piece of code be run once, at assertion time, to determine everything which secretariat is an instance-of, and then cache those values. It gets a little complex though. That's because if we've already created the secretariat frame, and later declare that living-thing is a sub-category-of of physical-thing, then for completeness, we must percolate through our entries and update the secretariat frame to now mention that secretariat is an instance-of physical-thing. This is absolutely necessary if at query time we only rely on cached values.

Obviously, we're digressing from the main thrust of this thread, and delving into the realms of truth maintenance and knowledge bases, but it is all relevant, and allows for some rather amazing generalizations to be made with regard to knowledge, and also it allows for using the knowledge in the knowledge base itself to enforce semantics. The real payoff is where the simple mention of one little fact actually creates a cascade of facts, creating real knowledge and supposed understanding and comprehension.

It kind of boils down to this: If I told you Citation was a race horse, you'd actually know a heck of a lot about Citation, certainly a great deal more than the simple fact that Citation is a race horse.

[edited by - bishop_pass on November 1, 2003 11:43:51 PM]

Share this post


Link to post
Share on other sites
instance are the leaf of the semantic tree
every thing above are abstract branch of the tree
instance are ''concrete''

he work better on a picture but i did not have time

>>>>>>>>>>>>>>>
be good
be evil
but do it WELL
>>>>>>>>>>>>>>>

Share this post


Link to post
Share on other sites
(Yar, I still think three pages deep into the lounge is no place for this thread... that aside...)

I've finished the prototype "bassert" function. I didn't want to use assert as I think it may be defined elsewhere and I wrote a far less exciting BAssert in C++ once.

In any event, here's what is going on, after hours of beating my head on the desk tonight until I found my salvation in the form of
symbol-value 


>(setf will '())
NIL

>will
NIL

>(bassert '(name "Will" will))
("Will")

>will
(NAME ("Will"))

>(bassert '(name "William" will))
("William" "Will")

>will
(NAME ("William" "Will"))

>(bassert '(age 26 will))
(26)

>will
(AGE (26) NAME ("William" "Will"))


While I'm still internally tackling the mechanics of the system on a higher level, I need to implement a way to only append to existing lists if that given attribute should be appended to, as obviously things like last name, proper first name and age won't have multiple entries. Cheers.

[edited by - kordova on November 3, 2003 1:19:07 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by bishop_pass
quote:
Original post by flangazor
Unless you can interact with the character (directly or indirectly) in question there is, in my opinion:

a.) little point
b.) not much going on of interest

While the history might supplement a more in depth character system, when it is alone it is not all that intriguing.

Comically, writing it in Lisp wouldn''t be much effort except for the tables of information you wish to select from (names, ethnicities, diseases, occupations, etc).
Your take on the subject, as well as Oluseyi''s, is that the end result is a list of traits articulated into a set of sentences. That''s rather dull, isn''t it? It seems you have chosen to conclude the matter, rather than explore it.

I myself think the subject at hand here dovetails nicely with my other thread, and in fact, the efforts here could serve as inputs to narratives generated by an application such as that envisioned in the other thread.


What is different from what you have proposed and selecting random fields from a database and heuristically linking them together?

I am still not sure which part is so interesting. The idea that you can set up a symbolic database and create dynamic entries, or the heuristic algorithms to generate the versimilitude.

It is entirely possible it hasn''t grokked with me.

Share this post


Link to post
Share on other sites
That's what I had originally done, though I found I couldn't use setf on my own defined getf, and decided to use property lists for the time being. I'll work on that next.

EDIT: Ok, got it.

[edited by - kordova on November 3, 2003 2:17:15 PM]

Share this post


Link to post
Share on other sites
Would it be advisable to write a sort of utility function such as:
(create-instance symbol-name symbol-category) 
for use that would, in the least, define the
instance-of  
sub list of
symbol-name  
? I find this reasonable in that at instantiation, all other categories can be found and appended to the instance-of list, and also so that you couldn't create a list, append a slot to it and then add it's instance-of member that would make the former slot illegal.

[edited by - kordova on November 5, 2003 3:18:17 PM]

Share this post


Link to post
Share on other sites
I have the tendency of, well, not speaking english.

My concern is that someone might do something like this:
(setf some-guy ''())
SOME-GUY

(bassert ''(power-source triple-a some-guy))
((POWER-SOURCE (TRIPLE-A)))

(bassert ''(instance-of human-male some-guy))
((INSTANCE-OF (HUMAN-MALE))(POWER-SOURCE (TRIPLE-A)))

; now here define the human-male category, which does not, nor will it likely ever, define a subslot of type power-source.
This is all under the assumption that the categories, subcategories and slots must be defined before adding the same to a frame/structure.

Share this post


Link to post
Share on other sites
Well, doesn''t bassert automatically make the additional assertion that power-source makes-sense-for human-male once some-guy is said to be an instance-of human-male?

No?

Probably not. bassert should deny the right to make some-guy an instance-of human-male when you attempt to assert it, because he has the slot power-source on him.

Let''s assume that we already had a frame for the slot power-source.

power-source:
instance-of: slot
makes-sense-for: powered-objects


Now, given that, as soon as one asserts that some-guy has a power-source, the bassert function then looks up the info for the power-source slot, determines that the power-source slot is indeed an instance-of slot (it had better be) and that it makes-sense-for powered-objects, thus bassert then asserts that some-guy is an instance-of powered-objects, and assuming that we have defined powered-objects to be mutually disjoint from living-things, then obviously, when we attempt to assert that some-guy is an instance-of human-male, it will deny that, unless human-male is not yet defined. Let''s say that it is not yet defined. Then, of course, when we ultimately try and declare that human-male is a sub-category of living-thing, it should deny that.

So, in the beginning, you want to bootstrap the knowledge base with enough meta-info and common sense to disallow such things. It''s possible.

Share this post


Link to post
Share on other sites
quote:
Original post by bishop_pass
So, in the beginning, you want to bootstrap the knowledge base with enough meta-info and common sense to disallow such things. It's possible.

Ok, that's where I reasoned that you stood. My other question is whether bassert should only allow slots to be added to existing structures (which will do all the necessary checks to make certain no illegal entries) and add a separate instantiation function taking the name and type. This would force a distinction between making and modifying which seems to me to have some benefits.

[edited by - kordova on November 5, 2003 12:11:28 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by kordova
quote:
Original post by bishop_pass
So, in the beginning, you want to bootstrap the knowledge base with enough meta-info and common sense to disallow such things. It''s possible.

Ok, that''s where I reasoned that you stood. My other question is whether bassert should only allow slots to be added to existing structures (which will do all the necessary checks to make certain no illegal entries) and add a separate instantiation function taking the name and type. This would force a distinction between making and modifying which seems to me to have some benefits.
Either way. Your preference would appear to be in part motivated by a desire for less errors. Certainly, you don''t want a typo to cause a new frame to be made when you meant an existing one.

I hope you have (and it appears that you have) caught on to the subtlety that slots are also frames, and part of a hierarchy themselves.

For example, instance-of is a frame, and it looks like this:


instance-of:
((instance-of (slot))
(makes-sense-for (thing))
(inverse (instances)))


So, given that info, we see that when we assert:
(instance-of human-male bob)

assert looks up the frame for instance-of and sees that the inverse is instances, and automatically asserts the inverse relation, that being:
(instances bob human-male)

Also, bob must be an instance-of thing for the slot instance-of to reside on the bob frame, this following from the fact that instance-of makes-sense-for thing.

And lastly, the context under which we use instance-of is to to be a slot on another frame, and that''s why instance-of is an instance-of slot, as opposed to something else, like a frog.

Share this post


Link to post
Share on other sites
Here's what I've got (in terms of utility functions) for the time being:

A. (define-slot '((this-makes-sense-for) this-number-of-entries object))
result: object:
((instance-of (slot))
(makes-sense-for (this-makes-sense-for))
(number-of-entries (this-number-of-entries)))


Details:
1. The parameter number-of-entries can be zero, a positive number or the symbol any.
2. The this-makes-sense-for field is in a list so that a slot may be defined with reusability in mind (something such as a name for example could and would be used just as readily be a person as by a horse).

B. (instantiate '(this-category object))
result: object:
((instance-of (this-category)))


Details:
1. This function also appends object to the (instances) field of this-category.

C. (define-category '(this-sub-category-of object))
result: object:
((sub-category-of (this-sub-category-of)))


Details:
1. This function also appends object to the (sub-categories) field of this-sub-category-of.

D. (b-assert '(slot value object) &optional overwrite-existing-value)
result: object:
((EXISTING-SLOT (EXISTING-VALUE)
(slot (value)))


Details:
1. If slot-makes-sense-for object, either:
a. (slot (value)) is added if an instance of it didn't exist beforehand.
b. value is appened to the existing value list if there is room.
c. value overwrites any existing value for the slot.

[edited by - kordova on November 9, 2003 3:03:47 PM]

Share this post


Link to post
Share on other sites