using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.Serialization.Formatters.Binary;
using battle;

namespace battle
{
    public class classBattleField : Panel
    {
        #region "objects"
        Form callingForm;
        public classBattle cLibBattle;
        public System.Windows.Forms.PictureBox picMap;
        public Bitmap bm_Map_Original;

        public Label lblFeedback = new Label();
        public Label lblNumbers;

        VScrollBar VSB;
        HScrollBar HSB;
        System.Windows.Forms.Timer tmrSizechange = new System.Windows.Forms.Timer();
        public System.Windows.Forms.Timer tmrAnimateUnit = new System.Windows.Forms.Timer();
        System.Windows.Forms.Timer tmrFeedback = new System.Windows.Forms.Timer();
        System.Windows.Forms.Timer tmrAnimateAttack = new System.Windows.Forms.Timer();
        public System.Windows.Forms.Button btnEndTurn;
        
        Point udrMouseHoverLoc = new Point();

        // debug start
        Label[] lblDebug;
        public System.Windows.Forms.Button btnDebug;
        TextBox txtDebug;
        // debug end

        Graphics gr;
        #endregion

        #region "variables"
        const int intVerticalOffset = 35;
        int intStandardCartesianStringFieldLength = 3;
        public Point udrCastleLoc = new Point();

        public classBattleUnit[] Unit_Q = new classBattleUnit[2];  // 2 Qs holding both red/blue units on this battlefield
        Semaphore unitQ_Semaphore = new Semaphore(0, 1);            // used in the enQ/deQ functions
        public classBattle.classTerrain[,] terrain;                   // 2d map of the terrain
        public Size szMapSize = new Size(10,10);
        public int intMapGridSize = 55;                             // pixel size of each map square as they appear on the screen

        //public classLib.classLib cLib = new classLib.classLib();

        public Random rnd = new Random();
        public classBattle.enuUnitColors TurnColor;                 // alternates between red/blue after endTurn()

        public classBattle.enuColorControlledBy[] ColorControl = new classBattle.enuColorControlledBy[2]; // each color can be controlled by either computer or player

        public enuAIStrategies strategyRed;
        public enuAIStrategies strategyBlue;

        public classArmyStats[] udrArmyStats = new classArmyStats[2];   // used to evaluate each color's strengths for AI comparison
        public classPOCSearchInfo[] udrPOC = new classPOCSearchInfo[2];

        public classBattleUnit AIControlledUnit;                    // pointer to unit currently under computer-AI control
        public classBattleUnit PlyerSelectedUnit;                   // pointer to unit currently controlled by player
        public classBattleUnit unit_Animated;                       // pointer to unit being animated
        public classBattleUnit hoverOverUnit;                       // pointer to unit under the mouse cursor

        public Point udrPlyrSelectedMoveMapSquare; // cartesian var identifying terrain index where player wants to move (before move is confirmed then at move)
        public bool[,] bolPlyrControlledTargetMap;                  // 2d boolean array of terrain location where the player can attack, from current location or during interface from projected move location
        public short[,] shoPOCMap;                                  // 2d short int array describing location of Points-of-Convergence used by AI for defensive positioning
        const int intScrollBarMax = 1000;

        // animation variables
        public classAnimation udrAnimation = new classAnimation();      // information for the animation functions to use in order to display animation correctly
        public static int intInterval = 10;
        public static int intCostAttack = 25;                       // unit's intMovementPoint deduction when attacking
        public static int intCostCounterAttack = 5;             // unit's intMovmentPoint deduction when counter-attacking
        public static int intCostCharge = 80;
        public bool bolKillBattle = false;
        public classOpenFieldDefenseInfo udrCDInfo = new classOpenFieldDefenseInfo();
        Point[] udrBlueLaunchLocs = new Point[(int)classBattle.intMaxInvasionForce];
        classBattle.classRedLaunchLocations udrRedLaunchLocs = new classBattle.classRedLaunchLocations();
        bool bolIntroShown = false;
        #endregion

        #region "enumerated types"
        public enum enuOpenFieldDefenseSearchTree_Command { GoToPriorityPos = 0, VacatePriorityPos }

        public enum enuUnitAnimationType
        {
            move = 0,
            attack,
            _numUnitAnimationTypes
        }

        public enum enuAttackAnimationPhase
        {
            init,
            showAttacker,
            animateBarrage,
            animateDefender,
            animateAttacker,
            animateBelligerents,
            _numAttackAnimatinoPhases
        }

        public enum enuAIStrategies
        {
            retreat,
            harass,
            defend_castle,
            hold_POCs,
            initDefense,
            _numRedAIStrategies
        }

        #endregion

        #region "structures"
        public class classAnimation
        {
            public bool flag;                                       // 
            public classBattleUnit unit;
            public enuUnitAnimationType typeAnimation;
            public enuAttackAnimationPhase animation_AttackPhase;
            public int intAnimationCounter;
        }

        public class classArmyStats
        {
            public classBattleUnit[] units;
            public classUnitStats total = new classUnitStats();
        }

        public class classUnitStats
        {
            public classTypeStat melee = new classTypeStat();
            public classTypeStat range = new classTypeStat();
        }

        public class classTypeStat
        {
            public double defense;
            public double offense;
        }

        public class classAIShortestPathToEnemy          // used in find-POC get shortestpath to ALL enemies as a return array
        {
            public string path = "";
            public classBattleUnit unit;
            public bool flag;
            public Point udrLoc;
            public int rating;
            public int intTotalMovementCost;
        }

        public class classTravelCost_Array
        {
            public classTravelCost[] Array;
            public Point udrLoc_Start;
        }

        public class classTravelCost
        {
            public Point udrLoc_End;
            public int intMovementPoints;
            public string path;
        }

        public class classPOCSearchInfo
        {
            public bool[,] bolAvoidMap;
            public short[,] POCMap;
            public classBattleUnit unit;
            public short shrNumFound;
            public classPOCLocs[] POCLocs;
            public int intDefensiveStrength;
        }

        public class classPOCLocs
        {
            public Point pt;
            public bool flag;
        }

        public class classOpenFieldDefenseInfo
        {
            public classOpenFieldDefense_MapUnit[,] deploymentMap;
            public bool[,] bolAvoidMap;
            public bool[,] bolFreeTravelZone;
            public classOpenFieldDefense_Position[] Pos;
            public string strMoveSequence;
            public bool bolInitialMovesComplete;
        }

        public enum enuOpenFieldDefensePosition_Status { empty, occupied, safe };

        public class classOpenFieldDefense_Position
        {
            public Point udrLoc;
            public classBattle.enuUnitTypes typeUnit;
            public enuOpenFieldDefensePosition_Status status;
            public bool bolSearchComplete;
            public classBattleUnit unit;
            public classOpenFieldDefenseSearch_UnitInfo[] suitableUnitsThatCanReachThisPos;
        }

        public class classOpenFieldDefense_MapUnit
        {
            public classBattle.enuUnitTypes typeUnit;
            public classBattleUnit unitThere;
        }


        public class classOpenFieldDefenseSearch_Result
        {
            public int intNumberPosLeftUnsolved;
            public string strMoves;
            public int intTotalMovementPointsLeft;
        }

        #endregion

        public classBattleField(classBattle _cLibBattle, Form _callingForm)
        {
            cLibBattle = _cLibBattle;
            callingForm = _callingForm;

            Controls.Add(lblFeedback);
            lblFeedback.AutoSize = true;

            initPanel();
        }
    
        void initPanel()
        {
            try
            { unitQ_Semaphore.Release(); }
            catch (Exception) { }
            BackColor = Color.Green;
            SizeChanged += new EventHandler(classBattleFieldMapSizeChanged);

            picMap = new PictureBox();
            VSB = new VScrollBar();
            HSB = new HScrollBar();
            btnEndTurn = new Button();
            btnDebug = new Button();
            txtDebug = new TextBox();

            lblNumbers = new Label();
            Controls.Add(lblNumbers);
            lblNumbers.Visible = true;
            lblNumbers.Left = -1000;

            /* debug button start 
            Controls.Add(btnDebug);
            btnDebug.AutoSize = true;
            btnDebug.Text = "debug";
            btnDebug.Click += new EventHandler(btnDebug_Click);
            /*  debug button end */

            Controls.Add(btnEndTurn);
            btnEndTurn.AutoSize = true;
            btnEndTurn.Text = "&End Turn";
            btnEndTurn.Click += new EventHandler(btnEndTurn_Click);

            Width = Screen.PrimaryScreen.WorkingArea.Width;
            Height = Screen.PrimaryScreen.WorkingArea.Height;
            tmrSizechange.Tick += new EventHandler(tmrSizechange_Tick);

            Control_Blue = classBattle.enuColorControlledBy.computer;
            Control_Red = classBattle.enuColorControlledBy.computer;

            tmrAnimateUnit.Interval = 250;
            tmrAnimateUnit.Tick += new EventHandler(tmrAnimateUnit_Tick);
            Controls.Add(txtDebug);
            txtDebug.Dock = DockStyle.Right;
            txtDebug.Width = 200;
            txtDebug.Click += new EventHandler(txtDebug_Click);

            Controls.Add(VSB);
            Controls.Add(HSB);
            Controls.Add(picMap);
            picMap.MouseMove += new MouseEventHandler(picMap_MouseMove);
            picMap.MouseClick += new MouseEventHandler(picMap_MouseClick);

            tmrAnimateAttack.Tick += new EventHandler(tmrAnimateAttack_Tick);
            tmrAnimateAttack.Interval = intInterval;

            HSB.Minimum = VSB.Minimum = 0;
            HSB.Maximum = VSB.Maximum = intScrollBarMax;

            VSB.ValueChanged += new EventHandler(VSB_ValueChanged);
            HSB.ValueChanged += new EventHandler(HSB_ValueChanged);
            if (terrain != null)
                displayMap();

            picMap.BackColor = cLibBattle.mainForm.BackColor;
            picMap.Visible = true;
            VSB.Visible = true;
            VSB.BringToFront();
            HSB.Visible = true;
            HSB.BringToFront();
            Visible = true;
            Disposed += new EventHandler(classBattleField_Disposed);
            for (int intColorCounter = 0; intColorCounter < 2; intColorCounter++)
                if (Unit_Q[intColorCounter] != null)
                    initDisposedUnitsOntoField(ref Unit_Q[intColorCounter]);

            tmrFeedback.Interval = 5000;
            tmrFeedback.Tick += new EventHandler(tmrFeedback_Tick);

  
            Visible = true;
        }

        #region "debug extras"

        void clearOldDebugLabels()
        {
            if (lblDebug != null)
            {
                for (int intLabelCounter = 0; intLabelCounter < classBattle.intMaxInvasionForce; intLabelCounter++)
                    if (lblDebug[intLabelCounter] != null)
                        lblDebug[intLabelCounter].Visible = false;
            }
        }

        void highlightPOC(classPOCSearchInfo udrPOC)
        {
            if (lblDebug == null)
                lblDebug = new Label[classBattle.intMaxInvasionForce];
            for (int intPOCLocCounter = 0; intPOCLocCounter < classBattle.intMaxInvasionForce; intPOCLocCounter++)
            {
                if (udrPOC.POCLocs != null && intPOCLocCounter < udrPOC.POCLocs.Length)
                {
                    if (lblDebug[intPOCLocCounter] == null)
                    {
                        Label lbl = new Label();
                         lbl.AutoSize = true;
                        picMap.Controls.Add(lbl);
                        lblDebug[intPOCLocCounter] = lbl;
                    }
                    lblDebug[intPOCLocCounter].Text = "POC";
                    lblDebug[intPOCLocCounter].Top = getGridMapObjectTop(udrPOC.POCLocs[intPOCLocCounter].pt);
                    lblDebug[intPOCLocCounter].Left = getGridMapObjectLeft(udrPOC.POCLocs[intPOCLocCounter].pt);
                    lblDebug[intPOCLocCounter].BackColor = (udrPOC.POCLocs[intPOCLocCounter].flag ? Color.Red : Color.Yellow);
                    lblDebug[intPOCLocCounter].Visible = true;

                }
                else
                {
                    if (lblDebug[intPOCLocCounter] != null)
                        lblDebug[intPOCLocCounter].Visible = false;
                }
            }
        }

        void highlightOpenFieldDefenseDeploymentMap(classOpenFieldDefenseInfo udrOFDInfo)
        {
            if (lblDebug == null)
                lblDebug = new Label[classBattle.intMaxInvasionForce];
            if (lblDebug.Length < udrOFDInfo.Pos.Length)
                Array.Resize<Label>(ref lblDebug, udrOFDInfo.Pos.Length);

            int intLabelCounter = 0;
            for (int intOFDPosCounter = 0; intOFDPosCounter < udrOFDInfo.Pos.Length; intOFDPosCounter++)
            {
                if (lblDebug[intLabelCounter] == null)
                {
                    Label lbl = new Label();
                    lbl.AutoSize = true;
                    picMap.Controls.Add(lbl);
                    lblDebug[intLabelCounter] = lbl;
                }

                lblDebug[intLabelCounter].Text = udrOFDInfo.Pos[intOFDPosCounter].typeUnit.ToString().ToUpper().Substring(0, 1);
                lblDebug[intLabelCounter].Top = getGridMapObjectTop(udrOFDInfo.Pos[intOFDPosCounter].udrLoc);
                lblDebug[intLabelCounter].Left = getGridMapObjectLeft(udrOFDInfo.Pos[intOFDPosCounter].udrLoc);

                switch (udrOFDInfo.Pos[intOFDPosCounter].status)
                {
                    case enuOpenFieldDefensePosition_Status.empty:
                        lblDebug[intLabelCounter].BackColor = Color.Yellow;
                        break;

                    case enuOpenFieldDefensePosition_Status.occupied:
                        lblDebug[intLabelCounter].BackColor = Color.Red;
                        break;

                    case enuOpenFieldDefensePosition_Status.safe:
                        lblDebug[intLabelCounter].BackColor = Color.Green;
                        break;
                }
                lblDebug[intLabelCounter].Visible = true;
                intLabelCounter++;
            }

            for (int intEraseLabelCounter = intLabelCounter; intEraseLabelCounter < lblDebug.Length; intEraseLabelCounter++)
            {
                if (lblDebug[intEraseLabelCounter] != null)
                    lblDebug[intEraseLabelCounter].Visible = false;
            }
        }

        #endregion

        #region "resize and interface"
        public void launchBattle()
        {
            PlaceMap();
            TurnColor = classBattle.enuUnitColors.blue;
            startTurn();
        }

        public void killBattle()
        {
            bolKillBattle = true;
            tmrSizechange.Enabled = false;
            tmrAnimateUnit.Enabled = false;
            tmrAnimateAttack.Enabled = false;
        }

        void initDisposedUnitsOntoField(ref classBattleUnit thisQ)
        {
            classBattleUnit thisUnit = thisQ;
            while (thisUnit != null)
            {
                thisUnit.initPics();
                thisUnit = thisUnit.next;
            }
        }

        void txtDebug_Click(object sender, EventArgs e)
        {
            txtDebug.Multiline = !txtDebug.Multiline;
        }

        void picMap_MouseClick(object sender, MouseEventArgs e)
        {
            lblFeedback.Visible = false;
            if (ColorControl[(int)TurnColor] == classBattle.enuColorControlledBy.player)
            {
                if (terrain[udrMouseHoverLoc.X, udrMouseHoverLoc.Y].unit != null)
                {
                    if (terrain[udrMouseHoverLoc.X, udrMouseHoverLoc.Y].unit.myClr == TurnColor)
                    {
                        unitPic_MouseClick(sender, e);
                        return;
                    }
                    else if (PlyerSelectedUnit != null
                             && PlyerSelectedUnit.myStage == classBattleUnit.enuUnitStage.showAllowedMoves)
                    {
                        unitPic_MouseClick(sender, e);
                        return;
                    }
                }
                if (PlyerSelectedUnit != null)
                {
                    bool[,] bolMoveMap = PlyerSelectedUnit.getAllowedMovesMap();
                    if (bolMoveMap[udrMouseHoverLoc.X, udrMouseHoverLoc.Y])
                    {
                        if (PlyerSelectedUnit.myStage == classBattleUnit.enuUnitStage.showAllowedMoves)
                        {
                            PlyrMove_ShowShortestPath(udrMouseHoverLoc);
                        }
                        else if (PlyerSelectedUnit.myStage == classBattleUnit.enuUnitStage.showSelectedPath)
                        {
                            if (udrPlyrSelectedMoveMapSquare.X == udrMouseHoverLoc.X
                                 && udrPlyrSelectedMoveMapSquare.Y == udrMouseHoverLoc.Y)
                            {
                                moveToTarget(ref PlyerSelectedUnit, udrMouseHoverLoc);
                                unshadeMap();
                            }
                            else
                            {
                                PlyrMove_ShowShortestPath(udrMouseHoverLoc);
                            }
                        }
                    }
                }
            }
        }

        void picMap_MouseMove(object sender, MouseEventArgs e)
        {
            Point udrLoc_New = determineMapGridLocByObjectLoc(e.X, e.Y);
            if (ptOnMap(udrLoc_New))
            {
                txtDebug.Text = (TurnColor == classBattle.enuUnitColors.blue ? strategyBlue.ToString() : strategyRed.ToString()) + "\r\n";
                txtDebug.Text += "(" + udrLoc_New.X.ToString() + ", " + udrLoc_New.Y.ToString() + ")"
                                + "\r\n" + terrain[udrLoc_New.X, udrLoc_New.Y].type.ToString()
                                + (terrain[udrLoc_New.X, udrLoc_New.Y].unit != null
                                    ?
                                    "\r\n" + terrain[udrLoc_New.X, udrLoc_New.Y].unit.myType.ToString()
                                    + "(" + terrain[udrLoc_New.X, udrLoc_New.Y].unit.intSize.ToString() + ")"
                                    + ", " + terrain[udrLoc_New.X, udrLoc_New.Y].unit.myRank.ToString()
                                    + (terrain[udrLoc_New.X, udrLoc_New.Y].unit.myType == classBattle.enuUnitTypes.artillery
                                            ?
                                            ", range : " + terrain[udrLoc_New.X, udrLoc_New.Y].unit.intRange.ToString()
                                            : "")
                                    + (terrain[udrLoc_New.X, udrLoc_New.Y].unit.terrain_next != null
                                        ?
                                        "\r\non top of : "
                                        + terrain[udrLoc_New.X, udrLoc_New.Y].unit.terrain_next.myType.ToString() + ", "
                                        + terrain[udrLoc_New.X, udrLoc_New.Y].unit.terrain_next.myRank.ToString() + ", "
                                        + terrain[udrLoc_New.X, udrLoc_New.Y].unit.terrain_next.intSize.ToString()
                                        :
                                        "")
                                    : "");
                if (ColorControl[(int)TurnColor] == classBattle.enuColorControlledBy.player)
                {
                    if (udrLoc_New.X != udrMouseHoverLoc.X
                        || udrLoc_New.Y != udrMouseHoverLoc.Y)
                    {
                        udrMouseHoverLoc = udrLoc_New;
                    }
                }
            }
        }

        void classBattleField_Disposed(object sender, EventArgs e)
        {
            initPanel();
        }

        void tmrSizechange_Tick(object sender, EventArgs e)
        {
            tmrSizechange.Enabled = false;
            setScrollBarLocations();
        }

        public void setScrollBarLocations()
        {
            VSB.Top = 0; VSB.Left = Width - VSB.Width - 15;
            VSB.Height = Height - VSB.Top - HSB.Height -35;
            VSB.Visible = true;
            VSB.BringToFront();

            HSB.Top = Height - HSB.Height-35; HSB.Left = 5;
            HSB.Width = VSB.Left - HSB.Left;
            HSB.Visible = true;
            HSB.BringToFront();
            resetScrollBars();
        }

        void pic_MouseLeave(object sender, EventArgs e)
        {
            hoverOverUnit = null;
            clearHoverOverUnitInfo();
        }

        void pic_MouseEnter(object sender, EventArgs e)
        {
            PictureBox pic = (PictureBox)sender;
            Point udrMapSquare = determineMapGridLocByObjectLoc(pic.Left, pic.Top);
            hoverOverUnit = terrain[udrMapSquare.X, udrMapSquare.Y].unit;
            showHoverOverUnitInfo();
        }

        void resetScrollBars()
        {
            // remember what was visible before size change
            Point udrVisiblePos = new Point();
            udrVisiblePos.X = -picMap.Left;
            udrVisiblePos.Y = -picMap.Top;

            Point udrVisibleMapSquare = determineMapGridLocByObjectLoc(udrVisiblePos);

            // adjust scroll bars to fit new size
            if (Width > picMap.Width)
                HSB.Visible = false;
            else
            {
                HSB.LargeChange = (int)(((double)Width / (double)picMap.Width) * intScrollBarMax / 2);
                HSB.Maximum = intScrollBarMax - HSB.LargeChange;
            }

            if (Height > picMap.Height)
                VSB.Visible = false;
            else
            {
                VSB.LargeChange = (int)(((double)Height / (double)picMap.Height) * intScrollBarMax / 2);
                VSB.Maximum = intScrollBarMax - VSB.LargeChange;
            }
            try
            {
                HSB.Value = (int)((double)udrVisibleMapSquare.X / (double)szMapSize.Width * HSB.Maximum);
                VSB.Value = (int)((double)udrVisibleMapSquare.Y / (double)szMapSize.Height * VSB.Maximum);
            }
            catch (Exception) { }
        }

        public void PlaceMap()
        {
            HSB_PlaceMap();
            VSB_PlaceMap();
        }

        void HSB_ValueChanged(object sender, EventArgs e) { HSB_PlaceMap(); }
        public void HSB_PlaceMap()
        {
            int intMaxLeft = picMap.Width - VSB.Left;
            double dblPercentageLeft = (double)HSB.Value / (double)(HSB.Maximum - HSB.LargeChange);
            picMap.Left = -(int)((double)intMaxLeft * dblPercentageLeft);
        }

        void VSB_ValueChanged(object sender, EventArgs e){VSB_PlaceMap();}
        public void VSB_PlaceMap()
        {
            int intMaxTop = picMap.Height - HSB.Top;
            double dblPercentageTop = (double)VSB.Value / (double)(VSB.Maximum - VSB.LargeChange);
            picMap.Top = -(int)((double)intMaxTop * dblPercentageTop);
        }

        void btnEndTurn_Click(object sender, EventArgs e)
        {
            endTurn();
        }

        public classBattle.enuColorControlledBy Control_Blue
        {
            set { ColorControl[(int)classBattle.enuUnitColors.blue] = value; }
            get { return ColorControl[(int)Control_Blue]; }
        }

        public classBattle.enuColorControlledBy Control_Red
        {
            set { ColorControl[(int)classBattle.enuUnitColors.red] = value; }
            get { return ColorControl[(int)Control_Red]; }
        }

        void btnDebug_Click(object sender, EventArgs e)
        {
            endTurn();
        }

        void classBattleFieldMapSizeChanged(object sender, EventArgs e)
        {
            Resize();
        }

        public void Resize()
        {
            tmrSizechange.Enabled = false;
            tmrSizechange.Interval = 100;
            if (!bolKillBattle)
                tmrSizechange.Enabled = true;
            placeFeedback();
        }

        void unitPic_MouseClick(object sender, MouseEventArgs e)
        {
            Point udrMapSquare = determineMapGridLocByObjectLoc(e.X, e.Y);

            if (ColorControl[(int)TurnColor] == classBattle.enuColorControlledBy.computer)
            {
                unshadeMap();
                return;
            }
            
            if (terrain[udrMapSquare.X, udrMapSquare.Y].unit != null)
                if (terrain[udrMapSquare.X, udrMapSquare.Y].unit.myClr == TurnColor)
                {
                    PlyerSelectedUnit = terrain[udrMapSquare.X, udrMapSquare.Y].unit;
                    PlyrMove_ShowAllowedMoves();
                }
                else
                {
                    if (PlyerSelectedUnit != null)
                        if (PlyerSelectedUnit.intMovementPoints >= intCostAttack
                            && bolPlyrControlledTargetMap[udrMapSquare.X, udrMapSquare.Y])
                            PlyrMove_AttackInterface(udrMapSquare);
                    PlyerSelectedUnit = null;
                    unshadeMap();
                }
        }

        void PlyrMove_AttackInterface(Point udrMapSquare)
        {
            PlyerSelectedUnit.unitIAmAttacking = terrain[udrMapSquare.X, udrMapSquare.Y].unit;
            PlyerSelectedUnit.udrAttackLoc = udrMapSquare;
            if (PlyerSelectedUnit.unitIAmAttacking != null)
            {
                classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults reply = classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults.cancelAttack;
                classPlyrAttackInterfaceMessageBox msgBox = new classPlyrAttackInterfaceMessageBox(PlyerSelectedUnit, PlyerSelectedUnit.unitIAmAttacking, reply);
                msgBox.Owner = cLibBattle.mainForm;
                msgBox.ShowDialog();
                reply = msgBox.Reply;
                msgBox.Dispose();

                switch (reply)
                {
                    case classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults.confirmNormalAttack:
                        PlyerSelectedUnit.attackType = classBattle.enuAttackTypes.normal;
                        break;

                    case classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults.confirmCharge:
                        PlyerSelectedUnit.attackType = classBattle.enuAttackTypes.charge;
                        break;

                    case classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults.confirmBarrageAttack:
                        PlyerSelectedUnit.attackType = classBattle.enuAttackTypes.barrage;
                        break;

                    case classPlyrAttackInterfaceMessageBox.enuPlyrAttackInterfaceResults.cancelAttack:
                        unshadeMap();
                        return;
                }
            }
            PlyerSelectedUnit.attack();
            unit_Animated = PlyerSelectedUnit;
            unshadeMap();
            udrAnimation.animation_AttackPhase = enuAttackAnimationPhase.init;
            launchAnimateAttackTimer();
        }
        #endregion

        #region "output display"
        public bool ptOnMap(Point udrLoc)
        {
            return (udrLoc.X >= 0
                    && udrLoc.X < szMapSize.Width
                    && udrLoc.Y >= 0
                    && udrLoc.Y < szMapSize.Height);
        }

        public void displayMapLoc(Point udrLoc, classBattle.enuTerrainImageSetting terrainImageSetting)
        { displayMapLoc(udrLoc, terrainImageSetting, false); }
        public void displayMapLoc(Point udrLoc, classBattle.enuTerrainImageSetting terrainImageSetting, bool bolForceResize)
        {
            if (ptOnMap(udrLoc))
            {
                Point pt = new Point(getGridMapObjectLeft(udrLoc.X), getGridMapObjectTop(udrLoc));
                displayMapLoc(pt, cLibBattle.getTerrainImage(terrain[udrLoc.X, udrLoc.Y].type, terrainImageSetting));
                if (terrain[udrLoc.X, udrLoc.Y].unit != null)
                    terrain[udrLoc.X, udrLoc.Y].unit.display(bolForceResize);
            }
        }

        public void displayMapLoc(Point pt, Image img)
        {
            using (Graphics g = Graphics.FromImage(picMap.Image))
            {
                g.DrawImage(img, pt.X, pt.Y, intMapGridSize, intMapGridSize);
            }
        }

        public void displayMap()
        { displayMap(false); }
        public void displayMap(bool bolForceResize)
        {
            if (szMapSize.Width == 0 || szMapSize.Height == 0)
                return;
            Bitmap entireMap = new Bitmap(szMapSize.Width * intMapGridSize, (int)((szMapSize.Height + .5) * intMapGridSize)); // make the bitmap large enough to contain all of your tiles.
            picMap.Image = entireMap;

            gr = Graphics.FromImage(entireMap);
            if (terrain != null)
                for (int intY = 0; intY < szMapSize.Height; intY++)
                {
                    for (int intX = 0; intX < szMapSize.Width; intX++)
                    {
                        displayMapLoc(cLibBattle.getPos(intX, intY), classBattle.enuTerrainImageSetting.normal, bolForceResize);
                    }
                }
            picMap.Width = szMapSize.Width * intMapGridSize;
            picMap.Height = (int)(((double)szMapSize.Height + .5) * intMapGridSize);

            displayUnitQ(ref  Unit_Q[(int)classBattle.enuUnitColors.blue]);
            displayUnitQ(ref Unit_Q[(int)classBattle.enuUnitColors.red]);

            picMap.Width = szMapSize.Width * intMapGridSize;
            picMap.Height = (int)(((double)szMapSize.Height + 0.5) * intMapGridSize);

            bm_Map_Original = new Bitmap(picMap.Image);
            picMap.Refresh();
            btnDebug.BringToFront();
        }

        void PlyrMove_ShowShortestPath(Point udrMapSquare)
        {
            shadeMap(PlyerSelectedUnit.getReachableMap(udrMapSquare));
            PlyerSelectedUnit.myStage = classBattleUnit.enuUnitStage.showSelectedPath;
            udrPlyrSelectedMoveMapSquare = udrMapSquare;
            PlyrMove_ShowAttackMap(udrMapSquare);
        }

        void PlyrMove_ShowAllowedMoves()
        {
            PlyerSelectedUnit.myStage = classBattleUnit.enuUnitStage.showAllowedMoves;
            if (!PlyerSelectedUnit.bolFullStop)
                shadeMap(PlyerSelectedUnit.getAllowedMovesMap());
            if (PlyerSelectedUnit.intMovementPoints >= intCostAttack)
                PlyrMove_ShowAttackMap(PlyerSelectedUnit.myPosition);
        }

        public void showTargetsOnMap(bool[,] bolTargetMap)
        {
            for (int intX = 0; intX < szMapSize.Width; intX++)
                for (int intY = 0; intY < szMapSize.Height; intY++)
                {
                    if (bolTargetMap[intX, intY]
                        && terrain[intX, intY].unit != null
                        && terrain[intX, intY].unit.myClr != PlyerSelectedUnit.myClr)
                        displayMapLoc(cLibBattle.getPos(intX, intY), classBattle.enuTerrainImageSetting.blackened);
                }
            picMap.Refresh();
        }

        public void shadeMap(bool[,] bolMap)
        {
            for (int intX = 0; intX < szMapSize.Width; intX++)
            {
                for (int intY = 0; intY < szMapSize.Height; intY++)
                {
                    displayMapLoc(cLibBattle.getPos(intX, intY), bolMap[intX, intY] ? classBattle.enuTerrainImageSetting.normal : classBattle.enuTerrainImageSetting.shaded);
                    if (terrain[intX, intY].unit != null)
                        terrain[intX, intY].unit.display();
                }
            }
            picMap.Refresh();
        }

        public void unshadeMap()
        {
            Image[] imgTerrain = { Battlefield_Simulator_2019.Resources.terrain_grass,
                                     Battlefield_Simulator_2019.Resources.terrain_forest,
                                     Battlefield_Simulator_2019.Resources.terrain_road,
                                     Battlefield_Simulator_2019.Resources.terrain_water,
                                     Battlefield_Simulator_2019.Resources.terrain_mountain ,
                                     Battlefield_Simulator_2019.Resources.terrain_castle };

            for (int intX = 0; intX < szMapSize.Width; intX++)
            {
                for (int intY = 0; intY < szMapSize.Height; intY++)
                {
                    displayMapLoc(cLibBattle.getPos(intX, intY), classBattle.enuTerrainImageSetting.normal);
                    if (terrain[intX, intY].unit != null)
                        terrain[intX, intY].unit.display();

                }
            }
            picMap.Refresh();
        }

        void clearHoverOverUnitInfo()
        {
        }

        void showHoverOverUnitInfo()
        {
        }
        void tmrFeedback_Tick(object sender, EventArgs e) { tmrFeedback.Enabled = lblFeedback.Visible = false; }


        void feedback_StartTurn()
        {
            if (TurnColor == classBattle.enuUnitColors.blue)
                feedback_BlueTurn();
            else
                feedback_RedTurn();
        }
        void feedback_RedTurn() { showFeedback("Red's Turn", Color.Magenta, Color.Black, new Font("Vivaldi", 22, FontStyle.Italic)); }
        void feedback_BlueTurn() { showFeedback("Blue's Turn", Color.LightBlue, Color.Black, new Font("Vivaldi", 22, FontStyle.Italic)); }
        void feedback_GameOver()
        {
            string strText = "";
            Color clrBackColor = Color.LightBlue;
            if (Unit_Q[(int)classBattle.enuUnitColors.blue] == null)
            {
                strText = "Game Over\r\nRed Wins";
                clrBackColor = Color.Magenta;
            }
            else
            {
                strText = "Game Over\r\nBlue Wins";
            }

            showFeedback(strText, clrBackColor, Color.Black, new Font("Vivaldi", 72, FontStyle.Italic));
        }

        public void showFeedback(string strText, Color clrBack, Color clrFore, Font thisFont)
        {
            lblFeedback.Text = strText;
            lblFeedback.Font = thisFont;
            lblFeedback.BackColor = clrBack;
            lblFeedback.ForeColor = clrFore;
            lblFeedback.Visible = true;
            placeFeedback();
            tmrFeedback.Enabled = false;
            tmrFeedback.Enabled = true;
        }

        void placeFeedback()
        {
            lblFeedback.Top = (Height - lblFeedback.Height) / 2;
            lblFeedback.Left = (Width - lblFeedback.Width) / 2;
        }

        public int getGridMapObjectTop(Point pos) { return getGridMapObjectTop(pos.X, pos.Y); }
        public int getGridMapObjectTop(int intX, int intY)
        {
            return (int)((double)(intY + (intIsEven(intX) ? 0.5 : 0)) * (double)(intMapGridSize));
        }
        public int getGridMapObjectLeft(Point pos) { return getGridMapObjectLeft(pos.X); }
        public int getGridMapObjectLeft(int intX) { return intMapGridSize * intX; }
        public Point getGridLocation(Point pos)
        {
            Point udrRetVal = new Point();
            udrRetVal.X = getGridMapObjectLeft(pos);
            udrRetVal.Y = getGridMapObjectTop(pos);
            return udrRetVal;
        }

        void displayUnitQ(ref classBattleUnit unitQ)
        {
            classBattleUnit thisUnit = unitQ;
            while (thisUnit != null)
            {
                thisUnit.display();

                thisUnit = thisUnit.next;
            }
        }

        public void loadBattleField(string strBattlefieldFilename)
        {
            /*
                fs = new FileStream(strFilename, FileMode.Create);
                formatter.Serialize(fs, (byte)szMapSize.Width);
                formatter.Serialize(fs, (byte)szMapSize.Height);

                for (int intY = 0; intY < szMapSize.Height; intY++)
                {
                    string strTerrain = "";
                    for (int intX = 0; intX < szMapSize.Width; intX++)
                        strTerrain += TerrainColumns[intX].Rows[intY].type.ToString().Substring(0, 1).ToUpper();
                    formatter.Serialize(fs, strTerrain);
                }

                for (classBattle.enuInitialUnitLocDirIndices dirCounter = 0; dirCounter < classBattle.enuInitialUnitLocDirIndices._num_RedInitialLocIndices; dirCounter++)
                    for (int intLocCounter = 0; intLocCounter < classBattle.intMaxInvasionForce; intLocCounter++)
                    {
                        formatter.Serialize(fs, (byte)udrRedLaunchLocs.dir[(byte)dirCounter].pt[intLocCounter].X);
                        formatter.Serialize(fs, (byte)udrRedLaunchLocs.dir[(byte)dirCounter].pt[intLocCounter].Y);
                    }

                for (int intLocCounter = 0; intLocCounter < classBattle.intMaxInvasionForce; intLocCounter++)
                {
                    formatter.Serialize(fs, (byte)udrBlueLaunchLocs[intLocCounter].X);
                    formatter.Serialize(fs, (byte)udrBlueLaunchLocs[intLocCounter].Y);
                }
             */

            BinaryFormatter formatter = new BinaryFormatter();
            FileStream fs = new FileStream(strBattlefieldFilename, FileMode.Open);
            szMapSize.Width  = (byte)formatter.Deserialize(fs);
            szMapSize.Height = (byte)formatter.Deserialize(fs);
            
            setIntStandardCartesianStringFieldLength();

            string[] strTerrain = new string[szMapSize.Height];
            for (int intY = 0; intY < szMapSize.Height; intY++)
                strTerrain[intY] = ((string)formatter.Deserialize(fs)).ToUpper();

            udrRedLaunchLocs = new classBattle.classRedLaunchLocations();
            udrRedLaunchLocs.dir = new classBattle.Point_Array[((int)classBattle.enuInitialUnitLocDirIndices._num_RedInitialLocIndices)];
            udrBlueLaunchLocs = new Point[classBattle.intMaxInvasionForce];

            for (classBattle.enuInitialUnitLocDirIndices dirCounter = 0; dirCounter < classBattle.enuInitialUnitLocDirIndices._num_RedInitialLocIndices; dirCounter++)
            {
                udrRedLaunchLocs.dir[(int)dirCounter] = new classBattle.Point_Array();
                udrRedLaunchLocs.dir[(int)dirCounter].pt = new Point[classBattle.intMaxInvasionForce];
                for (int intLocCounter = 0; intLocCounter < classBattle.intMaxInvasionForce; intLocCounter++)
                {
                    udrRedLaunchLocs.dir[(int)dirCounter].pt[intLocCounter] = new Point();
                    udrRedLaunchLocs.dir[(int)dirCounter].pt[intLocCounter].X = (byte)formatter.Deserialize(fs);
                    udrRedLaunchLocs.dir[(int)dirCounter].pt[intLocCounter].Y = (byte)formatter.Deserialize(fs);
                }
            }

            for (int intLocCounter = 0; intLocCounter < classBattle.intMaxInvasionForce; intLocCounter++)
            {
                udrBlueLaunchLocs[intLocCounter] = new Point();
                udrBlueLaunchLocs[intLocCounter].X = (byte)formatter.Deserialize(fs);
                udrBlueLaunchLocs[intLocCounter].Y = (byte)formatter.Deserialize(fs);
            }

            fs.Close();
            loadTerrain(strTerrain);
        }

        public void loadTerrain(string[] strTerrain)
        {
            if (strTerrain == null)
                return;

            szMapSize.Height = strTerrain.Length;
            szMapSize.Width = strTerrain[0].Length;

            string strTerrainTypes = "GFRWMC";
            Image[] imgTerrain = { Battlefield_Simulator_2019.Resources.terrain_grass,
                                   Battlefield_Simulator_2019.Resources.terrain_forest,
                                   Battlefield_Simulator_2019.Resources.terrain_road,
                                   Battlefield_Simulator_2019.Resources.terrain_water,
                                   Battlefield_Simulator_2019.Resources.terrain_mountain,
                                   Battlefield_Simulator_2019.Resources.terrain_castle
                                 };
            classBattle.enuTerrainTypes[] enuTerrains = { classBattle.enuTerrainTypes.grass, 
                                                          classBattle.enuTerrainTypes.forest,
                                                          classBattle.enuTerrainTypes.road,
                                                          classBattle.enuTerrainTypes.water,
                                                          classBattle.enuTerrainTypes.mountain,
                                                          classBattle.enuTerrainTypes.castle };
            terrain = new classBattle.classTerrain[szMapSize.Width, szMapSize.Height];
            for (int intY = 0; intY < szMapSize.Height; intY++)
            {
                strTerrain[intY] = strTerrain[intY].ToUpper();
                for (int intX = 0; intX < szMapSize.Width; intX++)
                {
                    try
                    {
                        int intIndexType = strTerrainTypes.IndexOf(strTerrain[intY][intX]);
                        terrain[intX, intY] = new classBattle.classTerrain();
                        terrain[intX, intY].type = enuTerrains[intIndexType];
                        if (terrain[intX, intY].type == classBattle.enuTerrainTypes.castle)
                            udrCastleLoc = cLibBattle.getPos(intX, intY);
                    }
                    catch (Exception) { }
                }
            }
            strategyBlue = enuAIStrategies.initDefense;

            displayMap();
        }

        public void addUnitsToBattleGround(ref  classBattleUnit[] units)
        { addUnitsToBattleGround(ref units, classBattle.enuInitialUnitLocDirIndices.Blue); }
        public void addUnitsToBattleGround(ref classBattleUnit[] units, classBattle.enuInitialUnitLocDirIndices initUnitDir)
        {
            if (initUnitDir == classBattle.enuInitialUnitLocDirIndices.Blue)
                addUnitsToBattleGround(ref units, udrBlueLaunchLocs);
            else
                addUnitsToBattleGround(ref units, udrRedLaunchLocs.dir[(int)initUnitDir].pt);
        }

        void addUnitsToBattleGround(ref classBattleUnit[] units, Point[] udrLocs)
        {
            for (int intUnitcounter = 0; intUnitcounter < units.Length; intUnitcounter++)
            {
                if (units[intUnitcounter] != null)
                    if (terrain[udrLocs[intUnitcounter].X, udrLocs[intUnitcounter].Y].unit == null)
                    {
                        units[intUnitcounter].myPosition = udrLocs[intUnitcounter];
                        enQUnit(ref units[intUnitcounter]);
                        units[intUnitcounter].setRangeForTerrain();
                        units[intUnitcounter].display();

                    }
            }
            if (units[0].myClr == classBattle.enuUnitColors.blue)
                reorderQ(ref Unit_Q[(int)classBattle.enuUnitColors.blue]);
            else
                reorderQ(ref Unit_Q[(int)classBattle.enuUnitColors.red]);
            newDay();
        }

        void displayTurnInfo()
        {
            //txtDebug.Text = TurnColor.ToString() + "'s turn";
        }

        void PlyrMove_ShowAttackMap(Point udrMapSquare)
        {
            bolPlyrControlledTargetMap = PlyerSelectedUnit.getAttackMapFromLoc(udrMapSquare, PlyerSelectedUnit.intRange);
            showTargetsOnMap(bolPlyrControlledTargetMap);
        }

        Point determineMapGridLocByObjectLoc(Point udrPos) { return determineMapGridLocByObjectLoc(udrPos.X, udrPos.Y); }
        Point determineMapGridLocByObjectLoc(int intLeft, int intTop)
        {
            Point udrRetVal = new Point();
            for (udrRetVal.X = 0; udrRetVal.X < szMapSize.Width; udrRetVal.X++)
                if (getGridMapObjectLeft(udrRetVal.X + 1) > intLeft)
                    break;
            for (udrRetVal.Y = 0; udrRetVal.Y < szMapSize.Height; udrRetVal.Y++)
                if (getGridMapObjectTop(udrRetVal.X, udrRetVal.Y + 1) > intTop)
                    break;

            return udrRetVal;
        }

        #endregion

        #region "turn/day changes"

        public void newDay()
        {
            classBattleUnit.intNumberBlueUnitsOnField = 0;
            classBattleUnit.intCurrentTurn = 0;
            classBattleUnit.bolBlueSideHasMoved = false;

            classBattleUnit thisUnit;
            thisUnit = Unit_Q[(int)classBattle.enuUnitColors.blue];
            while (thisUnit != null)
            {
                thisUnit.NewDay();
                classBattleUnit.intNumberBlueUnitsOnField++;
                thisUnit = thisUnit.next;
            }

            thisUnit = Unit_Q[(int)classBattle.enuUnitColors.red];
            while (thisUnit != null)
            {
                thisUnit.NewDay();
                thisUnit = thisUnit.next;
            }

            unshadeMap();
            PlyerSelectedUnit = null;
            TurnColor = (classBattle.enuUnitColors)0;

        }

        void startTurn()
        {
            if ((int)TurnColor == 0)
                newDay();
            displayTurnInfo();
            bool bolComputerControl = ColorControl[(int)TurnColor] == classBattle.enuColorControlledBy.computer;

            if (!bolComputerControl)
                feedback_StartTurn();
            btnEndTurn.Visible = !bolComputerControl;
            txtDebug.BackColor = (TurnColor == classBattle.enuUnitColors.red ? Color.Red : Color.Blue);

            if (Unit_Q[(int)TurnColor] == null)
                feedback_GameOver();

            if (bolComputerControl)
            {
                CommandArmy();
            }
        }



        public void endTurn()
        {
            setTimerAnimate = false;
            TurnColor = (classBattle.enuUnitColors)(((int)TurnColor + 1) % 2);
            btnDebug.BackColor = TurnColor == classBattle.enuUnitColors.red ? Color.Red : Color.Blue;
            unshadeMap();
            startTurn();
        }

        #endregion

        #region "unit Q"

        public void reorderQ(ref classBattleUnit thisQ)
        {
            classBattleUnit[] Qarray = new classBattleUnit[0];
            classBattleUnit thisUnit = thisQ;
            // create Qarray of pointers to each element in Q
            while (thisUnit != null)
            {
                Array.Resize<classBattleUnit>(ref Qarray, Qarray.Length + 1);
                Qarray[Qarray.Length - 1] = thisUnit;
                thisUnit = thisUnit.next;
            }

            // delete Q
            thisQ = null;

            // null all of their link ptrs
            for (int intUnitCounter = 0; intUnitCounter < Qarray.Length; intUnitCounter++)
                Qarray[intUnitCounter].next = Qarray[intUnitCounter].previous = null;

            // reorder array according to type - cavalry first, infantry second, artillery last
            classBattle.enuUnitTypes bestType;
            int intBestIndex;
            for (int intOuterLoop = 0; intOuterLoop < Qarray.Length - 1; intOuterLoop++)
            {
                bestType = Qarray[intOuterLoop].myType;
                intBestIndex = intOuterLoop;
                for (int intInnerLoop = intOuterLoop + 1; intInnerLoop < Qarray.Length; intInnerLoop++)
                {
                    if (Qarray[intInnerLoop].myType < bestType)
                    {
                        bestType = Qarray[intInnerLoop].myType;
                        intBestIndex = intInnerLoop;
                    }
                }
                if (intBestIndex != intOuterLoop)
                {
                    classBattleUnit tempUnit = Qarray[intOuterLoop];
                    Qarray[intOuterLoop] = Qarray[intBestIndex];
                    Qarray[intBestIndex] = tempUnit;
                }
            }

            // front end insertion 
            for (int intUnitCounter = 0; intUnitCounter < Qarray.Length; intUnitCounter++)
            {
                enQUnit(ref thisQ, ref Qarray[intUnitCounter]);
            }
            // artillery first in Q, infantry second, cavalry last
        }

        public void enQUnit(ref classBattleUnit unitNew)
        {
            if (unitNew.myClr == classBattle.enuUnitColors.blue)
                enQUnit(ref Unit_Q[(int)classBattle.enuUnitColors.blue], ref unitNew);
            else
                enQUnit(ref Unit_Q[(int)classBattle.enuUnitColors.red], ref unitNew);
        }

        public void enQUnit(ref classBattleUnit unitQ, ref classBattleUnit unitNew)
        {
            unitQ_Semaphore.WaitOne();

            if (unitNew == null)
            {
                unitQ_Semaphore.Release();
                return;
            }
            unitNew.battlefield = this;

            if (unitQ != null)
            {
                unitNew.next = unitQ;
                unitQ.previous = unitNew;

                unitNew.previous = null;
                unitQ = unitNew;
            }
            else
            {
                unitQ = unitNew;
                unitNew.next = null;
                unitNew.previous = null;
            }

            terrain[unitNew.myPosition.X, unitNew.myPosition.Y].unit = unitNew;
            unitNew.initPics();
            unitQ_Semaphore.Release();
        }

        public void deQUnit(ref classBattleUnit unitQ, ref classBattleUnit unitNew)
        {
            unitQ_Semaphore.WaitOne();
            if (unitNew == null)
            {
                return;
            }
            if (unitQ == null)
            {
                unitQ_Semaphore.Release();
                return;
            }

            if (unitNew == unitQ)
            {
                unitQ = unitQ.next;
                if (unitQ != null)
                    unitQ.previous = null;
            }
            else
            {
                try { unitNew.previous.next = unitNew.next; }
                catch (Exception) { }
                try 
                {
                    if (unitNew.next != null)
                        unitNew.next.previous = unitNew.previous;
                }
                catch (Exception) { }
            }

            unitNew.next = null;
            unitNew.previous = null;
            unitQ_Semaphore.Release();
        }

        public void moveToEndOfQ(ref classBattleUnit unitQ, ref classBattleUnit unitNew)
        {
            unitQ_Semaphore.WaitOne();
            if (unitNew == null)
            {
                return;
            }
            if (unitQ == null)
            {
                unitQ_Semaphore.Release();
                return;
            }

            if (unitNew == unitQ)
            {
                unitQ = unitQ.next;
                if (unitQ != null)
                    unitQ.previous = null;
            }
            else
            {
                try { unitNew.previous.next = unitNew.next; }
                catch (Exception) { }
                try { unitNew.next.previous = unitNew.previous; }
                catch (Exception) { }
            }

            unitNew.next = null;
            unitNew.previous = null;

            classBattleUnit thisUnit = unitQ;
            while (thisUnit.next != null)
                thisUnit = thisUnit.next;

            thisUnit.next = unitNew;
            unitNew.previous = thisUnit;

            unitQ_Semaphore.Release();
        }

        #endregion

        #region "AI"

        #region "general AI"
        public void CommandArmy()
        {
            // AI 
            evaluateArmyStats();


            switch (TurnColor)
            {
                case classBattle.enuUnitColors.red:
                    CommandArmy_Red();
                    break;

                case classBattle.enuUnitColors.blue:
                    CommandArmy_Blue();

                    break;
            }
        }

        void evaluateArmyStats()
        {
            for (classBattle.enuUnitColors clrCounter = classBattle.enuUnitColors.red; clrCounter <= classBattle.enuUnitColors.blue; clrCounter++)
                udrArmyStats[(int)clrCounter] = getArmyStats(ref Unit_Q[(int)clrCounter]);
        }

        classArmyStats getArmyStats(ref classBattleUnit unitQ)
        {
            classArmyStats udrRetVal = new classArmyStats();
            classBattleUnit thisUnit = unitQ;
            udrRetVal.units = new classBattleUnit[0];

            while (thisUnit != null)
            {
                Array.Resize<classBattleUnit>(ref udrRetVal.units, udrRetVal.units.Length + 1);
                thisUnit.setMyStats();
                udrRetVal.units[udrRetVal.units.Length - 1] = thisUnit;
                addUnitStats(ref udrRetVal.total, thisUnit.myStats);
                thisUnit = thisUnit.next;
            }
            return udrRetVal;
        }

        void addUnitStats(ref classUnitStats total, classUnitStats thisUnit)
        {
            total.melee.offense += thisUnit.melee.offense;
            total.melee.defense += thisUnit.melee.defense;

            total.range.defense += thisUnit.range.defense;
            total.range.offense += thisUnit.range.offense;
        }

        bool WeAreStronger()
        {
            //return true;
            int intOtherClr = (int)(TurnColor + 1) % 2;

            switch (TurnColor)
            {
                case classBattle.enuUnitColors.blue:
                    return (udrArmyStats[(int)TurnColor].total.melee.offense > udrArmyStats[intOtherClr].total.melee.offense * 1.5
                            && udrArmyStats[(int)TurnColor].total.range.offense > udrArmyStats[intOtherClr].total.range.offense * 1.5);

                case classBattle.enuUnitColors.red:
                    return (udrArmyStats[(int)TurnColor].total.melee.offense > udrArmyStats[intOtherClr].total.melee.offense
                            || udrArmyStats[(int)TurnColor].total.range.offense > udrArmyStats[intOtherClr].total.range.offense);

                default:
                    return false;
            }
        }

        void AI_ControlThisUnit()
        {
            if (AIControlledUnit != null)
            {
                //AIControlledUnit.NewDay();
                switch (AIControlledUnit.myClr)
                {
                    case classBattle.enuUnitColors.red:
                        switch (strategyRed)
                        {
                            case enuAIStrategies.harass:
                                AI_MoveUnit_Harras_Init();
                                break;

                            case enuAIStrategies.retreat:
                                AI_RetreatUnit();
                                break;
                        }
                        break;

                    case classBattle.enuUnitColors.blue:
                        // set unguarded POCs
                        classBattleUnit.udrUnGuardPOCs = new Point[0];
                        if (udrPOC[(int)classBattle.enuUnitColors.blue].POCLocs != null
                            && udrPOC[(int)classBattle.enuUnitColors.blue].POCLocs.Length > 0)
                            for (int intPOCCounter = 0; intPOCCounter < udrPOC[(int)classBattle.enuUnitColors.blue].POCLocs.Length; intPOCCounter++)
                            {
                                if (udrPOC[(int)classBattle.enuUnitColors.blue].POCLocs[intPOCCounter].flag)
                                    if (AIControlledUnit.countFriendlyUnitsNextToThisSquare(udrPOC[(int)classBattle.enuUnitColors.blue].POCLocs[intPOCCounter].pt) == 0)
                                    {
                                        Array.Resize<Point>(ref classBattleUnit.udrUnGuardPOCs, classBattleUnit.udrUnGuardPOCs.Length + 1);
                                        classBattleUnit.udrUnGuardPOCs[classBattleUnit.udrUnGuardPOCs.Length - 1] = udrPOC[(int)classBattle.enuUnitColors.blue].POCLocs[intPOCCounter].pt;
                                    }
                            }
                        switch (strategyBlue)
                        {
                            case enuAIStrategies.harass:
                                AI_MoveUnit_Harras_Init();
                                break;

                            case enuAIStrategies.retreat:
                                AI_RetreatUnit();
                                break;

                            case enuAIStrategies.hold_POCs:
                                AI_HoldPOCs();
                                break;

                            case enuAIStrategies.defend_castle:
                                AI_DefendCastle();
                                break;
                        }
                        break;
                }
            }
            else
            {
                endTurn();
            }
        }

        void AI_MoveUnit_Harras_Init()
        {
            Point udrNearestTarget = AIControlledUnit.getNearestAttackLocation(getBolDefaultAvoidMap());
            AIControlledUnit.strMovePath = AIControlledUnit.getNextMovePath(udrNearestTarget); //   AIControlledUnit.getShortestPath(AIControlledUnit.myPosition, udrNearestTarget, false);

            udrAnimation.unit = AIControlledUnit;
            launchAnimateUnitTimer();
        }

        void AI_RetreatUnit()
        {

        }

        void AI_DefendCastle()
        {
            if (AIControlledUnit.strMovePath.Length == 0)
            {
                if (udrCDInfo.strMoveSequence.Length > 0)
                {
                    int intCutChr = udrCDInfo.strMoveSequence.IndexOf(classOpenFieldDefenseSearchTreeNode.strSolutionFieldDelimiter);
                    if (intCutChr > 0)
                    {
                        string strAUnitID = Unit_Q[0].myIDstring;
                        string strNextMovePathAndUnitID = udrCDInfo.strMoveSequence.Substring(0, intCutChr);
                        udrCDInfo.strMoveSequence = udrCDInfo.strMoveSequence.Substring(strNextMovePathAndUnitID.Length + 1);

                        string strNextUnitID = strNextMovePathAndUnitID.Substring(0, strAUnitID.Length);
                        string strNextMovePath = strNextMovePathAndUnitID.Substring(strAUnitID.Length);

                        int intUnitID = Convert.ToInt16(strNextUnitID);
                        AIControlledUnit = getUnitFromQWithUnitID(intUnitID);
                        AIControlledUnit.strMovePath = strNextMovePath;
                    }
                }
                else
                {
                    if (!udrCDInfo.bolInitialMovesComplete)
                    {
                        AIControlledUnit = Unit_Q[(int)TurnColor];
                        udrCDInfo.bolInitialMovesComplete = true;
                    }
                }
            }

            udrAnimation.unit = AIControlledUnit;
            launchAnimateUnitTimer();
        }

        void AI_HoldPOCs()
        {
            Point udrNearestDefensibleLocation = AIControlledUnit.getNearestDefensibleLocation(ref udrPOC[(int)TurnColor]);
            AIControlledUnit.strMovePath = AIControlledUnit.getNextMovePath(udrNearestDefensibleLocation); //AIControlledUnit.getShortestPath(AIControlledUnit.myPosition, udrNearestDefensibleLocation, false);

            udrAnimation.unit = AIControlledUnit;
            launchAnimateUnitTimer();
        }

        public bool setTimerAnimate
        {
            set { tmrAnimateUnit.Enabled = value; }
            get { return tmrAnimateUnit.Enabled; }
        }

        public void tmrAnimateUnit_Tick(object sender, EventArgs e)
        {
            setTimerAnimate = false;
            tmrAnimateUnit.Interval = intInterval;
            if (udrAnimation.unit.strMovePath.Length >= 2)
            {
                string strDir = udrAnimation.unit.strMovePath.Substring(0, 2);
                udrAnimation.unit.strMovePath = udrAnimation.unit.strMovePath.Substring(2);
                if (udrAnimation.unit.moveOneStep(getDirFromString(strDir)))
                    setTimerAnimate = true;
                else if (ColorControl[(int)TurnColor] == classBattle.enuColorControlledBy.computer)
                {
                    if (TurnColor == classBattle.enuUnitColors.blue)
                    {
                        if (strategyBlue == enuAIStrategies.defend_castle)
                            if (!udrCDInfo.bolInitialMovesComplete)
                            {
                                AIControlledUnit = Unit_Q[(int)TurnColor];
                                udrCDInfo.bolInitialMovesComplete = true;
                            }
                            else
                            {
                                AI_ControlNextUnit();
                            }
                    }

                    AI_ControlNextUnit();
                }
                else
                    udrAnimation.flag = false;
            }
            else
            {
                if (ColorControl[(int)TurnColor] == classBattle.enuColorControlledBy.computer)
                {
                    if (strategyBlue == enuAIStrategies.defend_castle
                        && udrCDInfo.strMoveSequence.Length > 0)
                    {
                        if (!udrCDInfo.bolInitialMovesComplete)
                            AI_DefendCastle();
                        else
                            AI_ControlNextUnit();
                    }
                    else
                    {
                        if (AI_AttackUnit())
                        {
                            udrAnimation.animation_AttackPhase = enuAttackAnimationPhase.init;
                            unit_Animated = AIControlledUnit;
                            launchAnimateAttackTimer();
                        }
                        else
                        {
                            AI_ControlNextUnit();
                        }
                    }
                }
                else
                {
                    udrAnimation.flag = false;
                }
            }
        }

        public void launchAnimateAttackTimer()
        {
            if (!bolKillBattle)
                tmrAnimateAttack.Enabled = true;
        }

        void tmrAnimateAttack_Tick(object sender, EventArgs e)
        {
            System.Windows.Forms.Timer tmrME = (System.Windows.Forms.Timer)sender;
            tmrME.Enabled = false;
            tmrME.Interval = intInterval;
            udrAnimation.intAnimationCounter++;

            switch (unit_Animated.attackType)
            {
                case classBattle.enuAttackTypes.barrage:
                    switch (udrAnimation.animation_AttackPhase)
                    {
                        case enuAttackAnimationPhase.init:
                            classBarrage cLibBarrage = new classBarrage(this, unit_Animated);
                            picMap.Controls.Add(cLibBarrage);
                            cLibBarrage.go();
                            udrAnimation.animation_AttackPhase = enuAttackAnimationPhase.animateDefender;
                            udrAnimation.intAnimationCounter = 0;
                            break;

                        case enuAttackAnimationPhase.animateDefender:
                            if (udrAnimation.intAnimationCounter > 10)
                            {
                                unit_Animated.unitIAmAttacking.hit(unit_Animated.unitIAmAttacking.intTotalDamageFromHit);
                                launchAnimateUnitTimer();
                                udrAnimation.intAnimationCounter = 0;
                                udrAnimation.flag = false;
                                testIfBelligerentsDie();
                                return;
                            }
                            else
                                unit_Animated.unitIAmAttacking.shake();
                            if (!bolKillBattle)
                                tmrME.Enabled = true;
                            break;
                    }
                    break;

                case classBattle.enuAttackTypes.normal:
                    if (udrAnimation.intAnimationCounter > 10)
                    {
                        unit_Animated.unitIAmAttacking.hit(unit_Animated.unitIAmAttacking.intTotalDamageFromHit);
                        launchAnimateUnitTimer();
                        udrAnimation.intAnimationCounter = 0;
                        udrAnimation.flag = false;
                        testIfBelligerentsDie();
                        return;
                    }
                    else
                    {
                        unit_Animated.unitIAmAttacking.shake();
                        unit_Animated.shake();
                    }
                    if (!bolKillBattle)
                        tmrME.Enabled = true;
                    break;

                case classBattle.enuAttackTypes.charge:
                    if (udrAnimation.intAnimationCounter > 10)
                    {
                        unit_Animated.unitIAmAttacking.hit(unit_Animated.unitIAmAttacking.intTotalDamageFromHit);
                        launchAnimateUnitTimer();
                        udrAnimation.intAnimationCounter = 0;
                        udrAnimation.flag = false;
                        testIfBelligerentsDie();
                        return;
                    }
                    else
                    {
                        unit_Animated.unitIAmAttacking.shake();
                        unit_Animated.shake();
                        drawChargeGraphics();
                    }
                    if (!bolKillBattle)
                        tmrME.Enabled = true;
                    break;
            }
            udrAnimation.flag = false;
        }

        void drawChargeGraphics()
        {
            if (udrAnimation.intAnimationCounter == 10)
                return;
            int intStepDistance = udrAnimation.intAnimationCounter % 5 + 1;

            double dblFractionSize = 0.5;
            Bitmap bmpChargingHorse = new Bitmap(unit_Animated.myPosition.X > unit_Animated.unitIAmAttacking.myPosition.X
                                                                                ? Battlefield_Simulator_2019.Resources.horse_Left
                                                                                : Battlefield_Simulator_2019.Resources.horse_Right,
                                                 (int)(intMapGridSize * dblFractionSize),
                                                 (int)(intMapGridSize * dblFractionSize));

            Point ptMyCenter = new Point(getGridMapObjectLeft(unit_Animated.myPosition) + intMapGridSize / 2, getGridMapObjectTop(unit_Animated.myPosition) + intMapGridSize / 2);
            Point ptEnemyCenter = new Point(getGridMapObjectLeft(unit_Animated.unitIAmAttacking.myPosition) + intMapGridSize / 2, getGridMapObjectTop(unit_Animated.unitIAmAttacking.myPosition) + intMapGridSize / 2);

            int intDistance = (int)Math.Sqrt(Math.Pow(ptMyCenter.X - ptEnemyCenter.X, 2) + Math.Pow(ptMyCenter.Y - ptEnemyCenter.Y, 2));
            double dblAngle = Math.PI / 2;
            if (ptMyCenter.X == ptEnemyCenter.X)
            {
                if (ptMyCenter.Y > ptEnemyCenter.Y) dblAngle = -Math.PI / 2;
            }
            else
            {
                dblAngle = cLibBattle.arcTan(ptEnemyCenter.X - ptMyCenter.X, ptEnemyCenter.Y - ptMyCenter.Y);
            }

            Point ptNewCenter = new Point(ptMyCenter.X + (int)(((double)intStepDistance / 5.0) * intDistance * Math.Cos(dblAngle)),
                                          ptMyCenter.Y + (int)(((double)intStepDistance / 5.0) * intDistance * Math.Sin(dblAngle)));
            Point ptNewCorner = new Point(ptNewCenter.X - (int)((intMapGridSize * dblFractionSize) / 2), ptNewCenter.Y - (int)((intMapGridSize * dblFractionSize) / 2));

            bmpChargingHorse.MakeTransparent();
            using (Graphics g = Graphics.FromImage(picMap.Image))
            {
                g.DrawImage(bmpChargingHorse, ptNewCorner);
                picMap.Refresh();
            }
        }

        void testIfBelligerentsDie()
        {
            if (unit_Animated.unitIAmAttacking.bolDie)
            {
                unit_Animated.unitIAmAttacking.die();
                if (unit_Animated.bolFullStop)
                    if (!unit_Animated.thereIsAnEnemyUnitNextToThisSquare(unit_Animated.myPosition))
                        unit_Animated.bolFullStop = false;
            }
            if (unit_Animated.bolDie)
            {
                classBattleUnit defender = unit_Animated.unitIAmAttacking;
                unit_Animated.die();
                if (defender.bolFullStop)
                    if (!defender.thereIsAnEnemyUnitNextToThisSquare(defender.myPosition))
                        defender.bolFullStop = false;
            }
            //unit_Animated.setMyImage();
        }

        void AI_ControlNextUnit()
        {
            udrAnimation.flag = false;
            AIControlledUnit = AIControlledUnit.next;
            if (AIControlledUnit != null)
                AIControlledUnit.unitIAmAttacking = null;
            AI_ControlThisUnit();
        }

        public void killUnit(classBattleUnit thisUnit)
        {
            thisUnit.erase();
            deQUnit(ref Unit_Q[(int)thisUnit.myClr], ref thisUnit);
            if (terrain[thisUnit.myPosition.X, thisUnit.myPosition.Y].unit != null)
                terrain[thisUnit.myPosition.X, thisUnit.myPosition.Y].unit = terrain[thisUnit.myPosition.X, thisUnit.myPosition.Y].unit.terrain_next;
        }

        bool AI_AttackUnit()
        {
            if (AIControlledUnit.CanIAttackFromThisPos(AIControlledUnit.myPosition, AIControlledUnit.intRange)
                //&& AIControlledUnit.attackType != classBattle.enuAttackTypes._numAttackTypes
                && (AIControlledUnit.intMovementPoints >= intCostAttack + ((int)AIControlledUnit.myClr == 0 ? intCostCounterAttack * 3 : 0)))
            {
                AIControlledUnit.attack();
                return true;
            }
            return false;
        }

        public void moveToTarget(ref classBattleUnit thisUnit, Point udrTargetLoc_)
        {
            if (!udrAnimation.flag)
            {
                thisUnit.udrTargetLoc = udrTargetLoc_;
                thisUnit.strMovePath = thisUnit.getShortestPath(thisUnit.myPosition, thisUnit.udrTargetLoc);
                udrAnimation.unit = thisUnit;
                launchAnimateUnitTimer();
            }
        }

        public void AttackTarget(ref classBattleUnit thisUnit, Point udrTargetLoc_)
        {
            if (!udrAnimation.flag)
            {
                udrAnimation.unit = thisUnit;
                launchAnimateUnitTimer();
            }
        }

        public void launchAnimateUnitTimer()
        {
            setTimerAnimate = true;
        }

        void CommandArmy_Retreat()
        {
        }
        #endregion

        #region "Red AI"

        int[] countSizeTypesInQ(ref classBattleUnit unitQ)
        {
            int[] intRetVal = new int[(int)classBattle.enuUnitTypes._num_UnitTypes];
            for (int intcounter = 0; intcounter < (int)classBattle.enuUnitTypes._num_UnitTypes; intcounter++)
                intRetVal[intcounter] = 0;

            classBattleUnit thisUnit = unitQ;
            while (thisUnit != null)
            {
                intRetVal[(int)thisUnit.myType] += thisUnit.intSize;
                thisUnit = thisUnit.next;
            }
            return intRetVal;
        }

        int[] countUnitTypesInQ(ref classBattleUnit unitQ)
        {
            int[] intRetVal = new int[(int)classBattle.enuUnitTypes._num_UnitTypes];
            for (int intcounter = 0; intcounter < (int)classBattle.enuUnitTypes._num_UnitTypes; intcounter++)
                intRetVal[intcounter] = 0;

            classBattleUnit thisUnit = unitQ;
            while (thisUnit != null)
            {
                intRetVal[(int)thisUnit.myType]++;
                thisUnit = thisUnit.next;
            }
            return intRetVal;
        }

        enuAIStrategies DetermineStrategy()
        {
            int[] intNumUnitsBlueQ = countSizeTypesInQ(ref Unit_Q[(int)classBattle.enuUnitColors.blue]);
            int[] intNumUnitsRedQ = countSizeTypesInQ(ref Unit_Q[(int)classBattle.enuUnitColors.red]);

            if (WeAreStronger())
                return enuAIStrategies.harass;
            else
            {
                if (TurnColor == classBattle.enuUnitColors.blue)
                {
                    if (udrPOC[(int)TurnColor].POCLocs.Length > 0)
                    {
                        if ((intNumUnitsRedQ[(int)classBattle.enuUnitTypes.cavalry] + intNumUnitsRedQ[(int)classBattle.enuUnitTypes.infantry]) * 5
                            < intNumUnitsBlueQ[(int)classBattle.enuUnitTypes.cavalry] + intNumUnitsBlueQ[(int)classBattle.enuUnitTypes.infantry])
                            return enuAIStrategies.harass;
                        else
                            return enuAIStrategies.hold_POCs;
                    }
                    return enuAIStrategies.defend_castle;
                }
                else
                    return enuAIStrategies.harass;

            }
            displayTurnInfo();
        }

        void CommandArmy_Red()
        {
            // invading force
            if (Unit_Q[(int)TurnColor] != null)
            {
                strategyRed = DetermineStrategy();


                AIControlledUnit = Unit_Q[(int)TurnColor];
                AI_ControlThisUnit();
            }
        }
        #endregion

        #region "Blue AI"
        void CommandArmy_Blue()
        {
            // defending force
            clearOldDebugLabels();
            if (Unit_Q[(int)TurnColor] != null)
            {
                initDefense();
                strategyBlue = DetermineStrategy();
                switch (strategyBlue)
                {
                    case enuAIStrategies.defend_castle:
                        OFD_setMap();
                        OFD_setBestOpeningMoves();
                        udrCDInfo.bolInitialMovesComplete = false;
                        highlightOpenFieldDefenseDeploymentMap(udrCDInfo);
                        break;

                    case enuAIStrategies.hold_POCs:
                        highlightPOC(udrPOC[(int)classBattle.enuUnitColors.blue]);
                        break;

                    case enuAIStrategies.harass:
                        clearOldDebugLabels();
                        break;
                }

                AIControlledUnit = Unit_Q[(int)TurnColor];

                AI_ControlThisUnit();
            }
        }

        Point getPosTwoTurnsLeft(Point udrLoc, classBattle.enuDirections dir)
        {
            dir = cLibBattle.turnLeft(dir);
            dir = cLibBattle.turnLeft(dir);
            return getNextPos(udrLoc, dir);
        }

        bool posTwoTurnsLeftIsUnusedOFDLoc(classOpenFieldDefenseInfo udrCD, Point udrLoc, classBattle.enuDirections dir)
        { // unused Open-Field Defense Location
            Point udrPosTwooTurnsLeft = getPosTwoTurnsLeft(udrLoc, dir);
            return posUnusedOFDLoc(udrCD, udrPosTwooTurnsLeft);
        }

        bool posUnusedOFDLoc(classOpenFieldDefenseInfo udrCD, Point udrLoc)
        {
            return (udrCD.deploymentMap[udrLoc.X, udrLoc.Y].typeUnit == classBattle.enuUnitTypes._num_UnitTypes);
        }

        Point getPosTwoTurnsRight(Point udrLoc, classBattle.enuDirections dir)
        {
            dir = cLibBattle.turnRight(dir);
            dir = cLibBattle.turnRight(dir);
            return getNextPos(udrLoc, dir);
        }

        bool posTwoTurnsRightIsUnusedOFDLoc(classOpenFieldDefenseInfo udrCD, Point udrLoc, classBattle.enuDirections dir)
        {// unused Open-Field Defense Location
            Point udrPosTwoTurnsRight = getPosTwoTurnsRight(udrLoc, dir);
            return posUnusedOFDLoc(udrCD, udrPosTwoTurnsRight);
        }

        void setBolArrayAroundLoc(ref bool[,] bolArtilleryDangerZone, Point udrLoc)
        {
            for (classBattle.enuDirections dirCounter = 0; dirCounter < classBattle.enuDirections._num_directions; dirCounter++)
            {
                Point udrNewPos = getNextPos(udrLoc, dirCounter);
                if (ptOnMap(udrNewPos))
                    bolArtilleryDangerZone[udrNewPos.X, udrNewPos.Y] = true;
                bolArtilleryDangerZone[udrLoc.X, udrLoc.Y] = true;
            }
        }

        void addOFDPosToArray(ref classOpenFieldDefenseInfo udrOFDInfo, Point udrLoc, classBattle.enuUnitTypes typeUnit)
        {
            udrCDInfo.deploymentMap[udrLoc.X, udrLoc.Y].typeUnit = typeUnit;
            udrCDInfo.deploymentMap[udrLoc.X, udrLoc.Y].unitThere = terrain[udrLoc.X, udrLoc.Y].unit;

            Array.Resize<classOpenFieldDefense_Position>(ref udrOFDInfo.Pos, udrOFDInfo.Pos.Length + 1);
            for (int intCounter = 0; intCounter < udrOFDInfo.Pos.Length; intCounter++)
            {
                udrOFDInfo.Pos[intCounter] = new classOpenFieldDefense_Position();
            }

            udrOFDInfo.Pos[udrOFDInfo.Pos.Length - 1].typeUnit = typeUnit;
            udrOFDInfo.Pos[udrOFDInfo.Pos.Length - 1].udrLoc = udrLoc;
            udrOFDInfo.Pos[udrOFDInfo.Pos.Length - 1].unit = terrain[udrLoc.X, udrLoc.Y].unit;
            udrOFDInfo.Pos[udrOFDInfo.Pos.Length - 1].suitableUnitsThatCanReachThisPos = new classOpenFieldDefenseSearch_UnitInfo[0];

            if (terrain[udrLoc.X, udrLoc.Y].unit == null)
                udrOFDInfo.Pos[udrOFDInfo.Pos.Length - 1].status = enuOpenFieldDefensePosition_Status.empty;
            else
            {
                if (terrain[udrLoc.X, udrLoc.Y].unit.myType == typeUnit)
                    udrOFDInfo.Pos[udrOFDInfo.Pos.Length - 1].status = enuOpenFieldDefensePosition_Status.safe;
                else
                    udrOFDInfo.Pos[udrOFDInfo.Pos.Length - 1].status = enuOpenFieldDefensePosition_Status.occupied;
            }
        }

        public classBattleUnit getUnitFromQWithUnitID(int intUnitID)
        {
            classBattleUnit thisUnit;

            for (int intQcounter = 0; intQcounter <= 1; intQcounter++)
            {
                thisUnit = Unit_Q[intQcounter];
                while (thisUnit != null)
                {
                    if (thisUnit.MyID == intUnitID)
                        return thisUnit;
                    thisUnit = thisUnit.next;
                }
            }
            return null;
        }

        classOpenFieldDefenseSearchTreeNode OFD_getLastNodeInSearchTree(classOpenFieldDefenseSearchTreeNode thisNode)
        {
            /// this function iterates through the tree travelling down the last childnode in array
            /// until it reaches either a childnode that still has unexplored paths
            /// or a child node that has exhausted all paths and finds no solution
            /// when it finds an unexplored path that is the node that is returned
            /// when it reaches a node where there are no more options it climbs back up to parent and tries from there

            if (thisNode.childNodes.Length == 0)
            { // this is an end node
                return thisNode;
            }
            else
            {
                if (thisNode.childNodes[thisNode.childNodes.Length - 1].bolAllOptionsAreExhausted)
                    // this last childnode was a failure -> create a new one
                    return thisNode;
                else if (thisNode.childNodes[thisNode.childNodes.Length - 1].bolValidSolution)
                    // solution has already been found along this path -> try a new one
                    return thisNode;
                else // there are still possibilities along this path
                {
                    return OFD_getLastNodeInSearchTree(thisNode.childNodes[thisNode.childNodes.Length - 1]);
                }
            }
        }

        classOpenFieldDefenseSearch_Result OFD_GetResult(classOpenFieldDefenseSearchTreeNode thisNode)
        {
            classOpenFieldDefenseSearch_Result udrResult = new classOpenFieldDefenseSearch_Result();
            udrResult.intTotalMovementPointsLeft = 0;
            udrResult.strMoves = thisNode.strStepFromParent;
            udrResult.intNumberPosLeftUnsolved = thisNode.OFDInfo.Pos.Length-1;

            classOpenFieldDefenseSearchTreeNode parentNode  = thisNode;
            while (parentNode.parentNode != null)
            {
                parentNode = parentNode.parentNode;
                udrResult.strMoves = parentNode.strStepFromParent + udrResult.strMoves;
            }

            for (int intUnitcounter =0; intUnitcounter< thisNode.unitInfo.Length; intUnitcounter++)
                udrResult.intTotalMovementPointsLeft += thisNode.unitInfo[intUnitcounter].intMovementPointsLeft;
    
            return udrResult;
        }

        int OFD_getIndexPos(classOpenFieldDefense_Position[] PosArray, Point  udrLoc)
        {
            int intRetval = -1;

            for (int intPosCounter = 0; intPosCounter < PosArray.Length; intPosCounter++)
                if (PosArray[intPosCounter].udrLoc.X == udrLoc.X && PosArray[intPosCounter].udrLoc.Y == udrLoc.Y)
                    return intPosCounter;

            return intRetval;
        }

        classOpenFieldDefenseSearch_Result OFD_HandleNode_VPP(ref classOpenFieldDefenseSearchTreeNode thisNode, ref classBattleUnit unitWorker)
        {  // vacate Priority Position(inf OFDMap) 
            /// this node's unit to be moved is already determined by the need to vacate the priority position
            /// what's left to determine is the location where this unit is to move
            /// once all options are explored and no more solutions can be found along this path the 
            ///                      thisNode.bolAllOptionsAreExhausted = true;
            /// variable in this node is set making this path ineligible for further exploration by DFS algorithm
            classOpenFieldDefenseSearch_Result udrResult = new classOpenFieldDefenseSearch_Result();
            udrResult.intTotalMovementPointsLeft = 0;
            udrResult.strMoves = "";
            udrResult.intNumberPosLeftUnsolved = thisNode.OFDInfo.Pos.Length-1;

            Point udrMoveToLoc = new Point();
            Point udrMoveFromLoc = thisNode.OFDInfo.Pos[0].udrLoc;
            if (OFD_ThereIsAnUntriedPlaceForUnitToMoveFromPP(thisNode, ref udrMoveToLoc))
            {
                classOpenFieldDefenseSearchTreeNode nodeNewChild = OFD_getNode_Copy(thisNode);
                int intUnitIndex = OFD_getIndexUnitInUnitInfo(thisNode, thisNode.OFDInfo.Pos[0].unit.MyID);
                if (intUnitIndex >= 0)
                {
                    string strUnitId = thisNode.unitInfo[intUnitIndex].unit.myIDstring;
                    // if the unit is either on a square next to enemy or going to a square next to the enemy
                    // the getShortestPath() algorithm has to allow it to more - so the bolFreeTravelZone is reset before search
                    bool bolRememberFrom = thisNode.OFDInfo.bolFreeTravelZone[udrMoveFromLoc.X, udrMoveFromLoc.Y];
                    thisNode.OFDInfo.bolFreeTravelZone[udrMoveFromLoc.X, udrMoveFromLoc.Y] = false;

                    bool bolRememberTo = thisNode.OFDInfo.bolFreeTravelZone[udrMoveToLoc.X, udrMoveToLoc.Y];
                    thisNode.OFDInfo.bolFreeTravelZone[udrMoveToLoc.X, udrMoveToLoc.Y] = false;

                    string strPath = thisNode.unitInfo[intUnitIndex].unit.getShortestPath(udrMoveFromLoc, udrMoveToLoc, false, false, thisNode.OFDInfo.bolFreeTravelZone);

                    thisNode.OFDInfo.bolFreeTravelZone[udrMoveFromLoc.X, udrMoveFromLoc.Y] = bolRememberFrom;
                    thisNode.OFDInfo.bolFreeTravelZone[udrMoveToLoc.X, udrMoveToLoc.Y] = bolRememberTo;

                    int intMovementCost = thisNode.unitInfo[intUnitIndex].unit.getMovementCostAlongTravelPath(strPath, thisNode.OFDInfo.Pos[0].udrLoc);
                    int intPosIndexMoveto = OFD_getIndexPos(thisNode.OFDInfo.Pos, udrMoveToLoc);

                    if (intMovementCost <= thisNode.unitInfo[intUnitIndex].intMovementPointsLeft)
                    {
                        // only allow this if the unit can continue to move
                        // or if it is stopped by enemy AND it is in a square suitable to its type
                        if (!bolRememberTo
                            || (bolRememberTo && nodeNewChild.OFDInfo.Pos[intPosIndexMoveto].typeUnit == nodeNewChild.unitInfo[intUnitIndex].unit.myType))
                        {
                        nodeNewChild.OFDInfo.Pos[0].status = enuOpenFieldDefensePosition_Status.empty;
                        nodeNewChild.OFDInfo.Pos[0].unit = null;

                        nodeNewChild.unitInfo[intUnitIndex].intMovementPointsLeft -= intMovementCost;
                        nodeNewChild.unitInfo[intUnitIndex].pt = udrMoveToLoc;
                        nodeNewChild.unitInfo[intUnitIndex].fullstop = bolRememberTo;
                        
                        if (intPosIndexMoveto >= 0)
                        {
                            nodeNewChild.OFDInfo.Pos[intPosIndexMoveto].unit = nodeNewChild.unitInfo[intUnitIndex].unit;
                            nodeNewChild.OFDInfo.Pos[intPosIndexMoveto].status =
                                (nodeNewChild.OFDInfo.Pos[intPosIndexMoveto].typeUnit == nodeNewChild.OFDInfo.Pos[intPosIndexMoveto].unit.myType)
                                ? enuOpenFieldDefensePosition_Status.safe
                                : enuOpenFieldDefensePosition_Status.occupied;

                            if (bolRememberTo)
                            {
                                /// this unit has been stopped by the enemy
                                /// it is in a square suitable to its type - this unit cannot move 
                                nodeNewChild.OFDInfo.Pos[intPosIndexMoveto].bolSearchComplete = true;                             
                            }
                        }
                        

                        nodeNewChild.childNodes = new classOpenFieldDefenseSearchTreeNode[0];
                        nodeNewChild.parentNode = thisNode;
                        nodeNewChild.OFDInfo.Pos = OFD_getPrioritizedLocs(nodeNewChild.OFDInfo, nodeNewChild.unitInfo);
                        nodeNewChild.strStepFromParent = strUnitId
                                                       + strPath
                                                       + classOpenFieldDefenseSearchTreeNode.strSolutionFieldDelimiter;

                        if (nodeNewChild.OFDInfo.Pos[0].unit == null)
                            nodeNewChild.command = enuOpenFieldDefenseSearchTree_Command.GoToPriorityPos;
                        else
                            nodeNewChild.command = enuOpenFieldDefenseSearchTree_Command.VacatePriorityPos;
                        
                        Array.Resize<classOpenFieldDefenseSearchTreeNode>(ref thisNode.childNodes, thisNode.childNodes.Length + 1);
                        thisNode.childNodes[thisNode.childNodes.Length - 1] = nodeNewChild;

                        udrResult = OFD_GetResult(thisNode);
                        return udrResult;
                    }
                    }
                }
            }
         
            udrResult = OFD_GetResult(thisNode);
            return udrResult;
        }

        int OFD_getIndexUnitInUnitInfo(classOpenFieldDefenseSearchTreeNode thisNode, int intUnitID)
        {
            for (int intUnitCounter = 0; intUnitCounter < thisNode.unitInfo.Length; intUnitCounter++)
                if (intUnitID == thisNode.unitInfo[intUnitCounter].intUnitID)
                    return intUnitCounter;
            return -1;
        }

        string OFD_getCartesianString(Point udrLoc)
        { return udrLoc.X.ToString().PadLeft(intStandardCartesianStringFieldLength, '0') + udrLoc.Y.ToString().PadLeft(intStandardCartesianStringFieldLength, '0'); }

        bool OFD_ThereIsAnUntriedPlaceForUnitToMoveFromPP(classOpenFieldDefenseSearchTreeNode thisNode, ref Point udrMoveToLoc)
        {
            string strTriedTargets = "";
            if (thisNode.childNodes.Length >0)
                for (int intChildNodeCounter = 0; intChildNodeCounter < thisNode.childNodes.Length; intChildNodeCounter++)
                    strTriedTargets += classOpenFieldDefenseSearchTreeNode.strSolutionFieldDelimiter + OFD_getCartesianString(thisNode.childNodes[intChildNodeCounter].udrVPP_TargetLoc) + classOpenFieldDefenseSearchTreeNode.strSolutionFieldDelimiter;

            // first try locations which are OFD of suitable type
            for (int intPosCounter = 1; intPosCounter < thisNode.OFDInfo.Pos.Length; intPosCounter++)
            {
                if (thisNode.OFDInfo.Pos[intPosCounter].status == enuOpenFieldDefensePosition_Status.empty)
                    if (thisNode.OFDInfo.Pos[intPosCounter].typeUnit == thisNode.OFDInfo.Pos[0].unit.myType)
                    {
                        string strPath = thisNode.OFDInfo.Pos[0].unit.getShortestPath(thisNode.OFDInfo.Pos[0].udrLoc, thisNode.OFDInfo.Pos[intPosCounter].udrLoc, false, false, thisNode.OFDInfo.bolFreeTravelZone);
                        if (strPath.Length > 0)
                        {
                            int intCostMove = thisNode.OFDInfo.Pos[0].unit.getMovementCostAlongTravelPath(strPath, thisNode.OFDInfo.Pos[intPosCounter].udrLoc);
                            int intIndexUnitInUnitInfo = OFD_getIndexUnitInUnitInfo(thisNode, thisNode.OFDInfo.Pos[0].unit.MyID);
                            if (intIndexUnitInUnitInfo >= 0)
                                if (intCostMove < thisNode.unitInfo[intIndexUnitInUnitInfo].intMovementPointsLeft)
                                {
                                    if (!strTriedTargets.Contains(classOpenFieldDefenseSearchTreeNode.strSolutionFieldDelimiter + OFD_getCartesianString(thisNode.OFDInfo.Pos[intPosCounter].udrLoc) + classOpenFieldDefenseSearchTreeNode.strSolutionFieldDelimiter))
                                    {
                                        udrMoveToLoc = thisNode.OFDInfo.Pos[intPosCounter].udrLoc;
                                        return true;
                                    }
                                }
                        }
                    }
            }

            // second try locations outside OFD Map 
            


            // try any unused OFD type regardless of suitable type


            return false;
        }

        classOpenFieldDefenseSearch_Result OFD_HandleNode_GPP(ref classOpenFieldDefenseSearchTreeNode thisNode, ref classBattleUnit unitWorker)
        { // Go to Priority Position (in OFDMap)
            /// the target location is known for this step of the solution but the unit to move is undetermined
            /// this node finds a possible path when an eligible unit has enough movement points to reach the target
            /// then a child node is created with the problem altered according to this unit's movement and the node is added to the childNode array 
            /// of this node
            /// when all units have been tried and there are no more solutions left to find then this node's boolean 
            ///                               thisNode.bolAllOptionsAreExhausted = true;             is set to true and
            /// function returns the solution so far as a possible best incomplete solution
            /// 
            classOpenFieldDefenseSearch_Result udrResult = new classOpenFieldDefenseSearch_Result();
            udrResult.intTotalMovementPointsLeft = 0;
            udrResult.strMoves = "";
            udrResult.intNumberPosLeftUnsolved = thisNode.OFDInfo.Pos.Length-1;

            Point udrMoveFromLoc;
            Point udrMoveToLoc;

            if (thisNode.OFDInfo.Pos.Length == 0)
            {
                udrResult = OFD_GetResult(thisNode);
            }
            else
            {
                Point udrPriorityPos = thisNode.OFDInfo.Pos[0].udrLoc;
                if (thisNode.OFDInfo.Pos[0].unit != null)
                {
                    // this should not happen
                    MessageBox.Show("stop ");
                }
                else
                {
                    /// since the search for suitable units is a BFS expanding from Priority Pos
                    /// and they are entered in the order they are found
                    /// trying these in the same order is reasonable
                    /// and so the index of the next one to try is the same as the number of attemps made -> number of child nodes
                    int intUnusedUnitIndex = thisNode.childNodes.Length; 
                    if (intUnusedUnitIndex >= 0 && intUnusedUnitIndex < thisNode.OFDInfo.Pos[0].suitableUnitsThatCanReachThisPos.Length )
                    {
                        classOpenFieldDefenseSearchTreeNode nodeNewChild = OFD_getNode_Copy(thisNode);
                        string strUnitId = thisNode.OFDInfo.Pos[0].suitableUnitsThatCanReachThisPos[intUnusedUnitIndex].strUnitID;  //  .unitInfo[intUnusedUnitIndex].unit.myIDstring;
                        udrMoveFromLoc = thisNode.OFDInfo.Pos[0].suitableUnitsThatCanReachThisPos[intUnusedUnitIndex].pt;
                        udrMoveToLoc = thisNode.OFDInfo.Pos[0].udrLoc;

                        // if the unit is either on a square next to enemy or going to a square next to the enemy
                        // the getShortestPath() algorithm has to allow it to more - so the bolFreeTravelZone is reset before search
                        bool bolRememberFrom = thisNode.OFDInfo.bolFreeTravelZone[udrMoveFromLoc.X, udrMoveFromLoc.Y];
                        thisNode.OFDInfo.bolFreeTravelZone[udrMoveFromLoc.X, udrMoveFromLoc.Y] = false;

                        bool bolRememberTo = thisNode.OFDInfo.bolFreeTravelZone[udrMoveToLoc.X, udrMoveToLoc.Y];
                        thisNode.OFDInfo.bolFreeTravelZone[udrMoveToLoc.X, udrMoveToLoc.Y] = false;

                        string strPath = thisNode.OFDInfo.Pos[0].suitableUnitsThatCanReachThisPos[intUnusedUnitIndex].unit.getShortestPath(udrMoveFromLoc, udrMoveToLoc, false, false, thisNode.OFDInfo.bolFreeTravelZone);

                        thisNode.OFDInfo.bolFreeTravelZone[udrMoveFromLoc.X, udrMoveFromLoc.Y] = bolRememberFrom;
                        thisNode.OFDInfo.bolFreeTravelZone[udrMoveToLoc.X, udrMoveToLoc.Y] = bolRememberTo;

                        int intMovementCost = thisNode.OFDInfo.Pos[0].suitableUnitsThatCanReachThisPos[intUnusedUnitIndex].unit.getMovementCostAlongTravelPath(strPath, thisNode.OFDInfo.Pos[0].suitableUnitsThatCanReachThisPos[intUnusedUnitIndex].pt);

                        nodeNewChild.OFDInfo.Pos[0].bolSearchComplete = true;
                        nodeNewChild.OFDInfo.Pos[0].status = enuOpenFieldDefensePosition_Status.safe;
                        nodeNewChild.OFDInfo.Pos[0].unit = thisNode.OFDInfo.Pos[0].suitableUnitsThatCanReachThisPos[intUnusedUnitIndex].unit;

                        int intIndexPosUnitWas = OFD_getIndexPos(nodeNewChild.OFDInfo.Pos, thisNode.OFDInfo.Pos[0].suitableUnitsThatCanReachThisPos[intUnusedUnitIndex].pt);
                        if (intIndexPosUnitWas >0)
                        {
                            nodeNewChild.OFDInfo.Pos[intIndexPosUnitWas].unit = null;
                            nodeNewChild.OFDInfo.Pos[intIndexPosUnitWas].status = enuOpenFieldDefensePosition_Status.empty;
                        }

                        int intUnitInfoIndex = OFD_getIndexUnitInUnitInfo(nodeNewChild, thisNode.OFDInfo.Pos[0].suitableUnitsThatCanReachThisPos[intUnusedUnitIndex].intUnitID);

                        nodeNewChild.unitInfo[intUnitInfoIndex].intMovementPointsLeft -= intMovementCost;
                        nodeNewChild.unitInfo[intUnitInfoIndex].pt = nodeNewChild.OFDInfo.Pos[0].udrLoc;
                        nodeNewChild.unitInfo[intUnitInfoIndex].fullstop = true;

                        nodeNewChild.parentNode = thisNode;
                        nodeNewChild.OFDInfo.Pos = OFD_getPrioritizedLocs(nodeNewChild.OFDInfo, nodeNewChild.unitInfo);
                        nodeNewChild.strStepFromParent = strUnitId 
                                                       + strPath
                                                       + classOpenFieldDefenseSearchTreeNode.strSolutionFieldDelimiter;

                        Array.Resize<classOpenFieldDefenseSearchTreeNode>(ref thisNode.childNodes, thisNode.childNodes.Length + 1);
                        thisNode.childNodes[thisNode.childNodes.Length - 1] = nodeNewChild;

                        if (nodeNewChild.OFDInfo.Pos.Length > 0)
                        {
                            if (nodeNewChild.OFDInfo.Pos[0].unit == null)
                                nodeNewChild.command = enuOpenFieldDefenseSearchTree_Command.GoToPriorityPos;
                            else
                                nodeNewChild.command = enuOpenFieldDefenseSearchTree_Command.VacatePriorityPos;
                        }
                        else
                        {
                            thisNode.bolAllOptionsAreExhausted = true;
                            thisNode.bolValidSolution = true;
                            udrResult = OFD_GetResult(nodeNewChild);
                            return udrResult;
                        }

                        udrResult = OFD_GetResult(thisNode);
                        return udrResult;
                    }
                    else
                    {
                        thisNode.bolAllOptionsAreExhausted = true;
                        udrResult = OFD_GetResult(thisNode);
                        return udrResult;
                    }
                }
            }

            udrResult = OFD_GetResult(thisNode);
            return udrResult;
        }

        classOpenFieldDefense_Position OFD_getPosCopy(classOpenFieldDefense_Position ofdPos)
        {
            classOpenFieldDefense_Position udrRetVal = new classOpenFieldDefense_Position();
            udrRetVal.bolSearchComplete = ofdPos.bolSearchComplete;
            udrRetVal.status = ofdPos.status;
            udrRetVal.typeUnit = ofdPos.typeUnit;
            udrRetVal.udrLoc.X = ofdPos.udrLoc.X;
            udrRetVal.udrLoc.Y = ofdPos.udrLoc.Y;
            udrRetVal.unit = ofdPos.unit;
            return udrRetVal;
        }

        classOpenFieldDefenseSearchTreeNode OFD_getNode_Copy(classOpenFieldDefenseSearchTreeNode thisNode)
        {
            classOpenFieldDefenseInfo udrOFDInfo = new classOpenFieldDefenseInfo();
            udrOFDInfo.bolAvoidMap = getBolDefaultAvoidMap();
            udrOFDInfo.bolFreeTravelZone = thisNode.OFDInfo.bolFreeTravelZone;
            udrOFDInfo.deploymentMap = thisNode.OFDInfo.deploymentMap;

            udrOFDInfo.Pos = new classOpenFieldDefense_Position[thisNode.OFDInfo.Pos.Length];
            for (int intPosCounter = 0; intPosCounter < thisNode.OFDInfo.Pos.Length; intPosCounter++)
                udrOFDInfo.Pos[intPosCounter] = OFD_getPosCopy(thisNode.OFDInfo.Pos[intPosCounter]);

            classOpenFieldDefenseSearch_UnitInfo[] unitInfo = new classOpenFieldDefenseSearch_UnitInfo[thisNode.unitInfo.Length];
            for (int intUnitCounter = 0; intUnitCounter < thisNode.unitInfo.Length; intUnitCounter++)
            {
                unitInfo[intUnitCounter] = new classOpenFieldDefenseSearch_UnitInfo();
                unitInfo[intUnitCounter].intMovementPointsLeft = thisNode.unitInfo[intUnitCounter].intMovementPointsLeft;
                unitInfo[intUnitCounter].fullstop = thisNode.unitInfo[intUnitCounter].fullstop;
                unitInfo[intUnitCounter].intUnitID = thisNode.unitInfo[intUnitCounter].intUnitID;
                unitInfo[intUnitCounter].strUnitID = thisNode.unitInfo[intUnitCounter].strUnitID;
                unitInfo[intUnitCounter].pt.X = thisNode.unitInfo[intUnitCounter].pt.X;
                unitInfo[intUnitCounter].pt.Y = thisNode.unitInfo[intUnitCounter].pt.Y;
                unitInfo[intUnitCounter].unit = thisNode.unitInfo[intUnitCounter].unit;
            }

            classOpenFieldDefenseSearchTreeNode nodeCopy = new classOpenFieldDefenseSearchTreeNode(thisNode.parentNode, thisNode.command, thisNode.strStepFromParent, udrOFDInfo, unitInfo);
            return nodeCopy;
        }

        classOpenFieldDefenseSearch_UnitInfo[] OFD_getSuitableUnitsThatCanReachThisPos(classOpenFieldDefenseInfo udrOFDInfo, classOpenFieldDefenseSearch_UnitInfo[] unitInfo, Point udrLoc)
        {
           /// finds the shortest path to all enemy units
            /// is bound by bolAvoidMap, not to step on squares in boolean array set to true
            /// search outwards from loc and records the suitable units found in they order they are found
            int intInfinity = (int)Math.Pow(10, 5);
            classOpenFieldDefenseSearch_UnitInfo[] udrSuitableUnitsThatCanReachThisPosition = new classOpenFieldDefenseSearch_UnitInfo[0];

            classOpenFieldDefenseSearch_UnitInfo[,] udrUnitArray = new classOpenFieldDefenseSearch_UnitInfo[szMapSize.Width, szMapSize.Height];
            for (int intUnitCounter = 0; intUnitCounter < unitInfo.Length; intUnitCounter++)
                udrUnitArray[unitInfo[intUnitCounter].pt.X, unitInfo[intUnitCounter].pt.Y] = unitInfo[intUnitCounter];

            classBattleUnit.classPathMap[,] udrPathMap = new classBattleUnit.classPathMap[szMapSize.Width, szMapSize.Height];
            for (int intX = 0; intX < szMapSize.Width; intX++)
                for (int intY = 0; intY < szMapSize.Height; intY++)
                {
                    udrPathMap[intX, intY] = new classBattleUnit.classPathMap();
                    udrPathMap[intX, intY].direction = classBattle.enuDirections._num_directions;
                    udrPathMap[intX, intY].intMovementCost = intInfinity;
                    udrPathMap[intX, intY].unit = terrain[intX, intY].unit;
                }

            classShortestPathQelement SPEleQ = null;

            int intIndexLosInPosArray = OFD_getIndexPos(udrOFDInfo.Pos, udrLoc);

            classBattle.enuUnitTypes PriorityUnitType = udrOFDInfo.Pos[intIndexLosInPosArray].typeUnit;
            classBattleUnit unit_Worker = new classBattleUnit(PriorityUnitType, TurnColor, classBattle.enuUnitRanks.lieutenant);

            
            classShortestPathQelement firstQele = new classShortestPathQelement(0, udrLoc, classBattle.enuDirections._num_directions);
            unit_Worker.enQShortestPathElement(ref SPEleQ, ref firstQele);

            udrPathMap[firstQele.pos.X, firstQele.pos.Y].intMovementCost = 0;

            classShortestPathQelement thisQEle = unit_Worker.deQShortestPathElement(ref SPEleQ);
            bool[,] bolAvoidMap = getBolDefaultAvoidMap();
            int intMaxMovementCost =  unit_Worker.intMovementPoints_Max;
          
            while (thisQEle != null
                && thisQEle.intMovementCost < intMaxMovementCost)
            {
                string strDir = "";
                for (classBattle.enuDirections dirCounter = 0; dirCounter < classBattle.enuDirections._num_directions; dirCounter++)
                {
                    Point udrNextPos = getNextPos(thisQEle.pos, dirCounter);
                    if (udrNextPos.X >= 0 && udrNextPos.Y >= 0 && udrNextPos.X < szMapSize.Width && udrNextPos.Y < szMapSize.Height)
                    {
                        int intMovementCost_new = thisQEle.intMovementCost + getMovementCost(udrNextPos);
                        if (intMovementCost_new < udrPathMap[udrNextPos.X, udrNextPos.Y].intMovementCost
                            && !bolAvoidMap[udrNextPos.X, udrNextPos.Y])
                        {
                            strDir += dirCounter.ToString();
                        }
                    }
                }

                if (udrUnitArray[thisQEle.pos.X, thisQEle.pos.Y] != null)
                {
                    if (udrUnitArray[thisQEle.pos.X, thisQEle.pos.Y].unit.myType == PriorityUnitType)
                    {
                        if (!udrUnitArray[thisQEle.pos.X, thisQEle.pos.Y].fullstop)
                            if (thisQEle.intMovementCost < udrUnitArray[thisQEle.pos.X, thisQEle.pos.Y].intMovementPointsLeft)
                            {
                                Array.Resize<classOpenFieldDefenseSearch_UnitInfo>(ref udrSuitableUnitsThatCanReachThisPosition, udrSuitableUnitsThatCanReachThisPosition.Length + 1);
                                udrSuitableUnitsThatCanReachThisPosition[udrSuitableUnitsThatCanReachThisPosition.Length - 1] = udrUnitArray[thisQEle.pos.X, thisQEle.pos.Y];
                            }
                    }
                }


                while (strDir.Length > 0)
                {
                    string strThisDir = strDir.Substring(0, 2);
                    strDir = strDir.Substring(2);
                    classBattle.enuDirections dir_new = dir_new = getDirFromString(strThisDir);
                    Point udrPos_new = getNextPos(thisQEle.pos, dir_new);
                    int intMovementCost_new = thisQEle.intMovementCost + getMovementCost(udrPos_new);


                    if (intMovementCost_new < udrPathMap[udrPos_new.X, udrPos_new.Y].intMovementCost
                        && !bolAvoidMap[udrPos_new.X, udrPos_new.Y])
                    {
                        udrPathMap[udrPos_new.X, udrPos_new.Y].direction = getOppositedirection(dir_new);
                        udrPathMap[udrPos_new.X, udrPos_new.Y].intMovementCost = intMovementCost_new;
                        udrPathMap[udrPos_new.X, udrPos_new.Y].unit = terrain[udrPos_new.X, udrPos_new.Y].unit;

                        classShortestPathQelement NewQele = new classShortestPathQelement(udrPathMap[udrPos_new.X, udrPos_new.Y].intMovementCost, udrPos_new, udrPathMap[udrPos_new.X, udrPos_new.Y].direction);
                      unit_Worker.  enQShortestPathElement(ref SPEleQ, ref NewQele);
                    }
                }
                thisQEle = unit_Worker.deQShortestPathElement(ref SPEleQ);
            }
            return udrSuitableUnitsThatCanReachThisPosition;
            //return retval;
        }

        classOpenFieldDefense_Position[] OFD_getPrioritizedLocs(classOpenFieldDefenseInfo udrOFDInfo, classOpenFieldDefenseSearch_UnitInfo[] udrUnitInfo)
        {
            /// open field deployment
            /// given the list of positions left unprioritized and the information of current unit positions and movement pts at this point of solution
            /// all positions which do not currently have a unit of correct type on them - considered 'safe' but not 'searchcomplete'
            /// are counted for each type of unit, their values held in an integer array
            /// 
            /// the PRIORITY TYPE is given to artillery first if there are still artillery positions left to fill
            /// then to infantry, or cavalry last
            /// 
            /// during the search if a position is found that is of a type which has no unfilled locations then that position is set to 'bolsearchcomplete'
            ///  and is not added to the new array of positions which is returned to the calling function
            ///  
            /// those added to the return value are 'ranked' by counting the number of units that can reach them if they are of PRIORITY TYPE
            /// if they are not of priority type they are added and passed on for future priorities
            /// these units are added to the position's list of units that can reach it
            /// 
            /// then, when all positions have been scanned and a complete list is made for the return value
            /// these are prioritized with those or priority type set to the front of the array and reordered using the size of each position's 
            /// list of units that can reach them, placing the position with the least options (units that can reach it) at the top 0th element
            classOpenFieldDefense_Position[] udrRetVal = new classOpenFieldDefense_Position[0];
            int[] intNumUnSafePos = new int[(int)classBattle.enuUnitTypes._num_UnitTypes];
            bool[] bolPosLeftToSet = new bool[(int)classBattle.enuUnitTypes._num_UnitTypes];
            for (int intPosCounter = 0; intPosCounter < udrOFDInfo.Pos.Length; intPosCounter++)
            {   
                if (!udrOFDInfo.Pos[intPosCounter].bolSearchComplete)
                    intNumUnSafePos[(int)udrOFDInfo.Pos[intPosCounter].typeUnit]++;
                if (udrOFDInfo.Pos[intPosCounter].unit == null)
                    bolPosLeftToSet[(int)udrOFDInfo.Pos[intPosCounter].typeUnit] = true;
                else if (udrOFDInfo.Pos[intPosCounter].unit.myType != udrOFDInfo.Pos[intPosCounter].typeUnit)
                    bolPosLeftToSet[(int)udrOFDInfo.Pos[intPosCounter].typeUnit] = true;
            }
            
            classBattle.enuUnitTypes PriorityType = classBattle.enuUnitTypes.cavalry;
            if (intNumUnSafePos[(int)classBattle.enuUnitTypes.artillery] > 0 && bolPosLeftToSet[(int)classBattle.enuUnitTypes.artillery])
                PriorityType = classBattle.enuUnitTypes.artillery;
            else if (intNumUnSafePos[(int)classBattle.enuUnitTypes.infantry] > 0 && bolPosLeftToSet[(int)classBattle.enuUnitTypes.infantry])
                PriorityType = classBattle.enuUnitTypes.infantry;

            for (int intPosCounter = 0; intPosCounter < udrOFDInfo.Pos.Length; intPosCounter++)
            {
                if (!bolPosLeftToSet[(int)udrOFDInfo.Pos[intPosCounter].typeUnit])
                    udrOFDInfo.Pos[intPosCounter].bolSearchComplete = true;

                if (!udrOFDInfo.Pos[intPosCounter].bolSearchComplete)
                {
                    Array.Resize<classOpenFieldDefense_Position>(ref udrRetVal, udrRetVal.Length + 1);
                    udrRetVal[udrRetVal.Length - 1] = udrOFDInfo.Pos[intPosCounter];
                    udrRetVal[udrRetVal.Length - 1].suitableUnitsThatCanReachThisPos = new classOpenFieldDefenseSearch_UnitInfo[0];
                    udrRetVal[udrRetVal.Length - 1].typeUnit = udrOFDInfo.Pos[intPosCounter].typeUnit;
                    if (udrRetVal[udrRetVal.Length - 1].typeUnit == PriorityType)
                        udrRetVal[udrRetVal.Length - 1].suitableUnitsThatCanReachThisPos = OFD_getSuitableUnitsThatCanReachThisPos(udrOFDInfo, udrUnitInfo, udrRetVal[udrRetVal.Length - 1].udrLoc);
                }
            }

            int intBestCount = 0;
            int intBestIndex = 0;
            classOpenFieldDefense_Position udrTemp = new classOpenFieldDefense_Position();

            for (int intOuterLoop = 0; intOuterLoop < udrRetVal.Length - 1; intOuterLoop++)
            {
                if (udrRetVal[intOuterLoop].typeUnit == PriorityType
                    && (udrRetVal[intOuterLoop ].unit == null 
                            || (udrRetVal[intOuterLoop ].unit !=null && udrRetVal[intOuterLoop].typeUnit != udrRetVal[intOuterLoop].unit.myType)))
                {
                    intBestCount = udrRetVal[intOuterLoop].suitableUnitsThatCanReachThisPos.Length;
                    intBestIndex = intOuterLoop;
                }
                else
                {
                    intBestCount = 100000;
                    intBestIndex = intOuterLoop;
                }
                for (int intInnerLoop = intOuterLoop+1; intInnerLoop < udrRetVal.Length; intInnerLoop++)
                {
                    if (udrRetVal[intInnerLoop].typeUnit == PriorityType)
                    {
                        if (udrRetVal[intInnerLoop].unit == null 
                            || (udrRetVal[intInnerLoop].unit !=null && udrRetVal[intInnerLoop].typeUnit != udrRetVal[intInnerLoop].unit.myType))
                        {
                            if (udrRetVal[intInnerLoop].suitableUnitsThatCanReachThisPos.Length < intBestCount)
                            {
                                intBestCount = udrRetVal[intInnerLoop].suitableUnitsThatCanReachThisPos.Length;
                                intBestIndex = intInnerLoop;
                            }
                        }
                    }
                }
                if (intBestIndex != intOuterLoop)
                {
                    udrTemp = udrRetVal[intOuterLoop];
                    udrRetVal[intOuterLoop] = udrRetVal[intBestIndex];
                    udrRetVal[intBestIndex] = udrTemp;
                }
            }

            return udrRetVal;
        }

        void OFD_setBestOpeningMoves()
        {
            /// Open Field Defense - 
            /// this functions finds searches for a series of moves which will place the defending units
            /// in their 'ideal' locations as defined by the OFD_SetMap() function
            /// 
            /// it is a depth first search algorithm which attemps every possible reasonable combination
            /// keeping track of what it has tried with a tree-like data structure of nodes that branch
            /// out from a root node which is defined by the input problem - current field deployment
            /// taking in consideration 
            ///     - the movement cost and limited mobility range of each unit
            ///     - the fullstop incurred by units which step into a square adjacent to the enemy
            ///     - selecting the solution which results in closest complete solution
            ///        or the complete solution with the most remaining movement points shared by all units
            ///        
            /// there are two different types of move
            ///   - vacate priority position (when occupied by a unit not of correct type)
            ///   - occupy vacant priority position
            ///   
            /// all positions are defined in an array at root-node
            /// the next node to be explored is found travelling along the path of childnodes
            /// which still have unexplored paths to be tried
            /// this 'next node' is either a 'vacate' or 'occupy' node
            ///  - vacate : the unit to vacate the position is already determined
            ///             the location where it is to move is not
            ///             selecting a position is done by first trying to move it to an unoccupied OFDPos of its type
            ///             when these are exhausted and do not result in complete solution then other OFDPos are tried
            ///  - occupy : the location to be occupied is already determined
            ///             the unit to move into priority position is not
            ///             selecting a unit to move is done sequentially
            ///             
            /// positions are prioritized according to :
            ///   - first priority is type, all artillery units must be placed first as they have the least mobility, then infantry, finally cavalry
            ///   - second, once the priority type has been determined depending on the number of each type left, the pos where the least number of 
            ///        units of the correct type can move to is set to the highest priority
            ///   
            ///       
            /// for each move a new node is created and added to an array of childNodes
            /// a string strMovePath holds the unitIDstring and a sequence of 2-char move-commands describing the directions in which the unit is to move
            ///    e.g. 00001NWNWNWSWNWN_, unit #1 is to move northwest 3 times, southwest once, northwest and north
            /// 
            classOpenFieldDefenseSearch_Result udrResult = new classOpenFieldDefenseSearch_Result();
            udrResult.intNumberPosLeftUnsolved = udrCDInfo.Pos.Length;
            udrResult.intTotalMovementPointsLeft = 0;
            udrResult.strMoves = "";
            highlightOpenFieldDefenseDeploymentMap(udrCDInfo);
            int intStepsCount = 0;
            int intMaxSteps = 5000;
            udrCDInfo.bolAvoidMap = getBolDefaultAvoidMap();
            classOpenFieldDefenseSearchTreeNode rootNode = new classOpenFieldDefenseSearchTreeNode(null,
                                                                                                   enuOpenFieldDefenseSearchTree_Command.GoToPriorityPos, 
                                                                                                   "", 
                                                                                                   udrCDInfo, 
                                                                                                   OFD_getUnitInfo());
            rootNode.OFDInfo.Pos = OFD_getPrioritizedLocs(rootNode.OFDInfo, rootNode.unitInfo);
            if (rootNode.OFDInfo.Pos.Length > 0)
            {
                rootNode.command = (rootNode.OFDInfo.Pos[0].unit == null)
                    ? enuOpenFieldDefenseSearchTree_Command.GoToPriorityPos
                    : enuOpenFieldDefenseSearchTree_Command.VacatePriorityPos;

                do
                {
                    classOpenFieldDefenseSearchTreeNode thisNode = OFD_getLastNodeInSearchTree(rootNode);
                    classOpenFieldDefenseSearch_Result thisResult = new classOpenFieldDefenseSearch_Result();
                    switch (thisNode.command)
                    {
                        case enuOpenFieldDefenseSearchTree_Command.GoToPriorityPos:
                            thisResult = OFD_HandleNode_GPP(ref thisNode, ref Unit_Q[(int)TurnColor]);
                            break;

                        case enuOpenFieldDefenseSearchTree_Command.VacatePriorityPos:
                            thisResult = OFD_HandleNode_VPP(ref thisNode, ref Unit_Q[(int)TurnColor]);
                            break;
                    }

                    if (thisResult.intNumberPosLeftUnsolved == 0)
                    {
                        if (udrResult.intTotalMovementPointsLeft > thisResult.intTotalMovementPointsLeft)
                            udrResult = thisResult;
                    }
                    else if (udrResult.intNumberPosLeftUnsolved > thisResult.intNumberPosLeftUnsolved)
                        udrResult = thisResult;
                    intStepsCount++;

                } while (!rootNode.bolAllOptionsAreExhausted && intStepsCount < intMaxSteps);

            }
           udrCDInfo.strMoveSequence = udrResult.strMoves;
        }

        void OFD_setMap()
        { OFD_setMap(udrCastleLoc); }
        void OFD_setMap(Point udrDefendLoc)
        {
            /// Open Field Defense - deployment initialization
            /// 
            ///  this function sets-up a map of the 'ideal' locations where the defending units (TurnColor) should
            /// each deploy themselves around udrDefendLoc (cartesian) location - positions are defined by type NOT SPECIFIC UNIT
            /// first the artillery positions are selected
            /// 
            /// Selecting Artillery Positions
            /// i) bolAvoidMap[,] array of booleans is initialized to locations that are impassable
            /// ii) set first artillery position on defendLoc
            /// 
            /// 1) start from defendLoc and follow shortest path to nearest enemy given bolAvoidMap[,] of locations to avoid
            /// 2) follow path until first vacant (still not set in udrCDInfo) position then
            /// 3) randomly pick between left/right
            /// 4) move & turn in direction twice 
            /// 6) if this location is still vacant set artillery location there
            /// 6.1) if this location is not vacant move & turn in opposite direction twice 
            /// 6.2) if opposite dir loc is vacant set artillery location there
            /// 6.3 )if neither position is vacant set bolAvoidMap[,] to the first empty square along path from which we turned 
            /// 7) go to 1 until all artillery are positioned or no more spacefound
            /// 
            /// Selecting Infantry/Cavalry positions
            /// i) reset bolAvoidMap() to defaul impassable terrain map
            /// 1) move along path from defendLoc to nearest enemy never stepping on a bolAvoidMap[,] square
            /// 2) at first vacant square, place infantry/cavalry (infantry until all infantry have been positioned then cavalry last)
            /// 3) set this location on bolAvoidMap - force findShortestPath() to enemy algorithm to go around
            /// 4) go to 1 until all units have ideal positions mapped
            /// 
             classBattleUnit unit_worker;
            bool bolNeedToKillWorker = false;
        startSetOpenFieldDefenseDeployment:

            if (terrain[udrDefendLoc.X, udrDefendLoc.Y].unit == null)
            {
                unit_worker = new classBattleUnit(classBattle.enuUnitTypes.infantry, TurnColor, classBattle.enuUnitRanks.lieutenant);
                unit_worker.myPosition = udrDefendLoc;
                unit_worker.bolDie = true;
                enQUnit(ref unit_worker);
                bolNeedToKillWorker = true;
            }
            else
                unit_worker = terrain[udrDefendLoc.X, udrDefendLoc.Y].unit;

            udrCDInfo.Pos = new classOpenFieldDefense_Position[0];
            udrCDInfo.deploymentMap = new classOpenFieldDefense_MapUnit[szMapSize.Width, szMapSize.Height];
            for (int intX = 0; intX < szMapSize.Width; intX++)
                for (int intY = 0; intY < szMapSize.Height; intY++)
                {
                    udrCDInfo.deploymentMap[intX, intY] = new classOpenFieldDefense_MapUnit();
                    udrCDInfo.deploymentMap[intX, intY].typeUnit = classBattle.enuUnitTypes._num_UnitTypes;
                }

            udrCDInfo.bolFreeTravelZone  = getBolDefaultAvoidMap();
            classBattleUnit thisEnemyUnit = Unit_Q[((int)TurnColor + 1) % 2];
            while (thisEnemyUnit != null)
            {
                setBolArrayAroundLoc(ref udrCDInfo.bolFreeTravelZone, thisEnemyUnit.myPosition);
                thisEnemyUnit = thisEnemyUnit.next;
            }

            udrCDInfo.bolAvoidMap = getBolDefaultAvoidMap();
            int[] intNumUnitTypes = countUnitTypesInQ(ref Unit_Q[(int)TurnColor]);
            string strShortestPathToNearestEnemy;
            Point[] udrSearchStarLoc = new Point[0];
            addLocToArray(ref udrSearchStarLoc, udrDefendLoc);
            int intIndexCurrentSearchElement = 0;
            if (intNumUnitTypes[(int)classBattle.enuUnitTypes.artillery] > 0)
            {
                addOFDPosToArray(ref udrCDInfo, udrDefendLoc, classBattle.enuUnitTypes.artillery);

                for (int intArtilleryCounter = 1; intArtilleryCounter < intNumUnitTypes[(int)classBattle.enuUnitTypes.artillery]; intArtilleryCounter++)
                {
                startThisUnit:
                    if (intIndexCurrentSearchElement < udrSearchStarLoc.Length)
                    {
                        // find shortest path to nearest Red unit
                        strShortestPathToNearestEnemy = unit_worker.getShortestPathToNearestEnemyUnit(udrSearchStarLoc[intIndexCurrentSearchElement], udrCDInfo.bolAvoidMap);
                        if (strShortestPathToNearestEnemy.Length == 0)
                            goto exitSetOpenFieldDefense;
                        classBattle.enuDirections dirFirstStep = getDirFromString(strShortestPathToNearestEnemy.Substring(0, 2));
                        // place this unit on the nearest empty space along this path
                        if (strShortestPathToNearestEnemy.Length > 2)
                        {
                            Point udrNextStepInPath = getNextPos(udrSearchStarLoc[intIndexCurrentSearchElement], dirFirstStep);
                            Point[] udrPosNext = new Point[2];
                            udrPosNext[0] = getPosTwoTurnsLeft(udrNextStepInPath, dirFirstStep);
                            udrPosNext[1] = getPosTwoTurnsRight(udrNextStepInPath, dirFirstStep);

                            int intRndIndex = 1;// (int)(rnd.NextDouble() * 1000) % 2;
                            int intRndIndexAlt = 0;// (intRndIndex + 1) % 2;
                            if (posUnusedOFDLoc(udrCDInfo, udrPosNext[intRndIndex])
                                && !udrCDInfo.bolFreeTravelZone[udrPosNext[intRndIndex].X, udrPosNext[intRndIndex].Y])
                            {
                                addOFDPosToArray(ref udrCDInfo, udrPosNext[intRndIndex], classBattle.enuUnitTypes.artillery);
                                addLocToArray(ref udrSearchStarLoc, udrPosNext[intRndIndex]);
                            }
                            else if (posUnusedOFDLoc(udrCDInfo, udrPosNext[intRndIndexAlt])
                                    && !udrCDInfo.bolFreeTravelZone[udrPosNext[intRndIndexAlt].X, udrPosNext[intRndIndexAlt].Y])
                            {
                                addOFDPosToArray(ref udrCDInfo, udrPosNext[intRndIndexAlt], classBattle.enuUnitTypes.artillery);
                                addLocToArray(ref udrSearchStarLoc, udrPosNext[intRndIndexAlt]);
                            }
                            else
                            {
                                udrCDInfo.bolAvoidMap[udrNextStepInPath.X, udrNextStepInPath.Y] = true;
                                intIndexCurrentSearchElement++;
                                goto startThisUnit;
                            }
                        }
                        else
                        {
                            Point udrNextStepAfterNearestEmptySquare = new Point();
                            Point udrNearestEmptySquareAlongPathToEnemy = OFD_getNearestUnusedSquareAlongPath(udrDefendLoc, strShortestPathToNearestEnemy, udrCDInfo, ref udrNextStepAfterNearestEmptySquare);

                            addOFDPosToArray(ref udrCDInfo, udrNearestEmptySquareAlongPathToEnemy, classBattle.enuUnitTypes.artillery);
                            addLocToArray(ref udrSearchStarLoc, udrNearestEmptySquareAlongPathToEnemy);// ?

                            // set bolAvoidMap of next-step after nearest empty square to true
                            udrCDInfo.bolAvoidMap[udrNextStepAfterNearestEmptySquare.X, udrNextStepAfterNearestEmptySquare.Y] = true;
                        }
                    }
                }
            }

            // place melee units 
            int intNumberMeleeUnits = intNumUnitTypes[(int)classBattle.enuUnitTypes.cavalry]
                                    + intNumUnitTypes[(int)classBattle.enuUnitTypes.infantry];

            udrCDInfo.bolAvoidMap = getBolDefaultAvoidMap();
            for (int intMeleeCounter = 1; intMeleeCounter < intNumberMeleeUnits; intMeleeCounter++)
            {
                // find nearest path to Red
                strShortestPathToNearestEnemy = unit_worker.getShortestPathToNearestEnemyUnit(udrCastleLoc, udrCDInfo.bolAvoidMap);
                if (strShortestPathToNearestEnemy.Length == 0)
                    goto exitSetOpenFieldDefense;
                // place this unit on the nearest empty space along this path
                Point udrNextStepAfterNearestEmptySquare = new Point();
                Point udrNearestEmptySquareAlongPathToEnemy = OFD_getNearestUnusedSquareAlongPath(udrDefendLoc, strShortestPathToNearestEnemy, udrCDInfo, ref udrNextStepAfterNearestEmptySquare);
                if (udrNearestEmptySquareAlongPathToEnemy.X == udrDefendLoc.X && udrNearestEmptySquareAlongPathToEnemy.Y == udrDefendLoc.Y)
                {
                    Point udrNextPos = getNextPos(udrDefendLoc, getDirFromString(strShortestPathToNearestEnemy.Substring(0, 2)));
                    udrCDInfo.bolAvoidMap[udrNextPos.X, udrNextPos.Y] = true;
                }
                else
                {
                    addOFDPosToArray(ref udrCDInfo, udrNearestEmptySquareAlongPathToEnemy, intMeleeCounter < intNumUnitTypes[(int)classBattle.enuUnitTypes.infantry] ? classBattle.enuUnitTypes.infantry : classBattle.enuUnitTypes.cavalry);
                    // set bolAvoidMap of next-step after nearest empty square to true
                    udrCDInfo.bolAvoidMap[udrNearestEmptySquareAlongPathToEnemy.X, udrNearestEmptySquareAlongPathToEnemy.Y] = true;
                }
            }
        exitSetOpenFieldDefense:
            if (bolNeedToKillWorker)
            {
                int intIndexPosUWIsAt = OFD_getIndexPos(udrCDInfo.Pos, unit_worker.myPosition);
                if (intIndexPosUWIsAt >= 0)
                {
                    udrCDInfo.Pos[intIndexPosUWIsAt].status = enuOpenFieldDefensePosition_Status.empty;
                    udrCDInfo.Pos[intIndexPosUWIsAt].unit = null;
                }
                killUnit(unit_worker);
            }
            highlightOpenFieldDefenseDeploymentMap(udrCDInfo);
        }

        public classOpenFieldDefenseSearch_UnitInfo[] OFD_getUnitInfo()
        {
            classOpenFieldDefenseSearch_UnitInfo[] udrRetVal = new classOpenFieldDefenseSearch_UnitInfo[0];

            classBattleUnit thisUnit = Unit_Q[(int)TurnColor];

            while (thisUnit != null)
            {
                Array.Resize<classOpenFieldDefenseSearch_UnitInfo>(ref udrRetVal, udrRetVal.Length + 1);
                udrRetVal[udrRetVal.Length - 1] = new classOpenFieldDefenseSearch_UnitInfo();
                udrRetVal[udrRetVal.Length - 1].intUnitID = thisUnit.MyID;
                udrRetVal[udrRetVal.Length - 1].strUnitID = thisUnit.myIDstring;
                udrRetVal[udrRetVal.Length - 1].intMovementPointsLeft = thisUnit.intMovementPoints;
                udrRetVal[udrRetVal.Length - 1].pt = thisUnit.myPosition;
                udrRetVal[udrRetVal.Length - 1].unit = thisUnit;

                thisUnit = thisUnit.next;
            }

            return udrRetVal;
        }

        void addLocToArray(ref Point[] udrArrayLoc, Point udrLocNew)
        {
            Array.Resize<Point>(ref udrArrayLoc, udrArrayLoc.Length + 1);
            udrArrayLoc[udrArrayLoc.Length - 1] = cLibBattle.getPos(udrLocNew.X, udrLocNew.Y);
        }

        Point OFD_getNearestUnusedSquareAlongPath(Point udrStartLoc, string strPath, classOpenFieldDefenseInfo udrCDInfo, ref Point udrNextStep)
        {// nearest unused open-field-defense position along path to enemy
            Point udrRetVal = udrStartLoc;

            if (strPath.Length == 0)
                return udrCastleLoc;
            udrNextStep = getNextPos(udrStartLoc, getDirFromString(strPath.Substring(0, 2))); ;
            strPath = strPath.Substring(2);
            while (strPath.Length > 0 && udrCDInfo.deploymentMap[udrRetVal.X, udrRetVal.Y].typeUnit != classBattle.enuUnitTypes._num_UnitTypes)
            {
                udrRetVal = udrNextStep;
                udrNextStep = getNextPos(udrNextStep, getDirFromString(strPath.Substring(0, 2)));
                strPath = strPath.Substring(2);
            }

            return udrRetVal;
        }

        void initDefense()
        {
            udrPOC[(int)classBattle.enuUnitColors.blue] = determinePointsOfConvergence();

            /// udrPOC.POCMap describes map in terms of Points-of-Convergence (POCs)
            ///      positive values show the POC locations 
            ///      negative values show the locations where the enemy can travel unimpeded
            ///      zeroes are safe-squares behind the POCs where the enemy cannot reach defensive side
            ///      

            reorderQ(ref Unit_Q[(int)TurnColor]);
        }

        classPOCSearchInfo determinePointsOfConvergence()
        {
            classPOCSearchInfo[] udrPOC = new classPOCSearchInfo[0];
            classPOCSearchInfo[] udrPOCOptions = new classPOCSearchInfo[0];
            classBattleUnit unitGhost = null;

            if (terrain[udrCastleLoc.X, udrCastleLoc.Y].unit == null)
            {
                // create a ghost unit on the castle
                unitGhost = new classBattleUnit(classBattle.enuUnitTypes.cavalry, TurnColor, classBattle.enuUnitRanks.lieutenant);

                unitGhost.myPosition = udrCastleLoc;
                enQUnit(ref unitGhost);
                unitGhost.bolDie = true; // this setting ensures the unit is destroyed before taking the field
            }
            classBattleUnit unit_Castle = terrain[udrCastleLoc.X, udrCastleLoc.Y].unit;

            /// search sets of POCs which block the enemy
            /// finding POCs :
            /// 1) get shortest path from castle to any enemy unit
            /// 2) travel along the shortest path searching for a POC (findPOCOnPath function)
            /// 3) record this POC, then proceed until find the furthest POC which has never been found before
            /// 4) when reach the enemy keep the last POC found, disallow its square in next get shortest path (1 above)
            /// 5) repeat 1,2,3 until no path from castle can be found
            /// 
            /// 6) when a set of POCs blocking the enemy is found they are recorded as a set then ignored in future searches
            ///    until no POCs are found
            /// 7) result is sets of POCs extending further away from the castle, 
            /// 8) no set of POC is considered if the enemy will reach them before defending side
            ///  
            /// selecting a set of POCs to defend
            /// - each set of POCs is judged according to the travel cost for the shortest round trip to all POCs and back to start point
            /// - this value is then multiplied by the number of POCs
            /// - the set of POCs with the smallest 'rank' = num_POCs x travelCostRoundTrip is chosen
            ///    e.g. 1 POC = 0 and is ideal 1(num) x 0(travelcost)

            bool[,] bolIgnorePOCMap = new bool[szMapSize.Width, szMapSize.Height];

            do
            {
                Array.Resize<classPOCSearchInfo>(ref udrPOCOptions, udrPOCOptions.Length + 1);
                for (int intCounter = 0; intCounter < udrPOCOptions.Length; intCounter++)
                    udrPOCOptions[intCounter] = new classPOCSearchInfo();


                udrPOCOptions[udrPOCOptions.Length - 1].POCMap = new short[szMapSize.Width, szMapSize.Height];
                udrPOCOptions[udrPOCOptions.Length - 1].POCLocs = new classPOCLocs[0];
                for (int intX = 0; intX < szMapSize.Width; intX++)
                    for (int intY = 0; intY < szMapSize.Height; intY++)
                        udrPOCOptions[udrPOCOptions.Length - 1].POCMap[intX, intY] = (short)(impassableTerrain(terrain[intX, intY].type) ? -1 : 0);

                Array.Resize<classPOCSearchInfo>(ref udrPOC, udrPOC.Length + 1);
                for (int intCounter = 0; intCounter < udrPOC.Length; intCounter++)
                    udrPOC[intCounter] = new classPOCSearchInfo();
                udrPOC[udrPOC.Length - 1].bolAvoidMap = new bool[szMapSize.Width, szMapSize.Height];
                udrPOC[udrPOC.Length - 1].POCMap = new short[szMapSize.Width, szMapSize.Height];
                udrPOC[udrPOC.Length - 1].POCLocs = new classPOCLocs[0];
                udrPOC[udrPOC.Length - 1].unit = unit_Castle;
                for (int intX = 0; intX < szMapSize.Width; intX++)
                    for (int intY = 0; intY < szMapSize.Height; intY++)
                    {
                        udrPOC[udrPOC.Length - 1].bolAvoidMap[intX, intY] = impassableTerrain(terrain[intX, intY].type);
                        udrPOC[udrPOC.Length - 1].POCMap[intX, intY] = (short)(udrPOC[udrPOC.Length - 1].bolAvoidMap[intX, intY]
                                                                               || udrPOCOptions[udrPOCOptions.Length - 1].POCMap[intX, intY] == 1 ? -1 : 0);
                    }

                while (determinePointsOfConvergence(ref udrPOC[udrPOC.Length - 1], bolIgnorePOCMap))
                {
                    Point udrPOCLoc = cLibBattle.getPos(
                                                                udrPOC[udrPOC.Length - 1].POCLocs[
                                                                                                 udrPOC[udrPOC.Length - 1].POCLocs.Length - 1
                                                                                                 ].pt.X,
                                                                udrPOC[udrPOC.Length - 1].POCLocs[
                                                                                                 udrPOC[udrPOC.Length - 1].POCLocs.Length - 1
                                                                                                 ].pt.Y
                                                                );
                    if (udrPOC[udrPOC.Length - 1].POCMap[udrPOCLoc.X, udrPOCLoc.Y] > 0)
                        if (udrPOCOptions[udrPOCOptions.Length - 1].POCMap[udrPOCLoc.X, udrPOCLoc.Y] == 0)
                            addPOC(ref udrPOCOptions[udrPOCOptions.Length - 1],
                                       cLibBattle.getPos(udrPOCLoc.X, udrPOCLoc.Y),
                                       udrPOC[udrPOC.Length - 1].POCLocs[
                                                                      udrPOC[udrPOC.Length - 1].POCLocs.Length - 1
                                                                        ].flag);
                }
                for (int intLocCounter = 0; intLocCounter < udrPOC[udrPOC.Length - 1].POCLocs.Length; intLocCounter++)
                    bolIgnorePOCMap[udrPOC[udrPOC.Length - 1].POCLocs[intLocCounter].pt.X, udrPOC[udrPOC.Length - 1].POCLocs[intLocCounter].pt.Y] = true;
                setDefensiveStrength(ref udrPOCOptions[udrPOCOptions.Length - 1]);

                // rank POCs
                if (udrPOCOptions[udrPOCOptions.Length - 1].POCLocs.Length > 0)
                {
                    classBattleUnit thisUnit = Unit_Q[(int)(TurnColor + 1) % 2];
                    while (thisUnit != null)
                    {
                        if (udrPOCOptions[udrPOCOptions.Length - 1].POCMap[thisUnit.myPosition.X, thisUnit.myPosition.Y] <= 0)
                            thisUnit.RankPOCs(ref udrPOCOptions[udrPOCOptions.Length - 1]);
                        thisUnit = thisUnit.next;
                    }
                    reorderPOCs(ref udrPOCOptions[udrPOCOptions.Length - 1]);
                }
                clearInvalidPOCs(ref udrPOCOptions[udrPOCOptions.Length - 1]);
                /// once POC set is ranked the POCMap shows where the safe-squares are, 
                /// if there are no safe-squares around a POC it is considered invalid and is erased from the set
            } while (udrPOC[udrPOC.Length - 1].POCLocs.Length > 0);


            // select the best set of POCs
            int intBestDefensiveValue = udrPOCOptions[0].intDefensiveStrength;
            int intBestIndex = 0;
            for (int intPOCSetcounter = 1; intPOCSetcounter < udrPOCOptions.Length - 1; intPOCSetcounter++)
            {
                if (intBestDefensiveValue > udrPOCOptions[intPOCSetcounter].intDefensiveStrength)
                {
                    intBestDefensiveValue = udrPOCOptions[intPOCSetcounter].intDefensiveStrength;
                    intBestIndex = intPOCSetcounter;
                }
            }

            // set udrRetVal to best set of POCs
            classPOCSearchInfo udrRetVal = udrPOCOptions[intBestIndex];
            udrRetVal.bolAvoidMap = getBolDefaultAvoidMap();


            if (unitGhost != null)
                killUnit(unitGhost);
            return udrRetVal;
        }

        void setDefensiveStrength(ref classPOCSearchInfo udrPOCInfo)
        {
            Point[] udrLocs = new Point[udrPOCInfo.POCLocs.Length];
            for (int intLocCounter = 0; intLocCounter < udrPOCInfo.POCLocs.Length; intLocCounter++)
                udrLocs[intLocCounter] = udrPOCInfo.POCLocs[intLocCounter].pt;

            bool[,] bolAvoidMap = new bool[szMapSize.Width, szMapSize.Height];
            for (int intX = 0; intX < szMapSize.Width; intX++)
                for (int intY = 0; intY < szMapSize.Height; intY++)
                    bolAvoidMap[intX, intY] = (udrPOCInfo.POCMap[intX, intY] != 0);

            udrPOCInfo.intDefensiveStrength = udrPOCInfo.POCLocs.Length * getLowestTravelCostBetweenMultipleLocs(udrLocs, bolAvoidMap);
        }

        void reorderPOCs(ref classPOCSearchInfo udrPOC)
        {
            short shrBestValue = 0;
            int intBestIndex = 0;
            classPOCLocs udrTemp;
            if (udrPOC.POCLocs != null)
                for (int intOuterLoop = 0; intOuterLoop < udrPOC.POCLocs.Length - 1; intOuterLoop++)
                {
                    shrBestValue = udrPOC.POCMap[udrPOC.POCLocs[intOuterLoop].pt.X, udrPOC.POCLocs[intOuterLoop].pt.Y];
                    intBestIndex = intOuterLoop;
                    for (int intInnerLoop = intOuterLoop + 1; intInnerLoop < udrPOC.POCLocs.Length; intInnerLoop++)
                    {
                        if (shrBestValue < udrPOC.POCMap[udrPOC.POCLocs[intInnerLoop].pt.X, udrPOC.POCLocs[intInnerLoop].pt.Y])
                        {
                            shrBestValue = udrPOC.POCMap[udrPOC.POCLocs[intInnerLoop].pt.X, udrPOC.POCLocs[intInnerLoop].pt.Y];
                            intBestIndex = intInnerLoop;
                        }
                    }
                    if (intBestIndex != intOuterLoop)
                    {
                        udrTemp = udrPOC.POCLocs[intOuterLoop];
                        udrPOC.POCLocs[intOuterLoop] = udrPOC.POCLocs[intBestIndex];
                        udrPOC.POCLocs[intBestIndex] = udrTemp;
                    }
                }
        }

        bool determinePointsOfConvergence(ref classPOCSearchInfo udrPOCInfo, bool[,] bolIgnorePOCMap)
        {   
            classBattleField.classAIShortestPathToEnemy[] udrPathsTo_Red = udrPOCInfo.unit.getShortestPathToAllEnemyUnits(udrPOCInfo.unit.myPosition, udrPOCInfo.bolAvoidMap);
            for (int intCounter = 0; intCounter < udrPathsTo_Red.Length; intCounter++)
                udrPathsTo_Red[intCounter] = new classAIShortestPathToEnemy();
            Point udrLocPOC = cLibBattle.getPos(0, 0);

            if (udrPathsTo_Red.Length == 0)
                return false;  // the enemy can no longer reached and a set of POCs has been found which blocks the enemy from castle

            if (udrPathsTo_Red.Length > 1)
                reorderByPathLengthAIShortestPathToEnemyArray(ref udrPathsTo_Red); 

            for (int intPathCounter_Red = udrPathsTo_Red.Length - 1; intPathCounter_Red >= 0; intPathCounter_Red--)
            {
                if (udrPathsTo_Red[intPathCounter_Red].path.Trim().Length > 0)
                {
                    if (findPOCOnPath(ref udrPOCInfo, udrPathsTo_Red[intPathCounter_Red], bolIgnorePOCMap))
                    { // POC info of new POC is set in reference variable during findPOCOnPath()
                        ++udrPOCInfo.shrNumFound;
                        return true;
                    }
                }
            }
            return false;
        }

        bool CanUnitInQReachThisLocationWithMaxMovementPoints(ref classBattleUnit unitQ, Point udrLoc)
        {  // used to determine if a POC is threatened 
            classBattleUnit thisUnit = unitQ;
            while (thisUnit != null)
            {
                string strPath = thisUnit.getShortestPath(thisUnit.myPosition, udrLoc);
                int intMovementCost = thisUnit.getMovementCostAlongTravelPath(strPath);
                if (intMovementCost < thisUnit.intMovementPoints_Max)
                    return true;
                thisUnit = thisUnit.next;
            }
            return false;
        }

        int getTravelTimeForColorToReachLoc(ref classBattleUnit unit_ThisQ, Point udrLoc)
        {
            int intTravelTime = 1000000;
            classBattleUnit thisUnit = unit_ThisQ;
            while (thisUnit != null)
            {
                int intTravelTimeForThisUnit = thisUnit.getTravelTimeToLocInTurns(udrLoc);
                if (intTravelTimeForThisUnit < intTravelTime)
                    intTravelTime = intTravelTimeForThisUnit;
                thisUnit = thisUnit.next;
            }
            return intTravelTime;
        }

        void clearInvalidPOCs(ref classPOCSearchInfo udrPOCInfo)
        {
            // once the POCs have been ranked erase any POC that has no safe-zone around it
            for (int intPOCCounter = 0; intPOCCounter < udrPOCInfo.POCLocs.Length; intPOCCounter++)
            {
                while (intPOCCounter < udrPOCInfo.POCLocs.Length && countSafeSquaresAroundLoc(udrPOCInfo, udrPOCInfo.POCLocs[intPOCCounter].pt) == 0)
                {
                    udrPOCInfo.POCMap[udrPOCInfo.POCLocs[intPOCCounter].pt.X, udrPOCInfo.POCLocs[intPOCCounter].pt.Y] = -1;
                    udrPOCInfo.POCLocs[intPOCCounter] = udrPOCInfo.POCLocs[udrPOCInfo.POCLocs.Length - 1];
                    Array.Resize<classPOCLocs>(ref udrPOCInfo.POCLocs, udrPOCInfo.POCLocs.Length - 1);
                }
            }
        }

        int countSafeSquaresAroundLoc(classPOCSearchInfo udrPOCInfo, Point udrLoc)
        {
            int intRetVal = 0;
            Point udrNextPos;
            for (classBattle.enuDirections dirCounter = 0; dirCounter < classBattle.enuDirections._num_directions; dirCounter++)
            {
                udrNextPos = getNextPos(udrLoc, dirCounter);
                if (ptOnMap(udrNextPos))
                    if (udrPOCInfo.POCMap[udrNextPos.X, udrNextPos.Y] == 0)
                        intRetVal++;
            }
            return intRetVal;
        }

        classBattle.enuUnitColors WhoCanReachLocFirst(Point udrLoc)
        {
            int intTravelTimeForBlue = getTravelTimeForColorToReachLoc(ref Unit_Q[(int)classBattle.enuUnitColors.blue], udrLoc);
            int intTravelTimeForRed = getTravelTimeForColorToReachLoc(ref Unit_Q[(int)classBattle.enuUnitColors.red], udrLoc);

            return intTravelTimeForBlue <= intTravelTimeForRed ? classBattle.enuUnitColors.blue : classBattle.enuUnitColors.red;
        }

        bool findPOCOnPath(ref classPOCSearchInfo udrPOCInfo, classBattleField.classAIShortestPathToEnemy udrPathToEnemyUnit, bool[,] bolIgnorePOCMap)
        {
            /// receives udrPOCInfo : 
            ///             bolAvoidMap 2d boolean array(mapsized) barring the search algorithms from entering squares on map
            ///             shoPOCMap   2d boolean array(mapsized) init'd at zeros set to 1 where POCs are found
            ///                            the zeros are then set to -1 during 'ranking' phase 
            ///                            the final Map shows : -1 where enemy can reach, 0 safe squares, 1 POC locations
            ///             unit        classBattleUnit, unit of TurnColor used to find POC
            ///          udrPathToEnemy 
            ///             path        string describing steps in 2char enuDirection types set .ToString()
            ///             unit        classBattleUnit, unit at the other end of this path
            /// returns TRUE when it finds a POC, false otherwise
            /// 
            /// uses three variables of classCartesian indicating three contiguous steps along the path
            ///    for each step it sets the middle step's bolAvoidMap to true and asks : 'can i reach the third step in less than 4 moves'
            ///      if it can the algorithm steps forward by one step
            ///      if it can't it has potentially found a POC
            ///               
            string strOriginalPathToEnemy = udrPathToEnemyUnit.path;
            Point udrThisLoc = new Point();
            Point udrNextLoc = udrPOCInfo.unit.myPosition;
            Point udrAfterNextLoc = getNextPos(udrNextLoc, getDirFromString(udrPathToEnemyUnit.path.Substring(0, 2)));
            bool bolSkipOne = false;
            bool bolRetVal = false;
            Point udrLastPOCFound = new Point();

            if (terrain[udrAfterNextLoc.X, udrAfterNextLoc.Y].unit != null
                && terrain[udrAfterNextLoc.X, udrAfterNextLoc.Y].unit.myClr != TurnColor
                && !bolIgnorePOCMap[udrAfterNextLoc.X, udrAfterNextLoc.Y])
            {
                udrLastPOCFound = udrNextLoc;
                bolRetVal = true;
            }

            string strPath = udrPathToEnemyUnit.path.Substring(2);
            Point[] udrResetBolAvoid = new Point[0];

            int intMovementCostSoFar = 0;
            int intTotalMovementCost = udrPOCInfo.unit.getMovementCostAlongTravelPath(udrPathToEnemyUnit.path);

            while (strPath.Length > 0)
            {
                udrThisLoc = udrNextLoc;
                udrNextLoc = udrAfterNextLoc;
                udrAfterNextLoc = getNextPos(udrNextLoc, getDirFromString(strPath.Substring(0, 2)));

                intMovementCostSoFar += getMovementCost(udrNextLoc);

                strPath = strPath.Substring(2);
                udrPOCInfo.bolAvoidMap[udrNextLoc.X, udrNextLoc.Y] = true;
                if (!bolIgnorePOCMap[udrNextLoc.X, udrNextLoc.Y])
                {
                    if (!udrPOCInfo.unit.CanTravelBetweenLocsInLessThanNSteps(udrThisLoc, udrAfterNextLoc, udrPOCInfo.bolAvoidMap, 5))
                    {
                        if (getNumMoveSquaresAroundLoc(udrThisLoc, udrPOCInfo.bolAvoidMap) > 2)
                        { // tests if this is a clearing behind the POC - 
                            /// <=2   if  the castle is inside a narrow corridor
                            if (WhoCanReachLocFirst(udrNextLoc) == TurnColor
                                || getTravelTimeForColorToReachLoc(ref Unit_Q[(int)TurnColor], udrNextLoc) <= 1)
                            {  // allow this POC if TurnColor can reach it before the enemy, or this turn
                                if (!bolSkipOne)
                                {  /// bolSkipOne is set after finding a POC skip the next one if it is behind this one, e.g. corridor
                                   /// find furthest POCs first and remember the most recent one until you reach the enemy
                                   /// of a POC too far for defending side to reach before the enemy 
                                    udrLastPOCFound = udrNextLoc;
                                    bolRetVal = true;
                                }
                                bolSkipOne = true;
                            }
                            else
                            {
                                udrPOCInfo.bolAvoidMap[udrNextLoc.X, udrNextLoc.Y] = false;
                                goto exitFindPOCOnPath;
                            }
                        }
                        else
                            bolSkipOne = false;
                    }
                    else
                        bolSkipOne = false;
                }
                else
                {
                    udrPOCInfo.bolAvoidMap[udrNextLoc.X, udrNextLoc.Y] = false;
                    goto exitFindPOCOnPath;
                }
                udrPOCInfo.bolAvoidMap[udrNextLoc.X, udrNextLoc.Y] = false;
            }

            // test the end of path - see if the enemy unit the findShortestPath() function gave a path to, is standing on the POC
            if (udrPOCInfo.POCMap[udrAfterNextLoc.X, udrAfterNextLoc.Y] == 0)
            {
                if (udrPOCInfo.unit.countFriendlyUnitsNextToThisSquare(udrAfterNextLoc) > 0) // only a valid POC at end of path if Blue is already there
                {
                    if (terrain[udrAfterNextLoc.X, udrAfterNextLoc.Y].unit != null
                       && terrain[udrAfterNextLoc.X, udrAfterNextLoc.Y].unit.myClr != TurnColor
                        && !bolIgnorePOCMap[udrAfterNextLoc.X, udrAfterNextLoc.Y])
                    {
                        if (!udrPOCInfo.unit.CanTravelBetweenLocsInLessThanNSteps(udrThisLoc, udrAfterNextLoc, udrPOCInfo.bolAvoidMap, 5))
                        {
                            udrPOCInfo.bolAvoidMap[udrAfterNextLoc.X, udrAfterNextLoc.Y] = true;
                            addPOC(ref udrPOCInfo, udrAfterNextLoc, (WhoCanReachLocFirst(udrAfterNextLoc) == TurnColor)
                                        || getTravelTimeForColorToReachLoc(ref Unit_Q[(int)TurnColor], udrNextLoc) <= 1);
                            return true;
                        }
                        // the Red unit may be standing on a POC
                        // test this by looking past this Unit
                        // dir last step 
                        string strDirLastStep = strOriginalPathToEnemy.Substring(strOriginalPathToEnemy.Length - 2);
                        classBattle.enuDirections dirLastStep = getDirFromString(strDirLastStep);
                        Point udrNextStepForward = getNextPos(udrAfterNextLoc, dirLastStep);

                        classBattle.enuDirections dirNextLeft = (classBattle.enuDirections)(((int)dirLastStep - 1 + (int)classBattle.enuDirections._num_directions) % (int)classBattle.enuDirections._num_directions);
                        Point udrNextLeft = getNextPos(udrAfterNextLoc, dirNextLeft);

                        classBattle.enuDirections dirNextRight = (classBattle.enuDirections)(((int)dirLastStep + 1) % (int)(classBattle.enuDirections._num_directions));
                        Point udrNextRight = getNextPos(udrAfterNextLoc, dirNextRight);

                        bool bolRemember = udrPOCInfo.bolAvoidMap[udrAfterNextLoc.X, udrAfterNextLoc.Y];
                        udrPOCInfo.bolAvoidMap[udrAfterNextLoc.X, udrAfterNextLoc.Y] = true;

                        if (!impassableTerrain(udrNextStepForward))
                            if (!udrPOCInfo.unit.CanTravelBetweenLocsInLessThanNSteps(udrNextLoc, udrNextStepForward, udrPOCInfo.bolAvoidMap, 5))
                            {
                                bolRetVal = true;
                                udrLastPOCFound = udrAfterNextLoc;
                                goto quitLastStepSearch;
                            }

                        if (!impassableTerrain(udrNextLeft))
                            if (!udrPOCInfo.unit.CanTravelBetweenLocsInLessThanNSteps(udrNextLoc, udrNextLeft, udrPOCInfo.bolAvoidMap, 5))
                            {
                                bolRetVal = true;
                                udrLastPOCFound = udrAfterNextLoc;
                                goto quitLastStepSearch;
                            }

                        if (!impassableTerrain(udrNextRight))
                            if (!udrPOCInfo.unit.CanTravelBetweenLocsInLessThanNSteps(udrNextLoc, udrNextRight, udrPOCInfo.bolAvoidMap, 5))
                            {
                                bolRetVal = true;
                                udrLastPOCFound = udrAfterNextLoc;
                                goto quitLastStepSearch;
                            }

                    quitLastStepSearch:
                        udrPOCInfo.bolAvoidMap[udrAfterNextLoc.X, udrAfterNextLoc.Y] = bolRemember;
                    }
                }
            }

        exitFindPOCOnPath:
            if (bolRetVal)
            {
                udrPOCInfo.bolAvoidMap[udrLastPOCFound.X, udrLastPOCFound.Y] = true;

                bool bolValidPOC = CanUnitInQReachThisLocationWithMaxMovementPoints(ref Unit_Q[(int)classBattle.enuUnitColors.red], udrLastPOCFound);
                addPOC(ref udrPOCInfo, udrLastPOCFound, bolValidPOC);
                foreach (Point udrThisReset in udrResetBolAvoid)
                    udrPOCInfo.bolAvoidMap[udrThisReset.X, udrThisReset.Y] = false;
            }
            return bolRetVal;
        }

        void addPOC(ref classPOCSearchInfo udrPOC, Point udrLoc)
        { addPOC(ref udrPOC, udrLoc, true); }
        void addPOC(ref classPOCSearchInfo udrPOC, Point udrLoc, bool bolValid)
        {
            classPOCLocs udrPOCLoc = new classPOCLocs();
            udrPOCLoc.pt = udrLoc;
            udrPOCLoc.flag = bolValid;
            if (udrLoc.X == null || udrLoc.Y == null)
            {
                System.Windows.Forms.MessageBox.Show("fuck!");
            }
            else
            {
                Array.Resize<classPOCLocs>(ref udrPOC.POCLocs, udrPOC.POCLocs.Length + 1);
                udrPOC.POCLocs[udrPOC.POCLocs.Length - 1] = udrPOCLoc;
                udrPOC.POCMap[udrLoc.X, udrLoc.Y] = 1;
            }
        }

        public int getNumMoveSquaresAroundLoc(Point udrLoc)
        { return getNumMoveSquaresAroundLoc(udrLoc, getBolDefaultAvoidMap()); }
        public int getNumMoveSquaresAroundLoc(Point udrLoc, bool[,] bolAvoidMap)
        {
            /// returns the numbers of directions a unit can move in, total number of not impassable neaghbouring squares
            int intRetVal = 0;

            for (classBattle.enuDirections dirCounter = 0; dirCounter < classBattle.enuDirections._num_directions; dirCounter++)
            {
                Point udrNewPos = getNextPos(udrLoc, dirCounter);
                if (udrNewPos.X >= 0
                    && udrNewPos.X < szMapSize.Width
                    && udrNewPos.Y >= 0
                    && udrNewPos.Y < szMapSize.Height)
                    intRetVal += (!impassableTerrain(udrNewPos) && !bolAvoidMap[udrNewPos.X, udrNewPos.Y] ? 1 : 0);
            }

            return intRetVal;
        }

        public void reorderByPathLengthAIShortestPathToEnemyArray(ref classBattleField.classAIShortestPathToEnemy[] udrArray)
        {
            /// quick sort by path string length, longest first, shortest last
            classBattleField.classAIShortestPathToEnemy udrTemp = new classAIShortestPathToEnemy();
            int intLongest, intLongest_Index;

            for (int intOuterLoop = 0; intOuterLoop < udrArray.Length - 1; intOuterLoop++)
            {
                if (udrArray[intOuterLoop].path != null)
                    intLongest = udrArray[intOuterLoop].path.Length;
                else
                    intLongest = 0;
                intLongest_Index = intOuterLoop;
                for (int intInnerLoop = intOuterLoop + 1; intInnerLoop < udrArray.Length; intInnerLoop++)
                {

                    if (udrArray[intInnerLoop].path != null && udrArray[intInnerLoop].path.Length > intLongest)
                    {
                        intLongest_Index = intInnerLoop;
                        intLongest = udrArray[intLongest_Index].path.Length;
                    }
                }
                if (intLongest_Index != intOuterLoop)
                {
                    udrTemp = udrArray[intLongest_Index];
                    udrArray[intLongest_Index] = udrArray[intOuterLoop];
                    udrArray[intOuterLoop] = udrTemp;
                }
            }
        }

        public void reorderByPathLengthAIShortestPathToEnemyArray_byRating(ref classBattleField.classAIShortestPathToEnemy[] udrArray)
        {
            /// quick sort by path string rating, highest first, lowest last
            classBattleField.classAIShortestPathToEnemy udrTemp = new classAIShortestPathToEnemy();
            int intBest, intBest_Index;

            for (int intOuterLoop = 0; intOuterLoop < udrArray.Length - 1; intOuterLoop++)
            {
                intBest = udrArray[intOuterLoop].rating;
                intBest_Index = intOuterLoop;
                for (int intInnerLoop = intOuterLoop + 1; intInnerLoop < udrArray.Length; intInnerLoop++)
                {
                    if (udrArray[intInnerLoop].rating > intBest)
                    {
                        intBest_Index = intInnerLoop;
                        intBest = udrArray[intBest_Index].rating;
                    }
                }
                if (intBest_Index != intOuterLoop)
                {
                    udrTemp = udrArray[intBest_Index];
                    udrArray[intBest_Index] = udrArray[intOuterLoop];
                    udrArray[intOuterLoop] = udrTemp;
                }
            }
        }


        #endregion
        #endregion

        #region "helper functions"
        void setIntStandardCartesianStringFieldLength()
        {
            string strWidth = szMapSize.Width.ToString();
            string strHeight = szMapSize.Height.ToString();

            intStandardCartesianStringFieldLength = strWidth.Length > strHeight.Length ? strWidth.Length : strHeight.Length;
        }

        int getLowestTravelCostBetweenMultipleLocs(Point[] udrLocs, bool[,] bolAvoidMap)
        {
            if (udrLocs.Length == 0)
                return 0;
            classTravelCost_Array[] udrTravelCost = new classTravelCost_Array[udrLocs.Length];

            classBattleUnit unit_Worker = new classBattleUnit(classBattle.enuUnitTypes.cavalry, TurnColor, classBattle.enuUnitRanks.lieutenant);
            unit_Worker.bolDie = true;
            enQUnit(ref Unit_Q[(int)TurnColor], ref unit_Worker);

            // each loc has its own classTravelCost_array
            // each element in the array holds the target loc, path to target loc, and travel cost

            for (int intLocCounter = 0; intLocCounter < udrLocs.Length; intLocCounter++)
            {
                udrTravelCost[intLocCounter].Array = new classTravelCost[udrLocs.Length];
                udrTravelCost[intLocCounter].udrLoc_Start = udrLocs[intLocCounter];
                for (int intAltLocCounter = 0; intAltLocCounter < udrLocs.Length; intAltLocCounter++)
                {
                    udrTravelCost[intLocCounter].Array[intAltLocCounter].udrLoc_End = udrLocs[intAltLocCounter];
                    udrTravelCost[intLocCounter].Array[intAltLocCounter].path = unit_Worker.getShortestPath(udrTravelCost[intLocCounter].udrLoc_Start, udrLocs[intAltLocCounter], false, false, bolAvoidMap);
                    udrTravelCost[intLocCounter].Array[intAltLocCounter].intMovementPoints = unit_Worker.getMovementCostAlongTravelPath(udrTravelCost[intLocCounter].Array[intAltLocCounter].path, udrTravelCost[intLocCounter].udrLoc_Start);
                }
            }

            // reorder each array from least to most intmovementcost (excepting zeroes which are placed last - paths to themselves)
            for (int intArrayCounter = 0; intArrayCounter < udrTravelCost.Length; intArrayCounter++)
                reorderTravelCostArray(ref udrTravelCost[intArrayCounter].Array);


            int intRetVal = 0;

            Point udrStartLoc = udrTravelCost[0].udrLoc_Start;

            // remove starting loc
            for (int intTCCounter = 0; intTCCounter < udrTravelCost.Length; intTCCounter++)
                removeLocFromTravelCost(ref udrTravelCost[intTCCounter].Array, udrStartLoc);

            while (udrTravelCost.Length > 1)
            {
                // record next step loc
                Point udrLocFound = udrTravelCost[0].Array[0].udrLoc_End;
                // add travel cost to next nearest location from this point that is still left unseen
                intRetVal += udrTravelCost[0].Array[0].intMovementPoints;
                // find the index for this next location's array
                int intIndex = getIndexTravelCost_Array(udrTravelCost, udrLocFound);
                // copy next location's array onto the zeroeth element
                udrTravelCost[0] = udrTravelCost[intIndex];
                // copy last over the next's old copy
                udrTravelCost[intIndex] = udrTravelCost[udrTravelCost.Length - 1];
                // shrink the entire array by one
                Array.Resize<classTravelCost_Array>(ref udrTravelCost, udrTravelCost.Length - 1);
                // remove the new location from every array list
                for (int intTCCounter = 0; intTCCounter < udrTravelCost.Length; intTCCounter++)
                    removeLocFromTravelCost(ref udrTravelCost[intTCCounter].Array, udrLocFound);
            }

            // return to beginning and add the final cost
            string strPathBackToStart = unit_Worker.getShortestPath(udrTravelCost[0].udrLoc_Start, udrStartLoc);
            intRetVal += unit_Worker.getMovementCostAlongTravelPath(strPathBackToStart, udrTravelCost[0].udrLoc_Start);

            // deQUnit(ref Unit_Q[(int)TurnColor], ref unit_Worker);
            killUnit(unit_Worker);
            return intRetVal;
        }

        int getIndexTravelCost_Array(classTravelCost_Array[] udrTravelCost_Array, Point udrLoc)
        {
            for (int intTCCounter = 0; intTCCounter < udrTravelCost_Array.Length; intTCCounter++)
                if (udrTravelCost_Array[intTCCounter].udrLoc_Start.X == udrLoc.X && udrTravelCost_Array[intTCCounter].udrLoc_Start.Y == udrLoc.Y)
                    return intTCCounter;

            MessageBox.Show("we have a problem");
            return 0; // this shouldn't happen
        }

        void removeLocFromTravelCost(ref classTravelCost[] udrTravelCost, Point udrLocToRemove)
        {
            for (int intTCCounter = 0; intTCCounter < udrTravelCost.Length; intTCCounter++)
            {
                if (udrTravelCost[intTCCounter].udrLoc_End.X == udrLocToRemove.X && udrTravelCost[intTCCounter].udrLoc_End.Y == udrLocToRemove.Y)
                {
                    udrTravelCost[intTCCounter] = udrTravelCost[udrTravelCost.Length - 1];
                    Array.Resize<classTravelCost>(ref udrTravelCost, udrTravelCost.Length - 1);
                    reorderTravelCostArray(ref udrTravelCost);
                    return;
                }
            }
        }

        void reorderTravelCostArray(ref classTravelCost[] udrTravelCost)
        {
            int intBestIndex;
            classTravelCost udrTemp = new classTravelCost();

            for (int intOuterLoop = 0; intOuterLoop < udrTravelCost.Length - 1; intOuterLoop++)
            {
                intBestIndex = intOuterLoop;
                for (int intInnerLoop = intOuterLoop + 1; intInnerLoop < udrTravelCost.Length; intInnerLoop++)
                {
                    if ((udrTravelCost[intInnerLoop].intMovementPoints < udrTravelCost[intBestIndex].intMovementPoints
                           && udrTravelCost[intInnerLoop].intMovementPoints > 0)
                        || udrTravelCost[intBestIndex].intMovementPoints == 0)
                        intBestIndex = intInnerLoop;
                }
                if (intBestIndex != intOuterLoop)
                {
                    udrTemp = udrTravelCost[intOuterLoop];
                    udrTravelCost[intOuterLoop] = udrTravelCost[intBestIndex];
                    udrTravelCost[intBestIndex] = udrTemp;
                }
            }
        }
        public bool[,] getBolDefaultAvoidMap()
        {
            bool[,] bolRetVal = new bool[szMapSize.Width, szMapSize.Height];
            for (int intX = 0; intX < szMapSize.Width; intX++)
                for (int intY = 0; intY < szMapSize.Height; intY++)
                    bolRetVal[intX, intY] = impassableTerrain(terrain[intX, intY].type);
            return bolRetVal;
        }

        public classBattle.enuDirections getOppositedirection(classBattle.enuDirections dir)
        {
            switch (dir)
            {
                case classBattle.enuDirections.N_:
                    return classBattle.enuDirections.S_;

                case classBattle.enuDirections.NE:
                    return classBattle.enuDirections.SW;

                case classBattle.enuDirections.NW:
                    return classBattle.enuDirections.SE;

                case classBattle.enuDirections.S_:
                    return classBattle.enuDirections.N_;

                case classBattle.enuDirections.SE:
                    return classBattle.enuDirections.NW;

                case classBattle.enuDirections.SW:
                    return classBattle.enuDirections.NE;
            }
            return classBattle.enuDirections._num_directions;
        }
        public classBattle.enuDirections getDirFromString(string strDir)
        {
            switch (strDir)
            {
                case "NW":
                    return classBattle.enuDirections.NW;

                case "N_":
                    return classBattle.enuDirections.N_;

                case "NE":
                    return classBattle.enuDirections.NE;

                case "SW":
                    return classBattle.enuDirections.SW;

                case "S_":
                    return classBattle.enuDirections.S_;

                case "SE":
                    return classBattle.enuDirections.SE;

                default:
                    return classBattle.enuDirections._num_directions;
            }
        }

        public int getMovementCost(Point udrPos) 
        {
            if (udrPos.X <0 || udrPos.Y <0 || udrPos.X >= szMapSize.Width ||udrPos.Y >= szMapSize.Height)
            {
                ;
                return int.MaxValue;
            }
            else
            {
                classBattle.enuTerrainTypes eTerrainTye = terrain[udrPos.X, udrPos.Y].type;
                return getMovementCost(eTerrainTye);
            }


                
        }

        public int getMovementCost(classBattle.enuTerrainTypes terrainType)
        {
            double dblBasicTerrainCost = 40;
            double dblTerrainFactor = 1.0;
            switch (terrainType)
            {
                case classBattle.enuTerrainTypes.road:
                    dblTerrainFactor = .25; // 10
                    break;

                case classBattle.enuTerrainTypes.grass:
                    dblTerrainFactor = .7;  // 28
                    break;

                case classBattle.enuTerrainTypes.castle:
                    dblTerrainFactor = .75; // 30
                    break;

                case classBattle.enuTerrainTypes.forest:
                    dblTerrainFactor = 1.0; // 40
                    break;

                case classBattle.enuTerrainTypes.water:
                case classBattle.enuTerrainTypes.mountain:
                    dblTerrainFactor = 7.5; // 300
                    break;
            }
            return (int)(dblBasicTerrainCost * dblTerrainFactor);
        }

        public int CountUnitsInQ(classBattleUnit thisQ)
        {
            int intRetVal = 0;
            classBattleUnit thisUnit = thisQ;
            while (thisUnit != null)
            {
                intRetVal++;
                thisUnit = thisUnit.next;
            }
            return intRetVal;
        }

        public bool impassableTerrain(Point udrLoc)
        {
            if (udrLoc.X >= 0 && udrLoc.X < szMapSize.Width
                && udrLoc.Y >= 0 && udrLoc.Y < szMapSize.Height)
                return impassableTerrain(terrain[udrLoc.X, udrLoc.Y].type);
            else
                return true;

        }

        public bool impassableTerrain(classBattle.enuTerrainTypes typeTerrain)
        {
            switch (typeTerrain)
            {
                case classBattle.enuTerrainTypes.water:
                case classBattle.enuTerrainTypes.mountain:
                    return true;

                default:
                    return false;
            }
        }

        public Point getNextPos(Point unitPos, classBattle.enuDirections dir)
        {
            Point udrMoveVector = new Point();
            udrMoveVector.X = 0;
            udrMoveVector.Y = 0;
            if (intIsEven(unitPos.X))
            {
                switch (dir)
                {
                    case classBattle.enuDirections.NW:
                        udrMoveVector.X = -1;
                        udrMoveVector.Y = 0;
                        break;
                    case classBattle.enuDirections.N_:
                        udrMoveVector.X = 0;
                        udrMoveVector.Y = -1;
                        break;
                    case classBattle.enuDirections.NE:
                        udrMoveVector.X = 1;
                        udrMoveVector.Y = 0;
                        break;

                    case classBattle.enuDirections.SE:
                        udrMoveVector.X = 1;
                        udrMoveVector.Y = 1;
                        break;
                    case classBattle.enuDirections.S_:
                        udrMoveVector.X = 0;
                        udrMoveVector.Y = 1;
                        break;
                    case classBattle.enuDirections.SW:
                        udrMoveVector.X = -1;
                        udrMoveVector.Y = 1;
                        break;
                }
            }
            else
            {
                switch (dir)
                {
                    case classBattle.enuDirections.NW:
                        udrMoveVector.X = -1;
                        udrMoveVector.Y = -1;
                        break;
                    case classBattle.enuDirections.N_:
                        udrMoveVector.X = 0;
                        udrMoveVector.Y = -1;
                        break;
                    case classBattle.enuDirections.NE:
                        udrMoveVector.X = 1;
                        udrMoveVector.Y = -1;
                        break;

                    case classBattle.enuDirections.SE:
                        udrMoveVector.X = 1;
                        udrMoveVector.Y = 0;
                        break;
                    case classBattle.enuDirections.S_:
                        udrMoveVector.X = 0;
                        udrMoveVector.Y = 1;
                        break;
                    case classBattle.enuDirections.SW:
                        udrMoveVector.X = -1;
                        udrMoveVector.Y = 0;
                        break;
                }
            }
            unitPos.X += udrMoveVector.X;
            unitPos.Y += udrMoveVector.Y;
            return unitPos;
        }

        public bool intIsEven(int intInput) { return ((intInput % 2) == 0); }
        public string MsgBox(string strHeading, string strText, string strButtons)
        {
            classMessageBox msgBox = new classMessageBox(strHeading, strText, strButtons);
            msgBox.Owner = cLibBattle.mainForm;
            msgBox.ShowDialog();
            string reply = msgBox.Reply;
            msgBox.Dispose();
            return reply;
        }
        #endregion

    }

    public class classBarrage : System.Windows.Forms.PictureBox
    {
        double dblLength;
        double dblDistanceFromStart = 0;
        double dblBaseDistanceTravel = 0.1;
        double dblTheta;
        System.Windows.Forms.Timer tmrAnimate = new System.Windows.Forms.Timer();
        classBattle.classMaxMin udrMaxMinSize = new classBattle.classMaxMin();
        Point udrStart, udrEnd;
        //classLib.classLib cLib = new classLib.classLib();
        classBattleField battlefield;

        classBattleUnit attacker;
      
        int intDamage;

        public classBarrage(classBattleField battlefield_local,
                            classBattleUnit attacker_local)
        {
            battlefield = battlefield_local;
            attacker = attacker_local;

            udrStart.X = attacker.ptLoc.X + attacker.myBitMap.Width / 2;
            udrStart.Y = attacker.ptLoc.Y + attacker.myBitMap.Height / 2;

            udrEnd.X = attacker.unitIAmAttacking.ptLoc.X + attacker.unitIAmAttacking.myBitMap.Width / 2;
            udrEnd.Y = attacker.unitIAmAttacking.ptLoc.Y + attacker.unitIAmAttacking.myBitMap.Height / 2;

            udrMaxMinSize.Min = attacker.myBitMap.Width * 0.1;
            udrMaxMinSize.Max = attacker.myBitMap.Width * 0.2;

            Image = Battlefield_Simulator_2019.Resources.barrage;
            SizeMode = PictureBoxSizeMode.StretchImage;
            Height = Width = (int)udrMaxMinSize.Min;
            dblLength = Math.Sqrt(Math.Pow(udrEnd.X - udrStart.X, 2) + Math.Pow(udrEnd.Y - udrStart.Y, 2));

            //classMath cLibMath = new classMath();

            dblTheta = battlefield.cLibBattle.arcTan(udrEnd.X - udrStart.X, udrEnd.Y - udrStart.Y);
            Visible = false;
        }

        public void go()
        {
            tmrAnimate.Tick += new EventHandler(tmrAnimate_Tick);
            tmrAnimate.Interval = classBattleField.intInterval;
            if (!battlefield.bolKillBattle)
                tmrAnimate.Enabled = true;
        }

        void tmrAnimate_Tick(object sender, EventArgs e)
        {
            tmrAnimate.Enabled = false;
            tmrAnimate.Interval = classBattleField.intInterval;
            double dblDistanceTheta = (dblDistanceFromStart / dblLength) * Math.PI;

            double dblDistanceTravel = (Math.Abs(Math.Cos(dblDistanceTheta)) + 1) * (dblLength * dblBaseDistanceTravel * .5);
            dblDistanceFromStart += dblDistanceTravel;

            if (dblDistanceFromStart > dblLength)
                dblDistanceFromStart = dblLength;

            double dblFractionMaxSize = Math.Sin(dblDistanceTheta);

            Visible = false;

            Width = Height = (int)(udrMaxMinSize.Min + (udrMaxMinSize.Max - udrMaxMinSize.Min) * dblFractionMaxSize);
            Point udrCenterPos = new Point();
            udrCenterPos.X = (int)(udrStart.X + dblDistanceFromStart * Math.Cos(dblTheta));
            udrCenterPos.Y = (int)(udrStart.Y + dblDistanceFromStart * Math.Sin(dblTheta));

            Left = udrCenterPos.X - Width / 2;
            Top = udrCenterPos.Y - Height / 2;

            if (dblDistanceFromStart != dblLength)
            {
                if (!battlefield.bolKillBattle)
                    Visible = tmrAnimate.Enabled = true;
                BringToFront();
            }
            else
            {
                intDamage = attacker.getDamage(attacker.udrRangeDamage, attacker.unitIAmAttacking.dblRangeDefense);
                attacker.unitIAmAttacking.hit(intDamage);
                battlefield.unit_Animated = attacker;

                battlefield.launchAnimateAttackTimer();
                Dispose();
            }
        }
    }

    public class classPlyrAttackInterfaceMessageBox : Form
    {
        public enum enuPlyrAttackInterfaceResults { confirmNormalAttack, confirmBarrageAttack, confirmCharge, cancelAttack }

        enuPlyrAttackInterfaceResults reply;

        public classPlyrAttackInterfaceMessageBox(classBattleUnit attacker, classBattleUnit defender, enuPlyrAttackInterfaceResults default_reply)
        {
            reply = default_reply;

            int intDistanceBetween = attacker.getDistanceBetweenSquares(attacker.myPosition, defender.myPosition);
            bool bolBarrage = false;
            bool bolCharge = false;
            bool bolNormal = false;
            if (intDistanceBetween > 1)
            {
                if (intDistanceBetween <= attacker.intRange)
                    bolBarrage = true;
            }
            else
            {
                if (attacker.myType == classBattle.enuUnitTypes.cavalry
                    && attacker.intMovementPoints >= classBattleField.intCostCharge)
                    bolCharge = true;
                bolNormal = true;
            }

            System.Windows.Forms.Label lbl = new Label();
            lbl.Font = new Font("ms sans-serif", 12);
            Controls.Add(lbl);
            lbl.Text = "Do you want to attack:"
                     + "\r\n\r\nAttacker : "
                     + getUnitStats(attacker)
                     + "\r\n\r\nDefender : "
                     + getUnitStats(defender);
            Width = 500; Height = 300;
            lbl.Top = 25; lbl.Left = 5;
            lbl.Width = Width - 2 * lbl.Left; lbl.Height = 150;
            lbl.BackColor = BackColor;
            lbl.Visible = true;
            lbl.BorderStyle = BorderStyle.None;

            int intButtonLeft = Width - 10, intButtontop = lbl.Top + lbl.Height + 25;

            Button btnCancel = new Button();
            Controls.Add(btnCancel);
            btnCancel.AutoSize = true; btnCancel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
            btnCancel.Text = "Cancel";
            btnCancel.Top = intButtontop; btnCancel.Left = intButtonLeft - btnCancel.Width - 10;
            intButtonLeft = btnCancel.Left;

            btnCancel.Click += new EventHandler(btnCancel_Click);
            btnCancel.Visible = true;

            if (bolCharge)
            {
                Button btnCharge = new Button();
                Controls.Add(btnCharge);
                btnCharge.AutoSize = true; btnCharge.AutoSizeMode = AutoSizeMode.GrowAndShrink;
                btnCharge.Text = "Charge";
                btnCharge.Top = intButtontop; btnCharge.Left = intButtonLeft - btnCharge.Width - 10; ;
                intButtonLeft = btnCharge.Left;
                btnCharge.Click += new EventHandler(btnCharge_Click);
                btnCharge.Visible = true;
            }

            if (bolBarrage)
            {
                Button btnBarrage = new Button();
                Controls.Add(btnBarrage);
                btnBarrage.AutoSize = true; btnBarrage.AutoSizeMode = AutoSizeMode.GrowAndShrink;
                btnBarrage.Text = "Barrage";
                btnBarrage.Top = intButtontop; btnBarrage.Left = intButtonLeft - btnBarrage.Width - 10;
                intButtonLeft = btnBarrage.Left;
                btnBarrage.Click += new EventHandler(btnBarrage_Click);
                btnBarrage.Visible = true;
            }

            if (bolNormal)
            {
                Button btnNormal = new Button();
                Controls.Add(btnNormal);
                btnNormal.AutoSize = true; btnNormal.AutoSizeMode = AutoSizeMode.GrowAndShrink;
                btnNormal.Text = "Normal";
                btnNormal.Top = intButtontop; btnNormal.Left = intButtonLeft - btnNormal.Width - 10;
                intButtonLeft = btnNormal.Left;
                btnNormal.Click += new EventHandler(btnNormal_Click);
                btnNormal.Visible = true;
            }

            btnCancel.Focus();
            Text = "Attack?";
        }

        public enuPlyrAttackInterfaceResults Reply { get { return reply; } }

        void btnNormal_Click(object sender, EventArgs e)
        {
            reply = enuPlyrAttackInterfaceResults.confirmNormalAttack;
            Close();
        }

        void btnCharge_Click(object sender, EventArgs e)
        {
            reply = enuPlyrAttackInterfaceResults.confirmCharge;
            Close();
        }

        void btnBarrage_Click(object sender, EventArgs e)
        {
            reply = enuPlyrAttackInterfaceResults.confirmBarrageAttack;
            Close();
        }

        void btnCancel_Click(object sender, EventArgs e)
        {
            reply = enuPlyrAttackInterfaceResults.cancelAttack;
            Close();
        }

        string getUnitStats(classBattleUnit thisUnit)
        {
            return "\r\n\t" 
                    + thisUnit.myType.ToString() 
                    + "(" 
                    + thisUnit.intSize.ToString() 
                    + "), commanded by " 
                    + thisUnit.myRank.ToString().Replace("_", " ");
        }
    }

    public class classOpenFieldDefenseSearchTreeNode
    {
        public const string strSolutionFieldDelimiter = "."; 
        public string strStepFromParent;
        public classBattleField.enuOpenFieldDefenseSearchTree_Command command;
        public classOpenFieldDefenseSearchTreeNode[] childNodes;
        public classOpenFieldDefenseSearchTreeNode parentNode;
        public classBattleField.classOpenFieldDefenseInfo OFDInfo;
        public classOpenFieldDefenseSearch_UnitInfo[] unitInfo;

        public Point udrVPP_TargetLoc;

        public bool bolAllOptionsAreExhausted = false;
        public bool bolValidSolution = false;
        
        public classOpenFieldDefenseSearchTreeNode(classOpenFieldDefenseSearchTreeNode _Parentnode,
                                                   classBattleField.enuOpenFieldDefenseSearchTree_Command _command,
                                                   string _strStepFromParent,
                                                   classBattleField.classOpenFieldDefenseInfo _OFDInfo,
                                                   classOpenFieldDefenseSearch_UnitInfo[] _UnitInfo)
        {
            parentNode = _Parentnode;
            command = _command;
            strStepFromParent = _strStepFromParent;
            OFDInfo =_OFDInfo;
            unitInfo = _UnitInfo;
            childNodes = new classOpenFieldDefenseSearchTreeNode[0];
        }
    }

    public class classOpenFieldDefenseSearch_UnitInfo
    {
        public string strUnitID;
        public int intUnitID;
        public Point pt;
        public int intMovementPointsLeft;
        public bool fullstop;
        public classBattleUnit unit;
    }

}