snotite-ai-brownian-walker.js

total 0
used 0
limit 0
/* title: Brownian Walker categories: brownian random files: ../theatre/brain.js head point pointlist stage mouse dragging stroke ../point_src/random.js ../point_src/functions/range.js ../point_src/relative.js ../point_src/constrain-distance.js ../point_src/screenwrap.js --- A tiny walker to move towards a browian point. */ // const trainingData = [ // [0.93,0.93,.34,.34,0.33,0.63,.53,.53,.01], // [0.93,.34,0.63,0.63,.53,.01], // [.01,.01,.01], // [.34,.34,0.93,0.93,0.63,0.63,.53,.53,.34,.77] // ]; // const lstm = new brain.recurrent.LSTM(); // const result = lstm.train(trainingData, { // iterations: 15, // log: details => console.log(details), // errorThresh: 0.011 // }); // const run1 = lstm.run(0.01); // const run2 = lstm.run(.9); // const run3 = lstm.run(.53); // const run4 = lstm.run(.7); // console.log('run 1:', run1); // console.log('run 2:', run2); // console.log('run 3:', run3); // console.log('run 4:', run4); let fleeSettings = { tailLength: 3, turnSpeed: .18, maxTouchDistance: 6, viewSpaceOffset: 10, viewSpaceMultiplerSize: 3, forwardSpeed: 1.7, leashDistanceMultiplier: 2, radius: 1.2, color: 'green', } const sliderValue = function(value, min, max, step) { let d = {} if(step != undefined) { d.step = step } if(value != undefined) { d.value = value } if(min != undefined) { d.min = min } if(max != undefined) { d.max = max } return d; } const sliders = { tailLength: sliderValue( 3, 2, 40) , turnSpeed: sliderValue( .18, .1, 1, .05) , maxTouchDistance: sliderValue( 6, 6, 20) , viewSpaceOffset: sliderValue( 10, 10, 30) , viewSpaceMultiplerSize: sliderValue( 3, 2, 20) , forwardSpeed: sliderValue( 1.7, .1, 3, .10) , leashDistanceMultiplier: sliderValue( 2, 1.5, 20, .2) , radius: { step: .10, value: 1.2, min: 1, max: 20 } }; addSliderControlSet(sliders); addButton('Generate', { onclick: ()=> { let d = {} for(let k in sliders) { d[k] = parseFloat(appShared.miniApp.controls[k].value) }; stage.newWalker(Object.assign(d, { x: 300, y: 300})) } }) class Walker extends Point { init(d) { this.updateSpeed = 20 this.lookSpeedUpdateSpeed = 10 this.touchSpaceUpdateSpeed = 10 this.forwardSpeed = 1 this.turnSpeed = .2 this.maxTouchDistance = 10 this.viewSpaceOffset = -20 this.viewSpaceMultiplerSize = 10 this.modu = d.modu ==undefined? 0: d.modu this.leashDistanceMultiplier = 1 this.tailLength = 10 this.viewPoint = new Point(this.copy()) this.update(d) this.updateLookSpace() } step(delta=1){ this.modu += 1 if(this.modu % this.updateSpeed == 0) { // this.updateWalker() } if(this.modu % this.lookSpeedUpdateSpeed == 0) { // this.updateLookSpace() } if(this.modu % this.touchSpaceUpdateSpeed == 0) { // this.updateTouchSpace() } // this.turnTo(this.viewPoint, this.turnSpeed * delta) // this._relativeMove(this, new Point({x:1, y:0}), 1, ) // this.relative.forward(this.forwardSpeed * delta) } updateTouchSpace(){ if(this.distanceTo(this.viewPoint) < this.maxTouchDistance) { this.updateWalker() } } updateWalker(max=.5) { this.updateLookSpace() // this.viewPoint.xy = random.within(this.viewSpace, max) } updateLookSpace() { // let eyeballSpace = this.viewSpaceOffset // let viewSpaceSize = this.viewSpaceMultiplerSize // let r = this.radius * viewSpaceSize // this.viewSpace = this.project( // r // + this.radius // + eyeballSpace // ).update({radius: r}) } } const DummyBrain = { run(inputs){ // inputs = array of [-1,1] const [bearing, dist, touch] = inputs; const turn = 0.9 * bearing; const throttle = 0.6 * (1 - (dist+1)/2); // farther→faster return [turn, throttle]; // both in [-1,1] } }; const TrainedBrain = { run(input) { } } class TailWalker extends Walker { init(d) { super.init(d) this.points = PointList.generate.countOf(this.tailLength) this.points.each.radius = this.radius * .8 this.points.each.xy = this.xy this.points.unshift(this) this.sensors = [ new BearingToFood(), new DistToFood(100), new TouchFood() ]; this.sensors.forEach(s => s.init(this)); this.brain = DummyBrain } step(delta=1){ super.step(delta) this.updateLeash() this.updateBrain() } updateLeash(){ let h = this let leashLength = h.radius * h.leashDistanceMultiplier h.points.siblings().forEach((pair)=>{ // pair[1].track(pair[0], leashLength) pair[1].leash(pair[0], leashLength) }) } updateBrain(){ // new BearingToFood() // new DistToFood(100) // new TouchFood() const x = this.getInputs(); const y = this.brain.run(x); this.sensorString = x.map((z)=>z.toFixed(2).padStart(6)).join(', ') this.applyOutputs(y); } getInputs(){ return this.sensors.map(s => s.read() + sim.noise(.1)) } // e.g. [b,d,t] applyOutputs([turn, throttle]){ const clamp = (x,m=1)=>Math.max(-m,Math.min(m,x)); let r = clamp(turn) * this.turnSpeed; this.radians += r // this.turnTo(this.viewPoint, r) const speed = this.forwardSpeed * (0.5 + 0.5 * clamp(throttle)); // const speed = this.forwardSpeed * (0.5 + 0.5 * clamp(throttle)); this.relative.forward(speed); } } // returns a value in [-1, 1] class Sensor { init(host){ this.h = host } // keep a ref to snotite read(){ return 0 } // override } const sim = { food: new Point(300,300, 10) , noise: (v=.1)=> random.float(-1, 1) * v } class BearingToFood extends Sensor { read(){ const f = sim.food; if(!f) return 0; const a = Math.atan2(f.y - this.h.y, f.x - this.h.x) - this.h.radians; let b = (a + Math.PI) % (2*Math.PI); b = b < 0 ? b + 2*Math.PI : b; b -= Math.PI; return b / Math.PI; // [-1,1] } } class DistToFood extends Sensor { constructor(R=100){ super(); this.R = R } read(){ const f = sim.food; if(!f) return 0; const d = Math.hypot(f.x - this.h.x, f.y - this.h.y); return Math.max(-1, Math.min(1, (d/this.R)*2 - 1)); // far→+1, near→-1 } } class TouchFood extends Sensor { read(){ const f = sim.food; if(!f) return -1; return this.h.distanceTo(f) <= (this.h.radius + 10 + f.radius) ? 1 : -1; } } sim.samples = []; // {input:[bearing,dist,touch], output:[turn,throttle]} function tickWithRecorder(walker){ const x = walker.getInputs(); // already in [-1,1] const y = DummyBrain.run(x); // your current rule sim.samples.push({ input: x, output: y }); walker.applyOutputs(y); } const newNet = function(){ const net = new brain.NeuralNetwork({ inputSize: 3, // bearing, dist, touch hiddenLayers: [5, 7, 4], // tiny outputSize: 2 // turn, throttle }); return net; } const trainNet = function(net, samples=sim.samples, iterations=2000, learningRate=0.01) { net.train(samples, { iterations: iterations, learningRate, errorThresh: 0.005, log: true }); } class MainStage extends Stage { canvas = 'playspace' mounted(){ // this.screenWrap = new ScreenWrap() this.screenWrap.setDimensions({top: -50, left: -50, bottom: 900, right: 900}) this.delta = 1 this.doClear = true let s = this addButton('add', { onclick(){ s.newWalker(Object.assign({ x: 300, y: 300})) } }) addButton('add flee', { onclick(){ s.newWalker(Object.assign(fleeSettings, { x: 300, y: 300})) } }) addButton('clear', { onclick(){ s.walkers = new PointList() } }) this.walkers = new PointList() this.expertWalker = this.newWalker({ leashDistanceMultiplier: 1 , tailLength: 10 , forwardSpeed: 1.3 , turnSpeed: .8 , leashDistanceMultiplier: 3 , radius: 9 }) this.childWalker = this.newWalker({ leashDistanceMultiplier: 1 , tailLength: 7 , leashDistanceMultiplier: 8 , forwardSpeed: 1 , turnSpeed: .8 , radius: 1 }) // this.childWalker.brain = newNet() this.walkers.push(this.expertWalker, this.childWalker) this.recordSamples = false this.trainTicker = 0 this.drainCount = 0 this.dragging.add(sim.food) } freezeMode(){ this.delta = .1 this.doClear = false } startTrainChild() { let net = this.net; if(this.net == undefined) { this.net = net = newNet() } this.recordSamples = true } stopTrainChild(){ this.recordSamples = false this.drainSamples() } setChildBrain() { this.childWalker.brain = this.net } unsetChildBrain() { this.childWalker.brain = DummyBrain } newWalker(extra) { let w = this.generateWalker(this.genD(), extra) this.walkers.push(w) return w } onMousedown(ev){ tickWithRecorder(this.walkers[0]) sim.food.xy = Point.from(ev).xy } genD(){ let ri = random.int.bind(random); let rf = random.float.bind(random); let d = { lookSpeedUpdateSpeed: 10 , touchSpaceUpdateSpeed: 10 , updateSpeed: 50 , forwardSpeed: rf(.1, 2) , turnSpeed: rf(.1, .3) , maxTouchDistance: ri(6, 20) , viewSpaceOffset: ri(10, 30) , viewSpaceMultiplerSize: ri(2,10) , leashDistanceMultiplier: ri(1.5,7) , tailLength: ri(2,5) } return d } generateWalker(d, d2) { d = Object.assign({}, d) let p = new TailWalker({ x:random.int(500) , y:random.int(500) , radius: random.float(.5, 3) , rotation: random.int(160) , color: random.color() , modu: i }) p.init(Object.assign(d, d2)); return p } stepPerformTrainChild() { if(!this.recordSamples) { return } this.trainTicker++ if(this.trainTicker % 5) { tickWithRecorder(this.expertWalker) } if(sim.samples.length > 100) { this.drainSamples() } } drainSamples() { console.log('Drain Train') trainNet(this.net, sim.samples, 1000, .05) this.onDrain && this.onDrain() sim.samples = [] this.drainCount += 1 } printExample(x){ x = x || this.childWalker.getInputs(); console.log('input ', x); console.log('expert', this.expertWalker.brain.run(x)) console.log('result', Array.from(this.childWalker.brain.run(x))) } enableChild() { /* start training. Swap after drain */ this.onDrain = () => { if(this.drainCount > 1) { console.log('Switching Brain') // this.childWalker.brain = this.net this.setChildBrain() this.onDrain = undefined } } this.startTrainChild() } onDrain() {} draw(ctx) { this.doClear && this.clear(ctx) let food = sim.food let foodHit = false this.stepPerformTrainChild() this.walkers.forEach(p=>{ this.step(p, this.delta) this.drawPoint(ctx, p) if(food.distanceTo(p) < food.radius) { foodHit = true } }) if(foodHit) { sim.food.radius *= .97 } sim.food.pen.fill(ctx, foodHit? 'red': 'green') if(food.radius < 5) { sim.food.xy = random.point(600) food.radius = random.int(7, 12) } } drawPoint(ctx, h){ ctx.lineWidth = 1 // h.viewSpace.pen.circle(ctx, {color: '#222255'}) // h.viewPoint.pen.fill(ctx, 'red', 1) ctx.lineWidth = h.radius h.points?.pen.indicator(ctx, h.color, false) // h.points?.pen.quadCurve(ctx, h.color, false) // h.points?.pen.fill(ctx, '#aaa', 1) // h.pen.fill(ctx, h.color) h.pen.indicator(ctx, h.color) let sws = h.screenWrapSector if(sws?.dirty) { h.screenWrapSector_cache = `${sws.x}, ${sws.y}` } // h.text.offsetString(ctx, h.screenWrapSector_cache, {x:-10, y:-10}) // h.text.string(ctx, h.sensorString) } step(p, delta=1){ let didChange = this.screenWrap.perform(p) p.step(delta) if(didChange) { p.updateWalker(.5) } } } stage = MainStage.go(/*{ loop: true }*/)
Run
Meta Data
title Brownian Walker
imports ()
files ('../theatre/brain.js', 'head', 'point', 'pointlist', 'stage', 'mouse', 'dragging', 'stroke', '../point_src/random.js', '../point_src/functions/range.js', '../point_src/relative.js', '../point_src/constrain-distance.js', '../point_src/screenwrap.js')
unused_keys ()
unknown_keys ('categories',)
categories ['brownian', 'random']
filepath_exists True
path snotite-ai-brownian-walker.js
filepath snotite-ai-brownian-walker.js
clean_files ('../theatre/brain.js', '../point_src/core/head.js', '../point_src/pointpen.js', '../point_src/compass.js', '../point_src/center.js', '../point_src/point-content.js', '../point_src/pointdraw.js', '../point_src/relative-xy.js', '../point_src/pointcast.js', '../point_src/point.js', '../point_src/pointlistdraw.js', '../point_src/pointlistgradient.js', '../point_src/pointlistshape.js', '../point_src/pointlistgenerator.js', '../point_src/unpack.js', '../point_src/pointlist.js', '../point_src/pointlistpen.js', '../point_src/stage-resize.js', '../point_src/functions/resolve.js', '../point_src/stage.js', '../point_src/events.js', '../point_src/automouse.js', '../point_src/functions/clamp.js', '../point_src/distances.js', '../point_src/protractor.js', '../point_src/text/beta.js', '../point_src/dragging.js', '../point_src/setunset.js', '../point_src/stroke.js', '../point_src/random.js', '../point_src/functions/range.js', '../point_src/relative.js', '../point_src/constrain-distance.js', '../point_src/screenwrap.js')
markdown {'html': '<p>A tiny walker to move towards a browian point.</p>', 'content': 'title: Brownian Walker\ncategories: brownian\n random\nfiles:\n ../theatre/brain.js\n head\n point\n pointlist\n stage\n mouse\n dragging\n stroke\n ../point_src/random.js\n ../point_src/functions/range.js\n ../point_src/relative.js\n ../point_src/constrain-distance.js\n ../point_src/screenwrap.js\n---\n\nA tiny walker to move towards a browian point.'}