Find Your Hat Challenge Project (JavaScript)

Hello world :slightly_smiling_face:
Can anyone please help me understand what this is method does? if you can detail a little over .join(’\n’); it would be great.
Any input would be greatly appreciated.
Best regards

print(){
const displayString = this.field.map(row => {
return row.join(’’);}).join(’\n’);
console.log(displayString);
}

Hey!

The join method takes the elements of an array and combines them together to form a string, separated by the delimiter passed in (by default it’s a comma).

For example:

["Hello", "World", "!"].join("")
 // HelloWorld!

["Hello", "World", "!"].join("\n")
// Hello\nWorld\n!
// Which when printed is:
// Hello
// World
// !

In your case, it’s combining each of the rows together separated by an empty string, then combining all the rows to print the overall board, each separated by a newline so each row displays on an individual line when printed.

Hopefully that helps!

Here’s my solution! Not clean at all, but it works! :slight_smile:

1 Like

Welcome to the forums and well done on completing it!

The only very minor thing I would suggest is not to use var when defining variables as it can have some unintended consequences when it comes to scope and has generally fallen out of practice compared to using let or const instead. This seems like a pretty good article that goes into some depth on why it’s discouraged.

Great job on it, congrats! :slight_smile:

1 Like

Hello Notlyall,
Many thanks for explaining my missing part .join(’\n’). I wish I understood it all… I am closer though … Sorry if I annoy you…
so join(’\n’) is adding a new line ? I found something about that on stackoverflow too.
Does this mean. “#####”. ? or is it just commencing a new line “#####” ?
“new line”
“#####”

1 Like

Hey!

So what .join("\n") is doing is using “\n” as the character it inserts while joining the strings so:

["I", "Am", "Not", "Lyall"].join("\n") // "I\nAm\nNot\nLyall

\n is a special character because it represents a newline, for example:

print("Line One\nLine Two")
// Outputs:
Line One
Line Two

In your case specifically, using .join("\n") is inserting a newline between each row, so you get this:
▓▓▓
▓▓▓
▓▓▓
Rather than this:
▓▓▓▓▓▓▓▓▓

Internally, the string would be represented as "▓▓▓\n▓▓▓\n▓▓▓"


Not at all! Here to help! :smile:

That nailed it! Thank you so much!

1 Like

Thank you for the useful link!
I used to write code in C# and I’m having difficulties in using var and let in an appropriate way!
I’m gonna read the article you suggested and try to understand a bit more!
:slight_smile:

1 Like

No problem! Here’s a good general rule for it that I use personally:

  1. Use const normally when creating a variable, as this prevents it being accidentally changed in a situation where you would not want it to be reassigned. This helps to cut down on bugs that may occur from accidentally changing the wrong variable/a variable being updated that shouldn’t etc.
  2. Use let when creating a variable that you know will have to be reassigned (i.e in a loop), as using const would prevent that.
  3. Steer clear of using var as it’s better to use the two newer keywords instead in almost all situations.

Happy coding! :slight_smile:

1 Like
const prompt = require('prompt');
const sPrompt = require('prompt-sync')();
//const events = require('events');
const colors = require("@colors/colors/safe");

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

class Field {
  constructor(field_arr){
    this.field = field_arr;
    this.x = 0;
    this.y = 0;
  }

  print() {
    process.stdout.write(this.field.join(", "))
  }

  printField() {
      console.log(this.field);
  }

  static generateField(height, width, percentage){

    var newField = [];
    let probHoles = Math.floor(percentage);

    for (let i=0; i<height; i++){
      var newArr = [];
      for (let j=0; j<width; j++ ){
        if (i === 0 && j === 0){
          newArr.push(pathCharacter);
          continue;
        }
        let probVal = Math.floor(Math.random()*100);
        if (probVal <= probHoles){
          newArr.push(hole);
        }
        else{
          newArr.push(fieldCharacter);
        }
      }
      newField.push(newArr);
    }

    let assignedHat = false;
    while (!assignedHat) {

    let i = Math.floor(Math.random()*height)
    let j = Math.floor(Math.random()*width)

    if (i === 0 && j === 0) { continue }
    else { 
      newField[i][j] = hat
      assignedHat = true; }
    }

    return newField;
  }

  move(direction) {
      
    const dimensions = [this.field.length, this.field[0].length];
    let resChar;

    function checkChar(char){
        if (char === '^'){ return 'hat' }
        else if (char === 'O') { return 'hole' }
        else { return 1 }
    }
    
    switch(direction) {
        case "up": {
            if (this.x === 0) { return false }
            this.x -= 1;

            resChar = checkChar(this.field[this.x][this.y])
            if (isNaN(resChar)){
                return resChar;
            } else{
                this.field[this.x][this.y] = pathCharacter;
                return true
            }
        }

        case "down": {
            if (this.x === dimensions[0]){ return false }
            this.x += 1;
            resChar = checkChar(this.field[this.x][this.y])
            if (isNaN(resChar)){
                return resChar;
            } else{
                this.field[this.x][this.y] = pathCharacter;
                return true
            }
        }

        case "right": {
            if (this.y === dimensions[1]){ return false }
            this.y += 1;
            resChar = checkChar(this.field[this.x][this.y])
            if (isNaN(resChar)){
                return resChar;
            } else{
                this.field[this.x][this.y] = pathCharacter;
                return true
            }
        }

        case "left": {
            if (this.y === 0){ return false }
            this.y -= 1;
            resChar = checkChar(this.field[this.x][this.y])
            if (isNaN(resChar)){
                return resChar;
            } else{
                this.field[this.x][this.y] = pathCharacter;
                return true
            }
        }
      }

  }

};


function startGame() {

    const properties = [ 
        { name: "length",
        description: colors.magenta("Enter length"),
        validator: /^[0-9]+$/,
        warning: 'Height must be a numerical positive value only',
        required: true
    }, {
        name: 'width',
        description: colors.magenta("Enter width"),
        validator: /^[0-9]+$/,
        warning: 'Width must be a numerical positive value only',
        required: true
    },{
        name: "percentage",
        description: colors.magenta("Enter percentage (optional)"),
        validator: /^[0-9]+$/,
        warning: 'Percentage must be a numerical positive value only',
        required: false
    }]    

    prompt.start();

    function onErr(err) {
        console.log(err);
        return 1;
    }

    prompt.get(properties, function(err, result) {

        if (err) { return onErr(err); }
        
        let fieldArr = Field.generateField(result.length, result.width, result.percentage);
        let myField = new Field(fieldArr);
        myField.printField()
        return playRound(myField)

    });
    
};

const playRound = (field) => {

    const outOfRangeMessage = ["You have walked out of the field! Whoops! Better luck next round!",
    "You have moved outside of the field! Try another round.",
    "You have moved outside of the field! Good luck on the next round!",
    "You have fallen outside of the field! Oops! Better luck next time!"];

    const winLoseMessage = ["Congratulations!! You have found your hat and won!",
    "Oh no, you have fallen into the hole and cannot escape. Try again next game!"];

    const directions = ['up', 'down', 'left', 'right'];

    let direction = sPrompt('In which direction would you like to move? ').toLowerCase();
        
    if (directions.includes(direction)){

        let res = field.move(direction);
        if (!res) {
            field.printField()
            console.log(colors.red(outOfRangeMessage[Math.floor(Math.random()*outOfRangeMessage.length)]))
        } else if (isNaN(res)){
            if (res === 'hat'){
                field.printField()
                console.log(colors.green(winLoseMessage[0]))
            } else {
                field.printField()
                console.log(colors.red(winLoseMessage[1]))
            }
        } else {
            field.printField()
            playRound(field);
        }
    
    } else{ 
        console.log('You can only move up, down, left or right.')
    }

};

const playGame = new Promise((resolve, reject) => {

    let welcomeMessage = `Welcome! The objective of this game is to find your hat, \'^\', 
    while traversing through the maze, starting from the upper left corner.\n
    There will be holes, \'O\', in the ground which you would need to avoid and a field, \'░\', 
    which you would need to stay within in order to not lose.\n
    Before we begin, what is the length and width of the grid you would like to play within.\n 
    As an optional third parameter, you can include a number from 0-100 for the percentage of holes 
    you\'d wish to see in the maze.`;

    if (welcomeMessage){
        resolve(console.log(welcomeMessage))
    } else {
        reject('Error starting the game :(')
    }
});

playGame.then( () => { startGame() 
}).catch( (message) => {console.log(message)})

My Code

Just a couple of notes, i’m not 100% sure how the Promise() works in my case, specifically how it’s able to tell when to playRound() and when the game is over, but it works well so i’m not complaining. I didn’t do the more advanced challenges for this project (like adding difficulty) but i’ll revisit as I progress through the course.

If anyone has any suggestions on improving my code, I will gladly take it! Thanks!

Hello All,
This was a nice exercise to work on.
A part of the process where I got stuck a little was when I decided to allow for the player to determine the size of the field. At first I tried creating a method within the class that takes the parameters and call generateField()- both from within it as well as by passing an object to it. But I kept getting an error saying the generateField() doesn’t exist. Eventually I realized that a field has to be instantiated first for that to work, and in the current set up I didn’t see a way for it to be possible. The moment I took my game setup method out of the class it all worked out.
The exercise turned out to be a well needed refresher on JS and object oriented programming that I didn’t get to practice for a while

I hope this is helpful to anyone.
My solution: GitHub - SimonSaban/find-your-hat
Thank you.

Hi guys!
Here’s my solution code for the basic requirements.

I needed to clear the screen so I found out I could use:

process.stdout.write('\x1Bc'); 

I’d appreciate it if someone could throw more light on why this clears the screen?

Just completed this project. I struggled with this one a lot so I figured I’ll post my solution for others struggling with this project. (I met all basic requirements, did not build a maze solver though. If anyone successfully created a maze solver, please forward me a link I really couldn’t figure it out on my own) Looking forward to all comments and questions

Project Solution:

from github

Ok,I let the link to the code in GitHub. After two days of challenge I can say that I finished. I feel really happy now after this challenge cause I can feel how much I´ve learned in this time.

I´ve seen that someone asked about how to do when the user is out-of-bounds. I used the object try…catch since the program throw an error because it can´t read a undefined data. So I catch the error and the program says to the user that he/she lost.

Find Your Hat

1 Like

Hi all,

Here is the link to my repo for this project. This was definitely a challenging one! Definitely feel free to provide feedback. :grinning:

This.

The lessons don’t prepare students for this project.

3 Likes

also this post has 83 likes so it seems there is a problem…

My Solution

Git Hub
I had a hard time with this one. It took some time to complete the basic requirements of the project. Sadly, I was not able to complete the bonus challenges because I am ready to keep progressing on my path. While this was really tough for me, I learned a lot through trial and error, and through reviewing other solutions and hints. Thanks for the challenge, Codecademy!