def get_balls(game: GameType): m = BALL_MASS r = BALL_RADIUS ball_c = PoolBall(BallType.CUE, Coordinates(0, 0), m, r) ball_1 = PoolBall(BallType.ONE, Coordinates(0, 0), m, r) ball_2 = PoolBall(BallType.TWO, Coordinates(0, 0), m, r) ball_3 = PoolBall(BallType.THREE, Coordinates(0, 0), m, r) ball_4 = PoolBall(BallType.FOUR, Coordinates(0, 0), m, r) ball_5 = PoolBall(BallType.FIVE, Coordinates(0, 0), m, r) ball_6 = PoolBall(BallType.SIX, Coordinates(0, 0), m, r) ball_7 = PoolBall(BallType.SEVEN, Coordinates(0, 0), m, r) ball_8 = PoolBall(BallType.EIGHT, Coordinates(0, 0), m, r) ball_9 = PoolBall(BallType.NINE, Coordinates(0, 0), m, r) balls = { BallType.CUE: ball_c, BallType.ONE: ball_1, BallType.TWO: ball_2, BallType.THREE: ball_3, BallType.FOUR: ball_4, BallType.FIVE: ball_5, BallType.SIX: ball_6, BallType.SEVEN: ball_7, BallType.EIGHT: ball_8, BallType.NINE: ball_9, } return balls
def resolve_ball_wall_collision(ball: PoolBall, wall: Direction): """ Sets the new velocity for this ball after it has collided with a wall. *Assumes wall is in one of 4 directions: N, E, S, or W* :param ball: pool ball :param wall: which wall (N, E, S, W) """ if wall == Direction.NORTH or wall == Direction.SOUTH: ball.vel = Vector(ball.vel.x, -ball.vel.y) # Reverse y-direction else: # EAST or WEST ball.vel = Vector(-ball.vel.x, ball.vel.y) # Reverse x-direction
def test_resolve_ball_ball_collision_2d_two_moving(self): # Initialize ball objects ball_a = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.0, 0.0), mass=1.0, radius=0.5) ball_b = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.0, 0.0), mass=1.0, radius=0.5) # A enters from W (270 degrees), contacts B on its left (180 degrees) # A enters from E (0 degrees), contacts A on its right (0 degrees) # A and B should both reverse their direction ball_a.pos, ball_a.vel, ball_a.mass = Coordinates(-0.5, 0.0), Vector(1.0, 0.0), 3.0 ball_b.pos, ball_b.vel, ball_b.mass = Coordinates(0.5, 0.0), Vector(-1.0, 0.0), 3.0 resolve_ball_ball_collision(ball_a, ball_b) a_vel_new = Vector(-1.0, 0.0) b_vel_new = Vector(1.0, 0.0) self.assertVectorAlmostEqual(ball_a.vel, a_vel_new) self.assertVectorAlmostEqual(ball_b.vel, b_vel_new) # A enters from W (270 degrees), contacts B on its left (180 degrees) # A enters from E (0 degrees), contacts A on its right (0 degrees) # A and B should both reverse their direction ball_a.pos, ball_a.vel, ball_a.mass = Coordinates(-0.5, 0.0), Vector(1.0, 0.0), 3.0 ball_b.pos, ball_b.vel, ball_b.mass = Coordinates(0.5, 0.0), Vector(-1.0, 0.0), 3.0 resolve_ball_ball_collision(ball_a, ball_b) a_vel_new = Vector(-1.0, 0.0) b_vel_new = Vector(1.0, 0.0) self.assertVectorAlmostEqual(ball_a.vel, a_vel_new) self.assertVectorAlmostEqual(ball_b.vel, b_vel_new) """
def test_resolve_ball_ball_collision_1d_diagonal(self): # Initialize ball objects ball_a = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.0, 0.0), mass=1.0, radius=0.5) ball_b = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.0, 0.0), mass=1.0, radius=0.5) # 2-D, stationary target ball ball_a.pos, ball_a.vel, ball_a.mass = Coordinates(-1.0, -1.0), Vector(2.0, 2.0), 3.0 ball_b.pos, ball_b.vel, ball_b.mass = Coordinates(0.0, 0.0), Vector(0.0, 0.0), 3.0 resolve_ball_ball_collision(ball_a, ball_b) a_vel_new = Vector(0.0, 0.0) b_vel_new = Vector(2.0, 2.0) self.assertVectorAlmostEqual(ball_a.vel, a_vel_new) self.assertVectorAlmostEqual(ball_b.vel, b_vel_new) # 2-D, head-on collision, same speeds ball_a.pos, ball_a.vel, ball_a.mass = Coordinates(-1.0, -1.0), Vector(2.0, 2.0), 3.0 ball_b.pos, ball_b.vel, ball_b.mass = Coordinates(0.0, 0.0), Vector(-2.0, -2.0), 3.0 resolve_ball_ball_collision(ball_a, ball_b) a_vel_new = Vector(-2.0, -2.0) b_vel_new = Vector(2.0, 2.0) self.assertVectorAlmostEqual(ball_a.vel, a_vel_new) self.assertVectorAlmostEqual(ball_b.vel, b_vel_new)
def test_init(self): name = '1' pos = Coordinates(0, 0) mass = 1.0 radius = 1.0 p = PoolBall(name, pos, mass, radius) self.assertEqual(p.type, name) self.assertEqual(p.pos, pos) self.assertEqual(p.mass, mass) self.assertEqual(p.radius, radius) self.assertEqual(p.vel, Vector(0, 0)) # Default should be 0
def test_check_ball_ball_collision_y_axis(self): # Initialize ball objects ball_a = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.0, 0.0), mass=1.0, radius=0.5) ball_b = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.0, 0.0), mass=1.0, radius=0.5) # On x-axis, not touching ball_a.pos = Coordinates(0.0, 2.01) ball_b.pos = Coordinates(0.0, 0.0) result = check_ball_ball_collision(ball_a, ball_b) self.assertFalse(result) # On x-axis, touching ball_a.pos = Coordinates(0.0, 1.00) ball_b.pos = Coordinates(0.0, 0.0) result = check_ball_ball_collision(ball_a, ball_b) self.assertTrue(result) # On x-axis, overlapping ball_a.pos = Coordinates(0.0, 0.99) ball_b.pos = Coordinates(0.0, 0.0) result = check_ball_ball_collision(ball_a, ball_b) self.assertTrue(result)
def resolve_ball_ball_collision(a: PoolBall, b: PoolBall): """ Sets new velocity vectors after a ball-ball collision. :param a: ball A :param b: ball B """ # Taken from https://en.wikipedia.org/wiki/Elastic_collision#Two-dimensional_collision_with_two_moving_objects a_vel_new = a.vel - (2 * b.mass) / (a.mass + b.mass) * ( (a.vel - b.vel).dot_product(a.pos - b.pos) ) / get_distance(a.pos - b.pos)**2 * (a.pos - b.pos) b_vel_new = b.vel - (2 * a.mass) / (a.mass + b.mass) * ( (b.vel - a.vel).dot_product(b.pos - a.pos) ) / get_distance(b.pos - a.pos)**2 * (b.pos - a.pos) a.vel, b.vel = a_vel_new, b_vel_new
def test_resolve_ball_ball_collision_2d_one_moving(self): # Initialize ball objects ball_a = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.0, 0.0), mass=1.0, radius=0.5) ball_b = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.0, 0.0), mass=1.0, radius=0.5) # A enters from NW (135 degrees), contacts B on its left (180 degrees) # B is stationary, located at origin # A should exit S (270 degrees) # B should exit E (0 degrees) ball_a.pos, ball_a.vel, ball_a.mass = Coordinates(-1.0, 0.0), Vector(1.0, -1.0), 3.0 ball_b.pos, ball_b.vel, ball_b.mass = Coordinates(0.0, 0.0), Vector(0.0, 0.0), 3.0 resolve_ball_ball_collision(ball_a, ball_b) a_vel_new = Vector(0.0, -1.0) b_vel_new = Vector(1.0, 0.0) self.assertVectorAlmostEqual(ball_a.vel, a_vel_new) self.assertVectorAlmostEqual(ball_b.vel, b_vel_new) # A enters from 122.3 degrees, contacts B on its left (180 degrees) # B is stationary, located at origin # A should exit S (270 degrees), with y-component of initial magnitude # B should exit E (0 degrees), with x-component of initial magnitude mag = 7.14 ang = 32.3 ball_a.pos, ball_a.vel, ball_a.mass = Coordinates(-1.0, 0.0), Vector(mag * np.cos(ang), -mag * np.sin(ang)), 3.0 ball_b.pos, ball_b.vel, ball_b.mass = Coordinates(0.0, 0.0), Vector(0.0, 0.0), 3.0 resolve_ball_ball_collision(ball_a, ball_b) a_vel_new = Vector(0.0, -mag * np.sin(ang)) b_vel_new = Vector(mag * np.cos(ang), 0.0) self.assertVectorAlmostEqual(ball_a.vel, a_vel_new) self.assertVectorAlmostEqual(ball_b.vel, b_vel_new)
def test_check_ball_wall_collision(self): n, e, s, w = 1.0, 1.0, -1.0, -1.0 # Not touching any walls ball = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.0, 0.0), mass=1.0, radius=0.1) result = check_ball_wall_collision(ball, n, e, s, w) self.assertFalse(result) # Touching each wall ball_n = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.0, 0.9), mass=1.0, radius=0.1) result_n = check_ball_wall_collision(ball_n, n, e, s, w) ball_e = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.9, 0.0), mass=1.0, radius=0.1) result_e = check_ball_wall_collision(ball_e, n, e, s, w) ball_s = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.0, -0.9), mass=1.0, radius=0.1) result_s = check_ball_wall_collision(ball_s, n, e, s, w) ball_w = PoolBall(ball_type=BallType.CUE, pos=Coordinates(-0.9, 0.0), mass=1.0, radius=0.1) result_w = check_ball_wall_collision(ball_w, n, e, s, w) self.assertTrue(result_n) self.assertTrue(result_e) self.assertTrue(result_s) self.assertTrue(result_w) # Overlapping each wall ball_n = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.0, 0.91), mass=1.0, radius=0.1) result_n = check_ball_wall_collision(ball_n, n, e, s, w) ball_e = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.91, 0.0), mass=1.0, radius=0.1) result_e = check_ball_wall_collision(ball_e, n, e, s, w) ball_s = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.0, -0.91), mass=1.0, radius=0.1) result_s = check_ball_wall_collision(ball_s, n, e, s, w) ball_w = PoolBall(ball_type=BallType.CUE, pos=Coordinates(-0.91, 0.0), mass=1.0, radius=0.1) result_w = check_ball_wall_collision(ball_w, n, e, s, w) self.assertTrue(result_n) self.assertTrue(result_e) self.assertTrue(result_s) self.assertTrue(result_w) # Touching each corner ball_ne = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.9, 0.9), mass=1.0, radius=0.1) result_ne = check_ball_wall_collision(ball_ne, n, e, s, w) ball_nw = PoolBall(ball_type=BallType.CUE, pos=Coordinates(-0.9, 0.9), mass=1.0, radius=0.1) result_nw = check_ball_wall_collision(ball_nw, n, e, s, w) ball_se = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.9, -0.9), mass=1.0, radius=0.1) result_se = check_ball_wall_collision(ball_se, n, e, s, w) ball_sw = PoolBall(ball_type=BallType.CUE, pos=Coordinates(-0.9, -0.9), mass=1.0, radius=0.1) result_sw = check_ball_wall_collision(ball_sw, n, e, s, w) self.assertTrue(result_ne) self.assertTrue(result_nw) self.assertTrue(result_se) self.assertTrue(result_sw) # Overlapping each corner ball_ne = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.91, 0.91), mass=1.0, radius=0.1) result_ne = check_ball_wall_collision(ball_ne, n, e, s, w) ball_nw = PoolBall(ball_type=BallType.CUE, pos=Coordinates(-0.91, 0.91), mass=1.0, radius=0.1) result_nw = check_ball_wall_collision(ball_nw, n, e, s, w) ball_se = PoolBall(ball_type=BallType.CUE, pos=Coordinates(0.91, -0.91), mass=1.0, radius=0.1) result_se = check_ball_wall_collision(ball_se, n, e, s, w) ball_sw = PoolBall(ball_type=BallType.CUE, pos=Coordinates(-0.91, -0.91), mass=1.0, radius=0.1) result_sw = check_ball_wall_collision(ball_sw, n, e, s, w) self.assertTrue(result_ne) self.assertTrue(result_nw) self.assertTrue(result_se) self.assertTrue(result_sw)