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;
}
}