/// <summary> /// Creates a touch of a single method, and a list of calls in the order they should be called. /// </summary> /// <param name="method">The method of the touch.</param> /// <param name="calls">The calls in the order they should be called.</param> public Touch(Method method, Call [] calls) { BasicCall [] basic_calls = new BasicCall [calls.Length]; for (int i = 0; i < calls.Length; i++) { basic_calls [i] = new BasicCall(calls [i], new Touch.CallLocationList()); } this.basic_calls = basic_calls; start_method = method; }
// Functions /// <summary> /// Generates all the changes in the touch (could be computationally intensive). Called once when <see cref="TouchBase.changes"/> is accessed. /// </summary> public override Change[] ComputeChanges() { List <Change> changes = new List <Change> (); int method_call_length = method_calls == null ? 1 : method_calls.Length; int basic_call_length = basic_calls == null ? 1 : basic_calls.Length; int max_lead_length = method_calls == null ? start_method.lead_length : Math.Max(start_method.lead_length, method_calls.Select(x => x.method.lead_length).Max()); List <Change> [] change_states = new List <Change> [method_call_length * basic_call_length * max_lead_length]; for (int i = 0; i < change_states.Length; i++) { change_states [i] = new List <Change> (); } margin_calls = new Dictionary <int, char> (); right_hand_calls = new Dictionary <int, string> (); lead_head_line_indices = new List <int> (); right_hand_calls.Add(-1, "Go " + start_method.title); int lead_index = 0; int sub_lead_index = 0; int call_index = 0; int method_call_index = 0; int sub_splice_change_index = 0; int sub_splice_lead_index = 0; int attempted_calls_since_last_call = 0; int attempted_splices_since_last_splice = 0; int absolute_change_index = 0; CallPoint current_callpoint = null; Change current_change = Change.Rounds(stage); Method current_method = start_method; while (true) { #region Update calls if (current_callpoint == null) { // Not in a call => Could be starting a call if (basic_calls != null && basic_calls.Length > 0) { BasicCall basic_call = basic_calls [call_index]; Call call = basic_call.call; if ((sub_lead_index - call.from + 1) % call.every == 0) { CallLocationParameters call_location_parameters = new CallLocationParameters(current_method, current_change, absolute_change_index, sub_lead_index, lead_index, attempted_calls_since_last_call, attempted_splices_since_last_splice, this); if (basic_call.call_location.EvaluateBasicCall(call, call_location_parameters)) { current_callpoint = new CallPoint(call, absolute_change_index); attempted_calls_since_last_call = 0; if (!call.is_plain) { margin_calls.Add(absolute_change_index, call.preferred_notation); } } else { attempted_calls_since_last_call += 1; } } } } else { // In a call, so could be stopping a call if (absolute_change_index > current_callpoint.end_index) { sub_lead_index += current_callpoint.call.cover; if (sub_lead_index >= current_method.place_notations.Length) { lead_head_line_indices.Add(absolute_change_index - 1); sub_lead_index = 0; lead_index += 1; } call_index += 1; if (call_index >= basic_calls.Length) { call_index = 0; } current_callpoint = null; } } #endregion #region Update change (transposition) PlaceNotation notation; if (current_callpoint == null) { notation = current_method.place_notations [sub_lead_index]; } else { notation = current_callpoint.GetNotationAtIndex(absolute_change_index); if (notation == null) { notation = current_method.place_notations [sub_lead_index + absolute_change_index - current_callpoint.start_index]; } } current_change = current_change.Transpose(notation); // Add change to list changes.Add(current_change); #endregion #region Update method splicing calls if (method_calls != null && method_calls.Length > 0) { MethodCall current_method_call = method_calls [method_call_index]; if (sub_lead_index == current_method.lead_length + current_method_call.splice_start_index - 1) { CallLocationParameters parameters = new CallLocationParameters(current_method, current_change, absolute_change_index, sub_lead_index, lead_index, attempted_calls_since_last_call, attempted_splices_since_last_splice, this); if (current_method_call.location.EvaluateMethodCall(current_method_call, parameters)) { current_method = current_method_call.method; method_call_index += 1; sub_lead_index = current_method_call.splice_end_index - 1; // Set to splice_end_index - 1, because later in this iteration of the while loop, 1 will be added to it, to get splice_end_index. // Make sure that lead ends get counted even if there was a splice over the lead end. if (current_method_call.splice_end_index == 0) { sub_lead_index = current_method.lead_length - 1; } sub_splice_change_index = 0; sub_splice_lead_index = 0; attempted_splices_since_last_splice = 0; right_hand_calls.Add(absolute_change_index, current_method.title); if (method_call_index >= method_calls.Length) { method_call_index = 0; } } else { attempted_splices_since_last_splice += 1; } } } #endregion #region Stop if it comes to rounds (to whatever is the target change is) if (current_change == target_change) { // Add a final lead end. int lead_index_to_check = sub_lead_index + 1; if (current_callpoint != null && absolute_change_index == current_callpoint.end_index) { lead_index_to_check = sub_lead_index + current_callpoint.call.cover; } // Quickly check whether or not this is valid bool is_valid = true; if (rounds_checks == RoundsCheckLocations.AnyBackstroke && Utils.Mod(changes.Count, 2) == 1) { is_valid = false; } if (rounds_checks == RoundsCheckLocations.OnlyLeadEnds && lead_index_to_check != current_method.lead_length) { is_valid = false; } if (is_valid) { if (lead_index_to_check == current_method.lead_length) { lead_head_line_indices.Add(absolute_change_index); } comes_round = true; break; } } #endregion #region Update indices & lead ends. if (current_callpoint == null) { sub_lead_index += 1; if (sub_lead_index >= current_method.place_notations.Length) { lead_head_line_indices.Add(absolute_change_index); sub_lead_index = 0; sub_splice_lead_index += 1; lead_index += 1; } } absolute_change_index += 1; sub_splice_change_index += 1; #endregion #region Stop if touch goes on forever. If 100,000,000 changes are reached, then the code will stop. List <Change> change_state_list = change_states [call_index + basic_call_length * (method_call_index + method_call_length * sub_lead_index)]; if (change_state_list.Contains(current_change)) { comes_round = false; break; } change_state_list.Add(current_change); // It should never get this far if (absolute_change_index > 1e4) { throw new YourPealRingersDiedOfExhaustionException("Broke the laws of human endurance and got to 100,000,000 changes without coming round."); } #endregion } return(changes.ToArray()); }