Something went wrong. Try again.
bayes for days
Something went wrong. Try again.
Python
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195"""The audit separates "did not fire" from "could not fire"."""
import ioimport jsonimport unittestfrom pathlib import Pathfrom tempfile import TemporaryDirectory
from sds import firingaudit
def gun(weapon, name, usable=True, dead=False, blind=False): return { "weapon": weapon, "name": name, "location": "RA", "rearMounted": False, "usable": usable, "locationDestroyed": dead, "longRange": 9, "bears": [] if blind else [9], "bearsTwisted": [9], }
def offer(weapon, name, rung, alpha, declared): return { "weapon": weapon, "name": name, "target": 9, "range": 5, "toHit": 7, "damage": 5.0, "heat": 3, "rung": rung, "alpha": alpha, "declared": declared, }
def line(arsenal, offered): return json.dumps( { "seat": "sds", "phase": "FIRING", "unit": 1, "candidates": [], "chosen": 0, "firing": { "arsenal": arsenal, "offered": offered, "enemies": [{"unit": 9, "x": 1, "y": 1, "facing": 0, "range": 5}], }, } )
class FiringAudit(unittest.TestCase): def setUp(self): self.dir = TemporaryDirectory() self.addCleanup(self.dir.cleanup) self.log = Path(self.dir.name) / "a.decisions.jsonl"
def test_a_destroyed_gun_never_counts_as_declining_a_shot(self): self.log.write_text( line( [gun(3, "PPC"), gun(4, "Medium Laser", usable=False, dead=True)], [offer(3, "PPC", 1, 2, True)], ) + "\n" ) counts = firingaudit.tally(firingaudit.rows(self.log)) self.assertEqual(counts["Medium Laser"].dead, 1) self.assertEqual(counts["Medium Laser"].alive, 0) self.assertEqual(counts["Medium Laser"].offered, 0) self.assertEqual(counts["PPC"].declared, 1)
def test_an_undeclared_offer_keeps_the_rung_it_sat_on(self): self.log.write_text( line( [gun(3, "PPC"), gun(4, "Medium Laser")], [offer(3, "PPC", 1, 2, True), offer(4, "Medium Laser", 2, 2, False)], ) + "\n" ) counts = firingaudit.tally(firingaudit.rows(self.log)) laser = counts["Medium Laser"] self.assertEqual((laser.alive, laser.offered, laser.declared), (1, 1, 0)) self.assertEqual(laser.rungs, {2: 1}) self.assertEqual(laser.rate, 0.0)
def test_a_live_gun_with_no_legal_shot_is_its_own_column(self): self.log.write_text( line([gun(3, "PPC"), gun(4, "Medium Laser")], [offer(3, "PPC", 1, 1, True)]) + "\n" ) counts = firingaudit.tally(firingaudit.rows(self.log)) self.assertEqual(counts["Medium Laser"].no_offer, 1) self.assertEqual(counts["Medium Laser"].offered, 0)
def test_an_enemy_in_range_and_out_of_arc_is_counted_as_an_arc(self): self.log.write_text( line( [gun(3, "PPC"), gun(4, "Medium Laser", blind=True)], [offer(3, "PPC", 1, 1, True)], ) + "\n" ) counts = firingaudit.tally(firingaudit.rows(self.log)) laser = counts["Medium Laser"] self.assertEqual((laser.in_range, laser.arc, laser.twist), (1, 1, 1)) self.assertEqual(counts["PPC"].arc, 0)
def test_every_weapon_lands_under_exactly_one_reason(self): # The whole claim of the per-decision account: "three fired" is only # readable beside where the rest went, so the statuses must partition # the arsenal rather than overlap it. plan = json.loads( line( [ gun(3, "PPC"), gun(4, "Medium Laser"), gun(5, "Small Laser", blind=True), gun(6, "SRM 4", usable=False), gun(7, "LRM 5", dead=True), ], [offer(3, "PPC", 1, 2, True), offer(4, "Medium Laser", 2, 2, False)], ) )["firing"] found = firingaudit.account(plan) self.assertEqual(len(found["weapons"]), 5) self.assertEqual(sum(found["counts"].values()), 5) self.assertEqual( {w["name"]: w["status"] for w in found["weapons"]}, { "PPC": "fired", "Medium Laser": "held", "Small Laser": "twist", "SRM 4": "unusable", "LRM 5": "dead", }, ) self.assertEqual(found["declared"], 1) self.assertEqual((found["depth"], found["alpha"]), (1, 2))
def test_a_gun_the_volley_stopped_short_of_says_where_it_stopped(self): plan = json.loads( line( [gun(3, "PPC"), gun(4, "Medium Laser")], [offer(3, "PPC", 1, 2, True), offer(4, "Medium Laser", 2, 2, False)], ) )["firing"] held = next(w for w in firingaudit.account(plan)["weapons"] if w["status"] == "held") self.assertIn("stopped at 1", held["note"])
def test_a_gun_with_nothing_in_reach_is_out_of_range_not_out_of_arc(self): # The two read the same on the page and mean opposite things: one is # answered by turning, the other only by moving. far = gun(4, "Medium Laser") far["longRange"] = 2 plan = json.loads(line([gun(3, "PPC"), far], [offer(3, "PPC", 1, 1, True)]))["firing"] found = {w["name"]: w["status"] for w in firingaudit.account(plan)["weapons"]} self.assertEqual(found["Medium Laser"], "range")
def test_the_page_is_handed_a_sentence_for_every_status_it_shows(self): # `sds watch` prints these rather than wording the same status a second # way, so a status with no sentence is a blank cell on the page. plan = json.loads(line([gun(3, "PPC")], [offer(3, "PPC", 1, 1, True)]))["firing"] found = firingaudit.account(plan) self.assertEqual(sorted(found["why"]), sorted(found["counts"])) for label, sentence in found["why"].values(): self.assertTrue(label and sentence)
def test_a_row_without_the_diagnostic_is_skipped(self): self.log.write_text(json.dumps({"phase": "MOVEMENT", "candidates": []}) + "\n") self.assertEqual(firingaudit.rows(self.log), [])
def test_the_report_prints_every_weapon(self): self.log.write_text( line( [gun(3, "PPC"), gun(4, "Medium Laser")], [offer(3, "PPC", 1, 2, True), offer(4, "Medium Laser", 2, 2, False)], ) + "\n" ) out = io.StringIO() firingaudit.report(firingaudit.rows(self.log), out=out) text = out.getvalue() self.assertIn("PPC", text) self.assertIn("Medium Laser", text) self.assertIn("winning volley size", text)
if __name__ == "__main__": unittest.main()