npc0 commited on
Commit
35d9400
Β·
verified Β·
1 Parent(s): 8492e7a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -83
app.py CHANGED
@@ -1,6 +1,7 @@
1
  """
2
  Life Frontier: Partner's Concerto
3
  Single-app turn-based game with SQLite state management
 
4
  """
5
 
6
  import gradio as gr
@@ -789,7 +790,7 @@ def create_ui():
789
  *A turn-based strategy game for couples*
790
  """)
791
 
792
- # Session state
793
  session_state = gr.State({
794
  'room_id': None,
795
  'player_id': None,
@@ -812,33 +813,33 @@ def create_ui():
812
  create_btn = gr.Button("πŸ†• Create Room", variant="primary")
813
  join_btn = gr.Button("πŸšͺ Join Room", variant="secondary")
814
 
815
- join_result = gr.Markdown()
816
 
817
  with gr.Tab("🎯 Game Board"):
818
  gr.Markdown("### Game Status")
819
- status_display = gr.Markdown("*No game joined*")
820
 
821
  with gr.Row():
822
  # Player 1 Panel
823
  with gr.Column():
824
  gr.Markdown("### Player 1")
825
- p1_name = gr.Textbox(label="Name", interactive=False)
826
- p1_character = gr.Textbox(label="Character", interactive=False)
827
- p1_display = gr.JSON(label="Status")
828
  with gr.Row():
829
- p1_time = gr.Number(label="⏰ Time", interactive=False)
830
- p1_money = gr.Number(label="πŸ’° Money", interactive=False)
831
  with gr.Row():
832
- p1_hp = gr.Number(label="❀️ HP", interactive=False)
833
- p1_cp = gr.Number(label="🧠 CP", interactive=False)
834
  with gr.Row():
835
- p1_stress = gr.Number(label="😰 Stress", interactive=False)
836
- p1_qol = gr.Number(label="✨ QoL", interactive=False)
837
 
838
  # Center - Actions
839
  with gr.Column():
840
  gr.Markdown("### Your Actions")
841
- your_turn_msg = gr.Markdown("*Waiting for your turn...*")
842
 
843
  gr.Markdown("**Rest Actions**")
844
  with gr.Row():
@@ -850,10 +851,11 @@ def create_ui():
850
  tasks_display = gr.Dataframe(
851
  headers=["ID", "Name", "Type", "Time", "Req (Solo)", "Req (Coop)", "Reward"],
852
  label="Public Tasks",
853
- wrap=True
 
854
  )
855
 
856
- task_select = gr.Dropdown(label="Select Task", choices=[])
857
 
858
  with gr.Row():
859
  solo_task_btn = gr.Button("▢️ Execute Solo", variant="primary")
@@ -869,7 +871,7 @@ def create_ui():
869
  # Negotiation response (for partner)
870
  with gr.Group(visible=False) as negotiation_panel:
871
  gr.Markdown("### 🀝 Partner's Cooperation Request")
872
- negotiation_info = gr.Markdown()
873
  with gr.Row():
874
  response_cp = gr.Slider(1, 10, value=1, step=1, label="Your CP Investment")
875
  response_hp = gr.Slider(0, 5, value=0, step=1, label="Your HP Investment")
@@ -879,80 +881,83 @@ def create_ui():
879
 
880
  end_turn_btn = gr.Button("⏭️ End Turn", variant="secondary", size="lg")
881
 
882
- action_result = gr.Markdown()
883
 
884
  # Player 2 Panel
885
  with gr.Column():
886
  gr.Markdown("### Player 2")
887
- p2_name = gr.Textbox(label="Name", interactive=False)
888
- p2_character = gr.Textbox(label="Character", interactive=False)
889
- p2_display = gr.JSON(label="Status")
890
  with gr.Row():
891
- p2_time = gr.Number(label="⏰ Time", interactive=False)
892
- p2_money = gr.Number(label="πŸ’° Money", interactive=False)
893
  with gr.Row():
894
- p2_hp = gr.Number(label="❀️ HP", interactive=False)
895
- p2_cp = gr.Number(label="🧠 CP", interactive=False)
896
  with gr.Row():
897
- p2_stress = gr.Number(label="😰 Stress", interactive=False)
898
- p2_qol = gr.Number(label="✨ QoL", interactive=False)
899
 
900
  gr.Markdown("### Spectators")
901
- spectators_display = gr.Markdown("*No spectators*")
902
 
903
  refresh_btn = gr.Button("πŸ”„ Refresh Game State", variant="secondary")
904
 
905
  with gr.Tab("πŸ† Rankings"):
906
  rankings_table = gr.Dataframe(
907
  headers=["Rank", "Player", "Avg QoL", "Games", "Wins", "Win %"],
908
- label="Top Players"
 
909
  )
910
  refresh_rankings_btn = gr.Button("πŸ”„ Refresh Rankings")
911
 
912
  # ===== EVENT HANDLERS =====
913
 
914
- def create_room_handler(your_name, character_id):
915
  import uuid
916
  room_id = str(uuid.uuid4())[:8].upper()
917
  player_id = str(uuid.uuid4())[:8]
918
 
919
  if not db.create_room(room_id):
920
- return {}, "❌ Failed to create room. Please try again."
921
 
922
  success, slot, msg = db.join_room(room_id, player_id, your_name, character_id)
923
 
924
  if success:
925
- return {
926
  'room_id': room_id,
927
  'player_id': player_id,
928
  'player_slot': slot,
929
  'is_spectator': slot is None
930
- }, f"βœ… Room created: **{room_id}**\n\n{msg}\n\nShare this Room ID with your partner!"
 
931
 
932
- # If join fails, we should probably clean up the created room
933
  with db.get_connection() as conn:
934
  conn.execute("DELETE FROM rooms WHERE room_id = ?", (room_id,))
935
  conn.commit()
936
 
937
- return {}, f"❌ Failed to join room after creation: {msg}"
938
 
939
- def join_room_handler(room_id, your_name, character_id):
940
  import uuid
941
  player_id = str(uuid.uuid4())[:8]
942
 
943
  success, slot, msg = db.join_room(room_id, player_id, your_name, character_id)
944
 
945
  if success:
946
- return {
947
  'room_id': room_id,
948
  'player_id': player_id,
949
  'player_slot': slot,
950
  'is_spectator': slot is None
951
- }, f"βœ… {msg}"
952
- return {}, f"❌ {msg}"
 
953
 
954
  def refresh_game_state(session):
955
- empty_return = ("*No game joined*", "", "", {}, "", "", {}, [], gr.Dropdown(choices=[]), "*No spectators*", "")
956
 
957
  if not session.get('room_id'):
958
  return empty_return
@@ -978,9 +983,9 @@ def create_ui():
978
  p1_data = players[0] if len(players) > 0 else {}
979
  p2_data = players[1] if len(players) > 1 else {}
980
 
981
- p1_name = p1_data.get('name', '')
982
  p1_char = p1_data.get('character_id', '')
983
- p2_name = p2_data.get('name', '')
984
  p2_char = p2_data.get('character_id', '')
985
 
986
  # Tasks with better formatting
@@ -1020,50 +1025,50 @@ def create_ui():
1020
  else:
1021
  turn_msg = "⏳ **Partner's turn...** Click Refresh to see updates."
1022
 
1023
- return (status_md, p1_name, p1_char, p1_data, p2_name, p2_char, p2_data,
1024
- task_rows, gr.Dropdown(choices=task_options, value=None), spec_md, turn_msg)
1025
 
1026
  def execute_rest_handler(session, rest_type):
1027
  if not session.get('room_id') or session.get('is_spectator'):
1028
- return "❌ Not in game as player", session
1029
 
1030
  state = db.get_room_state(session['room_id'])
1031
  if not state:
1032
- return "❌ Room not found", session
1033
 
1034
  room = state['room']
1035
  if room['current_turn'] != session['player_slot']:
1036
- return "❌ Not your turn!", session
1037
 
1038
  player = state['players'][session['player_slot']]
1039
  success, msg, updates = execute_rest(player, rest_type)
1040
 
1041
  if success:
1042
  db.update_player(session['room_id'], session['player_slot'], updates)
1043
- return f"βœ… {msg}", session
1044
 
1045
- return f"❌ {msg}", session
1046
 
1047
  def execute_solo_task_handler(session, task_id):
1048
  if not session.get('room_id') or session.get('is_spectator'):
1049
- return "❌ Not in game as player", session
1050
 
1051
  if not task_id:
1052
- return "❌ Select a task first", session
1053
 
1054
  state = db.get_room_state(session['room_id'])
1055
  if not state:
1056
- return "❌ Room not found", session
1057
 
1058
  room = state['room']
1059
  if room['current_turn'] != session['player_slot']:
1060
- return "❌ Not your turn!", session
1061
 
1062
  player = state['players'][session['player_slot']]
1063
  task = next((t for t in TASKS if t['id'] == task_id), None)
1064
 
1065
  if not task:
1066
- return "❌ Task not found", session
1067
 
1068
  success, msg, updates = execute_solo_task(player, task)
1069
 
@@ -1074,24 +1079,24 @@ def create_ui():
1074
  updates['completed_tasks'] = json.dumps(completed)
1075
 
1076
  db.update_player(session['room_id'], session['player_slot'], updates)
1077
- return f"βœ… {msg}", session
1078
 
1079
- return f"❌ {msg}", session
1080
 
1081
  def end_turn_handler(session):
1082
  if not session.get('room_id') or session.get('is_spectator'):
1083
- return "❌ Not in game as player", session
1084
 
1085
  state = db.get_room_state(session['room_id'])
1086
  if not state:
1087
- return "❌ Room not found", session
1088
 
1089
  room = state['room']
1090
  if room['current_turn'] != session['player_slot']:
1091
- return "❌ Not your turn!", session
1092
 
1093
  db.end_turn(session['room_id'], session['player_slot'])
1094
- return "βœ… Turn ended. Waiting for partner...", session
1095
 
1096
  def load_rankings():
1097
  rankings = db.get_rankings(20)
@@ -1107,21 +1112,21 @@ def create_ui():
1107
  ])
1108
  return rows
1109
 
1110
- # Wire up events
1111
  create_btn.click(
1112
- create_room_handler,
1113
- inputs=[your_name_input, character_select],
1114
  outputs=[session_state, join_result]
1115
  )
1116
 
1117
  join_btn.click(
1118
- join_room_handler,
1119
- inputs=[room_id_input, your_name_input, character_select],
1120
  outputs=[session_state, join_result]
1121
  )
1122
 
1123
  refresh_btn.click(
1124
- refresh_game_state,
1125
  inputs=[session_state],
1126
  outputs=[status_display, p1_name, p1_character, p1_display,
1127
  p2_name, p2_character, p2_display, tasks_display, task_select,
@@ -1129,11 +1134,11 @@ def create_ui():
1129
  )
1130
 
1131
  quick_nap_btn.click(
1132
- lambda s: execute_rest_handler(s, "quick_nap"),
1133
  inputs=[session_state],
1134
- outputs=[action_result, session_state]
1135
  ).then(
1136
- refresh_game_state,
1137
  inputs=[session_state],
1138
  outputs=[status_display, p1_name, p1_character, p1_display,
1139
  p2_name, p2_character, p2_display, tasks_display, task_select,
@@ -1141,11 +1146,11 @@ def create_ui():
1141
  )
1142
 
1143
  full_sleep_btn.click(
1144
- lambda s: execute_rest_handler(s, "full_sleep"),
1145
  inputs=[session_state],
1146
- outputs=[action_result, session_state]
1147
  ).then(
1148
- refresh_game_state,
1149
  inputs=[session_state],
1150
  outputs=[status_display, p1_name, p1_character, p1_display,
1151
  p2_name, p2_character, p2_display, tasks_display, task_select,
@@ -1153,11 +1158,11 @@ def create_ui():
1153
  )
1154
 
1155
  deep_rest_btn.click(
1156
- lambda s: execute_rest_handler(s, "deep_rest"),
1157
  inputs=[session_state],
1158
- outputs=[action_result, session_state]
1159
  ).then(
1160
- refresh_game_state,
1161
  inputs=[session_state],
1162
  outputs=[status_display, p1_name, p1_character, p1_display,
1163
  p2_name, p2_character, p2_display, tasks_display, task_select,
@@ -1165,11 +1170,11 @@ def create_ui():
1165
  )
1166
 
1167
  solo_task_btn.click(
1168
- execute_solo_task_handler,
1169
  inputs=[session_state, task_select],
1170
- outputs=[action_result, session_state]
1171
  ).then(
1172
- refresh_game_state,
1173
  inputs=[session_state],
1174
  outputs=[status_display, p1_name, p1_character, p1_display,
1175
  p2_name, p2_character, p2_display, tasks_display, task_select,
@@ -1177,11 +1182,11 @@ def create_ui():
1177
  )
1178
 
1179
  end_turn_btn.click(
1180
- end_turn_handler,
1181
  inputs=[session_state],
1182
- outputs=[action_result, session_state]
1183
  ).then(
1184
- refresh_game_state,
1185
  inputs=[session_state],
1186
  outputs=[status_display, p1_name, p1_character, p1_display,
1187
  p2_name, p2_character, p2_display, tasks_display, task_select,
@@ -1189,12 +1194,12 @@ def create_ui():
1189
  )
1190
 
1191
  refresh_rankings_btn.click(
1192
- load_rankings,
1193
  outputs=[rankings_table]
1194
  )
1195
 
1196
  # Auto-refresh on tab switch
1197
- app.load(load_rankings, outputs=[rankings_table])
1198
 
1199
  return app
1200
 
@@ -1205,4 +1210,4 @@ if __name__ == "__main__":
1205
  server_name="0.0.0.0",
1206
  server_port=7860,
1207
  share=False
1208
- )
 
1
  """
2
  Life Frontier: Partner's Concerto
3
  Single-app turn-based game with SQLite state management
4
+ Upgraded for Gradio 5.49
5
  """
6
 
7
  import gradio as gr
 
790
  *A turn-based strategy game for couples*
791
  """)
792
 
793
+ # Session state - Gradio 5 style
794
  session_state = gr.State({
795
  'room_id': None,
796
  'player_id': None,
 
813
  create_btn = gr.Button("πŸ†• Create Room", variant="primary")
814
  join_btn = gr.Button("πŸšͺ Join Room", variant="secondary")
815
 
816
+ join_result = gr.Markdown(value="")
817
 
818
  with gr.Tab("🎯 Game Board"):
819
  gr.Markdown("### Game Status")
820
+ status_display = gr.Markdown(value="*No game joined*")
821
 
822
  with gr.Row():
823
  # Player 1 Panel
824
  with gr.Column():
825
  gr.Markdown("### Player 1")
826
+ p1_name = gr.Textbox(label="Name", interactive=False, value="")
827
+ p1_character = gr.Textbox(label="Character", interactive=False, value="")
828
+ p1_display = gr.JSON(label="Status", value={})
829
  with gr.Row():
830
+ p1_time = gr.Number(label="⏰ Time", interactive=False, value=0)
831
+ p1_money = gr.Number(label="πŸ’° Money", interactive=False, value=0)
832
  with gr.Row():
833
+ p1_hp = gr.Number(label="❀️ HP", interactive=False, value=0)
834
+ p1_cp = gr.Number(label="🧠 CP", interactive=False, value=0)
835
  with gr.Row():
836
+ p1_stress = gr.Number(label="😰 Stress", interactive=False, value=0)
837
+ p1_qol = gr.Number(label="✨ QoL", interactive=False, value=0)
838
 
839
  # Center - Actions
840
  with gr.Column():
841
  gr.Markdown("### Your Actions")
842
+ your_turn_msg = gr.Markdown(value="*Waiting for your turn...*")
843
 
844
  gr.Markdown("**Rest Actions**")
845
  with gr.Row():
 
851
  tasks_display = gr.Dataframe(
852
  headers=["ID", "Name", "Type", "Time", "Req (Solo)", "Req (Coop)", "Reward"],
853
  label="Public Tasks",
854
+ wrap=True,
855
+ value=[]
856
  )
857
 
858
+ task_select = gr.Dropdown(label="Select Task", choices=[], value=None)
859
 
860
  with gr.Row():
861
  solo_task_btn = gr.Button("▢️ Execute Solo", variant="primary")
 
871
  # Negotiation response (for partner)
872
  with gr.Group(visible=False) as negotiation_panel:
873
  gr.Markdown("### 🀝 Partner's Cooperation Request")
874
+ negotiation_info = gr.Markdown(value="")
875
  with gr.Row():
876
  response_cp = gr.Slider(1, 10, value=1, step=1, label="Your CP Investment")
877
  response_hp = gr.Slider(0, 5, value=0, step=1, label="Your HP Investment")
 
881
 
882
  end_turn_btn = gr.Button("⏭️ End Turn", variant="secondary", size="lg")
883
 
884
+ action_result = gr.Markdown(value="")
885
 
886
  # Player 2 Panel
887
  with gr.Column():
888
  gr.Markdown("### Player 2")
889
+ p2_name = gr.Textbox(label="Name", interactive=False, value="")
890
+ p2_character = gr.Textbox(label="Character", interactive=False, value="")
891
+ p2_display = gr.JSON(label="Status", value={})
892
  with gr.Row():
893
+ p2_time = gr.Number(label="⏰ Time", interactive=False, value=0)
894
+ p2_money = gr.Number(label="πŸ’° Money", interactive=False, value=0)
895
  with gr.Row():
896
+ p2_hp = gr.Number(label="❀️ HP", interactive=False, value=0)
897
+ p2_cp = gr.Number(label="🧠 CP", interactive=False, value=0)
898
  with gr.Row():
899
+ p2_stress = gr.Number(label="😰 Stress", interactive=False, value=0)
900
+ p2_qol = gr.Number(label="✨ QoL", interactive=False, value=0)
901
 
902
  gr.Markdown("### Spectators")
903
+ spectators_display = gr.Markdown(value="*No spectators*")
904
 
905
  refresh_btn = gr.Button("πŸ”„ Refresh Game State", variant="secondary")
906
 
907
  with gr.Tab("πŸ† Rankings"):
908
  rankings_table = gr.Dataframe(
909
  headers=["Rank", "Player", "Avg QoL", "Games", "Wins", "Win %"],
910
+ label="Top Players",
911
+ value=[]
912
  )
913
  refresh_rankings_btn = gr.Button("πŸ”„ Refresh Rankings")
914
 
915
  # ===== EVENT HANDLERS =====
916
 
917
+ def create_room_handler(your_name, character_id, session):
918
  import uuid
919
  room_id = str(uuid.uuid4())[:8].upper()
920
  player_id = str(uuid.uuid4())[:8]
921
 
922
  if not db.create_room(room_id):
923
+ return session, "❌ Failed to create room. Please try again."
924
 
925
  success, slot, msg = db.join_room(room_id, player_id, your_name, character_id)
926
 
927
  if success:
928
+ new_session = {
929
  'room_id': room_id,
930
  'player_id': player_id,
931
  'player_slot': slot,
932
  'is_spectator': slot is None
933
+ }
934
+ return new_session, f"βœ… Room created: **{room_id}**\n\n{msg}\n\nShare this Room ID with your partner!"
935
 
936
+ # If join fails, clean up the created room
937
  with db.get_connection() as conn:
938
  conn.execute("DELETE FROM rooms WHERE room_id = ?", (room_id,))
939
  conn.commit()
940
 
941
+ return session, f"❌ Failed to join room after creation: {msg}"
942
 
943
+ def join_room_handler(room_id, your_name, character_id, session):
944
  import uuid
945
  player_id = str(uuid.uuid4())[:8]
946
 
947
  success, slot, msg = db.join_room(room_id, player_id, your_name, character_id)
948
 
949
  if success:
950
+ new_session = {
951
  'room_id': room_id,
952
  'player_id': player_id,
953
  'player_slot': slot,
954
  'is_spectator': slot is None
955
+ }
956
+ return new_session, f"βœ… {msg}"
957
+ return session, f"❌ {msg}"
958
 
959
  def refresh_game_state(session):
960
+ empty_return = ("*No game joined*", "", "", {}, "", "", {}, [], [], "*No spectators*", "")
961
 
962
  if not session.get('room_id'):
963
  return empty_return
 
983
  p1_data = players[0] if len(players) > 0 else {}
984
  p2_data = players[1] if len(players) > 1 else {}
985
 
986
+ p1_name_val = p1_data.get('name', '')
987
  p1_char = p1_data.get('character_id', '')
988
+ p2_name_val = p2_data.get('name', '')
989
  p2_char = p2_data.get('character_id', '')
990
 
991
  # Tasks with better formatting
 
1025
  else:
1026
  turn_msg = "⏳ **Partner's turn...** Click Refresh to see updates."
1027
 
1028
+ return (status_md, p1_name_val, p1_char, p1_data, p2_name_val, p2_char, p2_data,
1029
+ task_rows, task_options, spec_md, turn_msg)
1030
 
1031
  def execute_rest_handler(session, rest_type):
1032
  if not session.get('room_id') or session.get('is_spectator'):
1033
+ return "❌ Not in game as player"
1034
 
1035
  state = db.get_room_state(session['room_id'])
1036
  if not state:
1037
+ return "❌ Room not found"
1038
 
1039
  room = state['room']
1040
  if room['current_turn'] != session['player_slot']:
1041
+ return "❌ Not your turn!"
1042
 
1043
  player = state['players'][session['player_slot']]
1044
  success, msg, updates = execute_rest(player, rest_type)
1045
 
1046
  if success:
1047
  db.update_player(session['room_id'], session['player_slot'], updates)
1048
+ return f"βœ… {msg}"
1049
 
1050
+ return f"❌ {msg}"
1051
 
1052
  def execute_solo_task_handler(session, task_id):
1053
  if not session.get('room_id') or session.get('is_spectator'):
1054
+ return "❌ Not in game as player"
1055
 
1056
  if not task_id:
1057
+ return "❌ Select a task first"
1058
 
1059
  state = db.get_room_state(session['room_id'])
1060
  if not state:
1061
+ return "❌ Room not found"
1062
 
1063
  room = state['room']
1064
  if room['current_turn'] != session['player_slot']:
1065
+ return "❌ Not your turn!"
1066
 
1067
  player = state['players'][session['player_slot']]
1068
  task = next((t for t in TASKS if t['id'] == task_id), None)
1069
 
1070
  if not task:
1071
+ return "❌ Task not found"
1072
 
1073
  success, msg, updates = execute_solo_task(player, task)
1074
 
 
1079
  updates['completed_tasks'] = json.dumps(completed)
1080
 
1081
  db.update_player(session['room_id'], session['player_slot'], updates)
1082
+ return f"βœ… {msg}"
1083
 
1084
+ return f"❌ {msg}"
1085
 
1086
  def end_turn_handler(session):
1087
  if not session.get('room_id') or session.get('is_spectator'):
1088
+ return "❌ Not in game as player"
1089
 
1090
  state = db.get_room_state(session['room_id'])
1091
  if not state:
1092
+ return "❌ Room not found"
1093
 
1094
  room = state['room']
1095
  if room['current_turn'] != session['player_slot']:
1096
+ return "❌ Not your turn!"
1097
 
1098
  db.end_turn(session['room_id'], session['player_slot'])
1099
+ return "βœ… Turn ended. Waiting for partner..."
1100
 
1101
  def load_rankings():
1102
  rankings = db.get_rankings(20)
 
1112
  ])
1113
  return rows
1114
 
1115
+ # Wire up events - Gradio 5 style with proper outputs
1116
  create_btn.click(
1117
+ fn=create_room_handler,
1118
+ inputs=[your_name_input, character_select, session_state],
1119
  outputs=[session_state, join_result]
1120
  )
1121
 
1122
  join_btn.click(
1123
+ fn=join_room_handler,
1124
+ inputs=[room_id_input, your_name_input, character_select, session_state],
1125
  outputs=[session_state, join_result]
1126
  )
1127
 
1128
  refresh_btn.click(
1129
+ fn=refresh_game_state,
1130
  inputs=[session_state],
1131
  outputs=[status_display, p1_name, p1_character, p1_display,
1132
  p2_name, p2_character, p2_display, tasks_display, task_select,
 
1134
  )
1135
 
1136
  quick_nap_btn.click(
1137
+ fn=lambda s: execute_rest_handler(s, "quick_nap"),
1138
  inputs=[session_state],
1139
+ outputs=[action_result]
1140
  ).then(
1141
+ fn=refresh_game_state,
1142
  inputs=[session_state],
1143
  outputs=[status_display, p1_name, p1_character, p1_display,
1144
  p2_name, p2_character, p2_display, tasks_display, task_select,
 
1146
  )
1147
 
1148
  full_sleep_btn.click(
1149
+ fn=lambda s: execute_rest_handler(s, "full_sleep"),
1150
  inputs=[session_state],
1151
+ outputs=[action_result]
1152
  ).then(
1153
+ fn=refresh_game_state,
1154
  inputs=[session_state],
1155
  outputs=[status_display, p1_name, p1_character, p1_display,
1156
  p2_name, p2_character, p2_display, tasks_display, task_select,
 
1158
  )
1159
 
1160
  deep_rest_btn.click(
1161
+ fn=lambda s: execute_rest_handler(s, "deep_rest"),
1162
  inputs=[session_state],
1163
+ outputs=[action_result]
1164
  ).then(
1165
+ fn=refresh_game_state,
1166
  inputs=[session_state],
1167
  outputs=[status_display, p1_name, p1_character, p1_display,
1168
  p2_name, p2_character, p2_display, tasks_display, task_select,
 
1170
  )
1171
 
1172
  solo_task_btn.click(
1173
+ fn=execute_solo_task_handler,
1174
  inputs=[session_state, task_select],
1175
+ outputs=[action_result]
1176
  ).then(
1177
+ fn=refresh_game_state,
1178
  inputs=[session_state],
1179
  outputs=[status_display, p1_name, p1_character, p1_display,
1180
  p2_name, p2_character, p2_display, tasks_display, task_select,
 
1182
  )
1183
 
1184
  end_turn_btn.click(
1185
+ fn=end_turn_handler,
1186
  inputs=[session_state],
1187
+ outputs=[action_result]
1188
  ).then(
1189
+ fn=refresh_game_state,
1190
  inputs=[session_state],
1191
  outputs=[status_display, p1_name, p1_character, p1_display,
1192
  p2_name, p2_character, p2_display, tasks_display, task_select,
 
1194
  )
1195
 
1196
  refresh_rankings_btn.click(
1197
+ fn=load_rankings,
1198
  outputs=[rankings_table]
1199
  )
1200
 
1201
  # Auto-refresh on tab switch
1202
+ app.load(fn=load_rankings, outputs=[rankings_table])
1203
 
1204
  return app
1205
 
 
1210
  server_name="0.0.0.0",
1211
  server_port=7860,
1212
  share=False
1213
+ )