"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
require("core-js/modules/es.array.includes.js");
require("core-js/modules/es.array.reduce.js");
require("core-js/modules/es.array.sort.js");
require("core-js/modules/es.string.includes.js");
require("core-js/modules/web.dom-collections.iterator.js");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function compact(array) {
  return array.filter(Boolean);
}
function difference(c, d) {
  return [c, d].reduce((a, b) => a.filter(c => !b.includes(c)));
}
function initial(array) {
  return array.slice(0, -1);
}
function groupBy(list, key) {
  const keys = list.map(item => item[key]);
  let dict = uniq(keys).reduce((prev, next) => {
    return _objectSpread(_objectSpread({}, prev), {}, {
      [next]: []
    });
  }, {});
  list.forEach(item => dict[item[key]].push(item));
  return dict;
}
function last(array) {
  return array[array.length - 1];
}
function range(n) {
  let end = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  return end ? Array.from(Array(end - n).keys()).map(k => k + n) : Array.from(Array(n).keys());
}
function uniq(array) {
  return [...new Set(array)];
}
function sampleSize(array, n, random) {
  n = n == null ? 1 : n;
  const length = array == null ? 0 : array.length;
  if (!length || n < 1) {
    return [];
  }
  n = n > length ? length : n;
  let index = -1;
  const lastIndex = length - 1;
  const result = [...array];
  while (++index < n) {
    const rand = index + Math.floor(random() * (lastIndex - index + 1));
    const value = result[rand];
    result[rand] = result[index];
    result[index] = value;
  }
  return result.slice(0, n);
}
function mulberry32(seed) {
  return function () {
    let t = seed += 0x6D2B79F5;
    t = Math.imul(t ^ t >>> 15, t | 1);
    t ^= t + Math.imul(t ^ t >>> 7, t | 61);
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  };
}
function mergeSetWith(row, oldSet, newSet) {
  row.forEach(box => {
    if (box.set === oldSet) box.set = newSet;
  });
}
function populateMissingSets(row, random) {
  const setsInUse = compact(uniq(row.map(row => row.set)));
  const allSets = range(1, row.length + 1);
  const availableSets = difference(allSets, setsInUse).sort(() => 0.5 - random());
  row.filter(box => !box.set).forEach((box, i) => box.set = availableSets[i]);
}
function mergeRandomSetsIn(row, random) {
  let probability = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.5;
  // Randomly merge some disjoint sets
  const allBoxesButLast = initial(row);
  allBoxesButLast.forEach((current, x) => {
    const next = row[x + 1];
    const differentSets = current.set !== next.set;
    const shouldMerge = random() <= probability;
    if (differentSets && shouldMerge) {
      mergeSetWith(row, next.set, current.set);
      current.right = false;
      next.left = false;
    }
  });
}
function addSetExits(row, nextRow, random) {
  // Randomly add bottom exit for each set
  const setsInRow = Object.values(groupBy(row, 'set'));
  const {
    ceil
  } = Math;
  setsInRow.forEach(set => {
    const exits = sampleSize(set, ceil(random() * set.length), random);
    exits.forEach(exit => {
      if (exit) {
        const below = nextRow[exit.x];
        exit.bottom = false;
        below.top = false;
        below.set = exit.set;
      }
    });
  });
}
function generate() {
  let width = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 8;
  let height = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : width;
  let closed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
  let seed = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
  const random = mulberry32(seed);
  const maze = [];
  const r = range(width);

  // Populate maze with empty cells:
  for (let y = 0; y < height; y += 1) {
    const row = r.map(x => {
      return {
        x,
        y,
        top: closed || y > 0,
        left: closed || x > 0,
        bottom: closed || y < height - 1,
        right: closed || x < width - 1
      };
    });
    maze.push(row);
  }

  // All rows except last:
  initial(maze).forEach((row, y) => {
    // TODO initial temp?
    populateMissingSets(row, random);
    mergeRandomSetsIn(row, random);
    addSetExits(row, maze[y + 1], random);
  });
  const lastRow = last(maze);
  populateMissingSets(lastRow, random);
  mergeRandomSetsIn(lastRow, random, 1);
  return maze;
}
var _default = exports.default = generate;