Бесконечный рекурсивный блок в 2d массиве JavaScript - PullRequest
1 голос
/ 07 ноября 2019

Я хочу реализовать метод draw (), который принимает 3 параметра width, height, padding и возвращает 2d массив, представляющий следующую форму:

[
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2],
[2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2],
[2,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,2],
[2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2],
[2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2],
[2,0,0,2,0,0,1,1,1,1,1,1,1,1,0,0,2,0,0,2],
[2,0,0,2,0,0,2,0,0,0,0,0,0,2,0,0,2,0,0,2],
[2,0,0,2,0,0,2,0,0,0,0,0,0,2,0,0,2,0,0,2],
[2,0,0,2,0,0,2,0,0,1,1,0,0,2,0,0,2,0,0,2],
[2,0,0,2,0,0,2,0,0,1,1,0,0,2,0,0,2,0,0,2],
[2,0,0,2,0,0,2,0,0,0,0,0,0,2,0,0,2,0,0,2],
[2,0,0,2,0,0,2,0,0,0,0,0,0,2,0,0,2,0,0,2],
[2,0,0,2,0,0,1,1,1,1,1,1,1,1,0,0,2,0,0,2],
[2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2],
[2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2],
[2,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,2],
[2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2],
[2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
]
  • Форма центрирована внутридруг друга, означает Padding делится на 2 с каждой стороны (например: Padding = 8 означает Padding Top 4, Padding Bottom 4, Padding Right 4, Padding Left 4)

  • top инижняя часть каждой фигуры представлена ​​цифрой 1, которая эквивалентна "-" тире буква, когда нарисовано

  • правая и левая сторона каждой фигуры представлена ​​цифрой 2, которая эквивалентнак "|"труба буква, когда рисуется

  • в других областях пробел представлен числом 0, что эквивалентно "" буква, когда рисуется

Я смог нарисовать внешний блок, но не смог сделать рекурсивный блок внутри массива, и мой код

function draw(width, height, padding){
   return Array.from({length: width}, (_, row) => {
    return Array.from({length: height}, (_, col) => {
        if(width < padding/2 || height < padding/2){
          return;
        }
        if(row === 0 || row === width - 1 ){ return 1; }
        if(col === 0 || col === height - 1){ return 2; }
        return 0;
      })
    });
  }
console.log(draw(20,20,4));

Демонстрация в действии, которую я хочу воспроизвести в JS https://infinite -спокойный-stream.herokuapp.com/draw.php

Ответы [ 4 ]

3 голосов
/ 08 ноября 2019

Вот интерактивный фрагмент, который реагирует на изменения в трех аргументах:

Я сохранил вашу идею использовать массивы с 0, 1 и 2, и преобразовать эту матрицу в строку только в самый последний момент. Но вы, конечно, можете сразу же сгенерировать финальные символы:

function draw(width, height, padding) {
    if (width <= padding+2 || height <= padding+2) { // Base case: innermost rectangle
        if (width <= 0 || height <= 0) return [];
        if (height < 2) return [Array(width).fill(1)];
        return [
            Array(width).fill(1),
            ...Array.from({length: height-2}, () => width < 2 ? [2] : [2, ...Array(width-2).fill(0), 2]),
            Array(width).fill(1),
        ];
    }
    return [
        Array(width).fill(1),
        ...Array.from({length: padding>>1}, () => [2, ...Array(width-2).fill(0), 2]),
        ...draw(width - padding - 2, height - padding - 2, padding).map((row,i) => 
            [2, ...Array(padding>>1).fill(0), ...row, ...Array(padding>>1).fill(0), 2]
        ),
        ...Array.from({length: padding>>1}, () => [2, ...Array(width-2).fill(0), 2]),
        Array(width).fill(1)
    ];
}

// I/O handling
let inputs = Array.from(document.querySelectorAll("input"));
let output = document.querySelector("pre");
document.addEventListener("input", refresh);

function refresh() {
    // Get input values
    let [width, height, padding] = inputs.map(input => Math.max(0, Math.round(+input.value)));
    padding = padding - (padding % 2); // Make sure it is a multiple of 2.
    let matrix = draw(width, height, padding);
    // Convert matrix of 0, 1, 2 to string
    output.textContent = matrix.map(row => row.map(i => " -|"[i]).join``).join`\n`;
}
refresh();
input { width: 4em }
pre { font-size: 8px }
Width: 
Height: 
Padding: 
2 голосов
/ 08 ноября 2019

Это упрощенный подход, использующий только приятную часть вычислений для простого квадранта массива (вы знаете, если вы посмотрите на первую версию).

Для сохранения требуемого шаблоназначение сингл имеет значение, которое описывает отношение между строкой и пробелом между.

В этом случае, если padding равно 4, вы получите значение 3, потому что 1 для строки itselft и 2, половина padding для выдержки между двумя строками.

Это значение называется delta и имеет значение 3, сумма 1 + 2.

Можно использовать подход, учитывающий квадранты

AB
CD

, используя только левый верхний квадрант A.

1 1 1 1 1 1 1 1 1 1 . . . . . . . . . .
2                   . . . . . . . . . .
2                   . . . . . . . . . .
2     1 1 1 1 1 1 1 . . . . . . . . . .
2     2             . . . . . . . . . .
2     2             . . . . . . . . . .
2     2     1 1 1 1 . . . . . . . . . .
2     2     2       . . . . . . . . . .
2     2     2       . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .

Все остальные позиции индексарассчитываются, если больше половины width или height. Затем он берет счет с правой / нижней стороны.

Окончательный расчет выполняется с помощью дельты размера строки (1) и половины отступа. Чтобы узнать, найдена ли линия / точка, вы можете взять остаток и проверить, равно ли значение нулю и правильное ли направление.

Небольшой взгляд на результат проверки направления

row <= col // 1
col <= row // 2

показывает,

1 1 1 1 1 1 1 1 1 1 . . . . . . . . . .
2 1 1 1 1 1 1 1 1 1 . . . . . . . . . .
2 2 1 1 1 1 1 1 1 1 . . . . . . . . . .
2 2 2 1 1 1 1 1 1 1 . . . . . . . . . .
2 2 2 2 1 1 1 1 1 1 . . . . . . . . . .
2 2 2 2 2 1 1 1 1 1 . . . . . . . . . .
2 2 2 2 2 2 1 1 1 1 . . . . . . . . . .
2 2 2 2 2 2 2 1 1 1 . . . . . . . . . .
2 2 2 2 2 2 2 2 1 1 . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . 

, которая в точности соответствует форме, нам нужно получить правильные значения, которые выглядят как сияние из центра массива.

Вместе с проверкой шаблона ипроверка направления

if (row % delta === 0 && row <= col) return 1;
if (col % delta === 0 && col <= row) return 2;

function draw(width, height, padding) {
    var delta = 1 + padding / 2;

    return Array.from({ length: height }, (_, row) => {
        if (row >= height / 2) row = height - row - 1;
        return Array.from({ length: width }, (_, col) => {
            if (col >= width / 2) col = width - col - 1;

            if (row % delta === 0 && row <= col) return 1;
            if (col % delta === 0 && col <= row) return 2;

            return ' ';
        });
    });
}

var result = draw(20, 18, 4);

result.forEach(a => console.log(a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
2 голосов
/ 07 ноября 2019

Я не могу сказать, если вы ищете полное решение. Этот сайт выглядит так, как будто он должен быть учебным сайтом. Вот некоторый код, который может помочь вам работать над интересной, рекурсивной частью решения.

Здесь важна функция layer, которая принимает два закодированных изображения и создает новое, помещая upper один поверх lower один с заданными x / y смещениями. Другие функции здесь предназначены только для упрощения демонстрации, анализа и отображения массива значений в виде строк.

Очевидно, я выбираю использовать "-", "|" и " " вместо ваших "1", "2" и "0". Это намного проще для тестирования, ИМХО, и будет очень легко изменить позже. Это имеет значение только в выражении (upper [i - y] [j - x] !== ' ')

const layer = (lower, upper, x, y, h = upper .length, w = upper [0] .length) => 
  lower .map (
    (row, i) => row .map (
      (col, j) => (i >= y && i - y < h) && 
                  (j >= x && j - x < w) && 
                  (upper [i - y] [j - x] !== ' ') 
        ? upper [i - y] [j - x]
        : lower [i] [j]
    )
  )

const show = (graph) => console .log (
  graph .map (s => s .join ('')) .join ('\n')
)

const parse = ss => ss .map (s => s.split(''))

const lower = parse ([
  '---------------------',
  '|                   |',
  '|                   |',
  '|                   |',
  '|                   |',
  '|                   |',
  '---------------------',
])

const upper = parse ([
  '------',
  '| 42 |',
  '------',
])

show (layer (lower, upper, 5, 2)) //=>
// ---------------------
// |                   |
// |    ------         |
// |    | 42 |         |
// |    ------         |
// |                   |
// ---------------------

show (layer (lower, upper, 12, 3)) //=>
// ---------------------
// |                   |
// |                   |
// |           ------  |
// |           | 42 |  |
// |           ------  |
// ---------------------

show (layer (lower, upper, 1, 1)) //=>
// ---------------------
// |------             |
// || 42 |             |
// |------             |
// |                   |
// |                   |
// ---------------------

Редактировать

Поскольку другие публикуют здесь полные решения, я включу мое, которое относится к рекурсивной структуре более серьезно.

const border = (x, y) => 
  [... Array (y)] .map ((_, i) => [...Array (x)] .map (
    (_, j) =>  i == 0 || i == y - 1 ? '1' : j == 0 || j == x - 1 ? '2' : '0' 
  ))

const draw = (w, h, p, hp = Math .floor (p / 2)) =>
  h <= p || w <= p   
    ? border(w, h)
  : layer (border (w, h), draw (w - p, h - p, p), hp, hp)

const layer = (lower, upper, x, y, h = upper .length, w = upper [0] .length) => 
  lower .map (
    (row, i) => row .map (
      (col, j) => (i >= y && i - y < h) && 
                  (j >= x && j - x < w) && 
                  (upper [i - y] [j - x] !== 0) 
        ? upper [i - y] [j - x]
        : lower [i] [j]
    )
  )

const show = (graph) => console .log (graph .map (ns => ns .join ('')) .join ('\n'), '', graph .map (ns => ns .join ('').replace(/1/g, '-').replace(/2/g, '|').replace(/0/g, ' ')) .join ('\n'))


show (draw (40, 20, 4))

layer, как указано выше, border создает закодированное изображение, содержащее внешнюю границу, а основная функция draw обрабатывает повторение внутрь, пока не останется места,(Я переключился на необходимые 0, 1, 2 в этих.) show просто отображает результат, как с цифрами, так и в ASCII art.

Примечание хотя мне и нравится эта разбивка проблемы, решение от Нины Шольц в целом проще. Я бы выбрал этот, если бы меня больше не интересовала базовая рекурсия.

1 голос
/ 08 ноября 2019

Вот подход по повторению. Просто возьмите якорь, нарисуйте прямоугольник. И перезвоните себе с переведенным якорем (согласно заполнению)

function draw(w,h,p){
    function fill({x,y}, w, h){
        if(w < 1 || h < 1){return M}
        for(let i=x; i < x+h; ++i){
            M[i][y] = 2;
            M[i][y+w-1] = 2;
        }
        for(let j=y; j < y+w; ++j){
            M[x][j] = 1;
            M[x+h-1][j] = 1;
        }
        //-p-2 to remove padding and the current borders
        return fill({x:x+p/2+1, y:y+p/2+1}, w-p-2, h-p-2)
    }
    let M = Array(h).fill(0).map(x=>Array(w).fill(0));
    return fill({x:0,y:0}, w, h);
}
console.log(draw(20,20,4).map(x=>x.join('')).join('\n'))
console.log(draw(5,5,2).map(x=>x.join('')).join('\n'))
...