diff --git a/fumen-utils/.gitignore b/fumen-utils/.gitignore new file mode 100644 index 0000000..d5f19d8 --- /dev/null +++ b/fumen-utils/.gitignore @@ -0,0 +1,2 @@ +node_modules +package-lock.json diff --git a/fumen-utils/list.js b/fumen-utils/list.js new file mode 100644 index 0000000..a703b4f --- /dev/null +++ b/fumen-utils/list.js @@ -0,0 +1,25 @@ +const fumen = require('tetris-fumen'); +const { argv } = require('node:process'); +const { fumenPageToInputState, fumenPagePlayMove, runFishCli } = require('./src/utils'); + +async function main() { + if (argv.length != 3) { + throw new Error('usage: node list '); + } + + let url = argv[2]; + let pages = fumen.decoder.decode(url); + if (pages.length !== 1) { + throw new Error('expected exactly one page'); + } + + let inputState = fumenPageToInputState(pages[0]); + let output = await runFishCli(['list'], inputState); + for (let move of output.moves) { + pages.push(fumenPagePlayMove(pages[0], move, { lock: false })); + } + + console.log(fumen.encoder.encode(pages)); +} + +main().catch(e => console.error(e)); diff --git a/fumen-utils/package.json b/fumen-utils/package.json new file mode 100644 index 0000000..c498ac1 --- /dev/null +++ b/fumen-utils/package.json @@ -0,0 +1,10 @@ +{ + "name": "fumen-utils", + "version": "1.0.0", + "description": "", + "author": "iitalics", + "license": "LGPL-2.1-or-later", + "dependencies": { + "tetris-fumen": "^1.1.3" + } +} diff --git a/fumen-utils/src/utils.js b/fumen-utils/src/utils.js new file mode 100644 index 0000000..98923d5 --- /dev/null +++ b/fumen-utils/src/utils.js @@ -0,0 +1,98 @@ +const fumen = require('tetris-fumen'); +const { Quiz } = require('tetris-fumen/lib/quiz'); +const { Page } = require('tetris-fumen/lib/decoder'); +const { env } = require('node:process'); +const { spawn } = require('node:child_process'); +const path = require('node:path'); + +function fumenPageToInputState(page) { + let board = new Array(40); + for (let y = 0; y < 23; y++) { + let row = board[y] = new Array(10); + for (let x = 0; x < 10; x++) { + row[x] = page.field.at(x, y); + if (row[x] === '_') { + row[x] = null; + } else if (row[x] === 'X') { + row[x] = 'G'; + } + } + } + board[23] = new Array(10); + for (let x = 0; x < 10; x++) { + board[23][x] = null + } + for (let y = 24; y < 40; y++) { + board[y] = board[23]; + } + + if (!page.flags.quiz) { + throw new Error('page doesnot specify queue'); + } + let quiz = new Quiz(page.comment); + let hold = quiz.hold; + let queue = [quiz.current, ...quiz.least]; + + return { + hold, + queue, + combo: 0, + back_to_back: 0, + board, + }; +} + +function fumenPagePlayMove(page, move, flags) { + let quiz = new Quiz(page.comment); + // console.log(`${move.location.x},${move.location.y},${move.location.orientation[0]}`); + return { + operation: { + type: move.location.type, + x: move.location.x, + y: move.location.y, + rotation: { + 'north': 'spawn', + 'east': 'right', + 'south': 'reverse', + 'west': 'left', + }[move.location.orientation], + }, + flags, + }; +} + +function runFishCli(args, input, cmd = getFishCliPath()) { + let fish = spawn(cmd, args, { stdio: ['pipe', 'pipe', 'inherit'] }); + + let data = []; + let output = null; + fish.stdout.on('data', (buf) => data.push(buf)); + fish.stdout.on('end', () => { + output = JSON.parse(Buffer.concat(data).toString()); + }); + + let exitPromise = new Promise((res, rej) => { + fish.on('exit', (code) => { + if (code === 0) { + res(output); + } else { + rej(new Error(`exit code: ${code}`)); + } + }); + }); + + if (input !== undefined) { + fish.stdin.end(JSON.stringify(input)); + } + return exitPromise; +} + +function getFishCliPath() { + return process.env['FISH_CLI'] || path.join(__dirname, '../../target/release/fish-cli'); +} + +module.exports = { + fumenPageToInputState, + fumenPagePlayMove, + runFishCli, +};