Newer
Older
Import / projects / Gameloft / bne_lib / tools / AutobotManager / src / BotManager.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Timers;
using System.Xml;
using System.Xml.Serialization;


namespace AutobotManager
{
    public enum BotState
    {
        BotState_Invalid = -1,
        BotState_IDLE,
        BotState_Running,
        BotState_Dying,

        BotState_Max,
    };

    public class BotManager
    {
        public class GameSettingsDefinition
        {
            [XmlElement("GameName")]
            public string m_GameName;
            [XmlElement("BotPath")]
            public string m_BotPath;
            [XmlElement("BotArgs")]
            public string m_BotArgs;
            [XmlArray("BotStyles"), XmlArrayItem("Item", typeof(string))]
            public string[] m_BotStyles;
            [XmlElement("AutobotServer")]
            public string m_AutobotServer;
            [XmlElement("AutobotServerPort")]
            public string m_AutobotServerPort;
            [XmlElement("AutobotScript")]
            public string m_AutobotScript;
            [XmlElement("BotIdentifierArg")]
            public string m_BotIdentifierArg;
            [XmlElement("BotStyleArg")]
            public string m_BotStyleArg;
            [XmlElement("BotStyleArgPassingMethod")]
            public string m_BotStyleArgPassingMethod;
            [XmlElement("BotScriptArg")]
            public string m_BotScriptArg;
        }

        [XmlRootAttribute("root", ElementName = "BotManagerSettings", Namespace = "", IsNullable = false)]
        public class SettingsDefinition
        {
            [XmlArray("Games"), XmlArrayItem("Game", typeof(GameSettingsDefinition))]
            public GameSettingsDefinition[] m_Settings;
            public bool m_valid;
        }

        #region Data
        public SettingsDefinition m_Settings;
        public String m_GameName;
        private BindingList<BotClass> m_BotList;
        private List<int> m_lFreeBotIDList;
        private int m_iMaxActiveBotIdCounter;
        private int m_iTotalNewInstanceCounter;
        private String m_sAppPath;
        private String m_sAppArgs;
        private int m_iMaxDataPrefix;
        private int m_iNextSim;
        private int m_iNextDataPrefix;
        private Form1 m_pParentForm;
        private Random m_oRandom;

        private BindingList<BotStyleRequired> m_BotStyleRequired;
        private BindingList<BotStyleItem> m_BotStyleList;
        private BindingList<BotStatusItem> m_BotStatusList;
        private BindingList<BotStatusItem> m_BotMetricList;
        private BindingList<BotStatusItem> m_BotGlobalStatusCountList;
        private int[] m_aiTotalStateCount;

        public int TotalCreated { get; set; }
        public int TotalCrashed { get; set; }
        public int TotalKilled { get; set; }

        #endregion // Data

        public BotManager(Form1 a_pParentForm)
        {
            m_BotList = new BindingList<BotClass>();
            m_lFreeBotIDList = new List<int>();
            m_oRandom = new Random();
            m_iMaxDataPrefix = -1;
            m_iNextDataPrefix = -1;
            m_iMaxActiveBotIdCounter = 0;
            m_iTotalNewInstanceCounter = 0;
            m_sAppPath = "";
            m_sAppArgs = "";
            m_iNextSim = 0;
            m_pParentForm = a_pParentForm;

            m_aiTotalStateCount = new int[(int)BotState.BotState_Max];
            m_BotStyleRequired = new BindingList<BotStyleRequired>();
            m_BotStyleList = new BindingList<BotStyleItem>();
            m_BotStatusList = new BindingList<BotStatusItem>();
            m_BotMetricList = new BindingList<BotStatusItem>();
            m_BotGlobalStatusCountList = new BindingList<BotStatusItem>();
            m_oRandom = new Random();
            TotalCreated = 0;
            TotalCrashed = 0;
            TotalKilled = 0;

            m_GameName = "";
            ParseSettings("GameBots.settings");
            UpdateGameStyles();
        }

        public void UpdateGameStyles()
        {
            m_BotStyleRequired.Clear();
            m_BotStyleList.Clear();

            GameSettingsDefinition settings = GetGamesSettings();
            if (settings != null)
            {
                String[] a_sBotStyles = settings.m_BotStyles;
                for (int i = 0; i < a_sBotStyles.Count(); ++i)
                {
                    m_BotStyleRequired.Add(new BotStyleRequired(a_sBotStyles[i], i, 0));
                    m_BotStyleList.Add(new BotStyleItem(a_sBotStyles[i], i));
                }
            }
        }

        #region Misc
        public void UpdateFromCommandLine(String[] args)
        {
            for (int i = 0; i < args.Length; i++)
            {
                if (0 == String.Compare(args[i], "--botpair", true) && ((i + 2) < args.Length))
                {
                    int iStyle = 0;
                    int iCount = 0;
                    bool bOkStyle = Int32.TryParse(args[i + 1], out iStyle);
                    bool bOkCount = Int32.TryParse(args[i + 2], out iCount);
                    
                    if (bOkStyle && bOkCount)
                    {
                        SetBotPair(iStyle, iCount);
                    }
                    i += 2;
                }
            }
        }

        public void SetBotPair(int a_iStyle, int a_iCount)
        {
            if (a_iStyle >= 0 && a_iStyle < m_BotStyleRequired.Count() && a_iCount >= 0)
            {
                m_BotStyleRequired[a_iStyle].RequiredCount = a_iCount;
            }
        }

        public void ParseSettings(string settings)
        {
            try
            {
                using (System.IO.TextReader reader = new System.IO.StreamReader(settings))
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(SettingsDefinition));
                    m_Settings = serializer.Deserialize(reader) as SettingsDefinition;
                    m_Settings.m_valid = true;
                    reader.Close();
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(
                  "Couldn't load '" + settings + "':\r\n" + e.Message,
                  "Error",
                  MessageBoxButtons.OK,
                  MessageBoxIcon.Error);
                m_Settings.m_valid = false;
            }
        }

        public GameSettingsDefinition GetGamesSettings()
        {
            if (m_Settings.m_valid)
            {
                for (int i = 0; i < m_Settings.m_Settings.Count(); ++i)
                {
                    if (m_Settings.m_Settings[i].m_GameName == m_GameName)
                    {
                        return m_Settings.m_Settings[i];
                    }
                }
            }
            return null;
        }

        public String[] GetGamesList()
        {
            String[] aList = {};
            if (m_Settings.m_valid)
            {
                aList = new String[m_Settings.m_Settings.Count()];
                for (int i = 0; i < m_Settings.m_Settings.Count(); ++i)
                {
                    aList.SetValue(m_Settings.m_Settings[i].m_GameName, i);
                }
            }            
            return aList;
        }

        public String GetArgs(string a_sArgs, int a_iDataPrefix, int a_iNextBotid, int a_iSimId, string a_sAppPath)
        {
            String sTempArg = a_sArgs;
            if (a_iDataPrefix >= 0)
            {
                sTempArg = sTempArg.Replace("\\data\\", "\\" + a_iDataPrefix.ToString() + "data\\");
                sTempArg = sTempArg.Replace("\\iosdata", "\\" + a_iDataPrefix.ToString() + "iosdata");
            }

            GameSettingsDefinition settings = GetGamesSettings();
            if (settings != null)
            {
                String sBotScript = settings.m_AutobotScript;
                try
                {
                    sBotScript = "\"" + System.IO.Path.GetDirectoryName(a_sAppPath) + "\\" + sBotScript + "\"";
                }
                catch
                {
                }

                if (settings.m_BotStyleArgPassingMethod == "PassByIndex")
                {
                    sTempArg += " " + String.Format(settings.m_BotStyleArg, a_iSimId);
                }
                else
                {
                    Debug.Assert(settings.m_BotStyleArgPassingMethod == "PassByString");
                    sTempArg += " " + String.Format(settings.m_BotStyleArg, m_BotStyleList[a_iSimId].SimName);
                }

                sTempArg += " " + String.Format(settings.m_BotIdentifierArg, m_pParentForm.m_TcpClient.MachineShortID, a_iNextBotid);
                sTempArg += " " + String.Format(settings.m_BotScriptArg, sBotScript);
            }

            return sTempArg;
        }

        public String GetPreviewArgs(int a_botStyle)
        {
            int iNextBotiD = PeekFreeBotiD();
            return GetArgs(m_sAppArgs, m_iNextDataPrefix, iNextBotiD, a_botStyle, m_sAppPath);
        }
        #endregion

        #region Bot Statistitics
        public int GetStateTotalCount(BotState a_eState)
        {
            return m_aiTotalStateCount[(int)a_eState];
        }

        public void BotStateChanged(BotClass aBot, BotState a_eOldState)
        {
            BotState a_eNewState = aBot.GetBotState();
            if (BotState.BotState_Invalid != a_eOldState)
            {
                m_BotStyleList[aBot.BotLogicID].ReduceStateType(a_eOldState);
                --m_aiTotalStateCount[(int)a_eOldState];
            }

            if (BotState.BotState_Invalid != a_eNewState)
            {
                m_BotStyleList[aBot.BotLogicID].AddStateType(a_eNewState);
                ++m_aiTotalStateCount[(int)a_eNewState];
            }
        }

        public void BotMetricEncountered(BotClass aBot, String a_sMetric)
        {
            foreach (var vMetric in m_BotMetricList)
            {
                if (vMetric.Status.Equals(a_sMetric))
                {
                    vMetric.Increment();
                    return;
                }
            }

            m_BotMetricList.Add(new BotStatusItem(a_sMetric, 1));
        }

        public void BotLuaStatusChanged(BotClass aBot, String a_sOldStatus)
        {
            foreach (var vStatus in m_BotStatusList)
            {
                if (vStatus.Status.Equals(a_sOldStatus))
                {
                    vStatus.Decrement();
                    if (0 == vStatus.CurrentCount)
                    {
                        m_BotStatusList.Remove(vStatus);
                    }
                    break;
                }
            }

            String sNewStatus = aBot.GetLuaBotState();

            if(sNewStatus.Length > 0)
            {
                bool bFound = false;

                foreach (var vStatus in m_BotGlobalStatusCountList)
                {
                    if (vStatus.Status.Equals(sNewStatus))
                    {
                        vStatus.Increment();
                        bFound = true;
                        break;
                    }
                }

                if (!bFound)
                {
                    m_BotGlobalStatusCountList.Add(new BotStatusItem(sNewStatus, 1));
                }

                foreach (var vStatus in m_BotStatusList)
                {
                    if (vStatus.Status.Equals(sNewStatus))
                    {
                        vStatus.Increment();
                        return;
                    }
                }

                m_BotStatusList.Add(new BotStatusItem(sNewStatus, 1));
            }
        }

        public int GetTotalRequiredCount()
        {
            int iTotal = 0;
            for (int i = 0; i < m_BotStyleRequired.Count(); ++i)
            {
                iTotal += m_BotStyleRequired[i].RequiredCount;
            }
            return iTotal;
        }

        public int GetNextRandomSimToLaunch()
        {
            List<KeyValuePair<int, int>> oKList = new List<KeyValuePair<int, int>>();
            int iCounter = 0;
            //assert(m_BotStyleRequired.Count()==m_BotStyleList.Count());
            for (int i = 0; i < m_BotStyleRequired.Count(); ++i)
            {
                int iNewRequired = m_BotStyleRequired[i].RequiredCount - m_BotStyleList[i].GetActiveCount();
                if (iNewRequired > 0)
                {
                    iCounter += iNewRequired;
                    oKList.Add(new KeyValuePair<int, int>(i, iCounter));
                }
            }

            int iRandom = m_oRandom.Next(iCounter);

            foreach (var oPair in oKList)
            {
                if (iRandom < oPair.Value)
                {
                    return oPair.Key;
                }
            }

            return 0;
        }
        #endregion

        #region Bot management
        public void Reset()
        {
            KillBots(m_BotList, true);

            m_BotList.Clear();
            m_lFreeBotIDList.Clear();
            m_iMaxActiveBotIdCounter = 0;
            m_iTotalNewInstanceCounter = 0;
            m_iNextSim = 0;

            m_aiTotalStateCount = new int[(int)BotState.BotState_Max];
            m_BotStatusList = new BindingList<BotStatusItem>();
            m_BotMetricList = new BindingList<BotStatusItem>();
            m_BotGlobalStatusCountList = new BindingList<BotStatusItem>();

            foreach(var oitem in m_BotStyleList)
            {
                oitem.Reset();
            }

            TotalCreated = 0;
            TotalCrashed = 0;
            TotalKilled = 0;
        }

        public void UpdateArguments(String sAppapth, String sCommandArgs, int a_iDataPrefix)
        {
            m_sAppPath = sAppapth;
            m_sAppArgs = sCommandArgs;
            m_iMaxDataPrefix = a_iDataPrefix;
            if(m_iMaxDataPrefix < 0)
            {
                m_iNextDataPrefix = -1;
            }
            else if (m_iNextDataPrefix < 0 || m_iNextDataPrefix > m_iMaxDataPrefix)
            {
                m_iNextDataPrefix = 0;
            }
        }

        public int PeekFreeBotiD()
        {
            int iBotId = m_iMaxActiveBotIdCounter;
            if (m_lFreeBotIDList.Count > 0)
            {
                iBotId = m_lFreeBotIDList[0];
            }
            return iBotId;
        }

        private void TakeBotiD(int a_iID)
        {
            m_lFreeBotIDList.Add(a_iID);
        }

        private int GetFreeBotiD()
        {
            int iBotId = m_iMaxActiveBotIdCounter;
            if (m_lFreeBotIDList.Count > 0)
            {
                iBotId = m_lFreeBotIDList[0];
                m_lFreeBotIDList.RemoveAt(0);
            }
            else
            {
                ++m_iMaxActiveBotIdCounter;
            }
            return iBotId;
        }

        public void CreateBotofType(int a_iSim)
        {
            int iNextBotiD = GetFreeBotiD();
            string sArgs = GetArgs(m_sAppArgs, m_iNextDataPrefix, iNextBotiD, a_iSim, m_sAppPath);

            BotClass oBot = new BotClass(m_iTotalNewInstanceCounter++
                                        , iNextBotiD
                                        , m_sAppPath
                                        , m_BotStyleList[a_iSim].SimName
                                        , a_iSim
                                        , sArgs
                                        , m_pParentForm
                                        , this
                                        );


            m_BotList.Add(oBot);

            if (m_iNextDataPrefix >= 0)
            {
                ++m_iNextDataPrefix;
            }


            m_BotStyleList[a_iSim].AddStateType(BotState.BotState_IDLE);
            ++m_aiTotalStateCount[(int)BotState.BotState_IDLE];
        }

        public void BotDied(BotClass aBot)
        {
            if (aBot.GetBotState() != BotState.BotState_Dying)
            {
                m_BotStyleList[aBot.BotLogicID].AddCrashed();
                ++TotalCrashed;
            }
            else
            {
                m_BotStyleList[aBot.BotLogicID].AddKill();
                ++TotalKilled;
            }

            aBot.SetLuaBotState(""); // this should update our live statistics
            aBot.SetBotState(BotState.BotState_Invalid); // this should update our live statistics

            TakeBotiD(aBot.BotId);
            m_BotList.Remove(aBot);
        }

        public int GetBotCount()
        {
            return m_BotList.Count();
        }

        public void KillBots(BindingList<BotClass> lSelectedItems, bool a_bForce)
        {
            List<BotClass> lBotstoPrune = new List<BotClass>();
            for (int i = 0; i < lSelectedItems.Count(); ++i)
            {
                BotClass oBot = lSelectedItems[i];
                if (oBot.GetBotState() == BotState.BotState_IDLE)
                {
                    oBot.SetBotState(BotState.BotState_Invalid);
                    lBotstoPrune.Add(oBot);
                }
                else if (oBot.GetBotState() == BotState.BotState_Running || oBot.GetBotState() == BotState.BotState_Dying)
                {
                    if(a_bForce)
                    {
                        oBot.ForceCancel();
                    }
                    else
                    {
                        oBot.Cancel();
                    }
                }
            }

            foreach (var aBot in lBotstoPrune)
            {
                m_BotList.Remove(aBot);
            }

        }

        public void KillAllBots()
        {
            KillBots(m_BotList, false);
        }

        public int KillRandomRunningBots(int a_iMaxKillThisFrame)
        {
            if (a_iMaxKillThisFrame <= 0)
            {
                return 0;
            }
            int iKillThisFrame = m_oRandom.Next(a_iMaxKillThisFrame + 1);
            int iTotalKilled = 0;
            while (iKillThisFrame > 0)
            {
                int iBottoKill = m_oRandom.Next(iKillThisFrame);
                for (int i = 0; i < m_BotList.Count; ++i)
                {
                    if (m_BotList[i].GetBotState() == BotState.BotState_Running)
                    {
                        iBottoKill--;
                        if (iBottoKill < 0)
                        {
                            m_BotList[i].Cancel();
                            ++iTotalKilled;
                            break;
                        }
                    }
                }
                --iKillThisFrame;
            }

            return iTotalKilled;
        }

        public BotClass StartNextBot()
        {
            for (int i = 0; i < m_BotList.Count; ++i)
            {
                if (m_BotList[i].GetBotState() == BotState.BotState_IDLE)
                {
                    m_BotList[i].Start();

                    m_BotStyleList[m_BotList[i].BotLogicID].AddCreated();
                    ++TotalCreated;
                    return m_BotList[i];
                }
            }
            return null;
        }
        #endregion // Bot management

        #region Ui Interface Helper
        public void BindingRequiredItemsUi(DataGridView a_dgStyleRequired)
        {
            a_dgStyleRequired.DataSource = m_BotStyleRequired;
            a_dgStyleRequired.Columns["RequiredCount"].DefaultCellStyle.ForeColor = Color.Blue;
        }

        public void BindingBotList(DataGridView a_dgBots)
        {
            a_dgBots.DataSource = null;
            a_dgBots.DataSource = m_BotList;
        }

        public void RefreshStatisticsUiBindings(DataGridView a_dgStyle, DataGridView a_dgStatus, DataGridView a_dgGlobalStatus, DataGridView a_dgGlobalMetrics, DataGridView a_dgBots)
        {
            //-----------------
            a_dgStyle.DataSource = null;
            a_dgStyle.DataSource = new List<BotStyleItem>(m_BotStyleList);
            a_dgStyle.Columns["TotalCrashed"].DefaultCellStyle.ForeColor = Color.Red;
            //-----------------
            a_dgStatus.DataSource = null;
            a_dgStatus.DataSource = new List<BotStatusItem>(m_BotStatusList);
            //-----------------
            a_dgGlobalStatus.DataSource = null;
            a_dgGlobalStatus.DataSource = new List<BotStatusItem>(m_BotGlobalStatusCountList);
            //-----------------
             a_dgGlobalMetrics.DataSource = null;
             a_dgGlobalMetrics.DataSource = new List<BotStatusItem>(m_BotMetricList);
            //-----------------
        }

        public bool IsNumericColumn(DataGridView a_dg)
        {
            return (a_dg.CurrentCell.OwningColumn == a_dg.Columns["RequiredCount"]);
        }
        #endregion Ui Interface Helper

        #region Summary
        public String GetSummary()
        {
            String sData = "";
            foreach(var oItem in m_BotStyleRequired)
            {
                if(sData.Length > 0)
                {
                    sData = sData + ",";
                }
                sData = sData + "Required_" +oItem.SimName + ":" + oItem.RequiredCount.ToString();
            }

            foreach(var oItem in m_BotStyleList)
            {
                if (sData.Length > 0)
                {
                    sData = sData + ",";
                }
                sData = sData + "Total_" + oItem.SimName + "_Created:" + oItem.CreatedCount.ToString()
                              + ",Total_" + oItem.SimName + "_Crashed:" + oItem.TotalCrashed.ToString()
                              + ",Total_" + oItem.SimName + "_Killed:" + oItem.TotalKilled.ToString()
                              + ",Total_" + oItem.SimName + "_Idle:" + oItem.Active_IDLE.ToString()
                              + ",Total_" + oItem.SimName + "_Running:" + oItem.Active_Running.ToString()
                              + ",Total_" + oItem.SimName + "_Dying:" + oItem.Active_Dying.ToString();
            }

            foreach(var oItem in m_BotStatusList)
            {
                if (sData.Length > 0)
                {
                    sData = sData + ",";
                }
                sData = sData + "Active_Status_" + oItem.Status + ":" + oItem.CurrentCount.ToString();
            }

            foreach (var oItem in m_BotMetricList)
            {
                if (sData.Length > 0)
                {
                    sData = sData + ",";
                }
                sData = sData + "Metric_" + oItem.Status + ":" + oItem.CurrentCount.ToString();
            }

            foreach(var oItem in m_BotGlobalStatusCountList)
            {
                if (sData.Length > 0)
                {
                    sData = sData + ",";
                }
                sData = sData + "Global_Status_" + oItem.Status + ":" + oItem.CurrentCount.ToString();
            }

            return sData;
        }
        #endregion // Summary
    }
}