Skip to content

Instantly share code, notes, and snippets.

@samsamm777
Last active September 26, 2017 16:02
Show Gist options
  • Save samsamm777/82c0f11d6cc06c2cb80bd17f3235f7b2 to your computer and use it in GitHub Desktop.
Save samsamm777/82c0f11d6cc06c2cb80bd17f3235f7b2 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"math/rand"
"strconv"
"time"
)
type Vector2D struct {
x int
y int
}
type TileMap struct {
width int
height int
tiles []int
seed int64
fillPercentage int
}
const (
tileWall int = 1
tileEmpty int = 0
)
// String will return our map as astring for debugging purposes. This can be printed
// to the cli output
func (t *TileMap) String() string {
str := ""
for i, val := range t.tiles {
str = str + strconv.Itoa(val) + " "
if (i+1)%t.width == 0 {
str = str + "\n"
}
}
return str
}
// generateTiles will generate the walls for the tile map
func (t *TileMap) generateTiles() {
for i := 0; i < len(t.tiles); i++ {
t.tiles[i] = tileEmpty
}
t.setSeed()
t.randomFillMap()
t.smoothMap(5)
tiles := t.getRegionTiles(15, 15)
fmt.Print(tiles)
}
// seed will set the seed value for a PRNG
func (t *TileMap) setSeed() {
if t.seed == 0 {
t.seed = time.Now().UTC().UnixNano()
}
rand.Seed(t.seed)
}
func (t *TileMap) tileFinder(ch chan Vector2D, done chan bool) {
chicken := 0
for {
select {
case pos := <-ch:
fmt.Print(pos)
ch <- pos
fmt.Print("NEVER")
chicken++
if chicken > 100 {
done <- true
}
}
}
}
func (t *TileMap) getRegionTiles(x int, y int) (tiles []Vector2D) {
ch := make(chan Vector2D, 10)
done := make(chan bool)
go t.tileFinder(ch, done)
ch <- Vector2D{x, y}
// block until the done channel is triggered
<-done
return tiles
}
// RandomFillMap will fill the map with random walls. We only set a wall if the
// prng is within our fillPercentage value
func (t *TileMap) randomFillMap() {
for i := 0; i < len(t.tiles); i++ {
r := rand.Intn(100)
x, y := t.getPosAtIndex(i)
if x == 0 || x == t.width-1 || y == 0 || y == t.height-1 {
t.tiles[i] = tileWall
} else {
if r < t.fillPercentage {
t.tiles[i] = tileWall
}
}
}
}
// SmoothMap will iteratively smooth the map. we simple check for the amount of walls
// nearby to decide whether we should smooth to walls or floors.
func (t *TileMap) smoothMap(iterations int) {
for k := 0; k < iterations; k++ {
for i := 0; i < len(t.tiles); i++ {
x, y := t.getPosAtIndex(i)
walls := t.calcWallCount(x, y)
if walls > 4 {
t.tiles[t.getIndexAtPosition(x, y)] = tileWall
} else if walls < 4 {
t.tiles[t.getIndexAtPosition(x, y)] = tileEmpty
}
}
}
}
// GetIndexAtPosition will get the index into the tile array using the coords
func (t *TileMap) getIndexAtPosition(x int, y int) int {
return x + (y * t.height)
}
// GetValueAtPosition will get the value of the tile at the given position
func (t *TileMap) getValueAtPosition(x int, y int) int {
return t.tiles[t.getIndexAtPosition(x, y)]
}
// GetPosAtIndex will get the x y positon at the given index
func (t *TileMap) getPosAtIndex(i int) (x int, y int) {
y = i % t.width
x = i / t.width
return x, y
}
func (t *TileMap) isInMapRange(x int, y int) bool {
return (x >= 0 && x < t.width && y >= 0 && y < t.height)
}
// calcWallCount calculates how many walls are nearby the coords
func (t *TileMap) calcWallCount(x int, y int) (numWalls int) {
numWalls = 0
for nX := x - 1; nX <= x+1; nX++ {
for nY := y - 1; nY <= y+1; nY++ {
if t.isInMapRange(nX, nY) {
if nX != x || nY != y {
v := t.getValueAtPosition(nX, nY)
numWalls += v
}
} else {
numWalls++
}
}
}
return numWalls
}
// NewMap will create a new tile map and return it
func NewMap(w int, h int, s int64, fp int) *TileMap {
tm := &TileMap{
width: w,
height: h,
tiles: make([]int, w*h),
seed: s,
fillPercentage: fp,
}
tm.generateTiles()
return tm
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment