using System.Collections.Generic; const double WaitPointsPerMinute = 10; sealed class Waiter { public DateTime Since, Seen; public int Need; } readonly object gate = new object(); readonly List<Bot> order; readonly Dictionary<Bot, Waiter> waiting = new Dictionary<Bot, Waiter>(); public Turns(IEnumerable<Bot> 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<bool> stopped, int maxMs) int end = Environment.TickCount + maxMs; if (stopped()) throw new StopException(); if (!order.Contains(b)) return true; if (!waiting.TryGetValue(b, out me)) waiting[b] = me = new Waiter { Since = DateTime.UtcNow }; 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)); 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); } if (holder == b) holder = null; if (!order.Remove(b)) return; if (holder == b) holder = null;