DEV Community

Daily Challenge #41 - Greed is Good

dev.to staff on August 15, 2019

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 thro...
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.