WeSwarm-C ++ 11 [v2.2]
截至2015年8月25日更新至v2.2。
v2.2-由于控制器报告军队的方式发生变化而进行了调整。
v2.1-TNT无法编译我的代码,因此我停止使用 stoi
。
v2.0-代码重构以及一些错误修复。
欢迎来到蜂群。我们的优势在于数量。我们永恒的意志是收集您所有的奖金,以使我们的产卵最大化。不要妨碍我们,以免您不知所措。不要试图打败我们,每杀死一个人,它就会占据三席。您可能会强迫我们做出牺牲,但绝不会强迫我们投降!
#include <cstdlib>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <set>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;
/// http://stackoverflow.com/questions/236129/split-a-string-in-c
vector<string> &split(const string &s, char delim, vector<string> &elems)
{
stringstream ss(s);
string item;
while (getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
/// http://stackoverflow.com/questions/236129/split-a-string-in-c
vector<string> split(const string &s, char delim)
{
vector<string> elems;
split(s, delim, elems);
return elems;
}
enum Allegiance { MINE, ENEMY, HOSTILE, NPC, ANY };
class Bonus
{
public:
Bonus(int id, int armies, int territoriesLeft)
{
this->id = id;
this->armies = armies;
this->territoriesLeft = territoriesLeft;
}
int getId()
{
return id;
}
int getArmies()
{
return armies;
}
int getTerritoriesLeft()
{
return territoriesLeft;
}
private:
/// id of the bonus.
int id;
/// number of extra armies that this bonus gives.
int armies;
/// number of territories in the bonus that still needs to be captured.
int territoriesLeft;
};
class Territory
{
public:
Territory(int row, int col, Bonus* bonus, int playerId, int armies, Allegiance allegiance)
{
this->row = row;
this->col = col;
this->bonus = bonus;
this->armies = armies;
this->allegiance = allegiance;
this->toAdd = 0;
this->toRemove = 0;
}
Territory(Territory *territory)
{
this->row = territory->getRow();
this->col = territory->getCol();
this->bonus = territory->getBonusPtr();
this->armies = territory->getArmies();
this->allegiance = ANY;
this->toAdd = 0;
this->toRemove = 0;
}
/// Ensures uniqueness
bool operator<(const Territory& other) const
{
return row < other.row && + col < other.col;
}
/// Return the minimum number of armies needed to conquer this territory.
int conquerNeeded()
{
/*
Say a player sends n armies from his/her territory to an opposing territory with o armies in it.
o will decrease by n * .6 rounded to the nearest integer;
however, at the same time, n will decrease by o * .7 rounded to the nearest integer.
The following rules dealing with whether or not the opposing territory has been captured will apply:
If o reaches zero AND n is greater than 0, the player will take over the territory, which will have n armies in it.
If both n and o become zero, o will automatically be set to 1 and the territory will not be captured.
If o remains greater than 0, the number of armies in the player's territory will increase by n and the opposing territory will not be captured.
*/
int o = this->armies; // Given o.
int n; // Solve for n.
int n1;
int n2;
if (this->allegiance != NPC) {
o = o + 5; // To account for potential reinforcement.
}
// resulto = o - 0.6n
// resultn = n - 0.7o
//
// We want a result of o = 0 and n = 1.
// 0 = o - 0.6n
// 1 = n - 0.7o
//
// Isolate n
// 0.6n = o
// n = o / 0.6
n1 = (int)ceil(o / 0.6);
// 0.7o = n - 1
// 0.7o + 1 = n
n2 = (int)ceil(0.7 * o + 1);
// Take the bigger of the two to guarantee o <= 0 and n >= 1
n = max(n1, n2);
return n;
}
/// Returns the minimum number of armies that must be added to this territory
/// to ensure that the territory cannot be taken over by an attack with n armies.
int reinforceNeeded(int n)
{
int o = this->armies; // Number of armies we already have.
int add = 0; // Solve for number of armies we need to add.
// resulto = o - 0.6n
// resultn = n - 0.7o
//
// We want a result of o = 1 at the very least.
// 1 = o - 0.6n
// 1 + 0.6n = o
int needed = (int)ceil(1 + 0.6 * n);
// We only need to reinforce if we don't have enough.
if (o < needed) {
add = needed - o;
}
return add;
}
void add(int toAdd)
{
if (toAdd > 0) {
this->toAdd = this->toAdd + toAdd;
}
}
void remove(int toRemove)
{
if (toRemove > 0) {
this->toRemove = this->toRemove + toRemove;
}
}
void deploy()
{
this->armies = this->armies + this->toAdd - this->toRemove;
this->toAdd = 0;
this->toRemove = 0;
}
int getRow()
{
return row;
}
int getCol()
{
return col;
}
int getArmies()
{
return armies;
}
int getAvaliableArmies()
{
return armies - 1 - toRemove;
}
int getToAdd()
{
return toAdd;
}
bool isToBeDefended()
{
return toAdd > 0;
}
Bonus getBonus()
{
if (bonus != nullptr) {
return *bonus;
}
return Bonus(-1, 1, 100);
}
Bonus *getBonusPtr()
{
return bonus;
}
bool isMine()
{
return allegiance == MINE;
}
bool isNPC()
{
return allegiance == NPC;
}
private:
/// Row number of this territory.
int row;
/// Column number of this territory.
int col;
/// The bonus that this territory is a part of.
Bonus* bonus;
/// number of armies contained in the territory.
int armies;
/// number of armies to add or send to the territory.
int toAdd;
/// number of armies to remove from this territory.
int toRemove;
/// Who this territory belongs to.
Allegiance allegiance;
};
/// Return whether Territory a is a neighbour of Territory b.
bool isNeighbour(Territory *a, Territory *b)
{
/*
n n n
n x n
n n n
*/
// A neighbouring territory is where either:
// row - 1 , col - 1
// row - 1 , col + 0
// row - 1 , col + 1
// row + 0 , col - 1
// row + 0 , col + 1
// row + 1 , col - 1
// row + 1 , col + 0
// row + 1 , col + 1
int rowA = a->getRow();
int colA = a->getCol();
int rowB = b->getRow();
int colB = b->getCol();
// The row and column is the same, so they're the same territory, but not neighbours.
if (rowA == rowB && colA == colB) {
return false;
}
// The difference of row : row and column : column is no more than 1.
// e.g. a territory at row 7 will have neighbour at row 6 and 8.
if (abs(rowA - rowB) <= 1 && abs(colA - colB) <= 1) {
return true;
}
// Special case for wrapping.
int checkRow = -1;
int checkCol = -1;
// Row is at 0. We need to check for 9 and 1.
// 1 is already covered by 0 - 1. Explicitly check the 0 - 9 case.
if (rowB == 0) {
checkRow = 9;
}
// Row is at 9. We need to check for 0 and 8.
// 8 is already covered by 9 - 9. Explicitly check the 9 - 0 case;
if (rowB == 9) {
checkRow = 0;
}
// Same thing for column
if (colB == 0) {
checkCol = 9;
}
if (colB == 9) {
checkCol = 0;
}
if ((rowA == checkRow && abs(colA - colB) <= 1) ||
(abs(rowA - rowB) <= 1 && colA == checkCol) ||
(rowA == checkRow && colA == checkCol)) {
return true;
}
return false;
}
/// Verify that territory has the correct allegiance.
bool isOfAllegiance(Territory *territory, Allegiance allegiance)
{
if (allegiance == MINE && territory->isMine()) {
return true;
}
else if (allegiance == ENEMY && !territory->isMine()) {
// Enemy means NOT mine, which includes NPCs.
return true;
}
else if (allegiance == HOSTILE && !territory->isMine() && !territory->isNPC()) {
// Specifically enemy PLAYERS.
return true;
}
else if (allegiance == NPC && territory->isNPC()) {
return true;
}
else if (allegiance == ANY) {
return true;
}
return false;
}
/// Return all neighbouring territories of a particular territory,
/// where the neighbouring territories fits the given allegiance.
set<Territory *> getNeighbours(Territory *territory, Allegiance allegiance, set<Territory *> territories)
{
set<Territory *> neighbours;
for (Territory *neighbour : territories) {
if (isNeighbour(neighbour, territory) && isOfAllegiance(neighbour, allegiance)) {
neighbours.insert(neighbour);
}
}
return neighbours;
}
/// Return the total number of armies near a particular territory that can be mobilized.
int getAvaliableArmiesNear(Territory *territory, Allegiance allegiance, set<Territory *> territories)
{
int armies = 0;
set<Territory *> neighbour = getNeighbours(territory, allegiance, territories);
for (Territory *near : neighbour) {
armies = armies + near->getAvaliableArmies();
}
return armies;
}
/// Return a set of all territories of a particular allegiance.
set<Territory *> getAllTerritories(Allegiance allegiance, set<Territory *> territories)
{
set<Territory *> t;
for (Territory *territory : territories) {
if (isOfAllegiance(territory, allegiance)) {
t.insert(territory);
}
}
return t;
}
/// Returns the priority of attacking this particular territory.
/// The lower the priority, the better. It is calculated based on
/// the number of territories left to claim a bonus, the number
/// of armies required to take it over, and the number of armies
/// getting this bonus will give us.
int calculateAttackPriority(Territory *territory)
{
Bonus bonus = territory->getBonus();
int territoriesLeft = bonus.getTerritoriesLeft();
int armiesNeeded = territory->conquerNeeded();
int armiesGiven = bonus.getArmies();
return (int)round(territoriesLeft * armiesNeeded / armiesGiven);
}
/// Return a map of int, Territories where int represent priority
/// and Territory is the territory to be attacked.
///
/// Higher priority = LESS important.
///
/// ALL territories that can be attacked will appear in the set.
map<int, Territory *> getAttackCandidates(set<Territory *> territories)
{
map<int, Territory *> attack;
set<Territory *> opponents = getAllTerritories(ENEMY, territories);
for (Territory *territory : opponents) {
int priority = calculateAttackPriority(territory);
// Check if the territory is already inserted.
auto findTerritory = attack.find(priority);
bool inserted = findTerritory != attack.end();
// Already inserted, so we decrease the priority until we can insert it.
while (inserted) {
priority = priority + 1;
findTerritory = attack.find(priority);
inserted = findTerritory != attack.end();
}
attack.insert({ priority, territory });
}
return attack;
}
/// Returns the priority of defending this particular territory.
/// The lower the priority, the better. It is calculated based on
/// whether or not we have this bonus, number of armies that can
/// potentially take it over, and the number of armies
/// getting this bonus will give us.
int calculateDefendPriority(Territory *territory, set<Territory *> territories)
{
Bonus bonus = territory->getBonus();
set<Territory *> enemies = getNeighbours(territory, ENEMY, territories);
int territoriesLeft = bonus.getTerritoriesLeft();
int armiesNeeded = territory->reinforceNeeded(getAvaliableArmiesNear(territory, HOSTILE, territories));
int armiesGiven = bonus.getArmies();
return (int)round((1 + territoriesLeft) * armiesNeeded / armiesGiven);
}
/// Return a map of int, pair<int, Territory> where int represent priority
/// and Territory is the territory to be defended.
///
/// Again, the higher the priority, the LESS important it is.
///
/// ALL territories that can be defended will appear in the set.
map<int, Territory *> getDefendCandidates(set<Territory *> territories)
{
map<int, Territory *> defend;
set<Territory *> mine = getAllTerritories(MINE, territories);
for (Territory *territory : mine) {
int priority = calculateDefendPriority(territory, territories);
// Check if the territory is already inserted.
auto findTerritory = defend.find(priority);
bool inserted = findTerritory != defend.end();
// Already inserted, so we decrease the priority until we can insert it.
while (inserted) {
priority = priority + 1;
findTerritory = defend.find(priority);
inserted = findTerritory != defend.end();
}
defend.insert({ priority, territory });
}
return defend;
}
/// Determine which territories to add armies to, and add to them accordingly.
/// Return a set which specifically lists the Territories that will have armies
/// added to them.
///
/// set<Territory> territories is a set of territories that are visible to us.
/// int armies is the number of armies we can add.
set<Territory *> getAdd(set<Territory *> territories, int armies)
{
set<Territory *> add;
// First we check whether there are any territories worth defending - i.e. we have bonus.
map<int, Territory *> defend = getDefendCandidates(territories);
for (auto pairs : defend) {
if (armies <= 0) {
break;
}
Territory *territory = pairs.second;
Bonus bonus = territory->getBonus();
int need = territory->reinforceNeeded(getAvaliableArmiesNear(territory, HOSTILE, territories));
// Make sure that we actually need to defend this, and it actually can be defended.
if (need > 0 && need <= armies + getAvaliableArmiesNear(territory, MINE, territories) + territory->getArmies()) {
if (need < armies) {
armies = armies - need;
territory->add(need);
add.insert(territory);
}
else {
// Do we really want to use up all our armies
// if it doen't even give us a bonus?
if (bonus.getTerritoriesLeft() != 0) {
continue;
}
territory->add(armies);
armies = 0;
add.insert(territory);
}
}
}
// Attacking is much easier. We simply allocate all the armies
// to a place beside where we wish to attack.
map<int, Territory *> attack = getAttackCandidates(territories);
for (auto pairs : attack) {
if (armies <= 0) {
break;
}
Territory *territory = pairs.second;
// Determine where to allocate.
set<Territory *> neighbours = getNeighbours(territory, MINE, territories);
// We'll just arbitrarily pick the first one that is an ally, though any one will work.
for (Territory *my : neighbours) {
// I am almost certain I messed up my logic somewhere around here.
// I'm supposed to initiate an attack if I got a good surround near a territory.
// However, it isn't working so I removed it and opted for a simpler logic.
// So far, it is doing well as is. If I start loosing I'll reimplement this ;)
//int need = territory->conquerNeeded() - getAvaliableArmiesNear(territory, MINE, territories);
int need = territory->conquerNeeded();
int a = territory->conquerNeeded();
int b = getAvaliableArmiesNear(territory, MINE, territories);
int c = my->getAvaliableArmies();
if (need <= 0) {
continue;
}
if (need < armies) {
armies = armies - need;
my->add(need);
add.insert(my);
}
else {
my->add(armies);
armies = 0;
add.insert(my);
}
break;
}
}
// Check if there are any armies left over,
// because we must add all our armies.
if (armies > 0) {
// This means that we are in a perfect position and it doesn't matter where we add it.
// So we'll just pick a random territory and put it there.
if (add.size() < 1) {
set<Territory *> mine = getAllTerritories(MINE, territories);
auto first = mine.begin();
Territory *random = *first;
random->add(armies);
add.insert(random);
}
else {
// In this case, we just throw it to the highest priority.
auto first = add.begin();
Territory *t = *first;
t->add(armies);
}
}
return add;
}
/// Return a set of set of Territories.
/// Each set have [0] as source and [1] as destination.
/// Number of armies to send will be in destination.
/// add is a list of territories with armies added to them.
set<pair<Territory *, Territory *>> getSend(set<Territory *> territories)
{
set<pair<Territory *, Territory *>> send;
// Attacking is much easier. We simply allocate all the armies
// to a place beside where we wish to attack.
map<int, Territory *> attack = getAttackCandidates(territories);
for (auto pairs : attack) {
Territory *territory = pairs.second;
int needed = territory->conquerNeeded();
set<Territory *> mine = getNeighbours(territory, MINE, territories);
// Find all our territories avaliable for attack.
for (Territory *my : mine) {
// We need to make sure we actually have enough!
int avaliable = my->getAvaliableArmies();
// We send all our attacking army from a single territory,
// So this one territory must have enough.
if (needed > 0 && avaliable >= needed) {
// Attack!
territory->add(needed); // represents number of armies to send.
my->remove(needed);
pair<Territory *, Territory *> attackOrder(my, territory); // src -> dst.
send.insert(attackOrder);
break;
}
}
}
// First we check whether there are any territories worth defending - i.e. we have bonus.
map<int, Territory *> defend = getDefendCandidates(territories);
for (auto pairs : defend) {
Territory *territory = pairs.second;
// Number of armies that will potentially attack.
int threat = getAvaliableArmiesNear(territory, HOSTILE, territories);
// The number of armies needed to reinforce this attack.
int needed = territory->reinforceNeeded(threat);
if (needed <= 0) {
continue;
}
// Check that we have enough to actually defend.
int avaliable = getAvaliableArmiesNear(territory, MINE, territories);
set<Territory *> neighbours = getNeighbours(territory, MINE, territories);
if (avaliable < needed) {
// Not enough, retreat!
for (Territory *my : neighbours) {
int retreat = territory->getAvaliableArmies();
if (retreat > 0) {
// Retreat!
// Remove from the territory in defense candidate.
territory->remove(retreat);
// Add to territory else where.
my->add(retreat);
pair<Territory *, Territory *> defendOrder(territory, my); // src -> dst.
send.insert(defendOrder);
}
// We retreat to a single territory.
// So we break as soon as we find a territory.
// If there is no territory, this loop won't run.
break;
}
}
else {
// Track how many we still need to add.
int stillneed = needed;
// Reinforce!
for (Territory *my : neighbours) {
// Do we need more?
if (stillneed <= 0) {
break;
}
// Check that it's not about to be reinforced.
// Otherwise, it is senseless to take armies away from a
// territory we intend to defend!
if (!my->isToBeDefended()) {
int canSend = my->getAvaliableArmies();
if (canSend > 0) {
// Reinforce!
// We create a copy of the territory when adding.
// Why? Because in this case, the destination Territory
// is only meant as a place holder territory simply
// for the purpose of having the toAdd value read.
Territory *territoryAdd = new Territory(territory);
territoryAdd->add(canSend);
// Remove from the territory we are sending from.
my->remove(canSend);
stillneed = stillneed - canSend;
pair<Territory *, Territory *> defendOrder(my, territoryAdd); // src -> dst.
send.insert(defendOrder);
}
}
}
}
}
return send;
}
/// Rules of Engagement:
/// 1. Collect Bonuses.
/// 2. Attack Weak Territories whenever possible.
///
/// Rules of Defense:
/// 1. Reinforce if possible.
/// 2. Otherwise, retreat and live to fight another day
///
/// For a given territory, we will prioritze attacking over
/// defending if we do not have the bonus yet for that territory.
/// If we have the bonus, we will prioritze defending over attacking.
int main(int argc, char* argv[])
{
// Note: cannot use stoi because of compilation problems.
int id = atoi(argv[1]);
int armies = atoi(argv[2]);
string territoriesIn = argv[3];
string bonusesIn = argv[4];
// First seperate by space, then seperate by comma.
vector<string> territoriesData = split(territoriesIn, ' ');
vector<string> bonusesData = split(bonusesIn, ' ');
set<Territory *> territories;
map<int, Bonus *> bonuses;
for (string data : bonusesData) {
// [id],[armies],[territories left]
vector<string> bonus = split(data, ',');
int id = atoi(bonus[0].c_str());
int armies = atoi(bonus[1].c_str());
int territoriesLeft = atoi(bonus[2].c_str());
Bonus *b = new Bonus(id, armies, territoriesLeft);
bonuses.insert({ id, b });
}
for (string data : territoriesData) {
// [row],[col],[bonus id],[player id],[armies]
vector<string> territory = split(data, ',');
int row = atoi(territory[0].c_str());
int col = atoi(territory[1].c_str());
int bonusId = atoi(territory[2].c_str());
int playerId = atoi(territory[3].c_str());
int armies = atoi(territory[4].c_str());
// We can assume that each territory always belongs to a bonus.
auto findBonus = bonuses.find(bonusId);
Bonus *bonus;
if (findBonus != bonuses.end()) {
bonus = findBonus->second;
}
else {
bonus = nullptr;
}
Allegiance allegiance = ENEMY;
if (playerId == id) {
allegiance = MINE;
}
else if (playerId == -1) {
allegiance = NPC;
}
Territory *t = new Territory(row, col, bonus, playerId, armies, allegiance);
territories.insert(t);
}
// Here we output our desire to add armies.
set<Territory *> add = getAdd(territories, armies);
string delimiter = "";
for (Territory *t : add) {
cout << delimiter << t->getRow() << "," << t->getCol() << "," << t->getToAdd();
delimiter = " ";
// Move added army to actual army.
t->deploy();
}
cout << endl;
// Here we output our desire to send armies.
set<pair<Territory *, Territory *>> send = getSend(territories);
delimiter = "";
// Note that if you do not want to move any armies, your program should print a space.
if (send.size() == 0) {
cout << " ";
}
else {
for (auto location : send) {
Territory *source = location.first;
Territory *destination = location.second;
cout << delimiter << source->getRow() << "," << source->getCol() << "," << destination->getRow() << "," << destination->getCol() << "," << destination->getToAdd();
delimiter = " ";
}
}
cout << endl;
return 0;
}
GIF动画
已封存:
v2.1:https://drive.google.com/uc ? export = download & id = 0B-BtKdd4FDDEU3lkNzVoTUpRTG8
v1.0:https://drive.google.com/uc? export = download & id = 0B- BtKdd4FDDEVzZUUlFydXo2T00