File size: 5,026 Bytes
8268d5c
 
 
 
 
 
 
2008a9e
8268d5c
 
6241033
8268d5c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1207d3b
 
 
 
 
 
 
 
 
 
 
 
 
 
8268d5c
1207d3b
8268d5c
1207d3b
 
 
 
 
 
8268d5c
1207d3b
 
 
8268d5c
1207d3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8268d5c
1207d3b
8268d5c
 
1207d3b
 
 
8268d5c
 
 
1207d3b
 
 
8268d5c
 
 
2008a9e
8268d5c
 
2008a9e
 
 
 
 
 
 
 
6241033
2008a9e
 
 
8268d5c
 
 
 
 
 
 
2008a9e
 
 
 
 
8268d5c
 
2008a9e
 
 
 
 
6241033
2008a9e
8268d5c
6241033
2008a9e
8268d5c
 
2008a9e
8268d5c
 
 
 
 
 
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
import pandas as pd
import difflib
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import gradio as gr
import numpy as np

# Load the data
def load_data():
    try:
        data = pd.read_csv('games_march2025_cleaned.csv', nrows=20000, on_bad_lines='skip', engine='python')
        return data
    except Exception as e:
        print(f"Error loading data: {e}")
        return None

# Prepare the feature vectors for similarity calculation
def prepare_features(data):
    selected_features = ['genres', 'price', 'average_playtime_2weeks', 'tags']
    
    for feature in selected_features:
        data[feature] = data[feature].fillna('')
    
    combined_features = (
        data['genres'] + ' ' +
        data['price'].astype(str) + ' ' +
        data['average_playtime_2weeks'].astype(str) + ' ' +
        data['tags'].astype(str)
    )
    
    vectorizer = TfidfVectorizer()
    feature_vectors = vectorizer.fit_transform(combined_features)
    
    return feature_vectors

# Function to get game recommendations
def get_recommendations(game_name, data, feature_vectors):
    titles = data['name'].tolist()
    close_matches = difflib.get_close_matches(game_name, titles)

    if not close_matches:
        print(f"Couldn't find a close match for: '{game_name}'")
        return "No match found. Try typing a more complete or accurate title."

    match = close_matches[0]
    print(f"Using closest match: {match}")
    game_idx = data[data['name'] == match].index[0]

    similarity = cosine_similarity(feature_vectors)
    scores = list(enumerate(similarity[game_idx]))
    similar_games = sorted(scores, key=lambda x: x[1], reverse=True)

    html = ""

    for i, (idx, score) in enumerate(similar_games[1:10], 1):  # skip the first one (it's the same game)
        game = data.loc[idx]
        name = game['name']
        desc = game['short_description'] or "No description provided."
        if len(desc) > 180:
            desc = desc[:180] + "..."

        img = game.get('header_image', '') or ""

        # Detect supported platforms
        platforms = []
        if game.get('windows') == 1:
            platforms.append('Windows')
        if game.get('mac') == 1:
            platforms.append('Mac')
        if game.get('linux') == 1:
            platforms.append('Linux')
        platforms_str = ", ".join(platforms) if platforms else "Unknown"

        price = game['price']
        meta_score = game.get('metacritic_score')
        meta_display = int(meta_score) if pd.notnull(meta_score) else "N/A"

        pos = game.get('positive', 0)
        neg = game.get('negative', 0)
        total = pos + neg
        pos_pct = f"{(pos / total * 100):.1f}%" if total > 0 else "N/A"

        # Build the card HTML
        html += f'''
        <div style="display:flex; margin-bottom:16px;">
            <img src="{img}" width="130" style="margin-right:12px; border-radius:4px;">
            <div>
                <h4>{i}. {name}</h4>
                <p><b>Platforms:</b> {platforms_str}</p>
                <p><b>Price:</b> ${price}</p>
                <p><b>Metacritic:</b> {meta_display}</p>
                <p><b>Positive Reviews:</b> {pos_pct}</p>
                <p>{desc}</p>
            </div>
        </div>
        <hr>
        '''

    return html


# Gradio interface function
def recommend_games(game_name, max_age, max_price, min_metacritic):
    data = load_data()
    if data is None:
        return "Failed to load data."
    
    # Apply filters BEFORE feature preparation
    data = data[
        (data['required_age'] <= max_age) &
        (data['price'] <= max_price) &
        ((data['metacritic_score'].fillna(0) >= min_metacritic) | data['metacritic_score'].isna())
    ].reset_index(drop=True)
    
    if data.empty:
        return "No games found."

    feature_vectors = prepare_features(data)
    recommendations_html = get_recommendations(game_name, data, feature_vectors)
    
    return recommendations_html

# Create the Gradio interface
with gr.Blocks(title="Steam Game Recommender") as demo:
    gr.Markdown("Steam Game Recommender")
    gr.Markdown("Enter a game you like and customize filters to get similar suggestions.")

    with gr.Row():
        input_text = gr.Textbox(label="Input Steam Game")
    
    with gr.Row():
        max_age_slider = gr.Slider(0, 21, value=17, label="Max Age Rating (Avoid Adult Games)")
        max_price_slider = gr.Slider(0.0, 100.0, value=60.0, step=0.5, label="Maximum Price ($)")
        min_metacritic_slider = gr.Slider(0, 100, value=50, step=1, label="Minimum Metacritic Score")

    with gr.Row():
        submit_btn = gr.Button("Get Recommendations")

    with gr.Row():
        output_text = gr.Markdown(label="Recommendations")

    submit_btn.click(
        fn=recommend_games,
        inputs=[input_text, max_age_slider, max_price_slider, min_metacritic_slider],
        outputs=output_text
    )

# Launch the app
if __name__ == "__main__":
    demo.launch()