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

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?

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?

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.