Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save solved BPs into store #73

Merged
merged 24 commits into from
May 6, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
cleanup
  • Loading branch information
marcincichocki committed May 6, 2021
commit 4ba6856d5394e47e77318d3d981ee168394167ac
2 changes: 1 addition & 1 deletion src/client-electron/worker/autosolver.ts
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ export class BreachProtocolAutosolver {
this.fileName = (await captureScreen(this.screenId)) as string;
this.recognitionResult = await this.recognize();

if (!this.recognitionResult.valid) {
if (!this.recognitionResult.isValid) {
return this.reject();
}

2 changes: 1 addition & 1 deletion src/common/util.test.ts
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ describe('utils', () => {
expect(getClosest(900, [720, 1080, 1440])).toBe(720);
});

it('should correctly maks', () => {
it('should correctly recognize flags in bit mask', () => {
const mask = new BitMask(4); // 100

// M 100
8 changes: 0 additions & 8 deletions src/common/util.ts
Original file line number Diff line number Diff line change
@@ -101,14 +101,6 @@ export function getClosest(n: number, list: number[]) {
return list[index];
}

export function mergeBy<T extends Record<string, any>, R>(
list: T[],
prop: keyof T,
fn: (element: T) => R
) {
return list.reduce((acc, el) => ({ ...acc, [el[prop]]: fn(el) }), {});
}

type JSONValue =
| string
| number
7 changes: 5 additions & 2 deletions src/core/common.ts
Original file line number Diff line number Diff line change
@@ -11,6 +11,9 @@ const values = HEX_NUMBERS.map((x, i) => String.fromCharCode(i + 97));
const HEX_MAP = new Map(HEX_NUMBERS.map((k, i) => [k, values[i]]));

export type BufferSize = 4 | 5 | 6 | 7 | 8 | 9;
export type GridRawData = HexNumber[];
export type DaemonRawData = HexNumber[];
export type DaemonsRawData = DaemonRawData[];

export function toHex(value: string) {
for (let [k, v] of HEX_MAP.entries()) {
@@ -84,8 +87,8 @@ export function byUniqueValue() {
}

export interface BreachProtocolRawData {
grid: HexNumber[];
daemons: HexNumber[][];
grid: GridRawData;
daemons: DaemonsRawData;
bufferSize: BufferSize;
}

12 changes: 4 additions & 8 deletions src/core/game.ts
Original file line number Diff line number Diff line change
@@ -11,9 +11,9 @@ import {
import { Sequence, SequenceJSON } from './sequence';

export type BreachProtocolResultJSON = {
sequence: SequenceJSON;
rawPath: string[];
path: string[];
rawPath: string[];
sequence: SequenceJSON;
};

export class BreachProtocolResult implements Serializable {
@@ -26,13 +26,9 @@ export class BreachProtocolResult implements Serializable {
) {}

toJSON(): BreachProtocolResultJSON {
const { rawPath, sequence, path } = this;
const { path, rawPath, sequence } = this;

return {
path,
rawPath,
sequence: sequence.toJSON(),
};
return { path, rawPath, sequence: sequence.toJSON() };
}

private resolvePath(path: string[]) {
26 changes: 15 additions & 11 deletions src/core/index.test.ts
Original file line number Diff line number Diff line change
@@ -3,10 +3,12 @@ import {
BreachProtocolRawData,
BufferSize,
cross,
DaemonRawData,
DaemonsRawData,
fromHex,
generateSquareMap,
getUnits,
HexNumber,
GridRawData,
resolveExitStrategy,
transformRawData,
} from './common';
@@ -93,7 +95,9 @@ describe('Breach protocol solve', () => {
// will start from the start every time that happens until we run
// out of buffer.
['E9', '55', '55'],
].map((s: HexNumber[]) => g1.solve([new Sequence(s, [new Daemon(s, 0)])]));
].map((s: DaemonRawData) =>
g1.solve([new Sequence(s, [new Daemon(s, 0)])])
);

results.forEach((result) => {
expect(result.path.length).toBeLessThanOrEqual(bufferSize);
@@ -114,14 +118,14 @@ describe('Breach protocol solve', () => {

it('should slice path if it contains accidental daemons', () => {
// prettier-ignore
const grid1: HexNumber[] = [
const grid1: GridRawData = [
'BD', '1C', 'E9', '1C', '55',
'1C', '1C', '55', '55', 'BD',
'1C', '1C', '1C', '55', 'BD',
'1C', 'E9', '55', '55', '55',
'1C', '55', '1C', '1C', '1C',
]
const daemons1: HexNumber[][] = [
const daemons1: DaemonsRawData = [
['1C', '55', '55'],
['55', '1C'],
];
@@ -141,14 +145,14 @@ describe('Breach protocol solve', () => {
expectResolvedSequenceToContainDaemons(result1);

// prettier-ignore
const grid2: HexNumber[] = [
const grid2: GridRawData = [
'E9', '1C', 'E9', '1C', 'BD',
'BD', 'BD', 'BD', '55', '1C',
'55', '1C', '1C', '55', '1C',
'1C', 'E9', '1C', 'BD', 'BD',
'E9', '1C', '55', 'BD', '55',
]
const daemons2: HexNumber[][] = [
const daemons2: DaemonsRawData = [
['55', 'E9', 'BD'],
['1C', '1C'],
];
@@ -170,7 +174,7 @@ describe('Breach protocol solve', () => {

describe('forceful exit', () => {
// prettier-ignore
const grid: HexNumber[] = [
const grid: GridRawData = [
'55', '1C', '1C', 'E9', '55',
'BD', 'BD', 'BD', '1C', '55',
'55', '1C', '55', '55', '1C',
@@ -181,7 +185,7 @@ describe('Breach protocol solve', () => {

it('should not use force exit when BP exits automatically', () => {
const bufferSize = 4;
const daemons: HexNumber[][] = [['55', 'BD', 'BD'], ['1C']];
const daemons: DaemonsRawData = [['55', 'BD', 'BD'], ['1C']];
const [p1] = parseDaemons(daemons);
const s1 = getSequenceFromPermutation(p1);
const result = new BreachProtocol(tGrid, bufferSize).solveForSequence(s1);
@@ -198,7 +202,7 @@ describe('Breach protocol solve', () => {

it('should exit when BP is completed', () => {
const bufferSize = 5;
const daemons: HexNumber[][] = [['55', 'BD', 'BD'], ['1C']];
const daemons: DaemonsRawData = [['55', 'BD', 'BD'], ['1C']];
const [p1] = parseDaemons(daemons);
const s1 = getSequenceFromPermutation(p1);
const result = new BreachProtocol(tGrid, bufferSize).solveForSequence(s1);
@@ -215,7 +219,7 @@ describe('Breach protocol solve', () => {

it('should force exit if leftover damon fit buffer', () => {
const bufferSize = 5;
const daemons: HexNumber[][] = [['55', 'BD', 'BD'], ['1C'], ['7A']];
const daemons: DaemonsRawData = [['55', 'BD', 'BD'], ['1C'], ['7A']];
const [p1] = parseDaemons(daemons);
const s1 = getSequenceFromPermutation(p1.slice(0, 2));
const result = new BreachProtocol(tGrid, bufferSize).solveForSequence(s1);
@@ -232,7 +236,7 @@ describe('Breach protocol solve', () => {

it('should force exit if leftover daemon overlap with sequence and fit buffer', () => {
const bufferSize = 5;
const daemons: HexNumber[][] = [
const daemons: DaemonsRawData = [
['55', 'BD', 'BD', '1C'],
['1C', '7A'],
];
30 changes: 19 additions & 11 deletions src/core/ocr/base.ts
Original file line number Diff line number Diff line change
@@ -82,9 +82,7 @@ export abstract class BreachProtocolFragment<
/** Check if recognized data is valid. */
abstract isValid(data: TData): boolean;

protected getBaseResultData(
rawData: TData
): BreachProtocolFragmentResultBase<TId> {
private getBaseResult(rawData: TData): BreachProtocolFragmentResultBase<TId> {
const { id, boundingBox } = this;
const isValid = this.isValid(rawData);

@@ -95,6 +93,21 @@ export abstract class BreachProtocolFragment<
};
}

protected getFragmentResult(
source: BreachProtocolSource,
rawData: TData,
buffer: Buffer,
threshold: number
): BreachProtocolFragmentResult<TData, TId> {
return {
...this.getBaseResult(rawData),
source,
rawData,
image: buffer.toString('base64'),
threshold,
};
}

protected getFragmentBoundingBox() {
const { p1, p2 } = this;
const { width, height, left, top } = this.container.getCroppedBoundingBox();
@@ -160,22 +173,17 @@ export abstract class BreachProtocolOCRFragment<
const lines = this.getLines(source.text);
const rawData = this.getRawData(lines);

return {
...this.getBaseResultData(rawData),
source,
rawData,
image: buffer.toString('base64'),
threshold,
};
return this.getFragmentResult(source, rawData, buffer, threshold);
}

async ocr(threshold: number) {
const fragment = this.container.threshold(this.fragment, threshold);
const buffer = await this.container.toBuffer(fragment);
const { data } = await this.recognizeFragment(buffer);
const boxes = data.words.map((w) => w.bbox);
const source = { boxes, text: data.text };

return { buffer, source: { boxes, text: data.text } };
return { buffer, source };
}

protected chunkLine(line: string) {
8 changes: 1 addition & 7 deletions src/core/ocr/buffer-size-trim.ts
Original file line number Diff line number Diff line change
@@ -38,13 +38,7 @@ export class BreachProtocolBufferSizeTrimFragment<
const { buffer, width } = await this.container.trim(this.fragment);
const bufferSize = await this.getBufferSizeFromPixels(width);

return {
...(this.getBaseResultData(bufferSize) as any),
source: null,
rawData: bufferSize,
image: buffer.toString('base64'),
threshold: null,
};
return this.getFragmentResult(null, bufferSize, buffer, null);
}

private async getBufferSizeFromPixels(width: number) {
8 changes: 1 addition & 7 deletions src/core/ocr/buffer-size.ts
Original file line number Diff line number Diff line change
@@ -133,13 +133,7 @@ export class BreachProtocolBufferSizeFragment<
// Cache valid threshold to limit ammount of computation required on following BPs.
BreachProtocolBufferSizeFragment.cachedThreshold = threshold;

return {
...(this.getBaseResultData(bufferSize) as any),
source: null,
rawData: bufferSize,
image: buffer.toString('base64'),
threshold,
};
return this.getFragmentResult(null, bufferSize, buffer, threshold);
}

private verifyControlGroups(row: Buffer, length: number) {
10 changes: 4 additions & 6 deletions src/core/ocr/daemons.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import { Point } from '@/common';
import { HexNumber } from '../common';
import { DaemonsRawData } from '../common';
import {
BreachProtocolFragmentResult,
BreachProtocolOCRFragment,
} from './base';

type DaemonsData = HexNumber[][];

export type BreachProtocolDaemonsFragmentResult = BreachProtocolFragmentResult<
DaemonsData,
DaemonsRawData,
'daemons'
>;

export class BreachProtocolDaemonsFragment<
TImage
> extends BreachProtocolOCRFragment<DaemonsData, TImage, 'daemons'> {
> extends BreachProtocolOCRFragment<DaemonsRawData, TImage, 'daemons'> {
readonly thresholds = new Map([
[1080, 60],
[1440, 45],
@@ -37,7 +35,7 @@ export class BreachProtocolDaemonsFragment<
return lines.map((l) => this.parseLine(l));
}

isValid(rawData: DaemonsData) {
isValid(rawData: DaemonsRawData) {
const isCorrectSize = rawData.every((d) => d.length <= 5);

return this.validateSymbols(rawData.flat()) && isCorrectSize;
10 changes: 4 additions & 6 deletions src/core/ocr/grid.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import { Point } from '@/common';
import { HexNumber } from '../common';
import { GridRawData } from '../common';
import {
BreachProtocolFragmentResult,
BreachProtocolOCRFragment,
} from './base';

type GridData = HexNumber[];

export type BreachProtocolGridFragmentResult = BreachProtocolFragmentResult<
GridData,
GridRawData,
'grid'
>;

export class BreachProtocolGridFragment<
TImage
> extends BreachProtocolOCRFragment<GridData, TImage, 'grid'> {
> extends BreachProtocolOCRFragment<GridRawData, TImage, 'grid'> {
readonly thresholds = new Map([
[1080, 120],
[1440, 120],
@@ -41,7 +39,7 @@ export class BreachProtocolGridFragment<
return n > 0 && Math.sqrt(n) % 1 === 0;
}

isValid(rawData: GridData) {
isValid(rawData: GridRawData) {
return this.validateSymbols(rawData) && this.isSquare(rawData.length);
}
}
10 changes: 5 additions & 5 deletions src/core/ocr/ocr.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'path';
import sharp from 'sharp';
import registry from '../../bp-registry/registry.json';
import { BufferSize, HexNumber } from '../common';
import { BufferSize, DaemonsRawData, GridRawData } from '../common';
import { BreachProtocolOCRFragment, FragmentId } from './base';
import { BreachProtocolBufferSizeFragment } from './buffer-size';
import { BreachProtocolBufferSizeTrimFragment } from './buffer-size-trim';
@@ -101,13 +101,13 @@ describe('raw data validation', () => {
});

// prettier-ignore
const grid: HexNumber[] = [
const grid: GridRawData = [
'BD', 'E9', '1C', '7A',
'FF', '55', '55', '1C',
'7A', '7A', 'BD', 'BD',
'1C', '55', 'E9', 'E9'
];
const daemons: HexNumber[][] = [
const daemons: DaemonsRawData = [
['BD', 'E9'],
['1C', '7A'],
['FF', '55'],
@@ -134,7 +134,7 @@ describe('raw data validation', () => {
grid.map(() => ' '),
grid.slice(1),
[],
] as HexNumber[][];
] as GridRawData[];

invalidGrids.forEach((grid) => {
expect(fragment.isValid(grid)).toBeFalsy();
@@ -148,7 +148,7 @@ describe('raw data validation', () => {
daemons.map(() => ['asd']),
daemons.map(() => [' ']),
daemons.map(() => [] as string[]),
] as HexNumber[][][];
] as DaemonsRawData[];

invalidDaemons.forEach((daemons) => {
expect(fragment.isValid(daemons)).toBeFalsy();
2 changes: 1 addition & 1 deletion src/core/ocr/ocr.ts
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ export class BreachProtocolRecognitionResult {

readonly rawData = this.reduceToRawData();

readonly valid = this.results.every((r) => r.isValid);
readonly isValid = this.results.every((r) => r.isValid);

constructor(public readonly results: BreachProtocolFragmentResults) {}

Loading