exploration.tests.test_analysis
- Authors: Nissi, Jada, Rachel, Kaitlyn, Kitty, Peter Mawhorter
- Consulted: Peter Mawhorter
- Date: 2022-12-5
- Purpose: Tests for exploration analysis.
1""" 2- Authors: Nissi, Jada, Rachel, Kaitlyn, Kitty, Peter Mawhorter 3- Consulted: Peter Mawhorter 4- Date: 2022-12-5 5- Purpose: Tests for exploration analysis. 6""" 7 8from .. import analysis, journal, core, base 9 10#Part of Rachel's journal: 11JOURNAL = """ 12S Start_room::Start 13 zz Starting_region 14 A gain attack 15 o right 16 o left 17xt left coin_room sr 18 o up above_coin_room fall 19 q _wall_kick_jumps # TODO: '?' syntax 20t sr 21xt right platforms rp 22 o up 23 o right 24xt up dangerplat ud 25""" 26 27BABY_JOURNAL = """ 28S Start 29 A gain jump 30 A gain attack 31 n button check 32 zz Wilds 33 o up 34 q _flight 35 o left 36xt left left_nook right 37 a geo_rock 38 At gain geo*15 39 At deactivate 40 o up 41 q _tall_narrow 42t right 43 o right 44 q attack 45""" 46 47BABY_JOURNAL_2 = """ 48S Start 49 A gain jump 50 A gain attack 51 n button check 52 zz Wilds 53 o up 54 q _flight 55 o left 56xt left left_nook right 57""" 58 59REVISITS_JOURNAL = """ 60S A 61x right B left 62x down C up 63x left D right # 4 decisions ABCD 64r up A down # revisit #1 for A 65t right # revisit #1 for B 66r down_left D up_right # revisit #1 for D 67t right # revisit #1 for C 68t up # revisit #2 for A 69t left # revisit #2 for B 70t down # revisit #2 for C 71 72# now we have: 2 revisits each for A, B, and C, and 1 revisit for D 73""" 74 75ACTIONS_JOURNAL = """ 76S A 77 oa alpha 78x right B left 79 oa beta 80t left 81 ta alpha 82 oa gamma 83x down C up 84 a delta 85 a epsilon 86x right D left 87w 88""" 89 90 91def test_CreateExploration() -> None: 92 """ 93 Simple test to make sure we can create an exploration object for 94 other tests in this file. 95 """ 96 ex = journal.convertJournal(JOURNAL) 97 assert len(ex) == 6 98 assert ex.getActiveDecisions(0) == set() 99 assert ex.getActiveDecisions(1) == {0} 100 101 assert bool(ex.getSituation(1).graph) is True 102 103 104def test_countActionsAtDecision() -> None: 105 """ 106 Test to make sure the number of actions in the whole graph 107 at each step is accurate. 108 """ 109 ex = journal.convertJournal(BABY_JOURNAL) 110 assert [ 111 analysis.totalActions(ex, i) 112 for i in range(len(ex)) 113 ] == [0, 0, 1, 1, 1] 114 115 now = ex.getSituation() 116 graph = now.graph 117 startID = graph.resolveDecision("Start") 118 nookID = graph.resolveDecision("left_nook") 119 assert analysis.actionCount(ex, len(ex) - 1, startID) == 0 120 assert analysis.actionCount(ex, len(ex) - 1, nookID) == 1 121 122 ex2 = journal.convertJournal(ACTIONS_JOURNAL) 123 assert [ 124 analysis.totalActions(ex2, i) 125 for i in range(len(ex2)) 126 ] == [0, 1, 2, 2, 3, 4, 5, 5, 5, 5] 127 assert [ 128 analysis.meanActions(ex2, i) 129 for i in range(len(ex2)) 130 ] == [0.0, 1/2, 2/2, 2/2, 3/3, 4/3, 5/3, 5/4, 5/4, 5/4] 131 132 for step in range(len(ex2)): 133 assert ( 134 analysis.totalActions(ex2, step) / 135 analysis.totalDecisionsSoFar(ex2, step) 136 == analysis.meanActions(ex2, step) 137 ) 138 139 140def test_count_actions() -> None: 141 """ 142 Tests the 'actionCount' metric and its total/mean combiner values. 143 """ 144 ex = journal.convertJournal(BABY_JOURNAL) 145 ex2 = journal.convertJournal(ACTIONS_JOURNAL) 146 147 assert analysis.actionCount(ex, 1, 0) == 0 148 assert analysis.actionCount(ex, 2, 1) == 0 149 150 151def test_describeProgress() -> None: 152 """ 153 Tests the `describeProgress` function. 154 """ 155 e1 = journal.convertJournal(BABY_JOURNAL) 156 e2 = journal.convertJournal(BABY_JOURNAL_2) 157 e3 = journal.convertJournal(REVISITS_JOURNAL) 158 159 description = analysis.describeProgress(e1) 160 assert description == """\ 161Start of the exploration 162Start exploring domain main at 0 (Start) 163 Gained capability 'attack' 164 Gained capability 'jump' 165At decision 0 (Start) 166 In region Wilds 167 There are transitions: 168 left to unconfirmed 169 up to unconfirmed; requires _flight 170 1 note(s) at this step 171Explore left from decision 0 (Start) to 2 (now Wilds::left_nook) 172At decision 2 (left_nook) 173 There are transitions: 174 right to 0 (Start) 175 There are actions: 176 geo_rock 177Do action geo_rock 178 Gained 15 geo(s) 179Take right from decision 2 (left_nook) to 0 (Start) 180At decision 0 (Start) 181 There are transitions: 182 left to 2 (left_nook) 183 right to unconfirmed; requires attack 184 up to unconfirmed; requires _flight 185Waiting for another action... 186End of the exploration. 187""" 188 189 description2 = analysis.describeProgress(e2) 190 assert description2 == """\ 191Start of the exploration 192Start exploring domain main at 0 (Start) 193 Gained capability 'attack' 194 Gained capability 'jump' 195At decision 0 (Start) 196 In region Wilds 197 There are transitions: 198 left to unconfirmed 199 up to unconfirmed; requires _flight 200 1 note(s) at this step 201Explore left from decision 0 (Start) to 2 (now Wilds::left_nook) 202At decision 2 (left_nook) 203 There are transitions: 204 right to 0 (Start) 205Waiting for another action... 206End of the exploration. 207""" 208 209 description3 = analysis.describeProgress(e3) 210 assert description3 == """\ 211Start of the exploration 212Start exploring domain main at 0 (A) 213At decision 0 (A) 214 There are transitions: 215 right to unconfirmed 216Explore right from decision 0 (A) to 1 (now B) 217At decision 1 (B) 218 There are transitions: 219 down to unconfirmed 220 left to 0 (A) 221Explore down from decision 1 (B) to 2 (now C) 222At decision 2 (C) 223 There are transitions: 224 left to unconfirmed 225 up to 1 (B) 226Explore left from decision 2 (C) to 3 (now D) 227At decision 3 (D) 228 There are transitions: 229 right to 2 (C) 230 up to 0 (A) 231Take up from decision 3 (D) to 0 (A) 232At decision 0 (A) 233 There are transitions: 234 down to 3 (D) 235 right to 1 (B) 236Take right from decision 0 (A) to 1 (B) 237At decision 1 (B) 238 There are transitions: 239 down to 2 (C) 240 down_left to 3 (D) 241 left to 0 (A) 242Take down_left from decision 1 (B) to 3 (D) 243At decision 3 (D) 244 There are transitions: 245 right to 2 (C) 246 up to 0 (A) 247 up_right to 1 (B) 248Take right from decision 3 (D) to 2 (C) 249At decision 2 (C) 250 There are transitions: 251 left to 3 (D) 252 up to 1 (B) 253Take up from decision 2 (C) to 1 (B) 254At decision 1 (B) 255 There are transitions: 256 down to 2 (C) 257 down_left to 3 (D) 258 left to 0 (A) 259Take left from decision 1 (B) to 0 (A) 260At decision 0 (A) 261 There are transitions: 262 down to 3 (D) 263 right to 1 (B) 264Take down from decision 0 (A) to 3 (D) 265At decision 3 (D) 266 There are transitions: 267 right to 2 (C) 268 up to 0 (A) 269 up_right to 1 (B) 270Waiting for another action... 271End of the exploration. 272""" 273 274 275def test_unexploredBranches() -> None: 276 """ 277 Tests the `unexploredBranches` and related count functions. 278 """ 279 ex = journal.convertJournal(JOURNAL) 280 assert analysis.unexploredBranches(ex.getSituation(0).graph) == [] 281 g1 = ex.getSituation(1).graph 282 assert g1.destinationsFrom(0) == { 283 'right': 1, 284 'left': 2 285 } 286 assert g1.nameFor(1) == '_u.0' 287 assert g1.nameFor(2) == '_u.1' 288 assert analysis.unexploredBranches(ex.getSituation(1).graph) == [ 289 (0, 'right'), 290 (0, 'left'), 291 ] 292 assert analysis.unexploredBranches(ex.getSituation(2).graph) == [ 293 (0, 'right'), 294 (2, 'up'), 295 ] 296 assert analysis.unexploredBranches(ex.getSituation(3).graph) == [ 297 (0, 'right'), 298 (2, 'up'), 299 ] 300 g4 = ex.getSituation(4).graph 301 assert g4.namesListing(set(g4.nodes)) == """\ 302 0 (Start_room::Start) 303 1 (Start_room::platforms) 304 2 (Start_room::coin_room) 305 3 (Start_room::above_coin_room) 306 4 (_u.3) 307 5 (_u.4) 308""" 309 assert analysis.unexploredBranches(ex.getSituation(4).graph) == [ 310 (1, 'up'), 311 (1, 'right'), 312 (2, 'up'), 313 ] 314 assert analysis.unexploredBranches(ex.getSituation(5).graph) == [ 315 (1, 'right'), 316 (2, 'up'), 317 ] 318 allPerStep = [ 319 analysis.unexploredBranchCount(ex, step) 320 for step in range(len(ex)) 321 ] 322 traversablePerStep = [ 323 analysis.traversableUnexploredCount(ex, step) 324 for step in range(len(ex)) 325 ] 326 assert allPerStep == [0, 2, 2, 2, 3, 2] 327 assert traversablePerStep == [0, 2, 1, 1, 2, 1] 328 # TODO 329 330 331def test_countBranches() -> None: 332 """ 333 Tests the `countBranches` function. 334 """ 335 ex = journal.convertJournal(BABY_JOURNAL) 336 ex2 = journal.convertJournal(BABY_JOURNAL_2) 337 338 # Note: as of v0.6, we can index an exploration to get a Situation, 339 # and most analysis functions want Situations as input 340 341 first = ex[0] 342 second = ex[1] 343 third = ex[2] 344 fourth = ex[3] 345 fifth = ex[4] 346 347 ex2first = ex2[0] 348 ex2second = ex2[1] 349 ex2third = ex2[2] 350 351 assert first.graph.namesListing(set(first.graph.nodes)) == """\ 352 0 (Start) 353""" 354 assert analysis.meanBranches(ex, 0) == 0 355 assert analysis.meanBranches(ex, 1) == 2 356 assert analysis.meanBranches(ex, 2) == 1.5 357 assert analysis.meanBranches(ex, 3) == 2 358 assert analysis.meanBranches(ex, 4) == 2.5 359 360 startID = fifth.graph.resolveDecision("Start") 361 nookID = fifth.graph.resolveDecision("left_nook") 362 assert analysis.branches(ex, 1, startID) == 2 363 assert analysis.branches(ex, 2, startID) == 2 364 assert analysis.branches(ex, 2, nookID) == 1 365 assert analysis.branches(ex, 4, startID) == 3 366 assert analysis.branches(ex, 4, nookID) == 2 367 368 assert analysis.meanBranches(ex2, 0) == 0 369 assert analysis.meanBranches(ex2, 1) == 2 370 assert analysis.meanBranches(ex2, 2) == 1.5 371 372 373def test_revisits() -> None: 374 """ 375 Tests the `arrivals`, `revisits`, and related total/mean/median 376 functions. 377 """ 378 379 babyExp = journal.convertJournal(BABY_JOURNAL) 380 graph = babyExp.getSituation().graph 381 startID = graph.resolveDecision("Start") 382 nookID = graph.resolveDecision("left_nook") 383 assert analysis.stepsVisited(babyExp) == { 384 startID: [1, 4], 385 nookID: [2, 3] 386 } 387 assert analysis.arrivals(babyExp, startID) == 2 388 assert analysis.revisits(babyExp, startID) == 1 389 assert analysis.arrivals(babyExp, nookID) == 1 390 assert analysis.revisits(babyExp, nookID) == 0 391 assert analysis.arrivals(babyExp, 28309823) == 0 392 assert analysis.revisits(babyExp, 28309823) == 0 393 394 fullExp = journal.convertJournal(JOURNAL) 395 graph = fullExp.getSituation().graph 396 startID = graph.resolveDecision("Start") 397 coinRoomID = graph.resolveDecision("coin_room") 398 platformsID = graph.resolveDecision("platforms") 399 dangerplatID = graph.resolveDecision("dangerplat") 400 assert analysis.revisits(fullExp, startID) == 1 401 assert analysis.revisits(fullExp, coinRoomID) == 0 402 assert analysis.revisits(fullExp, platformsID) == 0 403 assert analysis.revisits(fullExp, dangerplatID) == 0 404 405 revExp = journal.convertJournal(REVISITS_JOURNAL) 406 graph = revExp.getSituation().graph 407 aID = graph.resolveDecision('A') 408 bID = graph.resolveDecision('B') 409 cID = graph.resolveDecision('C') 410 dID = graph.resolveDecision('D') 411 assert analysis.revisits(revExp, aID) == 2 412 assert analysis.revisits(revExp, bID) == 2 413 assert analysis.revisits(revExp, cID) == 1 414 assert analysis.revisits(revExp, dID) == 2 415 416 assert analysis.totalRevisits(babyExp) == 1 417 assert analysis.meanRevisits(babyExp) == 1/5 418 assert analysis.medianRevisits(babyExp) == 0 419 assert analysis.totalRevisits(fullExp) == 1 420 assert analysis.meanRevisits(fullExp) == 1/6 421 assert analysis.medianRevisits(fullExp) == 0 422 assert analysis.totalRevisits(revExp) == 7 423 assert analysis.meanRevisits(revExp) == 7/4 424 assert analysis.medianRevisits(revExp) == 2.0 425 426 blankExp = core.DiscreteExploration() 427 assert analysis.totalRevisits(blankExp) == 0 428 assert analysis.meanRevisits(blankExp) is None 429 assert analysis.medianRevisits(blankExp) is None 430 431 soloExp = core.DiscreteExploration() 432 soloExp.start("start") 433 assert analysis.totalRevisits(soloExp) == 0 434 assert analysis.meanRevisits(soloExp) == 0 435 assert analysis.medianRevisits(soloExp) == 0 436 437 doubleExp = core.DiscreteExploration() 438 doubleExp.start("start") 439 doubleExp.observe("start", "up") 440 doubleExp.explore("up", "above", "down") 441 assert analysis.totalRevisits(doubleExp) == 0 442 assert analysis.meanRevisits(doubleExp) == 0 443 assert analysis.medianRevisits(doubleExp) == 0 444 445 446def test_makeFractionCombiner() -> None: 447 """ 448 Tests `makeFractionCombiner`. 449 """ 450 c = analysis.makeFractionCombiner(lambda k, x: x > 2, lambda k, x: x < 0) 451 d = { 452 'a': 1, 453 'b': -2, 454 'c': 5, 455 'd': 3, 456 'e': 2.1 457 } 458 assert c(d) == 0.75 # type:ignore 459 c = analysis.makeFractionCombiner(lambda k, x: x < 0, lambda k, x: x > 0) 460 assert c(d) == 1.0 # type:ignore 461 c = analysis.makeFractionCombiner(lambda k, x: x < 0) 462 assert c(d) == 0.2 # type:ignore 463 c = analysis.makeFractionCombiner(lambda k, x: x > 2) 464 assert c(d) == 0.6 # type:ignore 465 c = analysis.makeFractionCombiner(lambda k, x: x % 3 == 7) 466 assert c(d) == 0.0 # type:ignore 467 468def test_cycleBasis() -> None: 469 """ 470 Tests `cycleBasis`. 471 """ 472 # Test using a custom several-cycle decision graph: 473 g = core.DecisionGraph() 474 g.addDecision('A') # 0 475 g.addDecision('B') # 1 476 g.addDecision('C') # 2 477 g.addDecision('D') # 3 478 g.addDecision('E') # 4 479 g.addDecision('F') # 5 480 g.addDecision('G') # 6 481 g.addDecision('H') # 7 482 g.addDecision('I') # 8 483 assert analysis.cycleBasis(g) == [] 484 g.addTransition('A', 'left', 'B', 'right') 485 g.addTransition('B', 'down', 'C', 'upRight') 486 assert analysis.cycleBasis(g) == [] 487 g.addTransition('A', 'down', 'C', 'upLeft') # triangle 488 assert analysis.cycleBasis(g) == [[0, 1, 2]] 489 g.addTransition('C', 'down', 'D', 'up') 490 g.addTransition('D', 'down', 'E', 'up') # D is in-between 491 assert analysis.cycleBasis(g) == [[0, 1, 2]] 492 g.addTransition('E', 'downLeft', 'F', 'up') 493 g.addTransition('F', 'right', 'G', 'left') 494 g.addTransition('G', 'right', 'H', 'left') 495 assert analysis.cycleBasis(g) == [[0, 1, 2]] 496 g.addTransition('H', 'up', 'E', 'downRight') # 4-node cycle 497 assert {frozenset(c) for c in analysis.cycleBasis(g)} == { 498 frozenset({0, 1, 2}), 499 frozenset({4, 5, 6, 7}) 500 } 501 g.addTransition('G', 'down', 'I', 'up') # branches off 502 assert {frozenset(c) for c in analysis.cycleBasis(g)} == { 503 frozenset({0, 1, 2}), 504 frozenset({4, 5, 6, 7}) 505 } 506 g.setTransitionRequirement('C', 'down', base.ReqCapability('fly')) 507 g.setTransitionRequirement('E', 'up', base.ReqCapability('fly')) 508 assert {frozenset(c) for c in analysis.cycleBasis(g)} == { 509 frozenset({0, 1, 2}), 510 frozenset({4, 5, 6, 7}) 511 } 512 513 # Test using snippet from problematic journal 514 cotmStart = """\ 515# Setup 516P softerChecks on 517 518= enemy which common level rare rareLevel [ 519 gd enemy:{which} 520 oa farm{which}{common} 521 e { <{level}>luck{ gain {common}*1 }{ } } 522 oa farm{which}{rare} 523 e { <{rareLevel}>luck{ gain {rare}*1 }{ } } 524] 525 526= farm from what [ 527 ta farm{from}{what}%s 528] 529 530= cardEnemy which card level otherDrop otherLevel [ 531 gd enemy:{which} 532 oa farmCard{which}{card} 533 e { <{level}>luck{ gain {card}; deactivate }{ } } 534 oa farm{which}{otherDrop} 535 e { <{otherLevel}>luck{ gain {otherDrop}*1 }{ } } 536] 537 538= gotCard from which [ 539 ta farmCard{from}{which}%s 540] 541 542= subweapon which [ 543 oa take{which} 544 e { lose Dagger; lose HolyWater; lose Boomerang; lose Axe; gain {which} } 545] 546 547= getItem what [ 548 a get{what} 549 At { gain {what}*1; deactivate } 550] 551 552# Start 553S ConfrontationSite::main 554 zz Catacombs 555 o left 556 o down 557 m door:sealed 558 o right 559 qb door:unsealed 560 @ 561 xt down RubbleTowerShaft::shaft up 562 gd suspended 563 @ @ 564 a meetDracula 565 gt triggered 566 At follow down 567 568n at: RubbleTowerShaft::shaft 569 o fall bottom flyUp 570 qr ?flight 571 !a fallDown 572 q !?flight 573 gt triggered 574 At follow fall 575 n at: RubbleTowerShaft::bottom 576 577x down RubbleTowerBase::top up 578 > cardEnemy SkeletonBomber Salamander 1 Potion 6 579 n [ Real chances are 20% / 1%; this gives 25% / 0.78% ] 580 > gotCard SkeletonBomber Salamander 581 n [ Literally first enemy I touched; super lucky! ] 582x down mid up 583 > cardEnemy SkeletonBomber Salamander 1 Potion 6 584 o leftCrack 585 q ?airSlide 586x down bottom up 587 > cardEnemy SkeletonBomber Salamander 1 Potion 6 588 589x right CatacombsEntrance::top left 590 > cardEnemy SkeletonBomber Salamander 1 Potion 6 591 o upLeft 592 q ?higherJump 593 o upRight 594 q ?higherJump 595 > enemy Bat WristBand 4 Heart 7 596 n [ Real 2.5% / 0.5%; these are 3.125% / 0.39% ] 597 """ 598 exp = journal.convertJournal(cotmStart) 599 assert analysis.cycleBasis(exp.getSituation().graph) == []
JOURNAL =
"\nS Start_room::Start\n zz Starting_region\n A gain attack\n o right\n o left\nxt left coin_room sr\n o up above_coin_room fall\n q _wall_kick_jumps # TODO: '?' syntax\nt sr\nxt right platforms rp\n o up\n o right\nxt up dangerplat ud\n"
BABY_JOURNAL =
'\nS Start\n A gain jump\n A gain attack\n n button check\n zz Wilds\n o up\n q _flight\n o left\nxt left left_nook right\n a geo_rock\n At gain geo*15\n At deactivate\n o up\n q _tall_narrow\nt right\n o right\n q attack\n'
BABY_JOURNAL_2 =
'\nS Start\n A gain jump\n A gain attack\n n button check\n zz Wilds\n o up\n q _flight\n o left\nxt left left_nook right\n'
REVISITS_JOURNAL =
'\nS A\nx right B left\nx down C up\nx left D right # 4 decisions ABCD\nr up A down # revisit #1 for A\nt right # revisit #1 for B\nr down_left D up_right # revisit #1 for D\nt right # revisit #1 for C\nt up # revisit #2 for A\nt left # revisit #2 for B\nt down # revisit #2 for C\n\n# now we have: 2 revisits each for A, B, and C, and 1 revisit for D\n'
ACTIONS_JOURNAL =
'\nS A\n oa alpha\nx right B left\n oa beta\nt left\n ta alpha\n oa gamma\nx down C up\n a delta\n a epsilon\nx right D left\nw\n'
def
test_CreateExploration() -> None:
92def test_CreateExploration() -> None: 93 """ 94 Simple test to make sure we can create an exploration object for 95 other tests in this file. 96 """ 97 ex = journal.convertJournal(JOURNAL) 98 assert len(ex) == 6 99 assert ex.getActiveDecisions(0) == set() 100 assert ex.getActiveDecisions(1) == {0} 101 102 assert bool(ex.getSituation(1).graph) is True
Simple test to make sure we can create an exploration object for other tests in this file.
def
test_countActionsAtDecision() -> None:
105def test_countActionsAtDecision() -> None: 106 """ 107 Test to make sure the number of actions in the whole graph 108 at each step is accurate. 109 """ 110 ex = journal.convertJournal(BABY_JOURNAL) 111 assert [ 112 analysis.totalActions(ex, i) 113 for i in range(len(ex)) 114 ] == [0, 0, 1, 1, 1] 115 116 now = ex.getSituation() 117 graph = now.graph 118 startID = graph.resolveDecision("Start") 119 nookID = graph.resolveDecision("left_nook") 120 assert analysis.actionCount(ex, len(ex) - 1, startID) == 0 121 assert analysis.actionCount(ex, len(ex) - 1, nookID) == 1 122 123 ex2 = journal.convertJournal(ACTIONS_JOURNAL) 124 assert [ 125 analysis.totalActions(ex2, i) 126 for i in range(len(ex2)) 127 ] == [0, 1, 2, 2, 3, 4, 5, 5, 5, 5] 128 assert [ 129 analysis.meanActions(ex2, i) 130 for i in range(len(ex2)) 131 ] == [0.0, 1/2, 2/2, 2/2, 3/3, 4/3, 5/3, 5/4, 5/4, 5/4] 132 133 for step in range(len(ex2)): 134 assert ( 135 analysis.totalActions(ex2, step) / 136 analysis.totalDecisionsSoFar(ex2, step) 137 == analysis.meanActions(ex2, step) 138 )
Test to make sure the number of actions in the whole graph at each step is accurate.
def
test_count_actions() -> None:
141def test_count_actions() -> None: 142 """ 143 Tests the 'actionCount' metric and its total/mean combiner values. 144 """ 145 ex = journal.convertJournal(BABY_JOURNAL) 146 ex2 = journal.convertJournal(ACTIONS_JOURNAL) 147 148 assert analysis.actionCount(ex, 1, 0) == 0 149 assert analysis.actionCount(ex, 2, 1) == 0
Tests the 'actionCount' metric and its total/mean combiner values.
def
test_describeProgress() -> None:
152def test_describeProgress() -> None: 153 """ 154 Tests the `describeProgress` function. 155 """ 156 e1 = journal.convertJournal(BABY_JOURNAL) 157 e2 = journal.convertJournal(BABY_JOURNAL_2) 158 e3 = journal.convertJournal(REVISITS_JOURNAL) 159 160 description = analysis.describeProgress(e1) 161 assert description == """\ 162Start of the exploration 163Start exploring domain main at 0 (Start) 164 Gained capability 'attack' 165 Gained capability 'jump' 166At decision 0 (Start) 167 In region Wilds 168 There are transitions: 169 left to unconfirmed 170 up to unconfirmed; requires _flight 171 1 note(s) at this step 172Explore left from decision 0 (Start) to 2 (now Wilds::left_nook) 173At decision 2 (left_nook) 174 There are transitions: 175 right to 0 (Start) 176 There are actions: 177 geo_rock 178Do action geo_rock 179 Gained 15 geo(s) 180Take right from decision 2 (left_nook) to 0 (Start) 181At decision 0 (Start) 182 There are transitions: 183 left to 2 (left_nook) 184 right to unconfirmed; requires attack 185 up to unconfirmed; requires _flight 186Waiting for another action... 187End of the exploration. 188""" 189 190 description2 = analysis.describeProgress(e2) 191 assert description2 == """\ 192Start of the exploration 193Start exploring domain main at 0 (Start) 194 Gained capability 'attack' 195 Gained capability 'jump' 196At decision 0 (Start) 197 In region Wilds 198 There are transitions: 199 left to unconfirmed 200 up to unconfirmed; requires _flight 201 1 note(s) at this step 202Explore left from decision 0 (Start) to 2 (now Wilds::left_nook) 203At decision 2 (left_nook) 204 There are transitions: 205 right to 0 (Start) 206Waiting for another action... 207End of the exploration. 208""" 209 210 description3 = analysis.describeProgress(e3) 211 assert description3 == """\ 212Start of the exploration 213Start exploring domain main at 0 (A) 214At decision 0 (A) 215 There are transitions: 216 right to unconfirmed 217Explore right from decision 0 (A) to 1 (now B) 218At decision 1 (B) 219 There are transitions: 220 down to unconfirmed 221 left to 0 (A) 222Explore down from decision 1 (B) to 2 (now C) 223At decision 2 (C) 224 There are transitions: 225 left to unconfirmed 226 up to 1 (B) 227Explore left from decision 2 (C) to 3 (now D) 228At decision 3 (D) 229 There are transitions: 230 right to 2 (C) 231 up to 0 (A) 232Take up from decision 3 (D) to 0 (A) 233At decision 0 (A) 234 There are transitions: 235 down to 3 (D) 236 right to 1 (B) 237Take right from decision 0 (A) to 1 (B) 238At decision 1 (B) 239 There are transitions: 240 down to 2 (C) 241 down_left to 3 (D) 242 left to 0 (A) 243Take down_left from decision 1 (B) to 3 (D) 244At decision 3 (D) 245 There are transitions: 246 right to 2 (C) 247 up to 0 (A) 248 up_right to 1 (B) 249Take right from decision 3 (D) to 2 (C) 250At decision 2 (C) 251 There are transitions: 252 left to 3 (D) 253 up to 1 (B) 254Take up from decision 2 (C) to 1 (B) 255At decision 1 (B) 256 There are transitions: 257 down to 2 (C) 258 down_left to 3 (D) 259 left to 0 (A) 260Take left from decision 1 (B) to 0 (A) 261At decision 0 (A) 262 There are transitions: 263 down to 3 (D) 264 right to 1 (B) 265Take down from decision 0 (A) to 3 (D) 266At decision 3 (D) 267 There are transitions: 268 right to 2 (C) 269 up to 0 (A) 270 up_right to 1 (B) 271Waiting for another action... 272End of the exploration. 273"""
Tests the describeProgress function.
def
test_unexploredBranches() -> None:
276def test_unexploredBranches() -> None: 277 """ 278 Tests the `unexploredBranches` and related count functions. 279 """ 280 ex = journal.convertJournal(JOURNAL) 281 assert analysis.unexploredBranches(ex.getSituation(0).graph) == [] 282 g1 = ex.getSituation(1).graph 283 assert g1.destinationsFrom(0) == { 284 'right': 1, 285 'left': 2 286 } 287 assert g1.nameFor(1) == '_u.0' 288 assert g1.nameFor(2) == '_u.1' 289 assert analysis.unexploredBranches(ex.getSituation(1).graph) == [ 290 (0, 'right'), 291 (0, 'left'), 292 ] 293 assert analysis.unexploredBranches(ex.getSituation(2).graph) == [ 294 (0, 'right'), 295 (2, 'up'), 296 ] 297 assert analysis.unexploredBranches(ex.getSituation(3).graph) == [ 298 (0, 'right'), 299 (2, 'up'), 300 ] 301 g4 = ex.getSituation(4).graph 302 assert g4.namesListing(set(g4.nodes)) == """\ 303 0 (Start_room::Start) 304 1 (Start_room::platforms) 305 2 (Start_room::coin_room) 306 3 (Start_room::above_coin_room) 307 4 (_u.3) 308 5 (_u.4) 309""" 310 assert analysis.unexploredBranches(ex.getSituation(4).graph) == [ 311 (1, 'up'), 312 (1, 'right'), 313 (2, 'up'), 314 ] 315 assert analysis.unexploredBranches(ex.getSituation(5).graph) == [ 316 (1, 'right'), 317 (2, 'up'), 318 ] 319 allPerStep = [ 320 analysis.unexploredBranchCount(ex, step) 321 for step in range(len(ex)) 322 ] 323 traversablePerStep = [ 324 analysis.traversableUnexploredCount(ex, step) 325 for step in range(len(ex)) 326 ] 327 assert allPerStep == [0, 2, 2, 2, 3, 2] 328 assert traversablePerStep == [0, 2, 1, 1, 2, 1] 329 # TODO
Tests the unexploredBranches and related count functions.
def
test_countBranches() -> None:
332def test_countBranches() -> None: 333 """ 334 Tests the `countBranches` function. 335 """ 336 ex = journal.convertJournal(BABY_JOURNAL) 337 ex2 = journal.convertJournal(BABY_JOURNAL_2) 338 339 # Note: as of v0.6, we can index an exploration to get a Situation, 340 # and most analysis functions want Situations as input 341 342 first = ex[0] 343 second = ex[1] 344 third = ex[2] 345 fourth = ex[3] 346 fifth = ex[4] 347 348 ex2first = ex2[0] 349 ex2second = ex2[1] 350 ex2third = ex2[2] 351 352 assert first.graph.namesListing(set(first.graph.nodes)) == """\ 353 0 (Start) 354""" 355 assert analysis.meanBranches(ex, 0) == 0 356 assert analysis.meanBranches(ex, 1) == 2 357 assert analysis.meanBranches(ex, 2) == 1.5 358 assert analysis.meanBranches(ex, 3) == 2 359 assert analysis.meanBranches(ex, 4) == 2.5 360 361 startID = fifth.graph.resolveDecision("Start") 362 nookID = fifth.graph.resolveDecision("left_nook") 363 assert analysis.branches(ex, 1, startID) == 2 364 assert analysis.branches(ex, 2, startID) == 2 365 assert analysis.branches(ex, 2, nookID) == 1 366 assert analysis.branches(ex, 4, startID) == 3 367 assert analysis.branches(ex, 4, nookID) == 2 368 369 assert analysis.meanBranches(ex2, 0) == 0 370 assert analysis.meanBranches(ex2, 1) == 2 371 assert analysis.meanBranches(ex2, 2) == 1.5
Tests the countBranches function.
def
test_revisits() -> None:
374def test_revisits() -> None: 375 """ 376 Tests the `arrivals`, `revisits`, and related total/mean/median 377 functions. 378 """ 379 380 babyExp = journal.convertJournal(BABY_JOURNAL) 381 graph = babyExp.getSituation().graph 382 startID = graph.resolveDecision("Start") 383 nookID = graph.resolveDecision("left_nook") 384 assert analysis.stepsVisited(babyExp) == { 385 startID: [1, 4], 386 nookID: [2, 3] 387 } 388 assert analysis.arrivals(babyExp, startID) == 2 389 assert analysis.revisits(babyExp, startID) == 1 390 assert analysis.arrivals(babyExp, nookID) == 1 391 assert analysis.revisits(babyExp, nookID) == 0 392 assert analysis.arrivals(babyExp, 28309823) == 0 393 assert analysis.revisits(babyExp, 28309823) == 0 394 395 fullExp = journal.convertJournal(JOURNAL) 396 graph = fullExp.getSituation().graph 397 startID = graph.resolveDecision("Start") 398 coinRoomID = graph.resolveDecision("coin_room") 399 platformsID = graph.resolveDecision("platforms") 400 dangerplatID = graph.resolveDecision("dangerplat") 401 assert analysis.revisits(fullExp, startID) == 1 402 assert analysis.revisits(fullExp, coinRoomID) == 0 403 assert analysis.revisits(fullExp, platformsID) == 0 404 assert analysis.revisits(fullExp, dangerplatID) == 0 405 406 revExp = journal.convertJournal(REVISITS_JOURNAL) 407 graph = revExp.getSituation().graph 408 aID = graph.resolveDecision('A') 409 bID = graph.resolveDecision('B') 410 cID = graph.resolveDecision('C') 411 dID = graph.resolveDecision('D') 412 assert analysis.revisits(revExp, aID) == 2 413 assert analysis.revisits(revExp, bID) == 2 414 assert analysis.revisits(revExp, cID) == 1 415 assert analysis.revisits(revExp, dID) == 2 416 417 assert analysis.totalRevisits(babyExp) == 1 418 assert analysis.meanRevisits(babyExp) == 1/5 419 assert analysis.medianRevisits(babyExp) == 0 420 assert analysis.totalRevisits(fullExp) == 1 421 assert analysis.meanRevisits(fullExp) == 1/6 422 assert analysis.medianRevisits(fullExp) == 0 423 assert analysis.totalRevisits(revExp) == 7 424 assert analysis.meanRevisits(revExp) == 7/4 425 assert analysis.medianRevisits(revExp) == 2.0 426 427 blankExp = core.DiscreteExploration() 428 assert analysis.totalRevisits(blankExp) == 0 429 assert analysis.meanRevisits(blankExp) is None 430 assert analysis.medianRevisits(blankExp) is None 431 432 soloExp = core.DiscreteExploration() 433 soloExp.start("start") 434 assert analysis.totalRevisits(soloExp) == 0 435 assert analysis.meanRevisits(soloExp) == 0 436 assert analysis.medianRevisits(soloExp) == 0 437 438 doubleExp = core.DiscreteExploration() 439 doubleExp.start("start") 440 doubleExp.observe("start", "up") 441 doubleExp.explore("up", "above", "down") 442 assert analysis.totalRevisits(doubleExp) == 0 443 assert analysis.meanRevisits(doubleExp) == 0 444 assert analysis.medianRevisits(doubleExp) == 0
Tests the arrivals, revisits, and related total/mean/median
functions.
def
test_makeFractionCombiner() -> None:
447def test_makeFractionCombiner() -> None: 448 """ 449 Tests `makeFractionCombiner`. 450 """ 451 c = analysis.makeFractionCombiner(lambda k, x: x > 2, lambda k, x: x < 0) 452 d = { 453 'a': 1, 454 'b': -2, 455 'c': 5, 456 'd': 3, 457 'e': 2.1 458 } 459 assert c(d) == 0.75 # type:ignore 460 c = analysis.makeFractionCombiner(lambda k, x: x < 0, lambda k, x: x > 0) 461 assert c(d) == 1.0 # type:ignore 462 c = analysis.makeFractionCombiner(lambda k, x: x < 0) 463 assert c(d) == 0.2 # type:ignore 464 c = analysis.makeFractionCombiner(lambda k, x: x > 2) 465 assert c(d) == 0.6 # type:ignore 466 c = analysis.makeFractionCombiner(lambda k, x: x % 3 == 7) 467 assert c(d) == 0.0 # type:ignore
Tests makeFractionCombiner.
def
test_cycleBasis() -> None:
469def test_cycleBasis() -> None: 470 """ 471 Tests `cycleBasis`. 472 """ 473 # Test using a custom several-cycle decision graph: 474 g = core.DecisionGraph() 475 g.addDecision('A') # 0 476 g.addDecision('B') # 1 477 g.addDecision('C') # 2 478 g.addDecision('D') # 3 479 g.addDecision('E') # 4 480 g.addDecision('F') # 5 481 g.addDecision('G') # 6 482 g.addDecision('H') # 7 483 g.addDecision('I') # 8 484 assert analysis.cycleBasis(g) == [] 485 g.addTransition('A', 'left', 'B', 'right') 486 g.addTransition('B', 'down', 'C', 'upRight') 487 assert analysis.cycleBasis(g) == [] 488 g.addTransition('A', 'down', 'C', 'upLeft') # triangle 489 assert analysis.cycleBasis(g) == [[0, 1, 2]] 490 g.addTransition('C', 'down', 'D', 'up') 491 g.addTransition('D', 'down', 'E', 'up') # D is in-between 492 assert analysis.cycleBasis(g) == [[0, 1, 2]] 493 g.addTransition('E', 'downLeft', 'F', 'up') 494 g.addTransition('F', 'right', 'G', 'left') 495 g.addTransition('G', 'right', 'H', 'left') 496 assert analysis.cycleBasis(g) == [[0, 1, 2]] 497 g.addTransition('H', 'up', 'E', 'downRight') # 4-node cycle 498 assert {frozenset(c) for c in analysis.cycleBasis(g)} == { 499 frozenset({0, 1, 2}), 500 frozenset({4, 5, 6, 7}) 501 } 502 g.addTransition('G', 'down', 'I', 'up') # branches off 503 assert {frozenset(c) for c in analysis.cycleBasis(g)} == { 504 frozenset({0, 1, 2}), 505 frozenset({4, 5, 6, 7}) 506 } 507 g.setTransitionRequirement('C', 'down', base.ReqCapability('fly')) 508 g.setTransitionRequirement('E', 'up', base.ReqCapability('fly')) 509 assert {frozenset(c) for c in analysis.cycleBasis(g)} == { 510 frozenset({0, 1, 2}), 511 frozenset({4, 5, 6, 7}) 512 } 513 514 # Test using snippet from problematic journal 515 cotmStart = """\ 516# Setup 517P softerChecks on 518 519= enemy which common level rare rareLevel [ 520 gd enemy:{which} 521 oa farm{which}{common} 522 e { <{level}>luck{ gain {common}*1 }{ } } 523 oa farm{which}{rare} 524 e { <{rareLevel}>luck{ gain {rare}*1 }{ } } 525] 526 527= farm from what [ 528 ta farm{from}{what}%s 529] 530 531= cardEnemy which card level otherDrop otherLevel [ 532 gd enemy:{which} 533 oa farmCard{which}{card} 534 e { <{level}>luck{ gain {card}; deactivate }{ } } 535 oa farm{which}{otherDrop} 536 e { <{otherLevel}>luck{ gain {otherDrop}*1 }{ } } 537] 538 539= gotCard from which [ 540 ta farmCard{from}{which}%s 541] 542 543= subweapon which [ 544 oa take{which} 545 e { lose Dagger; lose HolyWater; lose Boomerang; lose Axe; gain {which} } 546] 547 548= getItem what [ 549 a get{what} 550 At { gain {what}*1; deactivate } 551] 552 553# Start 554S ConfrontationSite::main 555 zz Catacombs 556 o left 557 o down 558 m door:sealed 559 o right 560 qb door:unsealed 561 @ 562 xt down RubbleTowerShaft::shaft up 563 gd suspended 564 @ @ 565 a meetDracula 566 gt triggered 567 At follow down 568 569n at: RubbleTowerShaft::shaft 570 o fall bottom flyUp 571 qr ?flight 572 !a fallDown 573 q !?flight 574 gt triggered 575 At follow fall 576 n at: RubbleTowerShaft::bottom 577 578x down RubbleTowerBase::top up 579 > cardEnemy SkeletonBomber Salamander 1 Potion 6 580 n [ Real chances are 20% / 1%; this gives 25% / 0.78% ] 581 > gotCard SkeletonBomber Salamander 582 n [ Literally first enemy I touched; super lucky! ] 583x down mid up 584 > cardEnemy SkeletonBomber Salamander 1 Potion 6 585 o leftCrack 586 q ?airSlide 587x down bottom up 588 > cardEnemy SkeletonBomber Salamander 1 Potion 6 589 590x right CatacombsEntrance::top left 591 > cardEnemy SkeletonBomber Salamander 1 Potion 6 592 o upLeft 593 q ?higherJump 594 o upRight 595 q ?higherJump 596 > enemy Bat WristBand 4 Heart 7 597 n [ Real 2.5% / 0.5%; these are 3.125% / 0.39% ] 598 """ 599 exp = journal.convertJournal(cotmStart) 600 assert analysis.cycleBasis(exp.getSituation().graph) == []
Tests cycleBasis.