예제 #1
0
def test_next_event():
    random.seed(250)
    # We're going to assume that the game goes on long enough for a few queries not to reach the
    # end of the game
    map_ = P.Map.load('maps/quad.json')
    agent0, agent1 = random_agent.Agent(), random_agent.Agent()
    game = P.Game.start(map_, [agent0, agent1])
    assert next(game).method == 'place'

    assert game.next_event(method='reinforce').method == 'reinforce'
    nevents = len(game.world.event_log)
    assert game.next_event(method='act').method == 'act'
    assert len(game.world.event_log
               ) == nevents + 1, 'act should immediately follow reinforce'

    assert game.next_event(player_index=1).agent is agent1
    for _ in range(2):
        # do this twice to check that multiple conditions combine with AND, because agent0.reinforce
        # would be separated by both agent1.reinforce and agent0.act
        event = game.next_event(agent=agent0, method='reinforce')
        assert event.state.player_index == 0
        assert event.method == 'reinforce'

    with pytest.raises(StopIteration):
        game.next_event(predicate=lambda e: False)
예제 #2
0
def test_placement_phase_asymmetric():
    random.seed(500)
    map_ = P.Map.load('maps/tiny4.json')
    game = P.Game.start(
        map_,
        [random_agent.Agent(),
         random_agent.Agent(),
         random_agent.Agent()])
    placement_events = list(it.takewhile(lambda e: e.method == 'place', game))
    # game should now be paused before evaluating the first reinforce()

    assert not game.world.has_neutral
    n_armies = map_.initial_armies[3]
    assert len(
        placement_events
    ) == 3 * n_armies - 4, 'initial armies minus assigned territories'
    assert len(set(
        id(e.agent)
        for e in placement_events[:3])) == 3, 'placement is round-robin'

    armies_by_owner = collections.defaultdict(int)
    territories_by_owner = collections.defaultdict(int)
    for owner, armies in zip(game.world.owners, game.world.armies):
        armies_by_owner[owner] += armies
        territories_by_owner[owner] += 1
    assert all(n == n_armies for n in armies_by_owner.values())
    assert list(sorted(territories_by_owner.values())) == [
        1, 1, 2
    ], 'someone random gets an extra territory'
예제 #3
0
def test_play_game_fallback_agent_redeem_only():
    random.seed(250)
    map_ = P.Map.load('maps/quad.json')
    with pytest.raises(ValueError):
        P.Game.play(map_, [RedeemWrongAgent(), random_agent.Agent()])
    P.Game.play(map_,
                [P.FallbackAgent(RedeemWrongAgent()),
                 random_agent.Agent()])
예제 #4
0
def test_play_game_fallback_agent():
    random.seed(250)
    map_ = P.Map.load('maps/tiny4.json')
    with pytest.raises(ValueError):
        P.Game.play(map_, [EverythingWrongAgent(), random_agent.Agent()])
    P.Game.play(
        map_, [P.FallbackAgent(EverythingWrongAgent()),
               random_agent.Agent()])
예제 #5
0
def test_start_game():
    agents = [um.Mock(), um.Mock()]
    agents[0].place.side_effect = random_agent.Agent().place
    agents[1].place.side_effect = random_agent.Agent().place
    event = next(P.Game.start(P.Map.load('maps/tiny3.json'), agents))
    agents[0].start_game.assert_called_once()
    agents[1].start_game.assert_called_once()
    assert all(owner is not None for owner in event.state.world.owners), \
        'start_game should be called after the initial allocation of territories'
    assert all(armies == 1 for armies in event.state.world.armies), \
        'start_game should be called for all agents before any place() calls'
예제 #6
0
def test_watch_game():
    random.seed(987)
    map_ = P.Map.load('maps/tiny3.json')
    agents = [
        ConsistencyCheckingAgent(random_agent.Agent()),
        ConsistencyCheckingAgent(random_agent.Agent())
    ]
    name = 'test_watch_game_tmp.mp4'
    try:
        P.Game.watch(map_, agents, name)
        assert os.path.isfile(name)
    finally:
        if os.path.exists(name):
            os.remove(name)
예제 #7
0
def test_fuzz(map_name):
    SMALL_MAPS = {'mini', 'tiny3', 'tiny4'}
    random.seed(100)
    map_ = P.Map.load('maps/{}.json'.format(map_name))
    rand_agent = ConsistencyCheckingAgent(random_agent.Agent())
    for n_players in range(2, map_.max_players):
        agents = [rand_agent] * n_players
        ntrials = 1000 if map_name in SMALL_MAPS else 10
        results = [P.Game.play(map_, agents) for _ in range(ntrials)]
        for result in results:
            assert set(result.winners) | set(result.eliminated) == set(
                range(n_players))
            assert not (set(result.winners) & set(result.eliminated))
        if map_name in SMALL_MAPS:
            winners = dict(
                collections.Counter([r.outright_winner for r in results]))
            # small maps, aggressive agents => there should normally be a winner
            n_draws = winners.pop(None, 0)
            assert n_draws < 0.1 * ntrials
            # fairness means the wins should be distributed evenly
            # - this test could fail - if so, try increasing ntrials
            p_win = 1 / n_players
            norm_approx_std = (ntrials * p_win * (1 - p_win))**0.5
            assert max(winners.values()) - min(
                winners.values()) < 3 * norm_approx_std
예제 #8
0
def test_placement_phase():
    map_ = P.Map.load('maps/tiny3.json')
    game = P.Game.start(map_, [random_agent.Agent(), random_agent.Agent()])
    placement_events = list(it.takewhile(lambda e: e.method == 'place', game))
    # game should now be paused before evaluating the first reinforce()

    assert game.world.has_neutral
    n_armies = map_.initial_armies[2]
    assert len(
        placement_events
    ) == 3 * n_armies - 3, 'initial armies minus assigned territories'
    assert len(set(
        id(e.agent)
        for e in placement_events[:3])) == 3, 'placement is round-robin'
    assert all(a == n_armies for a in game.world.armies)
    assert set(game.world.owners) == {0, 1, 2}
예제 #9
0
def test_placement_phase_too_many_players():
    random.seed(400)
    map_ = P.Map.load('maps/tiny3.json')
    game = P.Game.start(map_, [random_agent.Agent()] * 4)
    with pytest.raises(ValueError) as e:
        list(game)
    assert '4' in str(e)
    assert '3' in str(e)
예제 #10
0
def test_play_game():
    random.seed(345)
    map_ = P.Map.load('maps/tiny4.json')
    agents = [
        ConsistencyCheckingAgent(random_agent.Agent()),
        ConsistencyCheckingAgent(random_agent.Agent())
    ]
    game = P.Game.start(map_, agents)
    assert game.map is map_
    for n, event in enumerate(game):
        # log should already contain this latest event
        assert game.world.event_log[-1] == event._replace(
            agent=repr(event.agent), state=event.state.player_index)
        assert len(game.world.event_log) == n + 1
    assert game.world._repr_svg_()
    assert event._repr_svg_()
    # game should be over
    assert len(game.result.winners) == 1 or game.world.turn == map_.max_turns
예제 #11
0
def test_placement_phase_error():
    random.seed(500)
    map_ = P.Map.load('maps/tiny3.json')
    bad_agent = um.Mock(__str__=um.Mock(return_value='BadMockAgent'))
    agents = [bad_agent, random_agent.Agent(), random_agent.Agent()]

    world, agents_and_states = _make_world(map_, agents)
    bad_agent.place = um.Mock(return_value=3)  # out-of-bounds placement
    with pytest.raises(ValueError) as e:
        list(P._placement_phase(world, agents_and_states, random))
    assert 'place' in str(e) and 'BadMockAgent' in str(e)
    assert '0..2' in str(e) and '3' in str(e)

    world, agents_and_states = _make_world(map_, agents)
    bad_agent.place = um.Mock(side_effect=lambda state: state.world.owners.
                              index(1))  # enemy territory placement
    with pytest.raises(ValueError) as e:
        list(P._placement_phase(world, agents_and_states, random))
    assert 'place' in str(e) and 'enemy' in str(e) and 'BadMockAgent' in str(e)
    assert '1' in str(e)
예제 #12
0
def test_map_world_state_game_repr():
    map_ = P.Map.load('maps/tiny3.json')
    game = P.Game.start(map_, [random_agent.Agent()] * 2)

    assert 'territories=3' in str(game.world.map)
    assert 'continents=1' in str(game.world.map)
    assert game.world.map._repr_svg_() is not None

    assert str(game.world.map) in str(game.world)
    assert 'players=3' in str(game.world)
    assert game.world._repr_svg_() is not None

    first_placement = next(game)
    state = game.agents_and_states[0][1]
    state._add_cards([1, 2, 3, 4])  # don't actually need real cards here
    assert 'territories=1/3' in str(state)
    assert 'armies=1/3' in str(state)
    assert 'cards=4' in str(state)
    assert state._repr_svg_() is not None

    assert str(first_placement.state) in str(first_placement)
    assert first_placement._repr_svg_() is not None
예제 #13
0
def mini(path):
    if os.path.isdir(path):
        shutil.rmtree(path)
    os.makedirs(path)

    map_ = P.Map.load('maps/mini.json')
    agents = [random_agent.Agent(min_attack=1)] * 3
    seed = 1000

    random.seed(seed)
    game = P.Game.start(map_, agents)
    with open('{}/start.svg'.format(path), 'w') as f:
        f.write(next(game).state.world._repr_svg_())
    with open('{}/end.svg'.format(path), 'w') as f:
        f.write(list(game)[-1].state.world._repr_svg_())

    random.seed(seed)
    for n, event in enumerate(P.Game.start(map_, agents)):
        with open('{}/step_{}.svg'.format(path, n), 'w') as f:
            f.write(event._repr_svg_())

    random.seed(seed)
    P.Game.watch(map_, agents, '{}/game.mp4'.format(path), dpi=144)
예제 #14
0
import random
import os
import sys
import shutil
import networkx as nx
import preem as P
from agents import random_agent

map_classic = P.Map.load('maps/classic.json')
map_classic.max_turns = 10  # otherwise it is far too long with just these silly random agents!
classic_agents = [random_agent.Agent()] * 3


def eg_classic_svg(name):
    random.seed(42)
    game = P.Game.start(map_classic, classic_agents)
    next(game)
    with open(name, 'w') as f:
        f.write(game.world._repr_svg_())


def eg_classic_mp4(name):
    random.seed(42)
    P.Game.watch(map_classic, classic_agents, name, dpi=144)


def agent_flow_svg(name):
    g = nx.DiGraph()
    g.graph.update(rankdir='LR', pad=.2)
    g.add_nodes_from(['place', 'redeem', 'reinforce', 'act'])
    g.add_edges_from([('place', 'place', dict(label='initial armies')),
예제 #15
0
def test_long_class_name():
    assert P._long_class_name(
        random_agent.Agent()) == 'agents.random_agent.Agent'