Skip to content

Instantly share code, notes, and snippets.

@codecademydev
Created September 8, 2020 15:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save codecademydev/105cd66655703e2d9ad7d80e08096cef to your computer and use it in GitHub Desktop.
Save codecademydev/105cd66655703e2d9ad7d80e08096cef to your computer and use it in GitHub Desktop.
Codecademy export
const prompt = require('prompt-sync')({sigint: true});
//The following is a solution found on stack overflow for comparing arrays, and not made by me. Everything else is mine.
// Warn if overriding existing method
if(Array.prototype.equals)
console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code.");
// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
};
// Hide method from for-in loops
Object.defineProperty(Array.prototype, "equals", {enumerable: false});
const hat = '^';
const hole = 'O';
const emptyFieldCharacter = '░';
const pathCharacter = '*';
//My solution to Find Your Hat:
class Field {
constructor() {
this._field = [];
this._initialPosition = {
i:0,
j:0
};
this._hatPosition = {
i:0,
j:0
};
this._bestSolutionField = [];
}
get field() {
return this._field;
}
get initialPosition() {
return this._initialPosition;
}
get hatPosition() {
return this._hatPosition;
}
findInitialPosition(field) {
field.forEach((row, rowIndex) => {
const indexOfAsteriskInRow = row.indexOf('*');
if (indexOfAsteriskInRow !== -1) {
this._initialPosition.j = indexOfAsteriskInRow;
this._initialPosition.i = rowIndex;
};
});
}
findHatPosition(field) {
field.forEach((row, rowIndex) => {
const indexOfHatInRow = row.indexOf('^');
if (indexOfHatInRow !== -1) {
this._hatPosition.j = indexOfHatInRow;
this._hatPosition.i = rowIndex;
};
});
}
get bestSolutionField() {
return this._bestSolutionField;
}
generateField(heigth, width, percentage) {
const slotsToFill =
heigth * width;
const fillWithHoles =
slotsToFill*percentage/100;
const fillWithEmpties =
slotsToFill - fillWithHoles -2;
const arrayToShuffle = [];
for (let i=0; i < slotsToFill; i++) {
i < fillWithHoles ?
arrayToShuffle.push(hole) :
i < fillWithHoles + fillWithEmpties ?
arrayToShuffle.push(emptyFieldCharacter) :
i < slotsToFill -1 ?
arrayToShuffle.push(hat) :
arrayToShuffle.push(pathCharacter);
}
const shuffledArray = [];
for (let j= arrayToShuffle.length -1; j > 0; j--) {
const randomI =
Math.floor(Math.random()*arrayToShuffle.length);
const lastElement = arrayToShuffle[j];
arrayToShuffle[j] = arrayToShuffle[randomI];
arrayToShuffle[randomI] = lastElement;
};
const formatedArray = [];
let l = 0;
for (let k=0; k < heigth; k++) {
const newRow = arrayToShuffle.slice(l, l + width);
l += width;
formatedArray.push(newRow);
};
this.findInitialPosition(formatedArray);
this.findHatPosition(formatedArray);
return this.isFieldValid(formatedArray) ?
this._field = formatedArray :
this.generateField(heigth, width, percentage);
}
isFieldValid(field) {
this.findHatPosition(field);
const testField = field.map(row => row.map(spot => spot));
const bestSolution = [];
const bestSolutionField = field.map(row => row.map(spot => spot));
const openSet = [];
const closedSet = [];
let initialSpot = {};
let hatSpot = {};
testField.forEach((row, iIndex, array) => {
row.forEach((spot, jIndex) => {
array[iIndex][jIndex] = new Spot(iIndex, jIndex, spot, this.hatPosition);
if (spot === `*`) {
initialSpot = array[iIndex][jIndex];
} else if (spot === `^`) {
hatSpot = array[iIndex][jIndex];
};
});
});
testField.forEach((row, iIndex, array) => {
row.forEach((spot, jIndex) => {
spot.findNeighbours(array);
});
});
openSet.push(initialSpot);
function removeFromArray(array, element) {
for (let i= array.length-1; i>=0; i--) {
if(array[i] === element) {
array.splice(i, 1);
};
};
};
while (openSet.length > 0) {
let indexShorterWay = 0;
for(let index = 0; index < openSet.length; index++) {
const candidateSpot = openSet[index];
const currentPathSpot = openSet[indexShorterWay];
const candidateHeuristic = candidateSpot.guessTotalDistanceCost();
const currentPathHeuristic = currentPathSpot.guessTotalDistanceCost();
if(candidateHeuristic < currentPathHeuristic) {
indexShorterWay = index;
};
const currentSpot = openSet[indexShorterWay];
const neighbours = currentSpot.neighbours;
if(currentSpot === hatSpot) {
let pushToSolution = currentSpot;
while (pushToSolution.previous) {
pushToSolution = pushToSolution.previous;
bestSolution.push(pushToSolution);
};
bestSolution.forEach((pathSpot) => {
bestSolutionField.forEach((row, rowIndex) => {
if(rowIndex === pathSpot.i) {
row.forEach((spot, spotIndex) => {
if(spotIndex === pathSpot.j) {
row.splice(spotIndex, 1, pathCharacter);
};
});
};
});
});
this._bestSolutionField = bestSolutionField;
return true;
}
removeFromArray(openSet, currentSpot);
closedSet.push(currentSpot);
neighbours.forEach((neighbour) => {
if (!closedSet.includes(neighbour)) {
const possibleNeighbourDistance = currentSpot.distanceRun +1;
if(openSet.includes(neighbour)) {
if(possibleNeighbourDistance < neighbour.distanceRun) {
neighbour.distanceRun = possibleNeighbourDistance;
};
} else {
neighbour.distanceRun = possibleNeighbourDistance;
neighbour.previous = currentSpot;
openSet.push(neighbour);
};
};
});
};
};
if(openSet.length <= 0) {
return false;
};
}
}
class Spot {
constructor(i, j, element, hatPosition) {
this.i = i;
this.j = j;
this.element = element;
this.distanceRun = 0;
this.xDistanceToHat = hatPosition.i - this.i;
this.yDistanceToHat = hatPosition.j - this.j;
this.distanceToHat = Math.sqrt(this.xDistanceToHat**2 + this.yDistanceToHat**2);
this.guessedTotalDistanceCost = this.guessTotalDistanceCost();
this.previous = undefined;
this.neighbours = [];
}
guessTotalDistanceCost() {
this.guessedTotalDistanceCost = this.distanceRun + this.distanceToHat;
return this.guessedTotalDistanceCost;
}
findNeighbours(field) {
const i = this.i;
const j = this.j;
if(field[i+1] && field[i+1][j] && field[i+1][j].element === emptyFieldCharacter
|| field[i+1] && field[i+1][j] && field[i+1][j].element === hat) {
this.neighbours.push(field[i+1][j]);
};
if(field[i-1] && field[i-1][j] && field[i-1][j].element === emptyFieldCharacter
|| field[i-1] && field[i-1][j] && field[i-1][j].element === hat) {
this.neighbours.push(field[i -1][j]);
};
if(field[i] && field[i][j+1] && field[i][j+1].element === emptyFieldCharacter
|| field[i] && field[i][j+1] && field[i][j+1].element === hat) {
this.neighbours.push(field[i][j +1]);
};
if(field[i] && field[i][j-1] && field[i][j-1].element === emptyFieldCharacter
|| field[i] && field[i][j-1] && field[i][j-1].element === hat) {
this.neighbours.push(field[i][j -1]);
};
}
}
class Game {
constructor() {
this._fieldObject = new Field();
this._currentPosition = {
i:0,
j:0
};
}
get fieldObject() {
return this._fieldObject;
}
get currentPosition() {
return this._currentPosition;
}
play() {
const enteredHeigth = Number(prompt(`Enter field heigth:`));
const enteredWidth = Number(prompt(`Enter field width:`));
const holePercentage = Number(prompt(`Enter percentage of holes:`));
this.fieldObject.generateField(enteredHeigth, enteredWidth, holePercentage);
this._currentPosition = this._fieldObject._initialPosition;
this.print();
this.move();
}
print() {
this.fieldObject.field.forEach(row => console.log(row.join(``)));
}
get initialPosition() {
return this.fieldObject.initialPosition;
}
askNextMove() {
let direction = prompt('Which way would you like to go?');
direction = direction.toLowerCase();
return direction;
}
move() {
const moves = {
up: 'w',
down:'s',
left:'a',
right:'d'
};
const direction = this.askNextMove();
const targetPosition = {
i: 0,
j: 0
}
if (direction === moves.up) {
targetPosition.i = this.currentPosition.i -1;
targetPosition.j = this.currentPosition.j
} else if (direction === moves.down) {
targetPosition.i = this.currentPosition.i +1;
targetPosition.j = this.currentPosition.j;
} else if (direction === moves.left) {
targetPosition.i = this.currentPosition.i;
targetPosition.j = this.currentPosition.j -1;
} else if (direction === moves.right) {
targetPosition.i = this.currentPosition.i;
targetPosition.j = this.currentPosition.j +1;
} else {
console.log("Invalid move: please use 'a' for left, 'w' for up, 's' for down and 'd' for right.");
this.move();
};
if (!this.fieldObject.field[targetPosition.i] || !this.fieldObject.field[targetPosition.i][targetPosition.j]) {
console.log('Oh no! You fell of the field! GAME OVER!')
this.willYouReplay();
return;
};
const target = this.fieldObject.field[targetPosition.i][targetPosition.j];
if (target === hole) {
console.log('Oh no! You fell in a hole! GAME OVER!')
this.willYouReplay();
return;
};
if (target === hat) {
console.log('Congratulations! You found your hat!');
if (this.fieldObject.field.equals(this.fieldObject.bestSolutionField)) {
console.log('Additionally, you took the shortest possible path! You must be really smart :)');
};
this.willYouReplay();
return;
};
if (target === emptyFieldCharacter) {
this._fieldObject._field[targetPosition.i].splice(targetPosition.j, 1, pathCharacter);
// this._field[this._currentPosition.i].splice(this._currentPosition.j, 1, emptyFieldCharacter);
this._currentPosition = targetPosition;
this.print();
this.move();
};
}
willYouReplay() {
let restart = prompt("Start Again? Type 'y' to start over or 'n' to exit.");
restart = restart.toLowerCase();
restart === 'y' ? this.play() :
restart === 'n' ? console.log(`Bye! Thanks for playing!`) :
willYouReplay();
}
}
const game = new Game();
game.play();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment