-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcharacter.py
380 lines (354 loc) · 21 KB
/
character.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
import random
from models import Thing, Object, Knowledge, Location, Goal, GoalType, Actions
from utilities import safe_append, safe_extend, persons_house, persons_shop, TAVERN
import output
class Character(Thing):
def __init__(self, name, location = None, positive_talking_points = set(), negative_talking_points = set(), political_views = set(), domains = set(), out = []):
Thing.__init__(self, name)
self.relationships = {}
self.schedule_time = 0
self.location = location if location is not None else Location(persons_house(self))
self.knowledge = [] # Knowledge character has acquired through witnessing or conversing with other characters
self.told_knowledge = {} # Knowledge that was already told. Key: other person, Value: knowledge
self.out = out # Queue output so we can filter out unimportant output later
self.positive_talking_points = positive_talking_points # type: list
self.negative_talking_points = negative_talking_points # type: list
self.goals = [Goal(GoalType.NONE)] # prioritised list of goals
self.political_views = political_views # type: set
self.domains = domains # type: set
self.dead = False
self.step = 0
self.protagonist = False
'''Methods to perform to react to newly acquired knowledge'''
self.reactions = {
Actions.KILL: self.react_to_kill,
Actions.BEAT_UP: self.react_to_beat_up
}
'''Methods to perform based on current goal (mainly deals with choosing which location to visit)'''
self.schedule_methods = {
GoalType.GET_OBJECT: self.sched_getObject,
GoalType.BEFRIEND: self.sched_befriend,
GoalType.KILL: self.sched_kill,
GoalType.NONE: self.sched_none
}
'''
Methods to perform when commiting some action
Returns tuple, where rhs is output that needs to be printed after SomeAction line,
and lhs is context required for that
'''
self.action_methods = {
Actions.KILL: self.do_kill,
Actions.BEAT_UP: self.do_beat_up,
Actions.INSULT: self.do_insult,
Actions.CONVERSE: self.do_converse
}
def setWorldData(self, locations, objects):
self.locations = locations
self.objects = objects
def __str__(self):
return "Name: {NAME} Goals {GOALS} Political views: {POLITICAL_VIEWS} Location: {LOCATION} Domains: {DOMAINS} Positive talking points: {POSITIVE_TALKING_POINTS} Negative talking points: {NEGATIVE_TALKING_POINTS} Dead: {DEAD}".format(NAME = self.name, GOALS = self.goals, LOCATION = self.location, POLITICAL_VIEWS = self.political_views, DOMAINS = self.domains, POSITIVE_TALKING_POINTS = self.positive_talking_points, NEGATIVE_TALKING_POINTS = self.negative_talking_points, DEAD = self.dead)
def __repr__(self):
return "<Character {}>".format(self.__str__())
def findThing(self, thing):
'''
If things location is known, visits that location and tries to find it. Otherwise default location visitation
Returns true if thing was found
'''
wrong_knowledge = False
tried_locations = [know.target for know in self.knowledge if know.action == Actions.NOT_LOCATED_IN]
for know in self.knowledge:
if know.action == Actions.LOCATED_IN and know.subject == thing and know.target not in tried_locations:
if self.location == know.target:
if thing.location == self.location:
return True
else:
wrong_knowledge = True
break
else:
context = [output.Context(output.KnowledgeLearned, {'learner': self, 'knowledge': know})]
self.out.append(output.GoesTo(self.step, self.location, context, self, know.target, True))
self.location = know.target
return False
elif know.action == Actions.OWNED_BY and know.subject == thing:
self.findThing(know.target)
if wrong_knowledge:
self.knowledge = [k for k in self.knowledge if not(k.subject == thing and k.action == Actions.LOCATED_IN and k.target == self.location)]
self.knowledge.append(Knowledge(self, thing, Actions.NOT_LOCATED_IN, self.location, self.step))
context = [output.Context(output.GoesTo, {'character': self, 'place': self.location})]
self.out.append(output.DidntFind(self.step, self.location, context, self, thing))
return False
self.sched_none(None, None)
def sched_getObject(self, arg1, arg2):
if self.findThing(arg1):
arg1.owner = self
arg1.location = None
self.goals = [goal for goal in self.goals if goal.target1 != arg1]
self.knowledge = [know for know in self.knowledge if not(know.action == Actions.LOCATED_IN and know.subject == arg1)]
self.knowledge.append(Knowledge(self, arg1, Actions.OWNED_BY, self, self.step))
context = [output.Context(output.GoesTo, {'character': self, 'place': self.location})]
self.out.append(output.Got(self.step, self.location, context, self, arg1))
def sched_befriend(self, arg1, arg2):
if arg2 == None:
print("not implemented")
else:
print("not implemented")
def sched_kill(self, arg1, arg2):
'''
If arg1 and arg2 not None, tries to manipulate arg1 into killing arg2 by generating lies
If only arg1 is defined, tries to kill arg1
'''
if arg2 == None:
self.findThing(arg1)
else:
# self wants arg1 to kill arg2
if arg1.location != self.location:
self.sched_none(None, None)
return
if arg1.relationships[self] < 0: # arg1 doesn't like me, better chat him up
method_tuple = self.do_converse(arg1, [])
converseOutput = [output.SomeAction(self.step, self.location, [], self, arg1, Actions.CONVERSE)] + method_tuple[1]
self.out.extend(converseOutput)
return
#Use knowledge to generate a lie that would turn arg1 against arg2
knownObjects = [know for know in self.knowledge if (know.action == Actions.LOCATED_IN or know.action == Actions.OWNED_BY) and type(know.subject) is Object]
acquaintances = [know for know in self.knowledge if know.subject == arg1 and know.action == Actions.LIKES and know.value >= 0 and know.value < 0.5]
friends = [know for know in self.knowledge if know.subject == arg1 and know.action == Actions.LIKES and know.value >= 0.5]
if knownObjects and random.random() < 0.5:
targetObjectKnowledge = random.choice(knownObjects)
self.tellLie(arg1, targetObjectKnowledge.subject, Actions.OWNED_BY, arg2, targetObjectKnowledge)
safe_extend(self.told_knowledge, arg1, [know for know in self.knowledge if know.subject == targetObjectKnowledge.subject])
else:
if friends:
deadFriends = [friend for friend in friends if friend.target.dead]
if deadFriends:
deadFriendKnowledge = random.choice(deadFriends)
self.tellLie(arg1, arg2, Actions.KILL, deadFriendKnowledge.target, deadFriendKnowledge)
safe_extend(self.told_knowledge, arg1, [know for know in self.knowledge if know.target == deadFriendKnowledge.target and know.action == Actions.KILL])
else:
friendKnowledge = random.choice(friends)
self.tellLie(arg1, arg2, Actions.BEAT_UP, friendKnowledge.target, friendKnowledge)
safe_extend(self.told_knowledge, arg1, [know for know in self.knowledge if know.target == friendKnowledge.target and know.action == Actions.BEAT_UP])
elif acquaintances:
acquaintanceKnowledge = random.choice(acquaintances)
self.tellLie(arg1, arg2, Actions.BEAT_UP, acquaintanceKnowledge.target, acquaintanceKnowledge)
safe_extend(self.told_knowledge, arg1, [know for know in self.knowledge if know.target == acquaintanceKnowledge.target and know.action == Actions.BEAT_UP])
def sched_none(self, arg1, arg2):
if self.schedule_time <= 0:
newlocation = random.choice([loc for loc in self.locations if self.location != loc])
self.out.append(output.GoesTo(self.step, self.location, [], self, newlocation))
self.location = newlocation
self.schedule_time = random.randint(2, 8)
'''if self.location.name == persons_house(self):
newlocation = Location(persons_shop(self))
self.out.append(output.GoesTo(self.step, self.location, [], self, newlocation))
self.location = newlocation
self.schedule_time = random.randint(2, 8)
else:
locations = [Location(persons_shop(x)) for x in self.relationships.keys() if not x.dead] + [Location(TAVERN), Location(persons_house(self))]
newlocation = random.choice(locations)
self.out.append(output.GoesTo(self.step, self.location, [], self, newlocation))
self.location = newlocation
if self.location.name == persons_house(self):
self.schedule_time = random.randint(2, 8)
elif self.location.name == TAVERN:
self.schedule_time = random.randint(1, 4)'''
def tellLie(self, target, arg1, action, arg2, contextKnowledge):
lie = Knowledge(self, arg1, action, arg2, self.step)
safe_append(target.told_knowledge, self, lie)
safe_append(self.told_knowledge, target, lie)
target.acquire_knowledge(lie)
context = [
output.Context(output.ExposManipulationMotivation, {'subject': self, 'killer': target}),
output.Context(output.KnowledgeLearned, {'learner': self, 'knowledge': contextKnowledge}),
output.Context(output.KnowledgeLearned, {'learner': target, 'knowledge': lie})
]
self.out.append(output.WasLying(self.step, self.location, context, self, target, lie))
def schedule_step(self, step):
self.knowledge = [know for know in self.knowledge if not(know.subject == self and know.action == Actions.LIKES)]
self.knowledge.extend([Knowledge(self, self, Actions.LIKES, x, step, self.relationships[x]) for x in self.relationships.keys()])
self.step = step
goal = self.goals[0]
self.schedule_methods[goal.type](goal.target1, goal.target2)
self.schedule_time -= 1
def change_relationship(self, person, delta, context, printNow=True):
if self == person or person not in self.relationships:
return
oldvalue = self.relationships[person]
self.relationships[person] += delta
if self.relationships[person] > 1:
self.relationships[person] = 1
if self.relationships[person] < -1:
self.relationships[person] = -1
pendingOutput = output.RelationshipChange(self.step, self.location, context, self, person, oldvalue, self.relationships[person])
if printNow:
self.out.append(pendingOutput)
else:
return pendingOutput
def source_trust(self, source):
if self == source:
return 1
return self.relationships[source]
def choose_who_to_trust(self, newsource, oldsources):
oldsources.sort(key=lambda source: self.source_trust(source), reverse=True)
if self.relationships[oldsources[0]] < self.relationships[newsource] or newsource == self:
for source in oldsources:
return True
if self.relationships[oldsources[0]] > self.relationships[newsource] or oldsources[0] == self:
return False
if random.random() < 0.5:
for source in oldsources:
return True
return False
def acquire_knowledge(self, knowledge):
if knowledge in self.knowledge:
return
if knowledge.subject == self and knowledge.source != self:
return
if knowledge.target == self and knowledge.source != self and knowledge.action.ignore_if_self_is_target:
return
discussionContext = [output.Context(output.SomeAction, {'source': self, 'target': knowledge.source, 'action': Actions.CONVERSE})]
conflicting_sources = [know.source for know in self.knowledge if know.subject == knowledge.subject and know.target != knowledge.target]
#Attempt at managing sources of conflicting knowledge and feeling betrayed if suspecting someone of lying
'''if conflicting_sources and knowledge.action.react_badly_if_conflicting_targets:
if self.choose_who_to_trust(knowledge.source, conflicting_sources):
self.knowledge.append(knowledge)
self.out.append(output.KnowledgeLearned(self.step, self.location, discussionContext, self, knowledge))
if knowledge.action in self.reactions.keys():
self.reactions[knowledge.action](knowledge)
for source in conflicting_sources:
self.knowledge = [know for know in self.knowledge if not(know.source == source)]
context = [output.Context(output.KnowledgeLearned, {'learner': self, 'knowledge.source': source})]
self.out.append(output.LieReveal(self.step, self.location, context, self, source))
self.change_relationship(source, -0.25, [])
else:
self.out.append(output.KnowledgeLearned(self.step, self.location, discussionContext, self, knowledge))
context = [output.Context(output.KnowledgeLearned, {'learner': self, 'knowledge': knowledge})]
self.out.append(output.NewInfoIsLie(self.step, self.location, context, self, knowledge))
self.change_relationship(knowledge.source, -0.25, [])
else:'''
self.knowledge.append(knowledge)
if not(knowledge.source == self and (knowledge.subject == self or knowledge.target == self)):
self.out.append(output.KnowledgeLearned(self.step, self.location, discussionContext, self, knowledge))
if knowledge.action in self.reactions.keys():
self.reactions[knowledge.action](knowledge)
def react_to_kill(self, knowledge):
if self == knowledge.target:
return
context = [
output.Context(output.ExposRelationship, {'source': self, 'target': knowledge.target}),
output.Context(output.ExposRelationship, {'source': knowledge.target, 'target': self}),
output.Context(output.RelationshipChange, {'source': self, 'target': knowledge.target}),
]
self.goals = [goal for goal in self.goals if not(goal.type == GoalType.KILL and (goal.target1 == knowledge.target or goal.target2 == knowledge.target))]
if self.relationships[knowledge.target] > 0.5:
self.relationships[knowledge.subject] = -1
self.goals.insert(0, Goal(GoalType.KILL, knowledge.subject))
context = [output.Context(output.KnowledgeLearned, {'learner': self, 'knowledge': knowledge})]
self.out.append(output.VowRevenge(self.step, self.location, context, self, knowledge.subject, knowledge.target))
elif self.relationships[knowledge.target] > 0:
self.change_relationship(knowledge.subject, -0.25, context)
elif self.relationships[knowledge.target] < -0.5:
self.change_relationship(knowledge.subject, 0.25, context)
def react_to_beat_up(self, knowledge):
if self == knowledge.target:
return
context = [
output.Context(output.ExposRelationship, {'source': self, 'target': knowledge.target}),
output.Context(output.ExposRelationship, {'source': knowledge.target, 'target': self}),
output.Context(output.RelationshipChange, {'source': self, 'target': knowledge.target}),
]
if self.relationships[knowledge.target] > 0.5:
self.change_relationship(knowledge.subject, -0.5, context)
elif self.relationships[knowledge.target] > 0:
self.change_relationship(knowledge.subject, -0.25, context)
elif self.relationships[knowledge.target] < -0.5:
self.change_relationship(knowledge.subject, 0.25, context)
def do_kill(source, target, chars_to_ignore):
target.dead = True
chars_to_ignore.append(target)
source.knowledge = [know for know in source.knowledge if not(know.action == Actions.OWNED_BY and know.target == target)]
additionalOutput = []
for object in source.objects:
if object.owner == target:
source.knowledge = [know for know in source.knowledge if not(know.action == Actions.LOCATED_IN and know.subject == object)]
object.owner = source
object.location = None
source.goals = [goal for goal in source.goals if goal.target1 != object]
context = [output.Context(output.Got, {'character': target, 'object': object})]
additionalOutput.append(output.Steal(source.step, source.location, context, source, target, object))
source.schedule_time = 0
return ([
output.Context(output.ExposRelationship, {'source': source, 'target': target}),
output.Context(output.ExposRelationship, {'source': target, 'target': source}),
output.Context(output.RelationshipChange, {'source': source, 'target': target}),
output.Context(output.VowRevenge, {'source': source, 'target': target}),
], additionalOutput)
def do_beat_up(source, target, chars_to_ignore):
pendingOutput = target.change_relationship(source, -1, [], False)
chars_to_ignore.append(target)
source.knowledge = [know for know in source.knowledge if not(know.action == Actions.OWNED_BY and know.target == target)]
additionalOutput = [pendingOutput]
for object in source.objects:
if object.owner == target:
source.knowledge = [know for know in source.knowledge if not(know.action == Actions.LOCATED_IN and know.subject == object)]
object.owner = source
object.location = None
source.goals = [goal for goal in source.goals if goal.target1 != object]
context = [output.Context(output.Got, {'character': target, 'object': object})]
additionalOutput.append(output.Steal(source.step, source.location, context, source, target, object))
source.schedule_time = 0
return ([
output.Context(output.ExposRelationship, {'source': source, 'target': target}),
output.Context(output.ExposRelationship, {'source': target, 'target': source}),
output.Context(output.RelationshipChange, {'source': source, 'target': target}),
], additionalOutput)
def do_insult(source, target, chars_to_ignore):
pendingOutput = target.change_relationship(source, -0.25, [], False)
return ([
output.Context(output.ExposRelationship, {'source': source, 'target': target}),
output.Context(output.ExposRelationship, {'source': target, 'target': source}),
output.Context(output.RelationshipChange, {'source': source, 'target': target}),
], [pendingOutput])
def do_converse(source, target, chars_to_ignore):
pendingOutput = target.change_relationship(source, 0.25, [], False)
if source in target.told_knowledge:
untold_knowledge = [x for x in target.knowledge if x not in target.told_knowledge[source]]
else:
untold_knowledge = [x for x in target.knowledge]
relationshipGossip = [x for x in untold_knowledge if x.action == Actions.LIKES]
eventGossip = [x for x in untold_knowledge if x.action != Actions.LIKES]
topics_rel = relationshipGossip if len(relationshipGossip) <= 3 else random.sample(relationshipGossip, 3)
topics_eve = eventGossip if len(eventGossip) <= 3 else random.sample(eventGossip, 3)
conversation_topics = topics_eve + topics_rel
for topic in conversation_topics:
topic.source = target
safe_append(target.told_knowledge, source, topic)
safe_append(source.told_knowledge, target, topic)
source.acquire_knowledge(topic)
return ([
output.Context(output.ExposRelationship, {'source': source, 'target': target}),
output.Context(output.ExposRelationship, {'source': target, 'target': source}),
output.Context(output.RelationshipChange, {'source': source, 'target': target}),
], [pendingOutput])
def make_decision(source, target):
'''
Decide which action to take when confronting target
'''
if [goal for goal in source.goals if goal.type == GoalType.KILL and goal.target1 == target and goal.target2 == None]:
return Actions.KILL
if [goal.target1 for goal in source.goals if goal.type == GoalType.GET_OBJECT and goal.target1.owner == target]:
if source.relationships[target] < -0.75:
return Actions.KILL
else:
return Actions.BEAT_UP
if source.relationships[target] < -0.75:
return Actions.KILL
if source.relationships[target] < -0.5:
return Actions.BEAT_UP
if source.relationships[target] < -0.25:
return Actions.INSULT
if source.relationships[target] > 0:
return Actions.CONVERSE
return Actions.NONE
def description(self):
"{NAME}".format(NAME=self.name)