|
import {randomBytes} from "crypto" |
|
import {stdout} from "process" |
|
import {log} from "console" |
|
const sleep = ms => new Promise(r => setTimeout(r, ms)) |
|
const clear = _ => stdout.write("\x1b[H\x1b[2J\x1b[3J") |
|
clear() |
|
|
|
const RandomnessThingy = class { |
|
static instance = null |
|
static getInstance(){ |
|
if(!RandomnessThingy.instance) |
|
RandomnessThingy.instance = new RandomnessThingy() |
|
return RandomnessThingy.instance |
|
} |
|
|
|
maxBestRuns = 10 |
|
bestRuns = [] // Perhaps "best" actually means "worse entropy"? |
|
randomAmt = 100 |
|
|
|
// TODO: add feed from TRNG (from my webcam-entropy) |
|
randomFns = { |
|
crypto: () => [...Float32Array.from(new Uint16Array(randomBytes(2*this.randomAmt).buffer)).map(x=>x/65536)], |
|
pseudo: () => Array.from(Array(this.randomAmt), _ => Math.random()), |
|
sequential: (n = Math.random()) => Array.from(Array(this.randomAmt), (_,i) => Math.abs(Math.sin(Math.PI * 2 * (i+n) / this.randomAmt))), //1-i/(this.randomAmt-1) |
|
} |
|
|
|
// "Best" i could find is 38 out of 100 (using the crypto PRNG source). |
|
|
|
randomFn = this.randomFns.crypto |
|
|
|
constructor(){ |
|
this.currentArr = null |
|
this.currentRandom = null |
|
this.currentIterations = 0 |
|
this.currentDiversity = 0 |
|
this.currentDiversityHistory = [] |
|
} |
|
saveIfBest(){ |
|
let {bestRuns, maxBestRuns, currentRandom, currentDiversityHistory, currentDiversity} = this |
|
this.bestRuns = [...bestRuns, { |
|
randomSeed: currentRandom, |
|
diversityHistory: currentDiversityHistory |
|
}].sort((a,b)=>b.diversityHistory.at(-1)-a.diversityHistory.at(-1)).slice(0,maxBestRuns) |
|
// I should do a better sorting, considering the whole diversityHistory... |
|
} |
|
regen(){ |
|
|
|
this.currentRandom = this.randomFn() |
|
this.currentArr = this.currentRandom |
|
this.currentIterations = 0 |
|
this.currentDiversityHistory = [] |
|
this.calculateDiversity() |
|
} |
|
iterate(){ |
|
let arr = this.currentArr |
|
|
|
this.currentArr = arr.map(val => |
|
arr[Math.round(val*(arr.length-1))] |
|
) |
|
} |
|
calculateDiversity(){ |
|
this.currentDiversity = new Set(this.currentArr).size |
|
this.currentDiversityHistory.push(this.currentDiversity) |
|
} |
|
printArr(arr){ |
|
let lines = [""] |
|
let cols = stdout.columns |
|
for(let num of arr){ |
|
let numStr = num.toFixed(12)+", " |
|
if(lines.at(-1).length+numStr.length >= cols){ |
|
lines.push("") |
|
} |
|
lines[lines.length-1] += numStr |
|
|
|
} |
|
log(lines.join("\n")) |
|
log("") |
|
} |
|
checkDiversity(){ |
|
const maxLastAmt = 3 |
|
const {currentDiversityHistory} = this |
|
if(currentDiversityHistory.length <= maxLastAmt) |
|
return true |
|
|
|
if(currentDiversityHistory.at(-1) == 100) |
|
return true |
|
|
|
for(let i = 0; i < maxLastAmt; i++){ |
|
if(currentDiversityHistory.at(-1-i) != currentDiversityHistory.at(-2-i)) |
|
return true |
|
} |
|
|
|
return false |
|
|
|
} |
|
async main(){ |
|
this.regen() |
|
|
|
while(1){ |
|
clear() |
|
|
|
log("Current:") |
|
this.printArr(this.currentArr) |
|
|
|
log("Diversity history: ", this.currentDiversityHistory.join(", ")) |
|
log("=".repeat(stdout.columns)) |
|
log("Best runs:") |
|
log(this.bestRuns.map(x=>x.diversityHistory).join("\n")) |
|
log("=".repeat(stdout.columns)) |
|
if(this.bestRuns.length > 0){ |
|
log("Best random seq:") |
|
this.printArr(this.bestRuns[0].randomSeed) |
|
} |
|
this.iterate() |
|
this.calculateDiversity() |
|
|
|
if(!this.checkDiversity()){ |
|
this.saveIfBest() |
|
this.regen() |
|
} else if(this.currentDiversityHistory.at(-1) == 100 && this.currentDiversityHistory.length > 3){ |
|
log("Got the best of all!") |
|
break |
|
} |
|
await sleep(50) |
|
} |
|
} |
|
|
|
} |
|
|
|
RandomnessThingy.getInstance().main() |