tetris-fumen
Version:
Fumen parser for tetris
231 lines (230 loc) • 9.02 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.encode = void 0;
var inner_field_1 = require("./inner_field");
var buffer_1 = require("./buffer");
var defines_1 = require("./defines");
var action_1 = require("./action");
var comments_1 = require("./comments");
var quiz_1 = require("./quiz");
var FieldConstants = {
GarbageLine: 1,
Width: 10,
};
function encode(pages) {
var updateField = function (prev, current) {
var _a = encodeField(prev, current), changed = _a.changed, values = _a.values;
if (changed) {
// フィールドを記録して、リピートを終了する
buffer.merge(values);
lastRepeatIndex = -1;
}
else if (lastRepeatIndex < 0 || buffer.get(lastRepeatIndex) === buffer_1.Buffer.tableLength - 1) {
// フィールドを記録して、リピートを開始する
buffer.merge(values);
buffer.push(0);
lastRepeatIndex = buffer.length - 1;
}
else if (buffer.get(lastRepeatIndex) < (buffer_1.Buffer.tableLength - 1)) {
// フィールドは記録せず、リピートを進める
var currentRepeatValue = buffer.get(lastRepeatIndex);
buffer.set(lastRepeatIndex, currentRepeatValue + 1);
}
};
var lastRepeatIndex = -1;
var buffer = new buffer_1.Buffer();
var prevField = (0, inner_field_1.createNewInnerField)();
var actionEncoder = (0, action_1.createActionEncoder)(FieldConstants.Width, 23, FieldConstants.GarbageLine);
var commentParser = (0, comments_1.createCommentParser)();
var prevComment = '';
var prevQuiz = undefined;
var innerEncode = function (index) {
var currentPage = pages[index];
currentPage.flags = currentPage.flags ? currentPage.flags : {};
var field = currentPage.field;
var currentField = field !== undefined ? (0, inner_field_1.createInnerField)(field) : prevField.copy();
// フィールドの更新
updateField(prevField, currentField);
// アクションの更新
var currentComment = currentPage.comment !== undefined
? ((index !== 0 || currentPage.comment !== '') ? currentPage.comment : undefined)
: undefined;
var piece = currentPage.operation !== undefined ? {
type: (0, defines_1.parsePiece)(currentPage.operation.type),
rotation: (0, defines_1.parseRotation)(currentPage.operation.rotation),
x: currentPage.operation.x,
y: currentPage.operation.y,
} : {
type: defines_1.Piece.Empty,
rotation: defines_1.Rotation.Reverse,
x: 0,
y: 22,
};
var nextComment;
if (currentComment !== undefined) {
if (currentComment.startsWith('#Q=')) {
// Quiz on
if (prevQuiz !== undefined && prevQuiz.format().toString() === currentComment) {
nextComment = undefined;
}
else {
nextComment = currentComment;
prevComment = nextComment;
prevQuiz = new quiz_1.Quiz(currentComment);
}
}
else {
// Quiz off
if (prevQuiz !== undefined && prevQuiz.format().toString() === currentComment) {
nextComment = undefined;
prevComment = currentComment;
prevQuiz = undefined;
}
else {
nextComment = prevComment !== currentComment ? currentComment : undefined;
prevComment = prevComment !== currentComment ? nextComment : prevComment;
prevQuiz = undefined;
}
}
}
else {
nextComment = undefined;
prevQuiz = undefined;
}
if (prevQuiz !== undefined && prevQuiz.canOperate() && currentPage.flags.lock) {
if ((0, defines_1.isMinoPiece)(piece.type)) {
try {
var nextQuiz = prevQuiz.nextIfEnd();
var operation = nextQuiz.getOperation(piece.type);
prevQuiz = nextQuiz.operate(operation);
}
catch (e) {
// console.error(e.message);
// Not operate
prevQuiz = prevQuiz.format();
}
}
else {
prevQuiz = prevQuiz.format();
}
}
var currentFlags = __assign({ lock: true, colorize: index === 0 }, currentPage.flags);
var action = {
piece: piece,
rise: !!currentFlags.rise,
mirror: !!currentFlags.mirror,
colorize: !!currentFlags.colorize,
lock: !!currentFlags.lock,
comment: nextComment !== undefined,
};
var actionNumber = actionEncoder.encode(action);
buffer.push(actionNumber, 3);
// コメントの更新
if (nextComment !== undefined) {
var comment = escape(currentPage.comment);
var commentLength = Math.min(comment.length, 4095);
buffer.push(commentLength, 2);
// コメントを符号化
for (var index_1 = 0; index_1 < commentLength; index_1 += 4) {
var value = 0;
for (var count = 0; count < 4; count += 1) {
var newIndex = index_1 + count;
if (commentLength <= newIndex) {
break;
}
var ch = comment.charAt(newIndex);
value += commentParser.encode(ch, count);
}
buffer.push(value, 5);
}
}
else if (currentPage.comment === undefined) {
prevComment = undefined;
}
// 地形の更新
if (action.lock) {
if ((0, defines_1.isMinoPiece)(action.piece.type)) {
currentField.fill(action.piece);
}
currentField.clearLine();
if (action.rise) {
currentField.riseGarbage();
}
if (action.mirror) {
currentField.mirror();
}
}
prevField = currentField;
};
for (var index = 0; index < pages.length; index += 1) {
innerEncode(index);
}
// テト譜が短いときはそのまま出力する
// 47文字ごとに?が挿入されるが、実際は先頭にv115@が入るため、最初の?は42文字後になる
var data = buffer.toString();
if (data.length < 41) {
return data;
}
// ?を挿入する
var head = [data.substr(0, 42)];
var tails = data.substring(42);
var split = tails.match(/[\S]{1,47}/g) || [];
return head.concat(split).join('?');
}
exports.encode = encode;
// フィールドをエンコードする
// 前のフィールドがないときは空のフィールドを指定する
// 入力フィールドの高さは23, 幅は10
function encodeField(prev, current) {
var FIELD_TOP = 23;
var FIELD_MAX_HEIGHT = FIELD_TOP + 1;
var FIELD_BLOCKS = FIELD_MAX_HEIGHT * FieldConstants.Width;
var buffer = new buffer_1.Buffer();
// 前のフィールドとの差を計算: 0〜16
var getDiff = function (xIndex, yIndex) {
var y = FIELD_TOP - yIndex - 1;
return current.getNumberAt(xIndex, y) - prev.getNumberAt(xIndex, y) + 8;
};
// データの記録
var recordBlockCounts = function (diff, counter) {
var value = diff * FIELD_BLOCKS + counter;
buffer.push(value, 2);
};
// フィールド値から連続したブロック数に変換
var changed = true;
var prev_diff = getDiff(0, 0);
var counter = -1;
for (var yIndex = 0; yIndex < FIELD_MAX_HEIGHT; yIndex += 1) {
for (var xIndex = 0; xIndex < FieldConstants.Width; xIndex += 1) {
var diff = getDiff(xIndex, yIndex);
if (diff !== prev_diff) {
recordBlockCounts(prev_diff, counter);
counter = 0;
prev_diff = diff;
}
else {
counter += 1;
}
}
}
// 最後の連続ブロックを処理
recordBlockCounts(prev_diff, counter);
if (prev_diff === 8 && counter === FIELD_BLOCKS - 1) {
changed = false;
}
return {
changed: changed,
values: buffer,
};
}