1

I have the variable G.playerStatsDifference defined as an array of objects:

playerStatsDifference: [{
        carpenter: 0,
        wood: 0,
        gunman: 0,
        gunpowder: 0,
        merchant: 0,
        gold: 0,
        fleet: 0,
        flagship: 0,
    }, {
        carpenter: 0,
        wood: 0,
        gunman: 0,
        gunpowder: 0,
        merchant: 0,
        gold: 0,
        fleet: 0,
        flagship: 0,
    }]

The point of this variable is to calculate the difference between G.playerStats which frequently changes.

My function to calculate the difference is:

const oldPlayerStats = JSON.parse(JSON.stringify(G.playerStats));
statsDifference(G, oldPlayerStats);  

for (let p = 0; p < 2; p++) { 
    for (let s = 0; s < 8; s++) { 
        Object.values(G.playerStatsDifference[p])[s] = Object.values(G.playerStats[p])[s] - Object.values(oldPlayerStats[p])[s];
    }    
}

The expected output would be to have playerStatsDifference

When running some tests I did some console logging and it gave me the correct calculations, but the G.playerStatsDiffence would not update.

Here is some of that testing, with the calulations being correct:

console.log("Current wood is " + Object.values(G.playerStats[0])[1]); //Current wood is 5
console.log("Old wood is " + Object.values(oldPlayerStats[0])[1]); //Old wood is 10
console.log(Object.values(G.playerStats[0])[1] - Object.values(oldPlayerStats[0])[1]); //-5

I thought maybe I was doing something wrong with the loops so I tried the following afterwards:

Object.values(G.playerStatsDifference[0])[1] = Object.values(G.playerStats[0])[1] - Object.values(oldPlayerStats[0])[1];

However this did not work either. Having said that, the following does work:

G.playerStatsDifference[0].wood = Object.values(G.playerStats[0])[1] - Object.values(oldPlayerStats[0])[1];

So it seems like I have some issue with the Object.values on G.playerStatsDifference. Any idea on why that is and how I can run that through the loop?

=====

EDIT: As those in the comments have pointed out my question is a bit confusing so I will try to clear it up here..

The G.playerStatsDifference value is supposed to track the difference between the previous value of G.playerStats and the current value of G.playerStats.

To do this I am setting the value of oldPlayerStats to equal G.playerStats and then updating G.playerStats to its new value.

I then need to run through the array of objects and subtract the value of G.playerStats from oldPlayerStats. This will produce the value of G.playerStatsDifference

That is what the loop is for, to go through each object key and do the calculation.

Hope this provides some clarity. Sorry for the poorly worded question.

10
  • Object.values is a static method of the root Object class. You can't assign to it. It also makes no sense to call it with the result of a math operation like subtraction. Commented Nov 20, 2022 at 15:22
  • So playerStatsDifference grows in size with every new playerStats announced? Commented Nov 20, 2022 at 15:22
  • @AndrewParks No, the values get replaced/updated. Commented Nov 20, 2022 at 15:22
  • 2
    @evilgenious448 I mean no offense when I say this (we all start somewhere, and you don't get better by not trying) but your approach is so broken I can't really understand what you're trying to accomplish. Maybe edit the question to include more background? Commented Nov 20, 2022 at 15:25
  • 1
    @JaredSmith All good, I will edit the question to try to make it more clear. No offence taken, appreciate you being polite with your response :) Commented Nov 20, 2022 at 15:26

2 Answers 2

1
const diffBetweenObjectValues = (a, b) => {
  return Object.entries(a).reduce((result, [aKey, aVal]) => {
    result[aKey] = aVal - (b[aKey] ?? 0);
    return result;
  }, {});
}

const stats = { a: 1, b: 2 };
const updatedStats = { a: 1, b: 1 };

// Initial player stats are { a: 1, b: 2 }
const player = { stats: stats, diff: {} };

// Set the diff, value is { a: 0, b: 1 }
player.diff = diffBetweenObjectValues(player.stats, updatedStats);

// Actually update the stats, value is { a: 1, b: 1 }
player.stats = updatedStats;

Note that if a key is present in b but not a it's ignored. Also note that this only works properly if all the property values are numeric.

You can put the state transition in a function and just run it when you need to update the stats (like every tick of the game loop).

Response to comment

Ok, lets add another helper function

const zip = (a, b) => a.map((x, i) => [x, b[i]]);

const players = [...]; // array of players
const statUpdates = [...];   // array of stat updates
zip(players, statUpdates).forEach(([player, stats]) => {
  player.diff = diffBetweenObjectValues(player.stats, stats);
  player.stats = stats;
});

Zip combines the array of players and the array of stat updates in to pairs, then iterate over them with forEach, destructure the bits back out, and run the update. You can also just use a for loop, which is faster but harder to read and easier to get wrong (e.g. off-by-one errors). I would stick with the version until/unless your profiler tells you it's too slow.

Update 2

const currentStats = [{ a: 1, b: 2 }, {a: 3, b: 2 }];
const updatedStats = [{ a: 0, b: 1 }, {a: 4, b: 1 }];
const diffedStats = zip(currentStats, updatedStats).map(([current, updated]) => {
  return diffBetweenObjectValues(current, updated);
});
Sign up to request clarification or add additional context in comments.

9 Comments

Hey Jared, just trying to wrap my head around this answer. Also, what would be right way to go about having the stats be an array with 2 objects, where stats[0] is the stats for the first player and stats[1] is the stats for the second player
@evilgenious448 see my edit to the answer.
Hey @Jared Smith, sorry for the delay with my reply, have been trying to integrate your solution into my code. The values for stats and updatedStats would be coming through as: const stats = [{ a: 1, b: 2 }, { a: 1, b: 2 }]; const updatedStats = [{ a: 1, b: 1 }, { a: 1, b: 1 }]; If I am correct in my understanding, your solution does not quite work with the values as 2 objects in an array.
@evilgenious448 Generally it is mistake to structure you code that way except in AAA gamedev in C++ and not even always there. In object-oriented code the object should own their state, player stats should be properties of player objects, not an array of separate entities linked up by position in the array. I think part of the reason people are having a hard time grokking your question is that basically no experienced developer would design it that way (again, no offense intended).
Just want to add one final comment that @Jared Smith is definitely someone StackOverflow needs more people to be like. Went above and beyond trying to help me, even when I was probably biting of a bit more than I could chew, he was patient and polite and helped me get to where I need to be. Wish I could give him all 500 of my points, he deserves them.
|
1

// for testing purposes, create an object with some random stats
const randomPlayerStats = () => Object.fromEntries(
  ['carpenter','wood','gunman','gunpowder','merchant','gold','fleet','flagship']
    .map(k=>[k,Math.random()*10|0]));

// array of the last player stats recorded for each player
let lastPlayerStats = [];

// create a new object from the existing object, subtracting each entry
// from the old object from the entry from the new object
// note: uses the ?? operator so that if there is no last object yet,
// the last object value will be treated as being zero
const difference = (playerStats, lastPlayerStats) => {
  let r = Object.fromEntries(Object.entries(playerStats).map(([k,v])=>
    [k, v-(lastPlayerStats?.[k]??0)]));
  lastPlayerStats = playerStats;
  return r;
};

// simulate 5 rounds of the game, with 2 players in the game
const playerCount = 2;
const simulatedRounds = 5;
for(let c=0;c<simulatedRounds;c++) {
  let playerStats = [...Array(playerCount).keys()].map(i=>randomPlayerStats());
  let playerStatsDifference = playerStats.map((s,i)=>
    difference(s, lastPlayerStats[i]??{}));
  console.log('playerStats:');
  console.log(playerStats);
  console.log('playerStatsDifference:');
  console.log(playerStatsDifference);
}

5 Comments

Hey @Andrew Parks, are you able to edit the answer to output the difference in the same structure as the stats. So { "carpenter": 4, "wood": 0, "gunman": 6, "gunpowder": 8, "merchant": 0, "gold": 8, "fleet": 5, "flagship": 5 }
@evilgenious448 oops, fixed
@AndrewParks you really need to add some explanation to this wall of code. It's not really useful in its current state.
@evilgenious448 I finally understand why playerStatsDifference had multiple entries - it's a multiplayer game. My answer now works for a multiplayer game
Hi @Andrew Parks, sorry I should have explained that it is a multiplayer game with 2 players. Thank you for editing your answer. It seems to work exactly how I want it. But your code is a bit new to me, so I would appreciate it if you could put some comments and saw what certain parts of the code do.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.