def test_damage_cap(self): state = setup_state() cmd = Cmd.NOP block = Block.WALL for player in [state.player, state.opponent]: state.map[player.x + 1, player.y] = block player.damage = 3 nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert prev.damage == 3 assert cur.damage == 5 for player in [state.player, state.opponent]: state.map[player.x + 1, player.y] = block player.damage = 4 nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert prev.damage == 4 assert cur.damage == 5 for player in [state.player, state.opponent]: state.map[player.x + 1, player.y] = block player.damage = 5 nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert prev.damage == 5 assert cur.damage == 5
def test_collision_rear_end(self): state = setup_state() state.player.speed = Speed.SPEED_1.value state.opponent.speed = Speed.SPEED_1.value state.player.x = 2 state.player.y = 2 state.opponent.x = 1 state.opponent.y = 2 nstate = next_state(state, Cmd.NOP, Cmd.ACCEL) assert nstate.player.x == state.player.x + state.player.speed assert nstate.player.y == state.player.y assert nstate.opponent.x == nstate.player.x - 1 assert nstate.opponent.y == state.opponent.y assert nstate.player.damage == 0 assert nstate.opponent.damage == 0 state.player.x = 1 state.player.y = 2 state.opponent.x = 2 state.opponent.y = 2 nstate = next_state(state, Cmd.ACCEL, Cmd.NOP) assert nstate.player.x == nstate.opponent.x - 1 assert nstate.player.y == state.player.y assert nstate.opponent.x == state.opponent.x + state.opponent.speed assert nstate.opponent.y == state.opponent.y assert nstate.player.damage == 0 assert nstate.opponent.damage == 0
def test_turn_into_cybertruck(self): state = setup_state() state.player.x = 1 state.opponent.x = 100 for player in [state.player, state.opponent]: player.y = 2 player.speed = 9 state.map[player.x, player.y - 1].set_cybertruck() state.map[player.x, player.y + 1].set_cybertruck() nstate = next_state(state, Cmd.LEFT, Cmd.LEFT) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.speed == 3 # check that the player ends up behind cybertruck (effectively # moving back one block) assert cur.x == prev.x - 1 assert cur.y == prev.y - 1 assert nstate.map[prev.x, prev.y - 1] != Block.CYBERTRUCK nstate = next_state(state, Cmd.RIGHT, Cmd.RIGHT) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.speed == 3 # check that the player ends up behind cybertruck (effectively # moving back one block) assert cur.x == prev.x - 1 assert cur.y == prev.y + 1 assert nstate.map[prev.x, prev.y + 1] != Block.CYBERTRUCK
def test_lizard(self): state = setup_state() state.player.lizards = 1 state.opponent.lizards = 1 cmd = Cmd.LIZARD nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x - prev.x == prev.speed assert cur.speed == prev.speed for player in [state.player, state.opponent]: for x in range(1, player.speed): state.map[player.x + x, player.y] = Block.MUD nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x - prev.x == prev.speed assert cur.speed == prev.speed for player in [state.player, state.opponent]: state.map[player.x + player.speed, player.y] = Block.MUD nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x - prev.x == prev.speed assert cur.speed == prev_speed(prev.speed)
def test_damage_speed_limit(self): state = setup_state() cmd = Cmd.ACCEL # normal, no damage state.player.speed = Speed.SPEED_3.value state.opponent.speed = Speed.SPEED_3.value nstate = next_state(state, cmd, cmd) assert nstate.player.speed == Speed.MAX_SPEED.value assert nstate.opponent.speed == Speed.MAX_SPEED.value # speed capped at SPEED_3, so should do nothing state.player.damage = 2 state.opponent.damage = 2 nstate = next_state(state, cmd, cmd) assert nstate.player.speed == Speed.SPEED_3.value assert nstate.opponent.speed == Speed.SPEED_3.value # should be able to accel to SPEED_3 state.player.speed = Speed.SPEED_2.value state.opponent.speed = Speed.SPEED_2.value nstate = next_state(state, cmd, cmd) assert nstate.player.speed == Speed.SPEED_3.value assert nstate.opponent.speed == Speed.SPEED_3.value # speed capped at SPEED_2 state.player.damage = 3 state.opponent.damage = 3 state.player.speed = Speed.SPEED_1.value state.opponent.speed = Speed.SPEED_1.value nstate = next_state(state, cmd, cmd) assert nstate.player.speed == Speed.SPEED_2.value assert nstate.opponent.speed == Speed.SPEED_2.value # speed capped at MIN_SPEED (0) state.player.lizards = 1 state.opponent.lizards = 1 for cmd in [ Cmd.ACCEL, Cmd.LEFT, Cmd.RIGHT, Cmd.DECEL, Cmd.NOP, Cmd.LIZARD, Cmd.BOOST ]: state.player.damage = 5 state.opponent.damage = 5 state.player.speed = Speed.MIN_SPEED.value state.opponent.speed = Speed.MIN_SPEED.value nstate = next_state(state, cmd, cmd) assert nstate.player.speed == Speed.MIN_SPEED.value assert nstate.opponent.speed == Speed.MIN_SPEED.value assert nstate.player.x == state.player.x assert nstate.player.y == state.player.y assert nstate.opponent.x == state.opponent.x assert nstate.opponent.y == state.opponent.y
def test_boost(self): state = setup_state() state.player.boosts = 1 state.opponent.boosts = 1 cmd = Cmd.BOOST nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x - prev.x == Speed.BOOST_SPEED.value assert cur.speed == Speed.BOOST_SPEED.value assert cur.boosts - prev.boosts == -1 assert cur.boosting == True assert cur.boost_counter == 5 # test no damage for i in range(5): pstate = nstate nstate = next_state(nstate, Cmd.NOP, cmd.NOP) for prev, cur in zip([pstate.player, pstate.opponent], [nstate.player, nstate.opponent]): assert cur.boost_counter - prev.boost_counter == -1 if i < 4: assert cur.boosting == True assert cur.speed == Speed.BOOST_SPEED.value else: assert cur.boosting == False assert cur.speed == Speed.MAX_SPEED.value # test with damage state.player.speed = Speed.MIN_SPEED.value state.opponent.speed = Speed.MIN_SPEED.value state.player.damage = 2 state.opponent.damage = 2 nstate = next_state(state, cmd, cmd) for i in range(5): pstate = nstate nstate = next_state(nstate, Cmd.NOP, cmd.NOP) for prev, cur in zip([pstate.player, pstate.opponent], [nstate.player, nstate.opponent]): assert cur.boost_counter - prev.boost_counter == -1 if i < 4: assert cur.boosting == True assert cur.speed == boost_speed(cur.damage) else: assert cur.boosting == False assert cur.speed == max_speed(cur.damage)
def test_decel_boost_cancel(self): state = setup_state() state.player.boosts = 1 state.opponent.boosts = 1 # test deceleration cancels boost nstate = next_state(state, Cmd.BOOST, Cmd.BOOST) assert nstate.player.boosting assert nstate.opponent.boosting nnstate = next_state(nstate, Cmd.DECEL, Cmd.DECEL) for player in [nnstate.player, nnstate.opponent]: assert not player.boosting assert player.boost_counter == 0 assert player.speed == Speed.MAX_SPEED.value
def test_tweet_place_in_future_path(self): state = setup_state() state.player.tweets = 1 state.player.x = 200 state.opponent.y = 1 state.opponent.x = 100 pred = lambda s: Cmd.RIGHT nstate = next_state(state, Cmd.NOP, pred(state)) nnstate = next_state(nstate, Cmd.NOP, pred(nstate)) match = Cmd(Cmd.TWEET, pos=(nstate.opponent.x + 3, nnstate.opponent.y)) assert offensive_search(state, pred_opp=pred) == match
def test_damage_boost_limit(self): state = setup_state() state.player.boosts = 1 state.opponent.boosts = 1 cmd = Cmd.BOOST # normal, no damage state.player.speed = Speed.SPEED_3.value state.opponent.speed = Speed.SPEED_3.value nstate = next_state(state, cmd, cmd) assert nstate.player.speed == Speed.BOOST_SPEED.value assert nstate.opponent.speed == Speed.BOOST_SPEED.value assert nstate.player.boosting assert nstate.opponent.boosting # speed capped at SPEED_3, so should do nothing state.player.damage = 2 state.opponent.damage = 2 nstate = next_state(state, cmd, cmd) assert nstate.player.speed == Speed.SPEED_3.value assert nstate.opponent.speed == Speed.SPEED_3.value assert nstate.player.boosting assert nstate.opponent.boosting # should be able to boost to SPEED_3 state.player.speed = Speed.SPEED_2.value state.opponent.speed = Speed.SPEED_2.value nstate = next_state(state, cmd, cmd) assert nstate.player.speed == Speed.SPEED_3.value assert nstate.opponent.speed == Speed.SPEED_3.value assert nstate.player.boosting assert nstate.opponent.boosting # speed capped at SPEED_2 state.player.damage = 3 state.opponent.damage = 3 state.player.speed = Speed.SPEED_1.value state.opponent.speed = Speed.SPEED_1.value nstate = next_state(state, cmd, cmd) assert nstate.player.speed == Speed.SPEED_2.value assert nstate.opponent.speed == Speed.SPEED_2.value assert nstate.player.boosting assert nstate.opponent.boosting
def search(state, opp_pred, max_search_depth): options = [] # holds the bfs queue queue = deque([[]]) while queue: actions = queue.popleft() cur_state = state for cmd in actions: # get opp cmd opp_cmd = opp_pred(cur_state) # calculate next state cur_state = next_state(cur_state, cmd, opp_cmd) # save final state options.append((actions, cur_state)) # as soon as we find an action that can take us outside of our current # view we stop at this depth since it is pretty pointless to search # further if cur_state.player.x >= cur_state.map.max_x: max_search_depth = min(len(actions), max_search_depth) if len(actions) < max_search_depth: queue += [actions + [v] for v in valid_actions(cur_state)] # filter out action sequences that have invalid (too short) lengths options = [o for o in options if len(o[0]) == max_search_depth] return options
def test_turn_collision_start_x(self): state = setup_state() def switch(state, switched): if switched: return (state.opponent, state.player) else: return (state.player, state.opponent) for switched in [True, False]: behind, ahead = switch(state, switched) behind.x = 10 behind.y = 2 behind.damage = 0 behind.speed = Speed.MAX_SPEED.value ahead.x = 11 ahead.y = 2 ahead.damage = 0 ahead.speed = Speed.SPEED_1.value state.map[behind.x, behind.y + 1] = Block.MUD nstate = next_state(state, Cmd.RIGHT, Cmd.RIGHT) nbehind, nahead = switch(nstate, switched) for cur, prev in zip([nbehind, nahead], [behind, ahead]): assert cur.y == prev.y + 1 assert cur.speed == prev.speed assert cur.damage == prev.damage == 0
def test_collision_same_block_lizarding(self): state = setup_state() def switch(state, switched): if switched: return (state.opponent, state.player, Cmd.NOP, Cmd.LIZARD) else: return (state.player, state.opponent, Cmd.LIZARD, Cmd.NOP) for switched in [True, False]: behind, ahead, p1, p2 = switch(state, switched) behind.x = 10 behind.y = 2 behind.speed = Speed.MAX_SPEED.value # 9 behind.lizards = 1 ahead.x = 11 ahead.y = 2 ahead.speed = Speed.SPEED_3.value # 8 assert behind.x + behind.speed == ahead.x + ahead.speed nstate = next_state(state, p1, p2) nbehind, nahead, _, _ = switch(nstate, switched) assert nahead.y == ahead.y assert nahead.x == ahead.x + ahead.speed assert nbehind.y == behind.y assert nbehind.x == nahead.x - 1
def test_hit_cybertruck_both_difflane(self): state = setup_state() state.player.x = 1 state.player.y = 1 state.player.speed = 9 state.opponent.x = 1 state.opponent.y = 3 state.opponent.speed = 6 state.map[3, 2].set_cybertruck() nstate = next_state(state, Cmd.RIGHT, Cmd.LEFT) # both end up right behind the cybertruck, where they collide and move # back -1x into their original lanes assert nstate.player.x == 1 assert nstate.player.y == 1 assert nstate.player.speed == Speed.SPEED_1.value assert nstate.opponent.x == 1 assert nstate.opponent.y == 3 assert nstate.opponent.speed == Speed.SPEED_1.value assert nstate.player.damage == 2 assert nstate.opponent.damage == 2 assert nstate.map[3, 2] == Block.EMPTY
def test_collision_rear_end_lizarding(self): state = setup_state() def switch(state, switched): if switched: return (state.opponent, state.player) else: return (state.player, state.opponent) for switched in [True, False]: behind, ahead = switch(state, switched) behind.x = 1 behind.y = 2 behind.speed = Speed.MAX_SPEED.value behind.lizards = 1 ahead.x = 2 ahead.y = 2 ahead.speed = Speed.SPEED_1.value ahead.lizards = 1 for p1, p2 in zip([Cmd.NOP, Cmd.LIZARD], [Cmd.LIZARD, Cmd.NOP]): nstate = next_state(state, p1, p2) nbehind, nahead = switch(nstate, switched) assert behind.y == ahead.y == nbehind.y == nahead.y assert behind.x < ahead.x assert nbehind.x > nahead.x assert nbehind.x == behind.x + behind.speed assert behind.damage == nbehind.damage assert ahead.damage == nahead.damage
def key(o): # scores the final state after all the cmds s = weights.score(cur_state, o[1]) # scores the next state given the first cmd if weights.next_state: nstate = next_state(cur_state, o[0][0], pred_opp(cur_state)) s += weights.next_state * weights.score(cur_state, nstate) return s
def test_valid_cmds(self): state = setup_state() state.opponent.y = 3 state.opponent.boosts = 1 state.opponent.lizards = 1 state.map[2, 3] = Block.MUD for action in valid_actions(state.switch()): nstate = next_state(state, Cmd.NOP, action) assert calc_opp_cmd(Cmd.NOP, state, nstate) == action
def test_tweet_ignore_fix(self): state = setup_state() state.player.tweets = 1 state.player.x = 100 state.opponent.x = 10 state.opponent.y = 4 state.opponent.damage = 2 def pred(state): if state.opponent.damage > 0: return Cmd.FIX return Cmd.LEFT nstate = next_state(state, Cmd.NOP, Cmd.LEFT) nnstate = next_state(nstate, Cmd.NOP, Cmd.LEFT) match = Cmd(Cmd.TWEET, pos=(nstate.opponent.x + 3, nnstate.opponent.y)) assert offensive_search(state, pred_opp=pred) == match
def test_validity(self): state = setup_state() opp_pred = lambda s: Cmd.ACCEL options = search(state, opp_pred, 4) for actions, final_state in options: cur_state = state for action in actions: cur_state = next_state(cur_state, action, opp_pred(cur_state)) assert cur_state == final_state
def test_decel(self): state = setup_state() cmd = Cmd.DECEL nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x - prev.x == prev_speed(prev.speed) assert cur.speed == prev_speed(prev.speed) for player in [state.player, state.opponent]: state.map[player.x + 1, player.y] = Block.MUD nstate = next_state(state, Cmd.DECEL, Cmd.DECEL) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x - prev.x == prev_speed(prev.speed) assert cur.speed == prev_speed(prev_speed(prev.speed))
def test_dont_tweet_on_own_pos(self): state = setup_state() state.player.tweets = 1 state.player.x = 200 state.player.y = 2 state.opponent.x = state.player.x - state.opponent.speed - 3 state.opponent.y = state.player.y pred = lambda s: Cmd.NOP nstate = next_state(state, Cmd.NOP, pred(state)) nnstate = next_state(nstate, Cmd.NOP, pred(nstate)) assert nstate.opponent.y == state.player.y assert nstate.opponent.x + 3 == state.player.x match = Cmd(Cmd.TWEET, pos=(nstate.opponent.x + 2, nnstate.opponent.y)) assert match.pos[0] == state.player.x - 1 assert match.pos[1] == state.player.y assert offensive_search(state, pred_opp=pred) == match
def test_right(self): state = setup_state() state.opponent.y = 3 cmd = Cmd.RIGHT nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y + 1 assert cur.x - prev.x == prev.speed - 1 assert cur.speed == prev.speed for player in [state.player, state.opponent]: state.map[player.x, player.y + 1] = Block.MUD nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y + 1 assert cur.x - prev.x == prev.speed - 1 assert cur.speed == prev_speed(prev.speed)
def test_opp_search_validity(self): state = setup_state() options = opp_search(state) state = state.switch() pred = lambda s: Cmd.ACCEL for actions, final_state in options: cur_state = state for action in actions: cur_state = next_state(cur_state, action, pred(cur_state)) assert cur_state == final_state
def test_hit_mud_next_round_boost_cancel(self): state = setup_state() state.player.boosts = 1 state.opponent.boosts = 1 state = next_state(state, Cmd.BOOST, Cmd.BOOST) assert state.player.boosting assert state.opponent.boosting # test hit mud in second boost round for player in [state.player, state.opponent]: state.map[player.x + 1, player.y] = Block.MUD nstate = next_state(state, Cmd.NOP, Cmd.NOP) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x - prev.x == Speed.BOOST_SPEED.value assert cur.speed == Speed.MAX_SPEED.value assert cur.boosting == False assert cur.boost_counter == 0
def test_collision_rear_end_same_block(self): state = setup_state() state.player.speed = 8 state.opponent.speed = 9 state.player.x = 2 state.player.y = 2 state.opponent.x = 1 state.opponent.y = 2 nstate = next_state(state, Cmd.NOP, Cmd.NOP) assert (state.player.x + state.player.speed == state.opponent.x + state.opponent.speed) assert nstate.player.x == state.player.x + state.player.speed assert nstate.player.y == state.player.y assert nstate.opponent.x == nstate.player.x - 1 assert nstate.opponent.y == state.opponent.y assert nstate.player.damage == 0 assert nstate.opponent.damage == 0 state.player.speed = 9 state.opponent.speed = 8 state.player.x = 1 state.player.y = 2 state.opponent.x = 2 state.opponent.y = 2 nstate = next_state(state, Cmd.NOP, Cmd.NOP) assert (state.player.x + state.player.speed == state.opponent.x + state.opponent.speed) assert nstate.player.x == nstate.opponent.x - 1 assert nstate.player.y == state.player.y assert nstate.opponent.x == state.opponent.x + state.opponent.speed assert nstate.opponent.y == state.opponent.y assert nstate.player.damage == 0 assert nstate.opponent.damage == 0
def test_fix(self): state = setup_state() state.player.damage = 3 state.opponent.damage = 3 cmd = Cmd.FIX nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x == prev.x assert cur.speed == prev.speed assert prev.damage == 3 assert cur.damage == 1 state.player.damage = 1 state.opponent.damage = 1 nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x == prev.x assert cur.speed == prev.speed assert prev.damage == 1 assert cur.damage == 0 state.player.damage = 0 state.opponent.damage = 0 nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x == prev.x assert cur.speed == prev.speed assert prev.damage == 0 assert cur.damage == 0
def test_collision_same_block(self): state = setup_state() state.player.x = 1 state.player.y = 2 state.opponent.x = 1 state.opponent.y = 4 nstate = next_state(state, Cmd.RIGHT, Cmd.LEFT) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x == prev.x + prev.speed - 2 assert cur.speed == prev.speed assert cur.score == prev.score assert prev.damage == 0 assert cur.damage == 0
def test_fix_on_ct(self): state = setup_state() for player in [state.player, state.opponent]: player.damage = 2 state.map[player.x, player.y].set_cybertruck() nstate = next_state(state, Cmd.FIX, Cmd.FIX) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.x == prev.x assert cur.y == prev.y assert cur.damage == prev.damage - 2 assert nstate.map[prev.x, prev.y] == Block.CYBERTRUCK
def test_hit_oil_spill(self): state = setup_state() cmd = Cmd.NOP block = Block.OIL_SPILL for player in [state.player, state.opponent]: state.map[player.x + 1, player.y] = block nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x - prev.x == prev.speed assert cur.speed == prev_speed(prev.speed) assert cur.score - prev.score == -4 assert nstate.map[prev.x + 1, prev.y] == block assert prev.damage == 0 assert cur.damage == 1
def test_hit_wall(self): state = setup_state() cmd = Cmd.NOP block = Block.WALL for player in [state.player, state.opponent]: state.map[player.x + 1, player.y] = block nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x - prev.x == prev.speed assert cur.speed == Speed.SPEED_1.value assert cur.score - prev.score == -5 assert nstate.map[prev.x + 1, prev.y] == block assert prev.damage == 0 assert cur.damage == 2
def test_hit_cybertruck(self): state = setup_state() cmd = Cmd.NOP block = Block.EMPTY # initing the map manually to try and trigger the bug where the # cybertruck gets removed from the map during next_state state.map = Map(global_map=state.map.global_map, raw_map=[ [{ 'position': { 'x': state.player.x + 2, 'y': state.player.y, }, 'surfaceObject': block.value, 'isOccupiedByCyberTruck': True }], [{ 'position': { 'x': state.opponent.x + 2, 'y': state.opponent.y, }, 'surfaceObject': block.value, 'isOccupiedByCyberTruck': True }], ]) for player in [state.player, state.opponent]: assert state.map[player.x + 2, player.y].block == block assert state.map[player.x + 2, player.y] == Block.CYBERTRUCK assert player.speed > 1 nstate = next_state(state, cmd, cmd) for prev, cur in zip([state.player, state.opponent], [nstate.player, nstate.opponent]): assert cur.y == prev.y assert cur.x == prev.x + 1 assert cur.speed == Speed.SPEED_1.value assert cur.score - prev.score == -7 assert nstate.map[prev.x + 2, prev.y] == block assert state.map[prev.x + 2, prev.y] == Block.CYBERTRUCK assert prev.damage == 0 assert cur.damage == 2