Codewars骰子游戏Greed的C语言代码出现不稳定行为求助
Greed骰子游戏C语言代码问题
问题规则
Greed是一款使用5枚六面骰子的游戏,单次掷骰子得分规则如下(输入为包含5个骰子点数的数组):
- 三个1(⚀)=1000分
- 三个6(⚅)=600分
- 三个5(⚄)=500分
- 三个4(⚃)=400分
- 三个3(⚂)=300分
- 三个2(⚁)=200分
- 单个1(⚀)=100分
- 单个5(⚄)=50分
单个骰子只能被计数一次,例如某个5只能计入三元组(得500分)或单个(得50分),不能同时计入。
示例计分:
| 掷骰结果 | 得分说明 |
|---|---|
5 1 3 4 1 | 250: 50(单个5) + 2 * 100(两个1) |
1 1 1 3 1 | 1100: 1000(三个1) + 100(剩余一个1) |
2 4 4 5 4 | 450: 400(三个4) + 50(单个5) |
注意:不可修改输入数组,否则无法通过所有测试。
我的代码
int score(const int dice[5]) { int score = 0; int digits[6] = { 0 }; for (int i = 0; i < 5; i++) { digits[dice[i]]++; } // Rules // 100 points for each 1 except when there is more than 2 if (digits[1] < 3) { score += digits[1] * 100; } else { score += (digits[1] % 3) * 100; } // 50 points for each 5 if (digits[5] < 3) { score += digits[5] * 50; } else { score += (digits[5] % 3) * 50; } // 1000 points for 3 1s for (int i = 1; i <= 6; i++) { int repeats = digits[i]; if (repeats >= 3) { switch (i) { case 1: score += 1000; break; case 2: score += 200; break; case 3: score += 300; break; case 4: score += 400; break; case 5: score += 500; break; case 6: score += 600; break; default: break; } } } return score; }
问题现象
本地运行所有测试用例均正常,但在Codewars平台上测试时而通过时而失败,怀疑存在机器/编译器相关的问题,恳请帮忙找出问题所在。
问题根源
代码中存在数组越界访问的未定义行为:
- 定义的
digits数组长度为6,索引范围是0~5,但骰子点数范围是1~6。当骰子出现点数6时,digits[6]会访问数组边界外的内存,这会导致不可预测的结果——有时刚好内存数据不影响计算,有时会破坏其他变量值,最终表现为测试时而通过时而失败。
修复方案
有两种可行的修复方式:
方式1:扩大数组长度
将digits数组长度改为7,让索引1~6都能被合法访问:
int score(const int dice[5]) { int score = 0; int digits[7] = { 0 }; // 长度改为7,索引0~6 for (int i = 0; i < 5; i++) { digits[dice[i]]++; } // 后续逻辑保持不变 if (digits[1] < 3) { score += digits[1] * 100; } else { score += (digits[1] % 3) * 100; } if (digits[5] < 3) { score += digits[5] * 50; } else { score += (digits[5] % 3) * 50; } for (int i = 1; i <= 6; i++) { int repeats = digits[i]; if (repeats >= 3) { switch (i) { case 1: score += 1000; break; case 2: score += 200; break; case 3: score += 300; break; case 4: score += 400; break; case 5: score += 500; break; case 6: score += 600; break; default: break; } } } return score; }
方式2:偏移骰子点数到合法索引
将骰子点数减1,映射到0~5的数组索引:
int score(const int dice[5]) { int score = 0; int digits[6] = { 0 }; for (int i = 0; i < 5; i++) { digits[dice[i] - 1]++; // 点数减1,映射到0~5索引 } // 调整后续逻辑的索引对应关系 int count1 = digits[0]; // digits[0]对应点数1的数量 if (count1 < 3) { score += count1 * 100; } else { score += (count1 % 3) * 100; } int count5 = digits[4]; // digits[4]对应点数5的数量 if (count5 < 3) { score += count5 * 50; } else { score += (count5 % 3) * 50; } for (int i = 0; i < 6; i++) { int repeats = digits[i]; if (repeats >= 3) { switch (i+1) { // 索引加1还原为骰子点数 case 1: score += 1000; break; case 2: score += 200; break; case 3: score += 300; break; case 4: score += 400; break; case 5: score += 500; break; case 6: score += 600; break; default: break; } } } return score; }
两种方式都能解决数组越界问题,确保在任何环境下都能稳定运行。
内容的提问来源于stack exchange,提问作者Nicklaus Caceres
相关产品推荐
相关产品推荐

