using System; using System.Collections.Generic; using System.Linq; using System.Threading; namespace IdleMafiaBot { sealed class Turns { const double WaitPointsPerMinute = 10; sealed class Waiter { public DateTime Since, Seen; public int Need; } readonly object gate = new object(); readonly List order; readonly Dictionary waiting = new Dictionary(); Bot holder; public Turns(IEnumerable bots) { order = bots.ToList(); } internal static double Score(int need, double waitedMinutes) { return Math.Max(0, need) + waitedMinutes * WaitPointsPerMinute; } public bool Take(Bot b, int need, Func stopped, int maxMs) { int end = Environment.TickCount + maxMs; lock (gate) while (true) { if (stopped()) throw new StopException(); if (!order.Contains(b)) return true; Waiter me; if (!waiting.TryGetValue(b, out me)) waiting[b] = me = new Waiter { Since = DateTime.UtcNow }; me.Need = need; me.Seen = DateTime.UtcNow; if (holder == null && Next() == b) { holder = b; waiting.Remove(b); return true; } int left = end - Environment.TickCount; if (left <= 0) return false; Monitor.Wait(gate, Math.Min(500, left)); } } Bot Next() { var now = DateTime.UtcNow; return waiting.Where(w => now - w.Value.Seen < TimeSpan.FromMinutes(1)) .OrderByDescending(w => Score(w.Value.Need, (now - w.Value.Since).TotalMinutes)).ThenBy(w => w.Value.Since).First().Key; } public bool Wanted(Bot b) { lock (gate) return waiting.Keys.Any(o => o != b); } public void Pass(Bot b) { lock (gate) { if (holder == b) holder = null; Monitor.PulseAll(gate); } } public void Leave(Bot b) { lock (gate) { if (!order.Remove(b)) return; waiting.Remove(b); if (holder == b) holder = null; Monitor.PulseAll(gate); } } } }