!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).RoomFinder={})}(this,(function(e){"use strict";function n(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}var t={exports:{}};!function(e){var n={single_source_shortest_paths:function(e,t,o){var i={},s={};s[t]=0;var r,a,l,d,c,h,f,u=n.PriorityQueue.make();for(u.push(t,0);!u.empty();)for(l in a=(r=u.pop()).value,d=r.cost,c=e[a]||{})c.hasOwnProperty(l)&&(h=d+c[l],f=s[l],(void 0===s[l]||f>h)&&(s[l]=h,u.push(l,h),i[l]=a));if(void 0!==o&&void 0===s[o]){var m=["Could not find a path from ",t," to ",o,"."].join("");throw new Error(m)}return i},extract_shortest_path_from_predecessor_list:function(e,n){for(var t=[],o=n;o;)t.push(o),o=e[o];return t.reverse(),t},find_path:function(e,t,o){var i=n.single_source_shortest_paths(e,t,o);return n.extract_shortest_path_from_predecessor_list(i,o)},PriorityQueue:{make:function(e){var t,o=n.PriorityQueue,i={};for(t in e=e||{},o)o.hasOwnProperty(t)&&(i[t]=o[t]);return i.queue=[],i.sorter=e.sorter||o.default_sorter,i},default_sorter:function(e,n){return e.cost-n.cost},push:function(e,n){var t={value:e,cost:n};this.queue.push(t),this.queue.sort(this.sorter)},pop:function(){return this.queue.shift()},empty:function(){return 0===this.queue.length}}};e.exports=n}(t);var o,i=t.exports;class s{constructor(e,t){this.name=e,this.reversed=t,n(this,"_type","ForkNode")}}function r(e){return"string"==typeof e?new s(e,!0):new s(e.name,!e.reversed)}class a{constructor(e,t){this.name=e,this.floor=t,n(this,"_type","StairNode")}}class l{constructor(e){n(this,"_type","BasicRoomNode"),n(this,"name",void 0),this.name=e}}function d(e){return null!=e&&!(e instanceof l)}function c(e){return e instanceof s?`ForkNode-$-${e.name}-$-${JSON.stringify(e.reversed)}`:e instanceof a?`StairNode-$-${e.name}-$-${JSON.stringify(e.floor)}`:`BasicRoomNode-$-${e.name}`}function h(e){const n=e.split("-$-");return"ForkNode"===n[0]?new s(n[1],JSON.parse(n[2])):"StairNode"===n[0]?new a(n[1],JSON.parse(n[2])):new l(n[1])}function f(e,n){const t=e.map((e=>({oneWay:e.oneWay,nodes:e.nodes.map((({nodeId:e,edgeLengthFromPreviousNodeInHallway:n})=>({nodeId:c(e),edgeLengthFromPreviousNodeInHallway:n})))}))),o=function(e,n){const t=e.map((e=>e.nodes)).flat().map((e=>e.nodeId)).filter((e=>e instanceof a));return[...new Set(t.map((e=>e.name)))].map((e=>{var o;return{floors:t.filter((n=>n.name===e)).sort(((e,n)=>n.floor-e.floor)).map(c),oneWay:null!==(o=n[e])&&void 0!==o&&o}}))}(e,n),i=e.map((e=>e.nodes)).flat().map((e=>e.nodeId)).filter((e=>e instanceof s)).filter((e=>!e.reversed)).map((e=>[c(e),c(r(e.name))]));const l={};return t.forEach((({oneWay:e,nodes:n})=>{n.forEach(((t,s)=>{const r=t.nodeId,a={};0!==s&&"forward"!==e&&(a[n[s-1].nodeId]=n[s].edgeLengthFromPreviousNodeInHallway),s!==n.length-1&&"backward"!==e&&(a[n[s+1].nodeId]=n[s+1].edgeLengthFromPreviousNodeInHallway),o.forEach((e=>{const n=e.floors,t=n.indexOf(r);-1!==t&&n.forEach(((n,o)=>{if(n===r)return;if("up"===e.oneWay&&o>t)return;if("down"===e.oneWay&&o<t)return;const i=Math.abs(t-o);a[n]=i*(1-.001*i)}))})),i.forEach((([e,n])=>{e===r?a[n]=1:n===r&&(a[e]=1)})),l[r]=a}))})),l}function u(e){const n=[...Object.keys(e),...Object.values(e).map((e=>Object.keys(e))).flat()],t=[...new Set(n)];if(0===t.length)return{connected:!0,connectedSections:[]};const o=[],i=[];function s(n){if(!o.includes(n)){o.push(n);const t=e[n]?Object.keys(e[n]):[];for(const e of t)s(e);i.unshift(n)}}for(const e of t)s(e);const r={};function a(n,t){if(![...Object.keys(r),...Object.values(r).flat()].includes(n)){Object.keys(r).includes(t)||(r[t]=[]),r[t].push(n);const o=Object.keys(e).filter((t=>Object.keys(e[t]).includes(n)));for(const e of o)a(e,t)}}for(const e of i)a(e,e);const l=Object.entries(r).map((([e,n])=>[...new Set([e,...n])]));return{connected:1===l.length,connectedSections:l}}function m(n){return n===e.Direction.LEFT?"left":n===e.Direction.RIGHT?"right":"front"}function p(e){return g(e)?"turn "+m(e):"go straight"}function g(n){return n===e.Direction.LEFT||n===e.Direction.RIGHT}function w(e,{capitalize:n=!0,periods:t=!1}){return e.trim().replace(/\n,/g,",").split("\n").filter((e=>""!==e)).map((e=>(n?e[0].toUpperCase()+e.slice(1):e)+(t?".":""))).join("\n")}e.Direction=void 0,(o=e.Direction||(e.Direction={}))[o.LEFT=-1]="LEFT",o[o.RIGHT=1]="RIGHT",o[o.FRONT=2]="FRONT";class y{constructor(e,{oneWayStaircases:t={}}={},o=e.flatMap((e=>e.nodes.map((e=>e.nodeId)).filter((e=>!(e instanceof l))).map((e=>e.name))))){this.hallways=e,this.allowedConnections=o,n(this,"graph",void 0),n(this,"roomsList",void 0),n(this,"oneWayStaircases",void 0),this.oneWayStaircases=t;const i=this.hallways.map((e=>({nodes:e.nodes.filter((({nodeId:e})=>e instanceof l||o.includes(e.name))),oneWay:e.oneWay})));this.graph=f(i,this.oneWayStaircases),this.roomsList=e.flatMap((e=>e.partList)).filter((e=>"name"in e&&null!=e.name)).flatMap((e=>e.aliases.concat(e.name))).sort()}withAllowedConnectionTypes(e){return new y(this.hallways,{oneWayStaircases:this.oneWayStaircases},"function"==typeof e?this.allowedConnections.filter(e):e)}getHallwayIndexAndIndex(e){const n=this.hallways.map((n=>n.getRoomInd(e))),t=n.findIndex((e=>-1!==e));return-1===t?null:[t,n[t]]}getHallwayIndexAndIndexFromNode(e){const n=this.hallways.map((n=>n.partList.findIndex((n=>"nodeId"in n&&null!=n.nodeId&&c(n.nodeId)===c(e))))),t=n.findIndex((e=>-1!==e));return[t,n[t]]}getStairConnectionInstruction(e,n,t){if(e.includes("elevator"))return`go to floor ${t}\n`;const o=t>n?"up":"down",i=Math.abs(n-t);return`go ${o} ${i} floor${i>1?"s":""} of stairs\n`}getDirections(e,n,{capitalize:t=!0,periods:o=!1}={capitalize:!0,periods:!1}){if(!this.isValidRoomName(e)||!this.isValidRoomName(n))return null;const[s,r]=this.getHallwayIndexAndIndex(e),[l,d]=this.getHallwayIndexAndIndex(n);if(1===this.hallways.length)return w(this.hallways[0].getDirectionsFromIndices(r,d,{isBeginningOfDirections:!0,isEndOfDirections:!0,entranceWasStraight:!1}),{capitalize:t,periods:o});const f=this.hallways[s].idOfClosestNodeToIndex(r,this.allowedConnections),u=this.hallways[l].idOfClosestNodeToIndex(d,this.allowedConnections);let m=!1;const p=(y=this.graph,v=f,I=u,i.find_path(y,c(v),c(I)).map((e=>h(e))));var y,v,I;let N="",[$,F]=[s,r];for(let e=1;e<p.length;e++){const n=p[e],[t,o]=this.getHallwayIndexAndIndexFromNode(n),i=p[e-1],[,s]=this.getHallwayIndexAndIndexFromNode(i);i instanceof a&&n instanceof a&&i.name===n.name?(N+=this.hallways[$].getDirectionsFromIndices(F,s,{isBeginningOfDirections:""===N,isEndOfDirections:!1,entranceWasStraight:m}),N+=this.getStairConnectionInstruction(i.name,i.floor,n.floor),[$,F]=this.getHallwayIndexAndIndexFromNode(n),m=!0):t!==$&&(N+=this.hallways[$].getDirectionsFromIndices(F,s,{isBeginningOfDirections:""===N,isEndOfDirections:!1,entranceWasStraight:m}),m=!g(this.hallways[$].partList[s].side),[$,F]=[t,o])}return N+=this.hallways[$].getDirectionsFromIndices(F,d,{isBeginningOfDirections:""===N,isEndOfDirections:!0,entranceWasStraight:m}),w(N,{capitalize:t,periods:o})}isValidRoomName(e){return"string"==typeof e&&null!=this.getHallwayIndexAndIndex(e)}}class v{constructor(t,o=e.Direction.LEFT,{edgeLengthFromPreviousNodeInHallway:i,prefix:s="room",aliases:r=[]}={}){this.name=t,this.side=o,n(this,"prefix","room"),n(this,"aliases",[]),n(this,"edgeLengthFromPreviousNodeInHallway",null),this.prefix=s,this.aliases=r,this.edgeLengthFromPreviousNodeInHallway=i}get fullName(){return(""===this.prefix?"":this.prefix+" ")+this.name}onPass(e,n){return""}onLeave(e,n,t){if(n){let n="";return n+=p(e*this.side),this.fullName&&(n+=` out of ${this.fullName}`),n+="\n",n}return t?p(e*this.side).replace(/^go /,"continue ")+"\n":g(this.side)?`, and then ${p(e*this.side)}\n`:""}onArrive(e,n){return g(this.side)||n?`continue, then ${p(this.side*e)} into ${this.fullName}\n`:`continue, then after entering ${this.fullName}, `}}class I extends v{constructor(t,o=e.Direction.LEFT,{edgeLengthFromPreviousNodeInHallway:i,prefix:r="room",aliases:a=[],nodeId:d}={}){var c;super(t,o,{edgeLengthFromPreviousNodeInHallway:i,prefix:r,aliases:a}),n(this,"nodeId",void 0),null!=d?this.nodeId=(c=d)instanceof s?c:new s(c,!1):null!=t&&(this.nodeId=new l(t))}}class N extends I{constructor(e,n,t,o=1){super(null,e,{nodeId:n,edgeLengthFromPreviousNodeInHallway:o}),this.destinationName=t}get fullName(){return this.destinationName}}class ${constructor(e,{allowFrontConnectionsInMiddle:t=!1,oneWay:o=!1}={}){this.partList=e,n(this,"allowFrontConnectionsInMiddle",void 0),n(this,"oneWay",void 0),this.allowFrontConnectionsInMiddle=t,this.oneWay=o}getRoomInd(e){return this.partList.findIndex((n=>"name"in n&&null!=n.name&&(n.name.toUpperCase().trim()===e.toUpperCase().trim()||n.aliases.map((e=>e.toUpperCase())).includes(e.toUpperCase()))))}idOfClosestNodeToIndex(e,n){return this.partList[e].nodeId}get nodes(){let e,n=this.partList.filter((e=>"nodeId"in e&&null!=e.nodeId));return n.map(((t,o)=>{let i,s;for(let e=o;e<n.length;e++)if(d(n[e].nodeId)){i=e;break}if(null!=e&&null!=i){var r;const t=i-e;s=(null!==(r=n[i].edgeLengthFromPreviousNodeInHallway)&&void 0!==r?r:1)/t}else s=1;return d(n[o].nodeId)&&(e=o),{edgeLengthFromPreviousNodeInHallway:s,nodeId:t.nodeId}}))}getDirectionsFromIndices(e,n,{isBeginningOfDirections:t,isEndOfDirections:o,entranceWasStraight:i}){const s=this.partList[e],r=this.partList[n];if(e===n)return`Bruh. You at ${s.fullName}\n`;let a="";const l=n>e?1:-1;a+=s.onLeave(l,t,i);for(let t=e;t!==n;t+=l){const e=this.partList[t],n=t-l,o=n>=0&&n<this.partList.length&&this.partList[t-l];a+=e.onPass(l,o)}return a+=r.onArrive(l,o),a}}class F extends v{get fullName(){return this._fullName}constructor(e,t,o="the stairs",i){super(null,e,{edgeLengthFromPreviousNodeInHallway:i}),n(this,"nodeId",void 0),n(this,"_fullName",void 0),this._fullName=o,this.nodeId=t}onLeave(e,n,t){return super.onLeave(e,!0,t)}onArrive(e){return`continue, then ${p(this.side*e)} into ${this.fullName}\n`}}class S{constructor(e){this.direction=e}onPass(e,n){let t="";return t+="continue, then "+p(this.direction*e),(n instanceof I||n instanceof F)&&g(n.side)&&(t+=` (after passing ${n.fullName} on your ${m(n.side*e)})`),t+="\n",t}}function x(e){return e instanceof a?`onFloor('${e.name}', ${e.floor})`:e instanceof l?`room '${e.name}'`:e.reversed?`reverseConnection('${e.name}')`:`'${e.name}'`}function L(n){const t=u(n.graph).connectedSections;let o=null;if(n.roomsList.forEach(((e,i)=>{n.roomsList.indexOf(e)!==i&&(o={valid:!1,reason:`There's more than one room with the name '${e}'`,connectedSections:t})})),null!=o)return o;for(const[e,o]of Object.entries(n.graph))for(const[n,i]of Object.entries(o))if(i<0)return{valid:!1,reason:`The edge from node ${x(h(e))} to node ${x(h(n))} has a negative weight`,connectedSections:t};const i=n.hallways.flatMap((e=>e.nodes)).map((({nodeId:e})=>e)).filter(d);for(const e of i)if(e instanceof a){const n=i.filter((e=>e instanceof a)).filter((n=>n.name===e.name));if(n.filter((n=>n.floor===e.floor)).length>1)return{valid:!1,reason:`There's more than one Stairs node with the name onFloor('${e.name}', ${e.floor}). One of the Stairs in the staircase should probably be on a different floor.`,connectedSections:t};if(1===n.length)return{valid:!1,reason:`There are Stairs with the nodeId onFloor('${e.name}', ${e.floor}) with no corresponding Stairs on a different floor. You need to either add Stairs somewhere else with the same name and a different floor, or remove this node.`,connectedSections:t}}else{if(i.filter((n=>n instanceof s&&n.name===e.name&&n.reversed===e.reversed)).length>1)return{valid:!1,reason:`There's more than one Fork with the nodeId ${x(e)}. One of them should probably ${e.reversed?"not ":""}be a reverseConnection.`,connectedSections:t};if(0===i.filter((n=>n instanceof s&&n.reversed!==e.reversed)).filter((({name:n})=>n===e.name)).length)return{valid:!1,reason:`There's a Fork with the nodeId ${x(e)} that doesn't have a corresponding ${e.reversed?"regular connection":"reverseConnection"}. You need to either add a Fork somewhere else with the nodeId ${x(r(e))} to connect it to this node, or remove this node.`,connectedSections:t}}if(n.hallways.forEach(((n,i)=>{n.allowFrontConnectionsInMiddle||n.partList.forEach(((s,r)=>{s instanceof S||null!=o||0!==r&&r!==n.partList.length-1&&s.side===e.Direction.FRONT&&(o={valid:!1,reason:`The element at position ${r} of the Hallway at position ${i} has the side FRONT, but it is not the first or last element of the hallway`,connectedSections:t})}))})),null!=o)return o;const l=n.hallways.findIndex((e=>0===e.nodes.map((e=>e.nodeId)).filter(d).filter((e=>n.allowedConnections.includes(e.name))).length));return n.hallways.length>1&&-1!==l?{valid:!1,reason:`The hallway at index ${l} has no connector nodes (Forks or Stairs) to connect it to the rest of the building.`,connectedSections:t}:u(n.graph).connected?(n.hallways.forEach((({partList:e},n)=>{null==o&&(e[0]instanceof S?o={valid:!1,reason:`There first element of the Hallway at position ${n} is a Turn. There is no reason to include a Turn here because it will never be passed.`,connectedSections:t}:e[e.length-1]instanceof S&&(o={valid:!1,reason:`There last element of the Hallway at position ${n} is a Turn. There is no reason to include a Turn here because it will never be passed.`,connectedSections:t}))})),null!=o?o:{valid:!0,connectedSections:t}):{valid:!1,reason:"Not all nodes are connected; see isValidBuilding(building).connectedSections to find which node groups are separated.",connectedSections:u(n.graph).connectedSections}}e.Building=y,e.Fork=N,e.ForkNode=s,e.Hallway=$,e.Room=I,e.SimpleHallway=class extends ${constructor(n,t,o){super([new N(e.Direction.LEFT,n,""),...t],{allowFrontConnectionsInMiddle:!0}),this.hallwayName=o}getDirectionsFromIndices(e,n){const t=this.partList[n].fullName,o=this.partList[e].fullName;return 0===e?`Enter ${t}, which is in ${this.hallwayName}\n`:0===n?`Exit ${o}\n`:`Exit ${o} and enter ${t} (both of which are in ${this.hallwayName})\n`}},e.StairNode=a,e.Stairs=F,e.Turn=S,e.assertValidBuilding=function(e){const n=L(e);if(!n.valid)throw`asserValidBuilding: This building is invalid. ${n.reason}`},e.dirToString=m,e.dirToTurnString=p,e.isLeftOrRight=g,e.isValidBuilding=L,e.onFloor=function(e,n){return new a(e,n)},e.reverseConnection=r,Object.defineProperty(e,"__esModule",{value:!0})}));
