Britbot
Commander.cs
Go to the documentation of this file.
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 }