-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinference.py
executable file
·744 lines (614 loc) · 26.8 KB
/
inference.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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
# inference.py
# ------------
# Licensing Information: You are free to use or extend these projects for
# educational purposes provided that (1) you do not distribute or publish
# solutions, (2) you retain this notice, and (3) you provide clear
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
#
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
# The core projects and autograders were primarily created by John DeNero
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
# Student side autograding was added by Brad Miller, Nick Hay, and
# Pieter Abbeel (pabbeel@cs.berkeley.edu).
import random
import itertools
from typing import List, Dict, Tuple
import busters
import game
import bayesNet as bn
from bayesNet import normalize
import hunters
from util import manhattanDistance, raiseNotDefined
from factorOperations import joinFactorsByVariableWithCallTracking, joinFactors
from factorOperations import eliminateWithCallTracking
from bayesNet import BayesNet
########### ########### ###########
########### QUESTION 1 ###########
########### ########### ###########
def constructBayesNet(gameState):
"""
Constructs a Bayes Net for the ghost hunting world.
"""
pacman = "Pacman"
ghost0 = "Ghost0"
ghost1 = "Ghost1"
obs0 = "Observation0"
obs1 = "Observation1"
walls = gameState.getWalls()
width = walls.width
height = walls.height
positions = [(x,y) for x in range(width) for y in range(height)]
maxDist = (width - 1) + (height - 1)
maxObs = maxDist + 7
variableDomains = {
pacman: positions,
ghost0: positions,
ghost1: positions,
obs0: list(range(maxObs + 1)),
obs1: list(range(maxObs + 1))
}
variables = [pacman, ghost0, ghost1, obs0, obs1]
inEdges = {
pacman: [],
ghost0: [],
ghost1: [],
obs0: [pacman, ghost0],
obs1: [pacman, ghost1]
}
outEdges = {
pacman: [obs0, obs1],
ghost0: [obs0],
ghost1: [obs1],
obs0: [],
obs1: []
}
return BayesNet(variables, inEdges, outEdges, variableDomains)
def inferenceByEnumeration(bayesNet: bn, queryVariables: List[str], evidenceDict: Dict):
"""
An inference by enumeration implementation provided as reference.
This function performs a probabilistic inference query that
returns the factor:
P(queryVariables | evidenceDict)
bayesNet: The Bayes Net on which we are making a query.
queryVariables: A list of the variables which are unconditioned in
the inference query.
evidenceDict: An assignment dict {variable : value} for the
variables which are presented as evidence
(conditioned) in the inference query.
"""
callTrackingList = []
joinFactorsByVariable = joinFactorsByVariableWithCallTracking(callTrackingList)
eliminate = eliminateWithCallTracking(callTrackingList)
# initialize return variables and the variables to eliminate
evidenceVariablesSet = set(evidenceDict.keys())
queryVariablesSet = set(queryVariables)
eliminationVariables = (bayesNet.variablesSet() - evidenceVariablesSet) - queryVariablesSet
# grab all factors where we know the evidence variables (to reduce the size of the tables)
currentFactorsList = bayesNet.getAllCPTsWithEvidence(evidenceDict)
# join all factors by variable
for joinVariable in bayesNet.variablesSet():
currentFactorsList, joinedFactor = joinFactorsByVariable(currentFactorsList, joinVariable)
currentFactorsList.append(joinedFactor)
# currentFactorsList should contain the connected components of the graph now as factors, must join the connected components
fullJoint = joinFactors(currentFactorsList)
# marginalize all variables that aren't query or evidence
incrementallyMarginalizedJoint = fullJoint
for eliminationVariable in eliminationVariables:
incrementallyMarginalizedJoint = eliminate(incrementallyMarginalizedJoint, eliminationVariable)
fullJointOverQueryAndEvidence = incrementallyMarginalizedJoint
# normalize so that the probability sums to one
# the input factor contains only the query variables and the evidence variables,
# both as unconditioned variables
queryConditionedOnEvidence = normalize(fullJointOverQueryAndEvidence)
# now the factor is conditioned on the evidence variables
# the order is join on all variables, then eliminate on all elimination variables
return queryConditionedOnEvidence
########### ########### ###########
########### QUESTION 4 ###########
########### ########### ###########
def inferenceByVariableEliminationWithCallTracking(callTrackingList=None):
def inferenceByVariableElimination(bayesNet, queryVariables, evidenceDict, eliminationOrder):
joinFactorsByVariable = joinFactorsByVariableWithCallTracking(callTrackingList)
eliminate = eliminateWithCallTracking(callTrackingList)
if eliminationOrder is None:
eliminationVariables = bayesNet.variablesSet() - set(queryVariables) - set(evidenceDict.keys())
eliminationOrder = sorted(list(eliminationVariables))
currentFactorsList = bayesNet.getAllCPTsWithEvidence(evidenceDict)
for eliminationVariable in eliminationOrder:
currentFactorsList, joinedFactor = joinFactorsByVariable(currentFactorsList, eliminationVariable)
if joinedFactor is not None:
currentFactorsList.append(joinedFactor)
reducedFactor = eliminate(joinedFactor, eliminationVariable)
currentFactorsList.remove(joinedFactor)
currentFactorsList.append(reducedFactor)
finalFactor = joinFactors(currentFactorsList)
return normalize(finalFactor)
return inferenceByVariableElimination
inferenceByVariableElimination = inferenceByVariableEliminationWithCallTracking()
def sampleFromFactorRandomSource(randomSource=None):
if randomSource is None:
randomSource = random.Random()
def sampleFromFactor(factor, conditionedAssignments=None):
"""
Sample an assignment for unconditioned variables in factor with
probability equal to the probability in the row of factor
corresponding to that assignment.
factor: The factor to sample from.
conditionedAssignments: A dict of assignments for all conditioned
variables in the factor. Can only be None
if there are no conditioned variables in
factor, otherwise must be nonzero.
Useful for inferenceByLikelihoodWeightingSampling
Returns an assignmentDict that contains the conditionedAssignments but
also a random assignment of the unconditioned variables given their
probability.
"""
if conditionedAssignments is None and len(factor.conditionedVariables()) > 0:
raise ValueError("Conditioned assignments must be provided since \n" +
"this factor has conditionedVariables: " + "\n" +
str(factor.conditionedVariables()))
elif conditionedAssignments is not None:
conditionedVariables = set([var for var in conditionedAssignments.keys()])
if not conditionedVariables.issuperset(set(factor.conditionedVariables())):
raise ValueError("Factor's conditioned variables need to be a subset of the \n"
+ "conditioned assignments passed in. \n" + \
"conditionedVariables: " + str(conditionedVariables) + "\n" +
"factor.conditionedVariables: " + str(set(factor.conditionedVariables())))
# Reduce the domains of the variables that have been
# conditioned upon for this factor
newVariableDomainsDict = factor.variableDomainsDict()
for (var, assignment) in conditionedAssignments.items():
newVariableDomainsDict[var] = [assignment]
# Get the (hopefully) smaller conditional probability table
# for this variable
CPT = factor.specializeVariableDomains(newVariableDomainsDict)
else:
CPT = factor
# Get the probability of each row of the table (along with the
# assignmentDict that it corresponds to)
assignmentDicts = sorted([assignmentDict for assignmentDict in CPT.getAllPossibleAssignmentDicts()])
assignmentDictProbabilities = [CPT.getProbability(assignmentDict) for assignmentDict in assignmentDicts]
# calculate total probability in the factor and index each row by the
# cumulative sum of probability up to and including that row
currentProbability = 0.0
probabilityRange = []
for i in range(len(assignmentDicts)):
currentProbability += assignmentDictProbabilities[i]
probabilityRange.append(currentProbability)
totalProbability = probabilityRange[-1]
# sample an assignment with probability equal to the probability in the row
# for that assignment in the factor
pick = randomSource.uniform(0.0, totalProbability)
for i in range(len(assignmentDicts)):
if pick <= probabilityRange[i]:
return assignmentDicts[i]
return sampleFromFactor
sampleFromFactor = sampleFromFactorRandomSource()
class DiscreteDistribution(dict):
"""
A DiscreteDistribution models belief distributions and weight distributions
over a finite set of discrete keys.
"""
def __getitem__(self, key):
self.setdefault(key, 0)
return dict.__getitem__(self, key)
def copy(self):
"""
Return a copy of the distribution.
"""
return DiscreteDistribution(dict.copy(self))
def argMax(self):
"""
Return the key with the highest value.
"""
if len(self.keys()) == 0:
return None
all = list(self.items())
values = [x[1] for x in all]
maxIndex = values.index(max(values))
return all[maxIndex][0]
def total(self):
"""
Return the sum of values for all keys.
"""
return float(sum(self.values()))
########### ########### ###########
########### QUESTION 5a ###########
########### ########### ###########
def normalize(self):
"""
Normalize the distribution such that the total value of all keys sums
to 1. The ratio of values for all keys will remain the same. In the case
where the total value of the distribution is 0, do nothing.
>>> dist = DiscreteDistribution()
>>> dist['a'] = 1
>>> dist['b'] = 2
>>> dist['c'] = 2
>>> dist['d'] = 0
>>> dist.normalize()
>>> list(sorted(dist.items()))
[('a', 0.2), ('b', 0.4), ('c', 0.4), ('d', 0.0)]
>>> dist['e'] = 4
>>> list(sorted(dist.items()))
[('a', 0.2), ('b', 0.4), ('c', 0.4), ('d', 0.0), ('e', 4)]
>>> empty = DiscreteDistribution()
>>> empty.normalize()
>>> empty
{}
"""
"*** YOUR CODE HERE ***"
raiseNotDefined()
"*** END YOUR CODE HERE ***"
def sample(self):
"""
Draw a random sample from the distribution and return the key, weighted
by the values associated with each key.
>>> dist = DiscreteDistribution()
>>> dist['a'] = 1
>>> dist['b'] = 2
>>> dist['c'] = 2
>>> dist['d'] = 0
>>> N = 100000.0
>>> samples = [dist.sample() for _ in range(int(N))]
>>> round(samples.count('a') * 1.0/N, 1) # proportion of 'a'
0.2
>>> round(samples.count('b') * 1.0/N, 1)
0.4
>>> round(samples.count('c') * 1.0/N, 1)
0.4
>>> round(samples.count('d') * 1.0/N, 1)
0.0
"""
"*** YOUR CODE HERE ***"
raiseNotDefined()
"*** END YOUR CODE HERE ***"
class InferenceModule:
"""
An inference module tracks a belief distribution over a ghost's location.
"""
############################################
# Useful methods for all inference modules #
############################################
def __init__(self, ghostAgent):
"""
Set the ghost agent for later access.
"""
self.ghostAgent = ghostAgent
self.index = ghostAgent.index
self.obs = [] # most recent observation position
def getJailPosition(self):
return (2 * self.ghostAgent.index - 1, 1)
def getPositionDistributionHelper(self, gameState, pos, index, agent):
try:
jail = self.getJailPosition()
gameState = self.setGhostPosition(gameState, pos, index + 1)
except TypeError:
jail = self.getJailPosition(index)
gameState = self.setGhostPositions(gameState, pos)
pacmanPosition = gameState.getPacmanPosition()
ghostPosition = gameState.getGhostPosition(index + 1) # The position you set
dist = DiscreteDistribution()
if pacmanPosition == ghostPosition: # The ghost has been caught!
dist[jail] = 1.0
return dist
pacmanSuccessorStates = game.Actions.getLegalNeighbors(pacmanPosition, \
gameState.getWalls()) # Positions Pacman can move to
if ghostPosition in pacmanSuccessorStates: # Ghost could get caught
mult = 1.0 / float(len(pacmanSuccessorStates))
dist[jail] = mult
else:
mult = 0.0
actionDist = agent.getDistribution(gameState)
for action, prob in actionDist.items():
successorPosition = game.Actions.getSuccessor(ghostPosition, action)
if successorPosition in pacmanSuccessorStates: # Ghost could get caught
denom = float(len(actionDist))
dist[jail] += prob * (1.0 / denom) * (1.0 - mult)
dist[successorPosition] = prob * ((denom - 1.0) / denom) * (1.0 - mult)
else:
dist[successorPosition] = prob * (1.0 - mult)
return dist
def getPositionDistribution(self, gameState, pos, index=None, agent=None):
"""
Return a distribution over successor positions of the ghost from the
given gameState. You must first place the ghost in the gameState, using
setGhostPosition below.
"""
if index == None:
index = self.index - 1
if agent == None:
agent = self.ghostAgent
return self.getPositionDistributionHelper(gameState, pos, index, agent)
########### ########### ###########
########### QUESTION 5b ###########
########### ########### ###########
def getObservationProb(self, noisyDistance: int, pacmanPosition: Tuple[int, int], ghostPosition: Tuple[int, int], jailPosition: Tuple[int, int]):
"""
Return the probability P(noisyDistance | pacmanPosition, ghostPosition).
"""
# If ghost is in jail
if ghostPosition == jailPosition:
# If observation is None, probability is 1, else 0
return 1.0 if noisyDistance is None else 0.0
# If ghost is not in jail
# If observation is None but ghost is not in jail, probability is 0
if noisyDistance is None:
return 0.0
# Compute the true Manhattan distance
trueDistance = manhattanDistance(pacmanPosition, ghostPosition)
# Get the observation probability from busters module
return busters.getObservationProbability(noisyDistance, trueDistance)
def setGhostPosition(self, gameState, ghostPosition, index):
"""
Set the position of the ghost for this inference module to the specified
position in the supplied gameState.
Note that calling setGhostPosition does not change the position of the
ghost in the GameState object used for tracking the true progression of
the game. The code in inference.py only ever receives a deep copy of
the GameState object which is responsible for maintaining game state,
not a reference to the original object. Note also that the ghost
distance observations are stored at the time the GameState object is
created, so changing the position of the ghost will not affect the
functioning of observe.
"""
conf = game.Configuration(ghostPosition, game.Directions.STOP)
gameState.data.agentStates[index] = game.AgentState(conf, False)
return gameState
def setGhostPositions(self, gameState, ghostPositions):
"""
Sets the position of all ghosts to the values in ghostPositions.
"""
for index, pos in enumerate(ghostPositions):
conf = game.Configuration(pos, game.Directions.STOP)
gameState.data.agentStates[index + 1] = game.AgentState(conf, False)
return gameState
def observe(self, gameState):
"""
Collect the relevant noisy distance observation and pass it along.
"""
distances = gameState.getNoisyGhostDistances()
if len(distances) >= self.index: # Check for missing observations
obs = distances[self.index - 1]
self.obs = obs
self.observeUpdate(obs, gameState)
def initialize(self, gameState):
"""
Initialize beliefs to a uniform distribution over all legal positions.
"""
self.legalPositions = [p for p in gameState.getWalls().asList(False) if p[1] > 1]
self.allPositions = self.legalPositions + [self.getJailPosition()]
self.initializeUniformly(gameState)
######################################
# Methods that need to be overridden #
######################################
def initializeUniformly(self, gameState):
"""
Set the belief state to a uniform prior belief over all positions.
"""
raise NotImplementedError
def observeUpdate(self, observation, gameState):
"""
Update beliefs based on the given distance observation and gameState.
"""
raise NotImplementedError
def elapseTime(self, gameState):
"""
Predict beliefs for the next time step from a gameState.
"""
raise NotImplementedError
def getBeliefDistribution(self):
"""
Return the agent's current belief state, a distribution over ghost
locations conditioned on all evidence so far.
"""
raise NotImplementedError
class ExactInference(InferenceModule):
"""
The exact dynamic inference module should use forward algorithm updates to
compute the exact belief function at each time step.
"""
def initializeUniformly(self, gameState):
"""
Begin with a uniform distribution over legal ghost positions (i.e., not
including the jail position).
"""
self.beliefs = DiscreteDistribution()
for p in self.legalPositions:
self.beliefs[p] = 1.0
self.beliefs.normalize()
########### ########### ###########
########### QUESTION 6 ###########
########### ########### ###########
def observeUpdate(self, observation, gameState):
"""
Update beliefs based on the distance observation and Pacman's position.
"""
########### ########### ###########
########### QUESTION 7 ###########
########### ########### ###########
def elapseTime(self, gameState: busters.GameState):
"""
Predict beliefs in response to a time step passing from the current
state.
The transition model is not entirely stationary: it may depend on
Pacman's current position. However, this is not a problem, as Pacman's
current position is known.
"""
"*** YOUR CODE HERE ***"
raiseNotDefined()
"*** END YOUR CODE HERE ***"
def getBeliefDistribution(self):
return self.beliefs
class ParticleFilter(InferenceModule):
"""
A particle filter for approximately tracking a single ghost.
"""
def __init__(self, ghostAgent, numParticles=300):
InferenceModule.__init__(self, ghostAgent)
self.setNumParticles(numParticles)
def setNumParticles(self, numParticles):
self.numParticles = numParticles
########### ########### ###########
########### QUESTION 9 ###########
########### ########### ###########
def initializeUniformly(self, gameState: busters.GameState):
"""
Initialize a list of particles. Use self.numParticles for the number of
particles. Use self.legalPositions for the legal board positions where
a particle could be located. Particles should be evenly (not randomly)
distributed across positions in order to ensure a uniform prior. Use
self.particles for the list of particles.
"""
self.particles = []
"*** YOUR CODE HERE ***"
raiseNotDefined()
"*** END YOUR CODE HERE ***"
def getBeliefDistribution(self):
"""
Return the agent's current belief state, a distribution over ghost
locations conditioned on all evidence and time passage. This method
essentially converts a list of particles into a belief distribution.
This function should return a normalized distribution.
"""
"*** YOUR CODE HERE ***"
raiseNotDefined()
"*** END YOUR CODE HERE ***"
########### ########### ###########
########### QUESTION 10 ###########
########### ########### ###########
def observeUpdate(self, observation: int, gameState: busters.GameState):
"""
Update beliefs based on the distance observation and Pacman's position.
The observation is the noisy Manhattan distance to the ghost you are
tracking.
There is one special case that a correct implementation must handle.
When all particles receive zero weight, the list of particles should
be reinitialized by calling initializeUniformly. The total method of
the DiscreteDistribution may be useful.
"""
"*** YOUR CODE HERE ***"
raiseNotDefined()
"*** END YOUR CODE HERE ***"
########### ########### ###########
########### QUESTION 11 ###########
########### ########### ###########
def elapseTime(self, gameState):
"""
Sample each particle's next state based on its current state and the
gameState.
"""
"*** YOUR CODE HERE ***"
raiseNotDefined()
"*** END YOUR CODE HERE ***"
class JointParticleFilter(ParticleFilter):
"""
JointParticleFilter tracks a joint distribution over tuples of all ghost
positions.
"""
def __init__(self, numParticles=600):
self.setNumParticles(numParticles)
def initialize(self, gameState, legalPositions):
"""
Store information about the game, then initialize particles.
"""
self.numGhosts = gameState.getNumAgents() - 1
self.ghostAgents = []
self.legalPositions = legalPositions
self.initializeUniformly(gameState)
########### ########### ###########
########### QUESTION 12 ###########
########### ########### ###########
def initializeUniformly(self, gameState):
"""
Initialize particles to be consistent with a uniform prior. Particles
should be evenly distributed across positions in order to ensure a
uniform prior.
"""
self.particles = []
"*** YOUR CODE HERE ***"
raiseNotDefined()
"*** END YOUR CODE HERE ***"
def addGhostAgent(self, agent):
"""
Each ghost agent is registered separately and stored (in case they are
different).
"""
self.ghostAgents.append(agent)
def getJailPosition(self, i):
return (2 * i + 1, 1)
def observe(self, gameState):
"""
Resample the set of particles using the likelihood of the noisy
observations.
"""
observation = gameState.getNoisyGhostDistances()
self.observeUpdate(observation, gameState)
########### ########### ###########
########### QUESTION 13 ###########
########### ########### ###########
def observeUpdate(self, observation, gameState):
"""
Update beliefs based on the distance observation and Pacman's position.
The observation is the noisy Manhattan distances to all ghosts you
are tracking.
There is one special case that a correct implementation must handle.
When all particles receive zero weight, the list of particles should
be reinitialized by calling initializeUniformly. The total method of
the DiscreteDistribution may be useful.
"""
"*** YOUR CODE HERE ***"
raiseNotDefined()
"*** END YOUR CODE HERE ***"
########### ########### ###########
########### QUESTION 14 ###########
########### ########### ###########
def elapseTime(self, gameState):
"""
Sample each particle's next state based on its current state and the
gameState.
"""
newParticles = []
for oldParticle in self.particles:
newParticle = list(oldParticle) # A list of ghost positions
# now loop through and update each entry in newParticle...
"*** YOUR CODE HERE ***"
raiseNotDefined()
"""*** END YOUR CODE HERE ***"""
newParticles.append(tuple(newParticle))
self.particles = newParticles
# One JointInference module is shared globally across instances of MarginalInference
jointInference = JointParticleFilter()
class MarginalInference(InferenceModule):
"""
A wrapper around the JointInference module that returns marginal beliefs
about ghosts.
"""
def initializeUniformly(self, gameState):
"""
Set the belief state to an initial, prior value.
"""
if self.index == 1:
jointInference.initialize(gameState, self.legalPositions)
jointInference.addGhostAgent(self.ghostAgent)
def observe(self, gameState):
"""
Update beliefs based on the given distance observation and gameState.
"""
if self.index == 1:
jointInference.observe(gameState)
def elapseTime(self, gameState):
"""
Predict beliefs for a time step elapsing from a gameState.
"""
if self.index == 1:
jointInference.elapseTime(gameState)
def getBeliefDistribution(self):
"""
Return the marginal belief over a particular ghost by summing out the
others.
"""
jointDistribution = jointInference.getBeliefDistribution()
dist = DiscreteDistribution()
for t, prob in jointDistribution.items():
dist[t[self.index - 1]] += prob
return dist