|
|
@ -3,10 +3,11 @@ package main |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"os" |
|
|
|
"os" |
|
|
|
|
|
|
|
"slices" |
|
|
|
"log" |
|
|
|
"log" |
|
|
|
|
|
|
|
"fmt" |
|
|
|
"math/rand" |
|
|
|
"math/rand" |
|
|
|
"time" |
|
|
|
"time" |
|
|
|
"strings" |
|
|
|
|
|
|
|
"github.com/gdamore/tcell/v2" |
|
|
|
"github.com/gdamore/tcell/v2" |
|
|
|
"github.com/gdamore/tcell/v2/encoding" |
|
|
|
"github.com/gdamore/tcell/v2/encoding" |
|
|
|
) |
|
|
|
) |
|
|
@ -19,8 +20,12 @@ const ( |
|
|
|
PATH_LIMIT = 1000 |
|
|
|
PATH_LIMIT = 1000 |
|
|
|
RENDER = true |
|
|
|
RENDER = true |
|
|
|
SHOW_RENDER = false |
|
|
|
SHOW_RENDER = false |
|
|
|
|
|
|
|
SHOW_PATHS = true |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type Map [][]rune |
|
|
|
|
|
|
|
type Paths [][]int |
|
|
|
|
|
|
|
|
|
|
|
type Position struct { |
|
|
|
type Position struct { |
|
|
|
x int |
|
|
|
x int |
|
|
|
y int |
|
|
|
y int |
|
|
@ -32,7 +37,8 @@ type Enemy struct { |
|
|
|
|
|
|
|
|
|
|
|
type Game struct { |
|
|
|
type Game struct { |
|
|
|
screen tcell.Screen |
|
|
|
screen tcell.Screen |
|
|
|
level [][]rune |
|
|
|
level Map |
|
|
|
|
|
|
|
paths Paths |
|
|
|
player Position |
|
|
|
player Position |
|
|
|
status string |
|
|
|
status string |
|
|
|
width int |
|
|
|
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() { |
|
|
|
func (game *Game) Render() { |
|
|
|
if !RENDER { return } |
|
|
|
if !RENDER { return } |
|
|
|
|
|
|
|
|
|
|
|
game.screen.Clear() |
|
|
|
game.screen.Clear() |
|
|
|
|
|
|
|
|
|
|
|
game.DrawMap() |
|
|
|
game.DrawMap() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if SHOW_PATHS { |
|
|
|
|
|
|
|
game.DrawPaths() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
game.DrawEntity('@', game.player, tcell.ColorYellow) |
|
|
|
game.DrawEntity('@', game.player, tcell.ColorYellow) |
|
|
|
|
|
|
|
|
|
|
|
for pos, _ := range game.enemies { |
|
|
|
for pos, _ := range game.enemies { |
|
|
@ -152,7 +174,8 @@ func MakeGame(width int, height int) (*Game) { |
|
|
|
game.height = height |
|
|
|
game.height = height |
|
|
|
game.enemies = make(map[Position]Enemy) |
|
|
|
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 { |
|
|
|
if RENDER { |
|
|
|
game.screen, err = tcell.NewScreen() |
|
|
|
game.screen, err = tcell.NewScreen() |
|
|
@ -273,12 +296,15 @@ func (game *Game) HuntAndKill() []Position { |
|
|
|
return dead_ends |
|
|
|
return dead_ends |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (game *Game) ClearMap() { |
|
|
|
func (game *Game) FillMap(target Map, setting rune) { |
|
|
|
row := strings.Repeat("#", game.width) |
|
|
|
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++ { |
|
|
|
for y := 0 ; y < game.height; y++ { |
|
|
|
as_runes := []rune(strings.Clone(row)) |
|
|
|
target[y] = slices.Repeat([]int{setting}, game.width) |
|
|
|
game.level[y] = as_runes |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -301,14 +327,73 @@ func (game *Game) MoveEnemy(from Position, to Position) { |
|
|
|
game.enemies[to] = enemy |
|
|
|
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() { |
|
|
|
func (game *Game) PathEnemies() { |
|
|
|
for pos, _ := range game.enemies { |
|
|
|
in_grid := make([][]int, game.height, game.height) |
|
|
|
possible := compass(pos.x, pos.y, 1) |
|
|
|
game.FillPaths(in_grid, 1) |
|
|
|
move_to := possible[rand.Int() % len(possible)] |
|
|
|
in_grid[game.player.y][game.player.x] = 1 |
|
|
|
if !game.Occupied(move_to.x, move_to.y) { |
|
|
|
|
|
|
|
game.MoveEnemy(pos, move_to) |
|
|
|
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) { |
|
|
|
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 { |
|
|
|
func (game *Game) MakeMap() []Position { |
|
|
|
game.ClearMap() |
|
|
|
game.FillMap(game.level, '#') |
|
|
|
game.Status("FIRST MAZE") |
|
|
|
game.Status("FIRST MAZE") |
|
|
|
dead_ends := game.HuntAndKill() |
|
|
|
dead_ends := game.HuntAndKill() |
|
|
|
|
|
|
|
|
|
|
|
game.ClearMap() |
|
|
|
game.FillMap(game.level, '#') |
|
|
|
game.AddRooms(dead_ends, game.height / 8) |
|
|
|
game.AddRooms(dead_ends, game.height / 8) |
|
|
|
|
|
|
|
|
|
|
|
game.Status("SECOND MAZE") |
|
|
|
game.Status("SECOND MAZE") |
|
|
|