UNPKG

@haelp/teto

Version:

A typescript-based controllable TETR.IO client.

188 lines (187 loc) 6.69 kB
"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