Not sure if the new AI is worth calling itself NullBot v3.0. Maybe "ZeroBot"? (:
Now it should not only play well, but also be easy to understand. I will provide a download and a git repository as soon as anything is ready; right now it already plays, but hardly better than Semperfi-JS.
Now NullBot shouldn't contain tons of dead code coming from ideas that proved ineffective. The code is now split into many small files, that should be relatively intuitive to navigate. Also, i'd like to make it 3.2-only, so that to be able to use some fancy functions without too much thinking.
Out of what is done, probably the thing i like the most is personality definition. It should be much more flexible (defining personalities for third-party mods should no longer be hacking), and it pretty much includes most of the suggestions by Shadow Wolf TJC.
To achieve that, i'm introducing a separate entity called a "ruleset". It is an include file that can be used by many personalities. It provides a summary of game stats and rules for the AI to use.
Personality definition is now in the main .js file. It includes the head, then its ruleset, then defines the personality, then includes the rest of the code.
Here is what i currently use as proof of concept.
nb_rulesets/standard.js.inc - Standard game's ruleset. Some things are still missing, like list of construction turrets or repair facilities.
Code: Select all
/*
* This file describes standard stats and strategies of
* the base (unmodded) game.
*
* If you want to make an AI specially designed for your mod, start by
* making a copy of this file and modifying it according to your mod's rules.
*
* Then provide a personality to use the ruleset, similar to
* how nb_generic.[js|ai] is provided for this ruleset.
*
*/
const structures = {
factories: [ "A0LightFactory", ],
templateFactories: [ "A0CyborgFactory", ],
vtolFactories: [ "A0VTolFactory1", ],
labs: [ "A0ResearchFacility", ],
gens: [ "A0PowerGenerator", ],
hqs: [ "A0CommandCentre", ],
derricks: [ "A0ResourceExtractor", ],
extras: [ "A0Sat-linkCentre", "A0LasSatCommand", ],
};
// NOTE: you cannot use specific stats as bases, but only stattypes
// probably better make use of .name rather than of .stattype here?
const modules = [
{ base: POWER_GEN, module: "A0PowMod1", count: 1, cost: MODULECOST.CHEAP },
{ base: FACTORY, module: "A0FacMod1", count: 2, cost: MODULECOST.EXPENSIVE },
{ base: VTOL_FACTORY, module: "A0FacMod1", count: 2, cost: MODULECOST.EXPENSIVE },
{ base: RESEARCH_LAB, module: "A0ResearchModule1", count: 1, cost: MODULECOST.EXPENSIVE },
];
const targets = []
.concat(structures.factories)
.concat(structures.templateFactories)
.concat(structures.vtolFactories)
.concat(structures.extras)
;
// body and propulsion arrays don't affect fixed template droids
const propulsionStats = {
ground: [
{ res: "R-Vehicle-Prop-Wheels", stat: "wheeled01" },
{ res: "R-Vehicle-Prop-Halftracks", stat: "HalfTrack" },
{ res: "R-Vehicle-Prop-Tracks", stat: "tracked01" },
],
hover: [
{ res: "R-Vehicle-Prop-Hover", stat: "hover01" },
],
vtol: [
{ res: "R-Vehicle-Prop-VTOL", stat: "V-Tol" },
],
}
const bodyStats = {
kinetic: [
{ res: "R-Vehicle-Body01", stat: "Body1REC", weight: WEIGHT.LIGHT, usage: BODYUSAGE.UNIVERSAL }, // viper
{ res: "R-Vehicle-Body05", stat: "Body5REC", weight: WEIGHT.MEDIUM, usage: BODYUSAGE.UNIVERSAL }, // cobra
{ res: "R-Vehicle-Body02", stat: "Body2SUP", weight: WEIGHT.LIGHT, usage: BODYUSAGE.UNIVERSAL }, // leopard
{ res: "R-Vehicle-Body11", stat: "Body11ABT", weight: WEIGHT.HEAVY, usage: BODYUSAGE.GROUND }, // python
],
thermal: [
{ res: "R-Vehicle-Body04", stat: "Body4ABT", weight: WEIGHT.LIGHT, usage: BODYUSAGE.UNIVERSAL }, // bug
{ res: "R-Vehicle-Body08", stat: "Body8MBT", weight: WEIGHT.MEDIUM, usage: BODYUSAGE.UNIVERSAL }, // scorpion
],
}
// Unlike bodies and propulions, weapon lines don't have any specific meaning.
// You can make as many weapon lines as you want for your ruleset.
const weaponStats = {
machineguns: {
role: ROLE.AP,
chataliases: "mg",
weapons: [
{ res: "R-Wpn-MG1Mk1", stat: "MG1Mk1", weight: WEIGHT.LIGHT }, // mg
{ res: "R-Wpn-MG2Mk1", stat: "MG2Mk1", weight: WEIGHT.LIGHT }, // tmg
],
vtols: [
{ res: "R-Wpn-MG3Mk1", stat: "MG3-VTOL", weight: WEIGHT.LIGHT }, // vtol hmg
],
defenses: [
{ res: "R-Defense-Tower01", stat: "GuardTower1", defrole: DEFROLE.GATEWAY }, // mg tower
],
templates: [
{ res: "R-Wpn-MG1Mk1", body: "CyborgChain1Ground", prop: "CyborgLegs", weapon: "CyborgChain1Ground" }, // mg cyborg
],
extras: [
"R-Wpn-MG-Damage01",
],
},
};
Code: Select all
/*
* This file defines a standard AI personality for the base game.
*
* It relies on ruleset definition in /rulesets/ to provide
* standard strategy descriptions and necessary game stat information.
*
* Then it passes control to the main code.
*
*/
// You can redefine these paths when you make a customized AI
// for a map or a challenge.
NB_PATH = "/multiplay/skirmish/" ;
NB_INCLUDES = NB_PATH + "nb_includes/";
NB_RULESETS = NB_PATH + "nb_rulesets/";
// please don't touch this line
include(NB_INCLUDES + "_head.js.inc");
////////////////////////////////////////////////////////////////////////////////////////////
// Start the actual personality definition
// the rules in which this personality plays
include(NB_RULESETS + "standard.js.inc");
// variables defining the personality
var personality = {
weaponPaths: [ weaponStats.machineguns, ], // weapons to use
maxDispersion: 5, // how dispersed may the tanks be
minTanks: 4, // minimal attack force at game start
becomeHarder: 2, // how much to increase attack force every 5 minutes
maxTanks: 32, // maximum attack force size
}
// this function describes the early build order
function buildOrder() {
var ret;
if (countStructList(structures.factories) < 1)
buildBasicStructure(structures.factories) != BUILDRET.UNAVAILABLE ? return:;
if (countStructList(structures.labs) < 1)
buildBasicStructure(structures.labs) != BUILDRET.UNAVAILABLE ? return:;
if (countStructList(structures.gens) < 1)
buildBasicStructure(structures.gens) != BUILDRET.UNAVAILABLE ? return:;
if (countStructList(structures.hqs) < 1)
buildBasicStructure(structures.hqs) != BUILDRET.UNAVAILABLE ? return:;
}
}
////////////////////////////////////////////////////////////////////////////////////////////
// Proceed with the main code
include(NB_INCLUDES + "_main.js.inc");
I'm also deprecating the notion of a cyborg in favor of a fixed template, since some mods (eg. WZMini) contain fixed templates that aren't on any cyborg propulsion.
Ideally, every object should only be mentioned at most once in these lists. This makes them shorter than NullBot's endless lists of things with tons of duplication.
I'm also trying to make the code ready for reverse adaptation that will become available when we get access to droid.weapon property. That's why, for instance, bodies are split into kinetic and thermal (depending on which armor is better).
_________________________________
If anybody helps me filling in rulesets, i'd be happy (: that's probably the only point of this announcement. The other point is to discuss the limitations of the approach.