Setup¶

In [1]:
from matplotlib.patches import Polygon as mpP
import matplotlib.pyplot as plt
from matplotlib.ticker import PercentFormatter as PF
import plotly.graph_objects as go
import plotly.express as px
import plotly.io as pio
import itertools
import numpy as np
import pandas as pd
from scipy.spatial import ConvexHull as CH
import seaborn as sns

Constants & Functions¶

In [2]:
v = ["#73D055",
     "#2D708E",
     "#440154"]





def fiddlerPlot() :
    sns.set()
    fig = plt.figure(figsize = (10, 10))
    ax = fig.add_subplot(xlim = (0, 1),
                         ylim = (0, 1))

    #Title setup.
    ax.set_title("Tour de Fiddler", fontsize = 28)
    
    #X-axis setup.
    ax.set_xlabel("Opponent Leg Feels", fontsize = 24)
    ax.xaxis.set_major_formatter(PF(xmax = 1))

    #Y-axis setup.
    ax.set_ylabel("Your Leg Feels", fontsize = 24)
    ax.yaxis.set_major_formatter(PF(xmax = 1))
    
    ax.tick_params(labelsize = 18)
       
    ax.add_patch(mpP([(0, 0), (0, 1), (1, 1), (0.5, 0.5), (0.5, 0)], label = "You Win", ec = v[0], fc = v[0]))
    ax.add_patch(mpP([(0.5, 0), (0.5, 0.5), (1, 1), (1, 0), (0.5, 0)], label = "Opponent Wins", ec = v[2], fc = v[2]))
    
    ax.legend(loc = 'lower center', bbox_to_anchor = (0.5, -0.175), ncol = 2, fancybox = True, shadow = True, fontsize = 18)

    fig.savefig("2025.07.25Fiddler.png", bbox_inches = 'tight')


    
    
    
def getVertices(d) :
        
    return(d[CH(d).vertices]) 
    
    
    
    

def ECplot() :
    # Array of cube.
    E = np.array(list(itertools.product(np.arange(0, 1.1, 0.5), repeat = 3)))
    
    # Dotted Lines.
    df = pd.DataFrame(E[[10, 1, 4, 3, 12, 9, 10, 13, 26, 24, 12, 13, 4, 8, 26, 20, 10]],
                      columns = ['x', 'y', 'z'])
    df['hoverdata'] = df.apply(lambda row: f"Your Leg Feels :               {str(int(row['z']*100))+'%'}<br>Opponent 1's Leg Feels : {str(int(row['y']*100))+'%'}<br>Opponent 2's Leg Feels : {str(int(row['x']*100))+'%'}", axis=1)

    #You.
    you_0 = E[[0, 1, 3, 4, 9, 10, 12, 13]]
    you_1 = E[[1, 2, 4, 8, 10, 13, 20, 26]]
    y_0 = [np.concatenate((getVertices(you_0),
                           getVertices(you_1))),
           v[0],
           'Your Win']
    
    #Opponent 1.
    opp_1 = E[[3, 4, 6, 8, 12, 13, 24, 26]]
    o_1 = [getVertices(opp_1),
           v[2],
           'Opp. 1 Win']

    #Opponent 2.
    opp_2 = E[[9, 10, 12, 13, 18, 20, 24, 26]]
    o_2 = [getVertices(opp_2),
           v[1],
           'Opp. 2 Win']

    #Graph Object.
    fig = go.Figure()

    #Add the regions where each bicyclist dominates over the other two.
    for m in [y_0, o_1, o_2] :
        fig.add_trace(go.Mesh3d(x = m[0][:, 0], 
                                y = m[0][:, 1], 
                                z = m[0][:, 2], 
                                color = m[1], 
                                opacity = 0.5,
                                alphahull = 0,
                                hoverinfo = 'skip',
                                name = m[2],
                                showlegend = True))

    #Border edges through space.
    edges = px.line_3d(df,
                       x = 'x',
                       y = 'y',
                       z = 'z',
                       custom_data = ['hoverdata'])
    fig.add_trace(edges.data[0])
    fig.data[3].update(#selector = {'type' : 'trace'},
                       hovertemplate = "%{customdata[0]}",
                       line = {'width' : 5,
                               'color' : 'black',
                               'dash' : 'dot'})        
        
        
        
    #Set the perspective/viewpoint.
    camera = {'up' : {'x' : 0, 'y' : 0, 'z' : 1},
              'center' : {'x' : 0, 'y' : 0, 'z' : 0},
              'eye' : {'x' : -2.1, 'y' : -1.5, 'z' : 0.25}}
              #'eye' : {'x' : 1.55, 'y' : -1.55, 'z' : 1.35}}

    #Update layout.
    fig.update_layout(title = {'text': 'Tour de Fiddler',
                               'font_size': 36,
                               'x': 0.5,
                               'xanchor': 'center'},
                      scene = {'xaxis' : {'title' : "Opponent 2's Leg Feels",
                                          'tickformat' : '.0%'},
                               'yaxis' : {'title' : "Opponent 1's Leg Feels",
                                          'tickformat' : '.0%'},
                               'zaxis' : {'title' : "Your Leg Feels",
                                          'tickformat' : '.0%'}},
                      legend = {'font' : {'size' : 18}},
                      scene_camera = camera,
                      autosize = False,
                      width = 800,
                      height = 800)

    pio.write_html(fig,
                   file = "2025.07.25EC.html",
                   auto_open = True)
    
    
    
    
def heatMap() :
    #Threshold percent of leg feels, number of opponents, your probability of winning.
    data = pd.DataFrame(columns = ['p',
                                   'n',
                                   'y_w'])

    for p in [i/1000 for i in range(0, 1001)] :
        for n in [j for j in range(0, 101)]:
             
            #You winning is the probability region where you sprint under everyone's threshold + your fraction of the remaining.
            temp = p**(n+1)
            you = temp + (1-temp) / (n+1)
            data.loc[len(data)] = [p, n, you]
    
    sns.set()
    fig = plt.figure(figsize = (12, 8))
    ax = fig.add_subplot(xlim = (0, 100),
                         ylim = (0, 1))
       
    heatmap = plt.scatter(x = data['n'],
                          y = data['p'],
                          c = data['y_w'],
                          cmap = 'viridis_r',
                          marker = "s",
                          s = 120,
                          alpha = 0.8)
    
   #Title setup.
    ax.set_title('Tour de Fiddler Scenarios', fontsize = 24)

    #X-axis setup.
    ax.set_xlabel("# of Opponents", fontsize = 22)
    #Y-axis setup.
    ax.set_ylabel("Opponent's Manager's Threshold", fontsize = 22)
    ax.yaxis.set_major_formatter(PF(xmax = 1))
    ax.tick_params(axis = 'both', which = 'major', labelsize = 18)

    #Colorbar.
    cb = plt.colorbar(heatmap,
                      format = '%.2f')
    
    cb.set_label('Probability of You Winning',
                 labelpad = -90,
                 rotation = 90,
                 fontsize = 22)
    cb.ax.tick_params(labelsize = 18)

    fig.savefig("2025.07.25Other.png", bbox_inches = 'tight')    

Extra Credit Plot¶

In [3]:
fiddlerPlot()
In [4]:
ECplot()
In [5]:
heatMap()
Rohan Lewis¶

2025.07.28¶