File size: 5,438 Bytes
a797471
 
 
 
 
dbad5ad
a797471
f57d6bf
a797471
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbad5ad
a797471
 
 
 
 
 
 
 
 
 
 
 
 
dbad5ad
 
a797471
 
 
 
dbad5ad
a797471
 
 
 
 
 
e64c363
a797471
 
 
 
 
 
 
 
dbad5ad
 
 
 
 
 
 
 
 
 
a797471
dbad5ad
 
 
 
 
 
 
 
 
a797471
 
dbad5ad
 
 
 
a797471
dbad5ad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a797471
 
 
 
 
 
dbad5ad
 
 
a797471
 
 
 
 
 
 
 
 
 
dbad5ad
f57d6bf
 
 
 
 
 
 
 
 
 
a797471
 
 
 
 
 
 
 
 
 
e64c363
dbad5ad
 
 
 
a797471
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
from collections import defaultdict


import streamlit as st
import streamlit.components.v1 as components
import plotly.graph_objects as go
import plotly.express as px
import numpy as np


st.set_page_config(page_title='Can you be truly random ?', layout = 'wide', page_icon = 'favicon.jpg', initial_sidebar_state = 'auto')



# Custom CSS to styles
st.markdown("""
    <style>
        button {
            padding-top: 50px !important;
            padding-bottom: 50px !important;
        }
    </style>
""", unsafe_allow_html=True)


max_history = 10

def set_state(x):
    if x == 1: st.toast('The journey begins!', icon='😍')
    st.session_state.stage = x

        
def reset_game():
    set_state(0)
    if 'n_buttons' not in st.session_state:
        st.session_state.n_buttons = 2
    st.session_state.history = []
    st.session_state.preds = defaultdict(lambda: defaultdict(int))
    st.session_state.pnl = [0]
    st.session_state.min = [0]
    st.session_state.max = [0]

if 'stage' not in st.session_state:
    reset_game()

st.title('Can your brain be random?')

if st.session_state.stage == 0:
    st.button('Begin', on_click=set_state, args=[1],use_container_width=True)
    
    st.session_state.n_buttons = st.slider(label="How many buttons to play with?", min_value=2, max_value=5,value=2)
    
    st.markdown(f'You will be presented with {st.session_state.n_buttons} buttons to randomly choose from')
    
    st.markdown("At each round, I will try to predict which button you click :-)")
    st.markdown(f"If I get it right, I earn {st.session_state.n_buttons-1} point(s), otherwise you earn 1 point")
    st.markdown(f"Play as long as you want and try to beat me!")
    
        
    

# def get_prev_seqs(history):
#     # st.write(history)
    
#     seqs = []
#     for h in range(max_history+1):
#         if len(history) >= h:
#             previous_seq = ''.join(history[-h:]) if h > 0 else ""
#             # st.write(previous_seq)
#             seqs.append(previous_seq) # from small to largest
#     return seqs
    
# def refresh_preds():
#     played = st.session_state.history[-1]
#     seqs = get_prev_seqs(st.session_state.history[:-1])
#     for seq in seqs:
#         # st.write(f'"{seq}"',played)
#         st.session_state.preds[seq][played] += 1

def make_pred(max_history=max_history,alpha=0.5):
    history = st.session_state.history
    
    denominator = 0
    scores = {str(i):0 for i in range(st.session_state.n_buttons)}
    recent = np.array(history[-max_history:])
    for i in range(1,len(history)):
        past = np.array(history[-i-max_history:-i])
        
        played = history[-i]
        decay = np.exp(-alpha*np.linspace(1,0,len(past)))
        similarity = ((past == recent[-len(past):])*decay).sum() / decay.sum()
        weight = len(history)/(len(history)+i) * similarity
        scores[played] += weight
        denominator += weight
    if not denominator:
        return get_random_play()
    result = {str(i):scores[str(i)]/denominator for i in range(st.session_state.n_buttons)}
    return result
    
def get_random_play():
    return {str(i):1/st.session_state.n_buttons for i in range(st.session_state.n_buttons)}

# def make_pred():
#     seqs = get_prev_seqs(st.session_state.history)
#     # st.write('seqs',seqs)
#     scores = defaultdict(float)
#     denominator = 0
#     for i,seq in enumerate(seqs):
#         weight = (i+1)**2
#         preds = st.session_state.preds[seq]
#         total = sum(preds.values())
        
#         if total:
#             for played,value in preds.items():
#                 scores[played] += weight*value/total
#             denominator += weight
#     if denominator:
#         scores = {played:value/denominator for played,value in scores.items()}
#         return scores
#     else:
#         return get_random_play()
        

def update_pnl(user_win):
    current_score = st.session_state.pnl[-1]
    current_score += -1 if user_win else (st.session_state.n_buttons-1)
    st.session_state.pnl.append(current_score)
    expected_change = (2**0.5) * ((st.session_state.n_buttons-1)/st.session_state.n_buttons)
    st.session_state.min.append(st.session_state.min[-1]-expected_change)
    st.session_state.max.append(st.session_state.max[-1]+expected_change)
    
def user_select(i,choice):
    st.session_state.history.append(str(i))
    if i == choice:
        st.toast("I win!", icon='πŸ€ͺ')
        update_pnl(user_win=False)
    else:
        st.toast('Well done!', icon='😍')
        update_pnl(user_win=True)
    
    # refresh_preds()

def compute_perf():
    data = np.array(st.session_state.pnl)
    if len(data) > 1:
        data = data[1:] - data[:-1]
        perf = data.mean()
        std = data.std()
        win = (data>0).mean()
        sharpe = perf / std
        return f'%age win={win:.2%} Sharpe={sharpe:.2f}'
    

if st.session_state.stage == 1:
    pred = make_pred()
    choice = max(pred,key=pred.get)
    
    cols = st.columns(st.session_state.n_buttons)
    for i,col in enumerate(cols):
        col.button(str(i),on_click=user_select, args=[str(i),choice],
        use_container_width=True)

    st.subheader(f'My earnings so far... {compute_perf()} :-)')
    fig = px.line(st.session_state.pnl)
    fig.update_layout(showlegend=False)
    st.plotly_chart(fig, use_container_width=True)
    
    st.button('Start over', on_click=reset_game, args=[],
        use_container_width=True)