# Daily Challenge #41 - Greed is Good

Greed is a dice game played with five six-sided dice. Using an array containing five six-sided dice values, write a function that will score a throw according to the following rules:

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

A single die can only be counted once in each roll. For example, a "5" can only count as part of a triplet (contributing to the 500 points) or alone (as 50 points), but not both in the same roll.

Example Scoring

5 1 3 4 1 => 50 + 2 * 100 = 250
1 1 1 3 1 => 1000 + 100 = 1100
2 4 4 5 4 => 400 + 50 = 450

You can try to fill the array with random values. If you have extra time, you can also try to keep track of the player's score over several throws.

This challenge comes from user JulianNicholls. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge for a future post? Email yo+challenge@dev.to with your suggestions!

### Discussion its pretty? no... but it works

function GG(arr) {
let map = [],
result = 0;

arr.join("").split("").forEach(el => map[el] = (map[el] || 0) + 1);

for (let index = 0; index <= 6; index++) {
if (map[index] != undefined) {
switch (index) {
case 1:
if (map[index] >= 3) {
result += 1000;

if ((map[index] - 3) > 0) result += (map[index] - 3) * 100;
} else {
result += map[index] * 100
}

break;
case 2:
if (map[index] >= 3) result += 200;

break;
case 3:
if (map[index] >= 3) result * 300;

break;
case 4:
if (map[index] >= 3) result += 400;

break;
case 5:
if (map[index] >= 3) {
result += 500;

if ((map[index] - 3) > 0) result += (map[index] - 3) * 50;
} else {
result += map[index] * 50
}

break;
case 6:
if (map[index] >= 3) result += 600;

break;
}
}
}

return result;
}

GG([5, 1, 3, 4, 1]) // 250

GG([1, 1, 1, 3, 1]) // 1100

GG([2, 4, 4, 5, 4]) // 450


Very understandable and faster! 👍

Thanks Peter 😃

### Perl

#!/usr/bin/env perl

use strict;
use warnings;
use utf8;
use feature qw{ say signatures };
no warnings qw{ experimental::signatures };

my @roll = map { d6() } 1 .. 5;
say join ' ', @roll;
say score(@roll);

sub score (@roll) {
my $score = 0; my$count;
map { $count->{$_} = 0 } 1 .. 6;
for my $d (@roll) {$count->{$d}++; } if ($count->{1} >= 3 ) { $score += 1000;$count->{1} -= 3 }
if ( $count->{2} >= 3 ) {$score += 200;  $count->{2} -= 3 } if ($count->{3} >= 3 ) { $score += 300;$count->{3} -= 3 }
if ( $count->{4} >= 3 ) {$score += 400;  $count->{4} -= 3 } if ($count->{5} >= 3 ) { $score += 500;$count->{5} -= 3 }
if ( $count->{6} >= 3 ) {$score += 600;  $count->{6} -= 3 } while ($count->{1} > 0 ) { $score += 100;$count->{1}-- }
while ( $count->{5} > 0 ) {$score += 50;  $count->{5}-- } return$score;
}

sub d6 {
return 1 + int rand 6;
}


JS implementation keeping the rules as data

function score( dice ) {
// Business rules of the game:
//  v: the dice value
//  c: the number of dice required to trigger the rule
//  p: the points added each time the rule is triggered
const scoringRules = [
{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}
]

// returns the score given a histogram mapping a dice value to the number of dice with that value
const scoreForHistogram = counts => {
let score = 0
for (const {v, c, p} of scoringRules) {
const ruleCount = Math.floor((counts[v] || 0) / c);
counts[v] -= ruleCount * c;
score += ruleCount * p;
}
return score;
};

// Generate the histogram required by scoreForHistogram
const counts = [...Array(7)].map(v=>0);// note that the rest of the logic uses 1-indexed arrays, so we need 7 entries to get a valid counts entry
for (const v of dice) counts[v]++;

return scoreForHistogram(counts)
}


Keep the rules as data is the way to go. 🔥🔥🔥

ruby <3

def score(values)
values.tally.sum do |n, cnt, acc = 0|
case [n, cnt]
in 1, 3.. then
cnt -= 3
acc += 1000
redo
in 6, 3.. then
cnt -= 3
acc += 600
redo
in 5, 3.. then
cnt -= 3
acc += 500
redo
in 4, 3.. then
cnt -= 3
acc += 400
redo
in 3, 3.. then
cnt -= 3
acc += 300
redo
in 2, 3.. then
cnt -= 3
acc += 200
redo
in 1, 1.. then
cnt -= 1
acc += 100
redo
in 5, 1.. then
cnt -= 1
acc += 50
redo
else
acc
end
end
end

p score([5, 1, 3, 4, 1]) # => 50 + 2 * 100 = 250
p score([1, 1, 1, 3, 1]) # => 1000 + 100 = 1100
p score([2, 4, 4, 5, 4]) # => 400 + 50 = 450


Nice use of Ruby 2.7's pattern matching :-)

Easy peasy, lemon squeezy

(language: Elixir)

defmodule Greed do
def score_dice([_|_] = dice) do
tally(dice)
|> score
end

defp tally(enumerable) do
Enum.reduce(enumerable, %{}, fn
item, tally ->
count = tally[item] || 0
put_in(tally[item], count+1)
end)
end

defp score(tally, total \\ 0)
defp score(%{1 => dice} = tally, total) when dice >= 3, do:
score(%{tally | 1 => dice - 3}, total + 1000)
defp score(%{1 => dice} = tally, total) when dice >= 1, do:
score(%{tally | 1 => dice - 1}, total + 100)
defp score(%{2 => dice} = tally, total) when dice >= 3, do:
score(%{tally | 2 => dice - 3}, total + 200)
defp score(%{3 => dice} = tally, total) when dice >= 3, do:
score(%{tally | 3 => dice - 3}, total + 300)
defp score(%{4 => dice} = tally, total) when dice >= 3, do:
score(%{tally | 4 => dice - 3}, total + 400)
defp score(%{5 => dice} = tally, total) when dice >= 3, do:
score(%{tally | 5 => dice - 3}, total + 500)
defp score(%{5 => dice} = tally, total) when dice >= 1, do:
score(%{tally | 5 => dice - 1}, total + 50)
defp score(%{6 => dice} = tally, total) when dice >= 3, do:
score(%{tally | 6 => dice - 3}, total + 600)
defp score(_totaled_tally, score), do:
score
end

Greed.score_dice([1,1,1,1,1])
# 1200
Greed.score_dice([1,1,1,1,3])
# 1100
Greed.score_dice([1,1,1,3,3])
# 1000
Greed.score_dice([1,3,1,3,3])
# 500
Greed.score_dice([1,3,5,3,3])
# 450



A super gross unreadable solution but fun none the less 😂

def score(inp):
def single_score(n, c):
return (n * (100 if n != 1 else 1000) if c >= 3 and n in t else 0) + ((n * (10 if n != 1 else 100)) * (c - (3 if c >= 3 else 0)) if n in s else 0)
return sum([single_score(n, c) for n, c in {i: inp.count(i) for i in inp}.items()])

score([2, 4, 4, 5, 4]) # 450


Here is my simple solution with Python:

def score(dice):
dice_count = [0, 0, 0, 0, 0, 0]

sum = 0

for point in dice:
dice_count[point-1] += 1

while dice_count >= 3:
sum += 1000
dice_count -= 3
if dice_count <= 2 and dice_count != 0:
sum += 100 * dice_count
if dice_count >= 3:
sum += 200
else:
sum += 0
if dice_count >= 3:
sum += 300
else:
sum += 0
if dice_count >= 3:
sum += 400
else:
sum += 0
while dice_count >= 3:
sum += 500
dice_count -= 3
if dice_count <= 2 and dice_count != 0:
sum += 50 * dice_count
if dice_count >= 3:
sum += 600
else:
sum += 0

return sum


Python

import random
def pounts(p):
if p == 1:
return 1000
else:
return p*100
def greed():
d=sorted(random.choices(range(1,7),k=5))
print(d)
dice = 1
p = 0
while len(d) > 0:
if d.count(dice) >= 3:
p += pounts(dice)
del d[:4]
elif (d == 1 or d == 5) and (dice == 5 or dice == 1):
if d.count(1) == 2 or d.count(5) == 2:
p += int(100 if d == 1 else 50) * 2
del d[:2]
else:
p += int(100 if d == 1 else 50)
d.pop(0)
dice += 1
else:
if d == dice and d.count(dice) == 1:
d.pop(0)
elif d == dice and d.count(dice) == 2:
del d[:2]
dice += 1
return p


JavaScript

const greedy = arr => {
let points = 0;
const dices = { 1:0, 2:0, 3:0, 4:0, 5:0, 6:0 };
arr.forEach(el => dices[el]++);

Object.keys(dices).forEach(key => {
if (dices[key] >= 3) {
points += key * (key == 1 ? 1000 : 100);
if (key == 1) points += (dices[key] - 3) * 100;
if (key == 5) points += (dices[key] - 3) * 50;
} else {
if (key == 1) points += dices[key] * 100;
if (key == 5) points += dices[key] * 50;
}
})

return points;
}


Live demo on CodePen.

### C#

using System;
using System.Linq;

class MainClass {
public static void Main (string[] args) {
try {
var greed = new Greed();
greed.Throw();

while (true) {
greed.Throw();
}
} catch(Exception) {
Console.WriteLine("\nYou win!, game over.");
}
}
}

class Greed {

public int Score = 0;
public int Throws = 0;

public double GetAveragePerThrow(){
if(this.Throws == 0){
return 0;
}

return this.Score / this.Throws;
}

public int Roll(Random random){
var rolled = random.Next(1, 7);

Console.Write(rolled);

return rolled;
}

public int GetScore(int[] rolls){
var score = 0;

var one = rolls.Count(c => c == 1);
var two = rolls.Count(c => c == 2);
var three = rolls.Count(c => c == 3);
var four = rolls.Count(c => c == 4);
var five = rolls.Count(c => c == 5);
var six = rolls.Count(c => c == 6);

if(one == 1){
score += 100;
}

if(one == 3){
score += 1000;
}

if(two == 3){
score += 200;
}

if(three == 3){
score += 300;
}

if(four == 3){
score += 400;
}

if(five == 1){
score += 50;
}

if(five == 3){
score += 500;
}

if(six == 3){
score += 600;
}

if(one == 5){
throw new Exception("You win.");
}

return score;
}

public int[] Throw(){
var rollsPerThrow = 5;
var rolls = new int[rollsPerThrow];
var random = new Random();

Console.WriteLine("Rolls:");
for(var i = 0; i < rollsPerThrow; i++){
rolls[i] = this.Roll(random);
}

var rollScore = this.GetScore(rolls);

this.Throws++;
this.Score += rollScore;

Console.WriteLine($"\nScore: \n{rollScore}"); Console.WriteLine($"Total score: {this.Score} - Throws: {this.Throws} - Average score per throw: {this.GetAveragePerThrow()}");

return rolls;
}
}


my attempt using Python:

from collections import Counter

def score(values):
"""
Greed is good scoring

>>> score((5,1,3,4,1))
250
>>> score((1,1,1,3,1))
1100
>>> score((2,4,4,5,4))
450
"""
count = Counter(values)
score = 0
for value, amount in count.items():
if value > 6:
raise ValueError("Invalid dice value")
if value == 1:
value = 10
trip, left = divmod(amount, 3)
score += trip * value * 100
if value in (10, 5):
score += left * value * 10
return score

if __name__ == "__main__":
import doctest

doctest.testmod(verbose=True)


import Data.Tuple (uncurry)

count :: (a -> Bool) -> [a] -> Int
count pred = length . filter pred

greed :: [Int] -> Int
greed rolls = let groups = [(i, count (==i) rolls) | i <- [1..6]]
points3 n = case n of
1 -> 1000
6 -> 600
5 -> 500
4 -> 400
3 -> 300
2 -> 200
_ -> 0
points1 n = case n of
1 -> 100
5 -> 50
_ -> 0
score num count
| count == 0 = 0
| count >= 3 = points3 num + score num (count - 3)
| otherwise = points1 num + score num (count - 1)
in sum $map (uncurry score) groups  P.S. Hoogle is really useful. I didn't know about the uncurry function, but I knew I needed a function to do what uncurry does, so I could search for (a -> b -> c) -> (a, b) -> c in hoogle! I love functional programming and feel the whole day like it's Friday // create random rolls const roll = () => Array(5) .fill() .map(() => Math.floor(Math.random() * 6) + 1) // score a roll const score = roll => roll .reduce((a, v) => a + Math.pow(10, v - 1), 0) .toString() .padStart(6, "0") .split("") .reduce((a, v, i) => a + [ v > 2 ? 600 : 0, (v > 2 ? 500 : 0) + v % 3 * 50, v > 2 ? 400 : 0, v > 2 ? 300 : 0, v > 2 ? 200 : 0, (v > 2 ? 1000 : 0) + v % 3 * 100 ][i], 0) // example scoring test = (iRoll, iScore) => ${iRoll.join(' ')} = ${score(iRoll)} (${score(iRoll) == iScore ? 'success' : 'fail'})
console.log(
test([5, 1, 3, 4, 1], 250),
test([1, 1, 1, 3, 1], 1100),
test([2, 4, 4, 5, 4], 450)
)

5 1 3 4 1 = 250 (success)
1 1 1 3 1 = 1100 (success)
2 4 4 5 4 = 450 (success)


My python sol :

def play_greed():
score = [random.randint(1, 6) for _ in range(5)]
count = [score.count(i) for i in range(1, 6+1)]
points = 0

if count < 3:
points += 100 * count
elif count == 3: points += 1000

if count < 3:
points+= 50 * count
elif count == 3: points += 500

if count == 3: points += 200
elif count == 3 : points += 300
elif count == 3 : points += 400
elif count == 3 : points += 600

return points


Ruby.  