Jump to content


Community Members
  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by agentx

  1. I think of it this way: The moment OnUpdate() is called the gamestate is a snapshot of the game. So gamestate is readonly by concept, no copy needed. You initiate changes to the game via Engine.PostCommand() or whatever level of the API you use.
  2. Did a full checkout on ps/, binaries run without further action. Bot took 2h for changes. Who can I thank for barterprizes? More economy on the API, please. Health...? Edit: chat events.
  3. If in a MP the host provides a seed to all connected bots, would probably make AI games more interesting.
  4. Solved: http://simjs.com/random.html has even pareto distribution. Needs a single line fix to "use strict".
  5. I hope, I get this right. In a 4 player MP game with 2 human and 2 bots, each human has two copies of bots running. Let's say bot in team green running at human 1 computer decides to build a field, then same moment bot green on human 2 decides to build a field at the same place. As result both human see a field pop up at the very same moment and same location. Which means both bots running on separate machines are always processing same message queue and have same outcome, right? So, no Math.random? What is needed to set up a MP game locally with bots? I've checked out new common API, ApplyEntitiesDelta is getting closer to O(n) and this looks great! var CreateEvents = state.events["Create"];var DestroyEvents = state.events["Destroy"];var RenamingEvents = state.events["EntityRenamed"];var TrainingEvents = state.events["TrainingFinished"];var ConstructionEvents = state.events["ConstructionFinished"];var MetadataEvents = state.events["AIMetadata"];var ownershipChangeEvents = state.events["OwnershipChanged"];I'm trying to get my head around running multiple copies of 0 A.D. Copy G is used to game. With custom shortcuts it can be used as virtual copy D to develop. Now how can I use copy A running next Alpha from trac and let them all share public.zip and by not downloading .5GB every day?
  6. > The bot gives the same result for every turn, no matter if it has been playing before, or it comes from a saved map? As that's what happens when the network connection of a player dies, and he rejoins. That's interesting, did not know a bot has to take care of this. Could you expand on what happens in this case? > Serialization also makes it possible to research the source of oos problems in a much nicer way. Well, it depends, how you got oos problems in the first place. >For the ticks, don't you get a tick every turn? Yes, I rely on them. > And for the planning, we will release v1 when ready, there's no schedule for it. Was talking about next alpha, that's the horizon.
  7. I would prefer api freezes long enough before release to allow adaption. Can trac tell me what is planned? Coding against moving targets is much easier with a good forecast. The only two things I currently assume is a tick and an accessible message queue. Don't need serialisation, bot deals with all maps saved or designed.
  8. The mayor i've mentioned is thinning. Probably at the end there is no mayor, each group has all info it needs, why mayors? At the end there is only time, space, food, wood, stone, metal and health. What the bot manages are resources, groups can request resources to build or train and places to do their stuff. They'll get them when they are available. Probably there is always a backlog, here decisions are made and resources not wasted. I've found a JavaScript implementation of SHOP2, a HTN planner. If the bot kinda gets an idea of future, that would be great. Some group thoughts: Cultures should celebrate victories, how could a dance company gather attention. Healers? How do they find units sending SOS, without attempting suicide. When should a unit garrison? At 50% health? What is the minimum/cheapest group in each civilisation able to level towsers? Can units form letters to ask for HELP ?
  9. I still think I can implement any behaviour. More difficulty is to provide a group with useful information. E.g. reading the map and extracting a path over a map. But, I've developed a few routines while playing like the gatherer group outlines above which I think could a bot use. Do you like to share your tactics?
  10. Well, that shoudn't stop from contributing. My bot will be heavily based on groups and their tasks. E.g. 5 females working on a field, they repair, rebuild if needed or garrison on attack and request new members on losses. What other kind of group behaviour could you think of? Ideas please!
  11. > There's an ownerShipChanged event you can check. When the ownership changes from invalid (-1) to something valid, you know a new entity has been created. Ok, thanks I'll check this. > The AI can show chat messages (it's done when an allied AI attacks). It has no mouse, and generally can only communicate through predefined commands. Yes, I know how to make a bot speaking, but I want it to listen...
  12. Was in need to query the templates more often than acceptable and code got littered with ifs, switches and very long condition lines. So I've spend my bot a triple store and its own query language. This is how it loads: : reading 1188 templates : 65/1188 nodes for athen : 139/1188 nodes for gaia : ignored: Brit(1), brit(59), cart(84), celt(69), gaul(55), hele(93), ..... : loaded 0/0 nodes from Hannibal.Data.RootNodes : created 79/ 79 nodes for classes : created 41/ 41 nodes for technologies : created 14/ 14 nodes for resources : created 4/ 4 nodes for resourcetypes : created 613 edges on member (entities membering classes) : created 613 edges on contain (classes contain entities) : created 111 edges on provide (entities providing resources) : created 165 edges on gather (entities gathering resources) : created 274 edges on build (entities build entities) : created 26 edges on train (entities train entities) : created 46 edges on hold (entities holding class) : created 3 edges on heal (healer heal classes) : created 39 edges on research (entity researches technology) : created 57 edges on require (entities require technology) : created 57 edges on enable (technology enables entities) : created 12 edges on accept (entities accepting resourcetype (dropsites)) : created 156 edges on carry (entities carrying resourcetype (ships, trader, gatherer)) : have 2 civs, 13 verbs, 342 nodes, 2172 edges: : have [accept, build, carry, contain, enable, gather, heal, hold, member, provide, require, research, train]The database has nodes and edges, edges represent a verb and have a direction. Nodes have a unique name. So you start a query with some nodes. Here are a few sessions, 'i' denotes the query (a plain string), 'o:' the parse tree and R a result line: Parser: i 'healer, hero' : o: ["healer, hero"] Q: executed: msecs: 0, records: 2 R: node: healer R: node: heroQueries always return nodes and obviously two classes return two class nodes Parser: i 'healer, hero CONTAIN' : o: ["healer, hero",["CONTAIN"]] Q: executed: msecs: 0, records: 6 R: node: units.athen.support.healer.a R: node: units.athen.support.healer.b R: node: units.athen.support.healer.e R: node: units.athen.hero.iphicrates R: node: units.athen.hero.pericles R: node: units.athen.hero.themistoclesHere I added a verb after and now the query has a direction, it points from classes to entities. All verbs are keywords and uppercase. LIMIT, SORT, RAND, WITH are keywords too. Parser: i 'infantry, hero CONTAIN SORT > costs.metal' : o: ["infantry, hero",["CONTAIN"],["SORT","> costs.metal"]] Q: executed: msecs: 3, records: 21 R: node: units.athen.hero.pericles, metal: 200 R: node: units.athen.hero.themistocles, metal: 200 R: node: units.athen.hero.pericles, metal: 200 R: node: units.athen.hero.themistocles, metal: 200 R: node: units.athen.champion.infantry, metal: 80 R: node: units.athen.champion.marine, metal: 80 R: node: units.athen.champion.ranged, metal: 50 R: node: units.athen.infantry.archer.a, metal: 50 R: node: units.athen.infantry.archer.b, metal: 50 R: node: units.athen.infantry.archer.e, metal: 50 R: node: units.athen.infantry.javelinist.a, metal: 50 R: node: units.athen.infantry.javelinist.b, metal: 50 R: node: units.athen.infantry.javelinist.e, metal: 50 R: node: units.athen.hero.iphicrates, metal: 0 R: node: units.athen.infantry.slinger.a, metal: 0 R: node: units.athen.infantry.slinger.b, metal: 0 R: node: units.athen.infantry.slinger.e, metal: 0 R: node: units.athen.infantry.spearman.a, metal: 0 R: node: units.athen.infantry.spearman.b, metal: 0 R: node: units.athen.infantry.spearman.e, metal: 0 R: node: units.athen.hero.iphicrates, metal: 0The syntax is simple, it starts either with nodes or a verb clause. Then you may put one verb clause after another verb clause to traverse the mesh. [ nodes | VERB [prop [, prop, [...]]] [WITH prop op value [, prop op value, [...]]] [SORT op prop [, op prop, [...]]] [LIMIT value] [RAND value] [VERB [...]]]The idea is the DB represents the involved civilisations, it's a knowledge base of their capabilities. Who can do what, what does it cost and how long does it take. Next step is to retrieve not only templates but real game units, buildings, etc. and in the far future queries written in SPARQL. Currently the code is not optimized and I don't know what happens when 8 bots load their own triple store each with 8 civilisations. You start the whole thing like this: civs = new Hannibal.Civilisations(this, ["athen", "gaia"], this.settings.templates);HCQ = Hannibal.Civilisations.Queryquery = new HCQ(civs , "infantry, hero CONTAIN SORT > costs.metal");nodes = query.execute();Code (still hot, but working) is attached, comments welcome! querylanguage.zip
  13. My first question was what objects the bot might use, so here is 'global' the equivalent of 'window' in a browser environment.logObject(global); Accessibility: function Accessibility() {"use strict";} ... AssocArraytoArray: function AssocArraytoArray(assocArray) {"use strict";var end ... BaseAI: function BaseAI(settings) {"use strict";if (!settings) {retu ... Class: function Class(data) {"use strict";var ctor;if (data._init) ... DoesModificationApply: function DoesModificationApply(modification, classes) {"use ... Engine: OBJECT (ProfileStart, ProfileStop, IncludeModule, DumpHeap, ForceGC, ...)[8] Entity: (function (sharedAI, entity) {"use strict";this._super.call( ... EntityCollection: function EntityCollection(sharedAI, entities, filters) {"use ... EntityTemplate: (function (template, techModifications) {"use strict";this._ ... Filters: OBJECT (byType, byClass, byClassesAnd, byClassesOr, byMetadata, ...)[27] GameState: (function () {"use strict";this.ai = null;this.cellSize = 4; ... GetTechModifiedProperty: function GetTechModifiedProperty(currentTechModifications, e ... ManhattanDistance: function ManhattanDistance(a, {"use strict";var dx = a[0] ... Map: function Map(sharedScript, originalMap, actualCopy) {"use st ... Memoize: function Memoize(funcname, func) {"use strict";return functi ... MemoizeInit: function MemoizeInit(obj) {"use strict";obj._memoizeCache = ... PickRandom: function PickRandom(list) {"use strict";if (list.length === ... PlayerID: NUMBER (1) Resources: function Resources(amounts, population) {"use strict";if (am ... ShallowClone: function ShallowClone(obj) {"use strict";var ret = {};for (v ... SharedScript: function SharedScript(settings) {"use strict";if (!settings) ... SquareVectorDistance: function SquareVectorDistance(a, {"use strict";var dx = a ... TERRITORY_PLAYER_MASK: NUMBER (63) Technology: function Technology(allTemplates, templateName) {"use strict ... TerrainAnalysis: function TerrainAnalysis() {"use strict";this.cellSize = 4;} ... VectorDistance: function VectorDistance(a, {"use strict";var dx = a[0] - ... aStarPath: function aStarPath(gameState, onWater, disregardEntities, ta ... copyPrototype: function copyPrototype(descendant, parent) {"use strict";var ... deepcopy: function deepcopy() {[native code]} ... error: function error() {[native code]} ... g_FoundationForbiddenComponents: OBJECT (ProductionQueue, ResourceSupply, ResourceDropsite, GarrisonHolder, ...)[4] g_ResourceForbiddenComponents: OBJECT (Cost, Decay, Health, UnitAI, UnitMotion, ...)[6] global: OBJECT (global, Engine, print, log, warn, ...)[49] inRange: function inRange(a, b, range) {"use strict";if (a === undefi ... log: function log() {[native code]} ... print: function print() {[native code]} ... warn: function warn() {[native code]} ...That already gives a hint on the rich API 0 A.D. provides. Engine looks interesting: logObject(Engine): DumpHeap: function DumpHeap() {[native code]} ... DumpImage: function DumpImage() {[native code]} ... ForceGC: function ForceGC() {[native code]} ... IncludeModule: function IncludeModule() {[native code]} ... PostCommand: function PostCommand() {[native code]} ... ProfileStart: function ProfileStart() {[native code]} ... ProfileStop: function ProfileStop() {[native code]} ... RegisterSerializablePrototype: function RegisterSerializablePrototype() {[native code]} ...It turns out the API functions and objects are either provided by the host environment/SpiderMonkey [warn, log, etc.] or by 0 A.D. itself written in C++ [PostCommand, etc] or by code written in JavaScript. Next part will extend XBot and get in touch with settings, gameState, sharedScript and BaseAI. To be continued. PS. if you can't wait, explore this folder: D:\Games\0 A.D. alpha\binaries\data\mods\public\simulation\ai\common-api-v3 or inspect the other example bots.
  14. There is a browser based JS debugger allowing breakpoints and variable inspection, but I got it running only once or twice, so dbgView from SysInternals is my debugging tool of choice. A bot has 3 possibilities to talk to you while running, 1) chatting, 2) the in game console and 3) logging to std, in this case dbgView. The console is of little use since it runs in another context. However, the Aegis Bot uses it quite well to explain its actions once debug is set in its config. I decided to use dbgView only and keep the chat function to entertain the human opponent. SpiderMonkey is very picky and demands properly written code. I use Sublime Text as editor and a JSLint plugin to make sure SpiderMonkey is happy before 0 A.D. fails to load the code. JSLint is even more picky, but there is nothing wrong with using a good coding style restricted to the good parts of JavaScript. In terms of style I tend to prefer Google's JavaScript Style Guide. You probably want to start developing with a simple map. Get used to the map editor Atlas and create a map with two Civil Centers one for your bot (Player 1) and one for Aegis (Player 2). It's actually quite simple. Since maps are XML files, direct editing is possible too. Atlas saves maps in a special windows folder, mine is: C:\Daten\My Games\0ad\mods\user\maps\scenarios My first map is attached. You start it with this command line:"D:\Games\0 A.D. alpha\binaries\system\pyrogenesis.exe" -quickstart -autostart=xbotmap01 -autostart-ai=1:xbot -autostart-ai=2:aegisAt this point you have an Bot which is functional, but as clever as a stone. It is a good idea to learn the hotkeys, triple clicks give you some advantages, also there is an useful developer overlay. xbotmap01.xml
  15. First I'll describe how to set up your system and show the basic layout of an 0 A.D. bot. It starts with downloading and installing the game, on my system that's the path d:\games\0.A.D\. Check out this guide for other systems and other paths mentioned below. A bit deeper is a huge zip file called public.zip, extract that into its folder keeping the folder structure intact. Now you'll find a folder called d:\Games\0 A.D. alpha\binaries\data\mods\public\simulation\ai Here the fun starts. Think of a good name, let's say XBot, and create a xbot sub folder as a sibling to qbot, testbot, aegis, etc, then two files are needed: data.json and _xbot.js.{ "name": "XBot", "description": "XBot - example", "constructor": "XBot", "useShared": true }Engine.IncludeModule("common-api-v3");function xbot(settings) { BaseAI.call(this, settings);}xbot.constructor = xbot;xbot.prototype = new BaseAI();xbot.prototype.CustomInit = function(gameState, sharedScript) {};xbot.prototype.OnUpdate = function(sharedScript) {};The files in your bot folder are loaded alphabetically, so the underscore makes sure the api is included first. I use a local.cfg file C:\Documents and Settings\Owner\Application Data\0ad\config in to ease development: windowed = truesplashscreenenable = falsepauseonfocusloss = truexres = 1024yres = 768jsdebugger.enable = false
  16. After playing 0 A.D. far to often I decided to program my own AI/bot. 0 A.D. is an interesting client for an AI because first it is so beautiful, second it requires solving real world problems and, well, bots need to be written in JavaScript, which is quite popular. So this is kind of a forum blog of a Linux/Internet guy developing on an old Windows XP license against Mozilla's SpiderMonkey having no clue about C++ programming or compiling. The following wouldn't be impossible without a working AI. Wraitii's excellent Aegis Bot is nearly game complete and was a challenging opponent for dozens of rounds. Aegis implements resource allocation, queueing and management, building placement, map exploration and more - all important things needed whatever logic an AI prefers. I love the idea of AIs showing their capabilties in a complex environment fighting against each other. Hopefully developers reading this feel challenged and Hannibal gets opponents really soon, which is the purpose of this writing. Since all code is open everybody can use and optimize each other solutions and strategies. If you are interested, this is a good starting point. I'll plan to continue this series at least until the sample bot trains units and let them gather resources. I have a few design goals in mind: With highest difficulty the AI should be unbeatable while the lowest should be just interesting enough to engage beginners. Unit groups should solve most problems more or less autonomously. And at a later point research would be required to restrict the AI to launch only attacks according to the selected civilisation. Hannibal will use kind of a state machine to implement behaviours based on frames. Frames will run in a special context/closure providing something as close to a domain specific language (DSL) as JavaScript, time and "strict mode" allows. Everything below and above refers to 0 A.D. Alpha 15 Osiris (running on Windows) using SpiderMonkey 4 impementing Javascript 1.85. Some statements might be based on untested information found in the wiki or this forum and could be wrong or misleading. Please, let me know!
  17. First I hacked a few lines into the shared code, so AI's could register a callback. Then I stepped deeper into your code and it looks like Aegisbot.savedEvents has all I need. So I started a simple game and watched event tv in dbgView. What I understood is 'Create' is called when training starts, but it lacks the owner. 'TrainingFinished' is more useful and 'Destroy' is nice too. Similar to 'ConstructionFinished'. What I want to achieve is what I call 'Groups' actually EntityCollections subclassed. Let's say the manager or mayor of a civic centre asks for more food. He launches a Group of 5 females and one field, all do not exist. So the group asks the major for females, once trained they get automatically assigned to the group choosing a place for the field and let them work. That part should work by push not by poll. It would be simple (one possibility) if postcommand returns the ids of the future entities. All needed then is a dictionary/map with the ids as key and the callback as value. When 'TrainingFinished' comes in I would just dispatch the message via callback and the id to the Group which then takes care. When 'Destroy' comes in the Group asks for a new female and the entry got deleted from the map . Similar with 'Destroy' field. I understand that is currently not possible, what I'm planning now, not tested, is polling idle entities not registered in a Group, check which Group has asked for these kind of entities and set up the dispatcher accordingly. That way the mayor only deals with resource allocation and training entities. Groups ensure no idle workers. And on top I believe that system would also work nicely with armies. Another idea is that in easy mode the mayor does not launch e.g. repair groups listening at 'Attack' events. Does that makes sense? Btw, two questions: Is there any possibilitiy to communicate with the AI once the game runs? Maybe with chat commands, clicking at hot spots, mouse gestures, konami code, whatever? Just to trigger functions. I've found the developer overlay, very useful, can an AI also draw shapes on the map, just for debugging? Or how can I use dumpimage? Google is of no help.
  18. Not sure if I'm already literate enough to pose this question properly, but here it goes: On game start from scratch or from saved game, I'd like to know whether there is a Civil Centre or not. From that point on I'm only interested in changes, e.g. destroyed by enemy or building of another one has finished. How can I keep track of the number of Civil Centres? To me looks like the engine is internally heavily based on messages/events and on each call 'SharedScript.prototype.ApplyEntitiesDelta' processes new events into a datastructure/collection ready to get filtered by an AI. So the proposed approach is to query this collection to find out the number of Civil Centers. If this is true so far there is a conflict: on one hand the number of CCs is critical to the AI and on the other hand querying the collection is very costly. This leads to the question how often can I afford to query the collection for the number of CCs? And the answer depends very much on the horse power of the local machine. Actually there many similar questions, number of females, warriors, champions, houses, towers, etc. I hope, here someone can intercept and tell me why this thinking is wrong and all these queries are not needed at all. If not... An alternate interface/API would give the AI all its items at startup and from then both only talk about changes. The AI would demand the construction of a building *and* provide a callback function the API uses to inform about changes like destroyed or construction finished. Since functions are first class citizens in JavaScript this would solve two things: Queries are no longer needed and coding an AI would be much simpler. I made a test and put this into CustomInit sharedScript.ApplyEntitiesDelta = function(state){ debMsg("ApplyEntitiesDelta: %s events", state.events.length); }Which works as expected at the cost of disconnecting the enemy AI from the game. May I ask to apply a little change to shared.js and allow AIs to implement ApplyEntitiesDelta as they like? Edit: I understand the consequences on the use of gamestate.js
  19. @blargin3: I've seen on the other thread the beautiful results of a random map generator, all with completely different topography. I'd guess a hardcoded city layout would lead to funny results on 50% of them. The key is to think differently and put computation at the cost side. Somewhere on youtube is a video of a group of little car like robots clearing a room full of scattered table tennis balls by moving all of them in the middle of said room. The algo implemented in each bot is surprisingly simple: move forward until three balls are in front, then stop, drive back a little bit, turn around towards a new random direction and repeat. I call that ant programming, simple unit behaviour leads to complex group behaviour. Here in Europe many cities developed without planning into efficient infrastructures. How would an algo for organic village growth look like?
  20. The idea came mostly from reading Wraitii's code. I've spend quite some hours fighting Aegis and now many things make sense. Without his work I would not have invested a second to think about a bot, because I had no idea where to start. I hope hopping doesn't develop into an issue, the frames should be clearly defined without overlap. The left over right vs. random is a good idea and easy to code, noted. One thing I plan differently are targets. So, instead of setting a goals like population=100, I'll try flow rates like population growth per minute, metal per minute, etc.. Still not sure if the math could be based on template data or needs heuristics or both. That should work quite well with AI difficulties too, because all beginner start with a low resource gathering rate. What I'm really interested in is an AI has far more possibilities on a map than the user handicapped with a mouse interface. I've never managed to use archers efficiently, they are quite powerful, if they are placed correctly and manage to keep themself out of trouble. Finally, I didn't train them at all or put them in towers if they are cheaper. An AI should be able to handle 3 or 4 groups of archers and clear an area effectively. Also tactics, on one end is the guerilla approach with let's say 20 micro groups of 3 hoplites, just attacking one enemy unit each and after the rush gathering around a healer while another bunch attacks, both alternating and on the other end the mega strike with 200 warriors per city center. I think there are useful attack pattern in between and an AI should be able to launch them all to keep the human enemy on the edge of his chair. The ultimate dream is this: I really wonder what kind of AI might come up with such a strategy? PS: Many thanks for your useful and detailed decision tree. Post is bookmarked. PPS: This is getting off topic, I'll start another thread with AI strategies. Let's discuss AI tournament obstacles here.
  21. I currently tend to implement behaviours like this: C.behaviour = { start: "whiteflag", whiteflag: ["economy", "attack", "defense", "victory"], economy: ["populate", "gather", "hunt", "expand", "research", "fortify", "mobilize", "defense", "victory"], mobilize: ["parade", "analyze"], attack: [...], defense: [...], victory: [], };The AI initializes on the start frame and on each call it checks the frames listed for their entry conditions. If at least one frame qualifies the AI switches to this new frame. The frames also have exit conditions, which let it fall back to the calling frame. So, for example if economy has an entry conditions like canTrain it directly changes on startup, the exit conditions are e.g. noPopulation && noBuilding. If the enemy destroys all, the AI falls back to whiteflag and the game is over. Or there is nothing left to fight and the AI enters victory. Some frames have tasks listed having no behaviour entry, e.g. fortify, these just get executed. The idea is each difficulty has its own behaviour with certain features. That way the AI does what it does in a convincing way. Maybe on an easy level the AI doesn't build towers, but on a hard level I don't want the towers placed in a random fashion.
  22. That's sad, but I like your trojan bot, he's a winner. On the other hand, if "inclusion in next alpha" is acceptable at all, manual review will happen probably anyway. Perhaps and depending on how dynamic a bot is coded Object.freeze() might be an option. Would you like to give it a try?
  23. > According to trac #2322, all AIs now live in a global compartment. So i presume an AI could cheat by patching another AI script away? That's a show stopper. Is it true AIs can change each other code? Or even worse, can manipulate/destroy arbitrary game objects (store-builders, etc)?
  24. > seriously what is needed to make 0AD run Aegis vs. Aegis? Solved that with: "D:\Games\0 A.D. alpha\binaries\system\pyrogenesis.exe" -autostart=Aitest01 -autostart-ai=1:aegis -autostart-ai=2:aegisand a simple map: The confrontation was short, four units died, two females got stuck after building a storehouse. I think I invest some weekends to see what is needed to watch an epic battle on a map like this (without stone and metal). A genetic AI would require a complete abstraction. Every available action needs to be referable as a string, so new generations could be mixed and mutations have a data structure to work on. That's probably the far end of the road. Also two good gen sets do not necessarily have clever offspring. Somehow a fit function has to decide off game whether there is progress at all. On the other hand eventually very optimized AIs would be generated - kinda AI mining, though.
  25. To me it look like the hurdles to start with an AI are currently quite high. It seems everybody expects kind of miracles not seeing it would take full-time months to beat Aegis while starting from scratch. The idea with the tournament was to lower these hurdles as far as possible. I think one AI is enough to start, seriously what is needed to make 0AD run Aegis vs. Aegis? From that point everybody with minimal knowledge can contribute at least by tweaking parameters. Debugging would be easier too since manual action is not required. If a game could continue from a saved mile stone automatic testing is very close. There are many other AI tournaments, every single one appears on the Hacker News front page. 0AD has on the plus side a pleasing visual environment. I can't think of a better client to demonstrate a capable JavaScript AI. What are you afraid of? That military steps in and offers funding?
  • Create New...