Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

Programma interpreteren en opslaan uit string

Pagina: 1
Acties:

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Voor een simulatie van genetic programming ben ik een programma aan het schrijven dat een wereld simuleert met daarin agenten. Deze agenten kunnen een aantal handelingen verrichten, waarvan sommigen invloed op de wereld hebben (zoals bewegen), andere zijn eigenlijk alleen interne logische operatoren (And, Not, IfThen etc).
Alle instucties/operatoren krijgen twee argumenten mee (twee bytes), en geven 1 byte terug.

Elke agent heeft zijn eigen DNA, dit dna beschrijft hoe het programma van de agent moet worden opgebouwd, dit opbouwen gebeurt dus eenmalig bij de "geboorte" van de agent. Daarna wordt telkens als de agent aan de beurt is (de agenten komen per ronde in een willekeurige volgorde aan de beurt) uitgevoerd.

Een voorbeeld:
Het dna van de agent beschrijft het volgende programma (hoe het dna dit beschrijft is nu even niet van belang):

IfThen (
DropFood ( 1010 0000, 0000 0101 ),
Attack ( 0000 0001, 0000 0000)
)

Attack ( 1111 0101, 0000 0000)

Hoe kan ik dit "programma" dan het beste opslaan zodat het ook uit te voeren is? Ik zat te denken aan een hoofdclasse voor alle instructies (genaamd "instructions"), elke mogelijke instructies heeft dan een klasse die hiervan erft, en er is een Number klasse, om ook nummers als argumenten (argumenten zijn namelijk dus ook altijd van het type instruction) mee te kunnen geven. Dit zorgt ervoor dat ik dus dit kan doen, in feite een soort boom dus:

code:
1
2
3
4
Code.add( new IfThen (
                        new DropFood(new Number (1010 0000), new Number (0000 0101)), 
                        new Atack (new Number (0000 0001), new Number(0000 0000)) ) )
Code.add( new Attack ( new Number (1111 0101), new Number(0000 0000) )


Tevens heeft elke klasse ook een execute() functie, dus als ik bijvoorbeeld de bovenstaande dropfood.execute() uitvoer roept hij weer een .execute op zijn argumenten aan, in dit geval de twee Number instanties, die elk dan gewoon hun nummer returnen, waarna DropFood dus genoeg informatie heeft en daadwerkelijk aan de slag kan.

Op zich lijkt mij dit een best makkelijke manier van opslaan, maar het punt is dat als ik nu bijvoorbeeld bovenstaand programma uit wil voeren, en DropFood zijn argumenten heeft berekend hij niet alleen een waarde terug moet geven aan de IfThen, maar dat de agent ook daadwerkelijk eten moet "droppen", voordat verder wordt gegaan met het "executen" van de rest van het programma. Maar DropFood is dus een object binnen een object binnen een object etc van een instantie in de agent. Hoe kan kan ik dan zorgen dat het DropFood een functie kan aanroepen van zijn verre parent, de agent, zodat de agent de stap zet? Daarnaast moet DropFood voor zijn uitvoer ook informatie kunnen krijgen van de parent over de huidige situatie, om te bepalen wat hij teruggeeft aan de IfThen.


Ik hoop dat het een beetje duidelijk is zo, en als jullie de hele opslag/executie van het programma anders zouden doen dan hoor ik dat natuurlijk ook graag.

Verwijderd

Gewoon een parser dus.
Gebruik een parser generator en definieer een taal. Dat soort code wil je niet zelf schrijven.

  • Macros
  • Registratie: Februari 2000
  • Laatst online: 11:06

Macros

I'm watching...

Ik heb zoiets voor mijn afstuderen geschreven. Daarbij was het belangrijk dat de code zo snel mogelijk uitvoerde omdat ik de programma's die zo gegenereerd werden ook heel vaak wilde kunnen draaien. Ik genereerde dus Java byte code direct en kon dat uitvoeren. Als ik het opnieuw zou doen, zou ik Java code of een gerelateerde taal genereren en dat compilen. Dit heeft natuurlijk alleen nut als je de programma's vaak genoeg draait. Anders kan je zoals Cheatah al aangaf beter geen compiler gebruiken.
Wat je zou kunnen doen, is je String vertalen naar een script, en dat uitvoeren. Ik weet niet welke taal je gebruikt, in Java kan je Javascript draaien, of andere jvm gebaseerde talen.
Er zijn 1000 wegen naar rome voor dit "probleem".

"Beauty is the ultimate defence against complexity." David Gelernter


Verwijderd

Welke taal gebruik je?
Met bv .NET kun je namelijk vanuit een programma zelf de compiler aanroepen.
Het is dus mogelijk om via code bv een C# class te bouwen in een string, en die string dan te compileren.
Je krijgt dan een assembly terug (je kunt kiezen of die naar disk geschreven moet worden, of alleen in-memory), en via reflection kun je classen in die assembly instantieren.
Als je met vooraf gedefinieerde interfaces werkt, is het vrij makkelijk om de instantie te casten naar een interface, en dan zonder verdere reflection methods aan te roepen etc. Het object werkt dan gewoon als ieder ander object dat die interface implementeert.

Op die manier kun je heel makkelijk een soort 'plugin'-systeem maken.

  • jark86
  • Registratie: December 2012
  • Laatst online: 08-08-2017
Het lijkt erop dat je parser combinators wilt gebruiken, hiermee zou je je boom prima op moeten kunnen bouwen.

Zie deze blogpost voor een goede tutorial: http://theorangeduck.com/...vented-parser-combinators

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
De oplossing van Macros lijkt mij het meest geschikt, ik ga de programma's namelijk zeker vaak draaien (elke ronde voor elke agent tot deze dood gaat). Is dat programma/je scriptie ook nog ergens te vinden trouwens?

Ik heb nog nauwelijk code geschreven (nu in c#), maar ik wil best op java overstappen. Ik vond hier iets over groovy:
code:
1
2
3
Binding binding = new Binding();
GroovyShell shell = new GroovyShell(binding);
Object value = shell.evaluate("for (x=0; x<5; x++){println "Hello"}; return x");


Is dit een goede keuze denken jullie? (ook wat de snelheid betreft)

  • Durandal
  • Registratie: Juli 2002
  • Laatst online: 15-11 15:41
Ik heb laatsgeleden voor een game waarbij de speler zelf zijn poppetjes kan programmeren ook naar alle mogelijkheden gezocht om userscripts te draaien en uitgaande van java kwam ik toch uit op javascript als de beste oplossing. Vooral ook omdat dat nog aardig snel draait, je niet met de problemen van multithreadig zit en omdat js gemaakt is om in een container te draaien en je zo dus de rest van het systeem afschermt. Javascript draait native in java tegenwoordig.
Als die zaken er voor jou niet toe doen omdat je geen gebruiers hebt dan zou ik gewoon java in java gebruiken en de compiler gebruiken (javac) om bytecode te maken en die dan te runnen. Kan niet veel sneller dan dat en je zit niet aan 2 talen vast.

Waar je nog wel mee zit zijn zaken als processor gebruik (infinite loops) en resource gebruik.
Ik ben trouwens ook wel geinteresseerd in genetic algorithms voor de AI van mijn npcs/animals. Waar ga je het voor gebruiken?

[ Voor 13% gewijzigd door Durandal op 09-12-2014 14:34 ]


  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Je bedoelt dus iets als dit?

Ik ben nog bezig met het uitdenken van zo'n beetje alles, maar mijn eerste opzet bestaat uit een wereld waarin dus agenten leven aan de hand van hun eigen programma dat is opgebouwd uit een stuk of 30 verschillende soorten instructies. Ze kunnen hierin dus bewegen, eten (door op eten te gaan staan), anderen aanvallen, informatie opvragen over omliggende cellen, eten droppen (voor anderen), anderen aanvallen etc.

Verder kunnen ze intern dus een heel programma opbouwen door bovengenoemde instructies te combineren met logische operatoren en hun geheugen. Ook hebben ze allemaal bijvoorbeeld een eigen ID, dat door agenten in de beurt kan worden "gezien", en dat ze zelf kunnen aanpassen. Ik hoop dus dat dit gaat worden gebruikt als een soort communicatie/uiterlijk. Verder bestaat de wereld uit een soort rgb gradient en de agenten hebben een betere kans in gevechten als hun kleur hier beter mee overeenkomt, zo hoop ik meer parallele evolutie te kunnen krijgen om het probleem van convergentie naar 1 soort tegen te gaan. Tevens ben ik nog bezig met het DNA van de agenten, om hopelijk interessante informatie te kunnen vinden wat betreft het Genotype-Fenotype probleem (de afwezigheid hiervan in de meeste simulaties is dus het probleem).

Daarnaast heb ik ook een manier bedacht (hoop ik) waarmee de agenten uiteindelijk een vorm van voortplanting kunnen ontwikkelen waarmee ze niet alleen hun eigen dna doorgeven, maar ook dat van een ander (zoals in de natuur). Dus de manier waarop ze voortplanten (zelfs de mate van bijvoorbeeld mutatie) is ook aan evolutie onderhevig, iets wat ik nog niet eerder ben tegen gekomen in andere simulaties.

Maar dit is allemaal dus nog niet volledig uitgewerkt. De simulatie heeft dus als enig doel om meer informatie te krijgen over kunstmatige evolutie op zich.

Verwijderd

Ik zei hetzelfde al eerder, maar dan in C#.
Maar in C# kun je ook de compiler als class aanroepen vanuit de code zelf, dus je hoeft niet eens te rommelen met commandlines, scriptjes etc.

Defineer een interface voor je agents...
Iets als:
code:
1
2
3
4
5
interface IAgent
{
  void Initialize();
  void Execute();
}


En dan in je code zou je gewoon zoiets als dit kunnen doen:
code:
1
2
3
4
5
6
7
8
9
string buildAgent(string name, string init, string exec)
{
    return "class " + name + " : IAgent\n{"
    + "void Initialize(){" + init + "}"
    + "void Execute(){" + exec + "}";
    + "}";
}

buildAgent("MyAgent", "MessageBox.Show("MyAgent.Initialize();", "MessageBox.Show("MyAgent.Execute();");


Om maar iets te noemen.

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Oke, dan ga ik het eerst eens met C# proberen. Wat jij dus eerder zij was dit doen om de string die je in je laatste post dus opbouwd met buildAgent te compileren en uit te voren toch?

Verwijderd

huub8 schreef op dinsdag 09 december 2014 @ 15:28:
Wat jij dus eerder zij was dit doen om de string die je in je laatste post dus opbouwd met buildAgent te compileren en uit te voren toch?
Ja, zoiets. Is al weer een tijdje geleden dat ik het zelf gebruikt heb, en ik heb de code hier nu even niet bij de hand... Maar dat is het idee idd: je stuurt een string naar de compiler en krijgt een in-memory assembly terug, en daaruit kun je dan je class instantieren.
Omdat jouw code volgens mij vrij eenvoudig "bloksgewijs" opgebouwd kan worden, moet het niet zo heel moeilijk zijn om met wat strings de dingen aan elkaar te plakken.

Offtopic: Ik heb toevallig vorige week zelf ongeveer iets als dit gemaakt, maar dan een programma dat stukjes assembly-code genereert.

[ Voor 8% gewijzigd door Verwijderd op 09-12-2014 16:18 ]


  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Op diezelfde pagina staat ook iets over delagates, en hoe deze veel sneller zijn. Zou ik deze niet beter kunnen gebruiken in plaats van reflection? (wat is het verschil trouwens precies? De uitleg die ik tot dusver vond is niet heel duidelijk)

[ Voor 24% gewijzigd door huub8 op 09-12-2014 18:36 ]


Verwijderd

huub8 schreef op dinsdag 09 december 2014 @ 18:28:
Op diezelfde pagina staat ook iets over delagates, en hoe deze veel sneller zijn. Zou ik deze niet beter kunnen gebruiken in plaats van reflection?
Delegates zijn sneller dan reflection, maar dan heb je het over aanroepen per keer.
De methode die ik beschrijf heeft eenmalig reflection nodig om het object te instantieren nadat het gecompileerd is.
Als ie eenmaal gecompileerd is, kun je de methoden van de interface direct aanroepen, net als met 'gewone' objecten, zonder verdere reflection of performance penalty.

Verder zijn delegates iets anders dan wat we hier beschreven hebben.
Een delegate is in feite een 'function pointer'. Met delegates kun je 'handmatig' object-georienteerd programmeren, door functie-pointers mee te geven aan objecten (ipv dat je dat op de normale manier doet via subclassen).

Dat kan in bepaalde gevallen handig zijn, omdat je iets meer mogelijkheden hebt om dynamisch code te 'configureren'.
In jouw geval zou je een Agent dan kunnen modelleren als bv een lijst van delegates die uitgevoerd worden voor de Execute().
Maar die delegates zelf zijn nog steeds wel hardcoded.

De methode waar we het hierboven over hadden ging over het zelf 'schrijven' van de code, door strings aan elkaar te plakken en dan te compileren.
Hierdoor heb je dus meer flexibiliteit (de code kan ook via een UI 'live' ge-edit worden, of je kunt code van disk laden, of via netwerk-verbindingen uitwisselen etc).
Het is dus maar net wat je precies zoekt.

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Oke, duidelijk. Wat ik alleen nog niet helemaal begrijp is hoe dit dan moet:
Verwijderd schreef op dinsdag 09 december 2014 @ 10:18:
...
Als je met vooraf gedefinieerde interfaces werkt, is het vrij makkelijk om de instantie te casten naar een interface, en dan zonder verdere reflection methods aan te roepen etc. Het object werkt dan gewoon als ieder ander object dat die interface implementeert.

Op die manier kun je heel makkelijk een soort 'plugin'-systeem maken.
Dit is trouwens de relevante code die ik nu heb:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
string code2 = @"
                            using System;
                            using System.Collections.Generic;
                            using System.Text;
                            using System.Threading.Tasks;

                            namespace ConsoleApplication3
                            {
                                class MyAgent : IAgent
                                {

                                    public static void Main()
                                        {
                                        " +
                                            "Console.WriteLine(\"Hello, world!\");"
                                            + @"
                                        }

                                    public Agent()
                                    {
                                    }

                                    public override string ToString()
                                    {" +
                                        "return \"A\";" + @"
                                    }
                                }
                            }";
            

            CSharpCodeProvider provider = new CSharpCodeProvider();
            CompilerParameters parameters = new CompilerParameters();

            // True - memory generation, false - external file generation
            parameters.GenerateInMemory = true;
            // True - exe file generation, false - dll file generation
            parameters.GenerateExecutable = true;

            CompilerResults results = provider.CompileAssemblyFromSource(parameters, code2);
            

            if (results.Errors.HasErrors)
            {
                StringBuilder sb = new StringBuilder();

                foreach (CompilerError error in results.Errors)
                {
                    sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
                }

                throw new InvalidOperationException(sb.ToString());
            }

            
            Assembly assembly = results.CompiledAssembly;
            Type program = assembly.GetType("ConsoleApplication3.Agent");
            MethodInfo main = program.GetMethod("Main");
            MethodInfo tostring = program.GetMethod("ToString");


            main.Invoke(null, null);

  • Caelorum
  • Registratie: April 2005
  • Laatst online: 22:58
Durandal schreef op dinsdag 09 december 2014 @ 14:30:
[...] kwam ik toch uit op javascript als de beste oplossing. Vooral ook omdat dat nog aardig snel draait, je niet met de problemen van multithreadig zit en omdat js gemaakt is om in een container te draaien en je zo dus de rest van het systeem afschermt. [...]
Mag ik vragen waarom je niet voor iets als Lua/luajit bent gegaan?

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Ik was al begonnen met C#, en aangezien dit toch de eerste opzet is en ik het programma dus waarschijnlijk nog een keer helemaal over ga schrijven leek mij dit wel prima.

Dat casten naar de interface is mij trouwens nog niet gelukt.

Verwijderd

huub8 schreef op dinsdag 09 december 2014 @ 19:53:
Oke, duidelijk. Wat ik alleen nog niet helemaal begrijp is hoe dit dan moet:
Die code roept met reflection de Main() aan in de gegenereerde code.
Ik bedoelde dit:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;

namespace AgentTest
{
    public interface IAgent
    {
        void Initialize();
        void Execute();
    }

    class Program
    {
        static string buildAgent(string name, string init, string exec)
        {
            return "using System;\n"
                + "namespace AgentTest {\n"
                + "class " + name + " : IAgent\n{\n"
                + "public void Initialize(){" + init + "}\n"
                + "public void Execute(){" + exec + "}\n"
                + "\n}\n}";
        }

        static void Main(string[] args)
        {
            string code = buildAgent("MyAgent", "Console.WriteLine(\"MyAgent.Initialize()\");", "Console.WriteLine(\"MyAgent.Execute()\");");
            
            CSharpCodeProvider provider = new CSharpCodeProvider();
            CompilerParameters parameters = new CompilerParameters();

            // True - memory generation, false - external file generation
            parameters.GenerateInMemory = true;
            // True - exe file generation, false - dll file generation
            parameters.GenerateExecutable = false;

            // Add reference to the assembly that contains the IAgent definition
            parameters.ReferencedAssemblies.Add("AgentTest.exe");
            CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
            
            if (results.Errors.HasErrors)
            {
                StringBuilder sb = new StringBuilder();

                foreach (CompilerError error in results.Errors)
                {
                    sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
                }

                throw new InvalidOperationException(sb.ToString());
            }

            Assembly assembly = results.CompiledAssembly;
            IAgent agent = (IAgent)assembly.CreateInstance("AgentTest.MyAgent");

            agent.Initialize();
            agent.Execute();
        }
    }
}


Zoals je ziet, zodra je eenmaal een instance hebt gemaakt van de class, werkt ie als ieder ander object dat IAgent implementeert. Je roept dus gewoon Initalize() of Execute() aan zoals bij een 'gewoon' object.

  • Durandal
  • Registratie: Juli 2002
  • Laatst online: 15-11 15:41
Caelorum schreef op dinsdag 09 december 2014 @ 21:38:
[...]

Mag ik vragen waarom je niet voor iets als Lua/luajit bent gegaan?
Ja, LUA was een van de beter opties maar ik had toch een paar redenen
1) Ik heb een aantal WoW addons in lua gemaakt een paar jaar geleden en ik vindt het een vreselijke script taal. Dingen zoals geen onderscheid tussen ongedefinieerd en 0/false kosten je veel debug tijd en er zijn geen goede IDE's/debuggers (toendertijd i.i.g.). Taal is ook heel anders dan java dus dan zit je altijd met 2 denkwijzen te worstelen.
2) Geen native java ondersteuning, dus dan zal je LUA extern moeten aanroepen.
3) Je zit met sandboxing dat in JS er al in zit.
http://lua-users.org/wiki/SandBoxes
Buiten dat moet je er voor zorgen dat je memory en CPU resourses niet worden opgevreten.
4) gebruikers zullen eerder ervaring hebben met javascript dan met LUA.

Ben je ook met zoiets bezig?

PS: (Oracle) Scripting In Java

[ Voor 6% gewijzigd door Durandal op 09-12-2014 22:47 ]


  • Durandal
  • Registratie: Juli 2002
  • Laatst online: 15-11 15:41
..
Zoals je ziet, zodra je eenmaal een instance hebt gemaakt van de class, werkt ie als ieder ander object dat IAgent implementeert. Je roept dus gewoon Initalize() of Execute() aan zoals bij een 'gewoon' object.
Ik weet niet wat die genetic code allemaal gaat kunnen (en dat is nu juist het idee denk ik :D ) maar je moet het wel wat inperken. Geen threads, oneindige loops, aanmaken van te veel variabelen en andere resource vreters.

Verwijderd

Durandal schreef op dinsdag 09 december 2014 @ 22:51:
[...]
Ik weet niet wat die genetic code allemaal gaat kunnen (en dat is nu juist het idee denk ik :D ) maar je moet het wel wat inperken. Geen threads, oneindige loops, aanmaken van te veel variabelen en andere resource vreters.
Het *moet* niet... ligt er maar net aan waar je heen wil :)
In principe kun je alle kanten op (je kunt in een Execute() ook weer code opnemen die nieuwe dingen compileert :) (insert Xzibit of Inception-reference)).

  • indigo79
  • Registratie: Februari 2009
  • Niet online
Durandal schreef op dinsdag 09 december 2014 @ 22:51:
[...]

Ik weet niet wat die genetic code allemaal gaat kunnen (en dat is nu juist het idee denk ik :D ) maar je moet het wel wat inperken. Geen threads, oneindige loops, aanmaken van te veel variabelen en andere resource vreters.
Ik zou zoiets met Erlang/OTP doen, kwestie van elke agent juist wél in zijn eigen proces te laten draaien. Dan kan je het eenvoudig en efficiënt naar meerdere cores, cpus en hosts schalen.

Iets als LISP (of Scheme) waar code en data principieel hetzelfde zijn is ook een leuke en leerzame optie.

[ Voor 9% gewijzigd door indigo79 op 09-12-2014 23:06 ]


  • Durandal
  • Registratie: Juli 2002
  • Laatst online: 15-11 15:41
Tja, dat is ook waar, maar dat hangt dan weer een beetje van je budget af denk ik.

  • indigo79
  • Registratie: Februari 2009
  • Niet online
Zowel Erlang/OTP als Lisp (en z'n dialecten Scheme en Clojure dat op de JVM draait) zijn vrij beschikbaar, dus daarvoor hoef je het je niet te laten.

Als je op een groot aantal systemen parallel wil draaien, zit je uiteraard met een significante hardwarekost (kopen of huren in de cloud), maar zelfs een low-end smartphone heeft tegenwoordig een dual-core processor, dus een parallelle implementatie vind ik voor dit soort toepassingen niet zo gek.

Voor het parallel implementeren van dit soort systemen prefereer ik Erlang/OTP (maar je kan ook eens naar LFE en Elixir kijken als de syntax je niet aanstaat) omdat je daarmee zonder veel overhead een groot aantal processen (in de virtuele machine; geen echte OS processen) kan starten (en omdat je met het OTP framework relatief eenvoudig betrouwbare applicaties kan schrijven).

LISP wordt al sinds z'n beginjaren voor dit soort toepassingen gebruikt. Als je het functioneel programmerren echt onder de knie krijgt, ga je je soms wel features missen in de meer gangbare programmeertalen (http://www.joelonsoftware.com/items/2006/08/01.html)...

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Wat het inperken van de code betreft hoeft dit lijkt mij in mijn geval helemaal niet. Ik ben voor nu namelijk van plan om elke agent om de beurt de kans te geven een x aantal instructies te laten uitvoeren, en daarna wordt de beurt gewoon afgekapt. Om te zorgen dat agenten dan niet midden in een handeling worden afgekapt geef ik hen ook de kans om zelf de beurt op te geven via een instructie, wellicht dat ik ze nog een instructie geef die hen verteld hoe veel instructies ze nog kunnen uitvoeren binnen de beurt zodat ze hier gemakkelijker rekening mee kunnen houden maar dat zit er nu niet in.

Tevens kost het uitvoeren van elke instructie een beetje voedsel (ze gaan dood als ze geen voedsel meer hebben)(ik moet nog bepalen hoeveel elke instructie gaat kosten) en er komt een maximale lengte voor het programma.


Wat betreft parallelle executie, uiteraard wil ik dit (later) zeker gaan gebruiken, maar nu is het nog meer een kwestie van mijn idee uitschrijven in code om zo de details wat meer uit te werken. Maar als ik het later wel parallel zou gaan doen, dan wordt het nog best ingewikkeld om te zorgen dat het allemaal "veilig" gebeurt, aangezien de agenten in dezelfde leefwereld zitten en invloed op deze wereld en elkaar hebben, toch?

Nog 1 ding, hoe kan ik het beste de wereld bereikbaar maken voor de agenten, zodat ze dus kunnen zien hoe het ervoor staat. Kan ik dan het beste een wereld object maken en dat doorgeven aan de agent die op dat moment aan de beurt is, dat dan al dan niet aangepast door die agent weer wordt teruggegeven, of het beste een object maken dat via functies aan te roepen is vanuit de agenten (zo ja, hoe?)?

[ Voor 33% gewijzigd door huub8 op 10-12-2014 15:09 ]


  • indigo79
  • Registratie: Februari 2009
  • Niet online
Vermits je overweegt je simulatie later te parallelliseren (en dat lijkt me voor dit soort toepassingen zeker wenselijk, ik zou dat ook doen), zou ik sowieso alle shared-memory toestanden vermijden. Je kan dat in principe veilig doen met een mutex, maar 't is in mijn ervaring veel gevoeliger voor fouten (en veel moeilijker schaalbaar van een shared memory architectuur naar een distributed memory architectuur) dan een actor model (http://en.wikipedia.org/wiki/Actor_model; standaard gebruikt door onder andere Erlang/OTP).

Als je wereld een object is (of verschillende delen van je wereld verschillende objecten) en je gewoon via methods met je wereld communiceert, kan je je object later eenvoudig vervangen door een actor. Maar laat vooral de verschillende agents niet direct in hetzelfde geheugengebied rommelen en zorg dat je ofwel in een atomische handeling kijkt of je iets kan pakken en het direct ook pakt, of ga ervan uit dat dingen tussen kijken en pakken toch verdwenen kunnen zijn...

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Nog een gerelateerde vraag: ik heb dus een wereld (tweedimensionale array) met daarin lege velden en velden met hoogstens 1 agent. Verder heeft elk veld 3 int waardes. De agenten bezitten een MoveTo instucties waarin ze meegeven in welke richting ze willen bewegen, en ze kunnen informatie van agenten in de beurt opvragen (bijvoorbeeld van de agent links van hem). Tot slot moet ik van de agenten in in willekeurige volgorde de execute kunnen uitvoeren.

Als ik dus een array maar voor de wereld, met tupels met daarin de ints en de agent, dan moet ik dus de hele wereld (willekeurig) doorlopen op zoek naar agenten om zo hun execute uit te voeren. Als ik echter in de agenten zelf de locatie bewaar en een agent vraagt informatie over een agent links van hem dan moet ik de hele list van agenten doorzoeken naar een agent links van hem. De locatie in beiden bewaren (array en agent zelf) lijkt me niet netjes...

Verwijderd

huub8 schreef op vrijdag 12 december 2014 @ 13:05:
De locatie in beiden bewaren (array en agent zelf) lijkt me niet netjes...
Dat ligt eraan.
Als je die regel zo strikt zou opvatten, dan is ook bijvoorbeeld een doubly-linked list uit den boze.
In sommige gevallen moet je voor de efficientie wat concessies doen qua netheid/simpliciteit.

Ik denk dat het in dit geval niet zoveel kwaad kan om ieder vakje in het speelveld ook bij te laten houden welke agent zich daar bevindt.
Je moet alleen je MoveTo() consequent implementeren zodat zowel de link in de agent als in de vakjes netjes worden aangepast. Net als bij een doubly-linked list heb je dus het 'probleemgeval' geisoleerd in een heel beperkt deel van de code, dus dat blijft goed te managen.

Dan kun je heel efficient de buren vinden:
Je kunt nog een extra tabel maken die voor ieder vakje een lijst geeft van de aanliggende vakjes.
Dus je weet meteen welke vakjes je moet checken.

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Het werkt nu perfect maar nu zet ik er dus zelf het programma (in een string) in, terwijl deze moet worden opgebouwd bij de creatie van de agent aan de hand van het DNA. Het DNA ziet er bijvoorbeeld als volgt uit:

[instructioncode][id]( [instructioncode][id]( number, number ) , GoToIf( condition, id, jumpid, parent ) ) [instructioncode][id]( number, number)

waarbij elk stukje dus ofwel dit is:
[instructioncode][id]( parameter1, parameter2 )

of het is een GoToIf:
GoToIf( condition, id, jumpid, parent )

een GoToIf kan zelf ook ingevuld worden als parameter

Het idee is hierbij dat elke instuctie een instructioncode heeft die dus aangeeft wat voor instructie het is, en elk exemplaar van een instructie heeft een uniek id. bij mutaties kan ik dus gewoon een bit in de instructioncode aanpassen en dan wordt het een andere instructie.

Verder is er in het DNA nog 1 extra instructie mogelijk, namelijk de GoToIf, met 4 parameters:

- condition: een stuk programma, hier kan dus een (gewoon, net als als de agent niet voortplatn) programma worden uitgevoerd dat alleen wordt aangeroepen vanuit deze GoToIf
- id: het unieke id van dit exemplaar van de GoToIf
- jumpid: als de code in condition ongelijk is aan 0000 0000 dan word naar de instructie gesprongen met dit id
- parent: Geeft aan of naar de instructie met het jumpid moet worden gesprongen dat zich bevindt in zijn eigen dna, of dat van de ander, dat voor het maken van een kind kan worden opgevraagd van een agent als die agent zich naast hem bevindt.

Zo kan een agent dus bijvoorbeeld loops inbouwen in het dna door bijvoorbeeld een vorm van een counter te implementeren in de condition van de GoToIf, of er kunnen meerdere geslachten ontstaan waarbij bepaalde stukken dna voor het ene geslacht overgeslagen en andere juist gelezen worden.

Mijn punt is nu, hoe sla ik dit dna het best op zodat het makkelijk kan worden gelezen en omgezet in code. Het is eigenlijk dus een soort boom, dus ik dacht dat jullie waarschijnlijk wel tips zouden hebben. Ook opmerkingen over de werking van het DNA zoals hier beschreven zijn natuurlijk welkom.

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Dus eigenlijk is mijn vraag hoe ik het efficiëntst een soort boom kan opslaan van alle instructies en hun parameters, die eventueel ook weer instructies zijn. Zodat ik het geheel makkelijk kan doorlopen om zo de namen van de instructies en hun parameters aan een string toe te kunnen voegen, en ik makkelijk een bepaalde instructie voor een andere kan verwisselen.

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Ik heb bovenstaande vraag zelf opgelost door een list met voor iedere instructie een unieke code, de parameters en de code voor het type functie te maken (in de vorm van een list van structs). De lijst van parameters bevat dus de unieke codes van de functies die daar zouden moeten staan (er is ook een number functie om gewone waardes te kunnen krijgen). Via een recursieve functie worden vervolgens alle parameters (en hun parameters etc) doorlopen.

Waar ik nu echter mee zit is dat ik ook een IfThen instructie wil hebben. Die neemt twee parameters, de conditie en de 'then' code, en werkt als volgt:
Hij evalueert de conditie, als dit anders (dus groter dan) 0 blijkt dan moet de then worden ge-evalueert, anders moet er simpelweg direct 0 terug worden gegeven en de then parameter moet dus helemaal niet worden ge-evalueert.

Hoe kan ik dit maken? Eigenlijk zou ik iets als dit willen kunnen schrijven:
MoveTo( IfThen ( 0, MoveTo (1) ) );

Hierbij moet de tweede MoveTo dus helemaal niet worden uitgevoerd, wat echter wel automatisch gebeurt toch?

Ik kan wel bij het opbouwen van het programma de IfThen instructie herschrijven naar een gewone
If(conditie){return conditie;}
else { return 'then';}

maar dit kan dan natuurlijk weer niet zomaar ingevuld worden als argument voor een functie parameter:
MoveTo( If(conditie){return conditie;} else { return 'then';} );

Is er een andere manier om dit wel voor elkaar te krijgen? Dus dat ik het toch op zo'n soort manier in een functie zou kunnen invullen? Als ik dit niet kan moet ik namelijk de recursieve code voor het opbouwen van het programma behoorlijk uitbreiden.

  • huub8
  • Registratie: Maart 2009
  • Laatst online: 28-06-2021
Ik heb het nu zo gedaan dat elke IfThen bij het "parsen" van de instructies in een aanroep naar een unieke nieuwe functie wordt omgezet, deze aanroep eindigt dan in de execute functie, en dus die unieke nieuwe functie (bv IfThen8(byte conditie)). Deze nieuwe functie komt dan onderin de classe terecht en heeft als het ware hardcoded de if statement:

"public byte IfThen8(byte condition) {if(condition > 0){return CreateChild(RandomByte(240));} else{ return 0; }}"

Misschien niet de mooiste oplossing, maar het werkt.

Maar een nieuw echt probleem is dat het compileren van de de nieuwe agents (dus elke keer als een agent een kind maakt) veel te lang duurt. Is het mogelijk om dit nog ver terug te brengen (kan ik ergens opties van het compilen veranderen bijvoorbeeld? Ik begin delegates langzaam te begrijpen, maar zijn deze voor mij geschikt en substantieel sneller? Zijn er nog andere opties?
Pagina: 1