From 10959ef7266018ff3bee284c97f82318fc4e9be1 Mon Sep 17 00:00:00 2001
From: Marc Di Luzio <marc.diluzio@gmail.com>
Date: Wed, 8 Jul 2020 19:40:15 +0100
Subject: [PATCH 1/5] Refactor populate to be an Atlas function

	This simplifies usage greatly
---
 pkg/atlas/atlas.go | 29 ++++++++++++++---------------
 1 file changed, 14 insertions(+), 15 deletions(-)

diff --git a/pkg/atlas/atlas.go b/pkg/atlas/atlas.go
index 1c194af..209d4b7 100644
--- a/pkg/atlas/atlas.go
+++ b/pkg/atlas/atlas.go
@@ -59,7 +59,7 @@ func NewAtlas(chunkSize int) Atlas {
 		UpperBound: vector.Vector{X: chunkSize, Y: chunkSize},
 	}
 	// Initialise the first chunk
-	a.Chunks[0].populate(chunkSize)
+	a.populate(0)
 	return a
 }
 
@@ -81,11 +81,8 @@ func (a *Atlas) SetObject(v vector.Vector, obj objects.Object) {
 func (a *Atlas) QueryPosition(v vector.Vector) (byte, objects.Object) {
 	c := a.worldSpaceToChunkWithGrow(v)
 	local := a.worldSpaceToChunkLocal(v)
+	a.populate(c)
 	chunk := a.Chunks[c]
-	if chunk.Tiles == nil {
-		chunk.populate(a.ChunkSize)
-		a.Chunks[c] = chunk
-	}
 	i := a.chunkTileIndex(local)
 	return chunk.Tiles[i], chunk.Objects[i]
 }
@@ -96,8 +93,13 @@ func (a *Atlas) chunkTileIndex(local vector.Vector) int {
 }
 
 // populate will fill a chunk with data
-func (c *Chunk) populate(size int) {
-	c.Tiles = make([]byte, size*size)
+func (a *Atlas) populate(chunk int) {
+	c := a.Chunks[chunk]
+	if c.Tiles != nil {
+		return
+	}
+
+	c.Tiles = make([]byte, a.ChunkSize*a.ChunkSize)
 	c.Objects = make(map[int]objects.Object)
 
 	// Set up the tiles
@@ -117,26 +119,23 @@ func (c *Chunk) populate(size int) {
 			c.Objects[i] = objects.Object{Type: objects.SmallRock}
 		}
 	}
+
+	a.Chunks[chunk] = c
 }
 
 // setTile sets a tile in a specific chunk
 func (a *Atlas) setTile(chunk int, local vector.Vector, tile byte) {
+	a.populate(chunk)
 	c := a.Chunks[chunk]
-	if c.Tiles == nil {
-		c.populate(a.ChunkSize)
-	}
-
 	c.Tiles[a.chunkTileIndex(local)] = tile
 	a.Chunks[chunk] = c
 }
 
 // setObject sets an object in a specific chunk
 func (a *Atlas) setObject(chunk int, local vector.Vector, object objects.Object) {
-	c := a.Chunks[chunk]
-	if c.Tiles == nil {
-		c.populate(a.ChunkSize)
-	}
+	a.populate(chunk)
 
+	c := a.Chunks[chunk]
 	i := a.chunkTileIndex(local)
 	if object.Type != objects.None {
 		c.Objects[i] = object

From ed9ecef80a8c6d834d9bf779fc6d53f2298bc2c5 Mon Sep 17 00:00:00 2001
From: Marc Di Luzio <marc.diluzio@gmail.com>
Date: Wed, 8 Jul 2020 20:10:28 +0100
Subject: [PATCH 2/5] Add perlin based generation for the terrain tiles

---
 go.mod                  |  1 +
 go.sum                  |  2 ++
 pkg/atlas/atlas.go      | 29 +++++++++++++++++++++++------
 pkg/atlas/atlas_test.go | 24 ++++++++++++++++++++++++
 4 files changed, 50 insertions(+), 6 deletions(-)

diff --git a/go.mod b/go.mod
index 2165ade..6c4320a 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/mdiluz/rove
 go 1.14
 
 require (
+	github.com/aquilax/go-perlin v0.0.0-20191229124216-0af9ce917c28
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/golang/protobuf v1.4.2
 	github.com/google/uuid v1.1.1
diff --git a/go.sum b/go.sum
index 029b75f..e579bf8 100644
--- a/go.sum
+++ b/go.sum
@@ -4,6 +4,8 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/aquilax/go-perlin v0.0.0-20191229124216-0af9ce917c28 h1:iQUvYFmTKLXaDf3N0YfsJG5vgVtA1La82fHFDkpX5y4=
+github.com/aquilax/go-perlin v0.0.0-20191229124216-0af9ce917c28/go.mod h1:z9Rl7EM4BZY0Ikp2fEN1I5mKSOJ26HQpk0O2TBdN2HE=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU=
diff --git a/pkg/atlas/atlas.go b/pkg/atlas/atlas.go
index 209d4b7..c6bcc37 100644
--- a/pkg/atlas/atlas.go
+++ b/pkg/atlas/atlas.go
@@ -4,6 +4,7 @@ import (
 	"log"
 	"math/rand"
 
+	"github.com/aquilax/go-perlin"
 	"github.com/mdiluz/rove/pkg/maths"
 	"github.com/mdiluz/rove/pkg/objects"
 	"github.com/mdiluz/rove/pkg/vector"
@@ -47,6 +48,9 @@ type Atlas struct {
 
 	// ChunkSize is the x/y dimensions of each square chunk
 	ChunkSize int `json:"chunksize"`
+
+	// perlin is the current perlin noise generator
+	perlin *perlin.Perlin
 }
 
 // NewAtlas creates a new empty atlas
@@ -57,6 +61,7 @@ func NewAtlas(chunkSize int) Atlas {
 		Chunks:     make([]Chunk, 1),
 		LowerBound: vector.Vector{X: 0, Y: 0},
 		UpperBound: vector.Vector{X: chunkSize, Y: chunkSize},
+		perlin:     perlin.NewPerlin(2, 2, 3, 100),
 	}
 	// Initialise the first chunk
 	a.populate(0)
@@ -102,12 +107,23 @@ func (a *Atlas) populate(chunk int) {
 	c.Tiles = make([]byte, a.ChunkSize*a.ChunkSize)
 	c.Objects = make(map[int]objects.Object)
 
-	// Set up the tiles
-	for i := 0; i < len(c.Tiles); i++ {
-		if rand.Intn(3) == 0 {
-			c.Tiles[i] = byte(TileRock)
-		} else {
-			c.Tiles[i] = byte(TileSand)
+	origin := a.chunkOriginInWorldSpace(chunk)
+	for i := 0; i < a.ChunkSize; i++ {
+		for j := 0; j < a.ChunkSize; j++ {
+
+			// Get the perlin noise value for this location
+			pl := a.perlin.Noise2D(float64(origin.X+i)/10, float64(origin.Y+j)/10)
+
+			// Choose a tile based on the perlin noise value
+			var tile Tile
+			switch {
+			case pl > 0.1:
+				tile = TileSand
+			default:
+				tile = TileRock
+			}
+
+			c.Tiles[j*a.ChunkSize+i] = byte(tile)
 		}
 	}
 
@@ -217,6 +233,7 @@ func (a *Atlas) worldSpaceToChunkWithGrow(v vector.Vector) int {
 		LowerBound: lower,
 		UpperBound: upper,
 		Chunks:     make([]Chunk, size.X*size.Y),
+		perlin:     a.perlin,
 	}
 
 	// Log that we're resizing
diff --git a/pkg/atlas/atlas_test.go b/pkg/atlas/atlas_test.go
index 60c3922..dedf4b1 100644
--- a/pkg/atlas/atlas_test.go
+++ b/pkg/atlas/atlas_test.go
@@ -1,6 +1,7 @@
 package atlas
 
 import (
+	"fmt"
 	"testing"
 
 	"github.com/mdiluz/rove/pkg/objects"
@@ -248,3 +249,26 @@ func TestAtlas_GetSetCorrect(t *testing.T) {
 		}
 	}
 }
+
+func TestAtlas_WorldGen(t *testing.T) {
+	a := NewAtlas(8)
+	// Spawn a large world
+	_, _ = a.QueryPosition(vector.Vector{X: 20, Y: 20})
+
+	// Print out the world for manual evaluation
+	num := 20
+	for j := num - 1; j >= 0; j-- {
+		for i := 0; i < num; i++ {
+			t, o := a.QueryPosition(vector.Vector{X: i, Y: j})
+			if o.Type != objects.None {
+				fmt.Printf("%c", o.Type)
+			} else if t != byte(TileNone) {
+				fmt.Printf("%c", t)
+			} else {
+				fmt.Printf(" ")
+			}
+
+		}
+		fmt.Print("\n")
+	}
+}

From 7b4541716abaf4db3eb2365a631a32ddcd91121d Mon Sep 17 00:00:00 2001
From: Marc Di Luzio <marc.diluzio@gmail.com>
Date: Wed, 8 Jul 2020 23:45:52 +0100
Subject: [PATCH 3/5] Add gravel tiles

---
 pkg/atlas/atlas.go | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/pkg/atlas/atlas.go b/pkg/atlas/atlas.go
index c6bcc37..144ce93 100644
--- a/pkg/atlas/atlas.go
+++ b/pkg/atlas/atlas.go
@@ -18,10 +18,13 @@ const (
 	TileNone = Tile(0)
 
 	// TileRock is solid rock ground
-	TileRock = Tile('.')
+	TileRock = Tile('-')
+
+	// TileGravel is loose rocks
+	TileGravel = Tile(':')
 
 	// TileSand is sand
-	TileSand = Tile(',')
+	TileSand = Tile('~')
 )
 
 // Chunk represents a fixed square grid of tiles
@@ -112,12 +115,14 @@ func (a *Atlas) populate(chunk int) {
 		for j := 0; j < a.ChunkSize; j++ {
 
 			// Get the perlin noise value for this location
-			pl := a.perlin.Noise2D(float64(origin.X+i)/10, float64(origin.Y+j)/10)
+			pl := a.perlin.Noise2D(float64(origin.X+i)/15, float64(origin.Y+j)/15)
 
 			// Choose a tile based on the perlin noise value
 			var tile Tile
 			switch {
-			case pl > 0.1:
+			case pl > 0.2:
+				tile = TileGravel
+			case pl > 0.05:
 				tile = TileSand
 			default:
 				tile = TileRock

From 4b715bdff3684eab0426932b6a48aa3110ed0634 Mon Sep 17 00:00:00 2001
From: Marc Di Luzio <marc.diluzio@gmail.com>
Date: Wed, 8 Jul 2020 23:58:11 +0100
Subject: [PATCH 4/5] Move to OpenSimplex noise

	Apart from other benefits, this produces much nicer direction agnostic noise
---
 go.mod             |  2 +-
 go.sum             |  4 ++--
 pkg/atlas/atlas.go | 18 +++++++++++-------
 3 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/go.mod b/go.mod
index 6c4320a..db01c84 100644
--- a/go.mod
+++ b/go.mod
@@ -3,12 +3,12 @@ module github.com/mdiluz/rove
 go 1.14
 
 require (
-	github.com/aquilax/go-perlin v0.0.0-20191229124216-0af9ce917c28
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/golang/protobuf v1.4.2
 	github.com/google/uuid v1.1.1
 	github.com/grpc-ecosystem/grpc-gateway v1.14.6
 	github.com/kr/pretty v0.1.0 // indirect
+	github.com/ojrac/opensimplex-go v1.0.1
 	github.com/robfig/cron v1.2.0
 	github.com/stretchr/testify v1.6.0
 	golang.org/x/net v0.0.0-20200602114024-627f9648deb9
diff --git a/go.sum b/go.sum
index e579bf8..a2bd9a2 100644
--- a/go.sum
+++ b/go.sum
@@ -4,8 +4,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/aquilax/go-perlin v0.0.0-20191229124216-0af9ce917c28 h1:iQUvYFmTKLXaDf3N0YfsJG5vgVtA1La82fHFDkpX5y4=
-github.com/aquilax/go-perlin v0.0.0-20191229124216-0af9ce917c28/go.mod h1:z9Rl7EM4BZY0Ikp2fEN1I5mKSOJ26HQpk0O2TBdN2HE=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU=
@@ -52,6 +50,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/ojrac/opensimplex-go v1.0.1 h1:XslvpLP6XqQSATUtsOnGBYtFPw7FQ6h6y0ihjVeOLHo=
+github.com/ojrac/opensimplex-go v1.0.1/go.mod h1:MoSgj04tZpH8U0RefZabnHV2AbLgv/2mo3hLJtWqSEs=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
diff --git a/pkg/atlas/atlas.go b/pkg/atlas/atlas.go
index 144ce93..0526488 100644
--- a/pkg/atlas/atlas.go
+++ b/pkg/atlas/atlas.go
@@ -4,10 +4,10 @@ import (
 	"log"
 	"math/rand"
 
-	"github.com/aquilax/go-perlin"
 	"github.com/mdiluz/rove/pkg/maths"
 	"github.com/mdiluz/rove/pkg/objects"
 	"github.com/mdiluz/rove/pkg/vector"
+	"github.com/ojrac/opensimplex-go"
 )
 
 // Tile describes the type of terrain
@@ -52,10 +52,14 @@ type Atlas struct {
 	// ChunkSize is the x/y dimensions of each square chunk
 	ChunkSize int `json:"chunksize"`
 
-	// perlin is the current perlin noise generator
-	perlin *perlin.Perlin
+	// noise is an OpenSimplex noise generator
+	noise opensimplex.Noise
 }
 
+const (
+	noiseSeed = 1024
+)
+
 // NewAtlas creates a new empty atlas
 func NewAtlas(chunkSize int) Atlas {
 	// Start up with one chunk
@@ -64,7 +68,7 @@ func NewAtlas(chunkSize int) Atlas {
 		Chunks:     make([]Chunk, 1),
 		LowerBound: vector.Vector{X: 0, Y: 0},
 		UpperBound: vector.Vector{X: chunkSize, Y: chunkSize},
-		perlin:     perlin.NewPerlin(2, 2, 3, 100),
+		noise:      opensimplex.New(noiseSeed),
 	}
 	// Initialise the first chunk
 	a.populate(0)
@@ -115,12 +119,12 @@ func (a *Atlas) populate(chunk int) {
 		for j := 0; j < a.ChunkSize; j++ {
 
 			// Get the perlin noise value for this location
-			pl := a.perlin.Noise2D(float64(origin.X+i)/15, float64(origin.Y+j)/15)
+			pl := a.noise.Eval2(float64(origin.X+i)/6, float64(origin.Y+j)/6)
 
 			// Choose a tile based on the perlin noise value
 			var tile Tile
 			switch {
-			case pl > 0.2:
+			case pl > 0.5:
 				tile = TileGravel
 			case pl > 0.05:
 				tile = TileSand
@@ -238,7 +242,7 @@ func (a *Atlas) worldSpaceToChunkWithGrow(v vector.Vector) int {
 		LowerBound: lower,
 		UpperBound: upper,
 		Chunks:     make([]Chunk, size.X*size.Y),
-		perlin:     a.perlin,
+		noise:      a.noise,
 	}
 
 	// Log that we're resizing

From 9682cfa7ea331c265b45c516b32a5ea207bbbf20 Mon Sep 17 00:00:00 2001
From: Marc Di Luzio <marc.diluzio@gmail.com>
Date: Thu, 9 Jul 2020 00:04:46 +0100
Subject: [PATCH 5/5] Spawn objects using OpenSimplex noise as well

---
 pkg/atlas/atlas.go | 57 ++++++++++++++++++++++++++++++----------------
 1 file changed, 37 insertions(+), 20 deletions(-)

diff --git a/pkg/atlas/atlas.go b/pkg/atlas/atlas.go
index 0526488..dd618e7 100644
--- a/pkg/atlas/atlas.go
+++ b/pkg/atlas/atlas.go
@@ -52,23 +52,29 @@ type Atlas struct {
 	// ChunkSize is the x/y dimensions of each square chunk
 	ChunkSize int `json:"chunksize"`
 
-	// noise is an OpenSimplex noise generator
-	noise opensimplex.Noise
+	// terrainNoise describes the noise function for the terrain
+	terrainNoise opensimplex.Noise
+
+	// terrainNoise describes the noise function for the terrain
+	objectNoise opensimplex.Noise
 }
 
 const (
-	noiseSeed = 1024
+	noiseSeed         = 1024
+	terrainNoiseScale = 6
+	objectNoiseScale  = 3
 )
 
 // NewAtlas creates a new empty atlas
 func NewAtlas(chunkSize int) Atlas {
 	// Start up with one chunk
 	a := Atlas{
-		ChunkSize:  chunkSize,
-		Chunks:     make([]Chunk, 1),
-		LowerBound: vector.Vector{X: 0, Y: 0},
-		UpperBound: vector.Vector{X: chunkSize, Y: chunkSize},
-		noise:      opensimplex.New(noiseSeed),
+		ChunkSize:    chunkSize,
+		Chunks:       make([]Chunk, 1),
+		LowerBound:   vector.Vector{X: 0, Y: 0},
+		UpperBound:   vector.Vector{X: chunkSize, Y: chunkSize},
+		terrainNoise: opensimplex.New(noiseSeed),
+		objectNoise:  opensimplex.New(noiseSeed),
 	}
 	// Initialise the first chunk
 	a.populate(0)
@@ -118,21 +124,31 @@ func (a *Atlas) populate(chunk int) {
 	for i := 0; i < a.ChunkSize; i++ {
 		for j := 0; j < a.ChunkSize; j++ {
 
-			// Get the perlin noise value for this location
-			pl := a.noise.Eval2(float64(origin.X+i)/6, float64(origin.Y+j)/6)
-
-			// Choose a tile based on the perlin noise value
+			// Get the terrain noise value for this location
+			t := a.terrainNoise.Eval2(float64(origin.X+i)/terrainNoiseScale, float64(origin.Y+j)/terrainNoiseScale)
 			var tile Tile
 			switch {
-			case pl > 0.5:
+			case t > 0.5:
 				tile = TileGravel
-			case pl > 0.05:
+			case t > 0.05:
 				tile = TileSand
 			default:
 				tile = TileRock
 			}
-
 			c.Tiles[j*a.ChunkSize+i] = byte(tile)
+
+			// Get the object noise value for this location
+			o := a.objectNoise.Eval2(float64(origin.X+i)/objectNoiseScale, float64(origin.Y+j)/objectNoiseScale)
+			var obj = objects.None
+			switch {
+			case o > 0.6:
+				obj = objects.LargeRock
+			case o > 0.5:
+				obj = objects.SmallRock
+			}
+			if obj != objects.None {
+				c.Objects[j*a.ChunkSize+i] = objects.Object{Type: obj}
+			}
 		}
 	}
 
@@ -238,11 +254,12 @@ func (a *Atlas) worldSpaceToChunkWithGrow(v vector.Vector) int {
 
 	// Create the new empty atlas
 	newAtlas := Atlas{
-		ChunkSize:  a.ChunkSize,
-		LowerBound: lower,
-		UpperBound: upper,
-		Chunks:     make([]Chunk, size.X*size.Y),
-		noise:      a.noise,
+		ChunkSize:    a.ChunkSize,
+		LowerBound:   lower,
+		UpperBound:   upper,
+		Chunks:       make([]Chunk, size.X*size.Y),
+		terrainNoise: a.terrainNoise,
+		objectNoise:  a.objectNoise,
 	}
 
 	// Log that we're resizing