@haelp/teto
Version:
A typescript-based controllable TETR.IO client.
188 lines (187 loc) • 6.69 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "GarbageQueue", {
enumerable: true,
get: function() {
return GarbageQueue;
}
});
const _utils = require("../utils");
_export_star(require("./legacy"), exports);
function _export_star(from, to) {
Object.keys(from).forEach(function(k) {
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
Object.defineProperty(to, k, {
enumerable: true,
get: function() {
return from[k];
}
});
}
});
return from;
}
class GarbageQueue {
options;
queue;
lastTankTime = 0;
lastColumn = null;
hasChangedColumn = false;
lastReceivedCount = 0;
rng;
// for opener phase calculations
sent = 0;
constructor(options){
this.options = (0, _utils.deepCopy)(options);
if (!this.options.cap.absolute) this.options.cap.absolute = Number.MAX_SAFE_INTEGER;
this.queue = [];
this.rng = new _utils.RNG(this.options.seed);
}
snapshot() {
return {
seed: this.rng.seed,
lastTankTime: this.lastTankTime,
lastColumn: this.lastColumn,
sent: this.sent,
hasChangedColumn: this.hasChangedColumn,
lastReceivedCount: this.lastReceivedCount,
queue: (0, _utils.deepCopy)(this.queue)
};
}
fromSnapshot(snapshot) {
this.queue = (0, _utils.deepCopy)(snapshot.queue);
this.lastTankTime = snapshot.lastTankTime;
this.lastColumn = snapshot.lastColumn;
this.rng = new _utils.RNG(snapshot.seed);
this.sent = snapshot.sent;
this.hasChangedColumn = snapshot.hasChangedColumn;
this.lastReceivedCount = snapshot.lastReceivedCount;
}
rngex() {
return this.rng.nextFloat();
}
get size() {
return this.queue.reduce((a, b)=>a + b.amount, 0);
}
resetReceivedCount() {
this.lastReceivedCount = 0;
}
receive(...args) {
this.queue.push(...args.filter((arg)=>arg.amount > 0));
while(this.size > this.options.cap.absolute){
const total = this.size;
if (this.queue.at(-1).amount <= total - this.options.cap.absolute) {
this.queue.pop();
} else {
this.queue.at(-1).amount -= total - this.options.cap.absolute;
}
}
}
confirm(cid, gameid, frame) {
const obj = this.queue.find((g)=>g.cid === cid && g.gameid === gameid);
if (!obj) return false;
obj.frame = frame;
obj.confirmed = true;
return true;
}
cancel(amount, pieceCount, legacy = {}) {
let send = amount, cancel = 0;
let cancelled = [];
if (pieceCount + 1 <= this.options.openerPhase - (legacy.openerPhase ? 1 : 0) && this.size >= this.sent) cancel += amount;
while((send > 0 || cancel > 0) && this.size > 0){
this.queue[0].amount--;
if (cancelled.length === 0 || cancelled[cancelled.length - 1].cid !== this.queue[0].cid) {
cancelled.push({
...this.queue[0],
amount: 1
});
} else {
cancelled[cancelled.length - 1].amount++;
}
if (this.queue[0].amount <= 0) {
this.queue.shift();
if (this.rngex() < this.options.messiness.change) {
this.#reroll_column();
this.hasChangedColumn = true;
}
}
if (send > 0) send--;
else cancel--;
}
this.sent += send;
return [
send,
cancelled
];
}
get #columnWidth() {
return Math.max(0, this.options.boardWidth - (this.options.garbage.holeSize - 1));
}
#reroll_column() {
const centerBuffer = this.options.messiness.center ? Math.round(this.options.boardWidth / 5) : 0;
let col;
if (this.options.messiness.nosame && this.lastColumn !== null) {
col = centerBuffer + Math.floor(this.rngex() * (this.#columnWidth - 1 - 2 * centerBuffer));
if (col >= this.lastColumn) col++;
} else {
col = centerBuffer + Math.floor(this.rngex() * (this.#columnWidth - 2 * centerBuffer));
}
this.lastColumn = col;
return col;
}
tank(frame, cap, hard) {
if (this.queue.length === 0) return [];
const res = [];
this.queue = this.queue.sort((a, b)=>a.frame - b.frame);
if (this.options.messiness.timeout && frame >= this.lastTankTime + this.options.messiness.timeout) {
this.#reroll_column();
this.hasChangedColumn = true;
}
const tankAll = false;
const lines = tankAll ? 400 : Math.floor(Math.min(cap, this.options.cap.max));
for(let i = 0; i < lines && this.queue.length !== 0; i++){
const item = this.queue[0];
// TODO: Fix this
// The real game uses an "active" system where garbages have a proptery, may be an issue later
if (item.frame + this.options.garbage.speed > (hard ? frame : frame - 1)) break;
item.amount--;
this.lastReceivedCount++;
let col = this.lastColumn;
if ((col === null || this.rngex() < this.options.messiness.within) && !this.hasChangedColumn) {
col = this.#reroll_column();
this.hasChangedColumn = true;
}
res.push({
...item,
amount: 1,
column: col,
id: item.cid
});
this.hasChangedColumn = false;
if (item.amount <= 0) {
this.queue.shift();
if (this.rngex() < this.options.messiness.change) {
this.#reroll_column();
this.hasChangedColumn = true;
}
}
}
return res;
}
round(amount) {
switch(this.options.rounding){
case "down":
return Math.floor(amount);
case "rng":
const floored = Math.floor(amount);
if (floored === amount) return floored;
const decimal = amount - floored;
return floored + (this.rngex() < decimal ? 1 : 0);
default:
throw new Error(`Invalid rounding mode ${this.options.rounding}`);
}
}
}
//# sourceMappingURL=index.js.map