我在 CodeWars 中遇到了一个名为 Greed is Good 的任务。任务是有规则的游戏。给你一个包含数字的数组,你需要根据规则返回最终分数。
规则:
Three 1's => 1000 points
Three 6's => 600 points
Three 5's => 500 points
Three 4's => 400 points
Three 3's => 300 points
Three 2's => 200 points
One 1 => 100 points
One 5 => 50 point
因此,如果我们有包含元素 [ 5 1 3 4 1 ] 的数组,结果将是 250 点,依此类推。
我尝试实施该解决方案并将任务分成几个任务。我编写了一个函数来计算数组中出现的次数,并返回具有值和计数的对象 -> { v: '3', c: '2' } 。然后我使用数组函数查找满足条件的元素并计算总和。我的代码已经通过了第一次测试,但接下来没有通过。
我需要根据规则计算字符的主要问题,例如,如果我们有一个包含五个 1 的数组,我需要像 { v: "1", c: 4, v: "1", c: 1 } 那样对它们进行计数。我的实现将它们全部计数,然后我的代码毫无意义。所以我试图找到另一种解决方案如何写得更简单并考虑上面的问题。
我的代码:
const rules = [
{ v: '1', c: 3, p: 1000 },
{ v: '6', c: 3, p: 600 },
{ v: '5', c: 3, p: 500 },
{ v: '4', c: 3, p: 400 },
{ v: '3', c: 3, p: 300 },
{ v: '2', c: 3, p: 200 },
{ v: '1', c: 1, p: 100 },
{ v: '5', c: 1, p: 50 }
]
const score = (dice) => {
const values = countValuesFromArr(dice)
const sum = values.map((el) => rules.filter((rulesEl) => rulesEl.v === el.v && rulesEl.c === el.c ? rulesEl.p : 0)).flat().reduce((a, b) => a + b.p )
return sum
}
const countValuesFromArr = (arr) => {
let counter = {}
for (el of arr) {
if (counter[el]) {
counter[el] += 1
} else {
counter[el] = 1
}
}
const values = Object.keys(counter).map(key => {
return { v: key, c: counter[key] };
});
return values
}
主要问题是您仅在
rulesEl.c === el.c
时应用分数,但 el.c
可能大于 rulesEl.c
,在这种情况下,您应该确定 c
可以从中减去多少次
el.c
并多次应用分数。其余的(如果有的话)仍然应该用于潜在地应用其他规则。因此,这种逻辑不能与 rules.filter
一起使用,因为这(错误地)意味着您只能对某个数字应用一次规则。
我实际上会首先迭代规则,因为这样你就不需要调用
filter
。相反,该规则将为您提供 counter
的属性,因此您无需通过迭代来查找它。
看起来是这样的:
function score( dice ) {
const counters = [null, 0, 0, 0, 0, 0, 0];
for (const value of dice) counters[value]++;
return rules.reduce((score, {v, c, p}) => {
const quot = Math.floor(counters[v] / c); // How many times can we apply the rule?
score += quot * p; // Apply it that many times
counters[v] %= c; // And remove the used digits
return score;
}, 0);
}