pabberpe commited on
Commit
4b40bf3
1 Parent(s): 0de90bb

Remove Lock on Game Start

Browse files
Files changed (1) hide show
  1. app.py +75 -117
app.py CHANGED
@@ -25,11 +25,11 @@ class GameResults:
25
 
26
  class GameState:
27
  def __init__(self):
28
- self.lock = Lock()
29
- self._reset()
 
30
 
31
- def _reset(self):
32
- """Internal reset method - should be called within a lock"""
33
  self.user_score = 0
34
  self.model_score = 0
35
  self.current_round = 0
@@ -39,50 +39,30 @@ class GameState:
39
  self.last_results: Optional[GameResults] = None
40
  self.processing_submission = False
41
 
42
- def reset(self):
43
- """Public reset method with lock protection"""
44
- with self.lock:
45
- self._reset()
46
-
47
- def start_new_game(self) -> tuple[bool, Optional[str]]:
48
- """
49
- Starts a new game and returns (success, first_image_path)
50
- """
51
- with self.lock:
52
- if self.is_game_active:
53
- return False, None
54
-
55
- try:
56
- self._reset()
57
- self.game_images = load_images()
58
- if not self.game_images:
59
- return False, None
60
-
61
- self.is_game_active = True
62
- return True, self.game_images[0]
63
- except Exception as e:
64
- print(f"Error starting new game: {e}")
65
- self._reset()
66
- return False, None
67
 
68
  def can_submit_guess(self) -> bool:
69
- with self.lock:
70
- return (
71
- self.is_game_active and
72
- not self.processing_submission and
73
- self.current_round < self.total_rounds and
74
- len(self.game_images) > self.current_round
75
- )
76
 
77
  def start_submission(self) -> bool:
78
- with self.lock:
79
  if not self.can_submit_guess():
80
  return False
81
  self.processing_submission = True
82
  return True
83
 
84
  def finish_submission(self, results: GameResults):
85
- with self.lock:
86
  if results.user_guess == results.correct_answer:
87
  self.user_score += 1
88
  if results.model_guess == results.correct_answer:
@@ -96,49 +76,35 @@ class GameState:
96
  self.is_game_active = False
97
 
98
  def get_current_image(self) -> Optional[str]:
99
- with self.lock:
100
- if not self.is_game_active or self.current_round >= len(self.game_images):
101
- return None
102
- return self.game_images[self.current_round]
103
-
104
- def get_game_state(self):
105
- """Get a snapshot of the current game state"""
106
- with self.lock:
107
- return {
108
- 'is_active': self.is_game_active,
109
- 'current_round': self.current_round,
110
- 'total_rounds': self.total_rounds,
111
- 'user_score': self.user_score,
112
- 'model_score': self.model_score,
113
- 'last_results': self.last_results
114
- }
115
 
116
  def get_game_over_message(self) -> str:
117
- with self.lock:
118
- if self.user_score > self.model_score:
119
- return """
120
- <div style='text-align: center; margin-top: 20px; font-size: 1.2em;'>
121
- 🎉 Congratulations! You won! 🎉<br>
122
- You've outperformed SuSy in detecting AI-generated images.<br>
123
- Click 'Start New Game' to play again.
124
- </div>
125
- """
126
- elif self.user_score < self.model_score:
127
- return """
128
- <div style='text-align: center; margin-top: 20px; font-size: 1.2em;'>
129
- Better luck next time! SuSy won this round.<br>
130
- Keep practicing to improve your detection skills.<br>
131
- Click 'Start New Game' to try again.
132
- </div>
133
- """
134
- else:
135
- return """
136
- <div style='text-align: center; margin-top: 20px; font-size: 1.2em;'>
137
- It's a tie! You matched SuSy's performance!<br>
138
- You're getting good at this.<br>
139
- Click 'Start New Game' to play again.
140
- </div>
141
- """
142
 
143
  def process_image(image):
144
  # Set Parameters
@@ -193,7 +159,9 @@ def process_image(image):
193
 
194
  return sorted_probs
195
 
196
- def load_images():
 
 
197
  real_image_folder = "real_images"
198
  fake_image_folder = "fake_images"
199
  real_images = [os.path.join(real_image_folder, img) for img in os.listdir(real_image_folder)]
@@ -203,21 +171,18 @@ def load_images():
203
  return selected_images
204
 
205
  def create_score_html() -> str:
206
- game_state_snapshot = game_state.get_game_state()
207
-
208
  results_html = ""
209
- if game_state_snapshot['last_results']:
210
- results = game_state_snapshot['last_results']
211
  results_html = f"""
212
  <div style='margin-top: 1rem; padding: 1rem; background-color: #e0e0e0; border-radius: 8px; color: #333;'>
213
  <h4 style='color: #333; margin-bottom: 0.5rem;'>Last Round Results:</h4>
214
- <p style='color: #333;'>Your guess: {results.user_guess}</p>
215
- <p style='color: #333;'>Model's guess: {results.model_guess}</p>
216
- <p style='color: #333;'>Correct answer: {results.correct_answer}</p>
217
  </div>
218
  """
219
 
220
- current_display_round = min(game_state_snapshot['current_round'] + 1, game_state_snapshot['total_rounds'])
221
 
222
  return f"""
223
  <div style='padding: 1rem; background-color: #f0f0f0; border-radius: 8px; color: #333;'>
@@ -225,58 +190,48 @@ def create_score_html() -> str:
225
  <div style='display: flex; justify-content: space-around;'>
226
  <div>
227
  <h4 style='color: #333;'>You</h4>
228
- <p style='font-size: 1.5rem; color: #333;'>{game_state_snapshot['user_score']}</p>
229
  </div>
230
  <div>
231
  <h4 style='color: #333;'>AI Model</h4>
232
- <p style='font-size: 1.5rem; color: #333;'>{game_state_snapshot['model_score']}</p>
233
  </div>
234
  </div>
235
  <div style='margin-top: 1rem;'>
236
- <p style='color: #333;'>Round: {current_display_round}/{game_state_snapshot['total_rounds']}</p>
237
  </div>
238
  {results_html}
239
  </div>
240
  """
241
 
242
- game_state = GameState()
243
-
244
  def start_game():
245
- """Initialize a new game"""
246
- success, first_image_path = game_state.start_new_game()
247
-
248
- if not success or not first_image_path:
249
- print("Failed to start new game")
250
  return [gr.update()] * 6
251
 
252
- try:
253
- current_image = Image.open(first_image_path)
254
-
255
- return (
256
- gr.update(value=current_image, visible=True),
257
- gr.update(visible=False),
258
- gr.update(visible=True, interactive=True),
259
- gr.update(visible=True, interactive=True),
260
- create_score_html(),
261
- gr.update(visible=False)
262
- )
263
- except Exception as e:
264
- print(f"Error starting game: {e}")
265
- game_state.reset()
266
- return [gr.update()] * 6
267
 
268
  def submit_guess(user_guess: str):
269
- """Handle user guess submission"""
270
  if not game_state.can_submit_guess():
271
  return [gr.update()] * 6
272
 
 
273
  if not game_state.start_submission():
274
  return [gr.update()] * 6
275
 
276
  try:
 
277
  current_image_path = game_state.get_current_image()
278
  if not current_image_path:
279
- game_state.processing_submission = False
280
  return [gr.update()] * 6
281
 
282
  current_image = Image.open(current_image_path)
@@ -284,9 +239,11 @@ def submit_guess(user_guess: str):
284
  model_guess = "Real" if model_prediction['Authentic'] > PROB_THRESHOLD else "Fake"
285
  correct_answer = "Real" if "real_images" in current_image_path else "Fake"
286
 
 
287
  results = GameResults(user_guess, model_guess, correct_answer)
288
  game_state.finish_submission(results)
289
 
 
290
  if not game_state.is_game_active:
291
  return (
292
  gr.update(value=None, visible=False),
@@ -297,6 +254,7 @@ def submit_guess(user_guess: str):
297
  gr.update(visible=True, value=game_state.get_game_over_message())
298
  )
299
 
 
300
  next_image_path = game_state.get_current_image()
301
  if not next_image_path:
302
  return [gr.update()] * 6
@@ -312,9 +270,9 @@ def submit_guess(user_guess: str):
312
  gr.update(visible=False)
313
  )
314
  except Exception as e:
315
- print(f"Error processing guess: {e}")
316
  game_state.processing_submission = False
317
- return [gr.update()] * 6
318
 
319
  # Custom CSS
320
  custom_css = """
 
25
 
26
  class GameState:
27
  def __init__(self):
28
+ # Lock only used for round transitions
29
+ self.round_lock = Lock()
30
+ self.reset()
31
 
32
+ def reset(self):
 
33
  self.user_score = 0
34
  self.model_score = 0
35
  self.current_round = 0
 
39
  self.last_results: Optional[GameResults] = None
40
  self.processing_submission = False
41
 
42
+ def start_new_game(self) -> bool:
43
+ if self.is_game_active:
44
+ return False
45
+ self.reset()
46
+ self.game_images = load_images()
47
+ self.is_game_active = True
48
+ return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  def can_submit_guess(self) -> bool:
51
+ return (
52
+ self.is_game_active and
53
+ not self.processing_submission and
54
+ self.current_round < self.total_rounds
55
+ )
 
 
56
 
57
  def start_submission(self) -> bool:
58
+ with self.round_lock:
59
  if not self.can_submit_guess():
60
  return False
61
  self.processing_submission = True
62
  return True
63
 
64
  def finish_submission(self, results: GameResults):
65
+ with self.round_lock:
66
  if results.user_guess == results.correct_answer:
67
  self.user_score += 1
68
  if results.model_guess == results.correct_answer:
 
76
  self.is_game_active = False
77
 
78
  def get_current_image(self) -> Optional[str]:
79
+ if not self.is_game_active or self.current_round >= len(self.game_images):
80
+ return None
81
+ return self.game_images[self.current_round]
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
  def get_game_over_message(self) -> str:
84
+ if self.user_score > self.model_score:
85
+ return """
86
+ <div style='text-align: center; margin-top: 20px; font-size: 1.2em;'>
87
+ 🎉 Congratulations! You won! 🎉<br>
88
+ You've outperformed SuSy in detecting AI-generated images.<br>
89
+ Click 'Start New Game' to play again.
90
+ </div>
91
+ """
92
+ elif self.user_score < self.model_score:
93
+ return """
94
+ <div style='text-align: center; margin-top: 20px; font-size: 1.2em;'>
95
+ Better luck next time! SuSy won this round.<br>
96
+ Keep practicing to improve your detection skills.<br>
97
+ Click 'Start New Game' to try again.
98
+ </div>
99
+ """
100
+ else:
101
+ return """
102
+ <div style='text-align: center; margin-top: 20px; font-size: 1.2em;'>
103
+ It's a tie! You matched SuSy's performance!<br>
104
+ You're getting good at this.<br>
105
+ Click 'Start New Game' to play again.
106
+ </div>
107
+ """
 
108
 
109
  def process_image(image):
110
  # Set Parameters
 
159
 
160
  return sorted_probs
161
 
162
+ game_state = GameState()
163
+
164
+ def load_images() -> List[str]:
165
  real_image_folder = "real_images"
166
  fake_image_folder = "fake_images"
167
  real_images = [os.path.join(real_image_folder, img) for img in os.listdir(real_image_folder)]
 
171
  return selected_images
172
 
173
  def create_score_html() -> str:
 
 
174
  results_html = ""
175
+ if game_state.last_results:
 
176
  results_html = f"""
177
  <div style='margin-top: 1rem; padding: 1rem; background-color: #e0e0e0; border-radius: 8px; color: #333;'>
178
  <h4 style='color: #333; margin-bottom: 0.5rem;'>Last Round Results:</h4>
179
+ <p style='color: #333;'>Your guess: {game_state.last_results.user_guess}</p>
180
+ <p style='color: #333;'>Model's guess: {game_state.last_results.model_guess}</p>
181
+ <p style='color: #333;'>Correct answer: {game_state.last_results.correct_answer}</p>
182
  </div>
183
  """
184
 
185
+ current_display_round = min(game_state.current_round + 1, game_state.total_rounds)
186
 
187
  return f"""
188
  <div style='padding: 1rem; background-color: #f0f0f0; border-radius: 8px; color: #333;'>
 
190
  <div style='display: flex; justify-content: space-around;'>
191
  <div>
192
  <h4 style='color: #333;'>You</h4>
193
+ <p style='font-size: 1.5rem; color: #333;'>{game_state.user_score}</p>
194
  </div>
195
  <div>
196
  <h4 style='color: #333;'>AI Model</h4>
197
+ <p style='font-size: 1.5rem; color: #333;'>{game_state.model_score}</p>
198
  </div>
199
  </div>
200
  <div style='margin-top: 1rem;'>
201
+ <p style='color: #333;'>Round: {current_display_round}/{game_state.total_rounds}</p>
202
  </div>
203
  {results_html}
204
  </div>
205
  """
206
 
 
 
207
  def start_game():
208
+ if not game_state.start_new_game():
 
 
 
 
209
  return [gr.update()] * 6
210
 
211
+ current_image = Image.open(game_state.get_current_image())
212
+
213
+ return (
214
+ gr.update(value=current_image, visible=True),
215
+ gr.update(visible=False),
216
+ gr.update(visible=True, interactive=True),
217
+ gr.update(visible=True, interactive=True),
218
+ create_score_html(),
219
+ gr.update(visible=False)
220
+ )
 
 
 
 
 
221
 
222
  def submit_guess(user_guess: str):
223
+ # Early return if we can't submit a guess
224
  if not game_state.can_submit_guess():
225
  return [gr.update()] * 6
226
 
227
+ # Mark submission as being processed
228
  if not game_state.start_submission():
229
  return [gr.update()] * 6
230
 
231
  try:
232
+ # Get current image and process it
233
  current_image_path = game_state.get_current_image()
234
  if not current_image_path:
 
235
  return [gr.update()] * 6
236
 
237
  current_image = Image.open(current_image_path)
 
239
  model_guess = "Real" if model_prediction['Authentic'] > PROB_THRESHOLD else "Fake"
240
  correct_answer = "Real" if "real_images" in current_image_path else "Fake"
241
 
242
+ # Update game state with results
243
  results = GameResults(user_guess, model_guess, correct_answer)
244
  game_state.finish_submission(results)
245
 
246
+ # Check if game is over
247
  if not game_state.is_game_active:
248
  return (
249
  gr.update(value=None, visible=False),
 
254
  gr.update(visible=True, value=game_state.get_game_over_message())
255
  )
256
 
257
+ # Get next image for the next round
258
  next_image_path = game_state.get_current_image()
259
  if not next_image_path:
260
  return [gr.update()] * 6
 
270
  gr.update(visible=False)
271
  )
272
  except Exception as e:
273
+ # If any error occurs, reset the processing flag
274
  game_state.processing_submission = False
275
+ raise e
276
 
277
  # Custom CSS
278
  custom_css = """