nice
This commit is contained in:
+2
-1
@@ -13,4 +13,5 @@ rats.spec
|
|||||||
.env
|
.env
|
||||||
release/
|
release/
|
||||||
unit/__pycache__
|
unit/__pycache__
|
||||||
conf/keybindings.yaml
|
conf/keybindings.yaml
|
||||||
|
conf/keybindings.json
|
||||||
@@ -2089,3 +2089,37 @@ INFO: 127.0.0.1:52734 - "GET / HTTP/1.1" 200 OK
|
|||||||
INFO: 127.0.0.1:52740 - "GET /users/DEV-3A2D87B7 HTTP/1.1" 200 OK
|
INFO: 127.0.0.1:52740 - "GET /users/DEV-3A2D87B7 HTTP/1.1" 200 OK
|
||||||
INFO: 127.0.0.1:41304 - "GET / HTTP/1.1" 200 OK
|
INFO: 127.0.0.1:41304 - "GET / HTTP/1.1" 200 OK
|
||||||
INFO: 127.0.0.1:41308 - "GET /users/DEV-3A2D87B7 HTTP/1.1" 200 OK
|
INFO: 127.0.0.1:41308 - "GET /users/DEV-3A2D87B7 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:33472 - "GET / HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:33474 - "GET /users/DEV-3A2D87B7 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:52616 - "GET /leaderboard/DEV-3A2D87B7?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35690 - "GET /leaderboard/global/top?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35706 - "GET /leaderboard/DEV-3A2D87B7?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35722 - "GET /leaderboard/global/top?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35724 - "GET /leaderboard/DEV-3A2D87B7?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:35736 - "GET /leaderboard/global/top?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:57592 - "GET /leaderboard/DEV-3A2D87B7?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:57608 - "GET /leaderboard/DEV-3A2D87B7?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:44530 - "POST /signup/DEV-3A2D87B7/AA HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:37822 - "GET / HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:37834 - "GET /users/DEV-3A2D87B7 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46744 - "GET /leaderboard/DEV-3A2D87B7?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:46752 - "GET /leaderboard/global/top?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:43292 - "GET /leaderboard/DEV-3A2D87B7?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:43296 - "GET /leaderboard/global/top?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:43300 - "GET /leaderboard/DEV-3A2D87B7?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:43310 - "GET /leaderboard/global/top?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59822 - "GET / HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:59832 - "GET /users/DEV-3A2D87B7 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:36158 - "GET / HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:36170 - "GET /users/DEV-3A2D87B7 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40986 - "GET / HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:40990 - "GET /users/DEV-3A2D87B7 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:39030 - "GET / HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:39046 - "GET /users/DEV-3A2D87B7 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:38630 - "GET /users/DEV-3A2D87B7 HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:38640 - "POST /signup/DEV-3A2D87B7/Alice HTTP/1.1" 409 Conflict
|
||||||
|
INFO: 127.0.0.1:38490 - "GET /leaderboard/DEV-3A2D87B7?limit=10 HTTP/1.1" 200 OK
|
||||||
|
INFO: Shutting down
|
||||||
|
INFO: Waiting for application shutdown.
|
||||||
|
INFO: Application shutdown complete.
|
||||||
|
INFO: Finished server process [41455]
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import uuid
|
|||||||
import platform
|
import platform
|
||||||
import hashlib
|
import hashlib
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from score_api_client import ScoreAPIClient
|
from engine.score_api_client import ScoreAPIClient
|
||||||
|
|
||||||
|
|
||||||
class UserProfileIntegration:
|
class UserProfileIntegration:
|
||||||
+408
-23
@@ -23,6 +23,9 @@ import sdl2
|
|||||||
import sdl2.ext
|
import sdl2.ext
|
||||||
from sdl2.ext.compat import byteify
|
from sdl2.ext.compat import byteify
|
||||||
|
|
||||||
|
# Import the user profile integration system
|
||||||
|
from engine.user_profile_integration import UserProfileIntegration
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class UserProfile:
|
class UserProfile:
|
||||||
@@ -57,13 +60,27 @@ class ProfileManager:
|
|||||||
self.profiles: Dict[str, UserProfile] = {}
|
self.profiles: Dict[str, UserProfile] = {}
|
||||||
self.active_profile: Optional[str] = None
|
self.active_profile: Optional[str] = None
|
||||||
|
|
||||||
|
# Initialize user profile integration system
|
||||||
|
self.integration = UserProfileIntegration(profiles_file)
|
||||||
|
self.device_id = self.integration.get_device_id()
|
||||||
|
self.api_enabled = self.integration.api_enabled
|
||||||
|
|
||||||
# UI State
|
# UI State
|
||||||
self.current_screen = "main_menu" # main_menu, profile_list, create_profile, edit_profile
|
self.current_screen = "main_menu" # main_menu, profile_list, create_profile, edit_profile, leaderboard, profile_stats
|
||||||
self.selected_index = 0
|
self.selected_index = 0
|
||||||
self.input_text = ""
|
self.input_text = ""
|
||||||
self.input_active = False
|
self.input_active = False
|
||||||
self.editing_field = None
|
self.editing_field = None
|
||||||
|
|
||||||
|
# Error dialog state
|
||||||
|
self.show_error = False
|
||||||
|
self.error_message = ""
|
||||||
|
self.error_timer = 0
|
||||||
|
|
||||||
|
# Leaderboard data
|
||||||
|
self.leaderboard_data = []
|
||||||
|
self.leaderboard_type = "device" # "device" or "global"
|
||||||
|
|
||||||
# Virtual keyboard state
|
# Virtual keyboard state
|
||||||
self.vk_cursor_x = 0 # Virtual keyboard cursor X
|
self.vk_cursor_x = 0 # Virtual keyboard cursor X
|
||||||
self.vk_cursor_y = 0 # Virtual keyboard cursor Y
|
self.vk_cursor_y = 0 # Virtual keyboard cursor Y
|
||||||
@@ -191,6 +208,15 @@ class ProfileManager:
|
|||||||
|
|
||||||
self.profiles[name.strip()] = profile
|
self.profiles[name.strip()] = profile
|
||||||
self.save_profiles()
|
self.save_profiles()
|
||||||
|
|
||||||
|
# Register with API server if available
|
||||||
|
if self.api_enabled:
|
||||||
|
result = self.integration.register_new_user(name.strip())
|
||||||
|
if result:
|
||||||
|
print(f"Profile {name.strip()} registered with server")
|
||||||
|
else:
|
||||||
|
print(f"Warning: Profile {name.strip()} created locally but not registered with server")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def delete_profile(self, name: str) -> bool:
|
def delete_profile(self, name: str) -> bool:
|
||||||
@@ -209,6 +235,10 @@ class ProfileManager:
|
|||||||
self.active_profile = name
|
self.active_profile = name
|
||||||
self.profiles[name].last_played = datetime.now().isoformat()
|
self.profiles[name].last_played = datetime.now().isoformat()
|
||||||
self.save_profiles()
|
self.save_profiles()
|
||||||
|
|
||||||
|
# Update integration system to load the new profile
|
||||||
|
self.integration.reload_profile()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -264,23 +294,31 @@ class ProfileManager:
|
|||||||
|
|
||||||
def handle_button_press(self, button: int):
|
def handle_button_press(self, button: int):
|
||||||
"""Handle gamepad button presses"""
|
"""Handle gamepad button presses"""
|
||||||
|
# If error dialog is shown, dismiss it on any button press
|
||||||
|
if self.show_error:
|
||||||
|
self.show_error = False
|
||||||
|
return
|
||||||
|
|
||||||
# Button mappings (common gamepad layout)
|
# Button mappings (common gamepad layout)
|
||||||
# 0: A/Cross - Confirm
|
# 0: A/Cross - Confirm
|
||||||
# 1: B/Circle - Back
|
# 1: B/Circle - Back
|
||||||
# 2: X/Square - Delete/Special
|
# 2: X/Square - Delete/Special
|
||||||
# 3: Y/Triangle - Menu
|
# 3: Y/Triangle - Menu
|
||||||
|
|
||||||
if button == 0: # A/Confirm
|
if button == 3: # A/Confirm
|
||||||
self.handle_confirm()
|
self.handle_confirm()
|
||||||
elif button == 1: # B/Back
|
elif button == 4: # B/Back
|
||||||
self.handle_back()
|
self.handle_back()
|
||||||
elif button == 2: # X/Delete
|
elif button == 9: # X/Delete
|
||||||
self.handle_delete()
|
self.handle_delete()
|
||||||
elif button == 3: # Y/Menu
|
elif button == 10: # Y/Menu
|
||||||
self.handle_menu()
|
self.handle_menu()
|
||||||
|
|
||||||
def navigate_up(self):
|
def navigate_up(self):
|
||||||
"""Navigate up in current screen"""
|
"""Navigate up in current screen"""
|
||||||
|
if self.show_error:
|
||||||
|
return
|
||||||
|
|
||||||
if self.current_screen == "main_menu":
|
if self.current_screen == "main_menu":
|
||||||
self.selected_index = max(0, self.selected_index - 1)
|
self.selected_index = max(0, self.selected_index - 1)
|
||||||
elif self.current_screen == "profile_list":
|
elif self.current_screen == "profile_list":
|
||||||
@@ -296,8 +334,11 @@ class ProfileManager:
|
|||||||
|
|
||||||
def navigate_down(self):
|
def navigate_down(self):
|
||||||
"""Navigate down in current screen"""
|
"""Navigate down in current screen"""
|
||||||
|
if self.show_error:
|
||||||
|
return
|
||||||
|
|
||||||
if self.current_screen == "main_menu":
|
if self.current_screen == "main_menu":
|
||||||
max_index = 3 # Create, Select, Settings, Exit (0-3)
|
max_index = 5 # Create, Select, Settings, Leaderboard, Stats, Exit (0-5)
|
||||||
self.selected_index = min(max_index, self.selected_index + 1)
|
self.selected_index = min(max_index, self.selected_index + 1)
|
||||||
elif self.current_screen == "profile_list":
|
elif self.current_screen == "profile_list":
|
||||||
max_index = len(self.profiles) # Profiles + Back
|
max_index = len(self.profiles) # Profiles + Back
|
||||||
@@ -305,6 +346,12 @@ class ProfileManager:
|
|||||||
elif self.current_screen == "edit_profile":
|
elif self.current_screen == "edit_profile":
|
||||||
max_index = 5 # 4 settings + save + back (0-5)
|
max_index = 5 # 4 settings + save + back (0-5)
|
||||||
self.selected_index = min(max_index, self.selected_index + 1)
|
self.selected_index = min(max_index, self.selected_index + 1)
|
||||||
|
elif self.current_screen == "leaderboard":
|
||||||
|
max_index = 2 # Toggle type, refresh, back (0-2)
|
||||||
|
self.selected_index = min(max_index, self.selected_index + 1)
|
||||||
|
elif self.current_screen == "profile_stats":
|
||||||
|
max_index = 0 # Just back button
|
||||||
|
self.selected_index = min(max_index, self.selected_index + 1)
|
||||||
elif self.current_screen == "create_profile" and self.input_active:
|
elif self.current_screen == "create_profile" and self.input_active:
|
||||||
# Virtual keyboard navigation
|
# Virtual keyboard navigation
|
||||||
max_y = len(self.virtual_keyboard) - 1
|
max_y = len(self.virtual_keyboard) - 1
|
||||||
@@ -315,16 +362,32 @@ class ProfileManager:
|
|||||||
|
|
||||||
def navigate_left(self):
|
def navigate_left(self):
|
||||||
"""Navigate left (for adjusting values)"""
|
"""Navigate left (for adjusting values)"""
|
||||||
|
if self.show_error:
|
||||||
|
return
|
||||||
|
|
||||||
if self.current_screen == "edit_profile":
|
if self.current_screen == "edit_profile":
|
||||||
self.adjust_setting(-1)
|
self.adjust_setting(-1)
|
||||||
|
elif self.current_screen == "leaderboard":
|
||||||
|
# Toggle between device and global leaderboard
|
||||||
|
if self.selected_index == 0:
|
||||||
|
self.leaderboard_type = "global" if self.leaderboard_type == "device" else "device"
|
||||||
|
self.load_leaderboard_data()
|
||||||
elif self.current_screen == "create_profile" and self.input_active:
|
elif self.current_screen == "create_profile" and self.input_active:
|
||||||
# Virtual keyboard navigation
|
# Virtual keyboard navigation
|
||||||
self.vk_cursor_x = max(0, self.vk_cursor_x - 1)
|
self.vk_cursor_x = max(0, self.vk_cursor_x - 1)
|
||||||
|
|
||||||
def navigate_right(self):
|
def navigate_right(self):
|
||||||
"""Navigate right (for adjusting values)"""
|
"""Navigate right (for adjusting values)"""
|
||||||
|
if self.show_error:
|
||||||
|
return
|
||||||
|
|
||||||
if self.current_screen == "edit_profile":
|
if self.current_screen == "edit_profile":
|
||||||
self.adjust_setting(1)
|
self.adjust_setting(1)
|
||||||
|
elif self.current_screen == "leaderboard":
|
||||||
|
# Toggle between device and global leaderboard
|
||||||
|
if self.selected_index == 0:
|
||||||
|
self.leaderboard_type = "global" if self.leaderboard_type == "device" else "device"
|
||||||
|
self.load_leaderboard_data()
|
||||||
elif self.current_screen == "create_profile" and self.input_active:
|
elif self.current_screen == "create_profile" and self.input_active:
|
||||||
# Virtual keyboard navigation
|
# Virtual keyboard navigation
|
||||||
max_x = len(self.virtual_keyboard[self.vk_cursor_y]) - 1
|
max_x = len(self.virtual_keyboard[self.vk_cursor_y]) - 1
|
||||||
@@ -332,6 +395,9 @@ class ProfileManager:
|
|||||||
|
|
||||||
def handle_confirm(self):
|
def handle_confirm(self):
|
||||||
"""Handle confirm action (A button)"""
|
"""Handle confirm action (A button)"""
|
||||||
|
if self.show_error:
|
||||||
|
return
|
||||||
|
|
||||||
if self.current_screen == "main_menu":
|
if self.current_screen == "main_menu":
|
||||||
if self.selected_index == 0: # Create Profile
|
if self.selected_index == 0: # Create Profile
|
||||||
self.current_screen = "create_profile"
|
self.current_screen = "create_profile"
|
||||||
@@ -344,7 +410,16 @@ class ProfileManager:
|
|||||||
if self.active_profile:
|
if self.active_profile:
|
||||||
self.current_screen = "edit_profile"
|
self.current_screen = "edit_profile"
|
||||||
self.selected_index = 0
|
self.selected_index = 0
|
||||||
elif self.selected_index == 3: # Exit
|
elif self.selected_index == 3: # Leaderboard
|
||||||
|
if self.api_enabled:
|
||||||
|
self.current_screen = "leaderboard"
|
||||||
|
self.selected_index = 0
|
||||||
|
self.load_leaderboard_data()
|
||||||
|
elif self.selected_index == 4: # Profile Stats
|
||||||
|
if self.active_profile:
|
||||||
|
self.current_screen = "profile_stats"
|
||||||
|
self.selected_index = 0
|
||||||
|
elif self.selected_index == 5: # Exit
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
elif self.current_screen == "profile_list":
|
elif self.current_screen == "profile_list":
|
||||||
@@ -377,18 +452,36 @@ class ProfileManager:
|
|||||||
elif self.selected_index == 5: # Back (index 5)
|
elif self.selected_index == 5: # Back (index 5)
|
||||||
self.current_screen = "main_menu"
|
self.current_screen = "main_menu"
|
||||||
self.selected_index = 0
|
self.selected_index = 0
|
||||||
|
|
||||||
|
elif self.current_screen == "leaderboard":
|
||||||
|
if self.selected_index == 1: # Refresh
|
||||||
|
self.load_leaderboard_data()
|
||||||
|
elif self.selected_index == 2: # Back
|
||||||
|
self.current_screen = "main_menu"
|
||||||
|
self.selected_index = 3
|
||||||
|
|
||||||
|
elif self.current_screen == "profile_stats":
|
||||||
|
if self.selected_index == 0: # Back
|
||||||
|
self.current_screen = "main_menu"
|
||||||
|
self.selected_index = 4
|
||||||
|
|
||||||
def handle_back(self):
|
def handle_back(self):
|
||||||
"""Handle back action (B button)"""
|
"""Handle back action (B button)"""
|
||||||
|
if self.show_error:
|
||||||
|
return
|
||||||
|
|
||||||
if self.current_screen == "main_menu":
|
if self.current_screen == "main_menu":
|
||||||
self.running = False
|
self.running = False
|
||||||
elif self.current_screen in ["profile_list", "create_profile", "edit_profile"]:
|
elif self.current_screen in ["profile_list", "create_profile", "edit_profile", "leaderboard", "profile_stats"]:
|
||||||
self.current_screen = "main_menu"
|
self.current_screen = "main_menu"
|
||||||
self.selected_index = 0
|
self.selected_index = 0
|
||||||
self.input_active = False
|
self.input_active = False
|
||||||
|
|
||||||
def handle_delete(self):
|
def handle_delete(self):
|
||||||
"""Handle delete action (X button)"""
|
"""Handle delete action (X button)"""
|
||||||
|
if self.show_error:
|
||||||
|
return
|
||||||
|
|
||||||
if self.current_screen == "profile_list":
|
if self.current_screen == "profile_list":
|
||||||
profile_names = list(self.profiles.keys())
|
profile_names = list(self.profiles.keys())
|
||||||
if self.selected_index < len(profile_names):
|
if self.selected_index < len(profile_names):
|
||||||
@@ -426,10 +519,18 @@ class ProfileManager:
|
|||||||
elif selected_char == 'DONE':
|
elif selected_char == 'DONE':
|
||||||
# Finish input
|
# Finish input
|
||||||
if self.input_text.strip():
|
if self.input_text.strip():
|
||||||
if self.create_profile(self.input_text):
|
# Check if profile already exists
|
||||||
self.current_screen = "main_menu"
|
if self.input_text.strip() in self.profiles:
|
||||||
self.selected_index = 0
|
self.show_error_dialog(f"Profile '{self.input_text.strip()}' already exists!")
|
||||||
self.input_active = False
|
else:
|
||||||
|
if self.create_profile(self.input_text):
|
||||||
|
self.current_screen = "main_menu"
|
||||||
|
self.selected_index = 0
|
||||||
|
self.input_active = False
|
||||||
|
else:
|
||||||
|
self.show_error_dialog("Failed to create profile!")
|
||||||
|
else:
|
||||||
|
self.show_error_dialog("Profile name cannot be empty!")
|
||||||
elif selected_char == 'CANCEL':
|
elif selected_char == 'CANCEL':
|
||||||
# Cancel input
|
# Cancel input
|
||||||
self.input_text = ""
|
self.input_text = ""
|
||||||
@@ -461,6 +562,27 @@ class ProfileManager:
|
|||||||
elif self.selected_index == 3: # Screen Shake
|
elif self.selected_index == 3: # Screen Shake
|
||||||
profile.settings["screen_shake"] = not profile.settings["screen_shake"]
|
profile.settings["screen_shake"] = not profile.settings["screen_shake"]
|
||||||
|
|
||||||
|
def load_leaderboard_data(self):
|
||||||
|
"""Load leaderboard data based on current type"""
|
||||||
|
if not self.api_enabled:
|
||||||
|
self.leaderboard_data = []
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.leaderboard_type == "device":
|
||||||
|
self.leaderboard_data = self.integration.get_device_leaderboard(10)
|
||||||
|
else: # global
|
||||||
|
self.leaderboard_data = self.integration.get_global_leaderboard(10)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading leaderboard: {e}")
|
||||||
|
self.leaderboard_data = []
|
||||||
|
|
||||||
|
def show_error_dialog(self, message: str):
|
||||||
|
"""Show an error dialog with the given message"""
|
||||||
|
self.show_error = True
|
||||||
|
self.error_message = message
|
||||||
|
self.error_timer = time.time() + 3.0 # Show error for 3 seconds
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
"""Main rendering method"""
|
"""Main rendering method"""
|
||||||
# Clear with dark background
|
# Clear with dark background
|
||||||
@@ -481,6 +603,17 @@ class ProfileManager:
|
|||||||
self.render_create_profile()
|
self.render_create_profile()
|
||||||
elif self.current_screen == "edit_profile":
|
elif self.current_screen == "edit_profile":
|
||||||
self.render_edit_profile()
|
self.render_edit_profile()
|
||||||
|
elif self.current_screen == "leaderboard":
|
||||||
|
self.render_leaderboard()
|
||||||
|
elif self.current_screen == "profile_stats":
|
||||||
|
self.render_profile_stats()
|
||||||
|
|
||||||
|
# Render error dialog on top if active
|
||||||
|
if self.show_error:
|
||||||
|
self.render_error_dialog()
|
||||||
|
# Auto-dismiss after timer expires
|
||||||
|
if time.time() > self.error_timer:
|
||||||
|
self.show_error = False
|
||||||
|
|
||||||
self.renderer.present()
|
self.renderer.present()
|
||||||
|
|
||||||
@@ -493,16 +626,25 @@ class ProfileManager:
|
|||||||
title = "Profile Manager"
|
title = "Profile Manager"
|
||||||
self.draw_text(title, 320, 20, self.colors['light_blue'], 'title', center=True)
|
self.draw_text(title, 320, 20, self.colors['light_blue'], 'title', center=True)
|
||||||
|
|
||||||
# Subtitle with active profile
|
# Subtitle with active profile and API status
|
||||||
if self.active_profile:
|
if self.active_profile:
|
||||||
active_text = f"Active: {self.active_profile}"
|
active_text = f"Active: {self.active_profile}"
|
||||||
self.draw_text(active_text, 320, 60, self.colors['light_green'], 'medium', center=True)
|
self.draw_text(active_text, 320, 60, self.colors['light_green'], 'medium', center=True)
|
||||||
else:
|
else:
|
||||||
self.draw_text("No active profile", 320, 60, self.colors['yellow'], 'medium', center=True)
|
self.draw_text("No active profile", 320, 60, self.colors['yellow'], 'medium', center=True)
|
||||||
|
|
||||||
|
# API connection status
|
||||||
|
api_status = "API: Connected" if self.api_enabled else "API: Offline"
|
||||||
|
api_color = self.colors['light_green'] if self.api_enabled else self.colors['red']
|
||||||
|
self.draw_text(api_status, 320, 80, api_color, 'tiny', center=True)
|
||||||
|
|
||||||
|
# Device ID display
|
||||||
|
device_text = f"Device: {self.device_id}"
|
||||||
|
self.draw_text(device_text, 320, 95, self.colors['light_gray'], 'tiny', center=True)
|
||||||
|
|
||||||
# Menu panel
|
# Menu panel
|
||||||
panel_x, panel_y = 120, 140
|
panel_x, panel_y = 120, 120
|
||||||
panel_width, panel_height = 400, 220
|
panel_width, panel_height = 400, 240
|
||||||
self.draw_panel(panel_x, panel_y, panel_width, panel_height,
|
self.draw_panel(panel_x, panel_y, panel_width, panel_height,
|
||||||
self.colors['dark_gray'], self.colors['gray'])
|
self.colors['dark_gray'], self.colors['gray'])
|
||||||
|
|
||||||
@@ -511,18 +653,27 @@ class ProfileManager:
|
|||||||
"Create Profile",
|
"Create Profile",
|
||||||
"Select Profile",
|
"Select Profile",
|
||||||
"Edit Settings",
|
"Edit Settings",
|
||||||
|
"Leaderboard",
|
||||||
|
"Profile Stats",
|
||||||
"Exit"
|
"Exit"
|
||||||
]
|
]
|
||||||
|
|
||||||
button_width = 280
|
button_width = 280
|
||||||
button_height = 35
|
button_height = 30
|
||||||
button_x = panel_x + 60
|
button_x = panel_x + 60
|
||||||
start_y = panel_y + 20
|
start_y = panel_y + 15
|
||||||
|
|
||||||
for i, item in enumerate(menu_items):
|
for i, item in enumerate(menu_items):
|
||||||
button_y = start_y + i * (button_height + 10)
|
button_y = start_y + i * (button_height + 8)
|
||||||
selected = (i == self.selected_index)
|
selected = (i == self.selected_index)
|
||||||
self.draw_button(item, button_x, button_y, button_width, button_height, selected)
|
|
||||||
|
# Gray out unavailable options
|
||||||
|
if (item == "Edit Settings" or item == "Profile Stats") and not self.active_profile:
|
||||||
|
self.draw_button(f"{item} (No Profile)", button_x, button_y, button_width, button_height, False, disabled=True)
|
||||||
|
elif item == "Leaderboard" and not self.api_enabled:
|
||||||
|
self.draw_button(f"{item} (Offline)", button_x, button_y, button_width, button_height, False, disabled=True)
|
||||||
|
else:
|
||||||
|
self.draw_button(item, button_x, button_y, button_width, button_height, selected)
|
||||||
|
|
||||||
# Controls help panel
|
# Controls help panel
|
||||||
help_panel_y = 380
|
help_panel_y = 380
|
||||||
@@ -748,6 +899,219 @@ class ProfileManager:
|
|||||||
self.draw_text("Left/Right: Adjust • Enter: Save/Back • Escape: Cancel",
|
self.draw_text("Left/Right: Adjust • Enter: Save/Back • Escape: Cancel",
|
||||||
320, 435, self.colors['light_gray'], 'tiny', center=True)
|
320, 435, self.colors['light_gray'], 'tiny', center=True)
|
||||||
|
|
||||||
|
def render_leaderboard(self):
|
||||||
|
"""Render leaderboard screen"""
|
||||||
|
# Header
|
||||||
|
self.renderer.fill((0, 0, 640, 80), self.colors['dark_gray'])
|
||||||
|
|
||||||
|
title = f"{self.leaderboard_type.title()} Leaderboard"
|
||||||
|
self.draw_text(title, 320, 15, self.colors['light_blue'], 'title', center=True)
|
||||||
|
|
||||||
|
if not self.api_enabled:
|
||||||
|
self.draw_text("Server offline - No leaderboard available", 320, 50,
|
||||||
|
self.colors['red'], 'medium', center=True)
|
||||||
|
self.draw_button("← Back", 270, 200, 100, 30, True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Toggle button
|
||||||
|
toggle_text = f"Type: {self.leaderboard_type.title()} (Left/Right to toggle)"
|
||||||
|
toggle_selected = (self.selected_index == 0)
|
||||||
|
self.draw_button(toggle_text, 120, 90, 400, 25, toggle_selected)
|
||||||
|
|
||||||
|
# Refresh button
|
||||||
|
refresh_selected = (self.selected_index == 1)
|
||||||
|
self.draw_button("Refresh", 150, 125, 80, 25, refresh_selected)
|
||||||
|
|
||||||
|
# Back button
|
||||||
|
back_selected = (self.selected_index == 2)
|
||||||
|
self.draw_button("← Back", 250, 125, 80, 25, back_selected)
|
||||||
|
|
||||||
|
# Leaderboard data
|
||||||
|
if not self.leaderboard_data:
|
||||||
|
self.draw_text("No leaderboard data available", 320, 200,
|
||||||
|
self.colors['yellow'], 'medium', center=True)
|
||||||
|
else:
|
||||||
|
# Leaderboard panel
|
||||||
|
panel_height = min(250, len(self.leaderboard_data) * 25 + 40)
|
||||||
|
self.draw_panel(50, 160, 540, panel_height,
|
||||||
|
self.colors['black'], self.colors['gray'])
|
||||||
|
|
||||||
|
# Headers
|
||||||
|
self.draw_text("Rank", 60, 175, self.colors['light_blue'], 'small')
|
||||||
|
self.draw_text("Player", 120, 175, self.colors['light_blue'], 'small')
|
||||||
|
self.draw_text("Score", 350, 175, self.colors['light_blue'], 'small')
|
||||||
|
self.draw_text("Games", 450, 175, self.colors['light_blue'], 'small')
|
||||||
|
|
||||||
|
if self.leaderboard_type == "global":
|
||||||
|
self.draw_text("Device", 520, 175, self.colors['light_blue'], 'small')
|
||||||
|
|
||||||
|
# Leaderboard entries
|
||||||
|
for i, entry in enumerate(self.leaderboard_data):
|
||||||
|
entry_y = 195 + i * 22
|
||||||
|
|
||||||
|
# Highlight current user
|
||||||
|
is_current_user = (self.active_profile and
|
||||||
|
entry.get('user_id') == self.active_profile)
|
||||||
|
text_color = self.colors['light_green'] if is_current_user else self.colors['light_gray']
|
||||||
|
|
||||||
|
# Rank
|
||||||
|
self.draw_text(str(entry.get('rank', i+1)), 60, entry_y, text_color, 'tiny')
|
||||||
|
|
||||||
|
# Player name
|
||||||
|
player_name = entry.get('user_id', 'Unknown')
|
||||||
|
if len(player_name) > 20:
|
||||||
|
player_name = player_name[:17] + "..."
|
||||||
|
self.draw_text(player_name, 120, entry_y, text_color, 'tiny')
|
||||||
|
|
||||||
|
# Score
|
||||||
|
score = entry.get('best_score', 0)
|
||||||
|
self.draw_text(str(score), 350, entry_y, text_color, 'tiny')
|
||||||
|
|
||||||
|
# Games
|
||||||
|
games = entry.get('total_games', 0)
|
||||||
|
self.draw_text(str(games), 450, entry_y, text_color, 'tiny')
|
||||||
|
|
||||||
|
# Device (global leaderboard only)
|
||||||
|
if self.leaderboard_type == "global":
|
||||||
|
device = entry.get('device_id', '')[:8]
|
||||||
|
self.draw_text(device, 520, entry_y, text_color, 'tiny')
|
||||||
|
|
||||||
|
# Instructions
|
||||||
|
self.draw_panel(10, 420, 620, 40, self.colors['black'], self.colors['dark_gray'])
|
||||||
|
self.draw_text("Left/Right: Toggle Type • Enter: Select • Escape: Back",
|
||||||
|
320, 435, self.colors['light_gray'], 'tiny', center=True)
|
||||||
|
|
||||||
|
def render_profile_stats(self):
|
||||||
|
"""Render detailed profile statistics"""
|
||||||
|
if not self.active_profile:
|
||||||
|
self.current_screen = "main_menu"
|
||||||
|
return
|
||||||
|
|
||||||
|
profile = self.profiles[self.active_profile]
|
||||||
|
|
||||||
|
# Header
|
||||||
|
self.renderer.fill((0, 0, 640, 80), self.colors['dark_gray'])
|
||||||
|
title = f"Stats: {profile.name}"
|
||||||
|
self.draw_text(title, 320, 15, self.colors['light_blue'], 'title', center=True)
|
||||||
|
|
||||||
|
# Get additional info from integration system
|
||||||
|
integration_info = self.integration.get_profile_info()
|
||||||
|
|
||||||
|
# Main stats panel
|
||||||
|
self.draw_panel(50, 90, 540, 260, self.colors['dark_gray'], self.colors['gray'])
|
||||||
|
|
||||||
|
# Basic stats
|
||||||
|
stats_y = 105
|
||||||
|
line_height = 20
|
||||||
|
|
||||||
|
self.draw_text("Profile Statistics", 320, stats_y, self.colors['white'], 'large', center=True)
|
||||||
|
stats_y += 30
|
||||||
|
|
||||||
|
# Local stats
|
||||||
|
self.draw_text("Local Statistics:", 60, stats_y, self.colors['light_blue'], 'medium')
|
||||||
|
stats_y += line_height
|
||||||
|
|
||||||
|
self.draw_text(f"Games Played: {profile.games_played}", 70, stats_y, self.colors['light_gray'], 'small')
|
||||||
|
stats_y += line_height
|
||||||
|
|
||||||
|
self.draw_text(f"Best Score: {profile.best_score}", 70, stats_y, self.colors['light_gray'], 'small')
|
||||||
|
stats_y += line_height
|
||||||
|
|
||||||
|
self.draw_text(f"Total Score: {profile.total_score}", 70, stats_y, self.colors['light_gray'], 'small')
|
||||||
|
stats_y += line_height
|
||||||
|
|
||||||
|
self.draw_text(f"Achievements: {len(profile.achievements)}", 70, stats_y, self.colors['light_gray'], 'small')
|
||||||
|
stats_y += line_height
|
||||||
|
|
||||||
|
# Creation and last played dates
|
||||||
|
try:
|
||||||
|
created = datetime.fromisoformat(profile.created_date).strftime("%Y-%m-%d")
|
||||||
|
last_played = datetime.fromisoformat(profile.last_played).strftime("%Y-%m-%d")
|
||||||
|
except:
|
||||||
|
created = "Unknown"
|
||||||
|
last_played = "Unknown"
|
||||||
|
|
||||||
|
self.draw_text(f"Created: {created}", 70, stats_y, self.colors['light_gray'], 'small')
|
||||||
|
stats_y += line_height
|
||||||
|
|
||||||
|
self.draw_text(f"Last Played: {last_played}", 70, stats_y, self.colors['light_gray'], 'small')
|
||||||
|
stats_y += line_height
|
||||||
|
|
||||||
|
# Server stats (if available)
|
||||||
|
if self.api_enabled and integration_info:
|
||||||
|
stats_y += 10
|
||||||
|
self.draw_text("Server Status:", 60, stats_y, self.colors['light_green'], 'medium')
|
||||||
|
stats_y += line_height
|
||||||
|
|
||||||
|
self.draw_text(f"Device ID: {integration_info.get('device_id', 'Unknown')}", 70, stats_y, self.colors['light_gray'], 'small')
|
||||||
|
stats_y += line_height
|
||||||
|
|
||||||
|
self.draw_text("✓ Profile synced with server", 70, stats_y, self.colors['light_green'], 'small')
|
||||||
|
else:
|
||||||
|
stats_y += 10
|
||||||
|
self.draw_text("Server Status:", 60, stats_y, self.colors['red'], 'medium')
|
||||||
|
stats_y += line_height
|
||||||
|
self.draw_text("✗ Server offline", 70, stats_y, self.colors['red'], 'small')
|
||||||
|
|
||||||
|
# Settings summary
|
||||||
|
stats_y = 105
|
||||||
|
settings_x = 350
|
||||||
|
|
||||||
|
self.draw_text("Current Settings:", settings_x, stats_y + 30, self.colors['light_blue'], 'medium')
|
||||||
|
stats_y += 50
|
||||||
|
|
||||||
|
difficulty = profile.settings.get('difficulty', 'normal').title()
|
||||||
|
self.draw_text(f"Difficulty: {difficulty}", settings_x + 10, stats_y, self.colors['light_gray'], 'small')
|
||||||
|
stats_y += line_height
|
||||||
|
|
||||||
|
sound_vol = profile.settings.get('sound_volume', 50)
|
||||||
|
self.draw_text(f"Sound Volume: {sound_vol}%", settings_x + 10, stats_y, self.colors['light_gray'], 'small')
|
||||||
|
stats_y += line_height
|
||||||
|
|
||||||
|
music_vol = profile.settings.get('music_volume', 50)
|
||||||
|
self.draw_text(f"Music Volume: {music_vol}%", settings_x + 10, stats_y, self.colors['light_gray'], 'small')
|
||||||
|
stats_y += line_height
|
||||||
|
|
||||||
|
screen_shake = "On" if profile.settings.get('screen_shake', True) else "Off"
|
||||||
|
self.draw_text(f"Screen Shake: {screen_shake}", settings_x + 10, stats_y, self.colors['light_gray'], 'small')
|
||||||
|
|
||||||
|
# Back button
|
||||||
|
back_selected = (self.selected_index == 0)
|
||||||
|
self.draw_button("← Back", 270, 370, 100, 30, back_selected)
|
||||||
|
|
||||||
|
# Instructions
|
||||||
|
self.draw_panel(10, 420, 620, 40, self.colors['black'], self.colors['dark_gray'])
|
||||||
|
self.draw_text("Enter: Back • Escape: Back to Main Menu",
|
||||||
|
320, 435, self.colors['light_gray'], 'tiny', center=True)
|
||||||
|
|
||||||
|
def render_error_dialog(self):
|
||||||
|
"""Render error dialog overlay"""
|
||||||
|
# Semi-transparent overlay
|
||||||
|
overlay_color = sdl2.ext.Color(0, 0, 0, 128) # Black with alpha
|
||||||
|
self.renderer.fill((0, 0, 640, 480), overlay_color)
|
||||||
|
|
||||||
|
# Error dialog box
|
||||||
|
dialog_width = 400
|
||||||
|
dialog_height = 120
|
||||||
|
dialog_x = (640 - dialog_width) // 2
|
||||||
|
dialog_y = (480 - dialog_height) // 2
|
||||||
|
|
||||||
|
# Dialog background with red border
|
||||||
|
self.draw_panel(dialog_x, dialog_y, dialog_width, dialog_height,
|
||||||
|
self.colors['black'], self.colors['red'], border_width=4)
|
||||||
|
|
||||||
|
# Error title
|
||||||
|
self.draw_text("ERROR", dialog_x + dialog_width // 2, dialog_y + 20,
|
||||||
|
self.colors['red'], 'large', center=True)
|
||||||
|
|
||||||
|
# Error message
|
||||||
|
self.draw_text(self.error_message, dialog_x + dialog_width // 2, dialog_y + 50,
|
||||||
|
self.colors['white'], 'medium', center=True)
|
||||||
|
|
||||||
|
# Dismiss instruction
|
||||||
|
self.draw_text("Press any key to continue...", dialog_x + dialog_width // 2, dialog_y + 80,
|
||||||
|
self.colors['light_gray'], 'small', center=True)
|
||||||
|
|
||||||
def draw_text(self, text: str, x: int, y: int, color, font_type='medium', center: bool = False):
|
def draw_text(self, text: str, x: int, y: int, color, font_type='medium', center: bool = False):
|
||||||
"""Draw text on screen with improved styling"""
|
"""Draw text on screen with improved styling"""
|
||||||
if not text:
|
if not text:
|
||||||
@@ -774,10 +1138,14 @@ class ProfileManager:
|
|||||||
for i in range(border_width):
|
for i in range(border_width):
|
||||||
self.renderer.draw_rect((x + i, y + i, width - 2*i, height - 2*i), border_color)
|
self.renderer.draw_rect((x + i, y + i, width - 2*i, height - 2*i), border_color)
|
||||||
|
|
||||||
def draw_button(self, text: str, x: int, y: int, width: int, height: int, selected: bool = False):
|
def draw_button(self, text: str, x: int, y: int, width: int, height: int, selected: bool = False, disabled: bool = False):
|
||||||
"""Draw a styled button"""
|
"""Draw a styled button"""
|
||||||
# Button colors based on state
|
# Button colors based on state
|
||||||
if selected:
|
if disabled:
|
||||||
|
bg_color = self.colors['black']
|
||||||
|
border_color = self.colors['dark_gray']
|
||||||
|
text_color = self.colors['dark_gray']
|
||||||
|
elif selected:
|
||||||
bg_color = self.colors['blue']
|
bg_color = self.colors['blue']
|
||||||
border_color = self.colors['light_blue']
|
border_color = self.colors['light_blue']
|
||||||
text_color = self.colors['white']
|
text_color = self.colors['white']
|
||||||
@@ -798,6 +1166,11 @@ class ProfileManager:
|
|||||||
"""Handle keyboard input for navigation only (no text input)"""
|
"""Handle keyboard input for navigation only (no text input)"""
|
||||||
key = event.key.keysym.sym
|
key = event.key.keysym.sym
|
||||||
|
|
||||||
|
# If error dialog is shown, dismiss it on any key press
|
||||||
|
if self.show_error:
|
||||||
|
self.show_error = False
|
||||||
|
return
|
||||||
|
|
||||||
# Navigation mode only
|
# Navigation mode only
|
||||||
if key == sdl2.SDLK_UP:
|
if key == sdl2.SDLK_UP:
|
||||||
self.navigate_up()
|
self.navigate_up()
|
||||||
@@ -852,22 +1225,34 @@ class ProfileManager:
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Entry point"""
|
"""Entry point"""
|
||||||
print("Starting Profile Manager...")
|
print("Starting Profile Manager with User Profile Integration...")
|
||||||
|
print("Features:")
|
||||||
|
print(" • Profile creation and management")
|
||||||
|
print(" • API server integration for leaderboards")
|
||||||
|
print(" • Device-specific and global leaderboards")
|
||||||
|
print(" • Detailed profile statistics")
|
||||||
|
print(" • Settings management")
|
||||||
|
print("")
|
||||||
print("Controls:")
|
print("Controls:")
|
||||||
print(" Arrow Keys: Navigate menus")
|
print(" Arrow Keys: Navigate menus")
|
||||||
print(" Enter/Space: Confirm/Select")
|
print(" Enter/Space: Confirm/Select")
|
||||||
print(" Escape: Back/Cancel")
|
print(" Escape: Back/Cancel")
|
||||||
print(" Delete/Backspace: Delete")
|
print(" Delete/Backspace: Delete")
|
||||||
print(" Tab: Menu (reserved)")
|
print(" Tab: Menu (reserved)")
|
||||||
print(" Keyboard typing: Text input when creating profiles")
|
print(" Left/Right: Adjust settings and toggle options")
|
||||||
print("")
|
print("")
|
||||||
print("Gamepad Controls (if connected):")
|
print("Gamepad Controls (if connected):")
|
||||||
print(" D-Pad: Navigate menus")
|
print(" D-Pad: Navigate menus")
|
||||||
print(" A Button: Confirm/Select")
|
print(" A Button: Confirm/Select")
|
||||||
print(" B Button: Back/Cancel")
|
print(" B Button: Back/Cancel")
|
||||||
print(" X Button: Delete")
|
print(" X Button: Delete")
|
||||||
|
print("")
|
||||||
|
|
||||||
manager = ProfileManager()
|
manager = ProfileManager()
|
||||||
|
print(f"Device ID: {manager.device_id}")
|
||||||
|
print(f"API Server: {'Connected' if manager.api_enabled else 'Offline'}")
|
||||||
|
print("")
|
||||||
|
|
||||||
manager.run()
|
manager.run()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import json
|
|||||||
|
|
||||||
from engine import maze, sdl2 as engine, controls, graphics, unit_manager, scoring
|
from engine import maze, sdl2 as engine, controls, graphics, unit_manager, scoring
|
||||||
from units import points
|
from units import points
|
||||||
from user_profile_integration import UserProfileIntegration, get_global_leaderboard
|
from engine.user_profile_integration import UserProfileIntegration, get_global_leaderboard
|
||||||
|
|
||||||
|
|
||||||
class MiceMaze(
|
class MiceMaze(
|
||||||
|
|||||||
Binary file not shown.
@@ -1,112 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Simple Profile Manager with API Integration Demo
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
from user_profile_integration import UserProfileIntegration
|
|
||||||
|
|
||||||
def main():
|
|
||||||
print("=== Profile Manager with API Integration ===")
|
|
||||||
|
|
||||||
# Initialize the integration
|
|
||||||
integration = UserProfileIntegration()
|
|
||||||
|
|
||||||
print(f"\\nDevice ID: {integration.get_device_id()}")
|
|
||||||
print(f"Active Profile: {integration.get_profile_name()}")
|
|
||||||
print(f"API Server: {'Connected' if integration.api_enabled else 'Offline'}")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
print("\\n=== MAIN MENU ===")
|
|
||||||
print("1. Show Profile Info")
|
|
||||||
print("2. View Online Leaderboard")
|
|
||||||
print("3. View All Device Users")
|
|
||||||
print("4. Submit Test Score")
|
|
||||||
print("5. Create New Profile")
|
|
||||||
print("6. Exit")
|
|
||||||
|
|
||||||
try:
|
|
||||||
choice = input("\\nSelect option (1-6): ").strip()
|
|
||||||
|
|
||||||
if choice == "1":
|
|
||||||
# Show profile info
|
|
||||||
info = integration.get_profile_info()
|
|
||||||
if info:
|
|
||||||
print(f"\\n=== PROFILE INFO ===")
|
|
||||||
for key, value in info.items():
|
|
||||||
print(f"{key.replace('_', ' ').title()}: {value}")
|
|
||||||
else:
|
|
||||||
print("No active profile loaded")
|
|
||||||
|
|
||||||
elif choice == "2":
|
|
||||||
# Show leaderboard
|
|
||||||
if not integration.api_enabled:
|
|
||||||
print("API server not available")
|
|
||||||
continue
|
|
||||||
|
|
||||||
print(f"\\n=== LEADERBOARD ===")
|
|
||||||
leaderboard = integration.get_device_leaderboard(10)
|
|
||||||
if leaderboard:
|
|
||||||
for entry in leaderboard:
|
|
||||||
print(f"{entry['rank']}. {entry['user_id']}: {entry['best_score']} pts ({entry['total_games']} games)")
|
|
||||||
else:
|
|
||||||
print("No scores recorded yet")
|
|
||||||
|
|
||||||
elif choice == "3":
|
|
||||||
# Show all users
|
|
||||||
if not integration.api_enabled:
|
|
||||||
print("API server not available")
|
|
||||||
continue
|
|
||||||
|
|
||||||
print(f"\\n=== DEVICE USERS ===")
|
|
||||||
users = integration.get_all_device_users()
|
|
||||||
if users:
|
|
||||||
for user in users:
|
|
||||||
print(f"{user['user_id']}: Best {user['best_score']}, {user['total_scores']} games")
|
|
||||||
else:
|
|
||||||
print("No users registered yet")
|
|
||||||
|
|
||||||
elif choice == "4":
|
|
||||||
# Submit test score
|
|
||||||
if not integration.current_profile:
|
|
||||||
print("No active profile to submit score for")
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
score = int(input("Enter test score: "))
|
|
||||||
result = integration.update_game_stats(score, True)
|
|
||||||
if result:
|
|
||||||
print(f"Score {score} submitted successfully!")
|
|
||||||
else:
|
|
||||||
print("Failed to submit score")
|
|
||||||
except ValueError:
|
|
||||||
print("Invalid score entered")
|
|
||||||
|
|
||||||
elif choice == "5":
|
|
||||||
# Create new profile - simplified for demo
|
|
||||||
name = input("Enter new profile name: ").strip()
|
|
||||||
if not name:
|
|
||||||
print("Name cannot be empty")
|
|
||||||
continue
|
|
||||||
|
|
||||||
success = integration.register_new_user(name)
|
|
||||||
if success or not integration.api_enabled:
|
|
||||||
print(f"Profile '{name}' created successfully!")
|
|
||||||
else:
|
|
||||||
print("Failed to create profile")
|
|
||||||
|
|
||||||
elif choice == "6":
|
|
||||||
print("Goodbye!")
|
|
||||||
break
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("Invalid choice")
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print("\\nGoodbye!")
|
|
||||||
break
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error: {e}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Test script for Mice Game Score API
|
|
||||||
"""
|
|
||||||
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
|
|
||||||
API_BASE_URL = "http://localhost:8000"
|
|
||||||
|
|
||||||
def test_api():
|
|
||||||
"""Test all API endpoints"""
|
|
||||||
print("Testing Mice Game Score API...")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
# Test device and user IDs
|
|
||||||
device_id = "DEV-TEST001"
|
|
||||||
user1 = "TestUser1"
|
|
||||||
user2 = "TestUser2"
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 1. Test root endpoint
|
|
||||||
print("\n1. Testing root endpoint:")
|
|
||||||
response = requests.get(f"{API_BASE_URL}/")
|
|
||||||
print(f"Status: {response.status_code}")
|
|
||||||
print(f"Response: {response.json()}")
|
|
||||||
|
|
||||||
# 2. Test user signup
|
|
||||||
print(f"\n2. Testing user signup for {user1}:")
|
|
||||||
response = requests.post(f"{API_BASE_URL}/signup/{device_id}/{user1}")
|
|
||||||
print(f"Status: {response.status_code}")
|
|
||||||
print(f"Response: {response.json()}")
|
|
||||||
|
|
||||||
print(f"\n3. Testing user signup for {user2}:")
|
|
||||||
response = requests.post(f"{API_BASE_URL}/signup/{device_id}/{user2}")
|
|
||||||
print(f"Status: {response.status_code}")
|
|
||||||
print(f"Response: {response.json()}")
|
|
||||||
|
|
||||||
# 4. Test duplicate signup (should fail)
|
|
||||||
print(f"\n4. Testing duplicate signup for {user1} (should fail):")
|
|
||||||
response = requests.post(f"{API_BASE_URL}/signup/{device_id}/{user1}")
|
|
||||||
print(f"Status: {response.status_code}")
|
|
||||||
print(f"Response: {response.json()}")
|
|
||||||
|
|
||||||
# 5. Test getting users for device
|
|
||||||
print(f"\n5. Testing get users for device {device_id}:")
|
|
||||||
response = requests.get(f"{API_BASE_URL}/users/{device_id}")
|
|
||||||
print(f"Status: {response.status_code}")
|
|
||||||
print(f"Response: {response.json()}")
|
|
||||||
|
|
||||||
# 6. Test score submission
|
|
||||||
print(f"\n6. Testing score submission for {user1}:")
|
|
||||||
score_data = {
|
|
||||||
"user_id": user1,
|
|
||||||
"device_id": device_id,
|
|
||||||
"score": 1500,
|
|
||||||
"game_completed": True
|
|
||||||
}
|
|
||||||
response = requests.post(
|
|
||||||
f"{API_BASE_URL}/score/{device_id}/{user1}",
|
|
||||||
json=score_data
|
|
||||||
)
|
|
||||||
print(f"Status: {response.status_code}")
|
|
||||||
print(f"Response: {response.json()}")
|
|
||||||
|
|
||||||
# 7. Test more score submissions
|
|
||||||
scores_to_test = [
|
|
||||||
(user1, 2000, True),
|
|
||||||
(user1, 1200, False),
|
|
||||||
(user2, 1800, True),
|
|
||||||
(user2, 2500, True)
|
|
||||||
]
|
|
||||||
|
|
||||||
print(f"\n7. Testing multiple score submissions:")
|
|
||||||
for user, score, completed in scores_to_test:
|
|
||||||
score_data = {
|
|
||||||
"user_id": user,
|
|
||||||
"device_id": device_id,
|
|
||||||
"score": score,
|
|
||||||
"game_completed": completed
|
|
||||||
}
|
|
||||||
response = requests.post(
|
|
||||||
f"{API_BASE_URL}/score/{device_id}/{user}",
|
|
||||||
json=score_data
|
|
||||||
)
|
|
||||||
print(f" {user} - Score: {score}, Completed: {completed} -> Status: {response.status_code}")
|
|
||||||
time.sleep(0.1) # Small delay between requests
|
|
||||||
|
|
||||||
# 8. Test score submission for non-registered user (should fail)
|
|
||||||
print(f"\n8. Testing score submission for non-registered user (should fail):")
|
|
||||||
score_data = {
|
|
||||||
"user_id": "NonExistentUser",
|
|
||||||
"device_id": device_id,
|
|
||||||
"score": 1000,
|
|
||||||
"game_completed": True
|
|
||||||
}
|
|
||||||
response = requests.post(
|
|
||||||
f"{API_BASE_URL}/score/{device_id}/NonExistentUser",
|
|
||||||
json=score_data
|
|
||||||
)
|
|
||||||
print(f"Status: {response.status_code}")
|
|
||||||
print(f"Response: {response.json()}")
|
|
||||||
|
|
||||||
# 9. Test getting user scores
|
|
||||||
print(f"\n9. Testing get scores for {user1}:")
|
|
||||||
response = requests.get(f"{API_BASE_URL}/scores/{device_id}/{user1}")
|
|
||||||
print(f"Status: {response.status_code}")
|
|
||||||
scores = response.json()
|
|
||||||
print(f"Number of scores: {len(scores)}")
|
|
||||||
for score in scores:
|
|
||||||
print(f" Score: {score['score']}, Completed: {score['game_completed']}, Time: {score['timestamp']}")
|
|
||||||
|
|
||||||
# 10. Test leaderboard
|
|
||||||
print(f"\n10. Testing leaderboard for device {device_id}:")
|
|
||||||
response = requests.get(f"{API_BASE_URL}/leaderboard/{device_id}")
|
|
||||||
print(f"Status: {response.status_code}")
|
|
||||||
leaderboard = response.json()
|
|
||||||
print("Leaderboard:")
|
|
||||||
for entry in leaderboard:
|
|
||||||
print(f" Rank {entry['rank']}: {entry['user_id']} - Best Score: {entry['best_score']} ({entry['total_games']} games)")
|
|
||||||
|
|
||||||
# 11. Test final user list
|
|
||||||
print(f"\n11. Final user list for device {device_id}:")
|
|
||||||
response = requests.get(f"{API_BASE_URL}/users/{device_id}")
|
|
||||||
users = response.json()
|
|
||||||
for user in users:
|
|
||||||
print(f" {user['user_id']}: Best Score: {user['best_score']}, Total Games: {user['total_scores']}")
|
|
||||||
|
|
||||||
print("\n" + "=" * 50)
|
|
||||||
print("API Testing completed successfully!")
|
|
||||||
|
|
||||||
except requests.exceptions.ConnectionError:
|
|
||||||
print("ERROR: Could not connect to API server.")
|
|
||||||
print("Make sure the API server is running with: python score_api.py")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"ERROR: {e}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test_api()
|
|
||||||
+35
-3
@@ -3,7 +3,7 @@
|
|||||||
"Player1": {
|
"Player1": {
|
||||||
"name": "Player1",
|
"name": "Player1",
|
||||||
"created_date": "2024-01-15T10:30:00",
|
"created_date": "2024-01-15T10:30:00",
|
||||||
"last_played": "2024-01-20T14:45:00",
|
"last_played": "2025-08-21T17:57:06.304649",
|
||||||
"games_played": 25,
|
"games_played": 25,
|
||||||
"total_score": 15420,
|
"total_score": 15420,
|
||||||
"best_score": 980,
|
"best_score": 980,
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
"Alice": {
|
"Alice": {
|
||||||
"name": "Alice",
|
"name": "Alice",
|
||||||
"created_date": "2024-01-10T09:15:00",
|
"created_date": "2024-01-10T09:15:00",
|
||||||
"last_played": "2025-08-21T13:00:29.825909",
|
"last_played": "2025-08-21T17:39:28.013833",
|
||||||
"games_played": 43,
|
"games_played": 43,
|
||||||
"total_score": 33000,
|
"total_score": 33000,
|
||||||
"best_score": 1250,
|
"best_score": 1250,
|
||||||
@@ -58,7 +58,39 @@
|
|||||||
"auto_save": true
|
"auto_save": true
|
||||||
},
|
},
|
||||||
"achievements": []
|
"achievements": []
|
||||||
|
},
|
||||||
|
"AA": {
|
||||||
|
"name": "AA",
|
||||||
|
"created_date": "2025-08-21T17:12:50.321070",
|
||||||
|
"last_played": "2025-08-21T17:12:50.321070",
|
||||||
|
"games_played": 0,
|
||||||
|
"total_score": 0,
|
||||||
|
"best_score": 0,
|
||||||
|
"settings": {
|
||||||
|
"difficulty": "normal",
|
||||||
|
"sound_volume": 50,
|
||||||
|
"music_volume": 50,
|
||||||
|
"screen_shake": true,
|
||||||
|
"auto_save": true
|
||||||
|
},
|
||||||
|
"achievements": []
|
||||||
|
},
|
||||||
|
"B0B": {
|
||||||
|
"name": "B0B",
|
||||||
|
"created_date": "2025-08-21T18:03:12.189612",
|
||||||
|
"last_played": "2025-08-21T18:04:57.426796",
|
||||||
|
"games_played": 0,
|
||||||
|
"total_score": 0,
|
||||||
|
"best_score": 0,
|
||||||
|
"settings": {
|
||||||
|
"difficulty": "normal",
|
||||||
|
"sound_volume": 50,
|
||||||
|
"music_volume": 50,
|
||||||
|
"screen_shake": true,
|
||||||
|
"auto_save": true
|
||||||
|
},
|
||||||
|
"achievements": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"active_profile": "MAT"
|
"active_profile": "B0B"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user