/// interface TrickierOptions { region : RegionRandom; map : RoomRandomMap; otherRoom : RoomRandom; } interface TrickyOptions extends TrickierOptions { otherRoomDirection : number; trickyRoomDirection : number; x : number; y : number; } interface RoomRandomNode { room : RoomRandom; coordinates : Array; distance : number; } class RoomRandom extends Room { public connectableOn : Array = [Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST]; public randomizable = true; // non-randomizable rooms don't get placed automatically public placed = false; public appearChance = 75; public extraConnectionChance = 75; public backgroundImage = "tomato"; public lastMap : RoomRandomMap; public constructor (id? : string, fodder? : boolean, forbidden? : boolean) { super(id, fodder); this.randomizable = forbidden != true; } public getBackgroundClass () { if (this.isImageDefined()) { return this.backgroundImage; } return "tomato"; } private isImageDefined () { try { for (var i = 0; i < document.styleSheets.length; i++) { var rules = document.styleSheets[i]['rules'] || document.styleSheets[i]['cssRules']; for (var x in rules) { if (typeof rules[x].selectorText == 'string' && rules[x].selectorText == "." + this.backgroundImage) { return true; } } } return false; } catch (e) { console.warn("Unable to read image"); return true; } } public isConnectableOn (oppositeDirection : number) { return this.connectableOn.indexOf(oppositeDirection) != -1; } public trickyCode : (options : TrickyOptions) => boolean; public getAnyDirection (options : TrickierOptions) : TrickyOptions { let directionShuffler = new Shuffler(this.connectableOn); for (let direction = directionShuffler.getOne(); direction != undefined; direction = directionShuffler.getOne()) { let oppositeDirection = OppositeDirection[Direction[direction]]; let otherCoordinates = options.map.getCoordinates(options.otherRoom); let wouldbeCoordinates = Room.shift(otherCoordinates, oppositeDirection); let trickyOptions = { otherRoom : options.otherRoom, otherRoomDirection : oppositeDirection, trickyRoomDirection : direction, map : options.map, region : options.region, x : wouldbeCoordinates[0], y : wouldbeCoordinates[1] }; if (this.isPlaceable(trickyOptions)) { return trickyOptions; } } } public isPlaceable (options : TrickyOptions) { if (!this.isConnectableOn(options.trickyRoomDirection) || !options.map.isFree(options.x, options.y)) { // This can't connect through that! // That coordinate isn't free! return false; } if (options.otherRoom == undefined || !options.otherRoom.isConnectableOn(options.otherRoomDirection)) { // There is no other room there? // The other room doesn't like it this way return false; } // Do I have my own tricky code? if (this.trickyCode != undefined) { return this.trickyCode(options); } return true; } public getDistanceTo (room : RoomRandom) { let otherCoordinates = this.lastMap.getCoordinates(room); return this.getDistanceToCoordinates(otherCoordinates); } public getDistanceToCoordinates (otherCoordinates) { let myCoordinates = this.lastMap.getCoordinates(this); if (myCoordinates != undefined && otherCoordinates != undefined) { let c1 = myCoordinates; let c2 = otherCoordinates; return Math.abs(c1[0] - c2[0]) + Math.abs(c1[1] - c2[1]); } } /** * This implementation is sufficiently fast for constant use. * @param pathEnd * @param map * @param availableRooms * @returns {Array} */ public findPathTo (pathEnd : RoomRandom, validRoom? : (room : RoomRandom) => boolean) { validRoom = validRoom == undefined ? () => {return true;} : validRoom; let map = this.lastMap; let endPosition = map.getCoordinates(pathEnd); let open = []; let distance = (c1, c2) => { return Math.abs(c1[0] - c2[0]) + Math.abs(c1[1] - c2[1]); }; let neighbors = (room : RoomRandom, x : number, y : number) => { let neighs = []; for (let direction = 0; direction < room.connections.length; direction++) { let otherRoom = room.connections[direction]; if (otherRoom != undefined && open.indexOf(otherRoom) == -1 && validRoom( otherRoom)) { let dirCoordinates = Room.shift([x, y], direction); let dir = [otherRoom, dirCoordinates, distance(endPosition, dirCoordinates)]; neighs.push(dir); } } return neighs.sort((a,b) => { return ( a[2]) - ( b[2]);}); }; let shortestPath = { length : map.getRoomCount() }; let noPath = shortestPath; let cPath = []; let findPath = (myArray) => { let room = myArray[0]; cPath.push(myArray); if (room == pathEnd) { if (shortestPath.length >= cPath.length) { shortestPath = cPath.slice(0); } } else if (shortestPath.length > (cPath.length)) { open.push(room); let otherRooms = neighbors(room, myArray[1][0], myArray[1][1]); for (let i = 0; i < otherRooms.length; i++) { if ((cPath.length + 1) < shortestPath.length) { findPath(otherRooms[i]); } } open.pop(); } cPath.pop(); }; findPath([this, map.getCoordinates(this)]); return shortestPath != noPath ? shortestPath : undefined; } public getBestDirectionTo (otherRoom : RoomRandom, validRoom? : (room : RoomRandom) => boolean) { let path = this.findPathTo(otherRoom, validRoom); if (path != undefined) { if (path.length == 1) { return undefined; } return this.connections.indexOf(path[1][0]); } } public getAStarPathTo (otherRoom : RoomRandom, validRoom? : (room : RoomRandom) => boolean) { validRoom = validRoom != undefined ? validRoom : () => {return true}; let distance = (c1, c2) => { return Math.abs(c1[0] - c2[0]) + Math.abs(c1[1] - c2[1]); }; let isVisited = (room) => { return visited.indexOf(room) != -1; }; let getNeighbors = (node : RoomRandomNode) => { let neighbors : Array = []; for (let direction = 0; direction < node.room.connections.length; direction++) { if (node.room.connections[direction] != undefined && !isVisited(node.room.connections[direction]) && validRoom( node.room.connections[direction])) { let coordinates = Room.shift(node.coordinates, direction); neighbors.push({ room : node.room.connections[direction], coordinates : coordinates, distance : distance(coordinates, endNode.coordinates) }); visited.push( node.room.connections[direction]); } } return neighbors; }; let getClosestPath = () => { let shortest = 0; for (let i = 1; i < open.length; i++) { let lastPoint = open[i][open[i].length - 1]; if (lastPoint.distance < open[shortest][open[shortest].length - 1].distance) { shortest = i; } } return shortest; }; let endNode = {room : otherRoom, coordinates : this.lastMap.getCoordinates(otherRoom), distance : 0}; let startCoordinates = this.lastMap.getCoordinates(this); let startNode = {room : this, coordinates : startCoordinates, distance : distance(startCoordinates, endNode.coordinates)}; let open : Array> = [[startNode]]; let closed : Array> = []; let shortestPath = this.lastMap.getRoomCount(); let shortestIndex; let myPath; let closest = 0; let visited : Array = [this]; while (open.length > 0) { myPath = open.splice(closest, 1)[0]; if (myPath[myPath.length - 1].distance == 0) { let push = closed.push(myPath); if (myPath.length < shortestPath) { shortestPath = myPath.length; shortestIndex = push - 1; } // lazy, first path is very likely to be the best break; } else { let neighbors = getNeighbors(myPath[myPath.length - 1]); for (let i = 0; i < neighbors.length; i++) { open.push(myPath.concat([neighbors[i]])); } } for (let i = open.length - 1; i >= 0; i--) { if (open[i].length >= shortestPath) { open.splice(i, 1); } } closest = getClosestPath(); } return closed[shortestIndex]; } public getAStarBestDirectionTo (otherRoom : RoomRandom, validRoom? : (room : RoomRandom) => boolean) { let path = this.getAStarPathTo(otherRoom, validRoom); if (path != undefined) { if (path.length == 1) { return undefined; } return this.connections.indexOf(path[1].room); } } public getConnectedDirection () { let shuffler = new Shuffler(Room.DIRECTIONS); for (let direction = shuffler.getOne(); direction != undefined; direction = shuffler.getOne()) { if (this.connections[direction] != undefined) { return direction; } } } // TODO: Return all things of type that are in placed RoomRandom. public static getActive (type : typeof Thing) { } } // random = new RoomRandom... // random.connectableOn = [Room.DIRECTION_NORTH, Room.DIRECTION_SOUTH...]