/**
* Overrides the default set methods to use `JSON.stringify`. This allows it to
* work with objects and arrays. See the docs for the built in [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set).
* @extends {Set}
*/
export class _Set extends Set {
/**
* Creates a new set. Can also be done with {@link constructors.S}.
* @param {Iterable.<*>} iterable Optional iterable to initialize the set with
* @returns {_Set}
*/
constructor(iterable = []) {
super()
for (const element of iterable) {
this.add(element)
}
}
add(item) {
return super.add(JSON.stringify(item))
}
has(item) {
return super.has(JSON.stringify(item))
}
delete(item) {
super.delete(JSON.stringify(item))
return item
}
/**
* Makes a copy of the set
* @returns {_Set}
*/
clone() {
return new _Set(this)
}
/**
* Creates set union in place with `other` and returns this set
* @param {_Set} other
* @returns {_Set}
* @example S([1, 2, 3]).union(S([3, 4, 5])) // { 1, 2, 3, 4, 5 }
*/
union(other) {
for (const item of other) this.add(item)
return this
}
/**
* Creates set intersection in place with `other` and returns this set
* @param {_Set} other
* @returns {_Set}
* @example S([1, 2, 3]).intersect(S([3, 4, 5])) // { 3 }
*/
intersect(other) {
for (const item of this)
if (!other.has(item))
this.delete(item)
return this
}
/**
* Creates set difference in place with `other` and returns this set
* @param {_Set} other
* @returns {_Set}
* @example S([1, 2, 3]).diff(S([3, 4, 5])) // { 1, 2 }
*/
diff(other) {
for (const item of other) this.delete(item)
return this
}
/**
* Creates symmetric set difference in place with `other` and returns this set
* @param {_Set} other
* @returns {_Set}
* @example S([1, 2, 3]).symdiff(S([3, 4, 5])) // { 1, 2, 4, 5 }
*/
symdiff(other) {
for (const item of other)
if (this.has(item)) this.delete(item)
else this.add(item)
return this
}
*[Symbol.iterator]() {
for (const item of super[Symbol.iterator]()) {
yield JSON.parse(item)
}
}
values() {
return super.values().map(JSON.parse)
}
}
/** Represents a 2D vector. */
export class Vec {
/**
* Creates a new vector. Can also be done with {@link constructors.V}.
* @param {number} x The x component
* @param {number} y The y component
* @returns {Vec}
*/
constructor(x, y) {
if (Array.isArray(x)) {
[x, y] = x
} if (typeof x === "object") {
y = x.y
x = x.x
}
if (typeof x !== "number" || typeof y !== "number") {
throw new TypeError(`Vec constructor must be given two numbers (not ${x} and ${y})`)
}
this.x = x
this.y = y
}
/**
* Moves the vector up by `n` units (decreases `y`), in place.
* @param {number} n The number of units to move
* @returns {Vec} The new vector
*/
upn(n) { this.y -= n; return this }
/**
* Moves the vector down by `n` units (increases `y`), in place.
* @param {number} n The number of units to move
* @returns {Vec} The new vector
*/
downn(n) { this.y += n; return this }
/**
* Moves the vector left by `n` units (decreases `x`), in place.
* @param {number} n The number of units to move
* @returns {Vec} The new vector
*/
leftn(n) { this.x -= n; return this }
/**
* Moves the vector right by `n` units (increases `x`), in place.
* @param {number} n The number of units to move
* @returns {Vec} The new vector
*/
rightn(n) { this.x += n; return this }
/**
* Moves the vector up by 1 unit using {@link Vec.upn}
* @returns {Vec} The new vector
*/
up() { return this.upn(1) }
/**
* Moves the vector down by 1 unit using {@link Vec.downn}
* @returns {Vec} The new vector
*/
down() { return this.downn(1) }
/**
* Moves the vector left by 1 unit using {@link Vec.leftn}
* @returns {Vec} The new vector
*/
left() { return this.leftn(1) }
/**
* Moves the vector right by 1 unit using {@link Vec.rightn}
* @returns {Vec} The new vector
*/
right() { return this.rightn(1) }
/**
* Add another vector to this one
* @param {Vec} other The other vector
* @returns {Vec} A new vector
*/
add(other) {
return new Vec(this.x + other.x, this.y + other.y)
}
/**
* Subtract another vector from this one
* @param {Vec} other The other vector
* @returns {Vec} A new vector
*/
sub(other) {
return new Vec(this.x - other.x, this.y - other.y)
}
/**
* Multiplies this vector by `factor`
* @param {number} factor The factor to multiply by
* @returns {Vec} A new vector
*/
mul(factor) {
return new Vec(this.x * factor, this.y * factor)
}
/**
* Divides this vector by `factor`
* @param {number} factor The factor to divide by
* @returns {Vec} A new vector
*/
div(factor) {
return new Vec(this.x / factor, this.y / factor)
}
/**
* Gets a list of the four adjacent vectors to this one
* @returns {Vec[]}
*/
adj() {
return [
this.add(new Vec(0, -1)),
this.add(new Vec(0, 1)),
this.add(new Vec(-1, 0)),
this.add(new Vec(1, 0))
]
}
inrect(a, b) {
if (b === undefined) {
b = a
a = new Vec(0, 0)
}
if (typeof b === "number") b = new Vec(b - 1, b - 1)
return this.x >= a.x && this.x <= b.x && this.y >= a.y && this.y <= b.y
}
/**
* Gets the cell at this vector on `grid`
* @template T
* @param {T[][]} grid The grid to get the cell from
* @returns {T}
*/
on(grid) {
return grid[this.y]?.[this.x]
}
/**
* Gets the size of the vector
* @returns {number}
*/
get size() {
return Math.hypot(this.x, this.y)
}
/**
* Clones the vector
* @returns {Vec}
*/
clone() {
return new Vec(this.x, this.y)
}
*[Symbol.iterator]() {
yield this.x
yield this.y
}
[Symbol.toPrimitive](hint) {
// if (hint === "string") return this.toString()
return this.size
}
}