Save results into folder

This commit is contained in:
electromagneticcyclone 2023-02-01 09:05:28 -03:00
parent ed1ce0706d
commit 684c75c9ee
GPG Key ID: D7E709AA465A55F9
4 changed files with 125 additions and 54 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@ node_modules
npm-debug.log*
best-found.txt
text/*.txt
results

View File

@ -2,7 +2,7 @@
Попытка создать самую эффективную кириллическую раскладку при помощи эволюционного алгоритма.
На данный момент не удаётся обогнать раскладку ЙУИЯФ.
На данный момент сгенерированно более 40 раскладок, среди которых есть достойные кандидаты.
## Инструкция
@ -11,7 +11,7 @@
2. Наполнить папку `text` файлами `*.txt`. Я использовал классические произведения
русской литературы, которые являются общественным достоянием.
3. Обработать каждый файл с помощью `convert.sh`. Файлы должны быть в кодировке *UTF-8*.
3. Обработать каждый файл с помощью `convert.sh`. Файлы должны быть в кодировке _UTF-8_.
Удобно пройтись по каждому файлу с помощью цикла в оболочке командной строки.
4. Сконфигурировать генератор. Настройки распологаются в файле `src/config.js`.
@ -22,4 +22,4 @@
5. Запустить генерацию командой `npm start`.
6. Добавить получившуюся раскладку в файл `src/compare.js` и сравнить с другими `npm test`.
6. Добавить получившуюся раскладку в файл `src/compare.js` и сравнить с другими `npm test`.

View File

@ -5,17 +5,17 @@
*/
// Generator config
exports.use_elites = true;
exports.mutate_level = 7; // Start mutate level
exports.use_elites = true;
exports.mutate_level = 7; // Start mutate level
exports.mutate_levels = [5, 3, 2, 1];
exports.mutate_thresholds = [780000, 850000, 980000, 990000];
exports.max_no_chage = 250;
exports.max_generation = 10000;
exports.POPULATION_SIZE = 15;
exports.EFFORT_LIMIT = 3000000;
exports.EFFORT_LIMIT = 3000000;
exports.SAME_FINGER_PENALTY = 10; // multiplier
exports.SAME_HAND_PENALTY = 1; // multiplier
exports.SAME_HAND_PENALTY = 1; // multiplier
// just a mapping for easier conversions
const COORDINATES = parseMapping(`
@ -62,9 +62,15 @@ exports.FINGERS = buildMapping(`
`);
const FINGER_NAMES = {
a: 'l-pinky', b: 'l-ring', c: 'l-middle', d: 'l-point',
h: 'r-pinky', g: 'r-ring', f: 'r-middle', e: 'r-point',
t: 'thumb'
a: "l-pinky",
b: "l-ring",
c: "l-middle",
d: "l-point",
h: "r-pinky",
g: "r-ring",
f: "r-middle",
e: "r-point",
t: "thumb",
};
for (const key in exports.FINGERS) {
@ -73,20 +79,26 @@ for (const key in exports.FINGERS) {
// parses a map into key-index -> letter thing
function parseMapping(string) {
return string.trim().split("\n").reduce((mapping, line) => {
const reps = { "\\n" : "\n", "space": " " };
line.trim().split(/\s+/).forEach(symbol =>
mapping.push(reps[symbol] || symbol)
);
return mapping;
}, []);
};
return string
.trim()
.split("\n")
.reduce((mapping, line) => {
const reps = { "\\n": "\n", space: " " };
line
.trim()
.split(/\s+/)
.forEach((symbol) => mapping.push(reps[symbol] || symbol));
return mapping;
}, []);
}
// creates a letter -> value mapping
function buildMapping(string) {
return parseMapping(string).reduce((mapping, value, i) =>
Object.assign(mapping, { [COORDINATES[i]] :
/^\d+$/.test(value) ? parseInt(value) : value
})
, {});
};
return parseMapping(string).reduce(
(mapping, value, i) =>
Object.assign(mapping, {
[COORDINATES[i]]: /^\d+$/.test(value) ? parseInt(value) : value,
}),
{}
);
}

View File

@ -1,21 +1,45 @@
const co = require("co");
const ui = require("./ui");
const { text, trigrams, code } = require("./data");
const Stats = require("./stats");
const Cluster = require("./cluster");
const Population = require("./population");
const { JCUKEN, Diktor, Diktor_VoronovMod, Redaktor, JUIYAF, Zubachev, Ruhal } = require("./presets");
const { use_elites, mutate_levels, mutate_thresholds, max_no_chage, max_generation, population_size, POPULATION_SIZE } = require("./config");
let { mutate_level } = require("./config");
const co = require("co");
const ui = require("./ui");
const { text, trigrams, code } = require("./data");
const Stats = require("./stats");
const Cluster = require("./cluster");
const Population = require("./population");
const {
JCUKEN,
Diktor,
Diktor_VoronovMod,
Redaktor,
JUIYAF,
Zubachev,
Ruhal,
} = require("./presets");
const {
use_elites,
mutate_levels,
mutate_thresholds,
max_no_chage,
max_generation,
population_size,
POPULATION_SIZE,
} = require("./config");
let { mutate_level } = require("./config");
const data = text;
const cluster = new Cluster(data);
const seed_layouts = [JCUKEN, Diktor, Diktor_VoronovMod, Redaktor, JUIYAF, Zubachev, Ruhal];
const data = text;
const cluster = new Cluster(data);
const seed_layouts = [
JCUKEN,
Diktor,
Diktor_VoronovMod,
Redaktor,
JUIYAF,
Zubachev,
Ruhal,
];
co(boot).catch(e => console.log(e.stack));
co(boot).catch((e) => console.log(e.stack));
function *boot() {
if(mutate_levels.length != mutate_thresholds.length) {
function* boot() {
if (mutate_levels.length != mutate_thresholds.length) {
die("Mutate settings are incorrect");
}
for (const promise of cluster.schedule(seed_layouts)) {
@ -26,55 +50,89 @@ function *boot() {
yield start(Population.random(), max_generation);
}
function *start(population, count) {
function* start(population, count) {
let last_layout, last_score, no_changes_in;
let rank = 0;
for (let i=0; i < count; i++) {
for (let i = 0; i < count; i++) {
const [layout, score] = yield handle(population);
if (!last_layout || last_layout.toString() !== layout.toString()) {
no_changes_in = 0;
if(rank < mutate_levels.length) {
if(score.total > mutate_thresholds[rank]) {
if (rank < mutate_levels.length) {
if (score.total > mutate_thresholds[rank]) {
mutate_level = mutate_levels[rank];
rank ++;
rank++;
}
}
} else {
no_changes_in ++;
no_changes_in++;
}
ui.addResult(layout, score, no_changes_in, max_no_chage);
last_layout = layout; last_score = score;
last_layout = layout;
last_score = score;
if (no_changes_in == max_no_chage) {
break;
}
population = population.next({elite: use_elites, mutate: mutate_level});
population = population.next({ elite: use_elites, mutate: mutate_level });
}
const vs = ui.vs_result(last_score);
const overheads = ui.getOverheads();
ui.destroy();
const fs = require("fs");
const file = new console.Console(
fs.createWriteStream(`./results/output${new Date().getTime()}.txt`)
);
console.log(`Total: ${last_score.total}, Dist: ${last_score.position},\n`);
console.log(`Symmetry: ${last_score.symmetry}%, Evenness: ${last_score.evenness}%,\n`);
console.log(`Hands: ${last_score.handsUsage.map(v => v+"%").join(" | ")},\n`);
console.log(`Overheads: Finger:${overheads.finger}%, Hand:${overheads.hand}%, Shift:${overheads.shift}%\n`);
console.log(
`Symmetry: ${last_score.symmetry}%, Evenness: ${last_score.evenness}%,\n`
);
console.log(
`Hands: ${last_score.handsUsage.map((v) => v + "%").join(" | ")},\n`
);
console.log(
`Overheads: Finger:${overheads.finger}%, Hand:${overheads.hand}%, Shift:${overheads.shift}%\n`
);
console.log(`Fingers ${last_score.fingersUsage}\n`);
console.log(`vs.Diktor: ${vs[0]}`);
console.log(`vs.JCUKEN: ${vs[1]}\n`);
console.log(last_layout.toCyrillic(), "\n");
console.log(last_layout.config);
file.log(`Total: ${last_score.total}, Dist: ${last_score.position},\n`);
file.log(
`Symmetry: ${last_score.symmetry}%, Evenness: ${last_score.evenness}%,\n`
);
file.log(
`Hands: ${last_score.handsUsage.map((v) => v + "%").join(" | ")},\n`
);
file.log(
`Overheads: Finger:${overheads.finger}%, Hand:${overheads.hand}%, Shift:${overheads.shift}%\n`
);
file.log(`Fingers ${last_score.fingersUsage}\n`);
file.log(`vs.Diktor: ${vs[0]}`);
file.log(`vs.JCUKEN: ${vs[1]}\n`);
file.log(last_layout.toCyrillic(), "\n");
file.log(last_layout.config);
}
function *handle(population) {
ui.newPopulation(population, max_generation, use_elites, mutate_level, POPULATION_SIZE);
function* handle(population) {
ui.newPopulation(
population,
max_generation,
use_elites,
mutate_level,
POPULATION_SIZE
);
const layouts = population.genomes.map(g => g.toLayout());
const layouts = population.genomes.map((g) => g.toLayout());
for (const promise of cluster.schedule(layouts)) {
const { layout, result } = yield promise;