Init.
This commit is contained in:
commit
011ea8bdeb
9 changed files with 239 additions and 0 deletions
133
src/main.ts
Executable file
133
src/main.ts
Executable file
|
@ -0,0 +1,133 @@
|
|||
#!/usr/bin/env -S deno run -A
|
||||
|
||||
import { writeAll } from "https://deno.land/std@0.224.0/io/mod.ts"
|
||||
import { mkSimplexNoise, SimplexNoise } from "npm:@spissvinkel/simplex-noise@1.0.1"
|
||||
|
||||
async function main() {
|
||||
const connections = Listener.listen(8080)
|
||||
const fishes = await Fishes.load("./assets/fishes")
|
||||
for await (const moved of fishes.movements()) {
|
||||
console.log(moved.name, "at", moved.position)
|
||||
const displayed = esc_clear() + fishes.fishes.map(display).join("") + "\n"
|
||||
connections.send(displayed)
|
||||
}
|
||||
}
|
||||
|
||||
class Listener {
|
||||
private constructor(
|
||||
private connections: Set<Deno.TcpConn>,
|
||||
) {}
|
||||
|
||||
public static listen(port: number) {
|
||||
const connections = new Set<Deno.TcpConn>()
|
||||
;(async () => {
|
||||
for await (const conn of Deno.listen({ transport: "tcp", port })) connections.add(conn)
|
||||
})()
|
||||
return new Listener(connections)
|
||||
}
|
||||
|
||||
public async send(text: string) {
|
||||
const encoded = new TextEncoder().encode(text)
|
||||
const promises = new Array<Promise<unknown>>()
|
||||
for (const conn of this.connections) {
|
||||
const promise = writeAll(conn, encoded).catch((_) => this.connections.delete(conn))
|
||||
promises.push(promise)
|
||||
}
|
||||
await Promise.all(promises)
|
||||
}
|
||||
}
|
||||
|
||||
class Fishes {
|
||||
private constructor(
|
||||
public fishes: Fish[],
|
||||
) {}
|
||||
|
||||
public static async load(path: string, max = 999) {
|
||||
const fishes = new Array<Fish>()
|
||||
for await (const entry of Deno.readDir(path)) {
|
||||
if (fishes.length >= max) break
|
||||
if (!entry.isFile) continue
|
||||
if (!entry.name.endsWith(".txt")) continue
|
||||
const name = entry.name.slice(0, entry.name.length - ".txt".length)
|
||||
const file_path = `${path}/${entry.name}`
|
||||
const content = await Deno.readTextFile(file_path)
|
||||
const shape = content.split("\n").filter((l) => l.length > 0)
|
||||
const size: [number, number] = [shape.map((l) => l.length).reduce((a, b) => Math.max(a, b)), shape.length]
|
||||
fishes.push(new Fish(name, size, shape, [Math.random() * 20, Math.random() * 20]))
|
||||
}
|
||||
console.log("Loaded", fishes.length, "fishes.")
|
||||
return new Fishes(fishes)
|
||||
}
|
||||
|
||||
public async *movements() {
|
||||
const noise = mkSimplexNoise(Math.random)
|
||||
while (true) {
|
||||
const picked = pick(this.fishes)
|
||||
picked.move_into(noise, [[0, 0], [80, 25]])
|
||||
yield picked
|
||||
await new Promise((res) => setTimeout(res, 200))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Fish {
|
||||
public constructor(
|
||||
public name: string,
|
||||
public size: [x: number, y: number],
|
||||
public shape: string[],
|
||||
public position: [number, number] = [0, 0],
|
||||
public age = 0,
|
||||
) {}
|
||||
|
||||
public move_into(noise: SimplexNoise, bounds: [[number, number], [number, number]]) {
|
||||
const base = [...this.name].map((c) => c.charCodeAt(0)).reduce((a, b) => a + b)
|
||||
const velocityX = noise.noise2D(this.age % 100, base)
|
||||
const velocityY = noise.noise2D(this.age % 100, base + 100)
|
||||
this.position[0] += velocityX
|
||||
this.position[1] += velocityY
|
||||
this.age += 1
|
||||
if (this.position[0] < bounds[0][0]) this.position[0] = bounds[0][0]
|
||||
if (this.position[1] < bounds[0][1]) this.position[1] = bounds[0][1]
|
||||
if (this.position[0] > bounds[1][0]) this.position[0] = bounds[1][0]
|
||||
if (this.position[1] > bounds[1][1]) this.position[1] = bounds[1][1]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ```
|
||||
* _ <name>
|
||||
* /
|
||||
* #####
|
||||
* #####
|
||||
* #####
|
||||
* ```
|
||||
*
|
||||
* @param fish Fish to display.
|
||||
*/
|
||||
function display(fish: Fish) {
|
||||
const tick_pad = " ".repeat(fish.size[0])
|
||||
// TODO : add position and color
|
||||
return [
|
||||
tick_pad + " _ " + fish.name,
|
||||
tick_pad + "/",
|
||||
...fish.shape,
|
||||
]
|
||||
.map((l, i) => esc_goto([fish.position[0], fish.position[1] + i]) + l)
|
||||
.join("") + "\n"
|
||||
}
|
||||
|
||||
function pick<T>(arr: T[], random = Math.random) {
|
||||
const index = Math.floor(random() * arr.length)
|
||||
return arr[index]
|
||||
}
|
||||
|
||||
function esc_goto(pos: [number, number]) {
|
||||
const [x, y] = pos.map(Math.floor)
|
||||
return "\u001B[" + y + ";" + x + "H"
|
||||
}
|
||||
|
||||
function esc_clear() {
|
||||
return "\u001B[3J"
|
||||
}
|
||||
|
||||
if (import.meta.main) await main()
|
Loading…
Add table
Add a link
Reference in a new issue