<jupyter_start><jupyter_text>Riddler ExpressYou and your friend are playing a game of “Acchi, Muite, Hoi” (or what some on Twitter have been calling the “lookaway challenge”). In the first round, you point in one of four directions: up, down, left or right. At the exact same time, your friend also looks in one of those four directions. If your friend looks in the same direction you’re pointing, you win! Otherwise, you switch roles as the game continues to the next round — now your friend points in a direction and you try to look away. As long as no one wins, you keep switching off who points and who looks.It seems like your chances of winning should be 50 percent, since there are exactly two players. But surely it’s not that simple. If both you and your friend choose your directions randomly in each round, what are your chances of winning?  Solution- Probability of A winning game 1: $0$, Probability of B winning game 1: $1/4$- Probability of A winning game 2: $1/4\cdot3/4$, Probability of B winning game 2: $0$- Probability of A winning game 3: $0$, Probability of B winning game 2: $1/4\cdot3/4\cdot3/4$- etc.Therefore, probability of A winning is $\dfrac{1}{4}\left(\dfrac{3}{4} + \dfrac{3^3}{4^3} + \dfrac{3^5}{4^5} + ...\right)= \dfrac{3}{16}\left(1 + \dfrac{9}{16} + \dfrac{9^2}{16^2} + ...\right) = \dfrac{3}{16}\left(\dfrac{1}{1-\frac{9}{16}}\right) = \dfrac{3}{16}\cdot\dfrac{16}{7} = \dfrac{3}{7}$Equivalently: If $X$ is the probability of winning when you start as the pointer, then the following equation holds:$$X = \dfrac{1}{4}\cdot0 + \dfrac{3}{4}\cdot(1-X) \Rightarrow X = \dfrac{3}{4} - \dfrac{3}{4}X \Rightarrow \dfrac{7}{4}X = \dfrac{3}{4} \Rightarrow X = \dfrac{3}{7}$$Equivalently: Let X be the win probability when you start as the pointer. Since you start as pointer on the first round, there is a 1/4 chance your opponent immediately wins (your win probability becomes 0) and a 3/4 chance you become the looker in the second round (your win probability becomes 1-X). Thus, X = 1/4\*0 + 3/4\*(1-X), which is solved by X = 3/7.  ClassicThe U.S. Open is upon us! <NAME> and <NAME> each submitted a tennis-themed puzzle that asked what it really takes to succeed in the tournament.Suppose you’re playing a match at the U.S. Open, and you’re slightly better than the competition: your chances of winning any given point are exactly 55 percent. (Yes, most players are more likely to win the points they serve, but we’re simplifying things a bit.) What are your chances of winning a three-set match, as played by the women, or a five-set match, as played by the men? And what are your chances of winning the whole tournament (seven consecutive matches)?If you’re not familiar with the scoring system in tennis, you can read more about it here. In short, the first to 4 points wins a game (as long as they’ve won 2 more points than their opponent), the first to six games wins a set (as long as they’ve won two more games), and the first to two sets (for women) or three sets (for men) wins the match. If at any point a set is tied at six games apiece, that set is decided by a tiebreaker, in which the first to 7 points wins (with the same 2-point margin rule applying here).You know, I thought tennis scoring was pretty straightforward until I had to write it down. Solution<jupyter_code>class TennisGame:
    def __init__(self, p_win_one_point, points_to_win, verbose=False):
        self.p_win_one_point = p_win_one_point
        self.p_lose_one_point = 1-self.p_win_one_point
        self.points_to_win = points_to_win
        self.verbose = verbose
        self.memo = {}
        
        # Deuce game: X = p**2 * 1 + 2p(1-p) * X + (1-p)**2 * 0
        self.p_win_from_deuce = self.p_win_one_point**2 / (1-2*self.p_win_one_point*self.p_lose_one_point)
    
    def p_win_intermediate(self, pts1, pts2):
        score = (pts1, pts2)
        
        if score in self.memo:
            return self.memo[score]

        if pts1 >= self.points_to_win and pts2 <= pts1-2:
            self.memo[score] = 1
            return self.memo[score]

        if pts2 >= self.points_to_win and pts1 <= pts2-2:
            self.memo[score] = 0
            return self.memo[score]

        if pts1 == self.points_to_win-1 and pts2 == self.points_to_win-1:
            self.memo[score] = self.p_win_from_deuce
            return self.memo[score]

        self.memo[score] = self.p_win_one_point * self.p_win_intermediate(pts1+1, pts2) + self.p_lose_one_point * self.p_win_intermediate(pts1, pts2+1)
        if self.verbose:
            print(f'Probability of winning {pts1}-{pts2} game: {self.memo[score]}')
        return self.memo[score]
    
    def p_win(self):
        return self.p_win_intermediate(0, 0)
game = TennisGame(p_win_one_point=0.55, points_to_win=4, verbose=True)
print(game.p_win())
class TennisSet:
    def __init__(self, p_win_one_point, points_to_win_game, games_to_win_set, verbose=False):
        self.p_win_one_point = p_win_one_point
        self.points_to_win_game = points_to_win_game
        self.games_to_win_set = games_to_win_set
        self.verbose = verbose
        self.memo = {}
        
        self.game = TennisGame(self.p_win_one_point, self.points_to_win_game)
        self.p_win_one_game = self.game.p_win()
    
    def p_win_intermediate(self, pts1, pts2):
        score = (pts1, pts2)
        
        if score in self.memo:
            return self.memo[score]

        if pts1 >= self.games_to_win_set and pts2 <= pts1-2:
            self.memo[score] = 1
            return self.memo[score]

        if pts2 >= self.games_to_win_set and pts1 <= pts2-2:
            self.memo[score] = 0
            return self.memo[score]

        if pts1 == self.games_to_win_set and pts2 == self.games_to_win_set:
            self.tiebreak = TennisGame(points_to_win=7, p_win_one_point=self.p_win_one_point)
            self.memo[score] = self.tiebreak.p_win()
            return self.memo[score]

        self.memo[score] = self.p_win_one_game * self.p_win_intermediate(pts1+1, pts2) + (1-self.p_win_one_game) * self.p_win_intermediate(pts1, pts2+1)
        if self.verbose:
            print(f'Probability of winning {pts1}-{pts2} set: {self.memo[score]}')
        return self.memo[score]
    
    def p_win(self):
        return self.p_win_intermediate(0, 0)
tennis_set = TennisSet(p_win_one_point=0.55, points_to_win_game=4, games_to_win_set=6, verbose=True)
print(tennis_set.p_win())
class TennisMatch:
    def __init__(self, p_win_one_point, points_to_win_game, games_to_win_set, sets_to_win_match, verbose=False):
        self.p_win_one_point = p_win_one_point
        self.points_to_win_game = points_to_win_game
        self.games_to_win_set = games_to_win_set
        self.sets_to_win_match = sets_to_win_match
        self.verbose = verbose
        self.memo = {}
        
        self.tennisSet = TennisSet(self.p_win_one_point, self.points_to_win_game, self.games_to_win_set)
        self.p_win_one_set = self.tennisSet.p_win()
    
    def p_win_intermediate(self, pts1, pts2):
        score = (pts1, pts2)
        
        if score in self.memo:
            return self.memo[score]

        if pts1 >= self.sets_to_win_match:
            self.memo[score] = 1
            return self.memo[score]

        if pts2 >= self.sets_to_win_match:
            self.memo[score] = 0
            return self.memo[score]

        self.memo[score] = self.p_win_one_set * self.p_win_intermediate(pts1+1, pts2) + (1-self.p_win_one_set) * self.p_win_intermediate(pts1, pts2+1)
        if self.verbose:
            print(f'Probability of winning {pts1}-{pts2} match: {self.memo[score]}')
        return self.memo[score]
    
    def p_win(self):
        return self.p_win_intermediate(0, 0)
%%time
mens_tennis_match = TennisMatch(p_win_one_point=0.55, 
                                points_to_win_game=4, 
                                games_to_win_set=6, 
                                sets_to_win_match=3, 
                                verbose=True)
print(f'Probability of winning men\'s match: {mens_tennis_match.p_win()*100:.7f}%')
%%time
womens_tennis_match = TennisMatch(p_win_one_point=0.55, 
                                  points_to_win_game=4, 
                                  games_to_win_set=6, 
                                  sets_to_win_match=2, 
                                  verbose=True)
print(f'Probability of winning women\'s match: {womens_tennis_match.p_win()*100:.7f}%')
print(f'Probability of winning men\'s tournament: {mens_tennis_match.p_win()**7*100:.7f}%')
print(f'Probability of winning women\'s tournament: {womens_tennis_match.p_win()**7*100:.7f}%')
%%time
p_win_mens_match, p_win_womens_match = {}, {}
for p_win_one_point in np.linspace(0,1,1000):
    mens_tennis_match = TennisMatch(p_win_one_point=p_win_one_point, 
                                    points_to_win_game=4, 
                                    games_to_win_set=6, 
                                    sets_to_win_match=3)
    p_win_mens_match[p_win_one_point] = mens_tennis_match.p_win()
    
    womens_tennis_match = TennisMatch(p_win_one_point=p_win_one_point, 
                                      points_to_win_game=4, 
                                      games_to_win_set=6, 
                                      sets_to_win_match=2)
    p_win_womens_match[p_win_one_point] = womens_tennis_match.p_win()
plt.figure(figsize=(12,8))
plt.plot(p_win_mens_match.keys(), p_win_mens_match.values(), label='P(Win Men\'s match)')
plt.plot(p_win_womens_match.keys(), p_win_womens_match.values(), label='P(Win Women\'s match)')
plt.plot(p_win_mens_match.keys(), [i**7 for i in p_win_mens_match.values()], label='P(Win Men\'s tournament)')
plt.plot(p_win_womens_match.keys(), [i**7 for i in p_win_womens_match.values()], label='P(Win Women\'s tournament)')
plt.grid()
plt.xlabel('Probability of winning one point')
plt.ylabel('Win probability')
plt.title('Win probabilities')
plt.legend(loc='best')
plt.xlim(0.4, 0.7)
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10454908220271349, 'fraction_numerical': 0.024441340782122904, 'mean_word_length': 2.9175459163735833, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '427283', 'license': 'MIT', 'n_tokens_mistral': 3591, 'n_tokens_neox': 3414, 'n_words': 896, 'path': '2019_09_07.ipynb', 'repo_name': 'rygao/Riddler538', 'size': '10036'}