def find_phase_number_between_ball_and_wheel( time_of_ball_in_front_of_mark, time_of_wheel_in_front_of_mark, wheel_revolution_time, way): diff_time = np.abs(time_of_ball_in_front_of_mark - time_of_wheel_in_front_of_mark) numbers_count = len(Wheel.NUMBERS) idx_phase = int( np.round((diff_time / wheel_revolution_time * numbers_count))) idx_zero = Wheel.find_index_of_number(0) # Should be always 0 if time_of_ball_in_front_of_mark > time_of_wheel_in_front_of_mark: # t(Wheel) < t(Ball) Wheel is ahead of phase # The question is: what is the value of the wheel when the ball # is in front of the mark? if way == Wheel.WheelWay.CLOCKWISE: idx = idx_zero - idx_phase elif way == Wheel.WheelWay.ANTICLOCKWISE: idx = idx_zero + idx_phase else: raise Exception('Unknown type.') else: # t(Wheel) > t(Ball) Ball is ahead of phase # The question is what is the value of the ball when the wheel # is in front of the wheel? if way == Wheel.WheelWay.CLOCKWISE: idx = idx_zero + idx_phase elif way == Wheel.WheelWay.ANTICLOCKWISE: idx = idx_zero - idx_phase else: raise Exception('Unknown type.') return Wheel.NUMBERS[Wheel.get_index(idx)]
def next_batch(debug=True): circ = 0.85 * np.pi cutoff_speed = 1.0 rev_time = 3 # units of time. init_max_time_value_first_ball_time = 1.0 # BALL PART abs_ball_times, abs_cutoff_time = generate_abs_times( circ, cutoff_speed, init_max_time_value_first_ball_time) Wheel.find_index_of_number(0) abs_ball_times -= abs_ball_times[0] # WHEEL PART initial_number = np.random.choice(Wheel.NUMBERS) # at time = 0 abs_wheel_times = generate_wheel_abs_times(rev_time, initial_number, max_len=len(abs_ball_times)) numbers = [] for abs_ball_time in np.append(abs_ball_times, abs_cutoff_time): abs_wheel_time = Helper.get_last_time_wheel_is_in_front_of_ref( abs_wheel_times, abs_ball_time) if abs_wheel_time is None: numbers.append(None) else: num = get_real_number(abs_ball_time, abs_wheel_time, rev_time) assert num == get_number(abs_ball_time - abs_wheel_time, 0, rev_time) numbers.append(num) number_landmark_number_cutoff = numbers[-1] ball_distance_travelled = distance_travelled(abs_ball_times[-1], abs_cutoff_time, neg_exp) ball_cutoff_shift = (ball_distance_travelled % circ) / circ * len( Wheel.NUMBERS) number_cutoff = Wheel.get_number_with_shift(number_landmark_number_cutoff, ball_cutoff_shift, Wheel.WheelWay.CLOCKWISE) numbers = np.array(numbers)[:-1] # ball_lap_times = np.diff(abs_ball_times) # wheel_lap_times = np.diff(abs_wheel_times) # cutoff_lap_time = abs_cutoff_time - abs_ball_times[-1] # number_cutoff = get_number(abs_cutoff_time, initial_number, rev_time) if debug: print('-' * 80) print('ABSOLUTE BALL TIMES =', abs_ball_times) print('ABSOLUTE WHEEL TIMES =', abs_wheel_times) print('ABS CUTOFF TIME =', abs_cutoff_time) print('NUMBERS =', numbers) print('NUMBER CUTOFF =', number_cutoff) # plot_a(ball_diff_times) # sleep(0.1) return { 'abs_ball_times': abs_ball_times, 'abs_wheel_times': abs_wheel_times, 'abs_cutoff_time': abs_cutoff_time, 'numbers': np.array(numbers), 'number_cutoff': number_cutoff }
def next_batch(debug=True): circ = 0.85 * np.pi rev_time = 3 # units of time. init_max_time_value_first_ball_time = 2.0 # 1 second max after t = 0. # BALL PART abs_ball_times, abs_cutoff_time = generate_abs_times( circ, init_max_time_value_first_ball_time) Wheel.find_index_of_number(0) abs_ball_times -= abs_ball_times[0] # WHEEL PART initial_number = np.random.choice(Wheel.NUMBERS) # at time = 0 abs_wheel_times = generate_wheel_abs_times(rev_time, initial_number, max_len=len(abs_ball_times)) numbers = [] for abs_ball_time in np.append(abs_ball_times, abs_cutoff_time): abs_wheel_time = Helper.get_last_time_wheel_is_in_front_of_ref( abs_wheel_times, abs_ball_time) if abs_wheel_time is None: numbers.append(None) else: numbers.append( get_real_number(abs_ball_time, abs_wheel_time, rev_time)) number_cutoff = numbers[-1] numbers = np.array(numbers) # ball_lap_times = np.diff(abs_ball_times) # wheel_lap_times = np.diff(abs_wheel_times) # cutoff_lap_time = abs_cutoff_time - abs_ball_times[-1] # number_cutoff = get_number(abs_cutoff_time, initial_number, rev_time) if debug: print('-' * 80) print('ABSOLUTE BALL TIMES =', abs_ball_times) # print('BALL LAP TIMES =', ball_lap_times) print('ABSOLUTE WHEEL TIMES =', abs_wheel_times) # print('BALL WHEEL TIMES =', wheel_lap_times) # print('CUTOFF DIFF TIME =', cutoff_lap_time) print('ABS CUTOFF TIME =', abs_cutoff_time) print('NUMBERS =', numbers) print('NUMBER CUTOFF =', number_cutoff) # plot_a(ball_diff_times) # sleep(0.1) return { 'abs_ball_times': abs_ball_times, # 'ball_lap_times': ball_lap_times, 'abs_wheel_times': abs_wheel_times, # 'wheel_lap_times': wheel_lap_times, # 'cutoff_lap_time': cutoff_lap_time, 'abs_cutoff_time': abs_cutoff_time, 'numbers': np.array(numbers), 'number_cutoff': number_cutoff }
def generate_wheel_abs_times(rev_time, initial_number, max_len): # initial number already reflects the position of the wheel at the t=0 for the ball. cur_time = 0 abs_times = [] shift = -1 for ii in range(len(Wheel.NUMBERS)): number = Wheel.get_number_with_shift(initial_number, ii, Wheel.WheelWay.CLOCKWISE) if number == 0: shift = ii break assert shift != -1 cur_time += (shift / len(Wheel.NUMBERS)) * rev_time abs_times.append(cur_time) for jj in range(max_len): cur_time += rev_time abs_times.append(cur_time) return np.array(abs_times)
def get_number(t, initial_number, rev_time): res_t = t % rev_time shift_to_add = res_t * len(Wheel.NUMBERS) return Wheel.get_number_with_shift(initial_number, shift_to_add, Wheel.WheelWay.CLOCKWISE)
def predict(ball_recorded_times, wheel_recorded_times, debug=True): # Last measurement is when the ball hits the diamond ring. ts_list = np.array(PredictorPhysics.LAP_TIMES_ALL_GAMES_LIST.copy()) dr_list = np.array(PredictorPhysics.DIAMOND_RING_ALL_GAMES_LIST.copy()) if ts_list is None or dr_list is None: raise CriticalException( 'Cache is not initialized. Call load_cache().') ts_list = TimeSeriesMerger.merge(ts_list) ts_mean = np.nanmean(ts_list, axis=0) last_time_ball_passes_in_front_of_ref = ball_recorded_times[-1] last_wheel_lap_time_in_front_of_ref = Helper.get_last_time_wheel_is_in_front_of_ref( wheel_recorded_times, last_time_ball_passes_in_front_of_ref) log( 'ref time of the prediction = {0:.2f}s'.format( last_time_ball_passes_in_front_of_ref), debug) ball_lap_times = np.diff(ball_recorded_times) wheel_lap_times = np.diff(wheel_recorded_times) ball_loop_count = len(ball_lap_times) index_of_rev_start = Helper.find_abs_start_index( ball_lap_times, ts_mean) log('index_of_rev_start = {}'.format(index_of_rev_start), debug) index_of_last_recorded_time = ball_loop_count + index_of_rev_start matched_game_indices = TimeSeriesMerger.find_nearest_neighbors( ball_lap_times, ts_list, index_of_rev_start, neighbors_count=Constants.NEAREST_NEIGHBORS_COUNT) log('matched_game_indices = {}'.format(matched_game_indices), debug) estimated_time_left = np.mean( np.sum(ts_list[matched_game_indices, index_of_last_recorded_time:], axis=1)) estimated_time_left += np.mean(dr_list[matched_game_indices], axis=0) log('estimated_time_left = {0:.2f}s'.format(estimated_time_left), debug) if estimated_time_left <= 0: raise PositiveValueExpectedException( 'estimated_time_left must be positive.') # biased estimator but still it should work here. max_residual_time = np.max(dr_list) # if we have [0, 0, 1, 2, 3, 0, 0], index_of_rev_start = 2 # rem_loops is actually useless to compute :) rem_loops = ts_list[matched_game_indices, index_of_last_recorded_time:].shape[1] rem_res_loop = np.mean( dr_list[matched_game_indices] / max_residual_time) # we should calibrate it more. number_of_revolutions_left_ball = rem_loops + rem_res_loop # TODO: for later. # we need to have a better approximation of: # We can maybe fit an ARIMA on the Abs Ball times # or get rem_res_loop accurate. if number_of_revolutions_left_ball <= 0: error_msg = 'rem_loops = {0:.2f}, rem_res_loop = {0:.2f}.'.format( rem_loops, rem_res_loop) raise PositiveValueExpectedException( 'number_of_revolutions_left_ball must be positive.' + error_msg) log( 'number_of_revolutions_left_ball = {0:.2f}'.format( number_of_revolutions_left_ball), debug) # the time values are always taken at the same diamond. diamond = Diamonds.detect_diamonds(number_of_revolutions_left_ball) log('diamond to be hit = {}'.format(diamond), debug) if diamond == Diamonds.DiamondType.BLOCKER: expected_bouncing_shift = Constants.EXPECTED_BOUNCING_SHIFT_BLOCKER_DIAMOND else: expected_bouncing_shift = Constants.EXPECTED_BOUNCING_SHIFT_FORWARD_DIAMOND shift_ball_cutoff = (number_of_revolutions_left_ball % 1) * len( Wheel.NUMBERS) time_at_cutoff_ball = last_time_ball_passes_in_front_of_ref + estimated_time_left if time_at_cutoff_ball < last_time_ball_passes_in_front_of_ref + Constants.SECONDS_NEEDED_TO_PLACE_BETS: raise PositiveValueExpectedException() wheel_last_revolution_time = wheel_lap_times[-1] # We want to find the number of the wheel where the ball passes in front of the mark. initial_number = Phase.find_phase_number_between_ball_and_wheel( last_time_ball_passes_in_front_of_ref, last_wheel_lap_time_in_front_of_ref, wheel_last_revolution_time, Constants.DEFAULT_WHEEL_WAY) shift_between_initial_time_and_cutoff = ( (estimated_time_left / wheel_last_revolution_time) % 1) * len( Wheel.NUMBERS) # Explanation: # shift_between_initial_time_and_cutoff - let's not focus on the ball. What is the configuration of the wheel # at the cutoff time, i.e. estimated_time_left seconds later. # shift_ball_cutoff - the ball is also moving during this time. Let's not focus on the wheel. Where is the ball # compared to the wheel at the cutoff time. We imagine that the wheel is fixed. # We then add the two quantities (composition of the kinetics) to know about the real shift. shift_to_add = shift_ball_cutoff + shift_between_initial_time_and_cutoff predicted_number_cutoff = Wheel.get_number_with_shift( initial_number, shift_to_add, Constants.DEFAULT_WHEEL_WAY) log( 'shift_between_initial_time_and_cutoff = {0:.2f}'.format( shift_between_initial_time_and_cutoff), debug) log('shift_ball_cutoff = {0:.2f}'.format(shift_ball_cutoff), debug) log('predicted_number_cutoff = {}'.format(predicted_number_cutoff), debug) log('expected_bouncing_shift = {}'.format(expected_bouncing_shift), debug) shift_to_add += expected_bouncing_shift predicted_number = Wheel.get_number_with_shift( initial_number, shift_to_add, Constants.DEFAULT_WHEEL_WAY) log('predicted_number is = {}'.format(predicted_number), debug) return predicted_number_cutoff, predicted_number