diff --git a/go.mod b/go.mod index bc08d84..4c93b52 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module edgaru089.ink/go/gl01 -go 1.17 +go 1.25 require ( github.com/aquilax/go-perlin v1.1.0 diff --git a/internal/io/io.go b/internal/io/io.go index da63c2f..8e3d3e8 100644 --- a/internal/io/io.go +++ b/internal/io/io.go @@ -28,4 +28,6 @@ var ( } } } + + StepFactors []float64 ) diff --git a/internal/util/slice.go b/internal/util/slice.go new file mode 100644 index 0000000..ffd491f --- /dev/null +++ b/internal/util/slice.go @@ -0,0 +1,9 @@ +package util + +func Slice2Float32[T int | float64](s []T) (sf []float32) { + sf = make([]float32, len(s)) + for i := range s { + sf[i] = float32(s[i]) + } + return +} diff --git a/internal/work/work.go b/internal/work/work.go index a13f9fd..9934132 100644 --- a/internal/work/work.go +++ b/internal/work/work.go @@ -1,11 +1,144 @@ package work -import "time" +import ( + "math" + "slices" + "time" + + "edgaru089.ink/go/gl01/internal/igwrap" + "edgaru089.ink/go/gl01/internal/io" + "github.com/inkyblackness/imgui-go/v4" +) + +var state struct { + inst [2000]*WorkerInstance + buf []*WorkerInstance + + steps int + + first_step int + + iter_disabled bool +} + +type iter_info struct { + first_step, last_step int + step_count int + + max_score float64 + + factor float64 +} + +var stats []iter_info func Init() { - + for i := range state.inst { + inst := RandomInstance() + state.inst[i] = &inst + } } func Update(delta time.Duration) { + state.steps++ + + for i := range state.inst { + state.inst[i].Step(state.steps) + } + + slices.SortFunc(state.inst[:], func(x, y *WorkerInstance) int { + if x.score > y.score { + return -1 + } else if x.score == y.score && x.pos.Sub(x.target).Len() < y.pos.Sub(y.target).Len() { + return -1 + } else { + return 1 + } + }) + + if imgui.Checkbox("Disable Iteration", &state.iter_disabled) { + + } + + if !state.iter_disabled && (imgui.Button("Iterate") || state.inst[50].score > 0) { + + factor := 0.0 + for _, f := range io.StepFactors { + factor += f + } + factor /= float64(len(io.StepFactors)) + io.StepFactors = io.StepFactors[0:0] + + // push stats + stats = append(stats, iter_info{ + first_step: state.first_step, + last_step: state.steps, + step_count: state.steps - state.first_step, + max_score: state.inst[0].score, + factor: factor, + }) + state.first_step = state.steps + + state.buf = state.buf[:0] + for i := range 50 { + state.inst[i].score = 0 + state.buf = append(state.buf, state.inst[i]) + for range 2000/50 - 1 { + inst := state.inst[i].Offspring() + inst.step0 = state.steps + state.buf = append(state.buf, &inst) + } + } + copy(state.inst[:], state.buf) + } + + for i := range 20 { + igwrap.Text("Score: %f\n", state.inst[i].score) + } + + if igwrap.Begin("Graph", nil, 0) { + imgui.PushItemWidth(-math.SmallestNonzeroFloat32) + s := make([]float32, len(stats)) + if len(stats) < 1 { + goto err + } + + for i, st := range stats { + s[i] = float32(st.step_count) + } + if len(s) > 400 { + s = s[len(s)-400:] + } + imgui.PlotLinesV( + "##Step Count", + s, + 0, + "", + math.MaxFloat32, + math.MaxFloat32, + imgui.Vec2{0, 200}, + ) + + s = make([]float32, len(stats)) + for i, st := range stats { + s[i] = float32(st.factor) + } + if len(s) > 400 { + s = s[len(s)-400:] + } + imgui.PlotLinesV( + "##Factor", + s, + 0, + "", + math.MaxFloat32, + math.MaxFloat32, + imgui.Vec2{0, 200}, + ) + + err: + imgui.PopItemWidth() + imgui.End() + } } diff --git a/internal/work/worker.go b/internal/work/worker.go new file mode 100644 index 0000000..746a441 --- /dev/null +++ b/internal/work/worker.go @@ -0,0 +1,122 @@ +package work + +import ( + "math/rand" + + "edgaru089.ink/go/gl01/internal/io" + "github.com/go-gl/mathgl/mgl64" +) + +type Worker struct { + t1, t2 mgl64.Mat4 + t3 mgl64.Mat2x4 +} + +func randomSlice64(s []float64) { + for i := range s { + s[i] = rand.NormFloat64() + } +} + +// RandomWorker generates a random worker. +func RandomWorker() (w Worker) { + randomSlice64(w.t1[:]) + randomSlice64(w.t2[:]) + randomSlice64(w.t3[:]) + return +} + +func mutateSlice64(s []float64, stddev float64) { + for i := range s { + s[i] = rand.NormFloat64()*stddev + s[i] + } +} + +// Offspring generates an offspring with the given variance. +func (w *Worker) Offspring(stddev float64) (s Worker) { + s = *w + mutateSlice64(s.t1[:], stddev) + mutateSlice64(s.t2[:], stddev) + mutateSlice64(s.t3[:], stddev) + return +} + +// Calculate runs a Vec4 through the Worker. +func (w *Worker) Calculate(input mgl64.Vec4) (output mgl64.Vec2) { + imm1 := w.t1.Mul4x1(input) + imm2 := w.t2.Mul4x1(imm1) + output = w.t3.Mul4x1(imm2) + return +} + +// WorkerInstance is a instance of a worker. +type WorkerInstance struct { + worker Worker + pos mgl64.Vec2 + target mgl64.Vec2 + + score float64 + + distance float64 + step0 int +} + +var ( + Radius float64 = 10 // Radius of all worker instances + Step float64 = 1 // Move length of a advance step + + OffspringStdVar float64 = 0.5 + + Score float64 = 1 + + TargetAreaSize = mgl64.Vec2{750, 750} +) + +func randomTarget() (t mgl64.Vec2) { + return mgl64.Vec2{ + TargetAreaSize[0] * (rand.Float64() - .5), + TargetAreaSize[1] * (rand.Float64() - .5), + } +} + +func (i *WorkerInstance) Step(step_num int) { + output := i.worker.Calculate(mgl64.Vec4{ + i.pos[0], + i.pos[1], + i.target[0], + i.target[1], + }) + + direction := mgl64.Vec2{output[0], output[1]}.Normalize() + step := direction.Mul(Step) + + i.pos = i.pos.Add(step) + if i.pos.Sub(i.target).Len() < Radius { + // Score! + i.score += Score + + io.StepFactors = append(io.StepFactors, (float64(step_num-i.step0)*Step)/i.distance) + + i.target = randomTarget() + i.step0 = step_num + i.distance = i.pos.Sub(i.target).Len() + } +} + +func (i *WorkerInstance) Offspring() (it WorkerInstance) { + it.pos = randomTarget() + it.target = randomTarget() + it.distance = it.pos.Sub(it.target).Len() + it.score = 0 + it.worker = i.worker.Offspring(OffspringStdVar) + return +} + +func RandomInstance() (i WorkerInstance) { + i.worker = RandomWorker() + i.pos = randomTarget() + i.target = randomTarget() + i.distance = i.pos.Sub(i.target).Len() + i.score = 0 + return +} diff --git a/main.cfg.json b/main.cfg.json index cb51509..47ed596 100644 --- a/main.cfg.json +++ b/main.cfg.json @@ -2,6 +2,6 @@ "WindowWidth": 1024, "WindowHeight": 768, - "FramerateLimit": 100 + "FramerateLimit": 4000 }