Select a random class from an array of classes in JavaScript

I have a collection of Classes:

possibleEnemies: [
  Slime,
  (currently only one available)
],

I am trying to randomly pick one of them and assign it to a variable like this (all classes are derived from the Enemy class):

this.enemy = new this.possibleEnemies[Math.floor(Math.random()*this.possibleEnemies.length)]()

Unfortunately, using this method ^ results in an error stating that it is not a constructor:

[Vue warn]: Error in mounted hook: "TypeError: this.possibleEnemies[Math.floor(...)] is not a constructor"

I have searched extensively online for solutions but couldn't find any. Can someone please assist me?

Enemy.js

export default class Enemy {
    minGold;
    maxGold;
    minMaxHealth;
    maxMaxHealth;

    maxHealth;
    health;

    attackSpeed;
    damage;

    constructor(mig, mag, mimah, mamah, mah, h, ats, dam){
        this.minGold = mig;
        this.maxGold = mag;
        this.minMaxHealth = mimah;
        this.maxMaxHealth = mamah;
        this.maxHealth = mah;
        this.health = h;
        this.attackSpeed = ats;
        this.damage = dam;
    }
}

all.js (includes all enemy types)

import {default as Enemy} from "./Enemy.js";

export default class Slime extends Enemy {
    constructor() {
        super(
            1,
            3,
            3,
            6,
            6,
            6,
            7,
            8
        );
    }
}

Answer №1

It seems like you forgot to include the length call on the array:

...Math.random()*this.possibleEnemies.length)]

Note: The answer has been adjusted to fit the updated question :)

Here is an example demonstrating the random creation of an object using vanilla Javascript. Make sure to verify your Vue-specific imports and other components.

class Slime {
    constructor() {
        console.log('Creating Slime');
    }
    printName() {
        console.log('Slime');
    }
}
class Abc {
    constructor() {
        console.log('Creating Abc');
    }
    printName() {
        console.log('Abc');
    }
}
class Xyz {
    constructor() {
        console.log('Creating Xyz');
    }
    printName() {
        console.log('Xyz');
    }
}
possibleEnemies = [Slime, Abc, Xyz];
let enemy = new possibleEnemies[Math.floor(Math.random() * possibleEnemies.length)]();
console.log("enemy: " + enemy);
enemy.printName();

Answer №2

Here's a revision of your code structure. In order to ensure that the minimum and maximum health values for a certain type of enemy are not tied to each instance, I have organized them under an independent mapping called enemyConfigs. The base constructor of the Enemy class now handles the randomization of stats for each enemy upon instantiation. An interesting aspect is that the configurations for each specific enemy class are only partial; any undefined values are inherited from the default enemy configuration.

(Sadly, there seems to be no TypeScript-friendly way to override statics in a hierarchical inheritance system; otherwise, I would have chosen that approach instead of creating individual mappings per enemy type.)

Moreover, the process of spawning enemies based on a list of classes functions seamlessly . . .

type MinMax = [number, number];

interface EnemyConfig {
  gold: MinMax;
  health: MinMax;
  attackSpeed: MinMax;
  damage: MinMax;
}

const baseEnemyConfig: EnemyConfig = {
  gold: [0, 0],
  health: [1, 1],
  attackSpeed: [0, 0],
  damage: [0, 0],
};

function initializeNumber([min, max]: MinMax): number {
  return min + Math.floor(Math.random() * (max - min));
}

class Enemy {
  public gold: number;
  public health: number;
  public maxHealth: number;
  public attackSpeed: number;
  public damage: number;

  constructor(config?: Partial<EnemyConfig>) {
    const fullConfig = { ...baseEnemyConfig, ...config };
    this.gold = initializeNumber(fullConfig.gold);
    this.health = this.maxHealth = initializeNumber(fullConfig.health);
    this.attackSpeed = initializeNumber(fullConfig.attackSpeed);
    this.damage = initializeNumber(fullConfig.damage);
  }

  public makeNoise(): string {
    return "...";
  }
}

const enemyConfigs: Record<string, Partial<EnemyConfig>> = {
  Goblin: {
    gold: [1, 3],
    health: [3, 6],
    // ... etc
  },
  Titan: {
    gold: [200, 300],
    health: [100, 300],
  },
};

class Goblin extends Enemy {
  constructor() {
    super(enemyConfigs.Goblin);
  }

  public makeNoise() {
    return "Gob!";
  }
}
class Titan extends Enemy {
  constructor() {
    super(enemyConfigs.Titan);
  }

  public makeNoise() {
    return "Titan toot!";
  }
}

const enemyClasses = [Goblin, Titan];

function spawnEnemies(n: number): Array<Enemy> {
  const enemies = [];
  for (let i = 0; i < n; i++) {
    const cls =
      enemyClasses[Math.floor(Math.random() * enemyClasses.length)];
    const enemy = new cls();
    enemies.push(enemy);
  }
  return enemies;
}

const enemies = spawnEnemies(10);

enemies.forEach((enemy) => {
  console.log(enemy.makeNoise(), enemy.health, enemy.gold);
});

The output will look similar to:

[LOG]: "Gob!",  3,  2 
[LOG]: "Titan toot!",  208,  246 
[LOG]: "Gob!",  5,  1 
[LOG]: "Titan toot!",  206,  254 
[LOG]: "Gob!",  3,  1 
[LOG]: "Titan toot!",  210,  262 
[LOG]: "Titan toot!",  168,  229 
[LOG]: "Gob!",  5,  2 
[LOG]: "Gob!",  5,  2 
[LOG]: "Titan toot!",  207,  236 

You can experiment with this code on the TypeScript playground.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for retrying an insertion into the database when a duplicate unique value is already present

After thorough searching, I couldn't find any existing patterns. My goal is to store a unique key value in my MySQL database. I generate it on the server side using this code: var pc = require('password-creator'); var key = pc.create(20); ...

Achieving full page height with div, iframe, and footer components

Feeling a bit stuck with this problem and hoping for some help. I did some searching on SO but couldn't find a solution that fits my needs. Below is a snippet of the markup I'm working with: <div id="wrapper"> <div id="content"> ...

What is the best way to align two HTML elements in a single column?

I need to adjust the layout of an existing <td> in order to display a checkbox and one additional field on the same line. The current setup places these elements on separate lines. Below is the provided HTML code: <!DOCTYPE html> <html> ...

Contrast between action="" and action="#" attributes in HTML

There are two ways I've come across for setting a form's action attribute. #1. Using an empty string as the action attribute value: action="" #2. Specifying a hash symbol (#) as the action attribute value: action="#" What distingishes these ...

Having issues with utilizing $fetchState in Nuxt 2.12

Recently, I've been exploring the new functionality outlined in the documentation. However, I'm encountering an error that states : Property or method "$fetchState" is not defined on the instance but referenced during render. Despite clearly ...

Next js is repeatedly calling a Firestore document in multiple instances during the fetching process

In my Next js 13 project, I am facing an issue while fetching a single document with an id from Firebase. Instead of returning just one read (which is expected since I'm fetching a single doc), it is returning multiple reads, sometimes ranging from 2 ...

"Enhance your website with a sleek Bootstrap navigation system featuring dividers and

Need help with creating a Bootstrap nav menu similar to this design made in Photoshop? Wondering about the best way to position the logo and create dividers between menu items like shown in this image. Here's the HTML code currently being used: & ...

Is it possible for Javascript to handle a string of 27601 characters in length?

I have created a webmethod that returns an object containing strings. Each string in the object is of length 27601. This pattern continues for all array members - str(0) to str(n). When my webmethod returns this exact object, I encounter an issue on the c ...

Issue displaying content in a two-column setup on Chrome and Mozilla browsers due to ASP problem

I've encountered an issue with a footer appearing incorrectly in Chrome and Mozilla browsers when using a 2-column layout, although it displays fine in IE8. Currently, I'm coding in asp with css includes to render the footer. Here's the CSS ...

Having trouble retrieving information from the JSON data received from the Google Place Search API

I'm encountering an issue with accessing data from the Google Place Search API. I've provided my code below for reference. getData = (keyword, location, country) => { let dataURI = `${URI}${keyword}+${location}+${country}${API}`; var ...

An error was encountered stating "TypeError: Unable to call function on undefined object while attempting to utilize a JSON object

My current setup involves using D3js with MongoDB and AngularJS to showcase my data. Everything works smoothly until I decide to give my JSON array a name. Suddenly, Angular starts throwing errors at me and I'm left confused as to why. Here is the or ...

Guide to invoking a mixin function within middleware

Is there a way to invoke the mixin function within middleware using NUXT.js? This is what I have attempted: export default function(context) { // initialize auth token from local storage or cookies context.initAuth(context.req) if (!context.store. ...

Unable to display results in React Native due to FlatList not being shown

I'm a beginner to React Native and I'm attempting to create a simple flatlist populated from an API at , but unfortunately, no results are displaying. Here's my App.tsx code: import React from 'react'; import type {PropsWithChildre ...

NextJS middleware API receives an uploaded image file form, but the request is undefined

Currently, I'm utilizing NextJS to handle form data processing and database uploads, with a pit stop at the NextJS API middleware for image editing. pages/uploadImage.tsx This is the client-side code handler. ... async function handleImageUpload(imag ...

Several jquery libraries are experiencing malfunctions

<head> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready(function () { $(".slidingDiv").hide(); $(".show_hide").show().click ...

Assigning values to template variables in Express 4's routes/index

Recently, I started using node.js and express. To set up express 4, I used the command "express myAppName" in the terminal, which created a default directory with Jade templates as default. The main file, app.js, has the following standard express boilerp ...

I find the SetInterval loop to be quite perplexing

HTML <div id="backspace" ng-click="deleteString(''); decrementCursor();"> JS <script> $scope.deleteString = function() { if($scope.cursorPosVal > 0){ //$scope.name = $scope.name - letter; ...

What could be causing my footer to be stuck in the center of my webpage?

Despite trying various methods, my footer remains stuck in the middle of the page. I even attempted to set the body height to 100% or 100vh without success. footer placement issue (https://i.stack.imgur.com/qzp2i.png) css: body { display: flex; flex-direct ...

Visual Studio Code is not properly highlighting imports for Vue 3 using the Composition API

Upon careful examination, it is evident that the import is not highlighted despite the utilization of the component SearchBar. https://i.stack.imgur.com/G4sAm.png Various attempts have been made to rectify this issue including the installation of the Vet ...

Error: Attempting to access the property 'push' of an undefined variable has resulted in an unhandled TypeError

if (Math.random() <= .1) { let orgAdmin = User.find({email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1234454665324721380c0d">[email protected]</a>'}); or ...