Yacht: Dig Deeper Solution Fails "Four of a Kind" Test

Adding new test cases is expensive. All existing solutions will be re-tested and that can take a lot of time and processing power. Therefore we usually only modify the tests if there’s a serious problem, e.g. if they are wrong or if they don’t cover a case that many solutions got wrong.

I don’t care much for the PR either. I’m trying to understand the scope of this problem, by ensuring that description and tests go hand in hand.

I did run them against your test file. I am not getting any failures. Unless the test file you pasted is not the correct one?

Test file:


# These tests are auto-generated with test data from:
# https://github.com/exercism/problem-specifications/tree/main/exercises/yacht/canonical-data.json
# File last updated on 2023-07-19

import unittest
import yacht


class YachtTest(unittest.TestCase):
    def test_yacht(self):
        self.assertEqual(yacht.score([5, 5, 5, 5, 5], yacht.YACHT), 50)

    def test_not_yacht(self):
        self.assertEqual(yacht.score([1, 3, 3, 2, 5], yacht.YACHT), 0)

    def test_ones(self):
        self.assertEqual(yacht.score([1, 1, 1, 3, 5], yacht.ONES), 3)

    def test_ones_out_of_order(self):
        self.assertEqual(yacht.score([3, 1, 1, 5, 1], yacht.ONES), 3)

    def test_no_ones(self):
        self.assertEqual(yacht.score([4, 3, 6, 5, 5], yacht.ONES), 0)

    def test_twos(self):
        self.assertEqual(yacht.score([2, 3, 4, 5, 6], yacht.TWOS), 2)

    def test_fours(self):
        self.assertEqual(yacht.score([1, 4, 1, 4, 1], yacht.FOURS), 8)

    def test_yacht_counted_as_threes(self):
        self.assertEqual(yacht.score([3, 3, 3, 3, 3], yacht.THREES), 15)

    def test_yacht_of_3s_counted_as_fives(self):
        self.assertEqual(yacht.score([3, 3, 3, 3, 3], yacht.FIVES), 0)

    def test_fives(self):
        self.assertEqual(yacht.score([1, 5, 3, 5, 3], yacht.FIVES), 10)

    def test_fives(self):
        self.assertEqual(yacht.score([5, 1, 5, 2, 6], yacht.FIVES), 10)

    def test_sixes(self):
        self.assertEqual(yacht.score([2, 3, 4, 5, 6], yacht.SIXES), 6)

    def test_full_house_two_small_three_big(self):
        self.assertEqual(yacht.score([2, 2, 4, 4, 4], yacht.FULL_HOUSE), 16)

    def test_full_house_three_small_two_big(self):
        self.assertEqual(yacht.score([5, 3, 3, 5, 3], yacht.FULL_HOUSE), 19)

    def test_two_pair_is_not_a_full_house(self):
        self.assertEqual(yacht.score([2, 2, 4, 4, 5], yacht.FULL_HOUSE), 0)

    def test_four_of_a_kind_is_not_a_full_house(self):
        self.assertEqual(yacht.score([1, 4, 4, 4, 4], yacht.FULL_HOUSE), 0)

    def test_yacht_is_not_a_full_house(self):
        self.assertEqual(yacht.score([2, 2, 2, 2, 2], yacht.FULL_HOUSE), 0)

    def test_four_of_a_kind_one(self):
        self.assertEqual(yacht.score([4, 6, 6, 6, 6], yacht.FOUR_OF_A_KIND), 24)

    def test_four_of_a_kind_two(self):
        self.assertEqual(yacht.score([6, 4, 6, 6, 6], yacht.FOUR_OF_A_KIND), 24)

    def test_four_of_a_kind_three(self):
        self.assertEqual(yacht.score([6, 6, 4, 6, 6], yacht.FOUR_OF_A_KIND), 24)

    def test_four_of_a_kind_four(self):
        self.assertEqual(yacht.score([6, 6, 6, 4, 6], yacht.FOUR_OF_A_KIND), 24)

    def test_four_of_a_kind_five(self):
        self.assertEqual(yacht.score([6, 6, 6, 6, 4], yacht.FOUR_OF_A_KIND), 24)

    def test_yacht_can_be_scored_as_four_of_a_kind(self):
        self.assertEqual(yacht.score([3, 3, 3, 3, 3], yacht.FOUR_OF_A_KIND), 12)

    def test_full_house_is_not_four_of_a_kind(self):
        self.assertEqual(yacht.score([3, 3, 3, 5, 5], yacht.FOUR_OF_A_KIND), 0)

    def test_little_straight(self):
        self.assertEqual(yacht.score([3, 5, 4, 1, 2], yacht.LITTLE_STRAIGHT), 30)

    def test_little_straight_as_big_straight(self):
        self.assertEqual(yacht.score([1, 2, 3, 4, 5], yacht.BIG_STRAIGHT), 0)

    def test_four_in_order_but_not_a_little_straight(self):
        self.assertEqual(yacht.score([1, 1, 2, 3, 4], yacht.LITTLE_STRAIGHT), 0)

    def test_no_pairs_but_not_a_little_straight(self):
        self.assertEqual(yacht.score([1, 2, 3, 4, 6], yacht.LITTLE_STRAIGHT), 0)

    def test_minimum_is_1_maximum_is_5_but_not_a_little_straight(self):
        self.assertEqual(yacht.score([1, 1, 3, 4, 5], yacht.LITTLE_STRAIGHT), 0)

    def test_big_straight(self):
        self.assertEqual(yacht.score([4, 6, 2, 5, 3], yacht.BIG_STRAIGHT), 30)

    def test_big_straight_as_little_straight(self):
        self.assertEqual(yacht.score([6, 5, 4, 3, 2], yacht.LITTLE_STRAIGHT), 0)

    def test_no_pairs_but_not_a_big_straight(self):
        self.assertEqual(yacht.score([6, 5, 4, 3, 1], yacht.BIG_STRAIGHT), 0)

    def test_choice(self):
        self.assertEqual(yacht.score([3, 3, 5, 6, 6], yacht.CHOICE), 23)

    def test_yacht_as_choice(self):
        self.assertEqual(yacht.score([2, 2, 2, 2, 2], yacht.CHOICE), 10)

code:

ONES = 1
TWOS = 2
THREES = 3
FOURS = 4
FIVES = 5
SIXES = 6
FULL_HOUSE = 'FULL_HOUSE'
FOUR_OF_A_KIND = 'FOUR_OF_A_KIND'
LITTLE_STRAIGHT = 'LITTLE_STRAIGHT'
BIG_STRAIGHT = 'BIG_STRAIGHT'
CHOICE = 'CHOICE'
YACHT = 'YACHT'

def score(dice, category):
    match category:
        case 1 | 2 | 3 | 4 | 5 | 6:
            return dice.count(category) * category
        case 'FULL_HOUSE' if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3]:
            return sum(dice)
        case 'FOUR_OF_A_KIND' if dice[0] == dice[3] or dice[1] == dice[4]:
            return sorted(dice)[1] * 4
        case 'LITTLE_STRAIGHT' if sorted(dice) == [1, 2, 3, 4, 5]:
            return 30
        case 'BIG_STRAIGHT' if sorted(dice) == [2, 3, 4, 5, 6]:
            return 30
        case 'YACHT' if all(num == dice[0] for num in dice):
            return 50
        case 'CHOICE':
            return sum(dice)
        case _:
            return 0

other code:

ONES = 1
TWOS = 2
THREES = 3
FOURS = 4
FIVES = 5
SIXES = 6
FULL_HOUSE = 'FULL_HOUSE'
FOUR_OF_A_KIND = 'FOUR_OF_A_KIND'
LITTLE_STRAIGHT = 'LITTLE_STRAIGHT'
BIG_STRAIGHT = 'BIG_STRAIGHT'
CHOICE = 'CHOICE'
YACHT = 'YACHT'
def score(dice, category):
    if category in (1,2,3,4,5,6):
         return dice.count(category) * category
    elif category == 'FULL_HOUSE':
        if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3]:
            return sum(dice) or 0
    elif category == 'FOUR_OF_A_KIND':
        dice = sorted(dice)
        if dice[0] == dice[3] or dice[1] == dice[4]:
            return dice[1] * 4 or 0
    elif category == 'LITTLE_STRAIGHT':
        if sorted(dice) == [1, 2, 3, 4, 5]:
            return 30 or 0
    elif category == 'BIG_STRAIGHT':
        if sorted(dice) == [2, 3, 4, 5, 6]:
            return 30 or 0
    elif category == 'YACHT':
        if all(num == dice[0] for num in dice):
            return 50
    elif category == 'CHOICE':
        return sum(dice)
    return 0

Did you leave out cases in your post above where you said that was the whole test file?

Yes, adding new tests and re-testing all existing solutions is an expensive operation.

However, the approaches in the “dig deeper” section could be updated, so that people who are digging deeper get a better understanding of the problem.