public override void Evaluate(RaceSpot myRs, RaceSpot[] rs) { Likelihood = 0; double thisProgress = _Race.RaceType.PercentComplete(myRs); double deltaProgress = thisProgress - _LastProgress; // This is a proactive tactic, so we same the likelihood double l = GetLikelihood(myRs); double scaledLikelihood = l * (1.0 - Math.Exp(-AGGRESSIVE_ATTACKS_PER_RACE * 0.04 * deltaProgress)); // Use the score as a monte-carlo distribution if (_Rnd.NextDouble() < scaledLikelihood) { // Am I in any sort of group and is that group going at less than my 5 minute pace double breakSpeed = TurboTrainer.PowerNormalisedSpeed(_Motor.PowerBands[1]); if (InGroup(myRs, rs) && myRs.s > 0 && myRs.s < breakSpeed) { // initial target speed = 15-30% of existing speed // Target break - between 30 secs and 2.5 minutes _StopTime = myRs.t + 30000 + (ulong)(120000 * _Rnd.NextDouble()); RequestedPower = _Motor.PowerBands[1]; // Break at 5 minute Likelihood = l; } } _LastProgress = thisProgress; }
protected RaceSpot GroupLeader(RaceSpot myRs, RaceSpot[] rs) { RaceSpot leader = rs.Where(p => !p.f && !p.a && (p.d - myRs.d) > 0 && (p.d - myRs.d) < GROUP_DISTANCE_MTR).OrderByDescending(p => p.d).FirstOrDefault(); return leader; }
protected double GetLikelihood(RaceSpot rs) { double likelihood = 0; if (_LikelihoodPoints.Count > 0) { double pcnt = _Race.RaceType.PercentComplete(rs); // Find two points that surround this percentage TacticPoint before = _LikelihoodPoints.Where(p => p.PercentCompleted <= pcnt).OrderBy(p => p.PercentCompleted).LastOrDefault(); TacticPoint after = _LikelihoodPoints.Where(p => p.PercentCompleted > pcnt).OrderBy(p => p.PercentCompleted).FirstOrDefault(); if (after == null) { likelihood = before.Value; } else { double denom = (after.PercentCompleted - before.PercentCompleted); double s = 0.5; if (denom > 0.0) { s = (pcnt - before.PercentCompleted) * after.Value / denom; } likelihood = (1 - s) * before.Value + s * after.Value; } } return likelihood; }
public abstract void Update(RaceSpot myRs, RaceSpot[] rs);
private void ChooseTactic(RaceSpot myRs, RaceSpot[] rs) { double maxLikelihood = 0; _CurrentTacticIdx = 0; // Default time trial tactic for (int idx = 0; idx < _TacticsCompendium.Count; idx++) { _TacticsCompendium[idx].Evaluate(myRs, rs); if (_TacticsCompendium[idx].Likelihood > maxLikelihood) { maxLikelihood = _TacticsCompendium[idx].Likelihood; _CurrentTacticIdx = idx; } } _ReviewTacticsTime = _TacticsCompendium[_CurrentTacticIdx].ReviewTime(); }
public override void Update(RaceSpot myRs, RaceSpot[] rs) { if (myRs.t > _StopTime) { // I'm knackered - call it off; Likelihood = 0; } }
public override void Update(RaceSpot myRs, RaceSpot[] rs) { // I'm in the lead! if (rs[0].id == _UserId) { Likelihood = 0; // Force cancellation } }
public override void Update(RaceSpot myRs, RaceSpot[] rs) { // Nothing to do here }
private void Start(object state) { // set the start time before releasing the race logging _raceStartTicks = Utilities.DbTime.ToUnsignedDbTicks(DateTime.UtcNow); // Enable the race for all - define initial positions int position = 1; try { _SpotLock.EnterWriteLock(); foreach (Race.Participant p in Race.Participants) { if (p.Status == @"Scheduled") { // Initial spot data Spot curSpot = new Spot(); curSpot.id = p.UserId; curSpot.t = _raceStartTicks; curSpot.s = 0; curSpot.c = 0; curSpot.h = 0; curSpot.p = 0; _Spots.Add(p.UserId, curSpot); } } } finally { _SpotLock.ExitWriteLock(); } try { _RaceSpotLock.EnterWriteLock(); foreach (Race.Participant p in Race.Participants) { /////////////// Need to revist this! if (p.Status == @"Scheduled") { // Initial Race data RaceSpot curRaceSpot = new RaceSpot(); curRaceSpot.id = p.UserId; curRaceSpot.d = 0; curRaceSpot.t = 0; curRaceSpot.s = 0; curRaceSpot.c = 0; curRaceSpot.k = 0; curRaceSpot.p = 0; curRaceSpot.e = 0; curRaceSpot.h = 0; curRaceSpot.b = 0; curRaceSpot.i = 0; curRaceSpot.v = 0; curRaceSpot.f = false; // not finished curRaceSpot.a = false; // not abandonned curRaceSpot.pos = position++; _RaceSpots.Add(p.UserId, curRaceSpot); _RaceSpotHistory.Add(p.UserId, new Stack<RaceSpot>()); // Need to clone here or the stack will just contain a reference to the original _RaceSpotHistory[p.UserId].Push(curRaceSpot.Copy()); } } // tell the db Race.Start(); } finally { _RaceSpotLock.ExitWriteLock(); } // Attach the race Processors CycliManager.Instance.AttachRiderProcessors(this, Race); // Start the race CycliManager.Instance.SendStartRace(Race); // Start the updater thread _UpdateRaceSpotTimer = new Timer(UpdateRaceSpotData, null, 0, 1000); }
public override void Evaluate(RaceSpot myRs, RaceSpot[] rs) { Likelihood = 0; // Do not use unless it's going to work int chasedIdx = 0; while (rs[chasedIdx].id != _UserId && Likelihood == 0 && chasedIdx < rs.Length) // Last condition shouldn't be needed but is a backstop { if (!rs[chasedIdx].f && !rs[chasedIdx].a) { // Only go throgh the chase routine if the lead rider is outside the drafting region double gap = rs[chasedIdx].d - myRs.d; if (gap > GROUP_DISTANCE_MTR) { double speedDiff = myRs.s - rs[chasedIdx].s; double speedDiffMPerSec = speedDiff * RaceType.KMPH_TO_M_PER_SEC; double timeToFinish = _Race.RaceType.EstimatedTimeToFinish(rs[chasedIdx], rs[chasedIdx].s); // Are we going to catch them anyway? if (speedDiffMPerSec * timeToFinish < gap) { // No we're not - do something about it! if (timeToFinish > 0) { double[] powers = _Motor.AvailablePower(); int bandIdx = _Motor.AlphaBands.Select(alpha => timeToFinish <= alpha).Count(p => p) - 1; if (bandIdx < 0) { // Longer than 1 hour - scale the power RequestedPower = _Motor.RecoverPower + _Motor.AlphaBands[0] * (powers[0] - _Motor.RecoverPower) / timeToFinish; } else { RequestedPower = powers[bandIdx]; } // What speed will this give? double maxSpeed = TurboTrainer.PowerNormalisedSpeed(RequestedPower); double maxSpeedMPerSec = (maxSpeed - rs[chasedIdx].s) * RaceType.KMPH_TO_M_PER_SEC; // Will this catch them? if (speedDiffMPerSec * timeToFinish >= gap) // Yes! Give it a go then Likelihood = GetLikelihood(myRs); } } } } chasedIdx++; } }
private int GetTargetValue(Race race, RaceSpot rs) { int v = int.MinValue; float x = (float)this.Race.RaceType.PercentComplete(rs); // scan the profile until we find the between float y = _RaceProfile.GetYValue(x); if (!float.IsNaN(y)) { v = race.ProfileMin + (int)(0.01 * (float)(race.ProfileMax - race.ProfileMin) * y); } return v; }
public RaceSpot Copy() { RaceSpot newrs = new RaceSpot(); newrs.id = id; newrs.pos = pos; newrs.pom = pom; newrs.d = d; newrs.t = t; newrs.s = s; newrs.g = g; newrs.c = c; newrs.h = h; newrs.p = p; newrs.e = e; newrs.i = i; newrs.v = v; newrs.f = f; newrs.a = a; newrs.dp = dp; return newrs; }
private double ExecuteTactics(RaceSpot myRs, RaceSpot[] rs) { DateTime timeNow = DateTime.UtcNow; if (_CurrentTacticIdx < 0 ||_ReviewTacticsTime < timeNow) { // Time to review ChooseTactic(myRs, rs); } else { _TacticsCompendium[_CurrentTacticIdx].Update(myRs, rs); // Some condition has been met in a simple update that forces a full review if (_TacticsCompendium[_CurrentTacticIdx].Likelihood < 0.05) // We really should be looking again at this score { ChooseTactic(myRs, rs); } } System.Diagnostics.Debug.WriteLine(string.Format("Virtual {0} {1} {2} {3} {4} {5}", myRs.t, this.UserName, myRs.pos, _TacticsCompendium[_CurrentTacticIdx].TacticName, _TacticsCompendium[_CurrentTacticIdx].RequestedPower, _Motor.Tank())); return _TacticsCompendium[_CurrentTacticIdx].RequestedPower; }
protected bool InGroup(RaceSpot myRs, RaceSpot[] rs) { // Are we in a group to break out? int inGroup = 0; foreach (RaceSpot r in rs) { if (Math.Abs(r.d - myRs.d) <= 10) { inGroup++; } } return (inGroup >= 2); }
public override void Update(RaceSpot myRs, RaceSpot[] rs) { throw new NotImplementedException(); }
public override void Evaluate(RaceSpot myRs, RaceSpot[] rs) { // Look at the available power band speeds to estimate double[] powers = _Motor.AvailablePower(); double[] speeds = powers.Select(p => TurboTrainer.PowerNormalisedSpeed(p)).ToArray(); double[] timesToFinish = speeds.Select(s => _Race.RaceType.EstimatedTimeToFinish(myRs, s)).ToArray(); int bandIdx = _Motor.AlphaBands.Zip(timesToFinish, (alpha, t) => t <= alpha).Count(p => p)-1; for (int j = 0; j < timesToFinish.Length; j++) { System.Diagnostics.Debug.WriteLine(string.Format("Timetrialing time to finish {0} {1:N2} {2:N2} {3:N2} {4:N2}", myRs.id, _Motor.AlphaBands[j], powers[j], speeds[j], timesToFinish[j])); } System.Diagnostics.Debug.WriteLine(string.Format("Timetrialing power index {0} {1} ", myRs.id, bandIdx)); double timeToFinish = timesToFinish[0]; if (bandIdx < 0) { // Longer than 1 hour - scale the power RequestedPower = _Motor.RecoverPower + _Motor.AlphaBands[0] * (powers[0] -_Motor.RecoverPower) / timesToFinish[0]; } else { timeToFinish = timesToFinish[bandIdx]; RequestedPower = powers[bandIdx]; } if (timeToFinish < _goTime) { Likelihood = 1.0; // Force the final sprint } else { Likelihood = 0.1; // Low score - this is the default } }
public override void Evaluate(RaceSpot myRs, RaceSpot[] rs) { RequestedPower = _Motor.RecoverPower; // Lowest likelihood Likelihood = 0.1; }
public override void Evaluate(RaceSpot myRs, RaceSpot[] rs) { // Nothing to do here - wheelsucking is an update by update action Likelihood = 0.0; RaceSpot hasLeader = GroupLeader(myRs, rs); if (hasLeader != null) { // What's my optimum power double leaderSpeed = hasLeader.s; double leaderPower = TurboTrainer.StandardPower(leaderSpeed); // For wheel sucking - only consider using 1hr power unless we're in the last 5 minutes // Use the leader speed to judge double timeToFinish = _Race.RaceType.EstimatedTimeToFinish(myRs, leaderSpeed); double[] powers = _Motor.AvailablePower(); double optimalPower = 0; int bandIdx = _Motor.AlphaBands.Select(alpha => timeToFinish <= alpha).Count(p => p) - 1; if (bandIdx < 0) { // Longer than 1 hour - scale the power RequestedPower = _Motor.RecoverPower + _Motor.AlphaBands[0] * (powers[0] - _Motor.RecoverPower) / timeToFinish; } else { optimalPower = powers[bandIdx]; } // Not 1.0 as we may need to go for a sprint, or be condering a break double wheelsuckPower = WHEELSUCK_POWER_FRAC * leaderPower; if (wheelsuckPower <= optimalPower) { // An aggressive rider be // (a) less likely to wheel suck Likelihood = (1 - _Aggression); // (b) break earlier if (timeToFinish < _offTime) { Likelihood = 0; } RequestedPower = wheelsuckPower; _leader = hasLeader; } } }
public override void Update(RaceSpot myRs, RaceSpot[] rs) { // Not used }
public override void Update(RaceSpot myRs, RaceSpot[] rs) { if (_leader != null) { double leaderPower = TurboTrainer.StandardPower(_leader.s); RequestedPower = WHEELSUCK_POWER_FRAC * leaderPower; } }
/// <summary> /// </summary> /// <param name="rs"></param> /// <returns></returns> public abstract void Evaluate(RaceSpot myRs, RaceSpot[] rs);
public void SendLeaderboard(string raceId, RaceSpot[] rs) { try { _ConnectionProcessLock.EnterReadLock(); // Is the user connection foreach (RaceSpot r in rs) { string connectionId; if (_connections.TryGetValue(r.id, out connectionId)) { Context.Clients.Client(connectionId).receiveLeaderboard(raceId, rs); } } } finally { _ConnectionProcessLock.ExitReadLock(); } }
public void Finish(RaceSpot[] rss, Dictionary<string, Stack<RaceSpot>> history) { string sqlRiderUpdate = @"update cycli_race_riders set Position=@p, Distance=@d, Time=@t, Energy=@e, PedalStrokes=@k, Heartbeats=@b, TSS=@i " + "where RaceId=@r and UserId=@u"; string sqlRaceUpdate = @"update cycli_races set Status='Finished' " + "where RaceId=@r and Status='Started'"; SQLiteDatabase db = new SQLiteDatabase(true); try { db.ExecuteNonQuery(sqlRaceUpdate, "@r", this.RaceId); foreach (RaceSpot rs in rss) { db.ExecuteNonQuery(sqlRiderUpdate, "@p", rs.pos, "@d", rs.d, "@t", rs.t, "@e", (int)rs.e, "@k", (int)rs.k, "@b", (int)rs.b, "@i", (int)rs.i, "@r", this.RaceId, "@u", rs.id); } db.CommitTransaction(); } catch (Exception ex) { try { db.RollbackTransaction(); } catch (Exception ex1) { } } finally { // Don't need to close on a transaction } // Do points as a separate transaction string raceSpotSql = @"insert into cycli_race_spots (UserId, RaceId, Time, Distance, Speed, Cadence, HR, Power) " + "values (@u, @r, @t, @d, @s, @c, @h, @p)"; db = new SQLiteDatabase(true); try { foreach (string userId in history.Keys) { Stack<RaceSpot> userHistory = history[userId]; foreach (RaceSpot rs in userHistory) { db.ExecuteNonQuery(raceSpotSql, "@r", this.RaceId, "@u", userId, "@t", rs.t, "@d", rs.d, "@s", rs.s, "@c", rs.c, "@h", rs.h, "@p", rs.p); } } db.CommitTransaction(); } catch (Exception ex) { try { db.RollbackTransaction(); } catch (Exception ex1) { } } finally { } // Mark the race as Finished this.Status = @"Finished"; }