def testDieChoiceDependsOnWinScore3(self):
        # State where choice deviates for low and high, but not medium scores
        state = TurnState(side_dice=SideDiceState({DieFace.Tank: 1}),
                          throw=DiceThrow({
                              DieFace.Ray: 5,
                              DieFace.Cow: 1,
                              DieFace.Human: 2,
                              DieFace.Chicken: 4
                          }))

        self.assertEqual(DieFace.Chicken,
                         self.action_selector.select_die(state))
        self.assertEqual(DieFace.Chicken,
                         self.action_selector.select_die(state, 4))
        self.assertEqual(DieFace.Chicken,
                         self.action_selector.select_die(state, 5))
        self.assertEqual(DieFace.Chicken,
                         self.action_selector.select_die(state, 6))

        self.assertEqual(DieFace.Ray,
                         self.action_selector.select_die(state, 1))
        self.assertEqual(DieFace.Ray,
                         self.action_selector.select_die(state, 2))
        self.assertEqual(DieFace.Ray,
                         self.action_selector.select_die(state, 3))
        self.assertEqual(DieFace.Ray,
                         self.action_selector.select_die(state, 7))
        self.assertEqual(DieFace.Ray,
                         self.action_selector.select_die(state, 8))
    def testDieChoiceDependsOnWinScore2(self):
        # State where choice deviates only for a couple of higher win scores.
        # It is more typical that there is a deviation for lower win scores up until a limit.
        state = TurnState(side_dice=SideDiceState({DieFace.Tank: 1}),
                          throw=DiceThrow({
                              DieFace.Ray: 4,
                              DieFace.Cow: 1,
                              DieFace.Human: 3,
                              DieFace.Chicken: 4
                          }))

        self.assertEqual(DieFace.Ray, self.action_selector.select_die(state))
        self.assertEqual(DieFace.Ray,
                         self.action_selector.select_die(state, 3))

        self.assertEqual(DieFace.Chicken,
                         self.action_selector.select_die(state, 4))
        self.assertEqual(DieFace.Chicken,
                         self.action_selector.select_die(state, 5))

        score_ray0 = self.action_selector.expected_score(
            SearchState(1, 4, 0, 0, 0))
        score_ray4 = self.action_selector.expected_score(
            SearchState(1, 4, 0, 0, 4))
        score_kip0 = self.action_selector.expected_score(
            SearchState(1, 0, 4, 1, 0))
        score_kip4 = self.action_selector.expected_score(
            SearchState(1, 0, 4, 1, 4))

        self.assertGreater(score_ray0, score_kip0)
        self.assertGreater(score_kip4, score_ray4)
        self.assertGreaterEqual(score_ray0, score_ray4)
        self.assertGreaterEqual(score_kip0, score_kip4)
    def testShouldStopWhenWinningEvenWhenOddsAreGood(self):
        state = TurnState(side_dice=SideDiceState({
            DieFace.Ray: 5,
            DieFace.Cow: 2
        }))

        self.assertTrue(self.action_selector.should_stop(state, 1))
        self.assertTrue(self.action_selector.should_stop(state, 2))
        self.assertFalse(self.action_selector.should_stop(state, 3))
        self.assertFalse(self.action_selector.should_stop(state))
	def testSimulator(self):
		state = SideDiceState({ DieFace.Ray: 9, DieFace.Cow: 2})
		num_runs = 100000
		scores = [
			(key, sum(1 for _ in iter))
			for key, iter in itertools.groupby(
				sorted(play_turn(self.action_selector, ini_side_dice = state)
				for _ in range(num_runs))
			)
		]
		print(scores)
		avg_score = sum(score * count for score, count in scores) / num_runs
		print(avg_score)
		self.assertAlmostEqual(avg_score, (314 / 3) / 36, delta = 0.01)
	def testExpectedScoreOfThreeDieThrow2(self):
		state = SearchState(4, 4, 2, 1, 0)
		expected_score = self.action_selector.expected_score(state)

		state = SideDiceState({ DieFace.Tank: 4, DieFace.Ray: 4, DieFace.Chicken: 2 })
		num_runs = 100000
		scores = [
			(key, sum(1 for _ in iter))
			for key, iter in itertools.groupby(
				sorted(play_turn(self.action_selector, ini_side_dice = state)
				for _ in range(num_runs))
			)
		]
		simulated_score = sum(score * count for score, count in scores) / num_runs
		print(scores, simulated_score, expected_score)
		self.assertAlmostEqual(expected_score, simulated_score, delta = 0.01)
    def testDieChoiceDependsOnWinScore1(self):
        # State with four different choices depending on win score
        state = TurnState(side_dice=SideDiceState({DieFace.Tank: 2}),
                          throw=DiceThrow({
                              DieFace.Ray: 2,
                              DieFace.Cow: 2,
                              DieFace.Human: 3,
                              DieFace.Chicken: 4
                          }))

        self.assertEqual(DieFace.Chicken,
                         self.action_selector.select_die(state))
        self.assertEqual(DieFace.Ray,
                         self.action_selector.select_die(state, 1))
        self.assertEqual(DieFace.Cow,
                         self.action_selector.select_die(state, 2))
        self.assertEqual(DieFace.Human,
                         self.action_selector.select_die(state, 3))