feat: implement comprehensive statistics tracking and refactor checkout engine

BREAKING CHANGE: Database schema updated from v3 to v9 (destructive migration)

Major Features:
- Statistics system: Track player performance (darts, points, matches, double-outs)
- Match state management: ONGOING/COMPLETED/CANCELED with state-based queries
- Settings activity: Day/night mode and standard game mode preferences
- CheckoutEngine refactored to standalone utility class with 1/2/3-dart methods

CheckoutConstants Overhaul:
- Generate all possible double-out combinations (~37,200 routes)
- Intelligent route selection (fewer darts > T20/T19 > higher doubles)
- Store both optimal routes and complete alternatives

GameActivity Enhancements:
- Automatic statistics tracking on turn submission and win
- Career average calculation and database updates
- Fixed race condition with dart value capture

Database Changes:
- Added Statistics entity and StatisticsDao
- Player ID migration: int → long for consistency
- Match entity: added MatchState enum and helper methods
- MatchDao: new state-based query methods

Developer Experience:
- Comprehensive JavaDoc across all new/modified classes
- Test harness for checkout generation validation
- Improved code organization with utils package separation
This commit is contained in:
Alexander Doerflinger
2026-01-30 08:21:26 +01:00
parent 2953a1bf67
commit c2f18d9328
26 changed files with 1246 additions and 185 deletions

View File

@@ -0,0 +1,31 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<!-- Sun (Top-Left Position) -->
<path
android:fillColor="@android:color/white"
android:pathData="M6.5,6.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0" />
<!-- Sun Rays (Using strokes to ensure visibility) -->
<path
android:strokeColor="@android:color/white"
android:strokeWidth="1.2"
android:strokeLineCap="round"
android:pathData="M6.5,1.5v1.5 M6.5,11.5v-1.5 M1.5,6.5h1.5 M11.5,6.5h-1.5 M3,3l1,1 M10,10l-1,-1 M3,10l1,-1 M10,3l-1,1" />
<!-- Diagonal Separator (Top-Right to Bottom-Left) -->
<path
android:strokeColor="@android:color/white"
android:strokeWidth="1.2"
android:strokeLineCap="round"
android:pathData="M18.5,5.5 L5.5,18.5" />
<!-- Moon Crescent (Bottom-Right Position) -->
<path
android:fillColor="@android:color/white"
android:pathData="M17.5,12.5c-2.48,0 -4.5,2.02 -4.5,4.5s2.02,4.5 4.5,4.5s4.5,-2.02 4.5,-4.5c0,-0.23 -0.02,-0.46 -0.05,-0.68c-0.49,0.68 -1.29,1.13 -2.2,1.13c-1.49,0 -2.7,-1.21 -2.7,-2.7c0,-0.91 0.45,-1.71 1.13,-2.2c-0.22,-0.03 -0.45,-0.05 -0.68,-0.05z" />
</vector>

View File

@@ -0,0 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<!-- Stylized Dartboard Section for Game Modes -->
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8s8,3.59 8,8S16.41,20 12,20z" />
<path
android:fillColor="@android:color/white"
android:pathData="M12,4.5c-4.14,0 -7.5,3.36 -7.5,7.5s3.36,7.5 7.5,7.5s7.5,-3.36 7.5,-7.5S16.14,4.5 12,4.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5s5,2.24 5,5S14.76,17 12,17z" />
<!-- Center Point (Bullseye) -->
<path
android:fillColor="@android:color/white"
android:pathData="M12,10.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5S12.83,10.5 12,10.5z" />
<!-- Radial Lines (Segments) -->
<path
android:fillColor="@android:color/white"
android:pathData="M12,4V2M12,22v-2M4,12H2m20,0h-2M6.34,6.34L4.93,4.93m14.14,14.14l-1.41,-1.41M6.34,17.66l-1.41,1.41M19.07,4.93l-1.41,1.41" />
</vector>

View File

@@ -3,7 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/midnight_black">
android:background="@color/surface_primary"
android:id="@+id/main">
<ImageView
android:id="@+id/btnDeletePlayer"

View File

@@ -4,7 +4,9 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background_primary">
tools:context=".GameActivity"
android:background="@color/background_primary"
android:id="@+id/main">
<!-- 1. HIGH-IMPACT SCOREBOARD (TOP) -->
<LinearLayout

View File

@@ -0,0 +1,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main">
<FrameLayout
android:id="@+id/settings"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Labels of the standard game mode preference -->
<string-array name="pref_standard_game_mode_labels">
<item>701</item>
<item>501</item>
<item>301</item>
<item>Cricket</item>
</string-array>
<!-- Values of the standard game mode preference -->
<string-array name="pref_standard_game_mode_values">
<item>@string/pref_game_mode_701_value</item>
<item>@string/pref_game_mode_501_value</item>
<item>@string/pref_game_mode_301_value</item>
<item>@string/pref_game_mode_cricket_value</item>
</string-array>
</resources>

View File

@@ -29,8 +29,36 @@
<string name="txt_game_btn_bull">Bull</string>
<string name="txt_game_btn_submit">Submit Turn</string>
<!-- Preference Strings -->
<string name="pref_key_day_night_mode">day_night_mode</string>
<string name="pref_key_standard_game_mode">standard_game_mode</string>
<string name="pref_game_mode_701_value">701</string>
<string name="pref_game_mode_501_value">501</string>
<string name="pref_game_mode_301_value">301</string>
<string name="pref_game_mode_cricket_value">Cricket</string>
<string name="pref_desc_day_night_mode">Day/Night Mode</string>
<string name="pref_title_standard_game_mode">Standard Game Mode</string>
<string name="pref_desc_standard_game_mode">The Standard Game Mode to be selected for the Quick Start\nCurrently selected: %s</string>
<!-- Image Content description -->
<string name="cd_txt_oche_logo">Application Logo</string>
<string name="cd_txt_settings_button">Settings</string>
<string name="cd_text_historic_record">Match History</string>
<string name="title_activity_settings">SettingsActivity</string>
<!-- Preference Titles -->
<string name="messages_header">Messages</string>
<string name="sync_header">Sync</string>
<!-- Messages Preferences -->
<string name="signature_title">Your signature</string>
<string name="reply_title">Default reply action</string>
<!-- Sync Preferences -->
<string name="sync_title">Sync email periodically</string>
<string name="attachment_title">Download incoming attachments</string>
<string name="attachment_summary_on">Automatically download attachments for incoming emails
</string>
<string name="attachment_summary_off">Only download attachments when manually requested</string>
</resources>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<SwitchPreference
app:key="@string/pref_key_day_night_mode"
app:title="@string/pref_desc_day_night_mode"
android:icon="@drawable/ic_day_night_mode"/>
<ListPreference
app:key="@string/pref_key_standard_game_mode"
app:title="@string/pref_title_standard_game_mode"
app:summary="@string/pref_desc_standard_game_mode"
android:icon="@drawable/ic_standard_game_mode"
android:defaultValue="@string/pref_game_mode_501_value"
android:entries="@array/pref_standard_game_mode_labels"
android:entryValues="@array/pref_standard_game_mode_values" />
</PreferenceScreen>