Files
gonum/tilegraph.go
2013-07-24 07:28:11 -07:00

218 lines
4.6 KiB
Go

package discrete
import (
"errors"
"strings"
)
type TileGraph struct {
tiles []bool
numRows, numCols int
}
func NewTileGraph(dimX, dimY int, isPassable bool) *TileGraph {
tiles := make([]bool, dimX*dimY)
if isPassable {
for i, _ := range tiles {
tiles[i] = true
}
}
return &TileGraph{
tiles: tiles,
numRows: dimX,
numCols: dimY,
}
}
func GenerateTileGraph(template string) (*TileGraph, error) {
rows := strings.Split(strings.TrimSpace(template), "\n")
tiles := make([]bool, 0)
colCheck := -1
for _, colString := range rows {
colCount := 0
cols := strings.NewReader(colString)
for cols.Len() != 0 {
colCount += 1
ch, _, err := cols.ReadRune()
if err != nil {
return nil, errors.New("Error while reading rune from input string")
}
switch ch {
case '\u2580':
tiles = append(tiles, false)
case ' ':
tiles = append(tiles, true)
default:
return nil, errors.New("Unrecognized character while reading input string")
}
}
if colCheck == -1 {
colCheck = colCount
} else if colCheck != colCount {
return nil, errors.New("Jagged rows, cannot generate graph.")
}
}
return &TileGraph{
tiles: tiles,
numRows: len(rows),
numCols: colCheck,
}, nil
}
func (graph *TileGraph) SetPassability(row, col int, passability bool) {
loc := row*graph.numCols + col
if loc >= len(graph.tiles) || row < 0 || col < 0 {
return
}
graph.tiles[loc] = passability
}
func (graph *TileGraph) String() string {
var outString string
for r := 0; r < graph.numRows; r++ {
for c := 0; c < graph.numCols; c++ {
if graph.tiles[r*graph.numCols+c] == false {
outString += "\u2580" // Black square
} else {
outString += " " // Space
}
}
outString += "\n"
}
return outString[:len(outString)-1] // Kill final newline
}
func (graph *TileGraph) PathString(path []int) string {
if path == nil || len(path) == 0 {
return graph.String()
}
var outString string
for r := 0; r < graph.numRows; r++ {
for c := 0; c < graph.numCols; c++ {
if id := r*graph.numCols + c; graph.tiles[id] == false {
outString += "\u2580" // Black square
} else if id == path[0] {
outString += "s"
} else if id == path[len(path)-1] {
outString += "g"
} else {
toAppend := " "
for _, num := range path[1 : len(path)-1] {
if id == num {
toAppend = "♥"
}
}
outString += toAppend
}
}
outString += "\n"
}
return outString[:len(outString)-1]
}
func (graph *TileGraph) Dimensions() (rows, cols int) {
return graph.numRows, graph.numCols
}
func (graph *TileGraph) IDToCoords(id int) (row, col int) {
col = (id % graph.numCols)
row = (id - col) / graph.numCols
return row, col
}
func (graph *TileGraph) CoordsToID(row, col int) (id int) {
if row < 0 || row >= graph.numRows || col < 0 || col >= graph.numCols {
return -1
}
id = row*graph.numCols + col
return id
}
func (graph *TileGraph) Successors(id int) []int {
if id < 0 || id >= len(graph.tiles) || graph.tiles[id] == false {
return nil
}
row, col := graph.IDToCoords(id)
neighbors := []int{graph.CoordsToID(row-1, col), graph.CoordsToID(row+1, col), graph.CoordsToID(row, col-1), graph.CoordsToID(row, col+1)}
realNeighbors := make([]int, 0, 4) // Will overallocate sometimes, but not by much. Not a big deal
for _, neighbor := range neighbors {
if neighbor != -1 && graph.tiles[neighbor] == true {
realNeighbors = append(realNeighbors, neighbor)
}
}
return realNeighbors
}
func (graph *TileGraph) IsSuccessor(id, succ int) bool {
return (id >= 0 && id < len(graph.tiles) && graph.tiles[id] == true) && (succ >= 0 && succ < len(graph.tiles) && graph.tiles[succ] == true)
}
func (graph *TileGraph) Predecessors(id int) []int {
return graph.Successors(id)
}
func (graph *TileGraph) IsPredecessor(id, pred int) bool {
return graph.IsSuccessor(id, pred)
}
func (graph *TileGraph) IsAdjacent(id, neighbor int) bool {
return graph.IsSuccessor(id, neighbor)
}
func (graph *TileGraph) NodeExists(id int) bool {
return id >= 0 && id < len(graph.tiles) && graph.tiles[id] == true
}
func (graph *TileGraph) Degree(id int) int {
return len(graph.Successors(id)) * 2
}
func (graph *TileGraph) EdgeList() [][2]int {
edges := make([][2]int, 0)
for id, passable := range graph.tiles {
if !passable {
continue
}
for _, succ := range graph.Successors(id) {
edges = append(edges, [2]int{id, succ})
}
}
return edges
}
func (graph *TileGraph) NodeList() []int {
nodes := make([]int, 0)
for id, passable := range graph.tiles {
if !passable {
continue
}
nodes = append(nodes, id)
}
return nodes
}
func (graph *TileGraph) IsDirected() bool {
return false
}