@@ -1,5 +1,6 @@ |
| 1 | # apps/trees/views.py | 1 | # apps/trees/views.py |
| 2 | from django.http import HttpResponse | 2 | from django.http import HttpResponse |
| | 3 | +from django.db import models |
| 3 | from rest_framework import viewsets, status | 4 | from rest_framework import viewsets, status |
| 4 | from rest_framework.decorators import action | 5 | from rest_framework.decorators import action |
| 5 | from rest_framework.response import Response | 6 | from rest_framework.response import Response |
@@ -99,6 +100,11 @@ class FileSystemTreeViewSet(viewsets.ModelViewSet): |
| 99 | "command": "score", | 100 | "command": "score", |
| 100 | "description": "Show current score and moles killed", | 101 | "description": "Show current score and moles killed", |
| 101 | "examples": ["score"] | 102 | "examples": ["score"] |
| | 103 | + }, |
| | 104 | + { |
| | 105 | + "command": "exit", |
| | 106 | + "description": "End the game and save your score", |
| | 107 | + "examples": ["exit"] |
| 102 | } | 108 | } |
| 103 | ], | 109 | ], |
| 104 | "special_paths": [ | 110 | "special_paths": [ |
@@ -239,7 +245,7 @@ class FileSystemTreeViewSet(viewsets.ModelViewSet): |
| 239 | response_data.update({ | 245 | response_data.update({ |
| 240 | 'mole_escaped': True, | 246 | 'mole_escaped': True, |
| 241 | 'escape_data': escape_data, | 247 | 'escape_data': escape_data, |
| 242 | - 'mole_direction': mole_direction, # Add this line | 248 | + 'mole_direction': mole_direction, |
| 243 | 'message': f"The mole escaped from {escape_data['old_location']}! A new mole appeared!" | 249 | 'message': f"The mole escaped from {escape_data['old_location']}! A new mole appeared!" |
| 244 | }) | 250 | }) |
| 245 | | 251 | |
@@ -252,12 +258,6 @@ class FileSystemTreeViewSet(viewsets.ModelViewSet): |
| 252 | session.save() | 258 | session.save() |
| 253 | except GameSession.DoesNotExist: | 259 | except GameSession.DoesNotExist: |
| 254 | pass | 260 | pass |
| 255 | - | | |
| 256 | - response_data.update({ | | |
| 257 | - 'mole_escaped': True, | | |
| 258 | - 'escape_data': escape_data, | | |
| 259 | - 'message': f"The mole escaped from {escape_data['old_location']}! A new mole appeared!" | | |
| 260 | - }) | | |
| 261 | | 261 | |
| 262 | return Response(response_data) | 262 | return Response(response_data) |
| 263 | | 263 | |
@@ -632,6 +632,34 @@ class FileSystemTreeViewSet(viewsets.ModelViewSet): |
| 632 | response_data['output'] = "No active session to score." | 632 | response_data['output'] = "No active session to score." |
| 633 | response_data['success'] = True | 633 | response_data['success'] = True |
| 634 | | 634 | |
| | 635 | + elif cmd == 'exit': |
| | 636 | + # Complete the game |
| | 637 | + tree.complete_game() |
| | 638 | + |
| | 639 | + # Complete the session if it exists |
| | 640 | + if session: |
| | 641 | + session.completed_at = timezone.now() |
| | 642 | + session.time_taken = session.completed_at - session.started_at |
| | 643 | + session.save() |
| | 644 | + |
| | 645 | + response_data['output'] = f"Game Over! Final score: {session.calculate_score()} | Moles killed: {session.moles_killed}" |
| | 646 | + response_data['score'] = session.calculate_score() |
| | 647 | + response_data['game_completed'] = True |
| | 648 | + response_data['session_completed'] = True |
| | 649 | + response_data['final_stats'] = { |
| | 650 | + 'score': session.calculate_score(), |
| | 651 | + 'moles_killed': session.moles_killed, |
| | 652 | + 'moles_escaped': session.moles_escaped, |
| | 653 | + 'commands_used': session.commands_used, |
| | 654 | + 'time_taken': str(session.time_taken), |
| | 655 | + 'directories_visited': session.directories_visited |
| | 656 | + } |
| | 657 | + else: |
| | 658 | + response_data['output'] = "Game ended. No session to record." |
| | 659 | + response_data['game_completed'] = True |
| | 660 | + |
| | 661 | + response_data['success'] = True |
| | 662 | + |
| 635 | elif cmd == 'help': | 663 | elif cmd == 'help': |
| 636 | response_data['output'] = """Available commands: | 664 | response_data['output'] = """Available commands: |
| 637 | cd <directory> - Change directory (supports ~, -, and ..) | 665 | cd <directory> - Change directory (supports ~, -, and ..) |
@@ -645,6 +673,7 @@ echo <text> - Display text (supports $HOME, $PWD, $OLDPWD) |
| 645 | tree [-L depth] - Display directory tree (use -L to limit depth) | 673 | tree [-L depth] - Display directory tree (use -L to limit depth) |
| 646 | killall moles - Eliminate moles (when in the same directory) | 674 | killall moles - Eliminate moles (when in the same directory) |
| 647 | score - Show current score and moles killed | 675 | score - Show current score and moles killed |
| | 676 | +exit - End the game and save your score |
| 648 | help - Show this help message | 677 | help - Show this help message |
| 649 | | 678 | |
| 650 | Special paths: | 679 | Special paths: |
@@ -715,12 +744,75 @@ class GameSessionViewSet(viewsets.ModelViewSet): |
| 715 | queryset = GameSession.objects.all() | 744 | queryset = GameSession.objects.all() |
| 716 | serializer_class = GameSessionSerializer | 745 | serializer_class = GameSessionSerializer |
| 717 | | 746 | |
| | 747 | + @action(detail=False, methods=['post']) |
| | 748 | + def save_player_name(self, request): |
| | 749 | + """Save player name for a completed session""" |
| | 750 | + session_id = request.data.get('session_id') |
| | 751 | + player_name = request.data.get('player_name', 'Anonymous') |
| | 752 | + |
| | 753 | + if not session_id: |
| | 754 | + return Response( |
| | 755 | + {'error': 'No session ID provided'}, |
| | 756 | + status=status.HTTP_400_BAD_REQUEST |
| | 757 | + ) |
| | 758 | + |
| | 759 | + try: |
| | 760 | + session = GameSession.objects.get(id=session_id) |
| | 761 | + session.player_name = player_name |
| | 762 | + session.save() |
| | 763 | + |
| | 764 | + # Get leaderboard position |
| | 765 | + better_scores = GameSession.objects.filter( |
| | 766 | + completed_at__isnull=False |
| | 767 | + ).exclude(id=session_id).annotate( |
| | 768 | + score=models.F('moles_killed') * 1000 # Simplified score calculation |
| | 769 | + ).filter(score__gt=session.calculate_score()).count() |
| | 770 | + |
| | 771 | + return Response({ |
| | 772 | + 'success': True, |
| | 773 | + 'player_name': player_name, |
| | 774 | + 'score': session.calculate_score(), |
| | 775 | + 'leaderboard_position': better_scores + 1 |
| | 776 | + }) |
| | 777 | + except GameSession.DoesNotExist: |
| | 778 | + return Response( |
| | 779 | + {'error': 'Session not found'}, |
| | 780 | + status=status.HTTP_404_NOT_FOUND |
| | 781 | + ) |
| | 782 | + |
| 718 | @action(detail=False, methods=['get']) | 783 | @action(detail=False, methods=['get']) |
| 719 | def leaderboard(self, request): | 784 | def leaderboard(self, request): |
| 720 | - """Get the leaderboard of fastest completions""" | 785 | + """Get the leaderboard of top scores""" |
| | 786 | + # Get top 10 completed sessions |
| 721 | completed_sessions = GameSession.objects.filter( | 787 | completed_sessions = GameSession.objects.filter( |
| 722 | completed_at__isnull=False | 788 | completed_at__isnull=False |
| 723 | - ).order_by('time_taken', 'commands_used')[:20] | 789 | + )[:20] # Get more than 10 to handle sorting by score |
| | 790 | + |
| | 791 | + leaderboard_data = [] |
| | 792 | + for session in completed_sessions: |
| | 793 | + score = session.calculate_score() |
| | 794 | + leaderboard_data.append({ |
| | 795 | + 'rank': 0, # Will be set after sorting |
| | 796 | + 'player_name': session.player_name, |
| | 797 | + 'score': score, |
| | 798 | + 'moles_killed': session.moles_killed, |
| | 799 | + 'moles_escaped': session.moles_escaped, |
| | 800 | + 'commands_used': session.commands_used, |
| | 801 | + 'time_taken': str(session.time_taken) if session.time_taken else 'N/A', |
| | 802 | + 'completed_at': session.completed_at |
| | 803 | + }) |
| | 804 | + |
| | 805 | + # Sort by score (highest first) |
| | 806 | + leaderboard_data.sort(key=lambda x: x['score'], reverse=True) |
| | 807 | + |
| | 808 | + # Keep only top 10 |
| | 809 | + leaderboard_data = leaderboard_data[:10] |
| 724 | | 810 | |
| 725 | - serializer = self.get_serializer(completed_sessions, many=True) | 811 | + # Update ranks after sorting |
| 726 | - return Response(serializer.data) | 812 | + for i, entry in enumerate(leaderboard_data, 1): |
| | 813 | + entry['rank'] = i |
| | 814 | + |
| | 815 | + return Response({ |
| | 816 | + 'leaderboard': leaderboard_data, |
| | 817 | + 'total_games': GameSession.objects.filter(completed_at__isnull=False).count() |
| | 818 | + }) |