worldhistory

git clone https://git.godosa.eu/worldhistory

master

raw · 3812 bytes

import unittest

import numpy as np

from tests.helpers import make_race
from worldhistory.conflict import apply_conflict, apply_curse, conflict
from worldhistory.state import new_state


def two(ca, cb):
    return [make_race("a", conflict=ca), make_race("b", conflict=cb)]


class ConflictTest(unittest.TestCase):
    def test_internal_scales_with_crowding(self):
        r = [make_race("a", conflict={"internal": 0.01})]
        loss, _ = conflict(np.array([[100.0, 100.0]]), np.ones((1, 2)), np.array([[0.5, 1.0]]), r)
        np.testing.assert_allclose(loss[0], [0.5, 1.0])

    def test_no_border_losses_alone_or_within_family(self):
        r = two({"border": 0.1}, {"border": 0.1})
        r[1].family = "a"
        loss, press = conflict(np.array([[50.0], [50.0]]), np.ones((2, 1)), np.zeros((2, 1)), r)
        np.testing.assert_array_equal(loss, 0)
        np.testing.assert_array_equal(press, 0)

    def test_warlike_neighbour_hurts_more(self):
        P = np.array([[50.0], [50.0]])
        r = two({"border": 0.1, "aggression": 0.1}, {"border": 0.1, "aggression": 0.9})
        loss, press = conflict(P, np.zeros((2, 1)), np.zeros((2, 1)), r)
        self.assertGreater(loss[0, 0], loss[1, 0])
        self.assertGreater(press[0, 0], press[1, 0])
        # a: attacked = 0.9*1/1*0.5 = 0.45; attacking b outside b's core = 0 -> loss 0.1*0.45*50
        self.assertAlmostEqual(float(loss[0, 0]), 0.1 * 0.45 * 50)

    def test_defend_in_core_raises_attacker_losses(self):
        P = np.array([[50.0], [50.0]])
        base = two({"border": 0.1, "aggression": 0.8}, {"border": 0.1, "defend": 1.0})
        hard = two({"border": 0.1, "aggression": 0.8}, {"border": 0.1, "defend": 3.0})
        core = np.array([[0.0], [1.0]])                            # the cell is b's core habitat
        l1, _ = conflict(P, core, np.zeros((2, 1)), base)
        l3, _ = conflict(P, core, np.zeros((2, 1)), hard)
        self.assertGreater(l3[0, 0], l1[0, 0])

    def test_yield_turns_losses_into_moves(self):
        st = new_state(1, 1, np.zeros((1, 6)), seed=0)
        st.P[0, 0] = 100.0
        apply_conflict(st, np.array([[10.0]]), [make_race(conflict={"yield": 0.8})])
        self.assertAlmostEqual(float(st.P[0, 0]), 98.0)
        self.assertAlmostEqual(float(st.push[0, 0]), 8.0)


class DreadCurseTest(unittest.TestCase):
    def test_dread_deters_attacks_and_repels(self):
        P = np.array([[50.0], [50.0]])
        plain = two({"border": 0.1}, {"border": 0.1, "aggression": 0.9})
        feared = two({"border": 0.1, "dread": 0.8}, {"border": 0.1, "aggression": 0.9})
        l0, p0 = conflict(P, np.zeros((2, 1)), np.zeros((2, 1)), plain)
        l1, p1 = conflict(P, np.zeros((2, 1)), np.zeros((2, 1)), feared)
        self.assertAlmostEqual(float(l1[0, 0]), 0.2 * float(l0[0, 0]))   # b's attacks on a × (1 − 0.8)
        self.assertAlmostEqual(float(p1[1, 0]), float(p0[1, 0]) + 0.8 * 0.5)  # b is repelled by a's dread × a's share

    def test_blame_falls_on_attackers_of_cursing_race(self):
        P = np.array([[50.0], [50.0]])
        r = two({"border": 0.1, "curse": 2.0}, {"border": 0.1, "aggression": 0.9})
        blame = np.zeros((2, 1))
        conflict(P, np.zeros((2, 1)), np.zeros((2, 1)), r, blame=blame)
        # share of a's clan killed by b = border 0.1 × attacked 0.9·1·0.5 = 0.045; × curse 2
        np.testing.assert_allclose(blame[:, 0], [0.0, 0.09])

    def test_curse_kills_and_fades(self):
        st = new_state(1, 2, np.zeros((1, 6)), seed=0)
        st.P[0] = [100.0, 100.0]
        apply_curse(st, np.array([[0.1, 0.0]]), decay=0.5)
        np.testing.assert_allclose(st.P[0], [90.0, 100.0])
        apply_curse(st, np.zeros((1, 2)), decay=0.5)
        np.testing.assert_allclose(st.P[0], [85.5, 100.0])                 # curse halves: 5 % this step