[1089] | 1 | """Simple examples of using the "frams" module to communicate directly with the Framsticks library (dll/so).
|
---|
| 2 |
|
---|
| 3 | For an introduction to Framsticks, its usage and scripting, see https://www.youtube.com/playlist?list=PLkPlXm7pOPatTl3_Gecx8ZaCVGeH4UV1L
|
---|
| 4 | For a list of available classes, objects, methods and fields, see http://www.framsticks.com/files/classdoc/
|
---|
[1162] | 5 | For a number of examples of scripting, see the "scripts" directory in the Framsticks distribution."""
|
---|
[1089] | 6 |
|
---|
[1081] | 7 | import sys
|
---|
[1103] | 8 | import json
|
---|
[1078] | 9 | import frams
|
---|
| 10 |
|
---|
[1087] | 11 | frams.init(*(sys.argv[1:])) # pass whatever args we have, init() is the right place to deal with different scenarios:
|
---|
[1085] | 12 | # frams.init() - should try to figure out everything (and might fail)
|
---|
[1087] | 13 | # frams.init('path/to/lib') - load the library from the specified directory and configure Framsticks path as "data" inside this directory
|
---|
| 14 | # frams.init('path/to/lib','-d/tmp/workdir/data') - as above, but set the working (writable) directory somewhere else (see also -D)
|
---|
| 15 | # frams.init('path/to/lib','-Lframs-objects-alt.dll') - use specified library location and non-default file name
|
---|
[1078] | 16 |
|
---|
| 17 | print('Available objects:', dir(frams))
|
---|
| 18 | print()
|
---|
| 19 |
|
---|
| 20 |
|
---|
| 21 | def extValueDetails(v):
|
---|
[1089] | 22 | """A helper function to display basic information about a variable of type ExtValue."""
|
---|
| 23 | return '\t"' + str(v) + '" frams type=' + str(v._type()) + ' frams class=' + str(v._class()) + ' python type=' + str(type(v._value()))
|
---|
[1078] | 24 |
|
---|
| 25 |
|
---|
[1089] | 26 | dic_as_string = '[100,2.2,"abc",[null,[],{}],XYZ[9,8,7]]'
|
---|
| 27 | print("We have the following string:\n\t'%s'" % dic_as_string)
|
---|
| 28 | print("Looks like a serialized dictionary, let's ask Framsticks String.deserialize() to do its job.")
|
---|
| 29 | v = frams.String.deserialize(dic_as_string)
|
---|
| 30 | print("Framsticks String.deserialize() returned\n\t", type(v))
|
---|
| 31 | print("More specifically, it is:")
|
---|
[1078] | 32 | print(extValueDetails(v))
|
---|
[1089] | 33 | print("Even though it is ExtValue (Framsticks' Vector), it supports iteration like a python vector, so let's inspect its elements:")
|
---|
[1078] | 34 | for e in v:
|
---|
| 35 | print(extValueDetails(e))
|
---|
| 36 |
|
---|
[1114] | 37 | print("\nNow let's play with the Framsticks simulator. Let's create a Genotype object and set fields in its custom 'data' dictionary.")
|
---|
[1078] | 38 | g = frams.GenePools[0].add('X')
|
---|
[1089] | 39 | g.name = "Snakis Py"
|
---|
[1078] | 40 | g.data['custom'] = 123.456
|
---|
[1089] | 41 | g.data['a'] = 'b' # implicit conversion, looks like python dictionary but still converts '3' and '4' to ExtValue
|
---|
| 42 | dic = frams.Dictionary.new() # let's create a Dictionary object from Framsticks
|
---|
| 43 | dic.set('1', '2') # calling set() from Framsticks Dictionary
|
---|
| 44 | dic['3'] = '4' # implicit conversion, looks like python dictionary but still converts '3' and '4' to ExtValue
|
---|
| 45 | g.data['d'] = dic
|
---|
| 46 | print(extValueDetails(g))
|
---|
[1078] | 47 |
|
---|
[1089] | 48 | print("Let's add a few mutants and display their data:")
|
---|
[1078] | 49 | for more in range(5):
|
---|
| 50 | frams.GenePools[0].add(frams.GenMan.mutate(g.geno))
|
---|
| 51 |
|
---|
| 52 | for g in frams.GenePools[0]:
|
---|
[1089] | 53 | print("\t%d. name='%s'\tgenotype='%s'\tdata=%s" % (g.index._value(), str(g.name), str(g.genotype), str(g.data)))
|
---|
| 54 |
|
---|
| 55 | print("Let's now change some property of the simulation. Current water level is", frams.World.wrldwat)
|
---|
| 56 | frams.World.wrldwat = 0.5
|
---|
| 57 | print("Now water level is", frams.World.wrldwat)
|
---|
| 58 | frams.World.wrldwat = frams.World.wrldwat._value() + 0.7
|
---|
| 59 | print("Now water level is", frams.World.wrldwat)
|
---|
| 60 |
|
---|
[1092] | 61 | initial_genotype = 'X(X,RX(X[T],X[G]))' # simple body with touch and gyroscope sensors
|
---|
[1089] | 62 | print("Let's perform a few simulation steps of the initial genotype:", initial_genotype)
|
---|
| 63 | frams.ExpProperties.initialgen = initial_genotype
|
---|
| 64 | frams.ExpProperties.p_mut = 0 # no mutation (the selection procedure will clone our initial genotype)
|
---|
| 65 | frams.ExpProperties.p_xov = 0 # no crossover (the selection procedure will clone our initial genotype)
|
---|
| 66 | frams.Populations[0].initial_nn_active = 1 # immediate simulation of neural network - no "waiting for stabilization" period
|
---|
| 67 | frams.World.wrldg = 5 # gravity=5x default, let it fall quickly
|
---|
| 68 |
|
---|
| 69 | frams.Simulator.init() # adds initial_genotype to gene pool (calls onInit() from standard.expdef)
|
---|
| 70 | frams.Simulator.start() # this does not actually start the simulation, just sets the "Simulator.running" status variable
|
---|
| 71 | step = frams.Simulator.step # cache reference to avoid repeated lookup in the loop (just for performance)
|
---|
| 72 | for s in range(15):
|
---|
| 73 | step() # first step performs selection and revives one genotype according to standard.expdef rules
|
---|
| 74 | creature = frams.Populations[0][0] # FramScript Creature object
|
---|
| 75 | mechpart0 = creature.getMechPart(0)
|
---|
| 76 | print('Step# = %d' % frams.Simulator.stepNumber._value(),
|
---|
| 77 | '\tSimulated_creatures =', frams.Populations[0].size._value(),
|
---|
| 78 | "\tpart0_xyz = (% .2f,% .2f,% .2f)" % (mechpart0.x._value(), mechpart0.y._value(), mechpart0.z._value()),
|
---|
| 79 | "\ttouch = % .3f\tgyro = % .3f" % (creature.getNeuro(0).state._value(), creature.getNeuro(1).state._value()))
|
---|
| 80 | frams.Simulator.stop()
|
---|
| 81 |
|
---|
[1103] | 82 | # changing expdef
|
---|
| 83 | testgenotype = "XrrX[G][-1:80][|,-1:0.9]X[|,-2:-21]"
|
---|
| 84 | evaluations = 100
|
---|
| 85 | print("\nLet's change the experiment definition (expdef) and evaluate genotype '%s' %d times." % (testgenotype, evaluations))
|
---|
| 86 | frams.Simulator.expdef = "standard-eval"
|
---|
| 87 | frams.ExpProperties.evalcount = evaluations
|
---|
| 88 | frams.ExpProperties.cr_v = 1
|
---|
| 89 | frams.ExpProperties.evalplan = ":velocity,vertpos,fit_stdev,time"
|
---|
| 90 | frams.GenePools[0].add(testgenotype)
|
---|
| 91 | frams.ExpProperties.evalsavefile = "" # no need to store results in a file - we will get evaluations directly from Genotype's "data" field
|
---|
| 92 | frams.Simulator.init()
|
---|
| 93 | frams.Simulator.start()
|
---|
[1116] | 94 | # step = frams.Simulator.step # cache reference to avoid repeated lookup in the loop (just for performance)
|
---|
| 95 | # while frams.Simulator.running._int(): # standard-eval.expdef sets running to 0 when the evaluation is complete
|
---|
| 96 | # step()
|
---|
| 97 | frams.Simulator.eval("while(Simulator.running) Simulator.step();") # loop in FramScript much faster than loop in python
|
---|
[1103] | 98 | for g in frams.GenePools[0]: # loop over all genotypes, even though we know we added only one
|
---|
| 99 | serialized_dict = frams.String.serialize(g.data[frams.ExpProperties.evalsavedata._value()])
|
---|
| 100 | print(json.loads(serialized_dict._string()))
|
---|
| 101 |
|
---|
[1112] | 102 | # sampling a Model in 3D
|
---|
| 103 | geno = "RXX(X,CXXX)"
|
---|
[1183] | 104 | print("\nNow build a Model from the genotype '%s' and sample it in 3D, then print a 2D projection." % geno)
|
---|
[1112] | 105 | import numpy as np
|
---|
| 106 |
|
---|
| 107 | matrix = np.zeros((20, 20, 20), dtype=int) # 3D matrix, "voxels"
|
---|
[1195] | 108 | m = frams.ModelGeometry.forModel(frams.Model.newFromString(geno))
|
---|
| 109 | m.geom_density = 20
|
---|
[1112] | 110 | for p in m.voxels():
|
---|
| 111 | # print('%f %f %f ' % (p.x._value(), p.y._value(), p.z._value()))
|
---|
| 112 | matrix[int(p.x._value() * 5 + 2), int(p.y._value() * 5 + 5), int(p.z._value() * 5 + 6)] += 1 # scaling and offsets adjusted manually to fit the matrix nicely
|
---|
| 113 | matrix = np.sum(matrix, axis=1) # sum along axis, make 2D from 3D ("projection")
|
---|
| 114 | np.set_printoptions(formatter={'int': lambda x: ('.' if x == 0 else str(x // 18))}) # print zeros as dots, x//18 to fit a larger range into a single digit
|
---|
| 115 | print(matrix)
|
---|
| 116 | np.set_printoptions() # avoids straange python errors: frams.py, line 48, in __del__ AttributeError: 'NoneType' object has no attribute 'extFree'
|
---|
| 117 |
|
---|
| 118 | #
|
---|
| 119 | #
|
---|
[1089] | 120 | # Note that implementing a complete expdef, especially a complex one, entirely in python may be inconvenient or impractical
|
---|
| 121 | # because you do not have access to "event handlers" like you have in FramScript - onStep(), onBorn(), onDied(), onCollision() etc.,
|
---|
| 122 | # so you would have to check various conditions in python in each simulation step to achieve the same effect.
|
---|