Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 199 additions & 0 deletions fix_duplicate_dragons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
"""Utility script to remove duplicate dragons from save database."""
import sys
import os

# Add src to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))

from src.save_system_sqlite import SQLiteSaveSystem
from src.utils.constants import SAVE_FILE


def remove_duplicate_dragons(db_file: str, dry_run: bool = True):
"""Remove duplicate dragons from database.

Dragons are considered duplicates if they have the same:
- dragon_type
- name
- speed
- strength
- rarity
- birth_time (within 1 second)

Args:
db_file: Path to SQLite database
dry_run: If True, only show what would be removed without actually removing
"""
print(f"Opening database: {db_file}")
save_system = SQLiteSaveSystem(db_file)

# Load all dragons
all_dragons = save_system.load_all_dragons()

if not all_dragons:
print("No dragons found in database.")
return

print(f"\nFound {len(all_dragons)} total dragons")

# Track seen dragons and duplicates
seen = {}
duplicates = []

for i, dragon_data in enumerate(all_dragons):
# Create signature for this dragon
signature = (
dragon_data.get('dragon_type'),
dragon_data.get('name'),
dragon_data.get('speed'),
dragon_data.get('strength'),
dragon_data.get('rarity'),
round(dragon_data.get('birth_time', 0)) # Round to nearest second
)

if signature in seen:
# This is a duplicate
original_idx = seen[signature]
original_db_id = all_dragons[original_idx].get('_db_id')
duplicate_db_id = dragon_data.get('_db_id')

duplicates.append({
'index': i,
'db_id': duplicate_db_id,
'data': dragon_data,
'original_index': original_idx,
'original_db_id': original_db_id
})

print(f"\n DUPLICATE FOUND:")
print(f" Dragon #{i+1}: {dragon_data.get('name')} (DB ID: {duplicate_db_id})")
print(f" - Type: {dragon_data.get('dragon_type')}, Stage: {dragon_data.get('stage')}")
print(f" - Speed: {dragon_data.get('speed')}, Strength: {dragon_data.get('strength')}, Rarity: {dragon_data.get('rarity')}")
print(f" - Duplicate of Dragon #{original_idx+1} (DB ID: {original_db_id})")
else:
# First time seeing this dragon
seen[signature] = i

if not duplicates:
print("\n✓ No duplicate dragons found!")
return

print(f"\n{'='*60}")
print(f"Found {len(duplicates)} duplicate dragon(s)")
print(f"Unique dragons: {len(seen)}")
print(f"{'='*60}")

if dry_run:
print("\n⚠ DRY RUN MODE - No changes will be made")
print("Run with --apply to actually remove duplicates")
return

# Remove duplicates
print("\n🗑️ Removing duplicate dragons...")
removed_count = 0

for dup in duplicates:
db_id = dup['db_id']
name = dup['data'].get('name')

if save_system.delete_dragon(db_id):
print(f" ✓ Removed: {name} (DB ID: {db_id})")
removed_count += 1
else:
print(f" ✗ Failed to remove: {name} (DB ID: {db_id})")

print(f"\n{'='*60}")
print(f"✓ Removed {removed_count} duplicate dragon(s)")
print(f"{'='*60}")

# Vacuum database to reclaim space
print("\nOptimizing database...")
save_system.vacuum()
print("✓ Database optimized")


def backup_database(db_file: str) -> str:
"""Create a backup of the database before modifications.

Args:
db_file: Path to database file

Returns:
Path to backup file
"""
import shutil
from datetime import datetime

if not os.path.exists(db_file):
print(f"Database file not found: {db_file}")
return None

timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_file = f"{db_file}.backup_{timestamp}"

try:
shutil.copy2(db_file, backup_file)
print(f"✓ Backup created: {backup_file}")
return backup_file
except Exception as e:
print(f"✗ Failed to create backup: {e}")
return None


def main():
"""Main function."""
import argparse

parser = argparse.ArgumentParser(
description="Remove duplicate dragons from Dragon Riders save database"
)
parser.add_argument(
'--db',
default=SAVE_FILE.replace('.json', '.db'),
help='Path to SQLite database file (default: dragon_save.db)'
)
parser.add_argument(
'--apply',
action='store_true',
help='Actually remove duplicates (default is dry run)'
)
parser.add_argument(
'--no-backup',
action='store_true',
help='Skip creating backup before applying changes'
)

args = parser.parse_args()

if not os.path.exists(args.db):
print(f"Error: Database file not found: {args.db}")
print("\nMake sure you're running this from the DragonRider directory")
return 1

print("="*60)
print("Dragon Riders - Duplicate Dragon Removal Tool")
print("="*60)

# Create backup if applying changes
if args.apply and not args.no_backup:
print("\nCreating backup before making changes...")
backup_file = backup_database(args.db)
if not backup_file:
print("\n⚠ Failed to create backup. Aborting.")
return 1
print()

# Remove duplicates
try:
remove_duplicate_dragons(args.db, dry_run=not args.apply)
print("\n✓ Done!")
return 0
except Exception as e:
print(f"\n✗ Error: {e}")
import traceback
traceback.print_exc()
return 1


if __name__ == "__main__":
sys.exit(main())
9 changes: 8 additions & 1 deletion src/dragon.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ def get_equipped_enchantments(self) -> list:

def to_dict(self) -> Dict[str, Any]:
"""Convert dragon to dictionary for saving."""
return {
data = {
"dragon_type": self.dragon_type,
"name": self.name,
"stage": self.stage,
Expand All @@ -385,6 +385,10 @@ def to_dict(self) -> Dict[str, Any]:
"enchantments": self.enchantments,
"max_enchantments": self.max_enchantments,
}
# Include database ID if it exists
if hasattr(self, '_db_id'):
data['_db_id'] = self._db_id
return data

@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "Dragon":
Expand Down Expand Up @@ -415,6 +419,9 @@ def from_dict(cls, data: Dict[str, Any]) -> "Dragon":
dragon.is_legendary = data.get("is_legendary", data["dragon_type"] in LEGENDARY_DRAGON_TYPES)
dragon.enchantments = data.get("enchantments", [])
dragon.max_enchantments = data.get("max_enchantments", 5)
# Restore database ID if present
if '_db_id' in data:
dragon._db_id = data['_db_id']
return dragon

def __str__(self) -> str:
Expand Down
Loading