1
1
forked from 0ad/0ad

Removes references to Testbot, replaces with JuBot.

This was SVN commit r9378.
This commit is contained in:
James Baillie 2011-05-01 15:02:33 +00:00
parent 88c44c7d73
commit 02ed11ac54
4 changed files with 171 additions and 25 deletions

View File

@ -1,5 +1,5 @@
{
"name": "JuBot",
"description": "Jubal's improved version of the 0AD TestBot.",
"constructor": "TestBotAI"
"constructor": "JuBotAI"
}

View File

@ -0,0 +1,146 @@
/*
* This is a primitive initial attempt at an AI player.
* The design isn't great and maybe the whole thing should be rewritten -
* the aim here is just to have something that basically works, and to
* learn more about what's really needed for a decent AI design.
*
* The basic idea is we have a collection of independent modules
* (EconomyManager, etc) which produce a list of plans.
* The modules are mostly stateless - each turn they look at the current
* world state, and produce some plans that will improve the state.
* E.g. if there's too few worker units, they'll do a plan to train
* another one. Plans are discarded after the current turn, if they
* haven't been executed.
*
* Plans are grouped into a small number of PlanGroups, and for each
* group we try to execute the highest-priority plans.
* If some plan groups need more resources to execute their highest-priority
* plan, we'll distribute any unallocated resources to that group's
* escrow account. Eventually they'll accumulate enough to afford their plan.
* (The purpose is to ensure resources are shared fairly between all the
* plan groups - none of them should be starved even if they're trying to
* execute a really expensive plan.)
*/
/*
* Lots of things we should fix:
*
* * Find entities with no assigned role, and give them something to do
* * Keep some units back for defence
* * Consistent terminology (type vs template etc)
* * ...
*
*/
function JuBotAI(settings)
{
// warn("Constructing JuBotAI for player "+settings.player);
BaseAI.call(this, settings);
this.turn = 0;
this.modules = [
new EconomyManager(),
new MilitaryAttackManager(),
];
this.planGroups = {
economyPersonnel: new PlanGroup(),
economyConstruction: new PlanGroup(),
militaryPersonnel: new PlanGroup(),
};
}
JuBotAI.prototype = new BaseAI();
JuBotAI.prototype.ShareResources = function(remainingResources, unaffordablePlans)
{
// Share our remaining resources among the plangroups that need
// to accumulate more resources, in proportion to their priorities
for each (var type in remainingResources.types)
{
// Skip resource types where we don't have any spare
if (remainingResources[type] <= 0)
continue;
// Find the plans that require some of this resource type,
// and the sum of their priorities
var ps = [];
var sumPriority = 0;
for each (var p in unaffordablePlans)
{
if (p.plan.getCost()[type] > p.group.getEscrow()[type])
{
ps.push(p);
sumPriority += p.priority;
}
}
// Avoid divisions-by-zero
if (!sumPriority)
continue;
// Share resources by priority, clamped to the amount the plan actually needs
for each (var p in ps)
{
var amount = Math.floor(remainingResources[type] * p.priority / sumPriority);
var max = p.plan.getCost()[type] - p.group.getEscrow()[type];
p.group.getEscrow()[type] += Math.min(max, amount);
}
}
};
JuBotAI.prototype.OnUpdate = function()
{
// Run the update every n turns, offset depending on player ID to balance the load
if ((this.turn + this.player) % 4 == 0)
{
var gameState = new GameState(this);
// Find the resources we have this turn that haven't already
// been allocated to an escrow account.
// (We need to do this before executing any plans, because those will
// distort the escrow figures.)
var remainingResources = gameState.getResources();
for each (var planGroup in this.planGroups)
remainingResources.subtract(planGroup.getEscrow());
Engine.ProfileStart("plan setup");
// Compute plans from each module
for each (var module in this.modules)
module.update(gameState, this.planGroups);
// print(uneval(this.planGroups)+"\n");
Engine.ProfileStop();
Engine.ProfileStart("plan execute");
// Execute as many plans as possible, and keep a record of
// which ones we can't afford yet
var unaffordablePlans = [];
for each (var planGroup in this.planGroups)
{
var plan = planGroup.executePlans(gameState);
if (plan)
unaffordablePlans.push({"group": planGroup, "priority": plan.priority, "plan": plan.plan});
}
Engine.ProfileStop();
this.ShareResources(remainingResources, unaffordablePlans);
// print(uneval(this.planGroups)+"\n");
// Reset the temporary plan data
for each (var planGroup in this.planGroups)
planGroup.resetPlans();
}
if (this.turn == 0){
this.chat("Good morning. Please prepare for annhilation. Jubal apologises for any inconvenience likely to be caused by your imminent demise.");
}
this.turn++;
};

View File

@ -298,7 +298,7 @@ var MilitaryAttackManager = Class({
trainDefenderSquad: function(gameState, planGroups)
{
var pendingdefense = gameState.getOwnEntitiesWithRole("defenders");
//TestBotAI.prototype.chat("Number of defenders is" + pendingdefense.length);
//JuBotAI.prototype.chat("Number of defenders is" + pendingdefense.length);
if (pendingdefense.length < this.defsquadmin && gameState.displayCiv() == "iber"){
planGroups.economyPersonnel.addPlan(122,
new UnitTrainingPlan(gameState,
@ -310,21 +310,21 @@ var MilitaryAttackManager = Class({
new UnitTrainingPlan(gameState,
"units/{civ}_infantry_spearman_b", 3, { "role": "defenders" })
);
//TestBotAI.prototype.chat("Training defenders");
//JuBotAI.prototype.chat("Training defenders");
}
else if (pendingdefense.length < this.defsquad && gameState.displayCiv() == "iber"){
planGroups.economyPersonnel.addPlan(110,
new UnitTrainingPlan(gameState,
"units/{civ}_infantry_swordsman_b", 3, { "role": "defenders" })
);
//TestBotAI.prototype.chat("Training defenders");
//JuBotAI.prototype.chat("Training defenders");
}
else if (pendingdefense.length < this.defsquad){
planGroups.economyPersonnel.addPlan(110,
new UnitTrainingPlan(gameState,
"units/{civ}_infantry_spearman_b", 3, { "role": "defenders" })
);
//TestBotAI.prototype.chat("Training defenders");
//JuBotAI.prototype.chat("Training defenders");
}
},
@ -648,25 +648,25 @@ var MilitaryAttackManager = Class({
if (whatnext > 0.85){
this.killstrat = 0;
// Regular "train a few guys and go kill stuff" type attack.
//TestBotAI.prototype.chat("Regular attack (" + gameState.displayCiv() + ")");
//TestBotAI.prototype.chat(whatnext);
//JuBotAI.prototype.chat("Regular attack (" + gameState.displayCiv() + ")");
//JuBotAI.prototype.chat(whatnext);
}
else if (whatnext > 0.55) {
this.killstrat = 2;
//TestBotAI.prototype.chat("Cavalry raid (" + gameState.displayCiv() + ")");
//TestBotAI.prototype.chat(whatnext);
//JuBotAI.prototype.chat("Cavalry raid (" + gameState.displayCiv() + ")");
//JuBotAI.prototype.chat(whatnext);
// Cavalry raid
}
else if (whatnext > 0.2) {
this.killstrat = 3;
//TestBotAI.prototype.chat("3 pronged assault (" + gameState.displayCiv() + ")");
//TestBotAI.prototype.chat(whatnext);
//JuBotAI.prototype.chat("3 pronged assault (" + gameState.displayCiv() + ")");
//JuBotAI.prototype.chat(whatnext);
// 3 prong
}
else {
this.killstrat = 1;
//TestBotAI.prototype.chat("Full assault (" + gameState.displayCiv() + ")");
//TestBotAI.prototype.chat(whatnext);
//JuBotAI.prototype.chat("Full assault (" + gameState.displayCiv() + ")");
//JuBotAI.prototype.chat(whatnext);
//Full Assault!
}
}
@ -732,25 +732,25 @@ var MilitaryAttackManager = Class({
if (whatnext > 0.8){
this.killstrat = 0;
// Regular "train a few guys and go kill stuff" type attack.
//TestBotAI.prototype.chat("Regular attack (" + gameState.displayCiv() + ")");
//TestBotAI.prototype.chat(whatnext);
//JuBotAI.prototype.chat("Regular attack (" + gameState.displayCiv() + ")");
//JuBotAI.prototype.chat(whatnext);
}
else if (whatnext > 0.5) {
this.killstrat = 2;
//TestBotAI.prototype.chat("Cavalry raid (" + gameState.displayCiv() + ")");
//TestBotAI.prototype.chat(whatnext);
//JuBotAI.prototype.chat("Cavalry raid (" + gameState.displayCiv() + ")");
//JuBotAI.prototype.chat(whatnext);
// Cavalry raid
}
else if (whatnext > 0.3) {
this.killstrat = 3;
//TestBotAI.prototype.chat("3 pronged assault (" + gameState.displayCiv() + ")");
//TestBotAI.prototype.chat(whatnext);
//JuBotAI.prototype.chat("3 pronged assault (" + gameState.displayCiv() + ")");
//JuBotAI.prototype.chat(whatnext);
// 3 prong
}
else {
this.killstrat = 1;
//TestBotAI.prototype.chat("Full assault (" + gameState.displayCiv() + ")");
//TestBotAI.prototype.chat(whatnext);
//JuBotAI.prototype.chat("Full assault (" + gameState.displayCiv() + ")");
//JuBotAI.prototype.chat(whatnext);
//Full Assault!
}
}

View File

@ -191,14 +191,14 @@ var BuildingConstructionPlan = Class({
{
var v = friendlyTiles[i];
var foe = enemyTiles[i];
//TestBotAI.prototype.chat(v);
//TestBotAI.prototype.chat(i);
//TestBotAI.prototype.chat(foe);
//JuBotAI.prototype.chat(v);
//JuBotAI.prototype.chat(i);
//JuBotAI.prototype.chat(foe);
if (v >= bestVal)
{
bestVal = v;
bestIdx = i;
//TestBotAI.prototype.chat("BestVal is " + bestVal + ", and bestIdx is " + bestIdx + ".");
//JuBotAI.prototype.chat("BestVal is " + bestVal + ", and bestIdx is " + bestIdx + ".");
}
}
}