From 797aeffa17bea04cff78519979b955d512844bd2 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 5 Nov 2017 19:44:42 -0800 Subject: [PATCH] Start sketching out new resize algo --- .../__fixtures__/resizeDoubleTestCases.txt | 157 ++++++++++++++ experiments/resizeAlgorithm/resizeUtils.js | 18 ++ .../resizeAlgorithm/resizeUtils.test.js | 199 ++++++++++++++++++ 3 files changed, 374 insertions(+) create mode 100644 experiments/resizeAlgorithm/__fixtures__/resizeDoubleTestCases.txt create mode 100644 experiments/resizeAlgorithm/resizeUtils.js create mode 100644 experiments/resizeAlgorithm/resizeUtils.test.js diff --git a/experiments/resizeAlgorithm/__fixtures__/resizeDoubleTestCases.txt b/experiments/resizeAlgorithm/__fixtures__/resizeDoubleTestCases.txt new file mode 100644 index 00000000..70eabf44 --- /dev/null +++ b/experiments/resizeAlgorithm/__fixtures__/resizeDoubleTestCases.txt @@ -0,0 +1,157 @@ +=== doubles a simple box +11111111 +11111111 +11111111 +--- +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +=== moves adjacent boxes +11111111 +11111111 +11111111 +22222222 +22222222 +22222222 +--- +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +2222222222222222 +2222222222222222 +2222222222222222 +2222222222222222 +2222222222222222 +2222222222222222 +=== moves the bottom adjacent box independent of order +22222222 +22222222 +22222222 +11111111 +11111111 +11111111 +--- +2222222222222222 +2222222222222222 +2222222222222222 +2222222222222222 +2222222222222222 +2222222222222222 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +=== moves the bottom adjacent box only in the y axis +22222222 +22222222 +22222222 + 11111111 + 11111111 + 11111111 +--- +2222222222222222 +2222222222222222 +2222222222222222 +2222222222222222 +2222222222222222 +2222222222222222 + 1111111111111111 + 1111111111111111 + 1111111111111111 + 1111111111111111 + 1111111111111111 + 1111111111111111 +=== moves the right adjacent box only in the y axis +22222222 +2222222211111111 +2222222211111111 + 11111111 +--- +2222222222222222 +22222222222222221111111111111111 +22222222222222221111111111111111 +22222222222222221111111111111111 +22222222222222221111111111111111 +22222222222222221111111111111111 + 1111111111111111 +=== does not move nearly adjacent boxes +11111111 +11111111 +11111111 + +22222222 +22222222 +22222222 +--- +1111111111111111 +1111111111111111 +1111111111111111 +1111111111111111 +3333333333333333 +3333333333333333 +2222222222222222 +2222222222222222 +2222222222222222 +2222222222222222 +=== does not push kitty corner neighbors +11111111 +11111111 +11111111 + 22222222 + 22222222 + 22222222 +--- +1111111111111111 +1111111111111111 +1111111111111111 +1111111133333333 +1111111133333333 +1111111133333333 +=== moves a box with two adjacent neighbors down and to the right + 11111111 + 11111111 +2233333311 +2233333311 +2222222244444444 +2222222244444444 + 44444444 + 44444444 +--- + 1111111111111111 + 1111111111111111 +223333333333333311 +223333333333333311 +223333333333333311 +223333333333333311 +223333333333333311 +223333333333333311 +22222222222222224444444444444444 +22222222222222224444444444444444 + 4444444444444444 + 4444444444444444 + 4444444444444444 + 4444444444444444 + 4444444444444444 + 4444444444444444 +=== wat + 2222 + 2222 +11114444 +11114444 +--- + 22222222 + 22222222 +111133332222 +111133332222 +1111111144444444 +1111111144444444 + 44444444 + 44444444 \ No newline at end of file diff --git a/experiments/resizeAlgorithm/resizeUtils.js b/experiments/resizeAlgorithm/resizeUtils.js new file mode 100644 index 00000000..bfff4d81 --- /dev/null +++ b/experiments/resizeAlgorithm/resizeUtils.js @@ -0,0 +1,18 @@ +export function double(boxes) { + const doubledBoxes = new Map(); + // Find the top/left most box + // Update the width/height of the first box. + // Find any boxes that are touching the bottom of that box + // Move those boxes down + // Find any boxes that are touching the right of that box + // Move those boxes right + // Repeat for boxes that are touching + // Resize any remaining boxes + for (const [key, box] of boxes) { + const width = box.width * 2; + const height = box.height * 2; + const top = box.top * 2; + doubledBoxes.set(key, { ...box, top, width, height }); + } + return doubledBoxes; +} diff --git a/experiments/resizeAlgorithm/resizeUtils.test.js b/experiments/resizeAlgorithm/resizeUtils.test.js new file mode 100644 index 00000000..566797da --- /dev/null +++ b/experiments/resizeAlgorithm/resizeUtils.test.js @@ -0,0 +1,199 @@ +import fs from "fs"; + +import { double } from "./resizeUtils"; + +function compositeContains(composite, value) { + return !!(composite & value); +} + +const decomposeValues = composite => + [1, 2, 4].filter(value => compositeContains(composite, value)); + +function matrixFromMap(map) { + return map + .trim() + .split("\n") + .map(row => row.trim().split("")); +} + +function walkMatrix(matrix, cb) { + matrix.forEach((row, y) => { + row.forEach((value, x) => { + cb(x, y, value); + }); + }); +} + +function boxesFromMatrix(matrix) { + const boxes = new Map(); + walkMatrix(matrix, (x, y, composite) => { + const values = decomposeValues(composite); + values.forEach(value => { + if (!boxes.has(value)) { + // Initialize the box + boxes.set(value, { top: y, left: x, width: 1, height: 1 }); + } else { + const box = boxes.get(value); + if (box.top === y && x - box.left === box.width) { + // The top side is still growing. + box.width++; + } else if (box.left === x && y - box.top === box.height) { + // The left side is still growing. + box.height++; + } + } + }); + }); + return boxes; +} + +function parseBoxes(map) { + return boxesFromMatrix(matrixFromMap(map)); +} + +function boxContains(box, pos) { + if ( + pos.x >= box.left && + pos.x < box.left + box.width && + pos.y >= box.top && + pos.y < box.top + box.height + ) { + return true; + } + return false; +} + +function matrixFromBoxes(boxes) { + let width = 0; + let height = 0; + boxes.forEach(box => { + width = Math.max(width, box.left + box.width); + height = Math.max(height, box.top + box.height); + }); + const matrix = []; + for (let y = 0; y < height; y++) { + if (!matrix[y]) { + matrix[y] = []; + } + for (let x = 0; x < width; x++) { + let cellNumber = 0; + boxes.forEach((box, boxNumber) => { + if (boxContains(box, { x, y })) { + cellNumber += Number(boxNumber); + } + }); + matrix[y].push(String(cellNumber || " ")); + } + } + return matrix; +} + +function mapFromMatrix(matrix) { + return matrix.map(row => row.join("")).join("\n"); +} + +function assertionsFromTxt(txtPath) { + return fs + .readFileSync(txtPath, "utf8") + .split(/\=\=\= /) + .filter(Boolean) + .map(assertionText => { + const lines = assertionText.split("\n"); + const [message, ...bodyLines] = lines; + let [input, output] = bodyLines.join("\n").split("---\n"); + input = input.trim(); + output = output.trim(); + return { message, input, output }; + }); +} + +xdescribe("integration", () => { + assertionsFromTxt( + "./js/__fixtures__/resizeDoubleTestCases.txt" + ).forEach(({ input, output, message }) => { + it(message, () => { + const matrix = matrixFromMap(input); + const boxes = boxesFromMatrix(matrix); + const doubledBoxes = double(boxes); + const derivedMatrix = matrixFromBoxes(doubledBoxes); + const derivedMap = mapFromMatrix(derivedMatrix); + expect(derivedMap).toEqual(output); + }); + }); +}); + +describe("matrixFromBoxes", () => { + it("can make a matrix from a box", () => { + const boxes = new Map([["1", { top: 0, left: 0, width: 4, height: 4 }]]); + expect(matrixFromBoxes(boxes)).toEqual( + matrixFromMap(` + 1111 + 1111 + 1111 + 1111 + `) + ); + }); + it("can make a matrix from two boxes", () => { + const boxes = new Map([ + ["1", { top: 0, left: 0, width: 4, height: 4 }], + ["2", { top: 1, left: 1, width: 2, height: 2 }] + ]); + expect(matrixFromBoxes(boxes)).toEqual( + matrixFromMap(` + 1111 + 1331 + 1331 + 1111 + `) + ); + }); +}); + +describe("parseBoxes", () => { + it("parses a box", () => { + const boxes = parseBoxes(` + 1111 + 1111 + 1111 + 1111 + `); + expect(boxes.get(1)).toEqual({ top: 0, left: 0, width: 4, height: 4 }); + }); + it("parses a smaller box", () => { + const boxes = parseBoxes(` + 11 + 11 + `); + expect(boxes.get(1)).toEqual({ top: 0, left: 0, width: 2, height: 2 }); + }); + it("parses nested boxes", () => { + const boxes = parseBoxes(` + 1111 + 1331 + 1331 + 1111 + `); + expect(boxes.get(1)).toEqual({ top: 0, left: 0, width: 4, height: 4 }); + expect(boxes.get(2)).toEqual({ top: 1, left: 1, width: 2, height: 2 }); + }); + it("parses overlapping boxes", () => { + const boxes = parseBoxes(` + 110 + 132 + 022 + `); + expect(boxes.get(1)).toEqual({ top: 0, left: 0, width: 2, height: 2 }); + expect(boxes.get(2)).toEqual({ top: 1, left: 1, width: 2, height: 2 }); + }); + it("parses three overlapping boxes", () => { + const boxes = parseBoxes(` + 1154 + 1374 + 0220 + `); + expect(boxes.get(1)).toEqual({ top: 0, left: 0, width: 3, height: 2 }); + expect(boxes.get(2)).toEqual({ top: 1, left: 1, width: 2, height: 2 }); + expect(boxes.get(4)).toEqual({ top: 0, left: 2, width: 2, height: 2 }); + }); +});