掷骰子🎲获取点数问题

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

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

掷一个骰子的情况: 一个骰子结果点数只能在[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: 投掷结果有误

题外话

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

相关推荐
布茹 ei ai1 分钟前
地表沉降监测分析系统(vue3前端+python后端+fastapi+网页部署)(开源分享)
前端·python·fastapi
不一样的少年_2 分钟前
WebTab等插件出事后:不到100行代码,带你做一个干净透明的新标签页
前端·javascript·浏览器
幸运小圣2 分钟前
关于Vue 3 <script setup> defineXXX API 总结
前端·javascript·vue.js
500佰5 分钟前
AI 财务案例 普通财务人的AI in ALL
前端·人工智能
军军3606 分钟前
动态星空粒子效果
前端
n***i958 分钟前
重新定义前端运行时:从浏览器脚本到分布式应用层的架构进化
前端·架构
AAA阿giao9 分钟前
从零开始:用 Vue 3 + Vite 打造一个支持流式输出的 AI 聊天界面
前端·javascript·vue.js
玉宇夕落9 分钟前
Vue 3 实现 LLM 流式输出:从零搭建一个简易 Chat 应用
前端·vue.js
开源之眼10 分钟前
github star都很多的 React Native 和 React 有什么区别?一文教你快速分清
前端
听风说图11 分钟前
AI编程助手为何总是"健忘"?
前端