Roll the dice calculator 'Vampire: The Masquerade' Style

Hi, everyone!

Have you ever played “Vampire: The Masquerade”? I did in my younger years. Trying to come up with an exercise that made me use most of what I’ve learned so far in the JavaScript course, I thought of writing a piece of code that could help players of that game to resolve ‘moves’ which involved rolling dices.

For those of you who don’t know the game, in order to overcome certain challenges which are met along the story that the player partakes in, that player must roll a particular amount of ten-sided dices against a specific difficulty (which is set by the narrator of the game) to check if the ‘move’ is successful or not.

I’ve written this that follows, and I believe it works kinda good enough for the explained purpose. Now, as always, any input will be so, so welcome :slight_smile:

// Roll the dice calculator 'Vampire: The Masquerade' style

let value;
let result = [];
let totalSuccess = [];

const rollDices = (dices, difficulty) => {

    function getValue () {
    value = Math.floor(Math.random() * 9)
    console.log(value); // Returns a random number among 0 and 9 and stores it as 'value'
    return value;
}

for (let i = 0; i < dices; i++) {
    result.push(getValue());
    console.log(result) // Runs 'getValue()' as many times as dices have been determined thru the parameters of the function 'rollDices' and pushes each one of the results into the 'result' array.  
}; 

result.forEach(number => {
    let success = 0;
    if (number === 1) {
        success--;
        success--;
    } else if (number === 0) {
        success++;
        success++;
    } else if (number >= difficulty) {
        success = 1;
    } else {
        success = 0;
    }
    console.log(success);
/*
A specific score is set according to the result of every 'dice' rolled:
     » 1 means a terrible failure, so it subtracts 2 points to the final 'success' score;
     » 0 means 10, which is an awesome success (thus, it adds up 2 point to the final 'success' score);
     » If the result of the 'dice' is equal or greater than the difficulty set, it is considered a 'simple' success (thus, it adds up 1 point to the final 'success' score);
     » Other than that, it means nothing (no changes to the final success score)  
*/
    return totalSuccess.push(success)
});

console.log(totalSuccess); // An array is created with the values associated to the different possibilities of success or failure as indicated formerly. Now we're going to 'reduce' that array to a single value that will finally indicate the success degree of the player's 'move'.

const accumulatedSuccess = totalSuccess.reduce((accumulator, currentValue) => {  
    return accumulator + currentValue;
});
   
let successDegree;

if (accumulatedSuccess < 0) {
    successDegree = 'Awful! Everything goes opposite of what it was expected!';
    } else if (accumulatedSuccess === 0) {
        successDegree = "You didn't make it!";
    } else if (accumulatedSuccess === 1 ) {
        successDegree = 'You barely made it... but yet, you did!';
    } else if (accumulatedSuccess === 2) {
        successDegree = 'Not too bad, but you could have done so much better!';
    } else if (accumulatedSuccess === 3) {
        successDegree = 'Kinda impressive... you got it quite right!';
    } else if (accumulatedSuccess === 4) {
        successDegree = 'Wow. Almost excellent!';
    } else if (accumulatedSuccess >= 5) {
        successDegree = 'Boom! Critical damage!';
    }
    return successDegree;
}

console.log(rollDices(2,9));

So that’s that. What do you think?

Cheers!

1 Like

Without having really gone through all of it, this stands out:

value will be assigned to one of 9 possible integer values: 0, 1, 2, 3, 4, 5, 6, 7, 8.
If you’re rolling a 10-sided die, 9 should also be possible to roll.

1 Like

Oh, right! Silly me! :sweat: Fixing it right away… Thanks!

1 Like

Okay, I’ve had time to check it out more thoroughly. Nice job. Consider what the final result is, and other ways you could get there. I’m including how I might accomplish the same thing using map() and reduce() chained together. Check it out if you like. Happy coding!

Code:
// Roll the dice calculator 'Vampire: The Masquerade' style

// let value;
// let result = [];
// let totalSuccess = [];

const rollDices = (dices, difficulty) => {

//     function getValue () {
//     value = Math.floor(Math.random() * 10)
//     console.log(value); // Returns a random number among 0 and 9 and stores it as 'value'
//     return value;
// }

// for (let i = 0; i < dices; i++) {
//     result.push(getValue());
//     console.log(result) // Runs 'getValue()' as many times as dices have been determined thru the parameters of the function 'rollDices' and pushes each one of the results into the 'result' array.  
// }; 

// result.forEach(number => {
//     let success = 0;
//     if (number === 1) {
//         success--;
//         success--;
//     } else if (number === 0) {
//         success++;
//         success++;
//     } else if (number >= difficulty) {
//         success = 1;
//     } else {
//         success = 0;
//     }
//     console.log(success);
// /*
// A specific score is set according to the result of every 'dice' rolled:
//     » 1 means a terrible failure, so it subtracts 2 points to the final 'success' score;
//     » 0 means 10, which is an awesome success (thus, it adds up 2 point to the final 'success' score);
//     » If the result of the 'dice' is equal or greater than the difficulty set, it is considered a 'simple' success (thus, it adds up 1 point to the final 'success' score);
//     » Other than that, it means nothing (no changes to the final success score)  
// */
//     return totalSuccess.push(success)
// });

// console.log(totalSuccess); // An array is created with the values associated to the different possibilities of success or failure as indicated formerly. Now we're going to 'reduce' that array to a single value that will finally indicate the success degree of the player's 'move'.

// const accumulatedSuccess = totalSuccess.reduce((accumulator, currentValue) => {  
//     return accumulator + currentValue;
// });

//Assign individual die values to an array
const rolls = [];
for (let n = 0; n < dices; n++) {
  rolls.push(Math.floor(Math.random() * 10)) 
}

// calculate accumulated success
const accumulatedSuccess = rolls.map(roll => 
  roll === 1 ?
    -2 :
  roll === 0 ?
    2 :
  roll >= difficulty ?
    1 :
  0).reduce((a, cVal) => a + cVal);
  
// could be all on one line:
// const accumulatedSuccess = rolls.map(roll => roll === 1 ? -2 : roll === 0 ? 2 : roll >= difficulty ? 1 : 0).reduce((a, cVal) => a + cVal);
console.log(`rolls: ${rolls}\naccumulatedSuccess: ${accumulatedSuccess}`);

   
let successDegree;

if (accumulatedSuccess < 0) {
    successDegree = 'Awful! Everything goes opposite of what it was expected!';
    } else if (accumulatedSuccess === 0) {
        successDegree = "You didn't make it!";
    } else if (accumulatedSuccess === 1 ) {
        successDegree = 'You barely made it... but yet, you did!';
    } else if (accumulatedSuccess === 2) {
        successDegree = 'Not too bad, but you could have done so much better!';
    } else if (accumulatedSuccess === 3) {
        successDegree = 'Kinda impressive... you got it quite right!';
    } else if (accumulatedSuccess === 4) {
        successDegree = 'Wow. Almost excellent!';
    //if it isn't less than 0 or 0-4, 5 or higher is all that remains possible, so no need for if...else
    } else { 
        successDegree = 'Boom! Critical damage!';
    }
    return successDegree;
}

console.log(rollDices(2,9));

Couldn’t help myself. Here’s another approach (similar, but more concise).

Code:
// Roll the dice calculator 'Vampire: The Masquerade' style

const rollDice = (dice, diff) => {
  const results = {
    6: 'Awful! Everything goes opposite of what it was expected!',
    0: 'You didn\'t make it!',
    1: 'You barely made it... but yet, you did!',
    2: 'Not too bad, but you could have done so much better!',
    3: 'Kinda impressive... you got it quite right!',
    4: 'Wow. Almost excellent!',
    5: 'Boom! Critical damage!'
  }
  const val = Array(dice).fill(1).map(r => r * Math.floor(Math.random() * 10)).map(v => v === 1 ? -2 : v === 0 ? 2 : v <= diff ? 1 : 0).reduce((a, b) => a + b);
  return `You scored ${val}. ${results[val < 0 ? 6 : val > 4 ? 5 : val]}`;
}

console.log(rollDice(2, 9));
console.log(rollDice(5, 7));
console.log(rollDice(4, 8));

Output(s):

You scored 2. Not too bad, but you could have done so much better!
You scored 4. Wow. Almost excellent!
You scored 1. You barely made it… but yet, you did!

You scored 2. Not too bad, but you could have done so much better!
You scored 3. Kinda impressive… you got it quite right!
You scored 0. You didn’t make it!

You scored 2. Not too bad, but you could have done so much better!
You scored 6. Boom! Critical damage!
You scored -1. Awful! Everything goes opposite of what it was expected!

Okay. Last edit. This is less concise, but more efficient because it doesn’t create any arrays.

Code:
// Roll the dice calculator 'Vampire: The Masquerade' style

const rollDice = (numDice, diff) => {
  console.log(`You're rolling ${numDice} dice. The difficulty is ${diff}. Good luck!`);
  const results = {
    6: 'Awful! Everything goes opposite of what it was expected!',
    0: 'You didn\'t make it!',
    1: 'You barely made it... but yet, you did!',
    2: 'Not too bad, but you could have done so much better!',
    3: 'Kinda impressive... you got it quite right!',
    4: 'Wow. Almost excellent!',
    5: 'Boom! Critical damage!'
  }
  const val = rD(numDice, diff);
  return `You scored ${val}. ${results[val < 0 ? 6 : val > 4 ? 5 : val]}`;
}

const rD = (n, d) => {
  let val = 0;
  for(let c = 0; c < n; c++) {
    const roll = Math.floor(Math.random() * 10);
    const score = roll === 0 ? 2 : roll === 1 ? -2 : roll >= d ? 1 : 0;
    console.log(`Roll #${c + 1}: ${roll}  score: ${score}`);
    val += score;
  }
  return val;
}

console.log(rollDice(2, 9) + "\n");
console.log(rollDice(5, 7) + "\n");
console.log(rollDice(4, 8) + "\n");

Output:

You’re rolling 2 dice. The difficulty is 9. Good luck!
Roll #1: 6 score: 0
Roll #2: 8 score: 0
You scored 0. You didn’t make it!

You’re rolling 5 dice. The difficulty is 7. Good luck!
Roll #1: 4 score: 0
Roll #2: 8 score: 1
Roll #3: 9 score: 1
Roll #4: 7 score: 1
Roll #5: 7 score: 1
You scored 4. Wow. Almost excellent!

You’re rolling 4 dice. The difficulty is 8. Good luck!
Roll #1: 1 score: -2
Roll #2: 1 score: -2
Roll #3: 2 score: 0
Roll #4: 4 score: 0
You scored -4. Awful! Everything goes opposite of what it was expected!

2 Likes

Wow. So awesome. Best of it all, I believe I’ve finally understood ternary operators thru your snippets :slight_smile:

About…

That’s made wonder a lot. For example, how’s concision and efficiency defined? And, among them, which one is preferable and why? Finally, is it there a reason why I should try to avoid arrays?

Thanks a lot!

Cheers!

2 Likes

I would define concise code as having fewer lines. Efficiency would be how much of the computer’s overall resources are needed to run our code (memory, number of total instructions to execute, etc.). I would opt for efficiency over conciseness.

You don’t need to avoid arrays. If we were to expand this project to the point where you wanted to be able to recall previous dice rolls from earlier turns, an array might be a good candidate for storing those values. I try to avoid creating them if I don’t need them, but frequently it’s fun to try out solutions to problems in every conceivable way just to see what’s possible. The final ‘best approach’ could very well be a combination of a few different methods. (I also like to try to condense every function into a one-liner. Rarely is this the most efficient method, but it forces me to see problems from different angles.)

You could research Big O notation to get an idea of how to calculate efficiency of code. There’s nothing wrong with arrays especially in a program like this. When we create an array, and then have to iterate over it a few times, and/or create new arrays from the values in the first array, we use up more resources on our machines. In my second solution, for example, I created an array, called map() on it which returns a new array, called map() on that array which returns another new array and then called reduce() on that array to produce a single value. In a small project, it really is no big deal. I just like to keep refactoring code until I come up with what seems as efficient as possible. It’s probably not there yet, but going back, and refactoring my old projects as I learn more is part of the fun. :slightly_smiling_face:

2 Likes