init
This commit is contained in:
178
grid.js
Normal file
178
grid.js
Normal file
@@ -0,0 +1,178 @@
|
||||
class Grid {
|
||||
constructor(w, h, fill = 0) {
|
||||
this.width = w
|
||||
this.height = h
|
||||
this.data = utils.createGridArray(w, h, fill)
|
||||
}
|
||||
|
||||
forEach(func) {
|
||||
this.data.map((r, y) => r.map((e, x) => func(e, new Point(x, y), this)))
|
||||
return this
|
||||
}
|
||||
|
||||
mapMut(func) { return this.forEach((e, pt, grid) => grid.set(pt, func(e, pt, grid))) }
|
||||
|
||||
fill(n) { return this.mapMut(() => n) }
|
||||
|
||||
fillFromArr(arr) {
|
||||
if (arr[0].length != this.width) {
|
||||
console.warn(`Grid.fillFromArr: Row size ${arr[0].length} does not match grid width ${this.width}`)
|
||||
}
|
||||
|
||||
if (arr.length != this.height) {
|
||||
console.warn(`Grid.fillFromArr: Column size ${arr.length} does not match grid height ${this.height}`)
|
||||
}
|
||||
|
||||
return this.mapMut((_, pt) => arr[pt.y][pt.x])
|
||||
}
|
||||
|
||||
fillFromStr(str) { return this.fillFromArr(str.split("\n")) }
|
||||
|
||||
static fromStr(str) {
|
||||
let arr = str.split("\n")
|
||||
return new Grid(arr[0].length, arr.length).fillFromArr(arr)
|
||||
}
|
||||
|
||||
get(pt) {
|
||||
if (this.contains(pt)) {
|
||||
return this.data[pt.y][pt.x]
|
||||
} else {
|
||||
console.error("Grid.get: Grid does not contain point " + pt + ":\n" + this)
|
||||
}
|
||||
}
|
||||
|
||||
set(pt, val) {
|
||||
if (this.contains(pt)) {
|
||||
this.data[pt.y][pt.x] = val
|
||||
} else {
|
||||
console.error("Grid.set: does not contain point " + pt + ":\n" + this)
|
||||
}
|
||||
}
|
||||
|
||||
getColumn(x) {
|
||||
if (x >= 0 && x < this.width) {
|
||||
return this.data.map((row) => row[x])
|
||||
} else {
|
||||
console.error("Grid.getColumn: does not contain column " + x + ":\n" + this)
|
||||
}
|
||||
}
|
||||
|
||||
getRow(y) {
|
||||
if (y >= 0 && y < this.height) {
|
||||
return this.data[y]
|
||||
} else {
|
||||
console.error("Grid.getRow: does not contain row " + y + ":\n" + this)
|
||||
}
|
||||
}
|
||||
|
||||
getSection(pt1, pt2) {
|
||||
if (pt2.x >= pt1.x && pt2.y >= pt2.y) {
|
||||
return new Grid(pt2.x - pt1.x + 1, pt2.y - pt1.y + 1).mapMut((_, pt) => this.get(pt.add(pt1)))
|
||||
} else {
|
||||
console.error("Grid.getSection: Second point " + pt2 + " behind first point " + pt1 + ":\n" + this)
|
||||
}
|
||||
}
|
||||
|
||||
findAll(func) {
|
||||
let points = []
|
||||
this.forEach((e, pt, grid) => func(e, pt, grid) ? points.push(pt) : 0)
|
||||
return points
|
||||
}
|
||||
|
||||
contains(pt) { return pt.x >= 0 && pt.x < this.width && pt.y >= 0 && pt.y < this.height }
|
||||
|
||||
getAdjNeighbors(pt) { return pt.getUnfilteredAdjNeighbors().filter((pt) => this.contains(pt)) }
|
||||
getDiagNeighbors(pt) { return pt.getUnfilteredDiagNeighbors().filter((pt) => this.contains(pt)) }
|
||||
getAllNeighbors(pt) { return pt.getUnfilteredAllNeighbors().filter((pt) => this.contains(pt)) }
|
||||
getAdjNeighborsThat(pt, func) { return pt.getUnfilteredAdjNeighbors().filter((pt) => this.contains(pt) && func(pt)) }
|
||||
getDiagNeighborsThat(pt, func) { return pt.getUnfilteredDiagNeighbors().filter((pt) => this.contains(pt) && func(pt)) }
|
||||
getAllNeighborsThat(pt, func) { return pt.getUnfilteredAllNeighbors().filter((pt) => this.contains(pt) && func(pt)) }
|
||||
|
||||
static BFS_CONTINUE = 0
|
||||
static BFS_STOP = 1
|
||||
static BFS_END = 2
|
||||
|
||||
bfs(pt, func, limit = 1000) {
|
||||
let visited = [].pt
|
||||
let toVisit = [pt].pt
|
||||
let count = 0
|
||||
let end
|
||||
|
||||
toVisit[0].path = [pt]
|
||||
|
||||
out: while (toVisit.length > 0 && count++ < limit) {
|
||||
let newToVisit = [].pt
|
||||
|
||||
toVisit.sort()
|
||||
|
||||
for (let i = 0; i < toVisit.length; i++) {
|
||||
let result = func(this.get(toVisit[i]), toVisit[i], this, visited);
|
||||
|
||||
if (result == Grid.BFS_CONTINUE) {
|
||||
newToVisit.pushUniq(...this.getAdjNeighborsThat(toVisit[i], (pt) => !pt.isIn(visited)).map((pt) => (pt.path = [...toVisit[i].path, pt], pt)))
|
||||
}
|
||||
|
||||
if (result == Grid.BFS_END) {
|
||||
end = toVisit[i]
|
||||
break out
|
||||
}
|
||||
}
|
||||
|
||||
visited.pushUniq(...toVisit)
|
||||
toVisit = newToVisit
|
||||
}
|
||||
|
||||
if (count >= limit) {
|
||||
console.warn("Limit reached. Aborted.")
|
||||
}
|
||||
|
||||
return end || visited[visited.length - 1]
|
||||
}
|
||||
|
||||
transpose() {
|
||||
this.data = this.data.transpose()
|
||||
this.width = this.data[0].length
|
||||
this.height = this.data.length
|
||||
return this
|
||||
}
|
||||
|
||||
reflectX() {
|
||||
this.data = this.data.map((row) => row.reverse())
|
||||
return this
|
||||
}
|
||||
|
||||
reflectY() {
|
||||
this.data.reverse()
|
||||
return this
|
||||
}
|
||||
|
||||
rotate90() { return this.transpose().reflectX() }
|
||||
rotate180() { return this.reflectX().reflectY() }
|
||||
rotate270() { return this.reflectX().transpose() }
|
||||
|
||||
rotate(n) {
|
||||
for (let i = 0; i < Math.abs(n); i++) {
|
||||
this[n > 0 ? "rotate90" : "rotate270"]()
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
allTransformations() {
|
||||
return [
|
||||
this.copy(),
|
||||
this.copy().rotate90(),
|
||||
this.copy().rotate180(),
|
||||
this.copy().rotate270(),
|
||||
this.copy().reflectX(),
|
||||
this.copy().reflectX().transpose().reflectX(),
|
||||
this.copy().reflectY(),
|
||||
this.copy().transpose()
|
||||
]
|
||||
}
|
||||
|
||||
copy() { return new Grid(this.width, this.height).mapMut((_, pt) => this.get(pt).copyDeep()) }
|
||||
toString(sep = "\t", ...pts) { return this.data.map((r, y) => r.map((e, x) => new Point(x, y).isIn(pts) ? "P" : e).join(sep)).join("\n") }
|
||||
print(sep = "\t", ...pts) { console.log(this.toString(sep, pts)) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user