File size: 7,457 Bytes
8bf595d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c9d7b4f
 
8bf595d
c9d7b4f
 
 
8bf595d
 
 
 
9b98acb
8bf595d
 
 
c9d7b4f
8bf595d
 
 
 
 
 
c9d7b4f
 
8bf595d
 
 
 
 
 
 
 
 
 
 
c9d7b4f
 
 
 
 
 
 
 
 
 
 
 
 
6281787
 
 
 
 
c9d7b4f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6281787
c9d7b4f
 
 
d87b3b3
ab23f0d
d87b3b3
ab23f0d
d87b3b3
 
c9d7b4f
 
 
 
 
 
 
9ba5d52
c9d7b4f
 
9ba5d52
c9d7b4f
 
9ba5d52
c9d7b4f
 
 
 
 
 
 
8bf595d
 
 
 
 
 
24463e1
8bf595d
 
 
c9d7b4f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
from __future__ import annotations

#%%
import os
# os.environ.setdefault("GRADIO_SERVER_PORT", "1080")
# os.environ.setdefault("TEXTGAMES_SHOW_HIDDEN_LEVEL", "1")
os.environ.setdefault("TEXTGAMES_LOADGAME_DIR", "problemsets")
os.environ.setdefault("TEXTGAMES_LOADGAME_ID", "42")
os.environ.setdefault("TEXTGAMES_MOCKUSER", "")
os.environ.setdefault("TEXTGAMES_OUTPUT_DIR", "user_outputs")
favicon_path = "textgames-scrabble-black2-ss.png"

#%%
from play_helper import css, declare_components, start_new_game, check_played_game, download_from_drive, upload_to_drive, _leaderboards
import pandas as pd
import gradio as gr
import random
import json
from textgames import GAME_NAMES


#%%
os.makedirs(os.getenv('TEXTGAMES_OUTPUT_DIR', '.'), exist_ok=True)


#%%
def generate_sid(fp):
    rand_int = random.randint(0, 1000)
    with open(fp, "w", encoding="utf8") as f:
        f.write(f"session_{rand_int:04}\n")
    upload_to_drive(fp, mime_type="text/plain", update=True)


#%%
def get_sid(uid, force_generate_sid=False):
    fp = f"{os.getenv('TEXTGAMES_OUTPUT_DIR')}/{uid}_sid.txt"
    if force_generate_sid:
        generate_sid(fp)
    if not os.path.exists(fp):
        download_from_drive(fp, mime_type="text/plain", compare_checksum=False)
    if not os.path.exists(fp):
        generate_sid(fp)
    with open(fp, "r", encoding="utf8") as f:
        sid = [_ for _ in f][-1]
    return sid.strip()


#%%
def greet(request: gr.OAuthProfile | None):
    user = {'email': os.getenv('TEXTGAMES_MOCKUSER', ''), 'name': ""}
    if request is not None:
        user = {'email': request.username, 'name': request.name, 'sid': get_sid(request.username)}
    return f"""
        Welcome to TextGames, {user['name'] or 'please login'}!
    """, user, user['email']


#%%
with gr.Blocks(title="TextGames", css=css, delete_cache=(3600, 3600)) as demo:
    ((m, logout_btn, solved_games_df, game_radio, level_radio, new_game_btn, render_toggle, reset_sid_btn),
     (session_state, is_solved, solved_games, user_state, uid_state),
     ) = declare_components(demo, greet, use_login_button=True)
    logout_btn.activate()

    reset_sid_checkbox = gr.Checkbox(False, visible=False, interactive=False)
    reset_sid_btn.click(
    #     lambda: [gr.update(interactive=False)]*2, None, [reset_sid_btn, new_game_btn]
    # ).then(
        lambda x: x, [reset_sid_checkbox], [reset_sid_checkbox],
        js="(x) => confirm('Only your best session is recorded on the leaderboard. Are you sure you want to start from the beginning? (cannot be undone)')"
    # ).then(
    #     lambda: [gr.update(interactive=True)]*2, None, [reset_sid_btn, new_game_btn]
    )

    def _resetting(confirmed, user):
        uid = user.get('email', None) if isinstance(user, dict) else None
        if not uid:
            gr.Warning("You need to log in first!")
        elif confirmed:
            user['sid'] = get_sid(uid, force_generate_sid=True)
            gr.Info("Successfully resets the game with new session. Enjoy the game! πŸ’ͺ")
        return user, False
    reset_sid_checkbox.change(
        lambda: [gr.update(interactive=False)]*3, None, [logout_btn, reset_sid_btn, new_game_btn]
    ).then(
        _resetting, [reset_sid_checkbox, user_state], [user_state, reset_sid_checkbox]
    ).then(
        check_played_game, [user_state, solved_games, solved_games_df], [solved_games, solved_games_df]

    ).then(
        lambda: [gr.update(interactive=True)]*3, None, [logout_btn, reset_sid_btn, new_game_btn]
    )


    @gr.render(inputs=[game_radio, level_radio, user_state, session_state, uid_state], triggers=[render_toggle.change])
    def _start_new_game(game_name, level, user, _session_state, _uid_state):
        if _session_state in [1, 2]:
            start_new_game(game_name, level, session_state, is_solved, solved_games, user=user, uid=_uid_state)

#%%
with (demo.route("Leaderboards", "/leaderboards") as demo_leaderboard):
    # gr.Markdown("Under Construction. Will be available soon.")
    def reload_leaderboard():
        ret_leaderboards = {}

        def add_dummies():
            return pd.DataFrame({
                'User': ['dummy'],
                'Solved': [sorted([g.split('\t', 1)[0] for g in GAME_NAMES])],
                'Attempts': [888],
                'Time': [8888.8888],
            })

        def sort_df(_cur_df):
            return _cur_df.sort_values(["Solved", "Attempts", "Time"], key=lambda c: {
                    "Solved": lambda s: -s.apply(len),
                }.get(c.name, lambda s: s)(c))

        if not os.path.exists(_leaderboards):
            for lv in ['1', '2', '3']:
                ret_leaderboards[lv] = add_dummies()

        else:
            datas = []
            with open(_leaderboards, "r", encoding="utf8") as f:
                for line in f:
                    datas.append(json.loads(line))
            concat = [{'Level': d['difficulty_level'], 'User': d['uid'], 'Session': d['sid'],
                       'Solved': d['game_name'].split('\t', 1)[0], 'Attempts': d['turns'], "Time": d['ed'] - d['st']
                       } for d in datas]
            df_leaderboards_all = pd.DataFrame(concat)

            def get_best(_cur_df):
                def _per_session(_df):
                    best = _df.groupby("Solved").apply(
                        lambda _df: _df.sort_values(["Attempts", "Time"]).iloc[0]
                    ).reset_index(drop=True)
                    ret = pd.DataFrame({
                        "Solved": [sorted(best.Solved.unique())], "Attempts": best.Attempts.sum(), "Time": best.Time.sum(),
                    })
                    return ret
                flat = _cur_df.groupby("Session").apply(_per_session)
                return sort_df(flat).iloc[0]

            for lv in ['1', '2', '3']:
                cur_df = df_leaderboards_all.loc[df_leaderboards_all.Level.eq(lv)].groupby("User").apply(get_best)
                cur_df = (
                    (sort_df(cur_df.reset_index()) if len(cur_df) else add_dummies()).rename({"Attempts": "Turns"}, axis=1)
                    .rename_axis("Rank").reset_index()
                )
                cur_df["Rank"] = list(range(1, len(cur_df)+1))
                ret_leaderboards[lv] = cur_df

        return ret_leaderboards

    df_leaderboards = {}

    # for lv, tab_name in [('1', "πŸš… Easy"), ('2', "πŸš€ Medium"), ('3', "πŸ›Έ Hard")]:
    with gr.Tab("πŸš… Easy") as tab1:
        lb_df_1 = gr.DataFrame(label="Rankings", col_count=(5, 'fixed'), interactive=False, show_search='filter')
        tab1.select(lambda: df_leaderboards['1'], None, [lb_df_1])
    with gr.Tab("πŸš€ Medium") as tab2:
        lb_df_2 = gr.DataFrame(label="Rankings", col_count=(5, 'fixed'), interactive=False, show_search='filter')
        tab2.select(lambda: df_leaderboards['2'], None, [lb_df_2])
    with gr.Tab("πŸ›Έ Hard") as tab3:
        lb_df_3 = gr.DataFrame(label="Rankings", col_count=(5, 'fixed'), interactive=False, show_search='filter')
        tab3.select(lambda: df_leaderboards['3'], None, [lb_df_3])

    def onload(progress=gr.Progress()):
        global df_leaderboards
        df_leaderboards = reload_leaderboard()
        return df_leaderboards['1']
    demo_leaderboard.load(onload, None, [lb_df_1])


#%%
# demo.launch()
demo.launch(
    favicon_path=favicon_path if os.path.exists(favicon_path) else None,
    show_api=False, enable_monitoring=False, pwa=False,
)


#%%

#%%


#%%


#%%


#%%


#%%