Closed gjacobrobertson closed 8 years ago
Python module and class responsibilities should be clear, contained, and only depend on the interfaces between components, not their implementations
NERO/agent.py
Module and experiment designers should be able to use existing algorithms in a flexible way
Existing NERO agents should converge towards player defined goals reasonably quickly, and the user interface should provide meaningful insight into progress being made
Code should be readable and maintainable
NeroAgent
RTNEATAgent
has been renamed to NEATAgent
which represents any agent that is backed by a C++ NEAT::Organism
. It acts by feeding its sensors into the inputs of a neural network, activating the network, and interpreting the outputs as an action vector. It is agnostic to population level AI i.e. it can be used on its own, as part of RTNEAT, generational NEAT, or any other team.agent.py
provides factory methods for creating NeroAgent objects from string descriptions of AIs to be used. e.g. agent.factory('neat') will return a new
NEATAgent`steve-blue-rtneat.xml
that contained information on how to instantiate a new RTNEATAgent
class onto a blue robot model, and users would have to create a new template for every combination of 3d model, team color, and agent AI. Now you can use a template such as steve-blue.xml
to create a simulation entity, instantiate an agent brain yourself, and then wire them together with a call to SimContext::InitObjectBrain
NeroTeam
class represents a set of agents in an environment and any population level AI for that team. It can be instantiated on its own to be a plain team of agents with no evolution or other population level methods.teams.py
provides factory methods for creating Team objects
create_agent(agent_ai, *args)
calls agent factory methods to instantiate a new agent and add it to the teamcreate_agents(agent_ai)
fills the entire population with newly created agents is_episode_over(agent)
hook into the environment lifecycle.reset(agent)
hook into the environment lifecyclekill_agent(agent)
marks an agent as deadreset_all()
resets an entire team to a playable state, resurrects dead agents, etcis_destroyed()
returns True iff the team is non-empty and all agents on the team have been killedstart_training()
starts any training AI'sstop_training()
stops any training AI'sRTNEATTeam
represents a NEAT::Population
managed by the RTNEAT algorithm. It uses is_episode_over
to hook agents killed periodically by the RTNEAT algorithm into the simulation lifecycle, and reset(agent)
to replace organisms killed by RTNEAT. reset_all
is used for generational NEAT to turn the population over the next epoch.RTNEATTeam
expects the agents on the team to be instances of NEATAgent
(or any subclass thereof)1 / ((d * d / c) + 1)
where d
is the distance being measured and c
is a scaling constant the spreads the reward out over the scale of the field.module.py
is mostly just an interface between the UI and and environment now.NeroTeam
into JSON. Before different agents were not clearly disambiguated in their serialized from, and thus deserializing required operation of a state machine that looked for heuristic signatures of different Agent representations. For instance, the presence of the word 'genomestart' signaled the beginning of a RTNEATAgent
serialization, and could not be disambiguated from any other Agent that used a NEAT::Genome
as part of its serialized representation.team_ai
and agents
. team_ai
is a key to a string that can be used by the team factory method to know which team class to instantiate, and agents
is a key to an array of serialized agents.agent_ai
and args
. agent_ai
is a key to a string that can be used by the agent factory, and args
is a key to an array of arguments that can be passed to the agent constructor to create an agent with the appropriate state. For instance, NEATAgents
put 1 line of their NEAT::Organism
representation into each arg, and read the args back into a stream that NEAT::Organism
can be constructed from.print_to_file(std::string &filename)
which created a lot of redundancy and poor separation of filesystem concerns with serialization concerns. NEAT
namespace classes have had these functions removed, and replaced with implementations of the <<
operator to write a serialization of an object to any std::ostream
, and filename based constructors replaced with std::istream
based constructors. In this way users of the rtneat
package can handle file system operations themselves and flexibly serialize and deserialize individual components into whatever format they wish.RTNEAT
class has been significantly simplified. It was previously the only interface between the OpenNERO simulation, the rtneat package,and python modules, and had many disparate responsibilities.RTNEAT
wraps a NEAT::Population
that must be instantiated outside of RTNEAT
RTNEAT
is an OpenNERO "puppeteer" AI that periodically marks a low fitness NEAT::Organism
in its population for removal.RTNEAT
defines a method for replacing organisms killed by itRTNEAT
does not do any brain-body mapping. This was needlessly complicated anywayRTNEAT
does not do any file system operationsRTNEAT
does not do any fitness evaluation. It expects up to date univariate fitness values to be assigned to each organism it is managing. Fitness function definition and multivariate reward combination is now the responsibility of the experimenter.NEAT
classes such as Population
, Organism
, Genome
, etc have boost-python wrappers defined and can be interacted with directly by python modules without using RTNEAT
NERO_Battle
module has been updated to make use of the changes made to NERO
reset(agent)
in battle mode despawns and calls team.kill_agent(agent)
instead of team.reset(agent)
. i.e. dead agents stay dead until the end of a battle. team.start_training
or team.stop_training
, allowing team implementations to distinguish between training AI and battle AI. For instance, RTNEATTeam
runs the RTNEAT
AI in training mode (killing agents periodically), but not in battle mode (that would be a pretty bad strategy). Furthermore it implements generational NEAT in reset_all()
allowing it to evolve between battles.NEAT::Population::epoch
can segfault when all organisms' fitness is 0. This is currently avoided by not calling epoch
in that situation, but the epoch
function is 500+ lines of code and could probably use some cleaning anyhow
This is a big Pull Request which encompasses a large number of changes and significantly refactors many part of the codebase. The overarching goal of these changes is to enable experimenters to more easily design new experiments and AI algorithms, primarily in the NERO environment. I will be attempting to document the many proposed changes here in subsequent comments.