def test_join_twice(self): "join_game fails when you try to join twice" (gid, pid) = b.new_game(Nicks.testNick) b.join_game(gid, Nicks.testNick2) b.join_game(gid, Nicks.testNick2) self.assertEqual(b.join_game(gid, Nicks.testNick2), b.Error.ALREADY_STARTED)
def test_notify_immediate_join(self): "Player gets notified immediately when other player is already in game" (gid, pid) = b.new_game(Nicks.testNick) b.join_game(gid, Nicks.testNick2) b.request_notify(pid, b.PlayerState.SUBMIT_GRID, None) # the notification should never have been registered at all self.assertFalse( pid in b.pending_notifications and (b.PlayerState.SUBMIT_GRID, None) in b.pending_notifications[pid])
def test_join_full(self): "Player.join fails if game is full" # note: this should never happen if the user uses the API # provided. join_game checks and returns an error before the # join call reaches the player. (gid, pid) = b.new_game(Nicks.testNick) b.join_game(gid, Nicks.testNick2) with self.assertRaises(b.GameFullException): b.players[pid].join(b.games[gid])
def test_notify_delayed_join(self): "Player gets notified when other player joins after some time" (gid, pid) = b.new_game(Nicks.testNick) b.request_notify(pid, b.PlayerState.SUBMIT_GRID, None) # notification has been registered as waiting self.assertTrue(pid in b.pending_notifications) self.assertTrue(b.PlayerState.SUBMIT_GRID in [x for (x, y) in b.pending_notifications[pid]]) b.join_game(gid, Nicks.testNick2) # now the notification should be gone. As per above, we assume # it was responded to self.assertFalse( pid in b.pending_notifications and (b.PlayerState.SUBMIT_GRID, None) in b.pending_notifications[pid])
def test_submit_invalid(self): "submit_grid fails with bad grids" # game starts, other player joins, we place first (gid, pid) = b.new_game(Nicks.testNick) otherpid = b.join_game(gid, Nicks.testNick2) # empty grid self.assertEqual(b.submit_grid(pid, []), b.Error.INVALID_GRID) # too few ships self.assertEqual(b.submit_grid(pid, [[0, 0, 1, 0], [0, 1, 0, 3]]), b.Error.INVALID_GRID) # ships overlap but otherwise valid self.assertEqual( b.submit_grid(pid, [[0, 0, 1, 0], [0, 0, 2, 0], [0, 0, 2, 0], [0, 0, 3, 0], [0, 0, 4, 0]]), b.Error.INVALID_GRID) # ship is rectangle, not line self.assertEqual( b.submit_grid(pid, [[0, 0, 1, 0], [0, 1, 2, 1], [0, 2, 2, 2], [0, 3, 3, 3], [0, 4, 4, 5]]), b.Error.INVALID_GRID) # ok grid, bad pid self.assertEqual( b.submit_grid(pid + 100, [[0, 0, 1, 0], [0, 1, 2, 1], [0, 2, 2, 2], [0, 3, 3, 3], [0, 4, 4, 4]]), b.Error.INVALID_PLYR_ID) # this should work self.assertEqual( b.submit_grid(pid, [[0, 0, 1, 0], [0, 1, 2, 1], [0, 2, 2, 2], [0, 3, 3, 3], [0, 4, 4, 4]]), 0) # but you can't submit twice self.assertEqual( b.submit_grid(pid, [[0, 0, 1, 0], [0, 1, 2, 1], [0, 2, 2, 2], [0, 3, 3, 3], [0, 4, 4, 4]]), b.Error.OUT_OF_TURN)
def test_normal_game(self): "state_codes are correct during a normal game, where one player wins" (gid, pid1) = b.new_game(Nicks.testNick) me = b.players[pid1] self.assertEqual(me.state_code, b.PlayerState.WAIT_FOR_JOIN) pid2 = b.join_game(gid, Nicks.testNick2) other = b.players[pid2] self.assertEqual(me.state_code, b.PlayerState.SUBMIT_GRID) self.assertEqual(other.state_code, b.PlayerState.SUBMIT_GRID) # they both submit the same grid, for simplicity b.submit_grid(pid1, [[0, 0, 1, 0], [0, 1, 2, 1], [0, 2, 2, 2], [0, 3, 3, 3], [0, 4, 4, 4]]) self.assertEqual(me.state_code, b.PlayerState.WAIT_FOR_SUBMIT) b.submit_grid(pid2, [[0, 0, 1, 0], [0, 1, 2, 1], [0, 2, 2, 2], [0, 3, 3, 3], [0, 4, 4, 4]]) self.assertEqual(me.state_code, b.PlayerState.BOMB) self.assertEqual(other.state_code, b.PlayerState.WAIT_FOR_BOMB) # here, we just play out some turns, with me bombing each of # their ships while they bomb an empty space for (x, y) in other.ship_points: b.bomb_position(pid1, x, y) b.bomb_position(pid2, 5, 5) # game should be over self.assertEqual(me.state_code, b.PlayerState.GAME_OVER) self.assertEqual(other.state_code, b.PlayerState.GAME_OVER) # we should have won self.assertEqual(b.get_game_end(pid1), (other.grid, 1, 1)) # they should have lost self.assertEqual(b.get_game_end(pid2), (me.grid, 1, 0))
def test_disconnect_notifies(self): "Opponent is notified when you disconnect" (gid, pid1) = b.new_game(b"") pid2 = b.join_game(gid, b"") b.request_notify(pid2, b.PlayerState.GAME_DIED, None) self.assertIn(pid2, b.pending_notifications) self.assertIn(b.PlayerState.GAME_DIED, [state for (state, x) in b.pending_notifications[pid2]]) b.disconnect(pid1) # Check notification has been responded to self.assertNotIn( b.PlayerState.GAME_DIED, [state for (state, x) in b.pending_notifications[pid2]])
def test_no_early_cull_dead(self): "Don't cull games before _both_ players leave" (gid, pid1) = b.new_game(b"") pid2 = b.join_game(gid, b"") self.assertIn(gid, b.games) b.disconnect(pid1) b.cull_inactive() # 1 player left: game should not be culled self.assertIn(gid, b.games) b.disconnect(pid2) b.cull_inactive() # 2 players left: should have been culled self.assertNotIn(gid, b.games)
def test_multiple_notifications(self): "Player waiting for multiple states is notified correctly" (gid, pid) = b.new_game(Nicks.testNick) # we wait for the same thing twice: both should be responded # to at the same time b.request_notify(pid, b.PlayerState.SUBMIT_GRID, None) b.request_notify(pid, b.PlayerState.SUBMIT_GRID, None) self.assertTrue(pid in b.pending_notifications) self.assertTrue(2, [x for (x, y) in b.pending_notifications[pid] ].count(b.PlayerState.SUBMIT_GRID)) # we also wait for something else: this should not be # responded to at the same time as the others b.request_notify(pid, b.PlayerState.BOMB, None) self.assertTrue(b.PlayerState.BOMB in [x for (x, y) in b.pending_notifications[pid]]) b.join_game(gid, Nicks.testNick2) # wait for join notifications should be gone self.assertFalse( pid in b.pending_notifications and (b.PlayerState.SUBMIT_GRID, None) in b.pending_notifications[pid]) # bomb notifications should still be here self.assertTrue(b.PlayerState.BOMB in [x for (x, y) in b.pending_notifications[pid]])
def test_bomb_history(self): "get_bomb_history fails with bad args but is correct otherwise" self.assertEqual(b.get_bombed_positions(123), (b.Error.INVALID_PLYR_ID, 0, [])) (gid, pid1) = b.new_game(Nicks.testNick) pid2 = b.join_game(gid, Nicks.testNick2) b.submit_grid(pid1, [[0, 0, 1, 0], [0, 1, 2, 1], [0, 2, 2, 2], [0, 3, 3, 3], [0, 4, 4, 4]]) b.submit_grid(pid2, [[0, 0, 1, 0], [0, 1, 2, 1], [0, 2, 2, 2], [0, 3, 3, 3], [0, 4, 4, 4]]) b.bomb_position(pid1, 0, 0) b.bomb_position(pid2, 0, 4) b.bomb_position(pid1, 0, 1) self.assertEqual(b.get_bombed_positions(pid1), (0, 1, [0, 4])) self.assertEqual(b.get_bombed_positions(pid2), (0, 2, [0, 0, 0, 1]))
def test_bomb_pos_invalid(self): "bomb_position fails with bad arguments or if called too early" # bad pid self.assertEqual(b.bomb_position(123, 0, 0), b.Error.INVALID_PLYR_ID) (gid, pid1) = b.new_game(Nicks.testNick) # before opponent joins self.assertEqual(b.bomb_position(pid1, 0, 0), b.Error.NO_OPPONENT) pid2 = b.join_game(gid, Nicks.testNick2) # before the bombing phase starts self.assertEqual(b.bomb_position(pid1, 0, 0), b.Error.OUT_OF_TURN) # players submit grid b.submit_grid(pid1, [[0, 0, 1, 0], [0, 1, 2, 1], [0, 2, 2, 2], [0, 3, 3, 3], [0, 4, 4, 4]]) b.submit_grid(pid2, [[0, 0, 1, 0], [0, 1, 2, 1], [0, 2, 2, 2], [0, 3, 3, 3], [0, 4, 4, 4]]) # bad position self.assertEqual(b.bomb_position(pid1, 100, 100), b.Error.INVALID_BOMB_TARGET) # this should work and hit self.assertEqual(b.bomb_position(pid1, 0, 0), 1)
def test_get_opponent_nickname(self): "get_opponent_nickname works" (gid, pid) = b.new_game(Nicks.testNick) pid2 = b.join_game(gid, Nicks.testNick2) self.assertEqual(Nicks.testNick, b.get_opponent_nickname(pid2)) self.assertEqual(Nicks.testNick2, b.get_opponent_nickname(pid))
def test_join_invalid(self): "join_game fails with invalid gid" (gid, pid) = b.new_game(Nicks.testNick) self.assertEqual(b.join_game(gid + 1, Nicks.testNick2), b.Error.NO_SUCH_GAME)