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; }
public override double EstimatedTimeToFinish(RaceSpot rs, double atSpeed) { // Simple - no need for speed if (atSpeed > 0.0) { // Speed is KM per Hr return (_Target -rs.d) / (atSpeed * KMPH_TO_M_PER_SEC); } return double.MaxValue; }
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 double PercentComplete(RaceSpot rs) { return rs.t / (double)(600*_Target); }
public abstract long Result(RaceSpot rs);
public abstract double EstimatedTimeToFinish(RaceSpot rs, double atSpeed);
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) { // Nothing to do here }
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 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++; } }
public override double PercentComplete(RaceSpot rs) { return 0.01* (double)rs.d; // Pursuit fixed distance is 10000m }
public override long Result(RaceSpot rs) { return (long)rs.t; }
public override double PercentComplete(RaceSpot rs) { return 100.0 * (double)rs.d / (double) _Target; }
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 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"; }
public abstract void Update(RaceSpot myRs, RaceSpot[] rs);
public abstract double PercentComplete(RaceSpot rs);
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 override double EstimatedTimeToFinish(RaceSpot rs, double atSpeed) { // Simple - no need for speed return 60000 * _Target - rs.t; }
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; }
public override long Result(RaceSpot rs) { return (long)(rs.d); }
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(); } }