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)