|
Britbot
|
00001 #region Usings 00002 00003 using System; 00004 using System.Collections.Generic; 00005 using System.Diagnostics; 00006 using System.Linq; 00007 using System.Threading; 00008 using System.Threading.Tasks; 00009 using Britbot.Simulator; 00010 using Pirates; 00011 00012 #endregion 00013 00014 namespace Britbot 00015 { 00019 public static class Commander 00020 { 00021 #region Static Fields & Consts 00022 00026 private static Stopwatch _turnTimer; 00027 00031 private static List<int> _deadPirates; 00032 00033 #endregion 00034 00035 #region Fields & Properies 00036 00040 public static List<Group> Groups { get; private set; } 00041 00042 #endregion 00043 00044 #region Constructors & Initializers 00045 00049 static Commander() 00050 { 00051 Groups = new List<Group>(); 00052 _turnTimer = new Stopwatch(); 00053 _deadPirates = new List<int>(); 00054 00055 #region Terrible Switch-Case 00056 00057 //TODO initial config should be better then this 00058 //Hookup the UltimateConfig() here 00059 00060 if (Bot.Game.Islands().Count == 1) 00061 { 00062 Groups.Add(new Group(0, Bot.Game.AllMyPirates().Count)); 00063 return; 00064 } 00065 00066 //TODO this is awfully specific for the game bots. We have to generalize this 00067 switch (Bot.Game.AllMyPirates().Count) 00068 { 00069 case 3: 00070 Groups.Add(new Group(0, 2)); 00071 Groups.Add(new Group(2, 1)); 00072 break; 00073 case 4: 00074 if (Bot.Game.AllEnemyPirates().Count > 4) 00075 { 00076 Groups.Add(new Group(0, 1)); 00077 Groups.Add(new Group(1, 1)); 00078 Groups.Add(new Group(2, 2)); 00079 } 00080 else 00081 { 00082 Groups.Add(new Group(0, 3)); 00083 Groups.Add(new Group(3, 1)); 00084 ; 00085 } 00086 break; 00087 case 5: 00088 Groups.Add(new Group(0, 2)); 00089 Groups.Add(new Group(2, 2)); 00090 Groups.Add(new Group(4, 1)); 00091 break; 00092 case 6: 00093 if (Bot.Game.EnemyIslands().Count > 0) 00094 { 00095 Groups.Add(new Group(0, 5)); 00096 Groups.Add(new Group(5, 1)); 00097 } 00098 else 00099 { 00100 Groups.Add(new Group(2, 4)); 00101 Groups.Add(new Group(0, 1)); 00102 Groups.Add(new Group(1, 1)); 00103 } 00104 break; 00105 case 7: 00106 Groups.Add(new Group(0, 2)); 00107 Groups.Add(new Group(2, 3)); 00108 Groups.Add(new Group(5, 2)); 00109 00110 break; 00111 case 8: 00112 if (Bot.Game.GetMyPirate(7).Loc.Row == 39) 00113 { 00114 Groups.Add(new Group(0, 4)); 00115 Groups.Add(new Group(4, 3)); 00116 Groups.Add(new Group(7, 1)); 00117 } 00118 else 00119 { 00120 Groups.Add(new Group(0, 3)); 00121 Groups.Add(new Group(3, 2)); 00122 Groups.Add(new Group(5, 2)); 00123 Groups.Add(new Group(7, 1)); 00124 } 00125 break; 00126 case 9: 00127 Groups.Add(new Group(0, 3)); 00128 Groups.Add(new Group(3, 3)); 00129 Groups.Add(new Group(6, 2)); 00130 Groups.Add(new Group(8, 1)); 00131 break; 00132 default: 00133 for (int i = 0; i < Bot.Game.AllMyPirates().Count; i++) 00134 Groups.Add(new Group(i, 1)); 00135 break; 00136 } 00137 00138 #endregion 00139 } 00140 00141 #endregion 00142 00146 public static Dictionary<Pirate, Direction> Play(CancellationToken cancellationToken, out bool onTime) 00147 { 00148 // Restart the timer 00149 Commander._turnTimer.Restart(); 00150 00151 // note that because this method is on a separate thread we need this try-catch 00152 // although we have one on our bot 00153 try 00154 { 00155 /* 00156 * "Initiating Commander Sequence..." 00157 */ 00158 00159 //dump Magic configuration and current stats 00160 Magic.DumpLog(); 00161 00162 //remove dead groups 00163 Commander.Groups.RemoveAll(g => g.Pirates.Count == 0); 00164 00165 //re allocates the new revived pirates 00166 Commander.AllocateRevived(); 00167 00168 //update the enemy analysis and info 00169 Enemy.Update(cancellationToken); 00170 00171 //update smartIslands 00172 SmartIsland.UpdateAll(); 00173 00174 //merge similar groups for extra power 00175 Commander.MergeSimilar(); 00176 00177 //calculate targets 00178 Commander.CalculateAndAssignTargets(cancellationToken); 00179 00180 //fix configuration 00181 ConfigHelper.ReConfigure(); 00182 00183 //swap pirates on groups the collide 00184 Commander.FixGroupArrangement(); 00185 00186 //Get the moves for all the pirates and return them 00187 Dictionary<Pirate, Direction> moves = GetAllMoves(cancellationToken); 00188 00189 //cloacking override 00190 SpecialOps.DoCloak(moves); 00191 00192 //update dead pirates list 00193 Commander._deadPirates = Bot.Game.AllMyPirates().Where(p => p.IsLost).ToList().ConvertAll(p => p.Id); 00194 00195 //we are done for this turn 00196 Logger.Write( 00197 string.Format("Commander done doing calculations and drinking coffee after {0}ms", 00198 _turnTimer.ElapsedMilliseconds), true); 00199 00200 //we are on time! 00201 onTime = true; 00202 00203 //return the carefully crafted moves 00204 return moves; 00205 } 00206 catch (OperationCanceledException) //catch task cancellation 00207 { 00208 Logger.Write("****** COMMANDER EXITING DUE TO TASK CANCELLATION ******", true); 00209 00210 //lower the max iteration bound so we will stop to timeout 00211 Magic.MaxIterator = (int)(Magic.MaxIterator * 0.93); 00212 00213 //we are not on time 00214 onTime = false; 00215 00216 //do some profiling 00217 Logger.Profile(); 00218 00219 //don't return null 00220 return new Dictionary<Pirate, Direction>(); 00221 } 00222 catch (Exception ex) //catch everyting else 00223 { 00224 Logger.Write("==========COMMANDER EXCEPTION============", true); 00225 Logger.Write("Commander almost crashed because of exception: " + ex.Message, true); 00226 00227 //print stack trace 00228 StackTrace exTrace = new StackTrace(ex, true); 00229 StackFrame frame = exTrace.GetFrame(0); 00230 Logger.Write( 00231 string.Format("The exception was thrown from method {0} at file {1} at line #{2}", frame.GetMethod(), 00232 frame.GetFileName(), frame.GetFileLineNumber()), true); 00233 00234 Logger.Write("==========COMMANDER EXCEPTION============", true); 00235 00236 //we are no on time 00237 onTime = false; 00238 00239 //di some prodiling 00240 Logger.Profile(); 00241 00242 //don't return null 00243 return new Dictionary<Pirate, Direction>(); 00244 } 00245 } 00246 00250 private static void AllocateRevived() 00251 { 00252 List<int> alive = Bot.Game.AllMyPirates().Where(p => !p.IsLost).ToList().ConvertAll(p => p.Id); 00253 List<int> revived = alive.Intersect(_deadPirates).ToList(); 00254 00255 foreach (int pid in revived) 00256 Groups.Add(new Group(new[] {pid})); 00257 } 00258 00263 public static int CalcMaxPrioritiesNum() 00264 { 00265 return (int) (Math.Pow(Magic.MaxIterator, 1.0 / Commander.Groups.Count)); 00266 } 00267 00273 private static void CalculateAndAssignTargets(CancellationToken cancellationToken) 00274 { 00275 //force groups to calculate priorities 00276 Commander.StartCalcPriorities(cancellationToken); 00277 00278 //read dimensions of iteration 00279 int[] dimensions = Commander.GetTargetsDimensions(); 00280 00281 //read all possible target-group assignment 00282 Score[][] possibleAssignments = Commander.GetPossibleTargetMatrix(); 00283 00284 //indexes of the best assignment yet 00285 int[] maxAssignment = new int[dimensions.Length]; 00286 00287 //maxScore setup 00288 double maxScore = Int32.MinValue; 00289 00290 //create new iteration object 00291 ExpIterator iterator = new ExpIterator(dimensions); 00292 00293 //Score array for calculations in each iteration 00294 Score[] scoreArr; 00295 00296 //create new simulated game 00297 SimulatedGame sg = new SimulatedGame(); 00298 00299 //iterating over all possible target assignments 00300 do 00301 { 00302 //Throwing an exception if cancellation was requested. 00303 cancellationToken.ThrowIfCancellationRequested(); 00304 00305 //set score array for current iteration 00306 scoreArr = Commander.GetSpecificAssignmentScores(possibleAssignments, iterator.Values); 00307 00308 //calculate new score 00309 double newScore = Commander.GlobalizeScore(sg, scoreArr, cancellationToken); 00310 00311 //check if the score is better 00312 if (newScore > maxScore) 00313 { 00314 //replace best 00315 maxScore = newScore; 00316 00317 //copy the array to the maxAssigment array 00318 Array.Copy(iterator.Values, maxAssignment, iterator.Values.Length); 00319 } 00320 } while (iterator.NextIteration()); 00321 00322 //read the "winning" assignment 00323 scoreArr = Commander.GetSpecificAssignmentScores(possibleAssignments, maxAssignment); 00324 00325 //now we got the best assignment, so just set it up 00326 for (int i = 0; i < dimensions.Length; i++) 00327 Commander.Groups[i].SetTarget(scoreArr[i].Target); 00328 } 00329 00338 private static double GlobalizeScore(SimulatedGame sg, Score[] scoreArr, CancellationToken cancellationToken) 00339 { 00340 // ReSharper disable once ConditionIsAlwaysTrueOrFalse 00341 if (Magic.UseBasicGlobalizing) //use simple globalizing if needed 00342 { 00343 #region Basic Globalizing 00344 double totalDensityBonus = 0; 00345 00346 double score = 0; 00347 score += Math.Pow(2, scoreArr.Sum(a => a.Value)) * 20; 00348 00349 foreach (Score s in scoreArr) 00350 { 00351 score -= s.Eta; 00352 score += totalDensityBonus * Magic.DensityBonusCoefficient; 00353 } 00354 00355 for (int i = 0; i < scoreArr.Length - 1; i++) 00356 { 00357 for (int j = i + 1; j < scoreArr.Length; j++) 00358 { 00359 if (scoreArr[i].Target.Equals(scoreArr[j].Target)) 00360 score -= 5000; 00361 } 00362 } 00363 00364 for (int i = 0; i < scoreArr.Length; i++) 00365 { 00366 if (scoreArr[i].Target == Groups[i].Target) 00367 { 00368 int bonus = (int) Math.Abs(score * Magic.DecisivenessBonus); 00369 score += bonus; 00370 } 00371 if (scoreArr[i].EnemyShips >= Groups[i].Pirates.Count) 00372 score -= 5000; 00373 } 00374 00375 return score; 00376 #endregion 00377 } 00378 else //actually simulate the game and determine the score by it 00379 { 00380 //reset simulation 00381 sg.ResetSimulation(); 00382 00383 //setup simulation events 00384 for (int i = 0; i < scoreArr.Length; i++) 00385 { 00386 switch (scoreArr[i].Type) 00387 { 00388 case TargetType.Island: 00389 sg.AddEvent(new GroupArrivalEvent((int) scoreArr[i].Eta, 00390 sg.Islands[((SmartIsland) (scoreArr[i].Target)).Id], sg.MyGroups[Commander.Groups[i].Id])); 00391 break; 00392 case TargetType.EnemyGroup: 00393 sg.AddEvent(new BattleEvent((int) scoreArr[i].Eta, 00394 sg.EnemyGroups[((EnemyGroup) (scoreArr[i].Target)).Id], sg.MyGroups[Commander.Groups[i].Id])); 00395 break; 00396 } 00397 } 00398 00399 //run the simulation and return its score 00400 return sg.RunSimulation(cancellationToken); 00401 } 00402 } 00403 00407 private static void MergeSimilar() 00408 { 00409 //setup merging lists 00410 List<List<Group>> allMerges = new List<List<Group>>(); 00411 00412 //iterate over all groups 00413 foreach (Group group in Commander.Groups) 00414 { 00415 //create a merging list and add the current group to it 00416 List<Group> toMerge = new List<Group> {group}; 00417 00418 //check if there are any mering list that should contain the current group 00419 List<List<Group>> sameTarget = 00420 allMerges.Where( 00421 gList => 00422 //Basically join all group sharing the same target and in reasonable distance from each other 00423 gList.Any(g => g.Target.Equals(group.Target) && g.MinDistance(group) < Magic.MaxJoinDistance)) 00424 .ToList(); 00425 00426 //if the sameTarget list constains some merging lists 00427 if (sameTarget.Count > 0) 00428 { 00429 //if there are, remove these merging lists 00430 allMerges.RemoveAll( 00431 gList => 00432 gList.Any(g => g.Target.Equals(group.Target) && g.MinDistance(group) < Magic.MaxJoinDistance)); 00433 00434 //merge these lists 00435 foreach (List<Group> gList in sameTarget) 00436 toMerge.AddRange(gList); 00437 } 00438 00439 //add the new group to the list of merges 00440 allMerges.Add(toMerge); 00441 } 00442 00443 //actually merge the group according to the merging lists 00444 foreach (List<Group> mergeList in allMerges) 00445 { 00446 Group first = mergeList.First(); 00447 00448 for (int i = 1; i < mergeList.Count; i++) 00449 first.Join(mergeList[i], false); 00450 } 00451 } 00452 00458 private static Dictionary<Pirate, Direction> GetAllMoves(CancellationToken cancellationToken) 00459 { 00460 //Remove all empty groups 00461 Commander.Groups.RemoveAll(g => g.Pirates.Count == 0); 00462 00463 //Update all groups 00464 Parallel.ForEach(Commander.Groups, g => g.Update()); 00465 00466 //A list with all the moves from all groups 00467 List<KeyValuePair<Pirate, Direction>> allMoves = 00468 new List<KeyValuePair<Pirate, Direction>>(Bot.Game.AllMyPirates().Count); 00469 00470 //Get the moves from each group we have 00471 foreach (Group group in Commander.Groups) 00472 allMoves.AddRange(group.GetGroupMoves(cancellationToken).ToList()); 00473 00474 //Convert the moves list to dictionary 00475 return allMoves.ToDictionary(pair => pair.Key, pair => pair.Value); 00476 } 00477 00487 private static Score[][] GetPossibleTargetMatrix() 00488 { 00489 //allocate array of array: array for each group's possible targets 00490 Score[][] possibleTargets = new Score[Commander.Groups.Count][]; 00491 00492 for (int i = 0; i < Commander.Groups.Count; i++) 00493 { 00494 //convert the priority list to an array (to enable quick access) 00495 possibleTargets[i] = Commander.Groups[i].Priorities.ToArray(); 00496 } 00497 00498 //return the matrix 00499 return possibleTargets; 00500 } 00501 00509 private static Score[] GetSpecificAssignmentScores(IReadOnlyList<Score[]> possibleAssignments, int[] assignment) 00510 { 00511 //declare the array to later be returned 00512 Score[] scoreArr = new Score[possibleAssignments.Count]; 00513 00514 //fill the array with the appropriate values 00515 for (int i = 0; i < scoreArr.Length; i++) 00516 //fill the i'th place of the score array with the target of the i'th group in the assignment index 00517 scoreArr[i] = possibleAssignments[i][assignment[i]]; 00518 00519 //return the result 00520 return scoreArr; 00521 } 00522 00528 private static int[] GetTargetsDimensions() 00529 { 00530 //allocate a new array for the dimensions of each group's target 00531 int[] dimensions = new int[Commander.Groups.Count]; 00532 00533 //go over all the groups and read number of priorities to dimension 00534 for (int i = 0; i < Groups.Count; i++) 00535 dimensions[i] = Commander.Groups[i].Priorities.Count; 00536 00537 return dimensions; 00538 } 00539 00544 private static void StartCalcPriorities(CancellationToken cancellationToken) 00545 { 00546 Parallel.ForEach(Commander.Groups,g => g.CalcPriorities(cancellationToken)); 00547 Logger.Write("Priorities Calculated"); 00548 } 00549 00553 private static void FixGroupArrangement() 00554 { 00555 //going over all pair of groups 00556 foreach (Group g1 in Commander.Groups) 00557 { 00558 foreach (Group g2 in Commander.Groups) 00559 { 00560 //check if it is the same 00561 if (g1.Equals(g2)) 00562 continue; 00563 00564 //check if they are messed out 00565 if (Group.CheckIfGroupsIntersects(g1, g2)) 00566 { 00567 //find larger group 00568 if (g1.Pirates.Count > g2.Pirates.Count) 00569 { 00570 Group.Switch(g1, g2); 00571 } 00572 else 00573 { 00574 Group.Switch(g2, g1); 00575 } 00576 } 00577 } 00578 } 00579 } 00580 00585 public static bool IsDefensive() 00586 { 00587 double ePPT = Math.Floor(Math.Pow(2, Bot.Game.EnemyIslands().Count - 1)); 00588 double myPPT = Math.Floor(Math.Pow(2, Bot.Game.MyIslands().Count - 1)); 00589 00590 double eN = Bot.Game.GetEnemyScore(); 00591 double myN = Bot.Game.GetMyScore(); 00592 00593 double maxTurns = 1000; 00594 00595 double turnUntilEnemy = (maxTurns - eN) / ePPT; 00596 double turnUntilMe = (maxTurns - myN) / myPPT; 00597 00598 if (turnUntilMe < turnUntilEnemy - 50) 00599 return true; 00600 return false; 00601 } 00602 } 00603 }
1.7.6.1