This commit is contained in:
Vick Scarlet
2021-08-15 11:01:01 +08:00
commit cc72b5a6b3
10 changed files with 730 additions and 0 deletions

108
.gitignore vendored Normal file
View File

@ -0,0 +1,108 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
excel/
data/
view/

3
.jshintrc Normal file
View File

@ -0,0 +1,3 @@
{
"esversion": 9
}

26
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "test",
"program": "${workspaceFolder}/app.js",
"skipFiles": [
"<node_internals>/**"
]
},
{
"type": "node",
"request": "launch",
"name": "convert",
"program": "${workspaceFolder}/util/xlsxTransform.js",
"skipFiles": [
"<node_internals>/**"
]
}
]
}

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# lifeRestart
Game Life Restart

8
package.json Normal file
View File

@ -0,0 +1,8 @@
{
"name": "xlsx_transform",
"module": "true",
"bin": "convert.js",
"dependencies": {
"xlsx": "^0.17.0"
}
}

129
src/condition.js Normal file
View File

@ -0,0 +1,129 @@
class Condition {
constructor(prop) {
this._prop = prop;
}
parse(condition) {
const conditions = [];
const length = condition.length;
const stack = [];
stack.unshift(conditions);
let cursor = 0;
const catchString = i => {
const str = condition.substring(cursor, i).trim();
cursor = i;
if(str) stack[0].push(str);
};
for(let i=0; i<length; i++) {
switch(condition[i]) {
case ' ': continue;
case '(':
catchString(i);
cursor ++;
const sub = [];
stack[0].push(sub);
stack.unshift(sub);
break;
case ')':
catchString(i);
cursor ++;
stack.shift();
break;
case '|':
case '&':
catchString(i);
catchString(i+1);
break;
default: continue;
}
}
catchString(length);
return conditions;
}
check(condition) {
const conditions = this.parse(condition);
return this.checkParsedConditions(conditions);
}
checkParsedConditions(conditions) {
if(!Array.isArray(conditions)) return this.checkProp(conditions);
if(conditions.length == 0) return true;
if(conditions.length == 1) return this.checkParsedConditions(conditions[0]);
let ret = this.checkParsedConditions(conditions[0]);
for(let i=1; i<conditions.length; i+=2) {
switch(conditions[i]) {
case '&':
if(ret) ret = this.checkParsedConditions(conditions[i+1]);
break;
case '|':
if(ret) return true;
ret = this.checkParsedConditions(conditions[i+1]);
break;
default: return false;
}
}
return ret;
}
checkProp(condition) {
const length = condition.length;
let i = condition.search(/[><\!\?=]/);
const prop = condition.substring(0,i);
const symbol = condition.substring(i, i+=(condition[i+1]=='='?2:1));
const d = condition.substring(i, length);
const propData = this.getProp(prop);
const conditionData = d[0]=='['? JSON.parse(d): Number(d);
switch(symbol) {
case '>': return propData > conditionData;
case '<': return propData < conditionData;
case '>=': return propData >= conditionData;
case '<=': return propData <= conditionData;
case '=':
if(Array.isArray(propData))
return propData.includes(conditionData);
return propData == conditionData;
case '!=':
if(Array.isArray(propData))
return !propData.includes(conditionData);
return propData == conditionData;
case '?':
if(Array.isArray(propData)) {
for(const p of propData)
if(conditionData.includes(p)) return true;
return false;
}
return conditionData.includes(propData);
case '!':
if(Array.isArray(propData)) {
for(const p of propData)
if(conditionData.includes(p)) return false;
return true;
}
return !conditionData.includes(propData);
default: return false;
}
}
getProp(prop) {
return this.prop.get(prop);
}
get prop() {return this._prop;}
set prop(p) {this._prop = p;}
}
export default Condition;

74
src/prop.js Normal file
View File

@ -0,0 +1,74 @@
class Prop {
constructor(initialData) {
this._data = {};
for(const key in initialData)
this.set(key, initialData[key]);
}
get(prop) {
switch(prop) {
case 'CHR':
case 'INT':
case 'STR':
case 'MNY':
case 'SPR':
case 'LIF':
case 'TLT':
case 'EVT':
return this._data[prop];
default: return 0;
}
}
set(prop, value) {
switch(prop) {
case 'CHR':
case 'INT':
case 'STR':
case 'MNY':
case 'SPR':
case 'LIF':
case 'TLT':
case 'EVT':
this._data[prop] = this.clone(value);
break;
default: return 0;
}
}
change(prop, value) {
switch(prop) {
case 'CHR':
case 'INT':
case 'STR':
case 'MNY':
case 'SPR':
case 'LIF':
this._data[prop] += value;
break;
case 'TLT':
case 'EVT':
const v = this._data[prop];
if(value<0) {
const index = v.indexOf(value);
if(index!=-1) v.splice(index,1);
}
if(!v.includes(value)) v.push(value);
break;
default: return;
}
}
clone(value) {
switch(typeof value) {
case 'object':
if(Array.isArray(value)) return value.map(v=>this.clone(v));
const newObj = {};
for(const key in value) newObj[key] = this.clone(value[key]);
return newObj;
default: return value;
}
}
}
export default Prop;

190
test.js Normal file
View File

@ -0,0 +1,190 @@
// const DEFAULT_PROP = {
// CHR: 5, // 颜值 charm CHR
// INT: 5, // 智力 intelligence INT
// STR: 5, // 体质 strength STR
// MNY: 5, // 家境 money MNY
// SPR: 5, // 快乐 spirit SPR
// LIF: 5, // 生命 life LIF
// TLT: [5], // 天赋 talent TLT
// EVT: [5], // 事件 event EVT
// };
// debug(
// '(STR<2&MNY>3)|(MNY<2&CHR<2)',
// '(STR<2&MNY>3)',
// '(STR>2&MNY>3)',
// '((((STR>2&MNY>2))))',
// '((((STR>2&MNY>2)|(MNY<2&CHR<2))))',
// '((((STR>2&MNY>2)|(MNY<2&CHR<2)&(STR>2&MNY>3))))',
// '((((STR>2&MNY>2)|(MNY<2&CHR<2))&(STR>2&MNY>3)))',
// 'EVT![1,2,3]',
// 'EVT![1,2]',
// 'EVT?[1,2,3]',
// 'EVT?[1,2]',
// );
// function getProp(prop) {
// switch(prop) {
// case 'CHR':
// case 'INT':
// case 'STR':
// case 'MNY':
// case 'SPR':
// case 'LIF':
// case 'TLT':
// case 'EVT': return DEFAULT_PROP[prop];
// default: return null;
// }
// }
// function check(condition) {
// const conditions = parseCondition(condition);
// return checkParsedCondition(conditions);
// }
// function checkParsedCondition(conditions) {
// if(!Array.isArray(conditions)) return checkLogic(conditions);
// if(conditions.length == 0) return true;
// if(conditions.length == 1) return checkParsedCondition(conditions[0]);
// let ret = checkParsedCondition(conditions[0]);
// for(let i=1; i<conditions.length; i+=2) {
// switch(conditions[i]) {
// case '&':
// if(ret) ret = checkParsedCondition(conditions[i+1]);
// break;
// case '|':
// if(ret) return true;
// ret = checkParsedCondition(conditions[i+1]);
// break;
// default: return false;
// }
// }
// return ret;
// }
// function checkLogic(condition) {
// const length = condition.length;
// let i = condition.search(/[><\!\?=]/);
// const prop = condition.substring(0,i);
// const symbol = condition.substring(i, i+=(condition[i+1]=='='?2:1));
// const d = condition.substring(i, length);
// const propData = getProp(prop);
// const conditionData = d[0]=='['? JSON.parse(d): Number(d);
// switch(symbol) {
// case '>': return propData > conditionData;
// case '<': return propData < conditionData;
// case '>=': return propData >= conditionData;
// case '<=': return propData <= conditionData;
// case '=':
// if(Array.isArray(propData))
// return propData.includes(conditionData);
// return propData == conditionData;
// case '!=':
// if(Array.isArray(propData))
// return !propData.includes(conditionData);
// return propData == conditionData;
// case '?':
// if(Array.isArray(propData)) {
// for(const p of propData)
// if(conditionData.includes(p)) return true;
// return false;
// }
// return conditionData.includes(propData);
// case '!':
// if(Array.isArray(propData)) {
// for(const p of propData)
// if(conditionData.includes(p)) return false;
// return true;
// }
// return !conditionData.includes(propData);
// default: return false;
// }
// }
// function parseCondition(condition) {
// const conditions = [];
// const length = condition.length;
// const stack = [];
// stack.unshift(conditions);
// let cursor = 0;
// const catchString = i => {
// const str = condition.substring(cursor, i).trim();
// cursor = i;
// if(str) stack[0].push(str);
// };
// for(let i=0; i<length; i++) {
// switch(condition[i]) {
// case ' ': continue;
// case '(':
// catchString(i);
// cursor ++;
// const sub = [];
// stack[0].push(sub);
// stack.unshift(sub);
// break;
// case ')':
// catchString(i);
// cursor ++;
// stack.shift();
// break;
// case '|':
// case '&':
// catchString(i);
// catchString(i+1);
// break;
// default: continue;
// }
// }
// catchString(length);
// return conditions;
// }
import Prop from './src/prop.js';
import Condition from './src/condition.js';
const prop = new Prop({
CHR: 5, // 颜值 charm CHR
INT: 5, // 智力 intelligence INT
STR: 5, // 体质 strength STR
MNY: 5, // 家境 money MNY
SPR: 5, // 快乐 spirit SPR
LIF: 5, // 生命 life LIF
TLT: [5], // 天赋 talent TLT
EVT: [5], // 事件 event EVT
});
const condition = new Condition(prop);
function debug(...conditions) {
for(const cond of conditions)
console.debug(condition.check(cond), '\t', cond);
}
debug(
'(STR<2&MNY>3)|(MNY<2&CHR<2)',
'(STR<2&MNY>3)',
'(STR>2&MNY>3)',
'((((STR>2&MNY>2))))',
'((((STR>2&MNY>2)|(MNY<2&CHR<2))))',
'((((STR>2&MNY>2)|(MNY<2&CHR<2)&(STR>2&MNY>3))))',
'((((STR>2&MNY>2)|(MNY<2&CHR<2))&(STR>2&MNY>3)))',
'EVT![1,2,3]',
'EVT![1,2]',
'EVT?[1,2,3]',
'EVT?[1,2]',
);

91
utils/xlsxTransform.js Normal file
View File

@ -0,0 +1,91 @@
import { readFile, writeFile, stat, readdir } from 'fs/promises';
import * as XLSX from 'xlsx';
import { join, extname, dirname } from 'path';
// const { readFile, writeFile, stat, readdir } = require('fs/promises');
// const XLSX = require('xlsx');
// const { join, extname, dirname } = require('path');
async function transform(filePath) {
const xlsxFileBuffer = await readFile(filePath);
const xlsx = XLSX.read(xlsxFileBuffer, {type: 'buffer'});
const sheets = xlsx.Sheets;
const data = {};
for(const sheetName in sheets) {
const sheetRawData = sheets[sheetName];
if(!sheetRawData['!ref']) break;
const rawData = XLSX.utils.sheet_to_json(sheetRawData);
const newData = [];
data[sheetName] = newData;
rawData.shift();
for(const row of rawData) {
const rowData = {};
newData.push(rowData)
for(const key in row) {
const cell = row[key];
if(key.includes(':')) {
const keys = key.split(':');
const lastKey = keys.pop();
let temp = rowData;
for(const subKey of keys)
if(!temp[subKey]) temp = temp[subKey] = {};
temp[lastKey] = cell;
} else if(key.includes('[]')) {
const aKey = key.split('[]')[0];
if(!rowData[aKey]) rowData[aKey] = [cell];
else rowData[aKey].push(cell);
} else {
rowData[key] = cell;
}
}
}
}
return data;
}
async function walk(filePath) {
const xlsxPaths = [];
if(Array.isArray(filePath)) {
for(const subPath of filePath)
xlsxPaths.push(await walk(subPath));
return xlsxPaths.flat();
}
const fileStat = await stat(filePath);
if(!fileStat.isDirectory()) {
const ext = extname(filePath);
if( ext=='.xls' || ext=='.xlsx' ) xlsxPaths.push(filePath);
return xlsxPaths;
}
const dirData = await readdir(filePath);
for(const subPath of dirData)
xlsxPaths.push(await walk(join(filePath, subPath)));
return xlsxPaths.flat();
}
async function main() {
const filePaths = process.argv.slice(2);
if(filePaths.length<0) process.exit(0);
const xlsxs = await walk(filePaths);
for(const p of xlsxs) {
const data = await transform(p);
const d = dirname(p);
for(const sheetName in data) {
const savePath = join(d, `${sheetName}.json`);
console.info(`[Transform] XLSX(${p}:${sheetName}) -> JSON(${savePath})`);
await writeFile(
savePath,
JSON.stringify(data[sheetName], null, 4),
);
}
}
console.info(`
------------------------
| Transform Complete |
------------------------
`);
setTimeout(()=>{}, 1000);
}
main();

99
yarn.lock Normal file
View File

@ -0,0 +1,99 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
adler-32@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/adler-32/-/adler-32-1.2.0.tgz#6a3e6bf0a63900ba15652808cb15c6813d1a5f25"
integrity sha1-aj5r8KY5ALoVZSgIyxXGgT0aXyU=
dependencies:
exit-on-epipe "~1.0.1"
printj "~1.1.0"
cfb@^1.1.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/cfb/-/cfb-1.2.0.tgz#6a4d0872b525ed60349e1ef51fb4b0bf73eca9a8"
integrity sha512-sXMvHsKCICVR3Naq+J556K+ExBo9n50iKl6LGarlnvuA2035uMlGA/qVrc0wQtow5P1vJEw9UyrKLCbtIKz+TQ==
dependencies:
adler-32 "~1.2.0"
crc-32 "~1.2.0"
printj "~1.1.2"
codepage@~1.14.0:
version "1.14.0"
resolved "https://registry.yarnpkg.com/codepage/-/codepage-1.14.0.tgz#8cbe25481323559d7d307571b0fff91e7a1d2f99"
integrity sha1-jL4lSBMjVZ19MHVxsP/5HnodL5k=
dependencies:
commander "~2.14.1"
exit-on-epipe "~1.0.1"
commander@~2.14.1:
version "2.14.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa"
integrity sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw==
commander@~2.17.1:
version "2.17.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
crc-32@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208"
integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==
dependencies:
exit-on-epipe "~1.0.1"
printj "~1.1.0"
exit-on-epipe@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692"
integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==
fflate@^0.3.8:
version "0.3.11"
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.3.11.tgz#2c440d7180fdeb819e64898d8858af327b042a5d"
integrity sha512-Rr5QlUeGN1mbOHlaqcSYMKVpPbgLy0AWT/W0EHxA6NGI12yO1jpoui2zBBvU2G824ltM6Ut8BFgfHSBGfkmS0A==
frac@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/frac/-/frac-1.1.2.tgz#3d74f7f6478c88a1b5020306d747dc6313c74d0b"
integrity sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==
printj@~1.1.0, printj@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222"
integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==
ssf@~0.11.2:
version "0.11.2"
resolved "https://registry.yarnpkg.com/ssf/-/ssf-0.11.2.tgz#0b99698b237548d088fc43cdf2b70c1a7512c06c"
integrity sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==
dependencies:
frac "~1.1.2"
wmf@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wmf/-/wmf-1.0.2.tgz#7d19d621071a08c2bdc6b7e688a9c435298cc2da"
integrity sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==
word@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/word/-/word-0.3.0.tgz#8542157e4f8e849f4a363a288992d47612db9961"
integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
xlsx@^0.17.0:
version "0.17.0"
resolved "https://registry.yarnpkg.com/xlsx/-/xlsx-0.17.0.tgz#028176a0140967dcee1817d221678461e47481c8"
integrity sha512-bZ36FSACiAyjoldey1+7it50PMlDp1pcAJrZKcVZHzKd8BC/z6TQ/QAN8onuqcepifqSznR6uKnjPhaGt6ig9A==
dependencies:
adler-32 "~1.2.0"
cfb "^1.1.4"
codepage "~1.14.0"
commander "~2.17.1"
crc-32 "~1.2.0"
exit-on-epipe "~1.0.1"
fflate "^0.3.8"
ssf "~0.11.2"
wmf "~1.0.1"
word "~0.3.0"