Source File: curse-you-go-rogue/03_pathing_enemies/pathing.go

package main

import (
  "slices"
)

func (game *Game) FillPaths(target Paths, setting int) {
  for y := 0 ; y < game.Height; y++ {
    target[y] = slices.Repeat([]int{setting}, game.Width)
  }
}


func (game *Game) PathAddNeighbors(neighbors []Position, closed Map, near Position) []Position {
  points := compass(near, 1)

  for _, pos := range points {
    // NOTE: if you also add !game.Occupied(pos.x, pos.y) it ????
    if closed[pos.Y][pos.X] == SPACE {
      closed[pos.Y][pos.X] = WALL
      neighbors = append(neighbors, pos)
    }
  }

  return neighbors
}

func (game *Game) CalculatePaths() {
  in_grid := make([][]int, game.Height, game.Height)
  game.FillPaths(in_grid, 1)
  in_grid[game.Player.Pos.Y][game.Player.Pos.X] = 0

  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 {
    open_pixels = game.PathAddNeighbors(open_pixels, closed, pos)
  }

  counter = 1
  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
      next_open = game.PathAddNeighbors(next_open, closed, pos)
    }
    open_pixels = next_open
    counter += 1
  }

  for _, pos := range open_pixels {
    game.Paths[pos.Y][pos.X] = counter
  }
}

func (game *Game) EnemyPathing() {
  for enemy_at, _ := range game.Enemies {
    // get the four directions
    dirs := compass(enemy_at, 1)

    // sort by closest path number
    slices.SortFunc(dirs, func(a Position, b Position) int {
      return game.Paths[a.Y][a.X] - game.Paths[b.Y][b.X]
    })

    // 0 dir is now the best direction
    move_to := dirs[0]

    // can we hear the player? occupied?
    can_hear := game.Paths[move_to.Y][move_to.X] < HEARING_DISTANCE
    occupied := game.Occupied(move_to)

    if can_hear && !occupied {
      // move the enemy in the best direction
      game.MoveEnemy(enemy_at, move_to)
    }
  }
}