static Move V4Strategy( MoveMessage message, SolverState solverState) { var startTime = DateTime.UtcNow; var deadLine = startTime.AddMilliseconds(900); var myId = solverState.initialState.punter.Value; var initialMap = solverState.initialState.map; var takenRivers = Utils.ConvertMovesToRivers(initialMap, solverState.moves, (id) => true) .ToLookup(river => river, river => true); var availableRivers = initialMap.rivers .Where(river => !takenRivers.Contains(river)) .ToList(); var canUseOptions = solverState.initialState.settings.options && solverState.moves.Count(move => move.option != null && move.option.punter == myId) < initialMap.mines.Count; var adjacencyMap = new AdjacencyMap(initialMap.rivers); var mineDistances = new MineDistances(initialMap, adjacencyMap); var setupDoneTime = DateTime.UtcNow; // if we see a choke, take it var chokeFindTask = Task.Run(() => { var punters = new List <int>() { myId }; if (solverState.initialState.punters.Value == 2) { punters.Add(1 - myId); } foreach (var punter in punters) { var availableOptions = new List <River>(); if (canUseOptions && punter == myId) { var takenOptions = Utils.ConvertMovesToRivers(initialMap, solverState.moves.Where(move => move.option != null), (id) => true) .ToLookup(river => river, river => true); availableOptions = Utils.ConvertMovesToRivers(initialMap, solverState.moves, (id) => id != myId) .Where(river => !takenOptions.Contains(river)) .ToList(); } var chokes = FindChokes( initialMap.mines, Utils.ConvertMovesToRivers(initialMap, solverState.moves, (id) => id == punter).ToList(), availableRivers.Concat(availableOptions).ToList(), deadLine.AddMilliseconds(-100)); if (chokes.Any()) { var river = chokes[0][0]; var chokeAnalysisDoneTime = DateTime.UtcNow; Log(myId, string.Format("[{0}] [{1}] [{2}/{3}]", punter == myId ? "TakeChoke " : "BlockChoke", (int)(chokeAnalysisDoneTime - startTime).TotalMilliseconds, (int)(setupDoneTime - startTime).TotalMilliseconds, (int)(chokeAnalysisDoneTime - setupDoneTime).TotalMilliseconds)); return(availableOptions.Contains(river) ? CreateOptionMove(myId, river) : CreateClaimMove(myId, river)); } } return(null); }); // otherwise just play a move that joins two trees, increases liberty, or increases score var trees = new TreeSet( Utils.ConvertMovesToRivers(initialMap, solverState.moves, (id) => id == myId), initialMap.mines); var riversToConsider = availableRivers .Where(river => trees.Contains(river.source) || trees.Contains(river.target)) .DefaultIfEmpty(availableRivers.First()); var riversConsidered = from river in riversToConsider where DateTime.UtcNow < deadLine let newTrees = trees.AddRiver(river) let treeCount = newTrees.Trees.Count let liberty = newTrees.ComputeLiberty(availableRivers, adjacencyMap) let score = newTrees.ComputeScore(mineDistances) select new { river = river, liberty = liberty, score = score, treeCount = treeCount }; var rankedRivers = riversConsidered .ToList() .OrderBy(i => i.treeCount) .ThenByDescending(i => i.liberty) .ThenByDescending(i => i.score); var ans = CreateClaimMove(myId, rankedRivers.First().river); var analysisDoneTime = DateTime.UtcNow; var zero = TimeSpan.FromTicks(0); var waitTime = deadLine - DateTime.UtcNow; waitTime = waitTime < zero ? zero : waitTime; chokeFindTask.Wait(waitTime); if (chokeFindTask.IsCompleted && chokeFindTask.Result != null) { return(chokeFindTask.Result); } var doneTime = DateTime.UtcNow; Log(myId, string.Format("[NormalMove] [{0}] [{1}/{2}/{3}] [Trees:{4}] [Liberties:{5}] [Score:{6}]", (int)(doneTime - startTime).TotalMilliseconds, (int)(setupDoneTime - startTime).TotalMilliseconds, (int)(analysisDoneTime - setupDoneTime).TotalMilliseconds, (int)(doneTime - analysisDoneTime).TotalMilliseconds, rankedRivers.First().treeCount, rankedRivers.First().liberty, rankedRivers.First().score)); return(ans); }
TreeSet(TreeSet other) { Trees = other.Trees.Select(i => new HashSet <int>(i)).ToList(); }