DEV Community

dev.to staff
dev.to staff

Posted on

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!

Top comments (18)

Collapse
 
danielsclet profile image
Daniel Santos • Edited

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
Collapse
 
hoffmann profile image
Peter Hoffmann

Very understandable and faster! 👍

Collapse
 
danielsclet profile image
Daniel Santos

Thanks Peter 😃

Collapse
 
jacoby profile image
Dave Jacoby

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;
}
Collapse
 
willsmart profile image
willsmart

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[6] entry
  for (const v of dice) counts[v]++;

  return scoreForHistogram(counts)
}
Collapse
 
itsdarrylnorris profile image
Darryl Norris

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

Collapse
 
hanachin profile image
Seiei Miyagi

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
Collapse
 
thepeoplesbourgeois profile image
Josh • Edited

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

Collapse
 
geraldhost profile image
Gerald Host

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
Collapse
 
peter279k profile image
peter279k

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[0] >= 3:
        sum += 1000
        dice_count[0] -= 3
    if dice_count[0] <= 2 and dice_count != 0:
        sum += 100 * dice_count[0]
    if dice_count[1] >= 3:
        sum += 200
    else:
        sum += 0
    if dice_count[2] >= 3:
        sum += 300
    else:
        sum += 0
    if dice_count[3] >= 3:
        sum += 400
    else:
        sum += 0
    while dice_count[4] >= 3:
        sum += 500
        dice_count[4] -= 3
    if dice_count[4] <= 2 and dice_count[4] != 0:
        sum += 50 * dice_count[4]
    if dice_count[5] >= 3:
        sum += 600
    else:
        sum += 0

    return sum
Collapse
 
matrossuch profile image
Mat-R-Such

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[0] == 1 or d[0] == 5) and (dice == 5 or dice == 1):
            if d.count(1) == 2 or d.count(5) == 2:
                p += int(100 if d[0] == 1 else 50) * 2
                del d[:2]
            else:
                p += int(100 if d[0] == 1 else 50)
                d.pop(0)
            dice += 1
        else:
            if d[0] == dice and d.count(dice) == 1:
                d.pop(0)
            elif d[0] == dice and d.count(dice) == 2:
                del d[:2]
            dice += 1
    return p
Collapse
 
alvaromontoro profile image
Alvaro Montoro

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.

Collapse
 
jacksoncds profile image
Jackson DaSilva

C#

Repl.it

using System;
using System.Linq;
using System.Threading;

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

        while (true) {
          Thread.Sleep(5);
          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;
  }
}
Collapse
 
splinter98 profile image
splinter98

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)
Collapse
 
craigmc08 profile image
Craig McIlwrath

Haskell:

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!

Collapse
 
hoffmann profile image
Peter Hoffmann • Edited

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)
Collapse
 
hectorpascual profile image
Héctor Pascual

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[0] < 3:
        points += 100 * count[0]
    elif count[0] == 3: points += 1000

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

    if count[1] == 3: points += 200
    elif count[2] == 3 : points += 300
    elif count[3] == 3 : points += 400
    elif count[5] == 3 : points += 600

    return points
Collapse
 
jamespotz profile image
James Robert Rooke

Ruby.