Module gamecontext
[hide private]
[frames] | no frames]

Source Code for Module gamecontext

  1  #!/usr/bin/python3 
  2  """ This module implements the GameContext class """ 
  3  from gameobject import GameObject 
  4   
  5   
6 -class GameContext(GameObject):
7 """ 8 A GameContext corresponds to a geographic location and is a collection 9 of GameObjects, GameActors and state attributes. They exist in 10 higherarchical relationships (e.g. kingdom, village, buiding, room). 11 """ 12
13 - def __init__(self, name="context", descr=None, parent=None):
14 """ 15 create a new GameObject 16 @param name: display name of this object 17 @param descr: human description of this object 18 """ 19 super(GameContext, self).__init__(name, descr) 20 self.parent = parent 21 self.party = [] 22 self.npcs = []
23
24 - def get(self, attribute):
25 """ 26 return the value of an attribute 27 28 Differs from base class because calls flow up the chain of 29 parents if this instance does not have the requested attribute. 30 31 @param attribute: name of attribute to be fetched 32 @return: (string) value (or None) 33 """ 34 if attribute in self.attributes: 35 return self.attributes[attribute] 36 elif self.parent is not None: 37 return self.parent.get(attribute) 38 return None
39
40 - def possible_actions(self, actor, context):
41 """ 42 return a list of possible actions in this context 43 44 This base class merely passes that list up to our parent. 45 46 @param actor: GameActor initiating the action 47 @param context: GameContext for this action (should be "self") 48 @return: list of possible GameActions 49 """ 50 # default: return our parent's list of possible_actions 51 actions = super(GameContext, self).possible_actions(actor, context) 52 return actions
53
54 - def accept_action(self, action, actor, context):
55 """ 56 receive and process the effects of an action 57 58 The only verb supported by this base class is SEARCH, which it passes 59 on to any hidden (RESISTANCE.SEARCH > 0) object in this context. 60 61 @param action: GameAction being performed 62 @param actor: GameActor initiating the action 63 @param context: GameContext in which the action is happening 64 65 @return: (boolean success, string description of the effect) 66 """ 67 68 if action.verb == "SEARCH": 69 found_stuff = False 70 result = "" 71 # look for any object with a RESISTANCE.SEARCH 72 for thing in self.objects: 73 concealment = thing.get("RESISTANCE.SEARCH") 74 if concealment is not None and concealment > 0: 75 # pass the SEARCH action on to that object 76 (success, descr) = thing.accept_action(action, actor, 77 context) 78 if success: 79 found_stuff = True 80 if result == "": 81 result = descr 82 else: 83 result += "\n " + descr 84 return(found_stuff, result) 85 86 # if we don't recognize this action, pass it up the chain 87 return super(GameContext, self).accept_action(action, 88 actor, context)
89
90 - def get_party(self):
91 """ 92 @return: list of player GameActors in this context 93 """ 94 return self.party
95
96 - def add_member(self, member):
97 """ 98 Add an player character to this context 99 @param member: (GameActor) player to be added 100 """ 101 if member not in self.party: 102 self.party.append(member)
103
104 - def remove_member(self, member):
105 """ 106 Remove a player character from this context 107 @param member: (GameActor) player to be removed 108 """ 109 if member in self.party: 110 self.party.remove(member)
111
112 - def get_npcs(self):
113 """ 114 return a list of the NPCs GameActors in this context 115 """ 116 return self.npcs
117
118 - def add_npc(self, npc):
119 """ 120 Add an NPC to this context 121 @param npc: (GameActor) the NPC to be added 122 """ 123 if npc not in self.npcs: 124 self.npcs.append(npc)
125
126 - def remove_npc(self, npc):
127 """ 128 Remove a non-player character from this context 129 @param npc: (GameActor) NPC to be removed 130 """ 131 if npc in self.npcs: 132 self.npcs.remove(npc)
133 134 135 # UNIT TESTING 136 # pylint: disable=too-many-statements
137 -def member_tests():
138 """ 139 exercise {add,remove}_member and get_party 140 - new & empty 141 - add one, add a second 142 - delete the first, add a third 143 - delete the third, delete the second 144 """ 145 a_1 = GameObject("a1") 146 a_2 = GameObject("a2") 147 a_3 = GameObject("a3") 148 149 tried = 0 150 passed = 0 151 152 print("creating a new GameContext and confirming no party") 153 cxt = GameContext() 154 party = cxt.get_party() 155 tried += 1 156 assert not party, "new GameContext is not empty" 157 passed += 1 158 159 print("adding a first member and confirming party of one") 160 cxt.add_member(a_1) 161 party = cxt.get_party() 162 tried += 2 163 assert len(party) == 1, "after adding first party member, len != 1" 164 assert a_1 in party,\ 165 "after adding first party member, he is not in the party" 166 passed += 2 167 168 print("adding a second member and confirming party of two") 169 cxt.add_member(a_2) 170 party = cxt.get_party() 171 tried += 3 172 assert len(party) == 2, "after adding second party member, len != 2" 173 assert a_1 in party,\ 174 "after adding second party member, first is no longer there" 175 assert a_2 in party,\ 176 "after adding second party member, he is not there" 177 passed += 3 178 179 print("removing first member and confirming party of one") 180 cxt.remove_member(a_1) 181 party = cxt.get_party() 182 tried += 3 183 assert len(party) == 1, "after removing first party member, len != 1" 184 assert a_1 not in party,\ 185 "after removing first party member, he is still there" 186 assert a_2 in party,\ 187 "after removing first party member, second is no longer there" 188 passed += 3 189 190 print("adding a third member and confirming party of two") 191 cxt.add_member(a_3) 192 party = cxt.get_party() 193 tried += 3 194 assert len(party) == 2, "after adding another party member, len != 2" 195 assert a_2 in party,\ 196 "after adding another party member, previous is no longer there" 197 assert a_3 in party, "after adding another party member, he is not there" 198 passed += 3 199 200 print("removing third member and confirming party of one") 201 cxt.remove_member(a_3) 202 party = cxt.get_party() 203 tried += 3 204 assert len(party) == 1, "after removing another party member, len != 1" 205 assert a_3 not in party,\ 206 "after removing another party member, he is still there" 207 assert a_2 in party,\ 208 "after removing another party member, second is no longer there" 209 passed += 3 210 211 print("removing final member and confirming no party") 212 cxt.remove_member(a_2) 213 party = cxt.get_party() 214 tried += 2 215 assert not party, "after removing final party member, len != 0" 216 assert a_2 not in party,\ 217 "after removing final party member, he is still there" 218 passed += 2 219 220 print() 221 return (tried, passed)
222 223 224 # pylint: disable=too-many-statements
225 -def npc_tests():
226 """ 227 exercise {add,remove}_npc and get_npcs 228 - new & empty 229 - add one, add a second 230 - delete the first, add a third 231 - delete the third, delete the second 232 """ 233 a_1 = GameObject("a1") 234 a_2 = GameObject("a2") 235 a_3 = GameObject("a3") 236 237 tried = 0 238 passed = 0 239 240 print("creating a new GameContext and confirming no NPCs") 241 cxt = GameContext() 242 party = cxt.get_npcs() 243 tried += 1 244 assert not party, \ 245 "new GameContext is not empty" 246 passed += 1 247 248 print("adding first NPC and confirming one NPC") 249 cxt.add_npc(a_1) 250 party = cxt.get_npcs() 251 tried += 2 252 assert len(party) == 1, \ 253 "after adding first NPC, len != 1" 254 assert a_1 in party, \ 255 "after adding first NPC, he is not in the party" 256 passed += 2 257 258 print("adding second NPC and confirming two NPCs") 259 cxt.add_npc(a_2) 260 party = cxt.get_npcs() 261 tried += 3 262 assert len(party) == 2, \ 263 "after adding second NPC, len != 2" 264 assert a_1 in party, \ 265 "after adding second NPC, first is no longer there" 266 assert a_2 in party, \ 267 "after adding second NPC, he is not there" 268 passed += 3 269 270 print("removing first NPC and confirming one NPC") 271 cxt.remove_npc(a_1) 272 party = cxt.get_npcs() 273 tried += 3 274 assert len(party) == 1, \ 275 "after removing first NPC, len != 1" 276 assert a_1 not in party, \ 277 "after removing first NPC, he is still there" 278 assert a_2 in party, \ 279 "after removing first NPC, second is no longer there" 280 passed += 3 281 282 print("adding third NPC and confirming two NPCs") 283 cxt.add_npc(a_3) 284 party = cxt.get_npcs() 285 tried += 3 286 assert len(party) == 2, \ 287 "after adding another NPC, len != 2" 288 assert a_2 in party, \ 289 "after adding another NPC, previous is no longer there" 290 assert a_3 in party, \ 291 "after adding another NPC, he is not there" 292 passed += 3 293 294 print("removing third NPC and confirming one NPC") 295 cxt.remove_npc(a_3) 296 party = cxt.get_npcs() 297 tried += 3 298 assert len(party) == 1, \ 299 "after removing another NPC, len != 1" 300 assert a_3 not in party, \ 301 "after removing another NPC, he is still there" 302 assert a_2 in party, \ 303 "after removing another NPC, second is no longer there" 304 passed += 3 305 306 print("removing final NPC and confirming no NPCs") 307 cxt.remove_npc(a_2) 308 party = cxt.get_npcs() 309 tried += 2 310 assert not party, \ 311 "after removing final NPC, len != 0" 312 assert a_2 not in party, \ 313 "after removing final NPC, he is still there" 314 passed += 2 315 316 print() 317 return (tried, passed)
318 319
320 -def get_tests():
321 """ 322 exercise hierarchical gets 323 324 set distinct and overlapping properties at various levels of nested 325 GameContexts, and see which values are returned from gets in 326 different levels: 327 - each level has a different value 328 - different levels define different properties 329 - a property in no level will return None 330 331 """ 332 tried = 0 333 passed = 0 334 335 c_1 = GameContext("C1") 336 c_2 = GameContext("C2", parent=c_1) 337 c_3 = GameContext("C3", parent=c_2) 338 339 print("gets are satisfied from nearest context") 340 c_1.set("CAME_FROM", 1) 341 c_2.set("CAME_FROM", 2) 342 c_3.set("CAME_FROM", 3) 343 344 ret = c_1.get("CAME_FROM") 345 tried += 1 346 assert ret == 1, \ 347 "grand-parent.get() returns " + str(ret) + "!=1" 348 passed += 1 349 ret = c_2.get("CAME_FROM") 350 tried += 1 351 assert ret == 2, \ 352 "parent.get() returns " + str(ret) + "!=2" 353 passed += 1 354 ret = c_3.get("CAME_FROM") 355 tried += 1 356 assert ret == 3, \ 357 "child.get() returns " + str(ret) + "!=1" 358 passed += 1 359 360 print("gets follow parents if necessary") 361 c_3.set("IN_THREE", 3) 362 ret = c_3.get("IN_THREE") 363 tried += 1 364 assert ret == 3, \ 365 "child.get() does not return its own value" 366 passed += 1 367 c_2.set("IN_TWO", 2) 368 ret = c_3.get("IN_TWO") 369 tried += 1 370 assert ret == 2, \ 371 "child.get() does not return parent's value" 372 passed += 1 373 c_1.set("IN_ONE", 1) 374 ret = c_3.get("IN_ONE") 375 tried += 1 376 assert ret == 1, \ 377 "child.get() does not return grand-parents value" 378 passed += 1 379 380 print("absent values reported as None") 381 ret = c_3.get("NOWHERE") 382 tried += 1 383 assert ret is None, \ 384 "get() of non-existent value returns " + str(ret) 385 passed += 1 386 387 print() 388 return (tried, passed)
389 390
391 -class TestObject(GameObject):
392 """ 393 A TestObject is a hidden object that will be found after a 394 specified number of searches. 395 """
396 - def __init__(self, name, searches):
397 """ 398 a TestObject will be hidden for a specified number of searches, 399 after which it will be found 400 401 @param name: (string) name of the object 402 @param searches: (int) number of searches to find it 403 """ 404 super(TestObject, self).__init__(name, "findable object") 405 self.set("RESISTANCE.SEARCH", searches) 406 self.set("SEARCHES", 0)
407
408 - def accept_action(self, action, actor, context):
409 """ 410 receive and process the effects of an action 411 412 The only verb supported by this test class is SEARCH, 413 which will succeed after a specified number of tries 414 415 @param action: GameAction being performed 416 @param actor: GameActor initiating the action 417 @param context: GameContext in which the action is happening 418 419 @return: (boolean success, string description of the effect) 420 """ 421 if action.verb != "SEARCH": 422 return (False, "Unsupported action") 423 424 resistance = self.get("RESISTANCE.SEARCH") 425 searches = self.get("SEARCHES") + 1 426 self.set("SEARCHES", searches) 427 428 if searches >= resistance: 429 return (True, self.name) 430 return (False, "!" + self.name)
431 432
433 -def search_tests():
434 """ 435 exercise search for hidden objects 436 - create a context with one non-hidden and three hidden test objects 437 - the test objects will be discovered after 1, 2, and 3 searches 438 - do three searches and confirm the right objects found by each 439 """ 440 cxt = GameContext("searchable") 441 442 # create three test objects w/different degrees of hiddenness 443 o_1 = TestObject("one", 1) 444 o_1.set("RESISTANCE.SEARCH", 1) 445 cxt.add_object(o_1) 446 o_2 = TestObject("two", 2) 447 o_2.set("RESISTANCE.SEARCH", 2) 448 cxt.add_object(o_2) 449 o_3 = TestObject("three", 3) 450 o_3.set("RESISTANCE.SEARCH", 3) 451 cxt.add_object(o_3) 452 453 # create a (verb only) search action 454 search = GameObject("SEARCH OPERATION") 455 search.verb = "SEARCH" 456 457 tried = 0 458 passed = 0 459 460 print("first search for (singly hidden object) " + o_1.name) 461 (success, descr) = cxt.accept_action(search, None, cxt) 462 tried += 2 463 assert success, "first SEARCH failed" 464 found = descr.replace('\n', ' ').split() 465 assert found == ['one', '!two', '!three'], \ 466 "first search returned " + descr 467 passed += 2 468 469 print("second search for (doubly hidden object) " + o_2.name) 470 (success, descr) = cxt.accept_action(search, None, cxt) 471 tried += 2 472 assert success, "second SEARCH failed" 473 found = descr.replace('\n', ' ').split() 474 assert found == ['one', 'two', '!three'], \ 475 "first search returned " + descr 476 passed += 2 477 478 print("third search for (tripply hidden object) " + o_3.name) 479 (success, descr) = cxt.accept_action(search, None, cxt) 480 tried += 2 481 assert success, "third SEARCH failed" 482 found = descr.replace('\n', ' ').split() 483 assert found == ['one', 'two', 'three'], \ 484 "third search returned " + descr 485 passed += 2 486 487 print() 488 489 return (tried, passed)
490 491
492 -def main():
493 """ 494 Run all unit-test cases and print out summary of results 495 """ 496 (t_1, p_1) = member_tests() 497 (t_2, p_2) = npc_tests() 498 (t_3, p_3) = get_tests() 499 (t_4, p_4) = search_tests() 500 tried = t_1 + t_2 + t_3 + t_4 501 passed = p_1 + p_2 + p_3 + p_4 502 if tried == passed: 503 print("Passed all {} GameContext tests".format(passed)) 504 else: 505 print("FAILED {}/{} GameContext tests".format(tried-passed, tried))
506 507 508 if __name__ == "__main__": 509 main() 510