public List<DeployArmies> AttackHard(BotState state, int armiesLeft) { string myName = state.MyPlayerName; string opponentName = state.OpponentPlayerName; List<DeployArmies> deployArmies = new List<DeployArmies>(); // pick the target with most strategic value (position and stack advantage) foreach (Region rr in state.EnemyBorders) { rr.tempSortValue = 0; // give higher count if positioned in top ranked expansion if (rr.SuperRegion.ArmiesReward == 2) rr.tempSortValue += 2; // give higher count if we have stack advantage int maxarmies = 0; foreach (Region rn in rr.Neighbors) { if (rn.OwnedByPlayer(myName)) { if (rn.Armies > maxarmies) maxarmies = rn.Armies; } } rr.tempSortValue += maxarmies - rr.Armies; } var lst = state.EnemyBorders.OrderByDescending(p => p.tempSortValue).ToList(); Region target = state.FullMap.GetRegion(lst[0].Id); // pick the neighbour (our territory) with the highest army count foreach (Region rn in target.Neighbors) { int ac = 0; if (rn.OwnedByPlayer(myName)) { ac += rn.Armies; } rn.tempSortValue = ac; } lst = target.Neighbors.OrderByDescending(p => p.tempSortValue).ToList(); Region attacker = lst[0]; // validate if (target.OwnedByPlayer(opponentName) && attacker.OwnedByPlayer(myName)) { deployArmies.Add(new DeployArmies(myName, attacker, armiesLeft)); state.ScheduleAttack(attacker, target, armiesLeft, attacker.Armies - 1 + armiesLeft); //state.scheduledAttack.Add(new Tuple<int, int, int>(attacker.Id, target.Id, attacker.Armies - 1 + armiesLeft)); armiesLeft = 0; } return deployArmies; }
/** * This method is called for at the second part of each round. This example attacks if a region has * more than 6 armies on it, and transfers if it has less than 6 and a neighboring owned region. * @return The list of PlaceArmiesMoves for one round */ public List <AttackTransferMove> GetAttackTransferMoves(BotState state, long timeOut) { List <AttackTransferMove> attackTransferMoves = new List <AttackTransferMove>(); String myName = state.MyPlayerName; int armies = 5; foreach (var fromRegion in state.VisibleMap.Regions) { if (fromRegion.OwnedByPlayer(myName)) //do an attack { List <Region> possibleToRegions = new List <Region>(); possibleToRegions.AddRange(fromRegion.Neighbors); while (possibleToRegions.Any()) { double rand = Random.NextDouble(); int r = (int)(rand * possibleToRegions.Count); Region toRegion = possibleToRegions[r]; if (toRegion.PlayerName != myName && fromRegion.Armies > 6) //do an attack { attackTransferMoves.Add(new AttackTransferMove(myName, fromRegion, toRegion, armies)); break; } else if (toRegion.PlayerName == myName && fromRegion.Armies > 1) //do a transfer { attackTransferMoves.Add(new AttackTransferMove(myName, fromRegion, toRegion, armies)); break; } else { possibleToRegions.Remove(toRegion); } } } } return(attackTransferMoves); }
/** * This method is called for at first part of each round. This example puts two armies on random regions * until he has no more armies left to place. * @return The list of PlaceArmiesMoves for one round */ public List <PlaceArmiesMove> GetPlaceArmiesMoves(BotState state, long timeOut) { List <PlaceArmiesMove> placeArmiesMoves = new List <PlaceArmiesMove>(); String myName = state.MyPlayerName; int armies = 2; int armiesLeft = state.StartingArmies; var visibleRegions = state.VisibleMap.Regions; while (armiesLeft > 0) { double rand = Random.NextDouble(); int r = (int)(rand * visibleRegions.Count); var region = visibleRegions.ElementAt(r); if (region.OwnedByPlayer(myName)) { placeArmiesMoves.Add(new PlaceArmiesMove(myName, region, armies)); armiesLeft -= armies; } } return(placeArmiesMoves); }
/** * A method used at the start of the game to decide which player start with what Regions. 6 Regions are required to be returned. * This example randomly picks 6 regions from the pickable starting Regions given by the engine. * @return : a list of m (m=6) Regions starting with the most preferred Region and ending with the least preferred Region to start with */ public List <Region> GetPreferredStartingRegions(BotState state, long timeOut) { int m = 6; List <Region> preferredStartingRegions = new List <Region>(); for (int i = 0; i < m; i++) { double rand = Random.NextDouble(); int r = (int)(rand * state.PickableStartingRegions.Count); int regionId = state.PickableStartingRegions[r].Id; Region region = state.FullMap.GetRegion(regionId); if (!preferredStartingRegions.Contains(region)) { preferredStartingRegions.Add(region); } else { i--; } } return(preferredStartingRegions); }
public List<DeployArmies> DeployAtRandom(List<Region> list, BotState state, string myName, int armiesLeft) { List<DeployArmies> deployArmies = new List<DeployArmies>(); while (armiesLeft > 0) { double rand = Random.NextDouble(); int r = (int)(rand * list.Count); Region region = state.FullMap.GetRegion(list[r].Id); // validate if it's really our region if (region.OwnedByPlayer(myName)) { deployArmies.Add(new DeployArmies(myName, region, 1)); region.PledgedArmies += 1; armiesLeft -= 1; } } return deployArmies; }
/** * This method is called for at the second part of each round. This example attacks if a region has * more than 6 armies on it, and transfers if it has less than 6 and a neighboring owned region. * @return The list of DeployArmiess for one round */ public List<AttackTransferMove> GetAttackTransferMoves(BotState state, long timeOut) { List<AttackTransferMove> attackTransferMoves = new List<AttackTransferMove>(); string myName = state.MyPlayerName; string opponentName = state.OpponentPlayerName; // process already scheduled attacks (during the deployment phase) if (state.scheduledAttack.Count > 0) { foreach (Tuple<int, int, int> tup in state.scheduledAttack) { Region from = state.FullMap.GetRegion(tup.Item1); Region to = state.FullMap.GetRegion(tup.Item2); int armyCount = tup.Item3; attackTransferMoves.Add(new AttackTransferMove(myName, from, to, armyCount, 4)); } } Region northafrica = state.FullMap.GetRegion(21); foreach (Region re in state.VisibleMap.Regions) { Region fromRegion = state.FullMap.GetRegion(re.Id); if (fromRegion.OwnedByPlayer(myName)) { bool borderingEnemy = false; List<Region> enemyBorders = new List<Region>(); int estimatedOpponentIncome = state.EstimatedOpponentIncome; foreach (Region reg in fromRegion.Neighbors) { Region region = state.FullMap.GetRegion(reg.Id); if (region.OwnedByPlayer(opponentName)) { borderingEnemy = true; enemyBorders.Add(region); } } enemyBorders = enemyBorders.OrderBy(p => p.Armies).ToList(); int armiesLeft = fromRegion.Armies + fromRegion.PledgedArmies - fromRegion.ReservedArmies - 1; // if we are neighboring a neutral north africa, go there if (fromRegion.HasNeighbour(21) && northafrica.OwnedByPlayer("neutral") && (state.StartingArmies > 5) && (armiesLeft > 4)) { attackTransferMoves.Add(new AttackTransferMove(myName, fromRegion, northafrica, armiesLeft, 4)); fromRegion.ReservedArmies += armiesLeft; armiesLeft = 0; } // if this region is bordering the enemy if (borderingEnemy) { // attack regions that have small number of armies with small number of armies, just to clear them out foreach (Region enmm in enemyBorders) { Region en = state.FullMap.GetRegion(enmm.Id); // only do small attacks when we are bordering multiple enemies // or target hasn't changed it's income on last turn and didn't receive any deploys/transfers either // or it's in another superregion if ((enemyBorders.Count > 2) || ((en.Armies == en.PreviousTurnArmies) && (!en.DeployedOrTransferedThisTurn) && (state.EnemyBorders.Count > 2))) { // attack small armies with little armies if ((en.Armies == 1) || (en.Armies == 2)) { if (armiesLeft > en.Armies * 2) { int used = en.Armies * 2; attackTransferMoves.Add(new AttackTransferMove(myName, fromRegion, en, used, 3)); fromRegion.ReservedArmies += used; armiesLeft -= used; } } } } // move any remaining armies to hotzonestack // this will make sure you're moving stacks back to important defensive position // and also help merge stacks when double bordering enemy if ((state.HotStackZone != -1) && (armiesLeft > 0)) { foreach (Region reg in fromRegion.Neighbors) { if ((reg.Id == state.HotStackZone) && (armiesLeft > 0)) { // low priority on leftovers // some of these we might have deployed this turn on territories of 1 // and enemy bots that schedule attacks of 2 will bump into them and fail int priority = 2; // high priority on anything substantial if (armiesLeft > 3) priority = 1; if (armiesLeft >= 10) priority = 0; attackTransferMoves.Add(new AttackTransferMove(myName, fromRegion, state.FullMap.GetRegion(reg.Id), armiesLeft, priority)); fromRegion.ReservedArmies += armiesLeft; armiesLeft = 0; } } } // do a sweep to second degree neighbors to also move them towards stack // needs to be in a different loop from first degree sweep, otherwise it would steal priority from moving into hotstockzone directly due to multiple neighboring if ((state.HotStackZone != -1) && (armiesLeft > 0) && (fromRegion.Id != state.HotStackZone)) { foreach (Region reg in fromRegion.Neighbors) { //Region regFM = state.FullMap.GetRegion(reg.Id); if (!state.FullMap.GetRegion(reg.Id).OwnedByPlayer(myName)) continue; foreach (Region reg2 in reg.Neighbors) { if ((reg2.Id == state.HotStackZone) && (armiesLeft > 0)) { attackTransferMoves.Add(new AttackTransferMove(myName, fromRegion, state.FullMap.GetRegion(reg.Id), armiesLeft, 6)); fromRegion.ReservedArmies += armiesLeft; armiesLeft = 0; } } } } //todo: enemy stack could be hidden and moving in, we can use history to check // divide stack armies on planned attacks foreach (Region en in enemyBorders) { Region enm = state.FullMap.GetRegion(en.Id); // determine how much is needed to not hit a wall int nstackcount = 0; foreach (Region reg in en.Neighbors) { Region regn = state.FullMap.GetRegion(reg.Id); if (regn.OwnedByPlayer(state.OpponentPlayerName)) { nstackcount += regn.Armies - 1; } } enm.tempSortValue = nstackcount; // store this value, we will use it further down on another enemyBorders cycle int needed = (int)Math.Ceiling((enm.Armies + estimatedOpponentIncome + nstackcount) * 1.17f); int max = (int)Math.Ceiling((enm.Armies + estimatedOpponentIncome + nstackcount) * 2.0f); // if it's bordering a suspected superregion // or there is only one enemyborder // or it's north africa and we have no sight to SA // we should be hitting it and it alone fullforce //todo: unless the border to super region is actually a double border (like middle east) if (state.FullMap.RegionBelongsToEnemySuperRegion(enm.Id, myName) || ((enm.Id == 21) && state.FullMap.SuperRegionCouldBelongToOpponent(2, opponentName)) || (enemyBorders.Count == 1)) { bool alreadyScheduled = false; // check if it's already scheduled foreach (AttackTransferMove atk in attackTransferMoves) { if ((atk.FromRegion.Id == fromRegion.Id) && (atk.ToRegion.Id == enm.Id)){ alreadyScheduled = true; // buff if it up if we have anything to buff it up with int used = armiesLeft; // only use minimum necessary ammount of armies to reach the max if (atk.Armies + used > max) used = (atk.Armies + armiesLeft) - max; // maybe we should clip until this is properly tested if (used > armiesLeft) used = armiesLeft; if (used < 0) used = 0; // update atk atk.Armies += used; Region from = state.FullMap.GetRegion(atk.FromRegion.Id); from.ReservedArmies += used; armiesLeft -= used; break; } } // if not previously scheduled then try to schedule it with whatever we have if ((armiesLeft > 0) && (!alreadyScheduled)) { int used = armiesLeft; if (armiesLeft > max) armiesLeft = max; attackTransferMoves.Add(new AttackTransferMove(myName, fromRegion, enm, used, 5)); if (armiesLeft >= max) attackTransferMoves[attackTransferMoves.Count - 1].Locked = true; fromRegion.ReservedArmies += used; armiesLeft -= used; } } } // if we have armiesleft at this point it means we are not threatening a superregion or have multiple borders // we should distribute the rest of the armies evenly if (armiesLeft > 0) { // make sure all of them are scheduled foreach (Region en in enemyBorders) { Region enm = state.FullMap.GetRegion(en.Id); // check if it's already scheduled bool alreadyScheduled = false; foreach (AttackTransferMove atk in attackTransferMoves) { if ((atk.FromRegion.Id == fromRegion.Id) && (atk.ToRegion.Id == enm.Id)) { alreadyScheduled = true; break; } } // if not previously scheduled then try to schedule it with single troop if ((!alreadyScheduled) && (armiesLeft > 0)) { attackTransferMoves.Add(new AttackTransferMove(myName, fromRegion, enm, 1, 5)); armiesLeft -= 1; fromRegion.ReservedArmies += 1; } } // try to buff them up evenly with whatever we have left bool allLocked = false; bool gotany = true; while ((armiesLeft > 0) && (!allLocked) && (gotany)) { gotany = false; int biggestenemystack = -1; foreach (Region en in enemyBorders) { Region enm = state.FullMap.GetRegion(en.Id); // determine how much is needed to not hit a wall int nstackcount = enm.tempSortValue; int needed = (int)Math.Ceiling((enm.Armies + estimatedOpponentIncome + nstackcount) * 1.17f); int max = (int)Math.Ceiling((enm.Armies + estimatedOpponentIncome + nstackcount) * 2.0f); // divide it equally amongst the different targets // but give a little advantage against the highest stack if (biggestenemystack < needed) biggestenemystack = needed; // buff already scheduled bool testLock = true; foreach (AttackTransferMove atk in attackTransferMoves) { // buff it to use more armies until it's reached max // don't buff if it's a small army attack (which always has priority of 3) if ((armiesLeft > 0) && (atk.FromRegion.Id == fromRegion.Id) && (atk.ToRegion.Id == enm.Id) && (atk.Priority != 3) && (!atk.Locked)){ int deploy = 1; if ((biggestenemystack == needed) && (armiesLeft > 4)) deploy = 5; if (armiesLeft > 0) { deploy = 1; } atk.Armies += deploy; Region from = state.FullMap.GetRegion(atk.FromRegion.Id); from.ReservedArmies += deploy; armiesLeft -= deploy; if (atk.Armies >= max) atk.Locked = true; gotany = true; } if (armiesLeft <= 0) break; if (!atk.Locked) testLock = false; } allLocked = testLock; } } } //todo: later: if we have multiple areas bordering an enemy, decide in which situations we should attack with all or sit, delay a lot and attack with 2 } else { // move leftovers to where they can border an enemy or finish the highest ranked expansion target superregion if (armiesLeft > 0) { bool eborder = false; // go through all the neighbours to see into which is more worth moving into foreach (Region a in fromRegion.Neighbors) { Region an = state.FullMap.GetRegion(a.Id); // check if this neighbour is a neutral being transfered into if (an.OwnedByPlayer("neutral")) { List<AttackTransferMove> tempATM = new List<AttackTransferMove>(); foreach (AttackTransferMove atm in attackTransferMoves) { // if we are already attacking this neutral and have troops to spare // why not use them to make sure we get the neutral? if ((armiesLeft > 0) && (atm.FromRegion.Id == fromRegion.Id) && (atm.ToRegion.Id == an.Id)) { Region from = state.FullMap.GetRegion(atm.FromRegion.Id); from.ReservedArmies++; atm.Armies++; armiesLeft--; } // if it is a neutral and is being attacked from another region, we might as well help out if ((armiesLeft > 1) && (atm.FromRegion.Id != fromRegion.Id) && (atm.ToRegion.Id == an.Id)) { tempATM.Add(new AttackTransferMove(myName, fromRegion, an, 2, 10)); armiesLeft -= 2; fromRegion.ReservedArmies += 2; } } if (tempATM.Count > 0) { foreach (AttackTransferMove atm in tempATM) attackTransferMoves.Add(atm); } } // check if it's not bordering an enemy // if it is, move leftover to where they can border an enemy // or help finish the highest ranked expansion target superregion more easily int count = 0; // neighbour doesnt belong to us, can't move there if (!an.OwnedByPlayer(myName)) { // tag this region as neighboring the enemy if (an.OwnedByPlayer(opponentName)) { eborder = true; } a.tempSortValue = 0; continue; } // give bonus depending on it's neighbours if (state.ExpansionTargets.Count > 2) { foreach (Region neigh in a.Neighbors) { Region nee = state.FullMap.GetRegion(neigh.Id); // facing opponent(s), maximum priority if (nee.OwnedByPlayer(opponentName)) count += 12; // bordering another superregion(s) if (an.SuperRegion.Id != nee.SuperRegion.Id) count += 4; // meeting up with stack(s) of mine if (nee.OwnedByPlayer(myName) && (nee.Armies > 6)) count += 3; // borders neutral(s) from top expansion target (probably itself), might help finish SR faster if (nee.OwnedByPlayer("neutral") && ((nee.SuperRegion.Id == state.ExpansionTargets[0].Id) || (nee.SuperRegion.Id == state.ExpansionTargets[1].Id))) count+=2; } } a.tempSortValue = count; } // process the heuristics var lst = fromRegion.Neighbors.OrderByDescending(p => p.tempSortValue).ToList(); if ((armiesLeft > 0) && (lst.Count>0)) { Region dest = state.FullMap.GetRegion(lst[0].Id); if (dest.OwnedByPlayer(state.MyPlayerName) && (!eborder)) { int priority = 5; if (dest.Id == state.HotStackZone) { priority = 2; // high priority on anything substantial if (armiesLeft > 3) priority = 1; if (armiesLeft >= 10) priority = 0; } attackTransferMoves.Add(new AttackTransferMove(myName, fromRegion, dest, armiesLeft, priority)); Region from = state.FullMap.GetRegion(fromRegion.Id); from.ReservedArmies += armiesLeft; armiesLeft = 0; } } } } } } // buff up any scheduled attack coming from regions with armies left lying around unused // remove any call that might run into a wall (unless it's high priority) List<AttackTransferMove> atmRemove = new List<AttackTransferMove>(); foreach (AttackTransferMove atm in attackTransferMoves) { Region from = state.FullMap.GetRegion(atm.FromRegion.Id); Region to = state.FullMap.GetRegion(atm.ToRegion.Id); int armyCount = atm.Armies; // if we have orders from a region with some unused troops, use them to buff up the attack int armiesAvail = from.Armies + from.PledgedArmies - 1; if (armiesAvail > from.ReservedArmies) { int narmies = armiesAvail - from.ReservedArmies; from.ReservedArmies += narmies; atm.Armies += narmies; armyCount += narmies; } //todo: remove potential excessive armies used (due to the finish region +1 bug/feature) // prevent from hitting a wall against opponent if (to.OwnedByPlayer(opponentName)) { bool attack = true; // if army count is lower then estimated: remove attack from schedule int nstackcount = 0; // check neighbours stack to have better estimate foreach (Region reg in to.Neighbors) { Region regn = state.FullMap.GetRegion(reg.Id); if (regn.OwnedByPlayer(state.OpponentPlayerName)) { nstackcount += regn.Armies - 1; } } if (armyCount <= to.Armies + state.EstimatedOpponentIncome + nstackcount) attack = false; // if armies are the same and there was no deployortransfer and it's not just 1 enemyborder: keep attack scheduled if ((armyCount > 1) && (armyCount > to.Armies * 2) && (to.Armies == to.PreviousTurnArmies) && (!to.DeployedOrTransferedThisTurn) && (state.EnemyBorders.Count > 1)) attack = true; // if priority is higher then 8: keep attack scheduled if (atm.Priority >= 8) attack = true; // if armycount is 2: keep attack scheduled if ((armyCount == 2) && (to.Armies == 1)) attack = true; if (!attack) { Console.Error.WriteLine("prevent hitting a wall from " + from.Id + " to " + to.Id + " with " + armyCount + " armies on round " + state.RoundNumber); atmRemove.Add(atm); } } // prevent from hitting a wall against neutral if (to.OwnedByPlayer("neutral")) { bool attack = true; // if army count is lower then estimated: remove attack from schedule if (armyCount <= to.Armies) attack = false; if (!attack) { Console.Error.WriteLine("prevent hitting a wall from " + from.Id + " to " + to.Id + " with " + armyCount + " armies on round " + state.RoundNumber); atmRemove.Add(atm); } } } foreach (AttackTransferMove atm in atmRemove) { attackTransferMoves.Remove(atm); } List<AttackTransferMove> sorted = attackTransferMoves.OrderByDescending(p => p.Armies).OrderBy(p => p.Priority).ToList(); return sorted; }
public List<DeployArmies> FinishSuperRegion(BotState state, int armiesLeft) { string myName = state.MyPlayerName; //string opponentName = state.OpponentPlayerName; List<DeployArmies> deployArmies = new List<DeployArmies>(); if (state.ExpansionTargets.Count == 0) return deployArmies; //todo: later: calculate if we should be attacking a particular region more strongly (in case there is chance of counter or defensive positioning) // if you have enough income to finish an area this turn, deploy for it foreach (Region reg in state.ExpansionTargets[0].SubRegions) { Region region = state.FullMap.GetRegion(reg.Id); // skip if you already own this region if (region.OwnedByPlayer(myName)) continue; if (!region.OwnedByPlayer("neutral")) { Console.Error.WriteLine("trying to finish a FTB with " + region.PlayerName + " on it is a bit silly"); break; } // find neighbour with highest available armies (will be the attacker) foreach (Region an in region.Neighbors) { Region a = state.FullMap.GetRegion(an.Id); if (!a.OwnedByPlayer(myName)) { an.tempSortValue = -1; } else { // more armies available the better int aArmies = a.Armies + a.PledgedArmies - a.ReservedArmies; an.tempSortValue = aArmies; // if that attacker has only one neutral of this superregion in sight, give it a little bonus // this will help determine that this is the territory attacking when there are others with same armies available int neutralcnt = 0; foreach (Region un in a.Neighbors) { Region u = state.FullMap.GetRegion(un.Id); if (u.OwnedByPlayer("neutral") && (u.SuperRegion.Id == reg.Id)) neutralcnt++; } if (neutralcnt == 1) an.tempSortValue++; } } var neigh = region.Neighbors.OrderByDescending(p => p.tempSortValue).ToList(); // make sure the attacking neighbour is owned by us, so we can deploy on it if (neigh[0].OwnedByPlayer(myName)) { int deployed = state.ScheduleNeutralAttack(neigh[0], region, armiesLeft); if (deployed > 0) { deployArmies.Add(new DeployArmies(myName, neigh[0], deployed)); armiesLeft -= deployed; } } else { Console.Error.WriteLine("trying to attack out of a territory owned by " + neigh[0].PlayerName + " on round " + state.RoundNumber + "! guess we ran out of available armies :("); } } if (armiesLeft < 0) Console.Error.WriteLine("exceeded army deployment on turn " + state.RoundNumber); // deploy the rest of our armies if (armiesLeft > 0) { if (state.EnemySighted) { List<DeployArmies> placings = DeployBorderingEnemy(state, armiesLeft); foreach (DeployArmies pl in placings) { deployArmies.Add(pl); } } } return deployArmies; }
public bool FinishableSuperRegion(BotState state) { string myName = state.MyPlayerName; int armiesLeft = state.StartingArmies; if (state.ExpansionTargets.Count == 0) return false; bool finishableSuperRegion = false; SuperRegion targetSR = state.FullMap.GetSuperRegion(state.ExpansionTargets[0].Id); if (state.ExpansionTargets.Count > 1) { finishableSuperRegion = targetSR.IsFinishable(armiesLeft, myName); } // it might be finishable but is it safe to finish without being a waste of armies? if (finishableSuperRegion) { if (!targetSR.IsSafeToFinish(state)) finishableSuperRegion = false; // africa without a foot in north africa is never safe to finish if ((targetSR.Id == 4) && !state.FullMap.GetRegion(21).OwnedByPlayer(myName)) finishableSuperRegion = false; } // the second expansion target might be finishable while the first is not if ((!finishableSuperRegion) && (state.ExpansionTargets.Count > 2)) { targetSR = state.FullMap.GetSuperRegion(state.ExpansionTargets[1].Id); finishableSuperRegion = targetSR.IsFinishable(armiesLeft, myName); if (finishableSuperRegion) { // swap expansiontarget 1 with 0 SuperRegion tmp = state.ExpansionTargets[0]; state.ExpansionTargets[0] = state.ExpansionTargets[1]; state.ExpansionTargets[1] = tmp; } // it might be finishable but is it safe to finish without being a waste of armies? if (finishableSuperRegion) { if (!targetSR.IsSafeToFinish(state)) finishableSuperRegion = false; // africa without a foot in north africa is never safe to finish if ((targetSR.Id == 4) && !state.FullMap.GetRegion(21).OwnedByPlayer(myName)) finishableSuperRegion = false; } } return finishableSuperRegion; }
// is only called when no enemy is in sight public List<DeployArmies> ExpandTowardsEnemyWithFullStack(BotState state, int armiesLeft) { string myName = state.MyPlayerName; //string opponentName = state.OpponentPlayerName; List<DeployArmies> deployArmies = new List<DeployArmies>(); // determine the best region the opponent is most likely in if (state.OpponentStartRegions.Count > 0) { // find next step on shortest path to get there // and schedule an attack in that direction int nextstep = state.FullMap.FindNextStep(state.OpponentStartRegions[0].Id); if (nextstep != -1) { Region region = state.FullMap.GetRegion(nextstep); // find our neighbour with highest available armies foreach (Region a in region.Neighbors) { int aArmies = a.Armies + a.PledgedArmies - a.ReservedArmies; if (aArmies < 0) aArmies = 0; if (!a.OwnedByPlayer(myName)) aArmies = -1; a.tempSortValue = aArmies; } var lst = region.Neighbors.OrderByDescending(p => p.tempSortValue).ToList(); Region neigh = state.FullMap.GetRegion(lst[0].Id); if (neigh.OwnedByPlayer(myName)) { deployArmies.Add(new DeployArmies(myName, neigh, armiesLeft)); int armiesCount = neigh.Armies + neigh.PledgedArmies - neigh.ReservedArmies + armiesLeft - 1; state.ScheduleAttack(neigh, region, armiesLeft, armiesCount ); } } } return deployArmies; }
public List<DeployArmies> ExpandMinimum(BotState state, int armiesLeft) { string myName = state.MyPlayerName; string opponentName = state.OpponentPlayerName; List<DeployArmies> deployArmies = new List<DeployArmies>(); if (state.ExpansionTargets.Count == 0) return deployArmies; // check if opponent might be there (or neighboring) // map has 12 starting spots, 3 of them are ours, 3 estimated for opponent // each gets removed as they are seen for the first time // so we can estimate decently the // so only apply this filter between 9 and 6 if ( state.OpponentStartRegions.Count > 6 ) { for (int i = 0; i < 2; i++) { foreach (Region reg in state.OpponentStartRegions[i].Neighbors) { Region neigh = state.FullMap.GetRegion(reg.Id); if (neigh.SuperRegion.Id == state.ExpansionTargets[0].Id) { // don't do minimum expansion here if (state.ExpansionTargets.Count == 0) return deployArmies; } } } } // check if we can do minimum expansion on our best found expansion target bool doMinimumExpansion = true; foreach (Region reg in state.ExpansionTargets[0].SubRegions) { Region a = state.FullMap.GetRegion(reg.Id); // only try to expand if enemy is not on it if (a.OwnedByPlayer(opponentName)) doMinimumExpansion = false; // find best subregion to expand into int count = 0; // must be a neutral if (!a.OwnedByPlayer("neutral")) { reg.tempSortValue = -1; continue; } foreach (Region neig in a.Neighbors) { Region neigh = state.FullMap.GetRegion(neig.Id); // if neighbor is the enemy, we shouldnt be thinking of expansion, we want to keep our stack close to the enemy if (neigh.OwnedByPlayer(opponentName)) { count -= 5; } // the more neighbours belong to me the better if (neigh.OwnedByPlayer(myName)) { count += 5; } // if it has neutrals on the target superregion its good if (neigh.OwnedByPlayer("neutral") && (neigh.SuperRegion.Id == a.SuperRegion.Id)) count++; // if it has unknowns on the target superregion its even better (means we will be able to finish it faster) if (neigh.OwnedByPlayer("unknown") && (neigh.SuperRegion.Id == a.SuperRegion.Id)) count += 1; // if it has only has 1 army it costs less to take, so its better if (neigh.Armies == 1) count++; // boost if this territory can take this neighbour without deploying, at all int armyCount = a.Armies + a.PledgedArmies - a.ReservedArmies - neigh.Armies * 2; // the more armies we'll have left the better if (armyCount > 0) count += armyCount; } reg.tempSortValue = count; } if (doMinimumExpansion) { var lst = state.ExpansionTargets[0].SubRegions.OrderByDescending(p => p.tempSortValue).ToList(); Region target = state.FullMap.GetRegion(lst[0].Id); if (target.OwnedByPlayer("neutral")) { // find best region to attack from, must be my territory foreach (Region an in lst[0].Neighbors) { Region a = state.FullMap.GetRegion(an.Id); // if it's not our territory, don't bother if (!a.OwnedByPlayer(myName)) { an.tempSortValue = -1; continue; } // best attacker is the one with more armies available int armyCount = a.Armies + a.PledgedArmies - a.ReservedArmies; if (armyCount < 0) armyCount = 0; an.tempSortValue = armyCount; } var atk = lst[0].Neighbors.OrderByDescending(p => p.tempSortValue).ToList(); Region attacker = state.FullMap.GetRegion(atk[0].Id); if (attacker.OwnedByPlayer(myName)) { int deployed = state.ScheduleNeutralAttack(attacker, target, armiesLeft); deployArmies.Add(new DeployArmies(myName, attacker, deployed)); armiesLeft -= deployed; } else { Console.Error.WriteLine("something went wrong with minimum expansion, tried to attack from a territory which wasnt mine"); } } else { Console.Error.WriteLine("something went wrong with minimum expansion on round " + state.RoundNumber + " maybe because all options are bad?"); } } else { //todo: deploy all on biggest stack of main area and attack if predicting to win //todo: later: find a better expansiontarget (without enemy), or risk finishing this one } return deployArmies; }
// more aggressive expansion, used only when game is stalled to help move things along for minimal expansion and finishregion to complete it public List<DeployArmies> ExpandGameStalled(BotState state, int armiesLeft) { string myName = state.MyPlayerName; //string opponentName = state.OpponentPlayerName; List<DeployArmies> deployArmies = new List<DeployArmies>(); if (state.ExpansionTargets.Count == 0) return deployArmies; // expand on the main expansion target SuperRegion expansionTarget = state.FullMap.GetSuperRegion(state.ExpansionTargets[0].Id); foreach (Region reg in expansionTarget.SubRegions) { Region region = state.FullMap.GetRegion(reg.Id); // skip if you already own this region if (region.OwnedByPlayer(myName)) continue; // find our neighbour with highest available armies foreach (Region a in region.Neighbors) { int aArmies = a.Armies + a.PledgedArmies - a.ReservedArmies; if (!a.OwnedByPlayer(myName)) aArmies = -1; a.tempSortValue = aArmies; } var lst = region.Neighbors.OrderByDescending(p => p.tempSortValue).ToList(); Region neigh = state.FullMap.GetRegion(lst[0].Id); if (neigh.OwnedByPlayer(myName)) { int deployed = state.ScheduleNeutralAttack(neigh, region, armiesLeft); if (armiesLeft >= deployed) { deployArmies.Add(new DeployArmies(myName, neigh, deployed)); //neigh.PledgedArmies += deployed; armiesLeft -= deployed; } } // only do the expansion for the first neutral region found //break; } return deployArmies; }
public List<Region> GetPreferredStartingRegions(BotState state, long timeOut) { // order the superregions by lower number of armies reward // they are the fastest to complete // you get income advantage if you finish them early on var lst = state.FullMap.SuperRegions.OrderBy(p => p.ArmiesReward).ToList(); // order the list of possible starting picks by proximity to the best superregions foreach (Region a in state.PickableStartingRegions) { a.tempSortValue = 0; // check if region is neighboring a better superregion var na = a.Neighbors.OrderBy(p => lst.IndexOf(a.SuperRegion)).ToList(); int bestindex = lst.IndexOf(a.SuperRegion); int ni = lst.IndexOf(na[0].SuperRegion); if (ni < bestindex) { bestindex = ni; // better to have region inside than bordering // counters in this map are hard due to starting armies being 2 // if we had more starting armies this value should be +=1 a.tempSortValue -= 1; } a.tempSortValue += lst.Count - bestindex; // little bonus for australia if (a.SuperRegion.Id == 6) { a.tempSortValue += 2; } // little bonus for north africa if (a.Id == 21) a.tempSortValue++; // little bonus if it's bordering north africa foreach (Region nei in a.Neighbors) { if (nei.Id == 21) a.tempSortValue++; } } var picks = state.PickableStartingRegions.OrderByDescending(p => p.tempSortValue).ToList(); // assume opponent will also choose optimum picks // this will be useful later when we need to predict where opponent started foreach (Region reg in picks) { state.OpponentStartRegions.Add(reg); } return picks; }
public List<DeployArmies> GetDeployArmiesMoves(BotState state, long timeOut) { string myName = state.MyPlayerName; string opponentName = state.OpponentPlayerName; bool enemySighted = state.EnemySighted; List<DeployArmies> deployArmies = new List<DeployArmies>(); int armiesLeft = state.StartingArmies; // figure out if the best listed superregion is finishable on this turn bool finishableSuperRegion = FinishableSuperRegion(state); if (enemySighted) { // on first rounds, attack any enemies on sight, unless we can finish a super region or have already attacked neutrals if ((state.RoundNumber < 5) && !finishableSuperRegion && !state.AttackedOneNeutral) { // if we have atleast 2 enemy sightings, pick one and hit it hard List<DeployArmies> deploy = AttackHard(state, armiesLeft); foreach (DeployArmies da in deploy) { deployArmies.Add(da); armiesLeft -= da.Armies; } } // if we are SABased, northafrica is ours, enemy is in africa, NA is not finished and enemy is not in NA, Region northafrica = state.FullMap.GetRegion(21); SuperRegion africa = state.FullMap.GetSuperRegion(northafrica.SuperRegion.Id); SuperRegion na = state.FullMap.GetSuperRegion(1); if (state.SABased && northafrica.OwnedByPlayer(myName) && africa.HasPlayer(state.FullMap, opponentName) && !na.HasPlayer(state.FullMap, opponentName) && (na.OwnedByPlayer(state.FullMap) != myName) && state.ExpansionTargets.Count > 0) { // do minimum expansion (should be NA or australia) List<DeployArmies> deploy = ExpandMinimum(state, armiesLeft); foreach (DeployArmies da in deploy) { deployArmies.Add(da); armiesLeft -= da.Armies; } // deploy all else on northafrica deployArmies.Add(new DeployArmies(myName, northafrica, armiesLeft)); armiesLeft = 0; } // if game is stuck with high stacks expand to get more income if (armiesLeft > 0) { bool bigstack = false; foreach (Region reg in state.VisibleMap.Regions) { if (reg.OwnedByPlayer(myName)) { if (reg.Armies > 180) { bigstack = true; } } } if (bigstack) { List<DeployArmies> expand = ExpandGameStalled(state, armiesLeft); foreach (DeployArmies da in expand) { deployArmies.Add(da); armiesLeft -= da.Armies; } } } if (finishableSuperRegion) { List<DeployArmies> deploy = FinishSuperRegion(state, armiesLeft); foreach (DeployArmies da in deploy) { deployArmies.Add(da); armiesLeft -= da.Armies; } } else { // used to have full attack against spotted enemy super region here, but it's now being taken care of during the next phase // minimum expansion if ((armiesLeft > 0) && (state.ExpansionTargets.Count > 0)) { // do minimum expansion, but only on expansiontargets that are close to being finished // or we know the game has been stalled for a while (stacks bigger then, lets say 60) // or we have no superregion at all, so we really need one //todo: and we are fairly certain the target sr is not already being bordered by enemy // check how many territories of the expansion target we already own int count = 0; foreach (Region reg in state.ExpansionTargets[0].SubRegions) { Region rn = state.FullMap.GetRegion(reg.Id); if (rn.OwnedByPlayer(myName)) count++; } bool issafe = true; // check if there are any estimated opponentstartingregions in or bordering this superregion // only check for south america, australia and africa, rest can be considered safe by default if (state.ExpansionTargets[0].ArmiesReward <= 3) { foreach (Region border in state.OpponentStartRegions) { foreach (Region n in border.Neighbors) { Region b = state.FullMap.GetRegion(n.Id); if (b.SuperRegion.Id == state.ExpansionTargets[0].Id) issafe = false; } } } // check if the game is starting to have big stacks bool bigstack = false; foreach (Region reg in state.VisibleMap.Regions) { if (reg.OwnedByPlayer(myName)) { if (reg.Armies > 60) { bigstack = true; // todo: refactor code below to find best path without region hardcoded logic // check if we are building a bigstack in middle of africa without having eyes on SA and northafrica doesnt belong to the enemy //Region northafrica = state.FullMap.GetRegion(21); if ((reg.SuperRegion.Id == 4) && state.FullMap.GetRegion(12).OwnedByPlayer("unknown") && !northafrica.OwnedByPlayer(opponentName)) { int nextstep = -1; if (northafrica.OwnedByPlayer("neutral")) nextstep = 21; if (northafrica.OwnedByPlayer("unknown")) { // go through east africa nextstep = 23; Region ea = state.FullMap.GetRegion(23); // if ea is taken by enemy, rethink if (ea.OwnedByPlayer(opponentName)) { // if congo is neutral, go through congo if (state.FullMap.GetRegion(24).OwnedByPlayer("neutral")) { nextstep = 24; } // if congo is unknown then i guess we're stuck in madagascar and have to flank through south africa if (state.FullMap.GetRegion(24).OwnedByPlayer("unknown")) { nextstep = 25; } } } if (nextstep != -1) { Region region = state.FullMap.GetRegion(nextstep); // find our neighbour with highest available armies foreach (Region a in region.Neighbors) { int aArmies = a.Armies + a.PledgedArmies - a.ReservedArmies; if (aArmies < 0) aArmies = 0; if (!a.OwnedByPlayer(myName)) aArmies = -1; a.tempSortValue = aArmies; } var lst = region.Neighbors.OrderByDescending(p => p.tempSortValue).ToList(); Region neigh = state.FullMap.GetRegion(lst[0].Id); if (neigh.OwnedByPlayer(myName)) { deployArmies.Add(new DeployArmies(myName, neigh, armiesLeft)); state.ScheduleAttack(neigh, region, armiesLeft, neigh.Armies + armiesLeft - 1); armiesLeft = 0; } } } } } } // do minimum expansion only when we have a big stack and have safe superregions if ((armiesLeft > 0) && issafe) { if ((count > state.ExpansionTargets[0].SubRegions.Count * 0.5) || (bigstack) || (state.StartingArmies == 5)) { List<DeployArmies> deploy = ExpandMinimum(state, armiesLeft); foreach (DeployArmies da in deploy) { deployArmies.Add(da); armiesLeft -= da.Armies; } } } } } // deploy rest of your income bordering the enemy List<DeployArmies> placings = DeployBorderingEnemy(state, armiesLeft); foreach (DeployArmies da in placings) { deployArmies.Add(da); armiesLeft -= da.Armies; } } else { // no enemy in sight, expand normally / strategically if (finishableSuperRegion) { List<DeployArmies> deploy = FinishSuperRegion(state, armiesLeft); foreach (DeployArmies da in deploy) { deployArmies.Add(da); armiesLeft -= da.Armies; } } else { if (state.OZBased) { // if we are based in australia and have no territory in africa, europe or south america // we should be making our way into africa bool thereyet = false; foreach (Region reg in state.VisibleMap.Regions) { if ((reg.SuperRegion.Id == 2) || //SA (reg.SuperRegion.Id == 3) || //europe (reg.SuperRegion.Id == 4)) //africa { thereyet = true; } } if (!thereyet) { // we dont have egypt (22) but have middle east (36) if (!state.FullMap.GetRegion(22).OwnedByPlayer(myName) && state.FullMap.GetRegion(36).OwnedByPlayer(myName)) { deployArmies.Add(new DeployArmies(myName, state.FullMap.GetRegion(36), armiesLeft)); state.ScheduleAttack(state.FullMap.GetRegion(36), state.FullMap.GetRegion(22), armiesLeft, state.FullMap.GetRegion(36).Armies - 1 + armiesLeft); //state.scheduledAttack.Add(new Tuple<int, int, int>(36, 22, state.FullMap.GetRegion(36).Armies - 1 + armiesLeft)); armiesLeft = 0; } else // we dont have middle east (36) but have india (37) if (!state.FullMap.GetRegion(36).OwnedByPlayer(myName) && state.FullMap.GetRegion(37).OwnedByPlayer(myName)) { deployArmies.Add(new DeployArmies(myName, state.FullMap.GetRegion(37), armiesLeft)); state.ScheduleAttack(state.FullMap.GetRegion(37), state.FullMap.GetRegion(36), armiesLeft, state.FullMap.GetRegion(37).Armies - 1 + armiesLeft); //state.scheduledAttack.Add(new Tuple<int, int, int>(37, 36, state.FullMap.GetRegion(37).Armies - 1 + armiesLeft)); armiesLeft = 0; } else // we dont have india (37) but have siam (38) if (!state.FullMap.GetRegion(37).OwnedByPlayer(myName) && state.FullMap.GetRegion(38).OwnedByPlayer(myName)) { deployArmies.Add(new DeployArmies(myName, state.FullMap.GetRegion(38), armiesLeft)); state.ScheduleAttack(state.FullMap.GetRegion(38), state.FullMap.GetRegion(37), armiesLeft, state.FullMap.GetRegion(38).Armies - 1 + armiesLeft); //state.scheduledAttack.Add(new Tuple<int, int, int>(38, 37, state.FullMap.GetRegion(38).Armies - 1 + armiesLeft)); armiesLeft = 0; } else // we dont have siam (38) but have indonesia (39) if (!state.FullMap.GetRegion(38).OwnedByPlayer(myName) && state.FullMap.GetRegion(39).OwnedByPlayer(myName)) { deployArmies.Add(new DeployArmies(myName, state.FullMap.GetRegion(39), armiesLeft)); state.ScheduleAttack(state.FullMap.GetRegion(39), state.FullMap.GetRegion(38), armiesLeft, state.FullMap.GetRegion(39).Armies - 1 + armiesLeft); //state.scheduledAttack.Add(new Tuple<int, int, int>(39, 38, state.FullMap.GetRegion(39).Armies - 1 + armiesLeft)); armiesLeft = 0; } else { Console.Error.WriteLine("ozbased turn without any action?! (round " + state.RoundNumber + ")"); } } else { // lets go check north africa Region northafrica = state.FullMap.GetRegion(21); if (northafrica.OwnedByPlayer("unknown") || northafrica.OwnedByPlayer("neutral")) { int nextstep = state.FullMap.FindNextStep(12); if (nextstep != -1) { Region region = state.FullMap.GetRegion(nextstep); // find our neighbour with highest available armies foreach (Region a in region.Neighbors) { int aArmies = a.Armies + a.PledgedArmies - a.ReservedArmies; if (aArmies < 0) aArmies = 0; if (!a.OwnedByPlayer(myName)) aArmies = -1; a.tempSortValue = aArmies; } var lst = region.Neighbors.OrderByDescending(p => p.tempSortValue).ToList(); Region neigh = state.FullMap.GetRegion(lst[0].Id); if (neigh.OwnedByPlayer(myName)) { deployArmies.Add(new DeployArmies(myName, neigh, armiesLeft)); state.ScheduleAttack(neigh, region, armiesLeft, neigh.Armies + armiesLeft - 1); armiesLeft = 0; } } } //todo: we have foot in africa/europe/south america but enemy could be expanding safely on north america... //todo: how can we be sure we are not wasting armies going to alaska? need to track historic of enemy deployments... } } if (state.AfricaBased) { // if africaBased and not saBased, go hard into brazil if (!state.SABased) { Region rnn = state.FullMap.GetRegion(21); // deploy all on north africa Region r = state.FullMap.GetRegion(12); // attack brazil state.ScheduleFullAttack(rnn, r, armiesLeft); deployArmies.Add(new DeployArmies(myName, rnn, armiesLeft)); armiesLeft = 0; } else if (state.FullMap.GetRegion(12).OwnedByPlayer(myName)) { // we have brazil and no enemy bordering, so we can predict enemy is coming at us from oz // so go hard into middle east Region rnn = state.FullMap.GetRegion(22); // deploy all on egypt Region r = state.FullMap.GetRegion(36); // attack middle east if (!r.OwnedByPlayer(myName)) { state.ScheduleFullAttack(rnn, r, armiesLeft); deployArmies.Add(new DeployArmies(myName, rnn, armiesLeft)); armiesLeft = 0; } } } // if saBased and no enemy on north africa, go hard into north africa from brazil if (state.SABased && !state.FullMap.GetRegion(21).OwnedByPlayer(opponentName)) { Region rnn = state.FullMap.GetRegion(12); // deploy all on brazil Region r = state.FullMap.GetRegion(21); // attack north africa if (!r.OwnedByPlayer(myName)) { state.ScheduleFullAttack(rnn, r, armiesLeft); deployArmies.Add(new DeployArmies(myName, rnn, armiesLeft)); armiesLeft = 0; } } if (state.SABased && state.FullMap.GetRegion(21).OwnedByPlayer(myName) && !state.NABased) { // do minimum expansion into north america // expansion target 0 should already be pointing at it if (armiesLeft > 0) { List<DeployArmies> deploy = ExpandMinimum(state, armiesLeft); foreach (DeployArmies da in deploy) { deployArmies.Add(da); armiesLeft -= da.Armies; } } // deploy rest on North Africa if (armiesLeft > 0) { deployArmies.Add(new DeployArmies(myName, state.FullMap.GetRegion(21), armiesLeft)); armiesLeft = 0; } } } // if no priority predictions occurs if (armiesLeft > 0) { List<DeployArmies> expand = ExpandTowardsEnemyWithFullStack(state, armiesLeft); foreach (DeployArmies da in expand) { deployArmies.Add(da); armiesLeft -= da.Armies; } } } return deployArmies; }
public BotParser(Bot bot) { this.bot = bot; CurrentState = new BotState(); }
public BotParser(Bot bot) { this.bot = bot; this.currentState = new BotState(); }