Find Your Hat Challenge Project (JavaScript)

Congratulations on completing your project!

Compare your project to our solution code and share your project below! Your solution might not look exactly like ours, and that’s okay! The most important thing right now is to get your code working as it should (you can always refactor more later). There are multiple ways to complete these projects and you should exercise your creative abilities in doing so.

This is a safe space for you to ask questions about any sample solution code and share your work with others! Simply reply to this thread to get the conversation started. Feedback is a vital component in getting better with coding and all ability levels are welcome here, so don’t be shy!

About community guidelines: This is a supportive and kind community of people learning and developing their skills. All comments here are expected to keep to our community guidelines


How do I share my own solutions?

  • If you completed the project off-platform, you can upload your project to your own GitHub and share the public link on the relevant project topic.
  • If you completed the project in the Codecademy learning environment, use the share code link at the bottom of your code editor to create a gist, and then share that link here.

Do I really need to get set up on GitHub?
Yes! Both of these sharing methods require you to get set up on GitHub, and trust us, it’s worth your time. Here’s why:

  1. Once you have your project in GitHub, you’ll be able to share proof of your work with potential employers, and link out to it on your CV.
  2. It’s a great opportunity to get your feet wet using a development tool that tech workers use on the job, every day.

Not sure how to get started? We’ve got you covered - read this article for the easiest way to get set up on GitHub.

Best practices for asking questions about the sample solution

  • Be specific! Reference exact line numbers and syntax so others are able to identify the area of the code you have questions about.
5 Likes

So where is the page we find said Gist link? Please post the landing page URL.

Hi - This is one of the challenge projects available as part of our Pro curriculum, so there is not a landing page, but you can see it here.

1 Like

Dear Codecademy,

First of all I want to thank you for your program. Its a great way to learn programming and web development.

But this time I have to say some words:

  1. First of all the Part 10 BROWSER COMPATIBILITY AND TRANSPILATION in javascript introduction is not well explained as in other parts.
    While doing it I had impression to copy and past without understand well like in other lessons.

  2. This project is far more difficult than all projects that I have done. (And I have done all challenges that gave me the path until this project).

  3. Even if I downloaded the solution I still don’t understand how it works…

Is there any way that some of your experimented developper makes a video to explain step by step how to build this game please ?

I think this project is really important and I don’t want to skip it. I managed to do it until step 3.
Than after step 3 its quit hard to understand how to start where to start what to do…

I think this project would be more helpful for people if there are more explanations and steps or a video that build it.

Thank you in advance.

180 Likes

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);
1 Like

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);
2 Likes

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.

15 Likes

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?

3 Likes

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.

1 Like

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.

7 Likes

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.

5 Likes

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.

3 Likes

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

3 Likes

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

1 Like