Click here to Skip to main content
15,892,059 members
Articles / Artificial Intelligence

Battlefield Simulator

Rate me:
Please Sign up or sign in to vote.
4.86/5 (40 votes)
17 Nov 2009CPOL17 min read 97.7K   2K   104  
KOEI-like old-school war game and battlefield simulator, with infantry, artillery, and cavalry units.
  • battlefield_c_2008.zip
    • BattleField
      • barrage.bmp
      • BattleField Intro.bmp
      • BattleField.sln
      • BattleField.suo
      • BattleField
        • BattleField.csproj
        • BattleField.csproj.user
        • BattleFieldIcon.ico
        • bin
          • Release
            • BattleField.pdb
            • Map001.map
            • Map002.map
            • Map003.map
            • Map004.map
            • map005.map
            • Map006.map
            • Map007.map
            • Map008.map
            • Map009.map
            • MapPlainField.map
            • MapPlainField_large.map
        • classBattle.cs
        • classBattleField.cs
        • classBattleUnit.cs
        • classMapEditor.cs
        • classMessageBox.cs
        • formBattleField.cs
        • formBattleField.Designer.cs
        • formBattleField.resx
        • Program.cs
        • Properties
        • Resources
          • barrage.bmp
          • BattleField Intro.bmp
          • Blue_Artillery.bmp
          • Blue_Cavalry.bmp
          • Blue_Infantry.bmp
          • Font_0.bmp
          • Font_1.bmp
          • Font_2.bmp
          • Font_3.bmp
          • Font_4.bmp
          • Font_5.bmp
          • Font_6.bmp
          • Font_7.bmp
          • Font_8.bmp
          • Font_9.bmp
          • Font_RightSlash.bmp
          • horse_Left.bmp
          • horse_Right.bmp
          • rank_captain.bmp
          • rank_colonel.bmp
          • rank_field_marshall.bmp
          • rank_general.bmp
          • rank_lieutenant.bmp
          • rank_major.bmp
          • Red_Artillery.bmp
          • Red_Cavalry.bmp
          • Red_Infantry.bmp
          • terrain_castle.bmp
          • terrain_castle_shaded.bmp
          • terrain_castle_target.bmp
          • terrain_forest.bmp
          • terrain_forest_shaded.bmp
          • terrain_forest_target.bmp
          • terrain_grass.bmp
          • terrain_grass_shaded.bmp
          • terrain_grass_target.bmp
          • terrain_mountain.bmp
          • terrain_mountain_shaded.bmp
          • terrain_mountain_target.bmp
          • terrain_road.bmp
          • terrain_road_shaded.bmp
          • terrain_road_target.bmp
          • terrain_water.bmp
          • terrain_water_shaded.bmp
          • terrain_water_target.bmp
      • BattleFieldIcon.ico
      • Blue_Artillery.bmp
      • Blue_Cavalry.bmp
      • Blue_Infantry.bmp
      • Font_0.bmp
      • Font_1.bmp
      • Font_2.bmp
      • Font_3.bmp
      • Font_4.bmp
      • Font_5.bmp
      • Font_6.bmp
      • Font_7.bmp
      • Font_8.bmp
      • Font_9.bmp
      • Font_RightSlash.bmp
      • horse_Left.bmp
      • horse_Right.bmp
      • rank_captain.bmp
      • rank_colonel.bmp
      • rank_field_marshall.bmp
      • rank_general.bmp
      • rank_lieutenant.bmp
      • rank_major.bmp
      • Red_Artillery.bmp
      • Red_Cavalry.bmp
      • Red_Infantry.bmp
      • terrain_castle.bmp
      • terrain_castle_shaded.bmp
      • terrain_castle_target.bmp
      • terrain_forest.bmp
      • terrain_forest_shaded.bmp
      • terrain_forest_target.bmp
      • terrain_grass.bmp
      • terrain_grass_shaded.bmp
      • terrain_grass_target.bmp
      • terrain_mountain.bmp
      • terrain_mountain_shaded.bmp
      • terrain_mountain_target.bmp
      • terrain_road.bmp
      • terrain_road_shaded.bmp
      • terrain_road_target.bmp
      • terrain_water.bmp
      • terrain_water_shaded.bmp
      • terrain_water_target.bmp
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;
        
        classBattle.udtCartesian udrMouseHoverLoc = new classBattle.udtCartesian();

        // 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 classBattle.udtCartesian udrCastleLoc = new classBattle.udtCartesian();

        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.udtTerrain[,] terrain;                   // 2d map of the terrain
        public classBattle.udtCartesian MapSize;
        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 udtArmyStats[] udrArmyStats = new udtArmyStats[2];   // used to evaluate each color's strengths for AI comparison
        public udtPOCSearchInfo[] udrPOC = new udtPOCSearchInfo[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 classBattle.udtCartesian 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 udtAnimation udrAnimation = new udtAnimation();      // 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 udtOpenFieldDefenseInfo udrCDInfo;
        classBattle.udtCartesian[] udrBlueLaunchLocs = new classBattle.udtCartesian[(int)classBattle.intMaxInvasionForce];
        classBattle.udtRedLaunchLocations udrRedLaunchLocs = new classBattle.udtRedLaunchLocations();
        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 struct udtAnimation
        {
            public bool flag;                                       // 
            public classBattleUnit unit;
            public enuUnitAnimationType typeAnimation;
            public enuAttackAnimationPhase animation_AttackPhase;
            public int intAnimationCounter;
        }

        public struct udtArmyStats
        {
            public classBattleUnit[] units;
            public udtUnitStats total;
        }

        public struct udtUnitStats
        {
            public udtTypeStat melee;
            public udtTypeStat range;
        }

        public struct udtTypeStat
        {
            public double defense;
            public double offense;
        }

        public struct udtAIShortestPathToEnemy          // used in find-POC get shortestpath to ALL enemies as a return array
        {
            public string path;
            public classBattleUnit unit;
            public bool flag;
            public classBattle.udtCartesian udrLoc;
            public int rating;
            public int intTotalMovementCost;
        }

        public struct udtTravelCost_Array
        {
            public udtTravelCost[] Array;
            public classBattle.udtCartesian udrLoc_Start;
        }

        public struct udtTravelCost
        {
            public classBattle.udtCartesian udrLoc_End;
            public int intMovementPoints;
            public string path;
        }

        public struct udtPOCSearchInfo
        {
            public bool[,] bolAvoidMap;
            public short[,] POCMap;
            public classBattleUnit unit;
            public short shrNumFound;
            public udtPOCLocs[] POCLocs;
            public int intDefensiveStrength;
        }

        public struct udtPOCLocs
        {
            public classBattle.udtCartesian Loc;
            public bool flag;
        }

        public struct udtOpenFieldDefenseInfo
        {
            public udtOpenFieldDefense_MapUnit[,] deploymentMap;
            public bool[,] bolAvoidMap;
            public bool[,] bolFreeTravelZone;
            public udtOpenFieldDefense_Position[] Pos;
            public string strMoveSequence;
            public bool bolInitialMovesComplete;
        }

        public enum enuOpenFieldDefensePosition_Status { empty, occupied, safe };

        public struct udtOpenFieldDefense_Position
        {
            public classBattle.udtCartesian udrLoc;
            public classBattle.enuUnitTypes typeUnit;
            public enuOpenFieldDefensePosition_Status status;
            public bool bolSearchComplete;
            public classBattleUnit unit;
            public classOpenFieldDefenseSearch_UnitInfo[] suitableUnitsThatCanReachThisPos;
        }

        public struct udtOpenFieldDefense_MapUnit
        {
            public classBattle.enuUnitTypes typeUnit;
            public classBattleUnit unitThere;
        }


        public struct udtOpenFieldDefenseSearch_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(udtPOCSearchInfo 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].Loc);
                    lblDebug[intPOCLocCounter].Left = getGridMapObjectLeft(udrPOC.POCLocs[intPOCLocCounter].Loc);
                    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(udtOpenFieldDefenseInfo 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)
        {
            classBattle.udtCartesian udrLoc_New = determineMapGridLocByObjectLoc(e.X, e.Y);
            if (LocOnMap(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;
            classBattle.udtCartesian udrMapSquare = determineMapGridLocByObjectLoc(pic.Left, pic.Top);
            hoverOverUnit = terrain[udrMapSquare.x, udrMapSquare.y].unit;
            showHoverOverUnitInfo();
        }

        void resetScrollBars()
        {
            // remember what was visible before size change
            classBattle.udtCartesian udrVisiblePos = new classBattle.udtCartesian();
            udrVisiblePos.x = -picMap.Left;
            udrVisiblePos.y = -picMap.Top;

            classBattle.udtCartesian 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)MapSize.x * HSB.Maximum);
                VSB.Value = (int)((double)udrVisibleMapSquare.y / (double)MapSize.y * 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)
        {
            classBattle.udtCartesian 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(classBattle.udtCartesian 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 LocOnMap(classBattle.udtCartesian udrLoc)
        {
            return (udrLoc.x >= 0
                    && udrLoc.x < MapSize.x
                    && udrLoc.y >= 0
                    && udrLoc.y < MapSize.y);
        }

        public void displayMapLoc(classBattle.udtCartesian udrLoc, classBattle.enuTerrainImageSetting terrainImageSetting)
        { displayMapLoc(udrLoc, terrainImageSetting, false); }
        public void displayMapLoc(classBattle.udtCartesian udrLoc, classBattle.enuTerrainImageSetting terrainImageSetting, bool bolForceResize)
        {
            if (LocOnMap(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 (MapSize.x == 0 || MapSize.y == 0)
                return;
            Bitmap entireMap = new Bitmap(MapSize.x * intMapGridSize, (int)((MapSize.y + .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 < MapSize.y; intY++)
                {
                    for (int intX = 0; intX < MapSize.x; intX++)
                    {
                        displayMapLoc(cLibBattle.getPos(intX, intY), classBattle.enuTerrainImageSetting.normal, bolForceResize);
                    }
                }
            picMap.Width = MapSize.x * intMapGridSize;
            picMap.Height = (int)(((double)MapSize.y + .5) * intMapGridSize);

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

            picMap.Width = MapSize.x * intMapGridSize;
            picMap.Height = (int)(((double)MapSize.y + 0.5) * intMapGridSize);

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

        void PlyrMove_ShowShortestPath(classBattle.udtCartesian 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 < MapSize.x; intX++)
                for (int intY = 0; intY < MapSize.y; 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 < MapSize.x; intX++)
            {
                for (int intY = 0; intY < MapSize.y; 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.Properties.Resources.terrain_grass,
                                     BattleField.Properties.Resources.terrain_forest,
                                     BattleField.Properties.Resources.terrain_road,
                                     BattleField.Properties.Resources.terrain_water,
                                     BattleField.Properties.Resources.terrain_mountain ,
                                     BattleField.Properties.Resources.terrain_castle };

            for (int intX = 0; intX < MapSize.x; intX++)
            {
                for (int intY = 0; intY < MapSize.y; 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(classBattle.udtCartesian 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(classBattle.udtCartesian pos) { return getGridMapObjectLeft(pos.x); }
        public int getGridMapObjectLeft(int intX) { return intMapGridSize * intX; }
        public classBattle.udtCartesian getGridLocation(classBattle.udtCartesian pos)
        {
            classBattle.udtCartesian udrRetVal = new classBattle.udtCartesian();
            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)
        {
            BinaryFormatter formatter = new BinaryFormatter();
            FileStream fs = new FileStream(strBattlefieldFilename, FileMode.Open);
            MapSize = (classBattle.udtCartesian)formatter.Deserialize(fs);
            setIntStandardCartesianStringFieldLength();

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

            udrRedLaunchLocs = new classBattle.udtRedLaunchLocations();
            udrRedLaunchLocs.dir = new classBattle.udtCartesian_Array[((int)classBattle.enuInitialUnitLocDirIndices._num_RedInitialLocIndices)];
            udrBlueLaunchLocs = new classBattle.udtCartesian[classBattle.intMaxInvasionForce];

            for (classBattle.enuInitialUnitLocDirIndices dirCounter = 0; dirCounter < classBattle.enuInitialUnitLocDirIndices._num_RedInitialLocIndices; dirCounter++)
            {
                udrRedLaunchLocs.dir[(int)dirCounter] = new classBattle.udtCartesian_Array();
                udrRedLaunchLocs.dir[(int)dirCounter].loc = new classBattle.udtCartesian[classBattle.intMaxInvasionForce];
                for (int intLocCounter = 0; intLocCounter < classBattle.intMaxInvasionForce; intLocCounter++)
                    udrRedLaunchLocs.dir[(int)dirCounter].loc[intLocCounter] = (classBattle.udtCartesian)formatter.Deserialize(fs);
            }

            for (int intLocCounter = 0; intLocCounter < classBattle.intMaxInvasionForce; intLocCounter++)
                udrBlueLaunchLocs[intLocCounter] = (classBattle.udtCartesian)formatter.Deserialize(fs);

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

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

            MapSize.y = strTerrain.Length;
            MapSize.x = strTerrain[0].Length;

            string strTerrainTypes = "GFRWMC";
            Image[] imgTerrain = { BattleField.Properties.Resources.terrain_grass,
                                   BattleField.Properties.Resources.terrain_forest,
                                   BattleField.Properties.Resources.terrain_road,
                                   BattleField.Properties.Resources.terrain_water,
                                   BattleField.Properties.Resources.terrain_mountain,
                                   BattleField.Properties.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.udtTerrain[MapSize.x, MapSize.y];
            for (int intY = 0; intY < MapSize.y; intY++)
            {
                strTerrain[intY] = strTerrain[intY].ToUpper();
                for (int intX = 0; intX < MapSize.x; intX++)
                {
                    try
                    {
                        int intIndexType = strTerrainTypes.IndexOf(strTerrain[intY][intX]);
                        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].loc);
        }

        void addUnitsToBattleGround(ref classBattleUnit[] units, classBattle.udtCartesian[] 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(classBattle.udtCartesian udrMapSquare)
        {
            bolPlyrControlledTargetMap = PlyerSelectedUnit.getAttackMapFromLoc(udrMapSquare, PlyerSelectedUnit.intRange);
            showTargetsOnMap(bolPlyrControlledTargetMap);
        }

        classBattle.udtCartesian determineMapGridLocByObjectLoc(classBattle.udtCartesian udrPos) { return determineMapGridLocByObjectLoc(udrPos.x, udrPos.y); }
        classBattle.udtCartesian determineMapGridLocByObjectLoc(int intLeft, int intTop)
        {
            classBattle.udtCartesian udrRetVal;
            for (udrRetVal.x = 0; udrRetVal.x < MapSize.x; udrRetVal.x++)
                if (getGridMapObjectLeft(udrRetVal.x + 1) > intLeft)
                    break;
            for (udrRetVal.y = 0; udrRetVal.y < MapSize.y; 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 { 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]);
        }

        udtArmyStats getArmyStats(ref classBattleUnit unitQ)
        {
            udtArmyStats udrRetVal = new udtArmyStats();
            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 udtUnitStats total, udtUnitStats 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 classBattle.udtCartesian[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].Loc) == 0)
                                    {
                                        Array.Resize<classBattle.udtCartesian>(ref classBattleUnit.udrUnGuardPOCs, classBattleUnit.udrUnGuardPOCs.Length + 1);
                                        classBattleUnit.udrUnGuardPOCs[classBattleUnit.udrUnGuardPOCs.Length - 1] = udrPOC[(int)classBattle.enuUnitColors.blue].POCLocs[intPOCCounter].Loc;
                                    }
                            }
                        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()
        {
            classBattle.udtCartesian 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()
        {
            classBattle.udtCartesian 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.Properties.Resources.horse_Left
                                                                                : BattleField.Properties.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;
            }
        }

        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, classBattle.udtCartesian 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, classBattle.udtCartesian 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();
            }
        }

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

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

        bool posUnusedOFDLoc(udtOpenFieldDefenseInfo udrCD, classBattle.udtCartesian udrLoc)
        {
            return (udrCD.deploymentMap[udrLoc.x, udrLoc.y].typeUnit == classBattle.enuUnitTypes._num_UnitTypes);
        }

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

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

        void setBolArrayAroundLoc(ref bool[,] bolArtilleryDangerZone, classBattle.udtCartesian udrLoc)
        {
            for (classBattle.enuDirections dirCounter = 0; dirCounter < classBattle.enuDirections._num_directions; dirCounter++)
            {
                classBattle.udtCartesian udrNewPos = getNextPos(udrLoc, dirCounter);
                if (LocOnMap(udrNewPos))
                    bolArtilleryDangerZone[udrNewPos.x, udrNewPos.y] = true;
                bolArtilleryDangerZone[udrLoc.x, udrLoc.y] = true;
            }
        }

        void addOFDPosToArray(ref udtOpenFieldDefenseInfo udrOFDInfo, classBattle.udtCartesian 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<udtOpenFieldDefense_Position>(ref udrOFDInfo.Pos, udrOFDInfo.Pos.Length + 1);
            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]);
                }
            }
        }

        udtOpenFieldDefenseSearch_Result OFD_GetResult(classOpenFieldDefenseSearchTreeNode thisNode)
        {
            udtOpenFieldDefenseSearch_Result udrResult = new udtOpenFieldDefenseSearch_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(udtOpenFieldDefense_Position[] PosArray, classBattle.udtCartesian  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;
        }

        udtOpenFieldDefenseSearch_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
            udtOpenFieldDefenseSearch_Result udrResult = new udtOpenFieldDefenseSearch_Result();
            udrResult.intTotalMovementPointsLeft = 0;
            udrResult.strMoves = "";
            udrResult.intNumberPosLeftUnsolved = thisNode.OFDInfo.Pos.Length-1;

            classBattle.udtCartesian udrMoveToLoc = new classBattle.udtCartesian();
            classBattle.udtCartesian 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].Loc = 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(classBattle.udtCartesian udrLoc)
        { return udrLoc.x.ToString().PadLeft(intStandardCartesianStringFieldLength, '0') + udrLoc.y.ToString().PadLeft(intStandardCartesianStringFieldLength, '0'); }

        bool OFD_ThereIsAnUntriedPlaceForUnitToMoveFromPP(classOpenFieldDefenseSearchTreeNode thisNode, ref classBattle.udtCartesian 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;
        }

        udtOpenFieldDefenseSearch_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
            /// 
            udtOpenFieldDefenseSearch_Result udrResult = new udtOpenFieldDefenseSearch_Result();
            udrResult.intTotalMovementPointsLeft = 0;
            udrResult.strMoves = "";
            udrResult.intNumberPosLeftUnsolved = thisNode.OFDInfo.Pos.Length-1;

            classBattle.udtCartesian udrMoveFromLoc;
            classBattle.udtCartesian udrMoveToLoc;

            if (thisNode.OFDInfo.Pos.Length == 0)
            {
                udrResult = OFD_GetResult(thisNode);
            }
            else
            {
                classBattle.udtCartesian 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].Loc;
                        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].Loc);

                        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].Loc);
                        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].Loc = 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;
        }

        udtOpenFieldDefense_Position OFD_getPosCopy(udtOpenFieldDefense_Position ofdPos)
        {
            udtOpenFieldDefense_Position udrRetVal = new udtOpenFieldDefense_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)
        {
            udtOpenFieldDefenseInfo udrOFDInfo = new udtOpenFieldDefenseInfo();
            udrOFDInfo.bolAvoidMap = getBolDefaultAvoidMap();
            udrOFDInfo.bolFreeTravelZone = thisNode.OFDInfo.bolFreeTravelZone;
            udrOFDInfo.deploymentMap = thisNode.OFDInfo.deploymentMap;

            udrOFDInfo.Pos = new udtOpenFieldDefense_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].Loc.x = thisNode.unitInfo[intUnitCounter].Loc.x;
                unitInfo[intUnitCounter].Loc.y = thisNode.unitInfo[intUnitCounter].Loc.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(udtOpenFieldDefenseInfo udrOFDInfo, classOpenFieldDefenseSearch_UnitInfo[] unitInfo, classBattle.udtCartesian 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[MapSize.x, MapSize.y];
            for (int intUnitCounter = 0; intUnitCounter < unitInfo.Length; intUnitCounter++)
                udrUnitArray[unitInfo[intUnitCounter].Loc.x, unitInfo[intUnitCounter].Loc.y] = unitInfo[intUnitCounter];

            classBattleUnit.udtPathMap[,] udrPathMap = new classBattleUnit.udtPathMap[MapSize.x, MapSize.y];
            for (int intX = 0; intX < MapSize.x; intX++)
                for (int intY = 0; intY < MapSize.y; intY++)
                {
                    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++)
                {
                    classBattle.udtCartesian udrNextPos = getNextPos(thisQEle.pos, dirCounter);
                    if (udrNextPos.x >= 0 && udrNextPos.y >= 0 && udrNextPos.x < MapSize.x && udrNextPos.y < MapSize.y)
                    {
                        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);
                    classBattle.udtCartesian 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;
        }

        udtOpenFieldDefense_Position[] OFD_getPrioritizedLocs(udtOpenFieldDefenseInfo 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
            udtOpenFieldDefense_Position[] udrRetVal = new udtOpenFieldDefense_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<udtOpenFieldDefense_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;
            udtOpenFieldDefense_Position udrTemp = new udtOpenFieldDefense_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
            /// 
            udtOpenFieldDefenseSearch_Result udrResult = new udtOpenFieldDefenseSearch_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);
                    udtOpenFieldDefenseSearch_Result thisResult = new udtOpenFieldDefenseSearch_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(classBattle.udtCartesian 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 udtOpenFieldDefense_Position[0];
            udrCDInfo.deploymentMap = new udtOpenFieldDefense_MapUnit[MapSize.x, MapSize.y];
            for (int intX = 0; intX < MapSize.x; intX++)
                for (int intY = 0; intY < MapSize.y; intY++)
                    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;
            classBattle.udtCartesian[] udrSearchStarLoc = new classBattle.udtCartesian[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)
                        {
                            classBattle.udtCartesian udrNextStepInPath = getNextPos(udrSearchStarLoc[intIndexCurrentSearchElement], dirFirstStep);
                            classBattle.udtCartesian[] udrPosNext = new classBattle.udtCartesian[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
                        {
                            classBattle.udtCartesian udrNextStepAfterNearestEmptySquare = new classBattle.udtCartesian();
                            classBattle.udtCartesian 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
                classBattle.udtCartesian udrNextStepAfterNearestEmptySquare = new classBattle.udtCartesian();
                classBattle.udtCartesian udrNearestEmptySquareAlongPathToEnemy = OFD_getNearestUnusedSquareAlongPath(udrDefendLoc, strShortestPathToNearestEnemy, udrCDInfo, ref udrNextStepAfterNearestEmptySquare);
                if (udrNearestEmptySquareAlongPathToEnemy.x == udrDefendLoc.x && udrNearestEmptySquareAlongPathToEnemy.y == udrDefendLoc.y)
                {
                    classBattle.udtCartesian 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].Loc = thisUnit.myPosition;
                udrRetVal[udrRetVal.Length - 1].unit = thisUnit;

                thisUnit = thisUnit.next;
            }

            return udrRetVal;
        }

        void addLocToArray(ref classBattle.udtCartesian[] udrArrayLoc, classBattle.udtCartesian udrLocNew)
        {
            Array.Resize<classBattle.udtCartesian>(ref udrArrayLoc, udrArrayLoc.Length + 1);
            udrArrayLoc[udrArrayLoc.Length - 1] = cLibBattle.getPos(udrLocNew.x, udrLocNew.y);
        }

        classBattle.udtCartesian OFD_getNearestUnusedSquareAlongPath(classBattle.udtCartesian udrStartLoc, string strPath, udtOpenFieldDefenseInfo udrCDInfo, ref classBattle.udtCartesian udrNextStep)
        {// nearest unused open-field-defense position along path to enemy
            classBattle.udtCartesian 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]);
        }

        udtPOCSearchInfo determinePointsOfConvergence()
        {
            udtPOCSearchInfo[] udrPOC = new udtPOCSearchInfo[0];
            udtPOCSearchInfo[] udrPOCOptions = new udtPOCSearchInfo[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[MapSize.x, MapSize.y];

            do
            {
                Array.Resize<udtPOCSearchInfo>(ref udrPOCOptions, udrPOCOptions.Length + 1);
                udrPOCOptions[udrPOCOptions.Length - 1].POCMap = new short[MapSize.x, MapSize.y];
                udrPOCOptions[udrPOCOptions.Length - 1].POCLocs = new udtPOCLocs[0];
                for (int intX = 0; intX < MapSize.x; intX++)
                    for (int intY = 0; intY < MapSize.y; intY++)
                        udrPOCOptions[udrPOCOptions.Length - 1].POCMap[intX, intY] = (short)(impassableTerrain(terrain[intX, intY].type) ? -1 : 0);

                Array.Resize<udtPOCSearchInfo>(ref udrPOC, udrPOC.Length + 1);
                udrPOC[udrPOC.Length - 1].bolAvoidMap = new bool[MapSize.x, MapSize.y];
                udrPOC[udrPOC.Length - 1].POCMap = new short[MapSize.x, MapSize.y];
                udrPOC[udrPOC.Length - 1].POCLocs = new udtPOCLocs[0];
                udrPOC[udrPOC.Length - 1].unit = unit_Castle;
                for (int intX = 0; intX < MapSize.x; intX++)
                    for (int intY = 0; intY < MapSize.y; 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))
                {
                    classBattle.udtCartesian udrPOCLoc = cLibBattle.getPos(
                                                                udrPOC[udrPOC.Length - 1].POCLocs[
                                                                                                 udrPOC[udrPOC.Length - 1].POCLocs.Length - 1
                                                                                                 ].Loc.x,
                                                                udrPOC[udrPOC.Length - 1].POCLocs[
                                                                                                 udrPOC[udrPOC.Length - 1].POCLocs.Length - 1
                                                                                                 ].Loc.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].Loc.x, udrPOC[udrPOC.Length - 1].POCLocs[intLocCounter].Loc.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
            udtPOCSearchInfo udrRetVal = udrPOCOptions[intBestIndex];
            udrRetVal.bolAvoidMap = getBolDefaultAvoidMap();


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

        void setDefensiveStrength(ref udtPOCSearchInfo udrPOCInfo)
        {
            classBattle.udtCartesian[] udrLocs = new classBattle.udtCartesian[udrPOCInfo.POCLocs.Length];
            for (int intLocCounter = 0; intLocCounter < udrPOCInfo.POCLocs.Length; intLocCounter++)
                udrLocs[intLocCounter] = udrPOCInfo.POCLocs[intLocCounter].Loc;

            bool[,] bolAvoidMap = new bool[MapSize.x, MapSize.y];
            for (int intX = 0; intX < MapSize.x; intX++)
                for (int intY = 0; intY < MapSize.y; intY++)
                    bolAvoidMap[intX, intY] = (udrPOCInfo.POCMap[intX, intY] != 0);

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

        void reorderPOCs(ref udtPOCSearchInfo udrPOC)
        {
            short shrBestValue = 0;
            int intBestIndex = 0;
            udtPOCLocs udrTemp;
            if (udrPOC.POCLocs != null)
                for (int intOuterLoop = 0; intOuterLoop < udrPOC.POCLocs.Length - 1; intOuterLoop++)
                {
                    shrBestValue = udrPOC.POCMap[udrPOC.POCLocs[intOuterLoop].Loc.x, udrPOC.POCLocs[intOuterLoop].Loc.y];
                    intBestIndex = intOuterLoop;
                    for (int intInnerLoop = intOuterLoop + 1; intInnerLoop < udrPOC.POCLocs.Length; intInnerLoop++)
                    {
                        if (shrBestValue < udrPOC.POCMap[udrPOC.POCLocs[intInnerLoop].Loc.x, udrPOC.POCLocs[intInnerLoop].Loc.y])
                        {
                            shrBestValue = udrPOC.POCMap[udrPOC.POCLocs[intInnerLoop].Loc.x, udrPOC.POCLocs[intInnerLoop].Loc.y];
                            intBestIndex = intInnerLoop;
                        }
                    }
                    if (intBestIndex != intOuterLoop)
                    {
                        udrTemp = udrPOC.POCLocs[intOuterLoop];
                        udrPOC.POCLocs[intOuterLoop] = udrPOC.POCLocs[intBestIndex];
                        udrPOC.POCLocs[intBestIndex] = udrTemp;
                    }
                }
        }

        bool determinePointsOfConvergence(ref udtPOCSearchInfo udrPOCInfo, bool[,] bolIgnorePOCMap)
        {   
            classBattleField.udtAIShortestPathToEnemy[] udrPathsTo_Red = udrPOCInfo.unit.getShortestPathToAllEnemyUnits(udrPOCInfo.unit.myPosition, udrPOCInfo.bolAvoidMap);
            classBattle.udtCartesian 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, classBattle.udtCartesian 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, classBattle.udtCartesian 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 udtPOCSearchInfo 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].Loc) == 0)
                {
                    udrPOCInfo.POCMap[udrPOCInfo.POCLocs[intPOCCounter].Loc.x, udrPOCInfo.POCLocs[intPOCCounter].Loc.y] = -1;
                    udrPOCInfo.POCLocs[intPOCCounter] = udrPOCInfo.POCLocs[udrPOCInfo.POCLocs.Length - 1];
                    Array.Resize<udtPOCLocs>(ref udrPOCInfo.POCLocs, udrPOCInfo.POCLocs.Length - 1);
                }
            }
        }

        int countSafeSquaresAroundLoc(udtPOCSearchInfo udrPOCInfo, classBattle.udtCartesian udrLoc)
        {
            int intRetVal = 0;
            classBattle.udtCartesian udrNextPos;
            for (classBattle.enuDirections dirCounter = 0; dirCounter < classBattle.enuDirections._num_directions; dirCounter++)
            {
                udrNextPos = getNextPos(udrLoc, dirCounter);
                if (LocOnMap(udrNextPos))
                    if (udrPOCInfo.POCMap[udrNextPos.x, udrNextPos.y] == 0)
                        intRetVal++;
            }
            return intRetVal;
        }

        classBattle.enuUnitColors WhoCanReachLocFirst(classBattle.udtCartesian 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 udtPOCSearchInfo udrPOCInfo, classBattleField.udtAIShortestPathToEnemy 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 udtCartesian 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;
            classBattle.udtCartesian udrThisLoc = new classBattle.udtCartesian();
            classBattle.udtCartesian udrNextLoc = udrPOCInfo.unit.myPosition;
            classBattle.udtCartesian udrAfterNextLoc = getNextPos(udrNextLoc, getDirFromString(udrPathToEnemyUnit.path.Substring(0, 2)));
            bool bolSkipOne = false;
            bool bolRetVal = false;
            classBattle.udtCartesian udrLastPOCFound = new classBattle.udtCartesian();

            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);
            classBattle.udtCartesian[] udrResetBolAvoid = new classBattle.udtCartesian[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);
                        classBattle.udtCartesian udrNextStepForward = getNextPos(udrAfterNextLoc, dirLastStep);

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

                        classBattle.enuDirections dirNextRight = (classBattle.enuDirections)(((int)dirLastStep + 1) % (int)(classBattle.enuDirections._num_directions));
                        classBattle.udtCartesian 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 (classBattle.udtCartesian udrThisReset in udrResetBolAvoid)
                    udrPOCInfo.bolAvoidMap[udrThisReset.x, udrThisReset.y] = false;
            }
            return bolRetVal;
        }

        void addPOC(ref udtPOCSearchInfo udrPOC, classBattle.udtCartesian udrLoc)
        { addPOC(ref udrPOC, udrLoc, true); }
        void addPOC(ref udtPOCSearchInfo udrPOC, classBattle.udtCartesian udrLoc, bool bolValid)
        {
            udtPOCLocs udrPOCLoc = new udtPOCLocs();
            udrPOCLoc.Loc = udrLoc;
            udrPOCLoc.flag = bolValid;
            if (udrLoc.x == null || udrLoc.y == null)
            {
                System.Windows.Forms.MessageBox.Show("fuck!");
            }
            else
            {
                Array.Resize<udtPOCLocs>(ref udrPOC.POCLocs, udrPOC.POCLocs.Length + 1);
                udrPOC.POCLocs[udrPOC.POCLocs.Length - 1] = udrPOCLoc;
                udrPOC.POCMap[udrLoc.x, udrLoc.y] = 1;
            }
        }

        public int getNumMoveSquaresAroundLoc(classBattle.udtCartesian udrLoc)
        { return getNumMoveSquaresAroundLoc(udrLoc, getBolDefaultAvoidMap()); }
        public int getNumMoveSquaresAroundLoc(classBattle.udtCartesian 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++)
            {
                classBattle.udtCartesian udrNewPos = getNextPos(udrLoc, dirCounter);
                if (udrNewPos.x >= 0
                    && udrNewPos.x < MapSize.x
                    && udrNewPos.y >= 0
                    && udrNewPos.y < MapSize.y)
                    intRetVal += (!impassableTerrain(udrNewPos) && !bolAvoidMap[udrNewPos.x, udrNewPos.y] ? 1 : 0);
            }

            return intRetVal;
        }

        public void reorderByPathLengthAIShortestPathToEnemyArray(ref classBattleField.udtAIShortestPathToEnemy[] udrArray)
        {
            /// quick sort by path string length, longest first, shortest last
            classBattleField.udtAIShortestPathToEnemy udrTemp = new udtAIShortestPathToEnemy();
            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.udtAIShortestPathToEnemy[] udrArray)
        {
            /// quick sort by path string rating, highest first, lowest last
            classBattleField.udtAIShortestPathToEnemy udrTemp = new udtAIShortestPathToEnemy();
            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 = MapSize.x.ToString();
            string strHeight = MapSize.y.ToString();

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

        int getLowestTravelCostBetweenMultipleLocs(classBattle.udtCartesian[] udrLocs, bool[,] bolAvoidMap)
        {
            if (udrLocs.Length == 0)
                return 0;
            udtTravelCost_Array[] udrTravelCost = new udtTravelCost_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 udtTravelCost_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 udtTravelCost[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;

            classBattle.udtCartesian 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
                classBattle.udtCartesian 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<udtTravelCost_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(udtTravelCost_Array[] udrTravelCost_Array, classBattle.udtCartesian 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 udtTravelCost[] udrTravelCost, classBattle.udtCartesian 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<udtTravelCost>(ref udrTravelCost, udrTravelCost.Length - 1);
                    reorderTravelCostArray(ref udrTravelCost);
                    return;
                }
            }
        }

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

            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[MapSize.x, MapSize.y];
            for (int intX = 0; intX < MapSize.x; intX++)
                for (int intY = 0; intY < MapSize.y; 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(classBattle.udtCartesian udrPos) { return getMovementCost(terrain[udrPos.x, udrPos.y].type); }
        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(classBattle.udtCartesian udrLoc)
        {
            if (udrLoc.x >= 0 && udrLoc.x < MapSize.x
                && udrLoc.y >= 0 && udrLoc.y < MapSize.y)
                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 classBattle.udtCartesian getNextPos(classBattle.udtCartesian unitPos, classBattle.enuDirections dir)
        {
            classBattle.udtCartesian udrMoveVector;
            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.udtMaxMin udrMaxMinSize;
        classBattle.udtCartesian 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.Properties.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);
            classBattle.udtCartesian udrCenterPos = new classBattle.udtCartesian();
            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.udtOpenFieldDefenseInfo OFDInfo;
        public classOpenFieldDefenseSearch_UnitInfo[] unitInfo;

        public classBattle.udtCartesian udrVPP_TargetLoc;

        public bool bolAllOptionsAreExhausted = false;
        public bool bolValidSolution = false;
        
        public classOpenFieldDefenseSearchTreeNode(classOpenFieldDefenseSearchTreeNode _Parentnode,
                                                   classBattleField.enuOpenFieldDefenseSearchTree_Command _command,
                                                   string _strStepFromParent,
                                                   classBattleField.udtOpenFieldDefenseInfo _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 classBattle.udtCartesian Loc;
        public int intMovementPointsLeft;
        public bool fullstop;
        public classBattleUnit unit;
    }

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO unemployable
Canada Canada
Christ Kennedy grew up in the suburbs of Montreal and is a bilingual Quebecois with a bachelor’s degree in computer engineering from McGill University. He is unemployable and currently living in Moncton, N.B. writing his next novel.

Comments and Discussions