diff --git a/main.go b/main.go index bba2f86..2855243 100644 --- a/main.go +++ b/main.go @@ -3,10 +3,11 @@ package main import ( "os" + "slices" "log" + "fmt" "math/rand" "time" - "strings" "github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2/encoding" ) @@ -19,8 +20,12 @@ const ( PATH_LIMIT = 1000 RENDER = true SHOW_RENDER = false + SHOW_PATHS = true ) +type Map [][]rune +type Paths [][]int + type Position struct { x int y int @@ -32,7 +37,8 @@ type Enemy struct { type Game struct { screen tcell.Screen - level [][]rune + level Map + paths Paths player Position status string width int @@ -63,12 +69,28 @@ func (game *Game) DrawMap() { } } +func (game *Game) DrawPaths() { + for y, row := range game.paths { + for x, path_num := range row { + if path_num <= 16 { + as_str := fmt.Sprintf("%x", path_num) + game.screen.SetContent(x, y, rune(as_str[0]), nil, tcell.StyleDefault) + } + } + } +} + func (game *Game) Render() { if !RENDER { return } game.screen.Clear() game.DrawMap() + + if SHOW_PATHS { + game.DrawPaths() + } + game.DrawEntity('@', game.player, tcell.ColorYellow) for pos, _ := range game.enemies { @@ -152,7 +174,8 @@ func MakeGame(width int, height int) (*Game) { game.height = height game.enemies = make(map[Position]Enemy) - game.level = make([][]rune, height, height) + game.level = make(Map, height, height) + game.paths = make(Paths, height, height) if RENDER { game.screen, err = tcell.NewScreen() @@ -273,12 +296,15 @@ func (game *Game) HuntAndKill() []Position { return dead_ends } -func (game *Game) ClearMap() { - row := strings.Repeat("#", game.width) +func (game *Game) FillMap(target Map, setting rune) { + for y := 0 ; y < game.height; y++ { + target[y] = slices.Repeat([]rune{setting}, game.width) + } +} +func (game *Game) FillPaths(target Paths, setting int) { for y := 0 ; y < game.height; y++ { - as_runes := []rune(strings.Clone(row)) - game.level[y] = as_runes + target[y] = slices.Repeat([]int{setting}, game.width) } } @@ -301,14 +327,73 @@ func (game *Game) MoveEnemy(from Position, to Position) { game.enemies[to] = enemy } +func (game *Game) CloneMap() Map { + // this is a shallow copy though + new_map := slices.Clone(game.level) + + for i, row := range new_map { + // this makes sure the row is an actual copy + new_map[i] = slices.Clone(row) + } + + return new_map +} + +func (game *Game) PathAddNeighbors(neighbors []Position, closed Map, near Position) { + points := compass(near.x, near.y, 1) + + for _, pos := range points { + if !game.Occupied(pos.x, pos.y) { + closed[pos.y][pos.x] = WALL + neighbors = append(neighbors, pos) + } + } +} + func (game *Game) PathEnemies() { - for pos, _ := range game.enemies { - possible := compass(pos.x, pos.y, 1) - move_to := possible[rand.Int() % len(possible)] - if !game.Occupied(move_to.x, move_to.y) { - game.MoveEnemy(pos, move_to) + in_grid := make([][]int, game.height, game.height) + game.FillPaths(in_grid, 1) + in_grid[game.player.y][game.player.x] = 1 + + game.FillPaths(game.paths, PATH_LIMIT) + closed := game.CloneMap() + starting_pixels := make([]Position, 0, 10) + open_pixels := make([]Position, 0, 10) + + counter := 0 + + for counter < game.height * game.width { + x := counter % game.width + y := counter / game.width + + if in_grid[y][x] == 0 { + game.paths[y][x] = 0 + closed[y][x] = WALL + starting_pixels = append(starting_pixels, Position{x, y}) } + + counter += 1 + } + + for _, pos := range starting_pixels { + game.PathAddNeighbors(open_pixels, closed, pos) } + + for counter < PATH_LIMIT && len(open_pixels) > 0 { + next_open := make([]Position, 0, 10) + for _, pos := range open_pixels { + game.paths[pos.y][pos.x] = counter + game.PathAddNeighbors(next_open, closed, pos) + } + open_pixels = next_open + counter += 1 + } + + for _, pos := range open_pixels { + game.paths[pos.y][pos.x] = counter + } + + dbg.Println("pathing is", game.paths) } func (game *Game) AddRooms(dead_ends []Position, size int) { @@ -323,11 +408,11 @@ func (game *Game) AddRooms(dead_ends []Position, size int) { } func (game *Game) MakeMap() []Position { - game.ClearMap() + game.FillMap(game.level, '#') game.Status("FIRST MAZE") dead_ends := game.HuntAndKill() - game.ClearMap() + game.FillMap(game.level, '#') game.AddRooms(dead_ends, game.height / 8) game.Status("SECOND MAZE")