Find Your Hat Challenge Project (JavaScript)

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

This is my solution:

It includes user-configurable grid size, hole chance, and how often to add new holes (“hard mode”).

Field.findOpenSpot() could be optimized by maintaining an array of open spots on the grid and picking from that. The current implementation just picks a random location and loops until it finds one that isn’t occupied. This could potentially take a very long time if the field is mostly full and will infinite loop if the field is completely full, but I want to get on with the next lesson.

P-P-P-Private Prinny reporting! I have an emergency communication from M-M-M-Master Etna, dood!

"Ok, so what in all the netherworlds is going on with this challenge? My Prinny squad have been exceptionally attentive to me recently and it’s creeping me out! They have told me that they would much rather suffer their typical chores than be forced to attempt this challenge!!

They report that while they have been working through the Create Video Games with Phaser.js course that this challenge quest appeared out of nowhere while they were learning what classes were. It uses some sort of application that they have never encountered before, and even after having viewed the solution they say that what relation it even has to these classes is entirely unclear as they are barely used!

I don’t know what MY Prinnies are up to trying to learn this stuff, but this challenge has caused significant defects to them and I demand some sort of explanation as to what this challenge has to do with helping them learn, whatever it is they are learning!

Yours sincerely,

Beauty Queen Etna <3

PS. The only thing I approve of my Prinnies doing regarding this is checking out the answers to the problem."

S-s-s-so, there you have it! Please help us figure out just what we are supposed to be doing here, or Master Etna may adopt this challenge as a new method of punishment!

Hi Private Prinny, thanks for your report!

Please take this message back to your Master Etna:
"This ‘Find Your Hat’ Challenge Project is quite different from the usual fetch side quests that you may have ordered your Prinny squad members to do!

Instead of our usual practice Projects that tell you what code to write and where to write it, this project, a Challenge Project, does not! Instead it gives you the premise with broad/general instructions to guide you through the completion. As you can see, for a Prinny, this might be too big of an ask. It would require someone of at least your stature to take up and finish since it takes much longer to complete and more brain power to get the job done right!

This particular challenge project is also different from the JS/Phaser material because it’ll make use of a bash terminal and node to run your JavaScript code (rather than directly outputting in the terminal for you or have a browser interpret your code). But rest assured it’s still all JavaScript!

I also think that your Prinnies did well in looking over the solutions. These solutions can definitely help if you’re ever in a pinch/stuck, or if you want to improve your ‘code literacy’. If you’re up for the task and you want to practice using JavaScript in more general programming, try taking up this side quest for yourself! Otherwise, it’s not needed to continue on your Phaser main quest."

Hope that helps, safe travels Prinny!