File size: 8,219 Bytes
699d672
 
9c848ec
d54daef
699d672
d54daef
9c848ec
699d672
 
 
 
 
d54daef
 
699d672
 
 
9c848ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
699d672
9c848ec
699d672
 
 
 
 
 
 
 
 
 
 
9c848ec
699d672
9c848ec
 
 
 
 
 
 
 
 
 
699d672
 
 
 
 
9c848ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
699d672
9c848ec
 
699d672
 
 
 
 
 
 
 
 
 
 
d54daef
 
 
 
 
 
9c848ec
d54daef
 
 
 
9c848ec
699d672
9c848ec
 
 
 
 
699d672
9c848ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
699d672
 
 
 
 
 
 
 
 
9c848ec
 
699d672
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
import gradio as gr
import json
import collections
from transformers import AutoModelForCausalLM, AutoTokenizer

# Define model name - use a very small model
MODEL_NAME = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"  # Chat-tuned 1.1B parameter model

print(f"Loading model {MODEL_NAME}...")
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME,
    low_cpu_mem_usage=True,  # CPU-friendly settings
    device_map="cpu"  # Force CPU usage
)
print("Model loaded successfully!")

def analyze_patterns(player_history, opponent_history):
    """Perform basic pattern analysis on the game history"""
    analysis = {}
    
    # Count frequencies of each move
    move_counts = collections.Counter(opponent_history)
    total_moves = len(opponent_history)
    
    analysis["move_frequencies"] = {
        "Rock": f"{move_counts.get('Rock', 0)}/{total_moves} ({move_counts.get('Rock', 0)*100/total_moves:.1f}%)",
        "Paper": f"{move_counts.get('Paper', 0)}/{total_moves} ({move_counts.get('Paper', 0)*100/total_moves:.1f}%)",
        "Scissors": f"{move_counts.get('Scissors', 0)}/{total_moves} ({move_counts.get('Scissors', 0)*100/total_moves:.1f}%)"
    }
    
    # Check response patterns (what opponent plays after player's moves)
    response_patterns = {
        "After_Rock": collections.Counter(),
        "After_Paper": collections.Counter(),
        "After_Scissors": collections.Counter()
    }
    
    for i in range(len(player_history) - 1):
        player_move = player_history[i]
        opponent_next = opponent_history[i + 1]
        response_patterns[f"After_{player_move}"][opponent_next] += 1
    
    analysis["response_patterns"] = {}
    for pattern, counter in response_patterns.items():
        if sum(counter.values()) > 0:
            most_common = counter.most_common(1)[0]
            analysis["response_patterns"][pattern] = f"{most_common[0]} ({most_common[1]}/{sum(counter.values())})"
    
    # Check for repeating sequences
    last_moves = opponent_history[-3:]
    repeated_sequences = []
    
    # Look for this sequence in history
    for i in range(len(opponent_history) - 3):
        if opponent_history[i:i+3] == last_moves:
            if i+3 < len(opponent_history):
                repeated_sequences.append(opponent_history[i+3])
    
    if repeated_sequences:
        counter = collections.Counter(repeated_sequences)
        most_common = counter.most_common(1)[0]
        analysis["sequence_prediction"] = f"After sequence {' → '.join(last_moves)}, opponent most often plays {most_common[0]} ({most_common[1]}/{len(repeated_sequences)} times)"
    
    return analysis

def format_rps_game_prompt(game_data):
    """Format Rock-Paper-Scissors game data into a prompt for the LLM with pattern analysis"""
    try:
        # Parse the JSON game data
        if isinstance(game_data, str):
            game_data = json.loads(game_data)
        
        # Extract key game information
        player_history = game_data.get("player_history", [])
        opponent_history = game_data.get("opponent_history", [])
        rounds_played = len(player_history)
        player_score = game_data.get("player_score", 0)
        opponent_score = game_data.get("opponent_score", 0)
        draws = game_data.get("draws", 0)
        
        # Perform pattern analysis
        pattern_analysis = analyze_patterns(player_history, opponent_history)
        
        # Format analysis for prompt
        move_frequencies = pattern_analysis.get("move_frequencies", {})
        response_patterns = pattern_analysis.get("response_patterns", {})
        sequence_prediction = pattern_analysis.get("sequence_prediction", "No clear sequence pattern detected")
        
        # Create a more specific prompt with the analysis
        prompt = f"""You are an expert Rock-Paper-Scissors strategy advisor helping a player win.

Game State:
- Rounds played: {rounds_played}
- Player score: {player_score}
- Opponent score: {opponent_score}
- Draws: {draws}
- Player's last 5 moves: {', '.join(player_history[-5:])}
- Opponent's last 5 moves: {', '.join(opponent_history[-5:])}

Pattern Analysis:
- Opponent's move frequencies:
  * Rock: {move_frequencies.get('Rock', 'N/A')}
  * Paper: {move_frequencies.get('Paper', 'N/A')}
  * Scissors: {move_frequencies.get('Scissors', 'N/A')}

- Opponent's response patterns:
  * After player's Rock: {response_patterns.get('After_Rock', 'No clear pattern')}
  * After player's Paper: {response_patterns.get('After_Paper', 'No clear pattern')}
  * After player's Scissors: {response_patterns.get('After_Scissors', 'No clear pattern')}

- Sequence analysis: {sequence_prediction}

Based on this pattern analysis, what should the player choose next (Rock, Paper, or Scissors)?
Explain your reasoning step-by-step, then end with: "Recommendation: [Rock/Paper/Scissors]"
"""
        return prompt
    except Exception as e:
        return f"Error formatting prompt: {str(e)}\n\nPlease provide game data in a valid JSON format."

def generate_advice(game_data):
    """Generate advice based on game data using the LLM"""
    try:
        # Format the prompt
        prompt = format_rps_game_prompt(game_data)
        
        # Generate response from LLM (with CPU-only settings)
        inputs = tokenizer(prompt, return_tensors="pt")
        
        # Set max_length to avoid excessive generation
        outputs = model.generate(
            inputs["input_ids"],
            max_new_tokens=150,  # Allow more tokens for a more detailed response
            do_sample=True, 
            temperature=0.7, 
            top_p=0.9
        )
        full_response = tokenizer.decode(outputs[0], skip_special_tokens=True)
        
        # Extract just the model's response (remove the prompt)
        if full_response.startswith(prompt):
            response = full_response[len(prompt):].strip()
        else:
            response = full_response
            
        # If model response is too short, add fallback advice
        if len(response) < 30:
            pattern_analysis = analyze_patterns(
                json.loads(game_data)["player_history"] if isinstance(game_data, str) else game_data["player_history"],
                json.loads(game_data)["opponent_history"] if isinstance(game_data, str) else game_data["opponent_history"]
            )
            
            # Simple fallback strategy based on pattern analysis
            move_freqs = pattern_analysis.get("move_frequencies", {})
            max_move = max(["Rock", "Paper", "Scissors"], 
                          key=lambda m: float(move_freqs.get(m, "0/0 (0%)").split("(")[1].split("%")[0]))
            
            # Choose counter to opponent's most frequent move
            if max_move == "Rock":
                suggestion = "Paper"
            elif max_move == "Paper":
                suggestion = "Scissors"
            else:
                suggestion = "Rock"
                
            response += f"\n\nBased on opponent's move frequencies, they play {max_move} most often. \nRecommendation: {suggestion}"
        
        return response
    except Exception as e:
        return f"Error generating advice: {str(e)}"

# Sample game data for the example
sample_game_data = {
    "player_history": ["Rock", "Paper", "Scissors", "Rock", "Paper"],
    "opponent_history": ["Scissors", "Rock", "Paper", "Scissors", "Rock"],
    "player_score": 3,
    "opponent_score": 2,
    "draws": 0
}

# Create Gradio interface
with gr.Blocks(title="Rock-Paper-Scissors AI Assistant") as demo:
    gr.Markdown("# Rock-Paper-Scissors AI Assistant")
    gr.Markdown("Enter your game data to get advice on your next move.")
    
    with gr.Row():
        with gr.Column():
            game_data_input = gr.Textbox(
                label="Game State (JSON)",
                placeholder=json.dumps(sample_game_data, indent=2),
                lines=10
            )
            submit_btn = gr.Button("Get Advice")
        
        with gr.Column():
            output = gr.Textbox(label="AI Advice", lines=10)
    
    submit_btn.click(generate_advice, inputs=[game_data_input], outputs=[output])

# Launch the app
demo.launch()