def testSatisfyConstraints4(self):
        h = HandInformation(hand_size=2, possible_cards=set(range(6)))
        h.addConstraint([0, 1, 2])
        h.addConstraint([0, 2, 3])
        h.addConstraint([0, 4, 5])

        hands = h.satisfyConstraints()

        self.assertEqual(len(hands), 3)
        # Hand order doesn't technically matter, but I've assumed an order.
        # Solutions computed by hand.

        # Hand 0, Card 0, all other cards allowed
        assertCardConstraints(self, hands[0], required_cards=[0])

        # Hand 1, Cards 2 and 4, but 0 not allowed
        assertCardConstraints(self,
                              hands[1],
                              required_cards=[2, 4],
                              missing_cards=[0])

        # Hand 2, Cards 2 and 5 allowed, but 0 and 4 not allowed
        assertCardConstraints(self,
                              hands[2],
                              required_cards=[2, 5],
                              missing_cards=[0, 4])
    def testSatisfyConstraintsImpossibleConstraint(self):
        h = HandInformation(hand_size=1, possible_cards=set(range(6)))
        h.addKnownCard(0)
        h.addConstraint([1, 2])

        hands = h.satisfyConstraints()
        self.assertEqual(len(hands), 0)
    def testNumPossibleHands(self):
        h1 = HandInformation(hand_size=3, possible_cards=set(PEOPLE))
        self.assertEqual(h1.numPossibleHands(), 20)

        h1.addConstraint([set(PEOPLE[0])])
        with self.assertRaises(ValueError):
            h1.numPossibleHands()
    def testCountPossibleCombsOverlappingSets(self):
        gsc = GameStateCounter(cache_path='')

        h1 = HandInformation(hand_size=3,
                             possible_cards=set([1, 2, 3, 4, 5, 6]))
        h2 = HandInformation(hand_size=3,
                             possible_cards=set([3, 4, 5, 6, 7, 8]))

        possible_hands = gsc.countPossibleStates([h1, h2])

        # Solution computed by hand.
        # There are 4 hands where 1 card from player 1 overlaps with player 2.
        # This corresponds to 4*5C3
        # There are 12 hands where 2 cards from player 1 overlap with player 2.
        # This corresponds with 12*4C3
        # There are 4 hands where 3 cards from player 1 overlap with player 2.
        # This corresponds to 4*3C3.
        # In total, its 4*5C3 + 12*4C3 + 4*3C3 = 92
        self.assertEqual(possible_hands, 92)
    def testNoCollisions(self):
        h1 = HandInformation(hand_size=3, possible_cards=set(range(6)))
        constraint1 = set((0, 1))
        h1.addConstraint(constraint1)

        h2 = HandInformation(hand_size=3, possible_cards=set(range(6)))
        constraint2 = set((2, 3))
        h2.addConstraint(constraint2)

        hand_lists = satisfyAllConstraints([h1, h2])

        self.assertEqual(len(hand_lists), 4)

        # Set 1. First hand 0, Second hand 2
        assertCardConstraints(self,
                              hand_lists[0][0],
                              required_cards=[0],
                              missing_cards=[2])
        assertCardConstraints(self,
                              hand_lists[0][1],
                              required_cards=[2],
                              missing_cards=[0])

        # Set 2. First hand 0, Second hand 3 and not 2
        assertCardConstraints(self,
                              hand_lists[1][0],
                              required_cards=[0],
                              missing_cards=[3])
        assertCardConstraints(self,
                              hand_lists[1][1],
                              required_cards=[3],
                              missing_cards=[0, 2])

        # Set 3. First hand 1 and not 0, Second hand 2
        assertCardConstraints(self,
                              hand_lists[2][0],
                              required_cards=[1],
                              missing_cards=[0, 2])
        assertCardConstraints(self,
                              hand_lists[2][1],
                              required_cards=[2],
                              missing_cards=[1])

        # Set 4. First hand 1 and not 0, Second hand 3 and not 2
        assertCardConstraints(self,
                              hand_lists[3][0],
                              required_cards=[1],
                              missing_cards=[0, 3])
        assertCardConstraints(self,
                              hand_lists[3][1],
                              required_cards=[3],
                              missing_cards=[1, 2])
    def testGetPossibleHandsWithConstraints(self):
        h = HandInformation(hand_size=3, possible_cards=set(PEOPLE))

        # Hand computed. This constraint will remove 4 possible cases.
        # (2,3,4), (2,3,5), (2,4,5), (3,4,5)
        constraint = set((PEOPLE[0], PEOPLE[1]))
        h.addConstraint(constraint)

        hands = h.getPossibleHands()
        self.assertEqual(len(hands), 16)

        distinct_hands = set()
        for hand in hands:
            # Ensure each hand satisfies the constraint
            self.assertTrue(any(c in hand.known_cards for c in constraint))

            # Ensure all hands are distinct
            self.assertTrue(hand not in distinct_hands)
            distinct_hands.add(hand)

        pass
    def testSinglePlayer(self):
        h = HandInformation(hand_size=3, possible_cards=set(range(6)))
        constraint1 = set((0, 1))
        h.addConstraint(constraint1)

        constraint2 = set((1, 2, 3))
        h.addConstraint(constraint2)

        hands = satisfyAllConstraints([h])

        # Hand computed solutions. Order doesn't technically matter, but I've
        # assumed the correct order.
        self.assertEqual(len(hands), 3)

        # Hand 0. Has card 1
        assertCardConstraints(self, hands[0][0], required_cards=[1])

        # Hand 1. Has cards 2 and 0, but not card 1
        assertCardConstraints(self,
                              hands[1][0],
                              required_cards=[2, 0],
                              missing_cards=[1])

        # Hand 2. Has cards 3 and 0, but not 1 or 2
        assertCardConstraints(self,
                              hands[2][0],
                              required_cards=[3, 0],
                              missing_cards=[1, 2])
    def testSatisfyConstraintsSatisfiedConstraint(self):
        # This test ensures that a satisifed constraint is not processed.
        h = HandInformation(hand_size=2, possible_cards=set(range(6)))
        h.addKnownCard(0)
        h.addConstraint([0, 1, 2])

        hands = h.satisfyConstraints()

        self.assertEqual(len(hands), 1)
        self.assertEqual(hands[0].hand_size, h.hand_size)
        self.assertEqual(hands[0].known_cards, h.known_cards)
        self.assertEqual(hands[0].possible_cards, h.possible_cards)
    def testSatisfyConstraints3(self):
        h = HandInformation(hand_size=2, possible_cards=set(range(6)))
        h.addConstraint([0, 1, 2])
        h.addConstraint([0, 2, 3])

        hands = h.satisfyConstraints()
        self.assertEqual(len(hands), 3)
        # Note that hand order technically doesn't matter, but I've matched
        # the order here for ease of testing.

        # Hand 0, Card 0, all other cards allowed
        assertCardConstraints(self, hands[0], required_cards=[0])

        # Hand 1, Card 2, but no card 0
        assertCardConstraints(self,
                              hands[1],
                              required_cards=[2],
                              missing_cards=[0])

        # Hand 2, Cards 3 and 1, but no cards 0 or 2
        assertCardConstraints(self,
                              hands[2],
                              required_cards=[1, 3],
                              missing_cards=[0, 2])
    def testSimpleCase(self):
        player_to_count = HandInformation(hand_size=3,
                                          possible_cards=set(
                                              [1, 2, 3, 4, 5, 6]))

        # One case to satisfy both players
        p1 = HandInformation(hand_size=1, possible_cards=set([7]))
        p2 = HandInformation(hand_size=1, possible_cards=set([8, 9, 10]))
        p2.addConstraint(set([6, 7, 8]))

        poss = countPlayerPossibilities(player_to_count, [p1, p2])

        self.assertEqual(len(poss), 20)
        for hand, count in poss.items():
            self.assertEqual(count, 1)
    def testSingleCollision(self):
        h1 = HandInformation(hand_size=3, possible_cards=set(range(6)))
        constraint1 = set((0, 1))
        constraint2 = set((1, 2, 3))
        h1.addConstraint(constraint1)
        h1.addConstraint(constraint2)

        h2 = HandInformation(hand_size=3, possible_cards=set(range(6)))
        constraint3 = set([1])
        h2.addConstraint(constraint3)

        hand_lists = satisfyAllConstraints([h1, h2])

        # Hand computed solutions. As usual, the order technically doesn't
        # matter, but I've hard-coded an order for this implementation.
        self.assertEqual(len(hand_lists), 2)

        # Set 1: First hand has 0 2 and not 1, Hand 2 has 1
        assertCardConstraints(self,
                              hand_lists[0][0],
                              required_cards=[0, 2],
                              missing_cards=[1])
        assertCardConstraints(self,
                              hand_lists[0][1],
                              required_cards=[1],
                              missing_cards=[0, 2])

        # Set 2. First hand has 0, 3 and not 1 nor 2. Hand 2 has 1
        assertCardConstraints(self,
                              hand_lists[1][0],
                              required_cards=[0, 3],
                              missing_cards=[1, 2])
        assertCardConstraints(self,
                              hand_lists[1][1],
                              required_cards=[1],
                              missing_cards=[0, 3])
    def testSatisfyConstraintsCheckHands(self):
        # This test ensures that the list of possible hands match between
        # the original hand and the list of sub-hands.

        h = HandInformation(hand_size=3, possible_cards=set(range(10)))
        h.addConstraint([0, 1, 2])

        possible_hands_sol = h.getPossibleHands()

        hands = h.satisfyConstraints()
        possible_hands_test = []
        for hand in hands:
            possible_hands_test.extend(hand.getPossibleHands())

        self.assertEqual(len(possible_hands_test), len(possible_hands_sol))

        for hand in possible_hands_test:
            self.assertTrue(hand in possible_hands_sol)
    def testCountPossibleCombsBaseCase(self):
        gsc = GameStateCounter(cache_path='')
        h = HandInformation(hand_size=3, possible_cards=set(range(6)))

        self.assertEqual(gsc.countPossibleStates([h]), 20)
    def testComplexSet(self):
        # A more complex scenario with some overlap.
        h1 = HandInformation(hand_size=3, possible_cards=set(range(6)))
        h1.addConstraint(set((0, 1, 2)))
        h2 = HandInformation(hand_size=3, possible_cards=set(range(1, 8)))
        h2.addConstraint(set((1, 2, 3)))

        hand_lists = satisfyAllConstraints([h1, h2])

        self.assertEqual(len(hand_lists), 7)

        # Set 1: First hand 0, second hand 1
        assertCardConstraints(self,
                              hand_lists[0][0],
                              required_cards=[0],
                              missing_cards=[1])
        assertCardConstraints(self,
                              hand_lists[0][1],
                              required_cards=[1],
                              missing_cards=[0])

        # Set 2: First hand 0, second hand 2 and not 1
        assertCardConstraints(self,
                              hand_lists[1][0],
                              required_cards=[0],
                              missing_cards=[2])
        assertCardConstraints(self,
                              hand_lists[1][1],
                              required_cards=[2],
                              missing_cards=[0, 1])

        # Set 3: First hand 0, second hand 3 and not 1 nor 2
        assertCardConstraints(self,
                              hand_lists[2][0],
                              required_cards=[0],
                              missing_cards=[3])
        assertCardConstraints(self,
                              hand_lists[2][1],
                              required_cards=[3],
                              missing_cards=[0, 1, 2])

        # Set 4: First hand 1 and not 0, second hand 2 and not 1
        assertCardConstraints(self,
                              hand_lists[3][0],
                              required_cards=[1],
                              missing_cards=[0, 2])
        assertCardConstraints(self,
                              hand_lists[3][1],
                              required_cards=[2],
                              missing_cards=[1])

        # Set 5: First hand 1 and not 0, second hand 3 and not 2 nor 1
        assertCardConstraints(self,
                              hand_lists[4][0],
                              required_cards=[1],
                              missing_cards=[3])
        assertCardConstraints(self,
                              hand_lists[4][1],
                              required_cards=[3],
                              missing_cards=[1, 2])

        # Set 6: First hand 2 and not 1 nor 0, second hand 1
        assertCardConstraints(self,
                              hand_lists[5][0],
                              required_cards=[2],
                              missing_cards=[0, 1])
        assertCardConstraints(self,
                              hand_lists[5][1],
                              required_cards=[1],
                              missing_cards=[2])

        # Set 7: First hand 2 and not 0 nor 1, second hand 3 and not 1 nor 2
        assertCardConstraints(self,
                              hand_lists[6][0],
                              required_cards=[2],
                              missing_cards=[0, 1, 3])
        assertCardConstraints(self,
                              hand_lists[6][1],
                              required_cards=[3],
                              missing_cards=[1, 2])
 def testLessThan(self):
     h1 = HandInformation(hand_size=2)
     h2 = HandInformation(hand_size=3)
     self.assertLess(h1, h2)
    def testMultipleConstraints(self):
        # A complex scenario involving 2 players. Solutions were hand computed
        # with much effort.

        h1 = HandInformation(hand_size=3, possible_cards=set(range(6)))
        h1.addConstraint(set((0, 1, 2)))
        h2 = HandInformation(hand_size=2, possible_cards=set(range(1, 8)))
        h2.addConstraint(set((1, 2, 3)))

        env = HandInformation(hand_size=3, possible_cards=set(range(8)))

        poss = countPlayerPossibilities(env, [h1, h2])

        self.assertEqual(len(poss), 56)

        for key, count in poss.items():
            # Count == 0
            if key.known_cards in [
                    set((0, 1, 2)),
                    set((0, 1, 3)),
                    set((0, 1, 4)),
                    set((0, 1, 5)),
                    set((0, 2, 3)),
                    set((0, 2, 4)),
                    set((0, 2, 5)),
                    set((0, 3, 4)),
                    set((0, 3, 5)),
                    set((0, 4, 5)),
                    set((1, 2, 3)),
                    set((1, 2, 4)),
                    set((1, 2, 5)),
                    set((1, 3, 4)),
                    set((1, 3, 5)),
                    set((1, 4, 5)),
                    set((2, 3, 4)),
                    set((2, 3, 5)),
                    set((2, 4, 5)),
                    set((3, 4, 5))
            ]:
                self.assertEqual(count,
                                 0,
                                 msg='Expected 0 game states from Hand {} '
                                 '(had {} game states)'.format(
                                     key.known_cards, count))
            # Count == 1
            elif key.known_cards in [
                    set((0, 1, 6)),
                    set((0, 1, 7)),
                    set((0, 2, 6)),
                    set((0, 2, 7)),
                    set((1, 2, 6)),
                    set((1, 2, 7)),
                    set((1, 3, 6)),
                    set((1, 3, 7)),
                    set((2, 3, 6)),
                    set((2, 3, 7))
            ]:
                self.assertEqual(count,
                                 1,
                                 msg='Expected 1 game state from Hand {} '
                                 '(had {} game states)'.format(
                                     key.known_cards, count))
            # Count == 2
            elif key.known_cards in [
                    set((0, 3, 6)),
                    set((0, 3, 7)),
                    set((1, 4, 6)),
                    set((1, 4, 7)),
                    set((1, 5, 6)),
                    set((1, 5, 7)),
                    set((2, 4, 6)),
                    set((2, 4, 7)),
                    set((2, 5, 6)),
                    set((2, 5, 7)),
                    set((3, 4, 6)),
                    set((3, 4, 7)),
                    set((3, 5, 6)),
                    set((3, 5, 7))
            ]:
                self.assertEqual(count,
                                 2,
                                 msg='Expected 2 game states from Hand {} '
                                 '(had {} game states)'.format(
                                     key.known_cards, count))
            # Count == 3
            elif key.known_cards in [
                    set((0, 4, 6)),
                    set((0, 4, 7)),
                    set((0, 5, 6)),
                    set((0, 5, 7)),
                    set((4, 5, 6)),
                    set((4, 5, 7))
            ]:
                self.assertEqual(count,
                                 3,
                                 msg='Expected 3 game states from Hand {} '
                                 '(had {} game states)'.format(
                                     key.known_cards, count))
            # Count == 5
            elif key.known_cards in [
                    set((1, 6, 7)),
                    set((2, 6, 7)),
                    set((3, 6, 7))
            ]:
                self.assertEqual(count,
                                 5,
                                 msg='Expected 5 game states from Hand {} '
                                 '(had {} game states)'.format(
                                     key.known_cards, count))
            # Count == 6
            elif key.known_cards in [set((4, 6, 7)), set((5, 6, 7))]:
                self.assertEqual(count,
                                 6,
                                 msg='Expected 6 game states from Hand {} '
                                 '(had {} game states)'.format(
                                     key.known_cards, count))
            # Count == 8
            elif key.known_cards in [set((0, 6, 7))]:
                self.assertEqual(count,
                                 8,
                                 msg='Expected 8 game states from Hand {} '
                                 '(had {} game states)'.format(
                                     key.known_cards, count))
            else:
                self.fail(
                    msg='Missing solution for Hand {}'.format(key.known_cards))
    def testRemovePossibleCard(self):
        h = HandInformation(hand_size=3)
        h.removePossibleCard(PEOPLE[0])

        self.assertFalse(PEOPLE[0] in h.possible_cards)