commit b3f3a77fc755e7b31afe47d218e027abb537f554 Author: ChaoticByte Date: Thu Aug 21 18:16:10 2025 +0200 Initial commit diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..44936f4 --- /dev/null +++ b/go.mod @@ -0,0 +1,14 @@ +module github.com/ChaoticByte/xels + +go 1.24.5 + +require github.com/hajimehoshi/ebiten/v2 v2.8.8 + +require ( + github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 // indirect + github.com/ebitengine/hideconsole v1.0.0 // indirect + github.com/ebitengine/purego v0.8.0 // indirect + github.com/jezek/xgb v1.1.1 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..df10e34 --- /dev/null +++ b/go.sum @@ -0,0 +1,16 @@ +github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 h1:Gk1XUEttOk0/hb6Tq3WkmutWa0ZLhNn/6fc6XZpM7tM= +github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325/go.mod h1:ulhSQcbPioQrallSuIzF8l1NKQoD7xmMZc5NxzibUMY= +github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= +github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= +github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE= +github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/hajimehoshi/ebiten/v2 v2.8.8 h1:xyMxOAn52T1tQ+j3vdieZ7auDBOXmvjUprSrxaIbsi8= +github.com/hajimehoshi/ebiten/v2 v2.8.8/go.mod h1:durJ05+OYnio9b8q0sEtOgaNeBEQG7Yr7lRviAciYbs= +github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= +github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= +golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw= +golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..96a2bf1 --- /dev/null +++ b/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "github.com/hajimehoshi/ebiten/v2" + + "github.com/ChaoticByte/xels/simulation" +) + + +type Application struct { + simulation simulation.Simulation +} + +func (a *Application) Update() error { + a.simulation.Update() + return nil +} + +func (a *Application) Draw(screen *ebiten.Image) { + // create image from xel grid + pixels := make([]byte, 4 * screen.Bounds().Dx() * screen.Bounds().Dy()) + for i := range len(a.simulation.Grid.Xels) { + xel := a.simulation.Grid.Xels[i] + var v byte = 0 + if xel.Energy > 0 { + v = byte(max(0, min(255, xel.Energy * 2 + 38))) + } + // r + pixels[i * 4] = v + // g + pixels[(i*4) + 1] = v + // b + pixels[(i*4) + 2] = v + // a + pixels[(i*4) + 3] = 255 + } + screen.WritePixels(pixels) +} + +func (a *Application) Layout(outsideWidth int, outsideHeight int) (int, int) { + return CanvasWidth, CanvasHeight +} + + +func main() { + ebiten.SetWindowSize(CanvasWidth, CanvasHeight) + ebiten.SetWindowTitle("Pixels - Main Window") + ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled) + ebiten.MaximizeWindow() + ebiten.SetTPS(MaxTps) + app := &Application{ + simulation: *simulation.NewSimulation(CanvasWidth, CanvasHeight), + } + InitGrid(app.simulation.Grid) + err := ebiten.RunGame(app) + if err != nil { panic(err) } +} diff --git a/settings.go b/settings.go new file mode 100644 index 0000000..96664a9 --- /dev/null +++ b/settings.go @@ -0,0 +1,12 @@ +package main + +import "github.com/ChaoticByte/xels/simulation" + +const CanvasWidth = 200 +const CanvasHeight = 200 +const MaxTps = 1000 +const SimStepsPerUpdate = 10 + +func InitGrid(grid *simulation.XelGrid) { + grid.GetXel(grid.GetCenterPosition()).Energy = 50000 +} diff --git a/simulation/simulation.go b/simulation/simulation.go new file mode 100644 index 0000000..51630c0 --- /dev/null +++ b/simulation/simulation.go @@ -0,0 +1,32 @@ +package simulation + +import ( + "math/rand/v2" +) + +type Simulation struct { + Grid *XelGrid +} + +func (sim *Simulation) Update() { + for range 10 { + // get all available xels with energy != 0 + available_positions := []Vector2{} + for i, xel := range sim.Grid.Xels { + if xel.Energy != 0 { + available_positions = append(available_positions, Vector2{X: i % sim.Grid.Width, Y: i/sim.Grid.Height}) + } + } + // choose random pos + pos := available_positions[rand.IntN(len(available_positions))] + xel := sim.Grid.GetXel(pos) + if xel.Energy == 0 { panic("xel energy null") } + xel.Step(pos, sim.Grid) + } +} + +func NewSimulation(width int, height int) *Simulation { + return &Simulation{ + Grid: NewXelGrid(width, height), + } +} diff --git a/simulation/vec2.go b/simulation/vec2.go new file mode 100644 index 0000000..d9212e9 --- /dev/null +++ b/simulation/vec2.go @@ -0,0 +1,6 @@ +package simulation + +type Vector2 struct { + X int + Y int +} diff --git a/simulation/xel.go b/simulation/xel.go new file mode 100644 index 0000000..59c5aec --- /dev/null +++ b/simulation/xel.go @@ -0,0 +1,69 @@ +package simulation + +import ( + "math/rand/v2" +) + +const ( + TurnNorth int = iota + TurnEast + TurnSouth + TurnWest +) + +type Xel struct { + Energy int64 +} + +func (xel *Xel) Step(ownPos Vector2, grid *XelGrid) { + // get other xel + var otherXelPos Vector2 + switch rand.IntN(4) { + case 0: + otherXelPos = Vector2{X: ownPos.X + 1, Y: ownPos.Y} + case 1: + otherXelPos = Vector2{X: ownPos.X - 1, Y: ownPos.Y} + case 2: + otherXelPos = Vector2{X: ownPos.X, Y: ownPos.Y + 1} + case 3: + otherXelPos = Vector2{X: ownPos.X, Y: ownPos.Y - 1} + } + otherXel := grid.GetXel(otherXelPos) + if otherXel == nil { return } + // interact + if (xel.Energy > 0 && otherXel.Energy > 0) || (xel.Energy < 0 && otherXel.Energy < 0) { + if rand.IntN(4) == 0 { + // create a higher energy xel and an inverted xel + xel.Energy += (2 * otherXel.Energy) + otherXel.Energy *= -1 + } else { + xel.Energy += otherXel.Energy + otherXel.Energy = 0 + } + return + } + if (xel.Energy < 0 && otherXel.Energy > 0) || (xel.Energy > 0 && otherXel.Energy < 0) { + // cancel out + xel.Energy += otherXel.Energy + otherXel.Energy = 0 + return + } + if (xel.Energy == 0 && otherXel.Energy != 0) || (xel.Energy != 0 && otherXel.Energy == 0) { + if xel.Energy > 1 || xel.Energy < -1 || otherXel.Energy > 1 || otherXel.Energy < -1 { // prevent integer 1 / 0 + // split + splitE := (xel.Energy + otherXel.Energy) / 2 + xel.Energy = splitE + ((xel.Energy + otherXel.Energy) % 2) + otherXel.Energy = splitE + } else { + // move + if xel.Energy != 0 { + otherXel.Energy = xel.Energy + xel.Energy = 0 + } else { + xel.Energy = otherXel.Energy + otherXel.Energy = 0 + } + } + return + } +} diff --git a/simulation/xelgrid.go b/simulation/xelgrid.go new file mode 100644 index 0000000..486d329 --- /dev/null +++ b/simulation/xelgrid.go @@ -0,0 +1,36 @@ +package simulation + +type XelGrid struct { + Width int // do not overwrite this value after init! + Height int // do not overwrite this value after init! + Xels []*Xel +} + +func (grid *XelGrid) GetCenterPosition() Vector2 { // just a helper + return Vector2{ + X: (grid.Width / 2) + (grid.Width % 2), + Y: (grid.Height / 2) + (grid.Height % 2), + } +} + +func (grid *XelGrid) GetXel(pos Vector2) *Xel { + if pos.X < 0 || pos.X >= grid.Width || pos.Y < 0 || pos.Y >= grid.Height { + return nil // out of bounds + } + i := (pos.Y * grid.Width) + pos.X + return grid.Xels[i] +} + +func NewXelGrid(width int, height int) *XelGrid { + grid := &XelGrid{ + Width: width, + Height: height, + Xels: make([]*Xel, width*height), + } + // init + for i := range len(grid.Xels) { + grid.Xels[i] = &Xel{} + } + // + return grid +}