Giter Site home page Giter Site logo

tetris-fumen's Introduction

tetris-fumen

tetris-fumen is a parser library to encode/decode tetris game state. Data compatible with fumen, 連続テト譜エディタ can be created and manipulated.

Features

  • Tetris game state can be packed into a string and easily unpacked
  • Provide simple manipulation for game field
  • Decoder supports v115 and v110
  • Encoder supports v115 only

Installation

npm install tetris-fumen

Specifications

Field

Field have 10x23 play area and 1 garbage line.

    |__________|          22 // Top
    |__________|          21
    |__________|          20
    |__________|          19
   ~~~~~~~~~~~~~~~         .
    |__________|           .
    |___JJJ____|           .
    |L__ZZJS___|           2
    |L___ZZSSOO|           1
    |LL IIIISOO|  <-  y =  0
    ============
    |XXXXXXXXX_|  <-  y = -1 // Garbage Line
     ^        ^
 x = 0 ...... 9

Mino

The type of mino is represented by below strings:

  • 'T', 'I', 'O', 'L', 'J', 'S', 'Z'
  • Gray => 'X' or 'GRAY'
  • Empty => '_' or 'EMPTY'

The rotation states of mino is represented by below strings:

  • No rotate => 'spawn'
  • Clockwise => 'right'
  • Counter Clockwise => 'left'
  • 180 rotation => 'reverse'

The position of mino is represented by (x, y); See harddrop's article SRS/How Guideline SRS Really Works for details

Getting Started

1. Decode

Data used in the code below

Javascript

const { decoder } = require('tetris-fumen');

const data = "v115@vhGRQYHAvItJEJmhCAUGJKJJvMJTNJGBJFKYPAUEzP?EJG98AQmqhECDdCA";
// const data = "https://harddrop.com/fumen/?v115@vhGRQYHAvItJEJmhCAUGJKJJvMJTNJGBJFKYPAUEzP?EJG98AQmqhECDdCA";  // Allow URL format
const pages = decoder.decode(data);

console.log(pages.length);  // 7

console.log(pages[0].comment);  // 'Opening'
console.log(pages[0].operation);  // { type: 'I', rotation: 'spawn', x: 4, y: 0 }

Typescript

import { decoder, Page } from 'tetris-fumen';

const data = 'v115@vhGRQYHAvItJEJmhCAUGJKJJvMJTNJGBJFKYPAUEzP?EJG98AQmqhECDdCA';
// const data = "https://harddrop.com/fumen/?v115@vhGRQYHAvItJEJmhCAUGJKJJvMJTNJGBJFKYPAUEzP?EJG98AQmqhECDdCA";  // Allow URL format
const pages: Page[] = decoder.decode(data);

console.log(pages.length);  // 7

console.log(pages[0].comment);  // 'Opening'
console.log(pages[0].operation);  // { type: 'I', rotation: 'spawn', x: 4, y: 0 }

2.1. Update decoded fumen

Loading data in the code below Generated data by the code below

Javascript

const { decoder, encoder } = require('tetris-fumen');

const pages = decoder.decode('v115@9gI8AeI8AeI8AeI8KeAgH');
pages[0].comment = '4 Lines';
pages[0].operation = { type: 'I', rotation: 'left', x: 9, y: 1 };

console.log(encoder.encode(pages));  // v115@9gI8AeI8AeI8AeI8Ke5IYJA0no2AMOprDTBAAA

2.2. Create from scratch

Data generated by the code below

Javascript

const { encoder, Field } = require('tetris-fumen');

const pages = [];
pages.push({
    field: Field.create(
        'LLL_______' +
        'LOO_______' +
        'JOO_______' +
        'JJJ_______',
        'XXXXXXXXX_',
    ),
    comment: 'Perfect Clear Opener',
});

pages.push({
    operation: {
        type: 'T', rotation: 'left', x: 9, y: 1,
    },
});

pages.push({
    operation: {
        type: 'Z', rotation: 'spawn', x: 7, y: 0,
    },
});

pages.push({
    operation: {
        type: 'S', rotation: 'spawn', x: 8, y: 2,
    },
});

pages.push({
    comment: 'Success: 61.19 %',
    flags: {
        mirror: true,
    },
});

pages.push({
    comment: '(Mirror)',
});

console.log(encoder.encode(pages));  // v115@9gilGeglRpGeg...../~/.....ciNEyoAVB

Typescript

import { encoder, EncodePage, Field } from 'tetris-fumen';

const pages: EncodePage[] = [];
pages.push({
    field: Field.create(
        'LLL_______' +
        'LOO_______' +
        'JOO_______' +
        'JJJ_______',
        'XXXXXXXXX_',
    ),
    comment: 'Perfect Clear Opener',
});

pages.push({
    operation: {
        type: 'T', rotation: 'left', x: 9, y: 1,
    },
});

pages.push({
    operation: {
        type: 'Z', rotation: 'spawn', x: 7, y: 0,
    },
});

pages.push({
    operation: {
        type: 'S', rotation: 'spawn', x: 8, y: 2,
    },
});

pages.push({
    comment: 'Success: 61.19 %',
    flags: {
        mirror: true,
    },
});

pages.push({
    comment: '(Mirror)',
});

console.log(encoder.encode(pages));  // v115@9gilGeglRpGeg...../~/.....ciNEyoAVB

3. Page details

Javascript

const { decoder } = require('tetris-fumen');
const pages = decoder.decode('v115@vhGRQYHAvItJEJmhCAUGJKJJvMJTNJGBJFKYPAUEzP?EJG98AQmqhECDdCA');
const page = pages[0];

/* index: Page number, begin from 0 */
console.log(page.index);  // 0

/* comment: Comment on the page */
console.log(page.comment);  // 'Opening'

/* operation: Placed mino states */
console.log(page.operation);  // { type: 'I', rotation: 'spawn', x: 4, y: 0 }

/* flags: Fumen options on the page
     - colorize: If true, apply guideline color to block
     - lock: If true, lock mino after placement
     - mirror: If true, flip field horizontally after placement
     - quiz: If true, comment is in quiz format
     - rise: If true, rise garbage after placement */  
console.log(page.flags);// { colorize: true, lock: true, mirror: false, quiz: false, rise: false }

/* field: Field object on the page before applying operation and flags */
const field = page.field;
console.log(field.at(4, 0));  // '_'  // EMPTY

field.put(page.operation);  // field object is mutable
console.log(field.at(4, 0));  // 'I'

/* mino: Same as `operation`, but have a more flexible operators */
const mino = page.mino();
console.log(mino.type, mino.rotation, mino.x, mino.y);  // I spawn 4 0
console.log(mino.operation());  // == page.operation
console.log(mino.positions());  // [{ x: 3, y: 0 }, { x: 4, y: 0 }, { x: 5, y: 0 }, { x: 6, y: 0 }]
                                // the position of the blocks
console.log(mino.isValid());  // true  // Whether it's correct as mino
mino.x = -1;
console.log(mino.isValid());  // false

4. Field details

Javascript

/* Create field */
const field = Field.create(
    'LLL_______' +
    'LOO_______' +
    'JOO_______' +
    'JJJ_______',  // field
    'XXXXXXXXX_',  // garbage (optional)
);

/* Get block type */
field.at(9, 0);  // '_'

/* Set block */
field.set(9, 0, 'O');
field.set(9, 1, 'GRAY');
field.set(9, 2, 'EMPTY');

field.set(0, -1, 'X');  // same as 'GRAY'
field.set(9, -1, '_');  // same as 'EMPTY'

/** Current:
      LLL_______
      LOO_______
      JOO______X
      JJJ______O
      XXXXXXXXX_
 */

/* Check if can fill piece */
field.canFill({ type: 'I', rotation: 'left', x: 9, y: 3 });  // true

/* Fill piece even if not on the ground, and return the mino as the placed */
field.fill({ type: 'I', rotation: 'left', x: 9, y: 3 });


/* Check if can fill and lock piece on the ground */
field.canLock({ type: 'O', rotation: 'spawn', x: 4, y: 0 });  // true
field.canLock({ type: 'O', rotation: 'spawn', x: 4, y: 1 });  // false

/* Harddrop and fill piece to the ground, and return the mino as the placed */
field.put({ type: 'O', rotation: 'spawn', x: 4, y: 10 });  // return `{ type: 'O', rotation: 'spawn', x: 4, y: 0 }`

/* Convert to string 
                        default
     @param `reduced`   true    If true, EMPTY line is not parsed
     @param `separator` '\n'    Specify characters between lines 
     @param `garbage`   true    If true, garbage is parsed */
console.log(field.str());

/** Current:
      _________I
      _________I
      LLL______I
      LOO______I
      JOO_OO___X
      JJJ_OO___O
      XXXXXXXXX_
 */

// Copy field
const copied = field.copy();
console.log(copied.str() === field.str());  // true
copied.set(0, 0, 'T');
console.log(copied.str() === field.str());  // false

tetris-fumen's People

Contributors

dependabot[bot] avatar knewjade avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

swng hsohliyt105

tetris-fumen's Issues

Make field.fill return piece position

I'd like to draw the current operation with a lightened color. I need the position after the piece is filled in the field. Example:

const pages = decoder.decode(data);
const field = pages[0].field.copy();
const changedTiles = field.fill(pages[0].operation);

for (let y = 0; y < 20; y++) {
  for (let x = 0; x < 10; x++) {
    myCanvas.draw(x, y, field.at(x, y));
  }
}
for (const [x, y] of changedTiles) {
  myCanvas.lighten(x, y);
}

Maybe we can also add this feature to field.put?

field.fill can't draw floating pieces

data: https://harddrop.com/fumen/?v115@vhAVLn
Code:

const {decoder} = require("tetris-fumen");
const pages = decoder.decode("v115@vhAVLn");
console.log(pages[0].field.str());
pages[0].field.fill(pages[0].operation);

Result:

Error: Cannot fill piece on field

REPL

Is this intended? Quote from README:

Fill piece even if not on the ground, and return the mino as the placed

If floating pieces are not allowed, how does it differ from field.put?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.