Britbot
Bot.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.Threading;
00007 using System.Threading.Tasks;
00008 using Britbot.Fallback;
00009 using Pirates;
00010 
00011 #endregion
00012 
00013 namespace Britbot
00014 {
00018     public class Bot : IPirateBot
00019     {
00020         #region Static Fields & Consts
00021 
00025         private static Dictionary<Pirate, Direction> _movesDictionary = new Dictionary<Pirate, Direction>();
00026 
00030         private static Dictionary<Pirate, Direction> _fallbackMoves = new Dictionary<Pirate, Direction>();
00031 
00035         private static Task _commanderTask;
00036 
00040         private static Task _fallbackTask;
00041 
00042         #endregion
00043 
00044         #region Fields & Properies
00045 
00049         public static IPirateGame Game { get; private set; }
00050 
00051         #endregion
00052 
00053         #region Interface Implementations
00054 
00059         public void DoTurn(IPirateGame state)
00060         {
00061             //update the game so other classes will get updated data
00062             Bot.Game = state;
00063             
00064             bool commanderOk = false;
00065 
00066             try
00067             {
00068                 /*                 
00069                  * NOTE: I changed the commander and Group classes so the do not move anything by themselves, they just return
00070                  * moving instructions in the form of a Dictionary<Pirate,Direction>. This is because we do not want to abort the commander 
00071                  * after it moved couple of pirates but not all of them                            
00072                  */
00073                 //run the commander and check if it returned true (so it went OK)
00074                 commanderOk = Bot.ExecuteBot();
00075             }
00076             catch (Exception ex) //catch any FATAL errors from the ExecuteBot method
00077             {
00078                 Logger.Write("=================BOT ERROR=====================", true);
00079                 Logger.Write("Bot almost crashed because of exception: " + ex.Message, true);
00080 
00081                 //print the exception stack trace
00082                 StackTrace exTrace = new StackTrace(ex, true);
00083                 StackFrame frame = exTrace.GetFrame(0);
00084                 Logger.Write(
00085                     string.Format("The exception was thrown from method {0} at file {1} at line #{2}", frame.GetMethod(),
00086                         frame.GetFileName(), frame.GetFileLineNumber()), true);
00087 
00088                 Logger.Write("=================BOT ERROR=====================", true);
00089             }
00090             finally //whatever has happened there, move pirates so we will not crash
00091             {
00092                 //Actually move stuff
00093                 if (commanderOk) //if the commander was OK, use its results
00094                     Mover.MoveAll(Bot._movesDictionary);
00095                 else //else use the fallback results
00096                     Mover.MoveAll(Bot._fallbackMoves);
00097             }
00098         }
00099 
00100         #endregion
00101 
00106         private static bool ExecuteBot()
00107         {
00108             //clear the last moves
00109             Bot._fallbackMoves.Clear();
00110             Bot._movesDictionary.Clear();
00111 
00112             //setup time check flag
00113             bool onTime = false;
00114 
00115             //setup time remaining
00116             int time = Bot.Game.TimeRemaining();
00117 
00118             //setup safe timeout
00119             int safeTimeout = (int)(time * 0.65);
00120             CancellationTokenSource cancellationSource = new CancellationTokenSource(safeTimeout);
00121 
00122             //Commander task setup and start
00123             Bot._commanderTask =
00124                 Task.Factory.StartNew(() =>
00125                 {
00126                     try
00127                     {
00128                         Bot._movesDictionary = Commander.Play(cancellationSource.Token, out onTime);
00129                     }
00130                     catch (Exception ex)
00131                     {
00132                         Logger.Write("TOP LEVEL EXCEPTION WAS CAUGHT ON THE COMMANDER TASK ON TURN " +
00133                                      Bot.Game.GetTurn(), true);
00134                         Logger.Write(ex.ToString(), true);
00135                     }
00136                 });
00137 
00138             //Fallback task setup and start
00139             Bot._fallbackTask =
00140                 Task.Factory.StartNew(() =>
00141                 {
00142                     try
00143                     {
00144                         Bot._fallbackMoves = FallbackBot.GetFallbackTurns(cancellationSource.Token);
00145                     }
00146                     catch (Exception ex)
00147                     {
00148                         Logger.Write("TOP LEVEL EXCEPTION WAS CAUGHT ON THE FALLBACK TASK ON TURN " +
00149                                      Bot.Game.GetTurn(), true);
00150                         Logger.Write(ex.ToString(), true);
00151                     }
00152                 });
00153 
00154             //Wait for the tasks until the same timeout is over.
00155             //bloacks this thread until safeTimeout elapses
00156             Task.WaitAll(new Task[] {Bot._commanderTask, Bot._fallbackTask}, safeTimeout);
00157 
00158             //if it's stuck...
00159             if (!onTime)
00160             {
00161                 Logger.Write("=================TIMEOUT=======================", true);
00162                 Logger.Write("Commander timed out or errorer, switching to fallback code", true);
00163                 Logger.Write("Time remaining: " + Bot.Game.TimeRemaining());
00164                 Logger.Write("=================TIMEOUT=======================", true);
00165             }
00166 
00167             //do some profiling
00168             Logger.Profile();
00169 
00170             //return if the commander is on time / error status
00171             return onTime;
00172         }
00173     }
00174 }