Find Your Hat Challenge Project (JavaScript)

Can anyone tell me how to know whether the hat is in bound or not ?

When you define the hat location, you can restrict its location to always be in bounds by using the field width and field height as your multiplier.

Something like:

const hatLocation = {
x: Math.floor(Math.random() * fieldW),
y: Math.floor(Math.random() * fieldH)
}

Where fieldW is the width of the field and fieldH is the height of the field.
If you want to run a check on the hat location you can use something similar to what you used to check if the player’s location is in bounds in the main Field class.

const hatInBounds = (hatLocation.x >= 0 && hatLocation.y >= 0 && hatLocation.x < fieldW && hatLocation.y < fieldH);

This could even be abstracted away to a helper function…

const inRange = (n, r) => 0 <= n && n < r;
let hatInBounds = inRange(hatLocation.x, fieldW) && inRange(hatLocation.y, fieldH);

Hello everybody,

Checkout my version… The code is maybe not perfect, but i inplemented levels and a scoreboard…

Byee.

1 Like

I agree that the directions are very hard to understand. I didn’t quite understand what I should do until I started, and then I thought, “ohhh, that’s what the directions meant!”.

Basically, when main.js is initiated it does these 3 things:

  1. create an array with Field.generateField()

  2. stores the array from 1) in an object with const var = new Field(array)

  3. console.log the starting field with .print()

At this point, what the player sees is the starting field, while 1-3 was done “behind the scenes” by the program. The next steps are a loop requiring player interaction. Basically the program:

  1. asks for input

  2. takes the input and do stuff

  3. calls myHatGame.print() to print out the updated field

  4. repeat 4-6 until the player wins or loses.

  5. program ends

I spent most of my time making the two functions, generateField() and print(), work properly. You’ll know they work properly when generateField() makes something like this:

[’*’, ‘░’, ‘O’],
[‘░’, ‘O’, ‘░’],
[‘░’, ‘^’, ‘░’],

And print() somehow turns that into

*░O
░O░
░^░

Once I got these two done, the rest kinda finished itself. The loop took a bit of trial and error, but it’s surprisingly simple once I got the hang of it! It’s step 1-3 that really made me my pull my hair out :sob: It’s totally doable though, so don’t be surprised if steps 1-3 took a couple of hours. It’s where you’ll spend 80% of the project time.

Hi guys,

This is my solution of this project:

You can adjust the field size and hole percentage by editing parameters on line 135.

Codecademy suggest to add a maze-solver function, that seems very hard!! Do anyone has finished this task?

Not too bad of a project. The maze validation took a few hours of troubleshooting. Unfortunately it seems like an unintended consequence is that most of the random fields that pass validation are very easy to traverses.

Could I have some help/feedback on my code please? I am really struggling with step 5; I can’t get the code to print an array with the characters.

This is my code:

const hat = '^';
const hole = 'O';
const fieldCharacter = '░';
const pathCharacter = '*';


class Field {
  constructor(field) {
      this._field = field;
      this._hatLocationHorizontal = field[0];
      this._hatLocationVertical = field[0];
   }
   print() {
     for (let i= 0; i < 3; i++)
  console.log(this._field[i].join(""));
   }  

static generateField(fieldW, fieldH, percentage) {
var gameArray = new Array(fieldW * fieldH); 
const hatStartLocation = () => {
[hat] = Math.floor(Math.random() * fieldW + fieldH);
if (hat === field[0][0]) {
hatStartLocation();
}
 gameArray.push([hat]);

};
const holesLocation = () => {
[hole] = Math.floor(Math.random() * percentage);
gameArray.push([hole]);
}
return gameArray;
   }

};

class NewField extends Field {
    constructor(field) {
        super(field);
    }
};

//Method to get user input

const readline = require("readline");
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});


rl.question("Which way would you like to move next?", function(move) {
  console.log(`Move player ${move}`);
  rl.close();
});

rl.on("close", function() {
  process.exit(0);
});


//Method to update the pathCharacter 

const moveUser = (move) => {
if (move === "down" && pathCharacter === fieldCharacter) {
 return readline;
} else if (move === "down" && pathCharacter === hole || pathCharacter > fieldH || pathCharacter > fieldW ) {
  return console.log("Game Over! Play again?");
} else if (move === "up" && pathCharacter === fieldCharacter) {
 return readline;
} else if (move === "up" && pathCharacter === hole || pathCharacter > fieldH || pathCharacter > fieldW ) {
  return console.log("Game Over! Play again?");
} else if (move === "left" && pathCharacter === fieldCharacter) {
 return nextMove;
} else if (move === "left" && pathCharacter === hole || pathCharacter > fieldH || pathCharacter > fieldW ) {
  return console.log("Game Over! Play again?");
} else if (move === "right" && pathCharacter === fieldCharacter) {
 return readline;
} else if (userInput === "right" && pathCharacter === hole || pathCharacter > fieldH || pathCharacter > fieldW ) {
  return console.log("Game Over! Play again?");
} else {
  return console.log("Congratulations, you won!");
}
};



console.log(Field.generateField(5, 5, .10));

Prints:

[,,,,,,,,,,,,,,,,,,,,,,,,,]

It is hard to avoid impossible games.
So I didn’t…

const prompt = require('prompt-sync')({ sigint: true });

const hat = '^';
const hole = 'O';
const fieldCharacter = '░';
const pathCharacter = '*';

class Field {
  constructor(height, width, percentage = 25) {
    this._height = height;
    this._width = width;
    this._percentage = percentage;
    this._field = Field.generateField(height, width, percentage);
  }

  print() {
    for (let row of this._field) {
      console.log(row.join(" "))
    }
  }

  play() {
    let posY = 0;
    let posX = 0;
    let userMove;
    let allowedMoves = this.allowedMoves(posY, posX);
    while (!allowedMoves) {
      this._field = Field.generateField(this._height, this._width, this._percentage);
    }
    while (this._field[posY][posX] !== hat && this._field[posY][posX] !== hole) {
      console.clear();
      this._field[posY][posX] = pathCharacter;
      this.print()
      allowedMoves = this.allowedMoves(posY, posX);
      if (!allowedMoves) {
        console.log("You can't move anymore. Maybe there was no path?")
      }
      userMove = prompt("Where do you want to go? Type U for Up, R for Right, D for Down, L for Left: ");
      while (!allowedMoves.includes(userMove.toUpperCase())) {
        userMove = prompt("The move you entered is invalid or forbidden. Please, try again: ");
      }
      switch (userMove.toUpperCase()) {
        case "U":
          posY -= 1;
          break;
        case "R":
          posX += 1;
          break;
        case "D":
          posY += 1;
          break;
        case "L":
          posX -= 1;
          break;
      }
    }
    if (this._field[posY][posX] === hat) {
      console.log("Yeah! You found your hat!")
    } else if (this._field[posY][posX] === hole) {
      console.log("Did you just fall in a hole?")
    }
  }

  allowedMoves(posY, posX) {
    const allowedMoves = [];
    // Up:
    if (posY && this._field[posY - 1][posX] !== pathCharacter) { allowedMoves.push("U"); }
    // Right:
    if (posX < this._field[0].length - 1 && this._field[posY][posX + 1] !== pathCharacter) { allowedMoves.push("R"); }
    // Down :
    if (posY < this._field.length - 1 && this._field[posY + 1][posX] !== pathCharacter) { allowedMoves.push("D"); }
    // Left:
    if (posX && this._field[posY][posX - 1] !== pathCharacter) { allowedMoves.push("L"); }
    return allowedMoves;
  }

  static generateField(height, width, percentage = 25) {
    const field = [] // Creating the field.
    if (percentage > 50) {
      percentage = 35;
      console.log("The percentage of holes, being too high, has been set to 35%")
    }
    let numberOfHoles = Math.floor(height * width * percentage / 100) // Defining the number of holes.
    // The position of the hat is generated randomly. It can't be [0][0].
    let hatY = 0;
    let hatX = 0;
    while (!hatX && !hatY) {
      hatY = Math.floor(Math.random() * height)
      hatX = Math.floor(Math.random() * width)
    }
    for (let row = 0; row < height; row++) {
      const rowArray = []
      for (let column = 0; column < width; column++) {
        // The position [0][0] is the starting point.
        if (row === 0 & column === 0) {
          rowArray.push(pathCharacter);
          continue;
        }
        // The hat is added if the position corresponds to its.
        if (row === hatY && column === hatX) {
          rowArray.push(hat);
          continue;
        }
        // Every other position is filled with a field character.
        rowArray.push(fieldCharacter);
      }
      field.push(rowArray);
    }
    for (numberOfHoles; numberOfHoles > 0; numberOfHoles--) {
      let holeY = 0;
      let holeX = 0;
      while (field[holeY][holeX] !== fieldCharacter) {
        holeY = Math.floor(Math.random() * height);
        holeX = Math.floor(Math.random() * width);
      }
      field[holeY][holeX] = hole;
    }
    return field;
  }
}

playGround = new Field(15, 25);
console.log(playGround.play())

I tried to use different kind of things:
while
for
switch
Etc…

I’ve spent 7 hrs (according to WakaTime extension) on this and I don’t really feel accomplished on finally getting it to work in a way that i wanted…

It’s not even pretty, no maze validation, all in all feeling totally disheartened.

Any idea why this method I put in is giving me a syntax error?

checkWin() {
        if (this._field[this.locationY][this.locationX] === hole) {
            console.log('Oops! You lost by falling into the hole!');
            return 'lose';
        }
        esle if (this._field[this.locationY][this.locationX] === hat) {
            console.log('Congratulations! You found the hat!');
            return 'win';
        }
        else {
            return 'cont';
        }
    }

Easy to miss that tiny typo.

1 Like

This took significantly longer then 2h to complete but i learnt a lot whilst doing it. I think it’s a significant jump from the JavaScript course but recommend anyone to attempt and insist on it.

The hardest bit for me was the static generateField method. Creating a method for custom 2D array was surprisingly challenging.

Features:

  1. Random start location

  2. Choice of 10 search algorithms to help you find the hat if possible at all
    AStarFinder,BestFirstFinder,BreadthFirstFinder,DijkstraFinder,IDAStarFinder,
    JumpPointFinder,BiAStarFinder,BiBestFirstFinder,BiBreadthFirstFinder,BiDijkstraFinder

This also satisfies the ‘field validator’ extended criteria

  1. Colorized hat,path,field,hole and help route

  2. Hard mode : holes added on up or down only; easy mode : no holdes added

  3. Variable grid size

Daryle Singh

This was a looong challenge!

It creates a random grid with random start location. Width, height and percentage of grid being holes can be set up. Easy mode - no added holes. Hard mode - can set up level of difficulty, the higher the level, the more likely a new hole added after a move. Before it lets a player start the game, it checks if the grid can be solved at easy level and if not, it forces creation of a new grid… I used Las Vegas algorithm that returns false if the solution is not found within permitted time (time being a function of squared grid size).

Feedback welcome.

Happy coding!

P

Wouldn’t be me if I didn’t pick on something trivial…

    let hatPosition = Math.floor(Math.random() * primeArrLength);
    if (hatPosition === startPosition) {
        do {
            hatPosition = Math.floor(Math.random() * primeArrLength);
        } while (hatPosition === startPosition);
    }

refactored…

  let hatPosition;
  do {
    hatPosition = Math.floor(Math.random() * primeArrLength);
  } while (hatPosition === startPosition);
1 Like

Thank you! I think I’ve put do while inside if several times, working now on correcting it all.

1 Like

Here is my solution:

this my solution

https://gist.github.com/78cf5d3acc9eabda48475052937ef102[Find tour hat]
Esta fue mi respuesta al proyecto; si pueden verla y analizar algún error en la sintaxis, se los agradecería