Javascript - Structure for a Reference Table

I'm not entirely sure if the wording in the title accurately describes what I want to do, let me explain:

Let's assume that in the game you get different types of points from leveling up. But the number of points of each type that you get at each level can be arbitrary.

For example, I get 3 offensive

for every 2 levels. But I get 2 defensive

at level 2, 4, 5 and 6. And maybe 1 supportive

indicates every level except the first one.


Now, here's what I did:

//Suppose I have my hero
var Sylin = new Hero();
Sylin.Level = 5;

//My goal is then to set
//    Sylin.OffensivePoint to 6 
//    Sylin.DefensivePoint to 6
//    Sylin.SupportivePoint to 4 

Sylin.prototype.OffensivePoint = function() {
    return 3*Math.floor(this.Level/2);
};

Sylin.prototype.DefensivePoint = function() {
    var defPoint = 0;
    for(var i=2; i <= this.Level; i++) {
        //maximum level being 6
        if(i==2 || i >= 4) {
            defPoint += 2;
        }
    }
    return defPoint;
};

Sylin.prototype.SupportivePoint = function() {
    return this.Level - 1;
};

      

Everything is fine and dandy, but if the max level is expanded the point lists will be updated and then it gets really awkward, especially if I have things like: 2 points every 3 levels, but 3 points on 9th and 13- m levels or something that is clearly missing from the template, so I can't always do it like what I have for OffensivePoint()

.


What I mean for this type of problem in general is there is a structure like this:

TotalPoint level
, 1. ,,,
2. ,, b
, 3. ,, c
, 4. ,, d
, 5. ,, e
and so on up to the maximum level

In code, I could then do:

Sylin.prototype.talentPoint = function() {
    return readTalentPointTable(this.Level); //?
};

      

But then it can still be confusing if there are 20 levels with 5 different types of points that you can get, say: /

...
,


EDIT


Ok, so I could do something like:

var OffensivePointTable = [0,0,2,2,4,6,8,8,8,10,12];
function getOffensivePoint(level) {
    return OffensivePointTable[level];
}

      

Would it be easier if I could store the data at the level at which the point is incremented, or at the grand total as above?

...
,


EDIT 2


Okay, can I possibly change the order of the struct to look at the type first, then the level?

var theTable = {
    o: [0,1,0,1,1,0,0,0],
    d: [0,0,2,0,2,0,2,0],
    s: [0,1,2,3,4,5,6,7]}

//then your CalculateStats:
Sylin.prototype.CalculateStats = function() {
    this.offensivePoint = 0;
    for(var i=1; i<= this.Level; i++) {
        this.offensivePoint += theTable[o][i];
    }
}

      

0


source to share


3 answers


You can use an object to store the number of points to increase in each table (I didn't use your exact numbers, but you get the idea):

var LevelPoints = {
  '1': {
      o: 1,
      d: 2,
      s: 1
  }
  '2': {
      o: 3,
      d: 1,
      s: 1
  }
  '3': {
      o: 1,
      d: 1,
      s: 0
  }
  '4': {
      o: 2,
      d: 3,
      s: 1
  }
  //etc.
}

      

For example, to access the level 2 attack point increase, use LevelPoints['2'].o

.

It takes a lot of input, I suppose, but sometimes just having all the data makes things easier. Making your code readable for you and easy to modify is always a pleasure. It's also useful as a quick reference - if you're wondering how many offensive points will be achieved at level 6, you'll know right away. No need to decode any procedural code. Of course, this is personal preference. Procedural approaches are faster and use less memory, so this is important to you. In this case, the difference will be negligible, so I recommend a data-driven approach.



Also note what I used var

to set this object. Because it can be used by all instances of the constructor Sylin

, setting it as an instance variable (using this

) is wasteful as it will create an object for each instance Sylin

. The use var

allows them to share it while preserving memory.

Alternatively, you can keep the running amount at each level, but for IMO it takes a lot of effort for no good reason. It takes less time to write the function:

Sylin.prototype.CalculateStats = function() {
    this.OffensivePoint = 0;
    this.DefensivePoint = 0;
    this.SupportivePoint = 0;

    for (var i = 1; i <= this.Level; i++) {
        this.OffensivePoint += LevelPoints[i].o;
        this.DefensivePoint += LevelPoints[i].d;
        this.SupportivePoint += LevelPoints[i].s;
    }
}

      

Then just run this function anytime the user changes the character level. There is no need to pass the level as the function already has access to the variable this.Level

.

+1


source


Why not store the points in an array of objects -

var pointsTable = [{offensivePionts: 1, defensivePoints: 1}, {offensivePoints: 1, defensivePoints: 2}]; //extend for any level

      

And then just get return points by referencing the corrent property -



function getOffensivePoints(level) {
    return pointsTable[level]['offensivePoints'];
}

      

You can easily extend the data structure with addLevel methods etc.

+1


source


Of course, you can always create an array of hard keys of all points, however you can also just code for exceptions and stick with the algorithm whenever you can.

Just an idea ... this would require your hero to keep track of his scores rather than recompiling them dynamically, but that's probably a good thing.

//have a map for exceptions
var pointExceptionsMap = {
    '9': {
        off: 3 //3 points of offense on level 9
    }
};

Sylin.prototype.levelUp = function () {
    var ex = pointExceptionsMap[++this.level];

    //update offense points (should be in another function)
    this.offense += (ex && typeof ex.off === 'number')? 
        ex.o /*exception points*/: 
        this.level % 2? 0 : 2; //2 points every 2 levels

};

      

Then, to level up, you make hero.levelUp()

and get points hero.offense

. I haven't tested anything but this idea. However, if you need to set the level directly, you can either have a function setLevel

that will call the levelUp

desired number of times, but you will have to use a modifier so that you can level up as well.

You can also use my current idea and find an efficient way to implement exceptional algorithms. For example, you could dynamically compile the number of points of violation and then add or remove points from that result based on exceptions. So if you need 2 points every 2 levels, but 3 for level 9, that means adding 1 extra point to the compiled points. However, since when you reach higher levels you want to keep this exception, you will also need to keep track of any added exclusion points.

EDIT: Also, there is nothing stopping you from using the function as a new exclusive algorithm instead of a simple number, and if you plan on tweaking the algorithms, you can simply allow users to override the defaults. For example, you might have a public function updateOffense

that encapsulates logic so that you can override it. It would be like a strategy design pattern .

EDIT2: Here's a complete example of what I was trying to explain, hope it helps!

var Hero = (function () {

    function Hero() {
        this.level = 0;
        this.stats = {
            off: 1,
            def: 0
        };
    }

    Hero.prototype = {
        statsExceptions: {
            '3': {
                off: 3 //get 3 points
            },
            '6': {
                def: function () {
                    //some algorithm, here we just return 4 def points
                    return 4;
                }
            }
        },
        levelUp: function () {
            ++this.level;
            updateStats.call(this, 1);

        },
        levelDown: function () {
            updateStats.call(this, -1);
            --this.level;
        },
        setLevel: function (level) {
            var levelFn = 'level' + (this.level < level? 'Up' : 'Down');

            while (this.level !== level) {
                this[levelFn]();
            }
        },

        statsFns: {
            off: function () {
                return (this.level % 2? 0 : 2);
            },
            def: function () {
                return 1;
            }
        }
    };

    function updateStats(modifier) {
        var stats = this.stats,
            fns = this.statsFns,
            exs = this.statsExceptions,
            level = this.level,
            k, ex, exType;

        for (k in stats) {
            if (stats.hasOwnProperty(k)) {
                ex = exs[level];
                ex = ex? ex[k] : void(0);
                exType = typeof ex;

                stats[k] += (exType === 'undefined'?
                    /*no exception*/
                    fns[k].call(this) :
                    /*exception*/
                    exType === 'function' ? ex.call(this) : ex) * modifier;
            }
        }
    }

    return Hero;
})();

var h = new Hero();

console.log(h.stats);

h.setLevel(6);

console.log(h.stats);

h.setLevel(0);

console.log(h.stats);

h.levelUp();

console.log(h.stats);

//create another type of Hero, with other rules
function ChuckNorris() {
    Hero.call(this); //call parent constructor
}

ChuckNorris.prototype = Object.create(Hero.prototype);

//Chuck gets 200 offense points per level y default
ChuckNorris.prototype.statsFns.off = function () {
    return 200;
};

//Not exceptions for him!
ChuckNorris.prototype.statsExceptions = {};

console.info('Chuck is coming!');

var c = new ChuckNorris();

c.setLevel(10);

console.log(c.stats);

      

+1


source







All Articles