import dash from dash import dcc, html import dash_bootstrap_components as dbc from dash.dependencies import Input, Output import pandas as pd import plotly.express as px from datetime import datetime, timedelta import itertools from PIL import Image # Use the Dash Bootstrap Components theme app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) # Load the initial data from the dice_roll_data.csv file into a Pandas DataFrame df = pd.read_csv('dice_roll_data.csv') # Calculate initial statistics result_counts = df['roll'].value_counts().sort_index() total_rolls = result_counts.sum() # Assuming df is the DataFrame containing the rolls current_roll = df['roll'].iloc[-1] if not df.empty else 0 # Default to 0 if the DataFrame is empty # Count consecutive occurrences of the current roll value consecutive_count = sum(1 for _ in itertools.takewhile(lambda x: x == current_roll, reversed(df['roll']))) # Initial health value, adjust as needed progress_value = 2000 max_progress_value = 2000 max_damage = 0 # Create initial figure with px.bar fig = px.bar(y=result_counts.index, x=result_counts.values, labels={'x': 'Count', 'y': 'Result'}, orientation='h') # Update layout for the bar chart to make the axis titles and tick labels larger fig.update_xaxes(tickfont=dict(size=24), title_font=dict(size=28)) fig.update_yaxes(tickfont=dict(size=24), title_font=dict(size=28)) # Add title to the bar chart # fig.update_layout(title_text="Allo", title_font=dict(size=32), title_x=0.5) # Calculate the end time for the countdown timer (December 2, 2023, at 3:30 pm) end_time = datetime(2023, 12, 2, 15, 30, 0) # Function to find the maximal consecutive occurrence of a given roll value def find_max_consecutive_occurrence(series, roll_value): max_consecutive_occurrence = 0 current_consecutive_occurrence = 0 for i in range(1, len(series)): if series.iloc[i] == roll_value and series.iloc[i - 1] == roll_value and (series.index[i] - series.index[i - 1]) == 1: current_consecutive_occurrence += 1 elif series.iloc[i] == roll_value: current_consecutive_occurrence = 1 else: current_consecutive_occurrence = 0 max_consecutive_occurrence = max(max_consecutive_occurrence, current_consecutive_occurrence) return max_consecutive_occurrence def calculate_cumulative_damage(rolls): cumulative_damage = [] current_value = None consecutive_count = 0 for roll in rolls: if roll == current_value: consecutive_count += 1 else: consecutive_count = 1 current_value = roll damage = roll * (2 ** (consecutive_count - 1)) cumulative_damage.append(damage) return cumulative_damage # Function to read values from the info.txt file def read_info_file(): try: with open('info.txt', 'r') as file: lines = file.readlines() if lines: values = lines[0].strip().split(',') if len(values) == 3: inference_rate, latency, power_consumption = map(float, values) return inference_rate, latency, power_consumption except Exception as e: print(f"Error reading info.txt: {e}") return None, None, None # Calculate the maximal consecutive occurrence for each roll max_consecutive_occurrences = {roll: find_max_consecutive_occurrence(df['roll'], roll) for roll in range(1, 7)} # Define layout using Dash components and Dash Bootstrap Components app.layout = dbc.Container( fluid=True, children=[ # Row 1: Cells 1 and 2 dbc.Row([ dbc.Col( dbc.Card( dbc.CardBody( [ html.Img(src=dash.get_asset_url('oogie.gif'), alt='Oogie Boogie', className='img-fluid mx-auto my-auto', style={'width': '75%', 'max-height': '100%', 'border-radius': '25px'}), dbc.Progress( id='progress-bar', value=progress_value, color="success", max=max_progress_value, style={'margin-top': '20px', 'height': '30px', 'background-color': 'red'}, ), html.P(f'Health: {progress_value:.0f}/{max_progress_value}', id='health-text', className='lead text-center fw-bold', style={'font-size': '24px', 'margin-top': '10px'}), ], className='text-center' ), className='border p-3', style={'height': '100%'} ), width=6 ), dbc.Col( # Cell 2: Countdown, System infos, and Table with max consecutive occurrences for each roll dbc.Card( [ dcc.Interval( id='interval-component', interval=1*1000, # in milliseconds n_intervals=0 ), html.P(id='countdown-timer', className='lead text-center fw-bold', style={'font-size': '32px'}), dbc.CardBody( [ # Add new text and values from info.txt html.Div( [ html.P(id='inference-rate-text', className='lead text-center fw-bold', style={'font-size': '24px', 'margin-right': '10px'}), html.P(id='latency-text', className='lead text-center fw-bold', style={'font-size': '24px', 'margin-right': '10px'}), html.P(id='power-consumption-text', className='lead text-center fw-bold', style={'font-size': '24px'}), ], style={'display': 'flex', 'align-items': 'center', 'justify-content': 'center', 'margin-top': '20px'}, ), html.Div( [ html.Img(src=dash.get_asset_url('dice-pc.png'), alt='Actuel', className='img-fluid mx-auto my-auto', style={'width': '50%', 'max-height': '100%', 'border-radius': '25px'}), ], style={'display': 'flex', 'align-items': 'center', 'justify-content': 'center'}, ), html.Div( [ html.Img(src=dash.get_asset_url('dice-fpga.png'), alt='Cible', className='img-fluid mx-auto my-auto', style={'width': '60%', 'max-height': '100%', 'border-radius': '25px'}), ], style={'display': 'flex', 'align-items': 'center', 'justify-content': 'center', 'margin-top': '20px'}, ), # html.Img(src=dash.get_asset_url('dice-pc.png'), alt='Actuel', className='img-fluid mx-auto my-auto', style={'width': '55%', 'max-height': '100%', 'border-radius': '25px'}), # html.Img(src=dash.get_asset_url('dice-fpga.png'), alt='Cible', className='img-fluid mx-auto my-auto', style={'width': '5%', 'max-height': '100%', 'border-radius': '25px'}), # dbc.Table( # id='max-consecutive-occurrence-table', # children=[ # html.Thead( # html.Tr([html.Th('Roll', style={'font-size': '28px'}), html.Th('Max Consecutive Occurrence', style={'font-size': '28px'})]) # ), # html.Tbody( # [html.Tr([html.Td(f'{roll}', style={'font-size': '24px'}), html.Td(f'{max_consecutive_occurrences[roll]}', style={'font-size': '24px'})]) for roll in range(1, 7)] # ) # ], # className='text-center', # style={'margin-top': '20px'} # ), ], className='text-center' ), ], className='border p-3', style={'height': '100%'} ), width=6 ) ], align='stretch', style={'margin-bottom': '20px'}), # Row 2: Cells 3 and 4 dbc.Row([ dbc.Col( # Cell 3 dbc.Card( dbc.CardBody( [ html.Div( [ html.Div( [ html.H5("Previous Roll", className="card-title fw-bold", style={'margin-bottom': '10px', 'font-size': '32px'}), html.Img(id='previous-roll-image', alt='previous roll image', className='img-fluid mx-auto my-auto', style={'width': '70%', 'max-height': '100%', 'border-radius': '15px'}), ], style={'padding-bottom': '20px', 'padding-right': '75px'} ), html.Div( [ html.H5("Current Roll", className="card-title fw-bold", style={'margin-bottom': '10px', 'font-size': '32px'}), html.Img(id='current-roll-image', alt='current roll image', className='img-fluid mx-auto my-auto', style={'width': '70%', 'max-height': '100%', 'border-radius': '15px'}), ], style={'padding-bottom': '20px'} ), ], style={'display': 'flex', 'justify-content': 'center', 'margin-top': '75px'} ), html.P(id='consecutive-count-text', className='lead text-center fw-bold', style={'font-size': '24px'}), html.P(id='damage-dealt-text', className='lead text-center fw-bold', style={'font-size': '24px'}), html.P(id='max-damage-text', className='lead text-center fw-bold', style={'font-size': '24px'}), ], className='text-center' ), className='border p-3', style={'height': '100%'} ), width=6 ), dbc.Col( # Cell 4: Bar Chart and Probability Table dbc.Card( [ dbc.CardBody( [ html.P(id='total-rolls-text', className='lead text-center fw-bold', style={'font-size': '32px'}), html.Div( [ # Bar Chart dcc.Graph( id='bar-chart', figure=fig, style={'height': '80%', 'width': '70%', 'display': 'inline-block'} ), # Probability Table dbc.Table( id='probability-table', style={'margin-top': '20px', 'width': '30%', 'display': 'inline-block'}, ), ], style={'width': '100%', 'display': 'flex', 'justify-content': 'space-between'} ), ], className='text-center' ), ], # Cell 4 className='border p-3', style={'height': '100%'} ), width=6 ) ], align='stretch') ] ) # Callback to update countdown timer text @app.callback(Output('countdown-timer', 'children'), [Input('interval-component', 'n_intervals')]) def update_timer(n_intervals): time_left = end_time - datetime.now() days, seconds = divmod(time_left.total_seconds(), 86400) hours, remainder = divmod(seconds, 3600) minutes, seconds = divmod(remainder, 60) # Format components with leading zeros formatted_hours = str(int(hours)).zfill(2) formatted_minutes = str(int(minutes)).zfill(2) formatted_seconds = str(int(seconds)).zfill(2) return f'Temps restant: {formatted_hours}:{formatted_minutes}:{formatted_seconds}' # Callback to update data every second @app.callback([Output('bar-chart', 'figure'), Output('total-rolls-text', 'children'), # Output('max-consecutive-occurrence-table', 'children'), Output('probability-table', 'children'), Output('inference-rate-text', 'children'), Output('latency-text', 'children'), Output('power-consumption-text', 'children'), Output('consecutive-count-text', 'children'), Output('damage-dealt-text', 'children'), Output('previous-roll-image', 'src'), Output('current-roll-image', 'src'), Output('progress-bar', 'value'), Output('health-text', 'children'), Output('max-damage-text', 'children'),], [Input('interval-component', 'n_intervals')]) def update_data(n_intervals): global df, max_consecutive_occurrences, total_rolls, consecutive_count, current_roll, progress_value, max_damage # Load the data from the dice_roll_data.csv file into a Pandas DataFrame df = pd.read_csv('dice_roll_data.csv') # Calculate statistics result_counts = df['roll'].value_counts().sort_index() total_rolls = result_counts.sum() # Create figure with px.bar fig = px.bar(y=result_counts.index, x=result_counts.values, labels={'x': 'Compte', 'y': 'Résultat'}, orientation='h') # Update layout for the bar chart to make the axis titles and tick labels larger fig.update_xaxes(tickfont=dict(size=24), title_font=dict(size=28)) fig.update_yaxes(tickfont=dict(size=24), title_font=dict(size=28)) # Add numbers on the bars fig.update_traces(text=result_counts.values, textposition='inside', textfont_size=20) # Calculate the maximal consecutive occurrence for each roll max_consecutive_occurrences = {roll: find_max_consecutive_occurrence(df['roll'], roll) for roll in range(1, 7)} # Calculate the total and maximal damage dealt and apply it to the progress bar total_damage_dealt = sum(calculate_cumulative_damage(df['roll'])) progress_value = max_progress_value - total_damage_dealt health_text = f'Points de vie: {progress_value:.0f}/{max_progress_value}' # Update the table in Cell 2 with the new max consecutive occurrences max_occurrence_table = [ html.Thead( html.Tr([html.Th('Résultat', style={'font-size': '28px'}), html.Th('Occurence Consécutive Max', style={'font-size': '28px'})]) ), html.Tbody( [html.Tr([html.Td(f'{roll}', style={'font-size': '24px'}), html.Td(f'{max_consecutive_occurrences[roll]}', style={'font-size': '24px'})]) for roll in range(1, 7)] ) ] # Read values from the info.txt file inference_rate, latency, power_consumption = read_info_file() # Update text in Cell 2 with values from info.txt inference_rate_text = f"Débit d'Inference: {inference_rate} fps" if inference_rate is not None else "Débit d'Inference: N/A" latency_text = f'Latence: {latency} µs' if latency is not None else 'Latence: N/A' power_consumption_text = f'Consomation Énergétique: {power_consumption} W' if power_consumption is not None else 'Consomation Énergétique: N/A' # Calculate probabilities probabilities = result_counts / total_rolls * 100 probability_table = [ html.Thead( html.Tr([html.Th('Résultat', style={'font-size': '28px'}), html.Th('Probabilités', style={'font-size': '28px'})]) ), html.Tbody( [html.Tr([html.Td(f'{roll}', style={'font-size': '24px'}), html.Td(f'{probabilities[roll]:.2f}%', style={'font-size': '24px'})]) for roll in range(1, 7)] ) ] # Update consecutive count and text current_roll = df['roll'].iloc[-1] if not df.empty else 0 consecutive_count = sum(1 for _ in itertools.takewhile(lambda x: x == current_roll, reversed(df['roll']))) consecutive_count_text = f'Vous avez lancé {current_roll} pour la {consecutive_count}{"ère" if consecutive_count == 1 else "ème"} fois de suite!' # Calculate damage dealt damage_dealt = current_roll * (2 ** (consecutive_count-1)) damage_dealt_text = f'Dégat infligé: {damage_dealt}' # Calculate max damage dealt if max_damage > damage_dealt: max_damage_text = f'Dégat Maximal: {max_damage}' else: max_damage = damage_dealt max_damage_text = f'Nouveau Dégat Maximal: {max_damage}! Bravo!' # Get image sources based on the current and previous rolls previous_roll_image_source = Image.open('assets/previous_roll.jpg') current_roll_image_source = Image.open('assets/current_roll.jpg') return fig, f'Total de tirs: {total_rolls}', probability_table, inference_rate_text, latency_text, power_consumption_text, consecutive_count_text, damage_dealt_text, previous_roll_image_source, current_roll_image_source, progress_value, health_text, max_damage_text if __name__ == '__main__': app.run_server(debug=True, host='0.0.0.0', dev_tools_hot_reload=False)