@gobstones/gobstones-lang
Version:
312 lines • 9.87 kB
JavaScript
/* eslint-disable no-underscore-dangle */
import fs from 'fs';
class BoardFormat {
constructor(formatName, description, extension, fromJboard, toJboard) {
this._formatName = formatName;
this._description = description;
this._extension = extension;
this._fromJboard = fromJboard;
this._toJboard = toJboard;
}
get formatName() {
return this._formatName;
}
get description() {
return this._description;
}
get extension() {
return this._extension;
}
get fromJboard() {
return this._fromJboard;
}
get toJboard() {
return this._toJboard;
}
}
export function apiboardFromJboard(jboard) {
const apiboard = {
head: { x: jboard.head[0], y: jboard.head[1] },
width: jboard.width,
height: jboard.height,
table: []
};
for (let y = 0; y < jboard.height; y++) {
const row = [];
for (let x = 0; x < jboard.width; x++) {
const cellO = jboard.board[x][y];
const cell = {
blue: cellO.a > 0 ? cellO.a : undefined,
black: cellO.n > 0 ? cellO.n : undefined,
red: cellO.r > 0 ? cellO.r : undefined,
green: cellO.v > 0 ? cellO.v : undefined
};
if (cell.blue === undefined)
delete cell.blue;
if (cell.black === undefined)
delete cell.black;
if (cell.red === undefined)
delete cell.red;
if (cell.green === undefined)
delete cell.green;
row.push(cell);
}
apiboard.table.unshift(row);
}
return apiboard;
}
export function apiboardToJboard(apiboard) {
const jboard = {
head: [apiboard.head.x, apiboard.head.y],
width: apiboard.width,
height: apiboard.height,
board: []
};
for (let x = 0; x < jboard.width; x++) {
const column = [];
for (let y = 0; y < jboard.height; y++) {
const cell = apiboard.table[jboard.height - y - 1][x];
const ca = 'blue' in cell ? cell.blue : 0;
const cn = 'black' in cell ? cell.black : 0;
const cr = 'red' in cell ? cell.red : 0;
const cv = 'green' in cell ? cell.green : 0;
column.push({
a: ca,
n: cn,
r: cr,
v: cv
});
}
jboard.board.push(column);
}
return jboard;
}
function gsboardFromJboard(jboard) {
const gsboard = {
x: jboard.head[0],
y: jboard.head[1],
sizeX: jboard.width,
sizeY: jboard.height,
table: []
};
for (let y = 0; y < jboard.height; y++) {
const row = [];
for (let x = 0; x < jboard.width; x++) {
const cell = jboard.board[x][y];
row.push({
blue: cell.a,
black: cell.n,
red: cell.r,
green: cell.v
});
}
gsboard.table.unshift(row);
}
return JSON.stringify(gsboard);
}
function gsboardToJboard(gsBoardString) {
const gsboard = JSON.parse(gsBoardString);
const jboard = {
head: [gsboard.x, gsboard.y],
width: gsboard.sizeX,
height: gsboard.sizeY,
board: []
};
for (let x = 0; x < jboard.width; x++) {
const column = [];
for (let y = 0; y < jboard.height; y++) {
const cell = gsboard.table[jboard.height - y - 1][x];
column.push({
a: cell.blue,
n: cell.black,
r: cell.red,
v: cell.green
});
}
jboard.board.push(column);
}
return jboard;
}
export function gbbFromJboard(jboard) {
const gbb = [];
gbb.push('GBB/1.0');
gbb.push(`size ${jboard.width.toString()} ${jboard.height.toString()}`);
for (let y = 0; y < jboard.height; y++) {
for (let x = 0; x < jboard.width; x++) {
const cell = jboard.board[x][y];
if (cell.a + cell.n + cell.r + cell.v === 0) {
continue;
}
let c = 'cell ' + x.toString() + ' ' + y.toString();
if (cell.a > 0) {
c += ' Azul ' + cell.a.toString();
}
if (cell.n > 0) {
c += ' Negro ' + cell.n.toString();
}
if (cell.r > 0) {
c += ' Rojo ' + cell.r.toString();
}
if (cell.v > 0) {
c += ' Verde ' + cell.v.toString();
}
gbb.push(c);
}
}
gbb.push(`head ${jboard.head[0].toString()} ${jboard.head[1].toString()}`);
return gbb.join('\n') + '\n';
}
export function gbbToJboard(gbb) {
let i = 0;
// DUMMY
const jboard = {
width: 0,
height: 0,
head: [],
board: []
};
const isWhitespace = (x) => x === ' ' || x === '\t' || x === '\r' || x === '\n';
function isNumeric(str) {
for (const char of str) {
if ('0123456789'.indexOf(char) === -1) {
return false;
}
}
return str.length > 0;
}
function skipWhitespace() {
/* Skip whitespace */
while (i < gbb.length && isWhitespace(gbb[i])) {
i++;
}
}
function readToken() {
const t = [];
skipWhitespace();
while (i < gbb.length && !isWhitespace(gbb[i])) {
t.push(gbb[i]);
i++;
}
return t.join('');
}
function readN(errmsg) {
const t = readToken();
if (!isNumeric(t)) {
throw Error(errmsg);
}
const ti = parseInt(t, 10);
if (ti < 0) {
throw Error(errmsg);
}
return ti;
}
function readRange(a, b, errmsg) {
const t = readN(errmsg);
if (t < a || t >= b) {
throw Error(errmsg);
}
return t;
}
if (readToken() !== 'GBB/1.0') {
throw Error('GBB/1.0: Board not in GBB/1.0 format.');
}
if (readToken() !== 'size') {
throw Error('GBB/1.0: Board lacks a size declaration.');
}
jboard.width = readN('GBB/1.0: Board width is not a number.');
jboard.height = readN('GBB/1.0: Board height is not a number.');
if (jboard.width <= 0 || jboard.height <= 0) {
throw Error('GBB/1.0: Board size should be positive.');
}
jboard.head = [0, 0];
jboard.board = [];
for (let w = 0; w < jboard.width; w++) {
const row = [];
for (let j = 0; j < jboard.height; j++) {
row.push({ a: 0, n: 0, r: 0, v: 0 });
}
jboard.board.push(row);
}
let headDeclared = false;
const colores = {
Azul: 'a',
A: 'a',
Negro: 'n',
N: 'n',
Rojo: 'r',
R: 'r',
Verde: 'v',
V: 'v'
};
while (i < gbb.length) {
const op = readToken();
if (op === '') {
break;
}
else if (op === 'head') {
if (headDeclared) {
throw Error('GBB/1.0: Head position cannot be declared twice.');
}
headDeclared = true;
const hx = readRange(0, jboard.width, 'GBB/1.0: Invalid head position.');
const hy = readRange(0, jboard.height, 'GBB/1.0: Invalid head position.');
jboard.head = [hx, hy];
}
else if (op === 'cell') {
const cx = readRange(0, jboard.width, 'GBB/1.0: Invalid cell position.');
const cy = readRange(0, jboard.height, 'GBB/1.0: Invalid cell position.');
const colorDeclared = {};
while (i < gbb.length) {
const color = readToken();
if (!(color in colores)) {
i -= color.length;
break;
}
const colorId = colores[color];
if (colorId in colorDeclared) {
throw Error('GBB/1.0: Color cannot be declared twice.');
}
const n = readN('GBB/1.0: Invalid amount of stones.');
jboard.board[cx][cy][colorId] = n;
}
}
else {
throw Error('GBB/1.0: Malformed board: unknown command "' + op + '".');
}
}
return jboard;
}
const BOARD_FORMAT_LIST = [
new BoardFormat('jboard', 'Representation of a board as a JavaScript object for internal usage.', 'jboard', JSON.stringify, JSON.parse),
new BoardFormat('gs-weblang-cli-json-board', `Representation of a board as a Javascript object used by the gs-weblang-cli tool.`, 'json', gsboardFromJboard, gsboardToJboard),
new BoardFormat('gbb', 'GBB/1.0', 'gbb', gbbFromJboard, gbbToJboard)
];
export const DEFAULT_FORMAT = 'gs-weblang-cli-json-board';
export const BOARD_FORMATS = {};
for (const boardFormat of BOARD_FORMAT_LIST) {
BOARD_FORMATS[boardFormat.formatName] = boardFormat;
}
function fileExtension(filename) {
const parts = filename.split('.');
return parts[parts.length - 1];
}
function fileBoardFormat(filename) {
const extension = fileExtension(filename);
for (const fmt of BOARD_FORMAT_LIST) {
if (extension === fmt.extension) {
return fmt;
}
}
return BOARD_FORMATS[DEFAULT_FORMAT];
}
export function readJboardFromFile(filename) {
const format = fileBoardFormat(filename);
const contents = fs.readFileSync(filename, 'utf8');
return format.toJboard(contents);
}
export function writeJboardToFile(filename, jboard) {
const format = fileBoardFormat(filename);
const contents = format.fromJboard(jboard);
fs.writeFileSync(filename, contents, 'utf8');
}
//# sourceMappingURL=board_formats.js.map