Let's build a little virtual pet simulator, in the style of those 1990s keychain pets. Every pet gets hungry, bored and tired over time. You have to look after them!
class Pet {
#hunger = 50; // 0 = full, 100 = starving
#happiness = 50; // 0 = miserable, 100 = ecstatic
static count = 0;
constructor(name, species) {
this.name = name;
this.species = species;
this.age = 0;
Pet.count++;
}
// keep values between 0 and 100
static #clamp(value) {
return Math.max(0, Math.min(100, value));
}
get mood() {
if (this.#hunger > 80) return "π« starving";
if (this.#happiness < 20) return "π’ sad";
if (this.#happiness > 80) return "π€© ecstatic";
return "π okay";
}
feed() {
this.#hunger = Pet.#clamp(this.#hunger - 30);
return `${this.name} munches happily. π`;
}
play() {
this.#happiness = Pet.#clamp(this.#happiness + 25);
this.#hunger = Pet.#clamp(this.#hunger + 10);
return `${this.name} plays! ${this.sound()}`;
}
timePasses() {
this.age++;
this.#hunger = Pet.#clamp(this.#hunger + 20);
this.#happiness = Pet.#clamp(this.#happiness - 15);
}
sound() {
return "(makes a noise)";
}
status() {
return `${this.name} the ${this.species} | age ${this.age} | hunger ${this.#hunger} | happiness ${this.#happiness} | ${this.mood}`;
}
}
class Dog extends Pet {
constructor(name) {
super(name, "dog");
}
sound() {
return "Woof woof! πΆ";
}
fetch() {
return `${this.name} fetches the ball! ${this.play()}`;
}
}
class Cat extends Pet {
constructor(name) {
super(name, "cat");
}
sound() {
return "Meow. π±";
}
play() {
// cats only play if they feel like it
return this.age % 2 === 0 ? super.play() : `${this.name} ignores you. πΌ`;
}
}
const pets = [new Dog("Bingo"), new Cat("Whiskers")];
console.log(`π You have ${Pet.count} pets.\n`);
for (let day = 1; day <= 3; day++) {
console.log(`--- Day ${day} ---`);
for (const pet of pets) {
pet.timePasses();
if (day === 2) console.log(pet.feed());
console.log(pet instanceof Dog ? pet.fetch() : pet.play());
console.log(pet.status());
}
}
Output
π You have 2 pets. --- Day 1 --- Bingo fetches the ball! Bingo plays! Woof woof! πΆ Bingo the dog | age 1 | hunger 80 | happiness 60 | π okay Whiskers ignores you. πΌ Whiskers the cat | age 1 | hunger 70 | happiness 35 | π okay --- Day 2 --- Bingo munches happily. π Bingo fetches the ball! Bingo plays! Woof woof! πΆ Bingo the dog | age 2 | hunger 80 | happiness 70 | π okay Whiskers munches happily. π Whiskers plays! Meow. π± Whiskers the cat | age 2 | hunger 70 | happiness 45 | π okay --- Day 3 --- Bingo fetches the ball! Bingo plays! Woof woof! πΆ Bingo the dog | age 3 | hunger 100 | happiness 80 | π« starving Whiskers ignores you. πΌ Whiskers the cat | age 3 | hunger 90 | happiness 30 | π« starving
Poor things! We forgot to feed them on day 3, and now they're starving. (Being a pet owner is hard. So is being a programmer.)
Look at all the OOP ideas working together:
- Private fields (
#hunger,#happiness) keep the stats safe from outside meddling. You can only change them throughfeed,playandtimePasses. - A private static method (
#clamp) is a helper that only the class can use. - A static property (
Pet.count) counts every pet ever created. - A getter (
mood) calculates a mood from the stats. - Inheritance:
DogandCatboth extendPet. They overridesound(), so the sharedplay()method makes the right noise for each animal. Catoverridesplay()and callssuper.play()only on even-numbered days. Dogs get an extrafetch()method.
- Add a
sleep()method toPetthat reduces hunger a little and increases happiness a little. Use it on day 3. - Create a
Parrotclass with its ownsound()and a specialtalk(words)method that repeats whatever you tell it. - Make the pets die (or, more kindly, "run away to Grandma's farm") if hunger reaches 100. Add an
isGonegetter and skip those pets in the loop.
Sophomore year done! You can now design programs out of classes and objects, the same way programmers do in Java, Python, C# and many other languages. And this holds no more mysteries for you. (Well, fewer mysteries. this is always a little mysterious.)
In Junior year, we'll learn to work like professional developers: splitting code into modules, running JavaScript with Node.js, and installing other people's code with npm.