public override ActionEnum Action(List <Element> elements, float angleToCenter, bool inZone, ref float xdelta, ref float ydelta, ref float zdelta, ref float angle) { // calculate the direction towards 'Lunch' angle = Collision.CalculateAngleFromPoint(X, Y, Lunch.X, Lunch.Y); // move towards 'Lunch' float x1, y1, x2, y2; Collision.CalculateLineByAngle(X, Y, angle, Speed, out x1, out y1, out x2, out y2); xdelta = x2 - x1; ydelta = y2 - y1; // determine if we should show our brainz sign if (ShowBrainz) { if (BrainzDuration.ElapsedMilliseconds > MaxBrainzDuration) { ShowBrainz = false; } } else if (Rand.Next() % 20 == 0) { ShowBrainz = true; BrainzDuration.Stop(); BrainzDuration.Reset(); BrainzDuration.Start(); } // always melee return(ActionEnum.Attack); }
public static Dictionary <Type, ElementProximity> ComputeProximity(Player self, List <Element> elements) { var closest = new Dictionary <Type, ElementProximity>(); foreach (var elem in elements) { if (elem.Id == self.Id) { continue; } ElementProximity proximity = null; Type type = elem.GetType(); // consolidate player types if (elem is Player) { type = typeof(Player); } // consolidate obstacle types if (elem is Obstacle) { type = typeof(Obstacle); } if (!closest.TryGetValue(type, out proximity)) { proximity = new ElementProximity() { Name = type.Name, Id = elem.Id, Distance = float.MaxValue }; closest.Add(type, proximity); } // calculate the distance between thees var distance = Collision.DistanceBetweenPoints(self.X, self.Y, elem.X, elem.Y); var angle = Collision.CalculateAngleFromPoint(self.X, self.Y, elem.X, elem.Y); // retain only the closest if (distance < proximity.Distance) { proximity.Id = elem.Id; proximity.Angle = angle; proximity.Distance = distance; } } return(closest); }
public override ActionEnum Action(List <Element> elements, float angleToCenter, bool inZone, ref float xdelta, ref float ydelta, ref float zdelta, ref float angle) { // never move xdelta = ydelta = 0; AI target = null; float min = Int32.MaxValue; // find the closet AI foreach (var elem in elements) { if (elem.Id == Id) { continue; } if (elem is AI && !(elem is MooTurret)) { var ai = elem as AI; var dist = Collision.DistanceBetweenPoints(X, Y, ai.X, ai.Y); if (dist < min) { min = dist; target = ai; } } } // get an angle to that AI if (target != null) { angle = Collision.CalculateAngleFromPoint(X, Y, target.X, target.Y); // and fire return(ActionEnum.Attack); } return(ActionEnum.None); }
public override ActionEnum Action(List <Element> elements, float angleToCenter, bool inZone, ref float xdelta, ref float ydelta, ref float zdelta, ref float angle) { var playerCount = 0; float playerX = 0; float playerY = 0; bool shouldMelee = false; // find proximity to all types var closest = AITraining.ComputeProximity(this, elements); // gather details about if there is a crowd foreach (var elem in elements) { if (elem.Id == Id) { continue; // found myself } if (!(elem is Player)) { continue; // only care about players } playerCount++; playerX += elem.X; playerY += elem.Y; } // calculate the average center (if there are players near by) if (playerCount >= 1) { playerX /= (float)playerCount; playerY /= (float)playerCount; } // choose an action - set the rules in reverse order in order to set precedence var action = ActionEnum.None; xdelta = ydelta = angle = 0; if (PreviousAngle < 0) { // choose an angle at random PreviousAngle = Rand.Next() % 360; } angle = PreviousAngle; // 3) Shield if (Shield < Constants.MaxShield) { ElementProximity helmet = null; if (closest.TryGetValue(typeof(Shield), out helmet)) { // there is health either close or touching if (IsTouching(helmet, Width / 2)) { // choose to pickup action = ActionEnum.Pickup; // set direction via another decision PreviousPickupId = helmet.Id; } else { // choose action via another decision angle = helmet.Angle; } } } // 2) Health if (Health < Constants.MaxHealth) { ElementProximity bandage = null; if (closest.TryGetValue(typeof(Health), out bandage)) { // there is health either close or touching if (IsTouching(bandage, Width / 2)) { // choose to pickup action = ActionEnum.Pickup; // set direction via another decision PreviousPickupId = bandage.Id; } else { // choose action via another decision angle = bandage.Angle; } } } // 1) Have weapon if (Primary != null && Primary is RangeWeapon) { var weapon = Primary as RangeWeapon; ElementProximity ammo = null; // need ammo if (weapon.Ammo < MinAmmo && closest.TryGetValue(typeof(Ammo), out ammo)) { // there is ammo either close or touching if (IsTouching(ammo, Width / 2)) { // choose to pickup action = ActionEnum.Pickup; // set direction via another decision PreviousPickupId = ammo.Id; } else { // choose action via another decision angle = ammo.Angle; } } // needs reload if (!weapon.RoundsInClip(out int rounds) && rounds == 0 && weapon.HasAmmo()) { // choose to reload action = ActionEnum.Reload; // choose direction via another decision } ElementProximity ak47 = null; // pick up ak47 if (!(Primary is AK47) && closest.TryGetValue(typeof(AK47), out ak47)) { // there is an AK47 either close or touching if (IsTouching(ak47, Width / 2)) { // choose to pickup action = ActionEnum.Pickup; // set direction via another decision PreviousPickupId = ak47.Id; } else { // choose action via another decision angle = ak47.Angle; } } ElementProximity player = null; // shoot a player if (weapon.CanShoot() && closest.TryGetValue(typeof(Player), out player)) { // choose to shoot action = ActionEnum.Attack; // move towards the player angle = player.Angle; } } // 0) No weapon if (Primary == null || !(Primary is RangeWeapon)) { // AI does not have a weapon, so go ahead and melee (if no other action) shouldMelee = true; // 0.b if near a player, melee ElementProximity player = null; if (closest.TryGetValue(typeof(Player), out player)) { if (IsTouching(player, Fists.Distance)) { // choose to melee action = ActionEnum.Attack; // turn towards the player angle = player.Angle; } } // 0.a is there a weapon within view ElementProximity weapon = null; if (!closest.TryGetValue(typeof(AK47), out weapon)) { if (!closest.TryGetValue(typeof(Pistol), out weapon)) { if (!closest.TryGetValue(typeof(Shotgun), out weapon)) { } } } if (weapon != null) { // there is a weapon either close or touching if (IsTouching(weapon, Width / 2)) { // choose to pickup action = ActionEnum.Pickup; // set direction via another decision PreviousPickupId = weapon.Id; } else { // choose action via another decision angle = weapon.Angle; } } } // if there are too many players, then run away if (playerCount >= 5) { // choose an angle opposite from where the center of the other players are angle = Collision.CalculateAngleFromPoint(X, Y, playerX, playerY); // go the opposite way angle = (angle + 180) % 360; } // choose defaults if (action == ActionEnum.None) { // default to move action = ActionEnum.Move; } // check if we are in the Zone if (inZone) { // we should be moving towards the center if (action == ActionEnum.Move) { // eek we are in the zone, indicate that we should be moving towards the center angle = angleToCenter; } } // check if we seem to be stuck if (IsStuck()) { // take some corrective action if (ShowDiagnostics) { System.Diagnostics.Debug.WriteLine("AI seems stuck"); } // try something new angle = Rand.Next() % 360; CorrectiveAngle = 0; } // check if our last movement was obstructed float moveAngle = (angle + CorrectiveAngle) % 360; if (CorrectiveAngle > 0) { CorrectiveAngle -= 15; } if (CorrectiveAngle < 0) { CorrectiveAngle = 0; } // save angle for next time PreviousAngle = moveAngle; // set course float x1, y1, x2, y2; Collision.CalculateLineByAngle(X, Y, moveAngle, 1, out x1, out y1, out x2, out y2); xdelta = x2 - x1; ydelta = y2 - y1; // normalize var sum = (float)(Math.Abs(xdelta) + Math.Abs(ydelta)); xdelta = xdelta / sum; ydelta = ydelta / sum; if (Math.Abs(xdelta) + Math.Abs(ydelta) > 1.0001) { throw new Exception("Invalid xdelta,ydelta : " + xdelta + "," + ydelta); } if (ShowDiagnostics) { System.Diagnostics.Debug.WriteLine("AI {0} {1} {2} {3}", action, angle, xdelta, ydelta); } // if our action is to move... do a melee while moving if (action == ActionEnum.Move && shouldMelee) { return(ActionEnum.Attack); } else { return(action); } }
public static ModelDataSet AsModelDataSet(this TrainingData data) { // transform to ModelDataSet and Normalize (0...1) var result = new ModelDataSet() { // core data CenterAngle = Normalize(data.CenterAngle, 360f), InZone = data.InZone ? 1f : 0, Health = Normalize(data.Health, (float)Constants.MaxHealth), Shield = Normalize(data.Shield, (float)Constants.MaxShield), Primary = Normalize(data.Primary), PrimaryAmmo = data.PrimaryAmmo >= Constants.MaxAmmo ? 1 : Normalize((float)data.PrimaryAmmo, (float)Constants.MaxAmmo), PrimaryClip = Normalize(data.Primary, data.PrimaryClip), Secondary = Normalize(data.Secondary), SecondaryAmmo = data.SecondaryAmmo >= Constants.MaxAmmo ? 1 : Normalize((float)data.SecondaryAmmo, (float)Constants.MaxAmmo), SecondaryClip = Normalize(data.Secondary, data.SecondaryClip), }; // outcome (not normalize) result.Action = (float)data.Action; result.FaceAngle = data.Angle; result.MoveAngle = Collision.CalculateAngleFromPoint(0, 0, data.Xdelta, data.Ydelta); // proximity foreach (var elem in data.Proximity) { switch (elem.Name) { case "Ammo": result.Angle_1 = Normalize(elem.Angle, 360f); result.Distance_1 = Normalize(elem.Distance, (float)Constants.ProximityViewWidth); break; case "Bandage": result.Angle_2 = Normalize(elem.Angle, 360f); result.Distance_2 = Normalize(elem.Distance, (float)Constants.ProximityViewWidth); break; case "Helmet": result.Angle_3 = Normalize(elem.Angle, 360f); result.Distance_3 = Normalize(elem.Distance, (float)Constants.ProximityViewWidth); break; case "AK47": result.Angle_4 = Normalize(elem.Angle, 360f); result.Distance_4 = Normalize(elem.Distance, (float)Constants.ProximityViewWidth); break; case "Shotgun": result.Angle_5 = Normalize(elem.Angle, 360f); result.Distance_5 = Normalize(elem.Distance, (float)Constants.ProximityViewWidth); break; case "Pistol": result.Angle_6 = Normalize(elem.Angle, 360f); result.Distance_6 = Normalize(elem.Distance, (float)Constants.ProximityViewWidth); break; case "Obstacle": result.Angle_7 = Normalize(elem.Angle, 360f); result.Distance_7 = Normalize(elem.Distance, (float)Constants.ProximityViewWidth); break; case "Player": result.Angle_8 = Normalize(elem.Angle, 360f); result.Distance_8 = Normalize(elem.Distance, (float)Constants.ProximityViewWidth); break; default: throw new Exception("Unknown proximity element type : " + elem.Name); } } return(result); }
public static int Main(string[] args) { for (int i = 0; i < args.Length; i++) { if (string.Equals(args[i], "train", StringComparison.OrdinalIgnoreCase)) { // train the model var type = i + 1 < args.Length ? args[i + 1] : ""; var directory = i + 2 < args.Length ? args[i + 2] : ""; return(ModelBuilding.TrainAndEvaulate(type, directory)); } else if (string.Equals(args[i], "check", StringComparison.OrdinalIgnoreCase)) { // purge the input var type = i + 1 < args.Length ? args[i + 1] : ""; var directory = i + 2 < args.Length ? args[i + 2] : ""; return(ModelBuilding.Check(type, directory)); } else if (string.Equals(args[i], "purge", StringComparison.OrdinalIgnoreCase)) { // purge the input var directory = i + 1 < args.Length ? args[i + 1] : ""; var deleted = Purge.Execute(directory); if (deleted >= 0) { return(0); } } else if (string.Equals(args[i], "run", StringComparison.OrdinalIgnoreCase)) { // do a trial run var type = i + 1 < args.Length ? args[i + 1] : ""; return(Executor.Run(type)); } else if (string.Equals(args[i], "test", StringComparison.OrdinalIgnoreCase)) { float x1, y1; float xdelta, ydelta; foreach (var angle in new float[] { 0, 45, 90, 135, 180, 225, 270, 315, 359 }) { Collision.CalculateLineByAngle(0, 0, angle, 1, out x1, out y1, out xdelta, out ydelta); var sum = (float)(Math.Abs(xdelta) + Math.Abs(ydelta)); xdelta = xdelta / sum; ydelta = ydelta / sum; Console.WriteLine("0: {0} {1},{2}", angle, xdelta, ydelta); } foreach (var pair in new float[][] { new float[] { 1, 0 }, new float[] { 0, 1 }, new float[] { -1, 0 }, new float[] { 0, -1 }, new float[] { 0.5f, 0.5f }, new float[] { -0.5f, 0.5f }, new float[] { 0.5f, -0.5f }, new float[] { -0.5f, -0.5f } } ) { var angle = Collision.CalculateAngleFromPoint(0, 0, pair[0], pair[1]); Console.WriteLine("1: {0} {1},{2}", angle, pair[0], pair[1]); } return(0); } else if (string.Equals(args[i], "serialize", StringComparison.OrdinalIgnoreCase)) { var count = Convert.ToInt32(i + 1 < args.Length ? args[i + 1] : "0"); var type = i + 2 < args.Length ? args[i + 2] : ""; var directory = i + 3 < args.Length ? args[i + 3] : ""; return(ModelBuilding.Serialize(directory, type, count)); } } // the verb was not understood return(Usage()); }
private void AIMove(object state) { // block usage if a menu is being displayed if (Map.IsPaused) { return; } // move the AI int index = (int)state; Stopwatch timer = new Stopwatch(); timer.Start(); if (Players[index] is AI) { AI ai = Players[index] as AI; float xdelta = 0; float ydelta = 0; float angle = 0; // the timer is reentrant, so only allow one instance to run if (System.Threading.Interlocked.CompareExchange(ref ai.RunningState, 1, 0) != 0) { return; } if (ai.IsDead) { // drop the current players goodies Map.Drop(ai); ai.SwitchWeapon(); Map.Drop(ai); // stop the timer AITimers[index].Dispose(); return; } // NOTE: Do not apply the ZoomFactor (as it distorts the AI when debugging) - TODO may want to allow this while parachuting // TODO will likely want to translate into a copy of the list with reduced details List <Element> elements = Map.WithinWindow(ai.X, ai.Y, Constants.ProximityViewWidth, Constants.ProximityViewHeight).ToList(); var angleToCenter = Collision.CalculateAngleFromPoint(ai.X, ai.Y, Background.X, Background.Y); var inZone = Background.Damage(ai.X, ai.Y) > 0; if (Constants.CaptureAITrainingData) { // capture what the ai sees AITraining.CaptureBefore(ai, elements, angleToCenter, inZone); } // get action from AI var action = ai.Action(elements, angleToCenter, inZone, ref xdelta, ref ydelta, ref angle); // turn ai.Angle = angle; // perform action bool result = false; Type item = null; switch (action) { case ActionEnum.Drop: item = Map.Drop(ai); result |= (item != null); ai.Feedback(action, item, result); break; case ActionEnum.Pickup: item = Map.Pickup(ai); result |= (item != null); ai.Feedback(action, item, result); break; case ActionEnum.Reload: var reloaded = ai.Reload(); result |= (reloaded == AttackStateEnum.Reloaded); ai.Feedback(action, reloaded, result); break; case ActionEnum.Attack: var attack = Map.Attack(ai); result |= attack == AttackStateEnum.FiredAndKilled || attack == AttackStateEnum.FiredWithContact || attack == AttackStateEnum.MeleeAndKilled || attack == AttackStateEnum.MeleeWithContact; ai.Feedback(action, attack, result); break; case ActionEnum.SwitchWeapon: var swap = ai.SwitchWeapon(); result |= swap; ai.Feedback(action, null, result); break; case ActionEnum.Move: case ActionEnum.None: break; default: throw new Exception("Unknown ai action : " + action); } // have the AI move float oxdelta = xdelta; float oydelta = ydelta; var moved = Map.Move(ai, ref xdelta, ref ydelta); ai.Feedback(ActionEnum.Move, null, moved); // ensure the player stays within the map if (ai.X < 0 || ai.X > Map.Width || ai.Y < 0 || ai.Y > Map.Height) { System.Diagnostics.Debug.WriteLine("Out of bounds"); } if (ai.RecordTraining) { // capture what the ai sees AITraining.CaptureAfter(ai, action, oxdelta, oydelta, angle, action == ActionEnum.None || action == ActionEnum.Move ? moved : result); } // set state back to not running System.Threading.Volatile.Write(ref ai.RunningState, 0); } timer.Stop(); if (timer.ElapsedMilliseconds > 100) { System.Diagnostics.Debug.WriteLine("**AIMove Duration {0} ms", timer.ElapsedMilliseconds); } }
public void KeyPress(char key) { // inputs that are accepted while a menu is displaying if (Map.IsPaused) { switch (key) { // menu case Constants.Esc: HideMenu(); break; } return; } // menu if (key == Constants.Esc) { ShowMenu(); return; } // for training we track the human movements (as the supervised set) if (Human.RecordTraining) { // capture what the user sees List <Element> elements = Map.WithinWindow(Human.X, Human.Y, Constants.ProximityViewWidth, Constants.ProximityViewHeight).ToList(); var angleToCenter = Collision.CalculateAngleFromPoint(Human.X, Human.Y, Background.X, Background.Y); var inZone = Background.Damage(Human.X, Human.Y) > 0; AITraining.CaptureBefore(Human, elements, angleToCenter, inZone); } // handle the user input ActionEnum action = ActionEnum.None; bool result = false; float xdelta = 0; float ydelta = 0; switch (key) { // move case Constants.Down: case Constants.Down2: case Constants.DownArrow: ydelta = 1; break; case Constants.Left: case Constants.Left2: case Constants.LeftArrow: xdelta = -1; break; case Constants.Right: case Constants.Right2: case Constants.RightArrow: xdelta = 1; break; case Constants.Up: case Constants.Up2: case Constants.UpArrow: ydelta = -1; break; case Constants.Switch: action = ActionEnum.SwitchWeapon; result = SwitchWeapon(Human); break; case Constants.Pickup: case Constants.Pickup2: action = ActionEnum.Pickup; result = Pickup(Human); break; case Constants.Drop3: case Constants.Drop2: case Constants.Drop4: case Constants.Drop: action = ActionEnum.Drop; result = Drop(Human); break; case Constants.Reload: case Constants.MiddleMouse: action = ActionEnum.Reload; result = Reload(Human); break; case Constants.Space: case Constants.LeftMouse: action = ActionEnum.Attack; result = Attack(Human); break; case Constants.RightMouse: // use the mouse to move in the direction of the angle float r = (Human.Angle % 90) / 90f; xdelta = 1 * r; ydelta = 1 * (1 - r); if (Human.Angle > 0 && Human.Angle < 90) { ydelta *= -1; } else if (Human.Angle > 180 && Human.Angle <= 270) { xdelta *= -1; } else if (Human.Angle > 270) { ydelta *= -1; xdelta *= -1; } break; } // if a move command, then move if (xdelta != 0 || ydelta != 0) { action = ActionEnum.Move; result = Move(Human, xdelta, ydelta); } // for training we track the human movements (as the supervised set) if (Human.RecordTraining) { // capture the result AITraining.CaptureAfter(Human, action, xdelta, ydelta, Human.Angle, result); } }
public void Paint() { // draw the map Background.Draw(Surface); // add center indicator if (Human.Z == Constants.Ground) { var centerAngle = Collision.CalculateAngleFromPoint(Human.X, Human.Y, Background.X, Background.Y); float x1, y1, x2, y2; var distance = Math.Min(Surface.Width, Surface.Height) * 0.9f; Collision.CalculateLineByAngle(Surface.Width / 2, Surface.Height / 2, centerAngle, (distance / 2), out x1, out y1, out x2, out y2); Surface.DisableTranslation(); { // draw an arrow var endX = x2; var endY = y2; x1 = endX; y1 = endY; Collision.CalculateLineByAngle(x1, y1, (centerAngle + 180) % 360, 50, out x1, out y1, out x2, out y2); Surface.Line(RGBA.Black, x1, y1, x2, y2, 10); x1 = endX; y1 = endY; Collision.CalculateLineByAngle(x1, y1, (centerAngle + 135) % 360, 25, out x1, out y1, out x2, out y2); Surface.Line(RGBA.Black, x1, y1, x2, y2, 10); x1 = endX; y1 = endY; Collision.CalculateLineByAngle(x1, y1, (centerAngle + 225) % 360, 25, out x1, out y1, out x2, out y2); Surface.Line(RGBA.Black, x1, y1, x2, y2, 10); } Surface.EnableTranslation(); } // draw all elements var hidden = new HashSet <int>(); var visiblePlayers = new List <Player>(); foreach (var elem in Map.WithinWindow(Human.X, Human.Y, Surface.Width * (1 / ZoomFactor), Surface.Height * (1 / ZoomFactor))) { if (elem.IsDead) { continue; } if (elem is Player) { visiblePlayers.Add(elem as Player); continue; } if (elem.IsTransparent) { // if the player is intersecting with this item, then do not display it if (Map.IsTouching(Human, elem)) { continue; } // check if one of the bots is hidden by this object for (int i = 0; i < Players.Length; i++) { if (Players[i].Id == Human.Id) { continue; } if (Map.IsTouching(Players[i], elem)) { hidden.Add(Players[i].Id); } } } elem.Draw(Surface); } // draw the players foreach (var player in visiblePlayers) { if (hidden.Contains(player.Id)) { continue; } player.Draw(Surface); } // add any ephemerial elements lock (Ephemerial) { var toremove = new List <EphemerialElement>(); var messageShown = false; foreach (var b in Ephemerial) { if (b is Message) { // only show one message at a time if (messageShown) { continue; } messageShown = true; } b.Draw(Surface); b.Duration--; if (b.Duration < 0) { toremove.Add(b); } } foreach (var b in toremove) { Ephemerial.Remove(b); } } // display the player counts Surface.DisableTranslation(); { Surface.Text(RGBA.Black, Surface.Width - 200, 10, string.Format("Alive {0} of {1}", Alive, Players.Length)); Surface.Text(RGBA.Black, Surface.Width - 200, 30, string.Format("Kills {0}", Human.Kills)); } Surface.EnableTranslation(); // show a menu if present if (Map.IsPaused) { if (Menu == null) { throw new Exception("Must initalize a menu to display"); } Menu.Draw(Surface); } }
public static int Check(string type, string path) { // load each model and then give a few predictions and check the results var rand = new Random(); var data = new List <TrainingData>(); var done = false; foreach (var kvp in AITraining.GetTrainingFiles(path)) { if (done) { break; } var file = kvp.Key; var count = kvp.Value; if (count > 0) { foreach (var d in AITraining.GetTraingingData(file)) { if (done) { break; } if (d.Result && d.Z == 0) { // sample ~15% if (rand.Next() % 6 == 0) { // test data set data.Add(d); } if (data.Count > 1000) { done = true; } } } } } foreach (var modelType in new ModelValue[] { ModelValue.Action, ModelValue.XY, ModelValue.Angle } ) { var map = new Dictionary <string, int>(); var modelPath = ""; if (modelType == ModelValue.Action) { modelPath = string.Format("action.{0}.model", type); } else if (modelType == ModelValue.XY) { modelPath = string.Format("xy.{0}.model", type); } else if (modelType == ModelValue.Angle) { modelPath = string.Format("angle.{0}.model", type); } else { throw new Exception("Unknown model type : " + modelType); } Model model = null; if (type.Equals("ml", StringComparison.OrdinalIgnoreCase)) { model = new ModelMLNet(Path.Combine(path, modelPath)); } else { model = new ModelOpenCV(Path.Combine(path, modelPath)); } var delta = 0d; var count = 0; var timer = new Stopwatch(); timer.Start(); foreach (var d in data) { var key = ""; if (false && modelType == ModelValue.XY) { float xdelta, ydelta; model.Predict(d.AsModelDataSet(), out xdelta, out ydelta); delta += Math.Abs(xdelta - d.Xdelta); delta += Math.Abs(ydelta - d.Ydelta); count += 2; key = string.Format("{0},{1} {2},{3}", Math.Round(d.Xdelta, 1), Math.Round(d.Ydelta, 1), Math.Round(xdelta, 1), Math.Round(ydelta, 1)); } else { var value = model.Predict(d.AsModelDataSet()); if (modelType == ModelValue.Action) { delta += Math.Abs(value - (float)d.Action); key = string.Format("{0},{1}", d.Action, Math.Round(value)); } else if (modelType == ModelValue.Angle) { delta += Math.Abs(value - d.Angle); key = string.Format("{0},{1}", Math.Round(d.Angle), Math.Round(value)); } else if (modelType == ModelValue.XY) { var moveAngle = Collision.CalculateAngleFromPoint(0, 0, d.Xdelta, d.Ydelta); delta += Math.Abs(value - moveAngle); key = string.Format("{0},{1}", Math.Round(moveAngle), Math.Round(value)); } } if (!map.ContainsKey(key)) { map.Add(key, 0); } map[key]++; } timer.Stop(); Console.WriteLine("{0} has an average delta of {1:f2}. This ran in {2}ms or {3:f2}ms per prediction", modelType, delta / (double)count, timer.ElapsedMilliseconds, (float)timer.ElapsedMilliseconds / (float)data.Count); foreach (var kvp in map.OrderByDescending(k => k.Value)) { Console.WriteLine("\t{0}\t{1}", kvp.Key, kvp.Value); } } return(0); }