掷骰子🎲获取点数问题

看过《赌神》系列电影的小伙伴知道,主角在赌桌上随机掷骰子,想要几点就要几点,这种技能可谓是神乎其技,令我们大饱眼福的同时也甚是羡慕,好想在现实生活中直接展示一番。

笔者虽然做不到这样的"神技",但是在之前开发中遇到了这样的需求,当下冥思苦想总算找到了掷点数的"奥秘"。

掷一个骰子的情况: 一个骰子结果点数只能在[1,6]的范围中选择。我投掷一个骰子,骰子结果比1小不行,比6大同样不行。注意:这里是不允许破坏骰子的。
掷两个骰子的情况: 两个骰子结果点数是在[2,12]之间选择。我现在想掷7点,两个骰子点数"互补" ,假设我第一次掷了6点,剩下的骰子点数就是1点;假设我第一次掷了5点,剩下的骰子点数只能是2点;假设我第一次掷了4点,剩下的骰子点数只能是3点...假设我第一次掷了1点,剩下的骰子数就得是6点了。
掷三个骰子的情况: 三个骰子结果点数是在[3,18]之间选择。我现在想掷10点,那么当第一个骰子为1点时,剩下两骰子就得分9点,如果当前第一个骰子是6点时,剩下两骰子就得分4点。每个骰子必须保证有值,最少是1点,最多6点。

总结以上规律,就会发现:

  1. 掷骰子必须保证有值。不管掷几点,骰子肯定有值,是1~6的随机值。

  2. 骰子点数是有范围的。一个骰子至少需要1点,最多也只能达到6点,一旦超出6点(小于1点)都认定投掷失败。

  3. 掷骰子次数不变,但剩余骰子要想得到预期结果的机会实际是逐步减少的。随着投掷骰子的次数增多,后序的骰子需要尽可能采取方式来"凑齐"最终的结果,否则本轮掷骰子是不成功的。

  4. 骰子之间展示的结果是相互影响的。诚然投掷骰子可以"贪一点",前期直接从最大点掷,那么剩下的点数可能需要往小点数掷,但有时候可能会出现0点/负数的存在,就需要重新投掷;如果前期掷骰子掷了最小点,那么剩下的投掷就可能需要往大点数掷,但这时候又意味着出现超过6点的情况。

投掷骰子的步骤

第一步: 实现投掷点数的随机值,每次投掷骰子的结果是有一个范围的,依据范围获取随机值。

js 复制代码
/**
 * 获取最大最小值之间的随机值
 * @param {Number} -min 最小值
 * @param {Number} -max 最大值
*/
function getRandom(min,max) {
  return Math.floor(Math.random(min,max) * (max -min + 1)  + min);
}

第二步: 既然已经获取到了每次的随机值,那么就可先确定好函数的大部分逻辑.

js 复制代码
/**
 * 根据点数和获取骰子点数的方法
 * @param {Number} -sum 投掷骰子的总和
 * @param {Number} -num 投掷骰子的个数 默认为3个
*/
function getPoints(sum, num = 3) {
  // 骰子总和不能小于骰子数  或者 骰子总和不能超过骰子数*6点
  if(sum < num || sum > num * 6) {
    throw new Error('投掷结果有误');
    return 0;
  }
  // 当骰子投掷结果刚好都是1点 无须计算
  if(sum === num * 1) {
    return new Array(num).fill(1);
  }
   // 当骰子投掷结果刚好都是6点 无须计算
  if(sum === num * 6) {
    return new Array(num).fill(6);
  }
  // 变量result用于收集可能投掷的骰子结果
  let result = [];
  // 进行掷骰子的操作。。。
  return result;
}

第三步: 确认掷骰子的范围 ---核心步骤

根据上述现象,当骰子只有一个,范围是[1,6],骰子投掷结果有着自身的限制

当骰子有两个时,为了保证每个骰子都有结果,第一个骰子投掷时是不是就得保证第二个骰子至少有一点(保证了另一个骰子的下限)。而当第一个骰子投掷如果是1点,一旦点数和(sum)超过7点,第二个骰子的点数必定会超出6点(同时需要保证另一个骰子的上限)。两个骰子投掷的结果不仅受投掷的点数和(sum)的影响,两个骰子的点数还相互之间有影响。

当骰子有三个时,也符合此现象,影响的范围更广。

所以如果在投掷骰子之前就判定好当前还有几次投掷的机会(剩下的投掷骰子数),并做出本轮投掷的点数限制,就可以尽可能保证本次投掷不会影响到其他投掷骰子。

js 复制代码
/**
 * 根据点数和获取每次骰子结果的最大最小值
 * @param {Number} -sum 投掷骰子的总和
 * @param {Number} -lastNum 剩下投掷的骰子数
*/
function getMaxAndMin(sum, lastNum) {
  const max =  sum - lastNum * 1 > 6 ? 6: sum - lastNum * 1;
  const min =  sum - lastNum * 6 < 1 ? 1: sum - lastNum * 6;
  return {
    max,
    min
  }
}

第四步: 完善掷骰子操作

js 复制代码
/**
 * 根据点数和获取骰子点数的方法
 * @param {Number} -sum 投掷骰子的总和
 * @param {Number} -num 投掷骰子的个数 默认为3个
*/
function getPoints(sum, num = 3) {
  // 骰子总和不能小于骰子数  或者 骰子总和不能超过骰子数*6点
  if(sum < num || sum > num * 6) {
    throw new Error('投掷结果有误');
    return 0;
  }
  // 当骰子投掷结果刚好都是1点
  if(sum === num * 1) {
    return new Array(num).fill(1);
  }
   // 当骰子投掷结果刚好都是6点
  if(sum === num * 6) {
    return new Array(num).fill(6);
  }
  // 变量result用于收集可能投掷的骰子结果
  let result = [];
  let tmp = sum;
  while(num > 1) {
    num--;
    // 获取最大最小值
    const {min, max} = getMaxAndMin(tmp, num);
    // 随机出本轮的值
    const value = getRandom(min,max);
    // 放入结果
    result.push(value);
    // 点数和减少进入下次判断
    tmp -= value;
  }
  // 将剩余值放入
  result.push(tmp);
  return result;
}

完整代码展示

js 复制代码
/**
 * 获取最大最小值之间的随机值
 * @param {Number} -min 最小值
 * @param {Number} -max 最大值
*/
function getRandom(min,max) {
  return Math.floor(Math.random(min,max) * (max -min + 1)  + min);
}

/**
 * 根据点数和获取每次骰子结果的最大最小值
 * @param {Number} -sum 投掷骰子的总和
 * @param {Number} -lastNum 剩下投掷的骰子数
*/
function getMaxAndMin(sum, lastNum) {
  const max =  sum - lastNum * 1 > 6 ? 6: sum - lastNum * 1;
  const min =  sum - lastNum * 6 < 1 ? 1: sum - lastNum * 6;
  return {
    max,
    min
  }
}

/**
 * 根据点数和获取骰子点数的方法
 * @param {Number} -sum 投掷骰子的总和
 * @param {Number} -num 投掷骰子的个数 默认为3个
*/
function getPoints(sum, num = 3) {
  // 骰子总和不能小于骰子数  或者 骰子总和不能超过骰子数*6点
  if(sum < num || sum > num * 6) {
    throw new Error('投掷结果有误');
    return 0;
  }
  // 当骰子投掷结果刚好都是1点
  if(sum === num * 1) {
    return new Array(num).fill(1);
  }
   // 当骰子投掷结果刚好都是6点
  if(sum === num * 6) {
    return new Array(num).fill(6);
  }
  // 变量result用于收集可能投掷的骰子结果
  let result = [];
  let tmp = sum;
  while(num > 1) {
    num--;
    // 获取最大最小值
    const {min, max} = getMaxAndMin(tmp, num);
    // 随机出本轮的值
    const value = getRandom(min,max);
    // 放入结果
    result.push(value);
    // 点数和减少进入下次判断
    tmp -= value;
  }
  // 将剩余值放入
  result.push(tmp);
  return result;
}

console.log(getPoints(10));
console.log(getPoints(22,5));
console.log(getPoints(1,2)); // 报错 Uncaught Error: 投掷结果有误

题外话

大家要是觉得有用,辛苦点赞!!送❀❀!

相关推荐
guokanglun1 小时前
CSS 响应式设计之媒体查询技术
前端·css·媒体
kali-Myon2 小时前
ctfshow-web入门-JWT(web345-web350)
前端·学习·算法·web安全·node.js·web·jwt
守望↪星空3 小时前
paddle表格识别数据制作
前端·chrome·paddle
kkkAloha3 小时前
常见error集合
前端·javascript·react.js
罔闻_spider3 小时前
webpack案例----pdd(anti-content)
前端·javascript·typescript
JiaLin_Denny4 小时前
nodejs和npm在gitbash中提示Not Found情况的解决办法
前端·npm·node.js
Xlbb.4 小时前
安全见闻1-5
前端·网络·人工智能·安全·网络安全
没资格抱怨4 小时前
函数节流和防抖
前端·javascript
空白诗5 小时前
前端隐藏元素的方式有哪些?HTML 和 CSS 中隐藏元素的多种方法
前端·css·html
LilySesy5 小时前
SAP+Internet主题HTML样式选择
前端·html·sap·abap·internet服务·its·扫码枪