diff --git a/bossfight-webapp/assets/current_roll.jpg b/bossfight-webapp/assets/current_roll.jpg new file mode 100644 index 0000000..da8a79c Binary files /dev/null and b/bossfight-webapp/assets/current_roll.jpg differ diff --git a/bossfight-webapp/assets/dice-fpga.png b/bossfight-webapp/assets/dice-fpga.png new file mode 100644 index 0000000..84b0c7e Binary files /dev/null and b/bossfight-webapp/assets/dice-fpga.png differ diff --git a/bossfight-webapp/assets/dice-pc.png b/bossfight-webapp/assets/dice-pc.png new file mode 100644 index 0000000..518777f Binary files /dev/null and b/bossfight-webapp/assets/dice-pc.png differ diff --git a/bossfight-webapp/assets/oogie.gif b/bossfight-webapp/assets/oogie.gif new file mode 100644 index 0000000..7f79dc3 Binary files /dev/null and b/bossfight-webapp/assets/oogie.gif differ diff --git a/bossfight-webapp/assets/previous_roll.jpg b/bossfight-webapp/assets/previous_roll.jpg new file mode 100644 index 0000000..5c29818 Binary files /dev/null and b/bossfight-webapp/assets/previous_roll.jpg differ diff --git a/bossfight-webapp/dice_roll_data.csv b/bossfight-webapp/dice_roll_data.csv new file mode 100644 index 0000000..deb5e04 --- /dev/null +++ b/bossfight-webapp/dice_roll_data.csv @@ -0,0 +1,73 @@ +id,roll +1,6 +2,3 +3,1 +4,5 +5,5 +6,2 +7,4 +8,4 +9,4 +10,6 +11,4 +12,2 +13,2 +14,2 +15,4 +16,6 +17,5 +18,6 +19,2 +20,3 +21,6 +22,5 +23,1 +24,4 +25,3 +26,3 +27,3 +28,3 +29,5 +30,1 +31,5 +32,6 +33,1 +34,4 +35,3 +36,1 +37,2 +38,2 +39,2 +40,1 +41,2 +42,6 +43,1 +44,3 +45,4 +46,1 +47,4 +48,2 +49,4 +50,2 +51,2 +52,3 +53,5 +54,2 +55,1 +56,2 +57,3 +58,2 +59,1 +60,5 +61,2 +62,4 +63,5 +64,4 +65,3 +66,3 +67,2 +68,5 +69,3 +1,2 +2,5 +3,3 \ No newline at end of file diff --git a/bossfight-webapp/info.txt b/bossfight-webapp/info.txt new file mode 100644 index 0000000..a2beb90 --- /dev/null +++ b/bossfight-webapp/info.txt @@ -0,0 +1 @@ +13019,76.81,1.73 \ No newline at end of file diff --git a/bossfight-webapp/main.py b/bossfight-webapp/main.py new file mode 100644 index 0000000..b96cefa --- /dev/null +++ b/bossfight-webapp/main.py @@ -0,0 +1,375 @@ +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)