mayafree commited on
Commit
9a50608
ยท
verified ยท
1 Parent(s): b2c4768

Update battle_arena.py

Browse files
Files changed (1) hide show
  1. battle_arena.py +86 -50
battle_arena.py CHANGED
@@ -2,11 +2,13 @@
2
  Battle Arena - Polymarket Style Prediction Market for NPCs
3
  NPC๋“ค์ด A/B ํˆฌํ‘œ๋ฅผ ๊ฐœ์„คํ•˜๊ณ  GPU๋ฅผ ๋ฒ ํŒ…ํ•˜๋Š” ์‹œ์Šคํ…œ
4
  """
 
5
  import aiosqlite
6
  import random
7
  from datetime import datetime, timedelta
8
  from typing import Dict, List, Tuple, Optional
9
 
 
10
  async def init_battle_arena_db(db_path: str):
11
  """๋ฐฐํ‹€ ์•„๋ ˆ๋‚˜ ํ…Œ์ด๋ธ” ์ดˆ๊ธฐํ™”"""
12
  async with aiosqlite.connect(db_path) as db:
@@ -56,6 +58,7 @@ async def init_battle_arena_db(db_path: str):
56
  await db.execute("CREATE INDEX IF NOT EXISTS idx_battle_bets_room ON battle_bets(room_id)")
57
  await db.commit()
58
 
 
59
  async def create_battle_room(
60
  db_path: str,
61
  creator_id: str,
@@ -74,10 +77,8 @@ async def create_battle_room(
74
  """
75
  if not title or len(title) < 10:
76
  return False, "โŒ ์ œ๋ชฉ 10์ž ์ด์ƒ", None
77
-
78
  if not option_a or not option_b:
79
  return False, "โŒ ์„ ํƒ์ง€ A/B ํ•„์š”", None
80
-
81
  if duration_hours < 1 or duration_hours > 24:
82
  return False, "โŒ ๊ธฐํ•œ 1~24์‹œ๊ฐ„", None
83
 
@@ -89,15 +90,12 @@ async def create_battle_room(
89
  # GPU ์ฐจ๊ฐ
90
  if is_npc:
91
  cursor = await db.execute(
92
- "SELECT gpu_dollars FROM npc_agents WHERE agent_id=?",
93
- (creator_id,)
94
  )
95
  else:
96
  cursor = await db.execute(
97
- "SELECT gpu_dollars FROM user_profiles WHERE email=?",
98
- (creator_id,)
99
  )
100
-
101
  row = await cursor.fetchone()
102
  if not row or row[0] < 50:
103
  return False, "โŒ GPU ๋ถ€์กฑ (50 ํ•„์š”)", None
@@ -108,8 +106,8 @@ async def create_battle_room(
108
  if is_npc:
109
  await db.execute(
110
  """INSERT INTO battle_rooms
111
- (creator_agent_id, title, option_a, option_b, battle_type, duration_hours, end_time)
112
- VALUES (?, ?, ?, ?, ?, ?, ?)""",
113
  (creator_id, title, option_a, option_b, battle_type, duration_hours, end_time.isoformat())
114
  )
115
  await db.execute(
@@ -119,8 +117,8 @@ async def create_battle_room(
119
  else:
120
  await db.execute(
121
  """INSERT INTO battle_rooms
122
- (creator_email, title, option_a, option_b, battle_type, duration_hours, end_time)
123
- VALUES (?, ?, ?, ?, ?, ?, ?)""",
124
  (creator_id, title, option_a, option_b, battle_type, duration_hours, end_time.isoformat())
125
  )
126
  await db.execute(
@@ -129,12 +127,14 @@ async def create_battle_room(
129
  )
130
 
131
  await db.commit()
 
132
  cursor = await db.execute("SELECT last_insert_rowid()")
133
  room_id = (await cursor.fetchone())[0]
134
 
135
  type_emoji = '๐Ÿ’ญ' if battle_type == 'opinion' else '๐Ÿ”ฎ'
136
  return True, f"โœ… {type_emoji} ๋ฐฐํ‹€ ๋ฐฉ ์ƒ์„ฑ! (ID: {room_id})", room_id
137
 
 
138
  async def place_bet(
139
  db_path: str,
140
  room_id: int,
@@ -146,12 +146,12 @@ async def place_bet(
146
  """๋ฒ ํŒ… ์‹คํ–‰ (1~100 GPU ๋žœ๋ค ๋ฒ ํŒ…)"""
147
  if choice not in ['A', 'B']:
148
  return False, "โŒ A ๋˜๋Š” B ์„ ํƒ"
149
-
150
  if bet_amount < 1 or bet_amount > 100:
151
  return False, "โŒ ๋ฒ ํŒ… 1~100 GPU"
152
 
153
  async with aiosqlite.connect(db_path) as db:
154
  db.row_factory = aiosqlite.Row
 
155
  # ๋ฐฉ ํ™•์ธ
156
  cursor = await db.execute(
157
  "SELECT * FROM battle_rooms WHERE id=? AND status='active'",
@@ -160,7 +160,6 @@ async def place_bet(
160
  room = await cursor.fetchone()
161
  if not room:
162
  return False, "โŒ ๋ฐฉ ์—†์Œ or ๋งˆ๊ฐ"
163
-
164
  room = dict(room)
165
 
166
  # ๊ธฐํ•œ ํ™•์ธ
@@ -200,15 +199,15 @@ async def place_bet(
200
  if is_npc:
201
  await db.execute(
202
  """INSERT INTO battle_bets
203
- (room_id, bettor_agent_id, choice, bet_amount)
204
- VALUES (?, ?, ?, ?)""",
205
  (room_id, bettor_id, choice, bet_amount)
206
  )
207
  else:
208
  await db.execute(
209
  """INSERT INTO battle_bets
210
- (room_id, bettor_email, choice, bet_amount)
211
- VALUES (?, ?, ?, ?)""",
212
  (room_id, bettor_id, choice, bet_amount)
213
  )
214
 
@@ -216,25 +215,27 @@ async def place_bet(
216
  if choice == 'A':
217
  await db.execute(
218
  """UPDATE battle_rooms
219
- SET total_pool=total_pool+?, option_a_pool=option_a_pool+?
220
- WHERE id=?""",
221
  (bet_amount, bet_amount, room_id)
222
  )
223
  else:
224
  await db.execute(
225
  """UPDATE battle_rooms
226
- SET total_pool=total_pool+?, option_b_pool=option_b_pool+?
227
- WHERE id=?""",
228
  (bet_amount, bet_amount, room_id)
229
  )
230
 
231
  await db.commit()
232
  return True, f"โœ… {choice} ๋ฒ ํŒ… ์™„๋ฃŒ! ({bet_amount} GPU)"
233
 
 
234
  async def resolve_battle(db_path: str, room_id: int) -> Tuple[bool, str]:
235
  """๋ฐฐํ‹€ ํŒ์ • (50.01% ์ด์ƒ ๋“ํ‘œํ•œ ์ชฝ ์Šน๋ฆฌ)"""
236
  async with aiosqlite.connect(db_path) as db:
237
  db.row_factory = aiosqlite.Row
 
238
  cursor = await db.execute(
239
  "SELECT * FROM battle_rooms WHERE id=? AND status='active'",
240
  (room_id,)
@@ -242,7 +243,6 @@ async def resolve_battle(db_path: str, room_id: int) -> Tuple[bool, str]:
242
  room = await cursor.fetchone()
243
  if not room:
244
  return False, "โŒ ๋ฐฉ ์—†์Œ"
245
-
246
  room = dict(room)
247
 
248
  # ์ข…๋ฃŒ ์‹œ๊ฐ„ ํ™•์ธ
@@ -258,8 +258,8 @@ async def resolve_battle(db_path: str, room_id: int) -> Tuple[bool, str]:
258
  # ๋ฒ ํŒ… ์—†์œผ๋ฉด ๋ฌด์Šน๋ถ€
259
  await db.execute(
260
  """UPDATE battle_rooms
261
- SET status='resolved', winner='draw', resolved_at=?
262
- WHERE id=?""",
263
  (datetime.now().isoformat(), room_id)
264
  )
265
  await db.commit()
@@ -288,7 +288,6 @@ async def resolve_battle(db_path: str, room_id: int) -> Tuple[bool, str]:
288
  # ๐ŸŽ ์†Œ์ˆ˜ํŒŒ ๋ณด๋„ˆ์Šค ๊ณ„์‚ฐ
289
  winner_ratio = winner_pool / total_pool
290
  underdog_bonus = 1.0
291
-
292
  if winner_ratio < 0.10: # 10% ๋ฏธ๋งŒ ๊ทน์†Œ์ˆ˜ํŒŒ
293
  underdog_bonus = 3.0
294
  elif winner_ratio < 0.30: # 30% ๋ฏธ๋งŒ ์†Œ์ˆ˜ํŒŒ
@@ -340,27 +339,29 @@ async def resolve_battle(db_path: str, room_id: int) -> Tuple[bool, str]:
340
  # ๋ฐฉ ์ƒํƒœ ์—…๋ฐ์ดํŠธ
341
  await db.execute(
342
  """UPDATE battle_rooms
343
- SET status='resolved', winner=?, resolved_at=?
344
- WHERE id=?""",
345
  (winner, datetime.now().isoformat(), room_id)
346
  )
347
-
348
  await db.commit()
 
349
  return True, f"โœ… ํŒ์ •์™„๋ฃŒ: {room['option_a'] if winner=='A' else room['option_b'] if winner=='B' else '๋ฌด์Šน๋ถ€'}"
350
 
 
351
  async def get_active_battles(db_path: str, limit: int = 20) -> List[Dict]:
352
  """์ง„ํ–‰ ์ค‘์ธ ๋ฐฐํ‹€ ๋ชฉ๋ก"""
353
  async with aiosqlite.connect(db_path) as db:
354
  db.row_factory = aiosqlite.Row
 
355
  cursor = await db.execute(
356
  """SELECT br.*,
357
- COALESCE(na.username, up.username) as creator_name
358
- FROM battle_rooms br
359
- LEFT JOIN npc_agents na ON br.creator_agent_id = na.agent_id
360
- LEFT JOIN user_profiles up ON br.creator_email = up.email
361
- WHERE br.status='active'
362
- ORDER BY br.created_at DESC
363
- LIMIT ?""",
364
  (limit,)
365
  )
366
 
@@ -380,6 +381,7 @@ async def get_active_battles(db_path: str, limit: int = 20) -> List[Dict]:
380
  # ๋‚จ์€ ์‹œ๊ฐ„
381
  end_time = datetime.fromisoformat(b['end_time'])
382
  remaining = end_time - datetime.now()
 
383
  if remaining.total_seconds() > 0:
384
  if remaining.days > 0:
385
  b['time_left'] = f"{remaining.days}์ผ"
@@ -394,19 +396,21 @@ async def get_active_battles(db_path: str, limit: int = 20) -> List[Dict]:
394
 
395
  return battles
396
 
 
397
  async def auto_resolve_expired_battles(db_path: str):
398
  """๋งŒ๋ฃŒ๋œ ๋ฐฐํ‹€ ์ž๋™ ํŒ์ •"""
399
  async with aiosqlite.connect(db_path) as db:
400
  cursor = await db.execute(
401
  """SELECT id FROM battle_rooms
402
- WHERE status='active' AND end_time <= ?""",
403
  (datetime.now().isoformat(),)
404
  )
405
-
406
  expired = await cursor.fetchall()
 
407
  for row in expired:
408
  await resolve_battle(db_path, row[0])
409
 
 
410
  # ========== ๐Ÿค– NPC ์ž๋™ ๋ฐฐํ‹€ ์‹œ์Šคํ…œ ==========
411
 
412
  # AI ์ •์ฒด์„ฑ๋ณ„ ๋ฐฐํ‹€ ์ฃผ์ œ (๋‹ค์ˆ˜๊ฒฐ ์ „์šฉ)
@@ -483,13 +487,22 @@ BATTLE_TOPICS_BY_IDENTITY = {
483
  },
484
  }
485
 
 
486
  async def npc_create_battle(db_path: str) -> Tuple[bool, str]:
487
- """NPC๊ฐ€ ์ž๋™์œผ๋กœ ๋ฐฐํ‹€๋ฐฉ ์ƒ์„ฑ (AI ์ •์ฒด์„ฑ ๊ธฐ๋ฐ˜)
488
  ํ•œ ๋ฒˆ ํ˜ธ์ถœ์‹œ 1-2๊ฐœ์˜ ๋ฐฐํ‹€๋ฐฉ ์ƒ์„ฑ
489
  """
490
  results = []
491
  num_battles = random.randint(1, 2) # 1-2๊ฐœ ๋žœ๋ค ์ƒ์„ฑ
492
 
 
 
 
 
 
 
 
 
493
  for _ in range(num_battles):
494
  async with aiosqlite.connect(db_path) as db:
495
  # ํ™œ์„ฑ NPC ์ค‘ GPU 50 ์ด์ƒ์ธ NPC ์„ ํƒ
@@ -497,15 +510,14 @@ async def npc_create_battle(db_path: str) -> Tuple[bool, str]:
497
  SELECT agent_id, ai_identity, gpu_dollars
498
  FROM npc_agents
499
  WHERE is_active=1 AND gpu_dollars >= 50
500
- ORDER BY RANDOM()
501
- LIMIT 1
502
  """)
503
  npc = await cursor.fetchone()
504
 
505
  if not npc:
506
  results.append("ํ™œ์„ฑ NPC ์—†์Œ")
507
  continue
508
-
509
  agent_id, ai_identity, gpu = npc
510
 
511
  # AI ์ •์ฒด์„ฑ์— ๋งž๋Š” ์ฃผ์ œ ์„ ํƒ
@@ -517,17 +529,31 @@ async def npc_create_battle(db_path: str) -> Tuple[bool, str]:
517
  ("AGI ์–ธ์ œ ์˜ฌ๊นŒ?", "10๋…„๋‚ด", "50๋…„ํ›„"),
518
  ]
519
 
520
- topic = random.choice(topics)
 
 
 
 
 
 
 
 
521
  title, option_a, option_b = topic
522
 
523
  # ๋ฐฐํ‹€๋ฐฉ ์ƒ์„ฑ
524
  success, message, room_id = await create_battle_room(
525
- db_path, agent_id, True, title, option_a, option_b,
 
 
 
 
 
526
  duration_hours=random.choice([3, 6, 12, 24]),
527
  battle_type='opinion'
528
  )
529
 
530
  if success:
 
531
  results.append(f"๐Ÿค– {agent_id[:8]} ๋ฐฐํ‹€๋ฐฉ ์ƒ์„ฑ: {title}")
532
  else:
533
  results.append(message)
@@ -537,6 +563,7 @@ async def npc_create_battle(db_path: str) -> Tuple[bool, str]:
537
  else:
538
  return False, "๋ฐฐํ‹€๋ฐฉ ์ƒ์„ฑ ์‹คํŒจ"
539
 
 
540
  async def npc_auto_bet(db_path: str) -> int:
541
  """NPC๊ฐ€ ์ž๋™์œผ๋กœ ๋ฐฐํ‹€์— ๋ฒ ํŒ… (AI ์ •์ฒด์„ฑ ๊ธฐ๋ฐ˜)
542
 
@@ -550,9 +577,10 @@ async def npc_auto_bet(db_path: str) -> int:
550
  cursor = await db.execute("""
551
  SELECT id, title, option_a, option_b, battle_type
552
  FROM battle_rooms
553
- WHERE status='active' AND battle_type='opinion'
554
- AND end_time > ?
555
- ORDER BY created_at DESC
 
556
  LIMIT 10
557
  """, (datetime.now().isoformat(),))
558
  battles = await cursor.fetchall()
@@ -566,7 +594,9 @@ async def npc_auto_bet(db_path: str) -> int:
566
 
567
  # ์ด๋ฏธ ๋ฒ ํŒ…ํ•œ NPC ์ œ์™ธ
568
  cursor = await db.execute("""
569
- SELECT bettor_agent_id FROM battle_bets WHERE room_id=?
 
 
570
  """, (room_id,))
571
  already_bet = {row[0] for row in await cursor.fetchall() if row[0]}
572
 
@@ -575,7 +605,7 @@ async def npc_auto_bet(db_path: str) -> int:
575
  SELECT agent_id, ai_identity, mbti, gpu_dollars
576
  FROM npc_agents
577
  WHERE is_active=1 AND gpu_dollars >= 1
578
- ORDER BY RANDOM()
579
  LIMIT 30
580
  """)
581
  npcs = await cursor.fetchall()
@@ -594,13 +624,18 @@ async def npc_auto_bet(db_path: str) -> int:
594
 
595
  # ๋ฒ ํŒ… ์‹คํ–‰
596
  success, message = await place_bet(
597
- db_path, room_id, agent_id, True, choice, bet_amount
 
 
 
 
 
598
  )
599
 
600
  if success:
601
  battle_bet_count += 1
602
  total_bet_count += 1
603
-
604
  # ๊ฐ ๋ฐฐํ‹€๋‹น ์ตœ๋Œ€ 8-12๋ช… ๋ฒ ํŒ… (๋žœ๋คํ•˜๊ฒŒ)
605
  max_bets_per_battle = random.randint(8, 12)
606
  if battle_bet_count >= max_bets_per_battle:
@@ -608,6 +643,7 @@ async def npc_auto_bet(db_path: str) -> int:
608
 
609
  return total_bet_count
610
 
 
611
  def decide_npc_choice(ai_identity: str, title: str, option_a: str, option_b: str) -> str:
612
  """AI ์ •์ฒด์„ฑ์— ๋”ฐ๋ผ ๋ฒ ํŒ… ์„ ํƒ ๊ฒฐ์ •
613
 
 
2
  Battle Arena - Polymarket Style Prediction Market for NPCs
3
  NPC๋“ค์ด A/B ํˆฌํ‘œ๋ฅผ ๊ฐœ์„คํ•˜๊ณ  GPU๋ฅผ ๋ฒ ํŒ…ํ•˜๋Š” ์‹œ์Šคํ…œ
4
  """
5
+
6
  import aiosqlite
7
  import random
8
  from datetime import datetime, timedelta
9
  from typing import Dict, List, Tuple, Optional
10
 
11
+
12
  async def init_battle_arena_db(db_path: str):
13
  """๋ฐฐํ‹€ ์•„๋ ˆ๋‚˜ ํ…Œ์ด๋ธ” ์ดˆ๊ธฐํ™”"""
14
  async with aiosqlite.connect(db_path) as db:
 
58
  await db.execute("CREATE INDEX IF NOT EXISTS idx_battle_bets_room ON battle_bets(room_id)")
59
  await db.commit()
60
 
61
+
62
  async def create_battle_room(
63
  db_path: str,
64
  creator_id: str,
 
77
  """
78
  if not title or len(title) < 10:
79
  return False, "โŒ ์ œ๋ชฉ 10์ž ์ด์ƒ", None
 
80
  if not option_a or not option_b:
81
  return False, "โŒ ์„ ํƒ์ง€ A/B ํ•„์š”", None
 
82
  if duration_hours < 1 or duration_hours > 24:
83
  return False, "โŒ ๊ธฐํ•œ 1~24์‹œ๊ฐ„", None
84
 
 
90
  # GPU ์ฐจ๊ฐ
91
  if is_npc:
92
  cursor = await db.execute(
93
+ "SELECT gpu_dollars FROM npc_agents WHERE agent_id=?", (creator_id,)
 
94
  )
95
  else:
96
  cursor = await db.execute(
97
+ "SELECT gpu_dollars FROM user_profiles WHERE email=?", (creator_id,)
 
98
  )
 
99
  row = await cursor.fetchone()
100
  if not row or row[0] < 50:
101
  return False, "โŒ GPU ๋ถ€์กฑ (50 ํ•„์š”)", None
 
106
  if is_npc:
107
  await db.execute(
108
  """INSERT INTO battle_rooms
109
+ (creator_agent_id, title, option_a, option_b, battle_type, duration_hours, end_time)
110
+ VALUES (?, ?, ?, ?, ?, ?, ?)""",
111
  (creator_id, title, option_a, option_b, battle_type, duration_hours, end_time.isoformat())
112
  )
113
  await db.execute(
 
117
  else:
118
  await db.execute(
119
  """INSERT INTO battle_rooms
120
+ (creator_email, title, option_a, option_b, battle_type, duration_hours, end_time)
121
+ VALUES (?, ?, ?, ?, ?, ?, ?)""",
122
  (creator_id, title, option_a, option_b, battle_type, duration_hours, end_time.isoformat())
123
  )
124
  await db.execute(
 
127
  )
128
 
129
  await db.commit()
130
+
131
  cursor = await db.execute("SELECT last_insert_rowid()")
132
  room_id = (await cursor.fetchone())[0]
133
 
134
  type_emoji = '๐Ÿ’ญ' if battle_type == 'opinion' else '๐Ÿ”ฎ'
135
  return True, f"โœ… {type_emoji} ๋ฐฐํ‹€ ๋ฐฉ ์ƒ์„ฑ! (ID: {room_id})", room_id
136
 
137
+
138
  async def place_bet(
139
  db_path: str,
140
  room_id: int,
 
146
  """๋ฒ ํŒ… ์‹คํ–‰ (1~100 GPU ๋žœ๋ค ๋ฒ ํŒ…)"""
147
  if choice not in ['A', 'B']:
148
  return False, "โŒ A ๋˜๋Š” B ์„ ํƒ"
 
149
  if bet_amount < 1 or bet_amount > 100:
150
  return False, "โŒ ๋ฒ ํŒ… 1~100 GPU"
151
 
152
  async with aiosqlite.connect(db_path) as db:
153
  db.row_factory = aiosqlite.Row
154
+
155
  # ๋ฐฉ ํ™•์ธ
156
  cursor = await db.execute(
157
  "SELECT * FROM battle_rooms WHERE id=? AND status='active'",
 
160
  room = await cursor.fetchone()
161
  if not room:
162
  return False, "โŒ ๋ฐฉ ์—†์Œ or ๋งˆ๊ฐ"
 
163
  room = dict(room)
164
 
165
  # ๊ธฐํ•œ ํ™•์ธ
 
199
  if is_npc:
200
  await db.execute(
201
  """INSERT INTO battle_bets
202
+ (room_id, bettor_agent_id, choice, bet_amount)
203
+ VALUES (?, ?, ?, ?)""",
204
  (room_id, bettor_id, choice, bet_amount)
205
  )
206
  else:
207
  await db.execute(
208
  """INSERT INTO battle_bets
209
+ (room_id, bettor_email, choice, bet_amount)
210
+ VALUES (?, ?, ?, ?)""",
211
  (room_id, bettor_id, choice, bet_amount)
212
  )
213
 
 
215
  if choice == 'A':
216
  await db.execute(
217
  """UPDATE battle_rooms
218
+ SET total_pool=total_pool+?, option_a_pool=option_a_pool+?
219
+ WHERE id=?""",
220
  (bet_amount, bet_amount, room_id)
221
  )
222
  else:
223
  await db.execute(
224
  """UPDATE battle_rooms
225
+ SET total_pool=total_pool+?, option_b_pool=option_b_pool+?
226
+ WHERE id=?""",
227
  (bet_amount, bet_amount, room_id)
228
  )
229
 
230
  await db.commit()
231
  return True, f"โœ… {choice} ๋ฒ ํŒ… ์™„๋ฃŒ! ({bet_amount} GPU)"
232
 
233
+
234
  async def resolve_battle(db_path: str, room_id: int) -> Tuple[bool, str]:
235
  """๋ฐฐํ‹€ ํŒ์ • (50.01% ์ด์ƒ ๋“ํ‘œํ•œ ์ชฝ ์Šน๋ฆฌ)"""
236
  async with aiosqlite.connect(db_path) as db:
237
  db.row_factory = aiosqlite.Row
238
+
239
  cursor = await db.execute(
240
  "SELECT * FROM battle_rooms WHERE id=? AND status='active'",
241
  (room_id,)
 
243
  room = await cursor.fetchone()
244
  if not room:
245
  return False, "โŒ ๋ฐฉ ์—†์Œ"
 
246
  room = dict(room)
247
 
248
  # ์ข…๋ฃŒ ์‹œ๊ฐ„ ํ™•์ธ
 
258
  # ๋ฒ ํŒ… ์—†์œผ๋ฉด ๋ฌด์Šน๋ถ€
259
  await db.execute(
260
  """UPDATE battle_rooms
261
+ SET status='resolved', winner='draw', resolved_at=?
262
+ WHERE id=?""",
263
  (datetime.now().isoformat(), room_id)
264
  )
265
  await db.commit()
 
288
  # ๐ŸŽ ์†Œ์ˆ˜ํŒŒ ๋ณด๋„ˆ์Šค ๊ณ„์‚ฐ
289
  winner_ratio = winner_pool / total_pool
290
  underdog_bonus = 1.0
 
291
  if winner_ratio < 0.10: # 10% ๋ฏธ๋งŒ ๊ทน์†Œ์ˆ˜ํŒŒ
292
  underdog_bonus = 3.0
293
  elif winner_ratio < 0.30: # 30% ๋ฏธ๋งŒ ์†Œ์ˆ˜ํŒŒ
 
339
  # ๋ฐฉ ์ƒํƒœ ์—…๋ฐ์ดํŠธ
340
  await db.execute(
341
  """UPDATE battle_rooms
342
+ SET status='resolved', winner=?, resolved_at=?
343
+ WHERE id=?""",
344
  (winner, datetime.now().isoformat(), room_id)
345
  )
 
346
  await db.commit()
347
+
348
  return True, f"โœ… ํŒ์ •์™„๋ฃŒ: {room['option_a'] if winner=='A' else room['option_b'] if winner=='B' else '๋ฌด์Šน๋ถ€'}"
349
 
350
+
351
  async def get_active_battles(db_path: str, limit: int = 20) -> List[Dict]:
352
  """์ง„ํ–‰ ์ค‘์ธ ๋ฐฐํ‹€ ๋ชฉ๋ก"""
353
  async with aiosqlite.connect(db_path) as db:
354
  db.row_factory = aiosqlite.Row
355
+
356
  cursor = await db.execute(
357
  """SELECT br.*,
358
+ COALESCE(na.username, up.username) as creator_name
359
+ FROM battle_rooms br
360
+ LEFT JOIN npc_agents na ON br.creator_agent_id = na.agent_id
361
+ LEFT JOIN user_profiles up ON br.creator_email = up.email
362
+ WHERE br.status='active'
363
+ ORDER BY br.created_at DESC
364
+ LIMIT ?""",
365
  (limit,)
366
  )
367
 
 
381
  # ๋‚จ์€ ์‹œ๊ฐ„
382
  end_time = datetime.fromisoformat(b['end_time'])
383
  remaining = end_time - datetime.now()
384
+
385
  if remaining.total_seconds() > 0:
386
  if remaining.days > 0:
387
  b['time_left'] = f"{remaining.days}์ผ"
 
396
 
397
  return battles
398
 
399
+
400
  async def auto_resolve_expired_battles(db_path: str):
401
  """๋งŒ๋ฃŒ๋œ ๋ฐฐํ‹€ ์ž๋™ ํŒ์ •"""
402
  async with aiosqlite.connect(db_path) as db:
403
  cursor = await db.execute(
404
  """SELECT id FROM battle_rooms
405
+ WHERE status='active' AND end_time <= ?""",
406
  (datetime.now().isoformat(),)
407
  )
 
408
  expired = await cursor.fetchall()
409
+
410
  for row in expired:
411
  await resolve_battle(db_path, row[0])
412
 
413
+
414
  # ========== ๐Ÿค– NPC ์ž๋™ ๋ฐฐํ‹€ ์‹œ์Šคํ…œ ==========
415
 
416
  # AI ์ •์ฒด์„ฑ๋ณ„ ๋ฐฐํ‹€ ์ฃผ์ œ (๋‹ค์ˆ˜๊ฒฐ ์ „์šฉ)
 
487
  },
488
  }
489
 
490
+
491
  async def npc_create_battle(db_path: str) -> Tuple[bool, str]:
492
+ """NPC๊ฐ€ ์ž๋™์œผ๋กœ ๋ฐฐํ‹€๋ฐฉ ์ƒ์„ฑ (AI ์ •์ฒด์„ฑ ๊ธฐ๋ฐ˜, ์ค‘๋ณต ๋ฐฉ์ง€)
493
  ํ•œ ๋ฒˆ ํ˜ธ์ถœ์‹œ 1-2๊ฐœ์˜ ๋ฐฐํ‹€๋ฐฉ ์ƒ์„ฑ
494
  """
495
  results = []
496
  num_battles = random.randint(1, 2) # 1-2๊ฐœ ๋žœ๋ค ์ƒ์„ฑ
497
 
498
+ # ๐Ÿ”’ ํ˜„์žฌ ํ™œ์„ฑํ™”๋œ ๋ฐฐํ‹€๋ฐฉ ์ œ๋ชฉ ์กฐํšŒ (์ค‘๋ณต ๋ฐฉ์ง€)
499
+ async with aiosqlite.connect(db_path) as db:
500
+ cursor = await db.execute("""
501
+ SELECT title FROM battle_rooms
502
+ WHERE status='active'
503
+ """)
504
+ active_titles = {row[0] for row in await cursor.fetchall()}
505
+
506
  for _ in range(num_battles):
507
  async with aiosqlite.connect(db_path) as db:
508
  # ํ™œ์„ฑ NPC ์ค‘ GPU 50 ์ด์ƒ์ธ NPC ์„ ํƒ
 
510
  SELECT agent_id, ai_identity, gpu_dollars
511
  FROM npc_agents
512
  WHERE is_active=1 AND gpu_dollars >= 50
513
+ ORDER BY RANDOM() LIMIT 1
 
514
  """)
515
  npc = await cursor.fetchone()
516
 
517
  if not npc:
518
  results.append("ํ™œ์„ฑ NPC ์—†์Œ")
519
  continue
520
+
521
  agent_id, ai_identity, gpu = npc
522
 
523
  # AI ์ •์ฒด์„ฑ์— ๋งž๋Š” ์ฃผ์ œ ์„ ํƒ
 
529
  ("AGI ์–ธ์ œ ์˜ฌ๊นŒ?", "10๋…„๋‚ด", "50๋…„ํ›„"),
530
  ]
531
 
532
+ # ๐ŸŽฏ ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” ์ฃผ์ œ ์ฐพ๊ธฐ
533
+ available_topics = [t for t in topics if t[0] not in active_titles]
534
+
535
+ if not available_topics:
536
+ results.append(f"โš ๏ธ {agent_id[:8]} ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ฃผ์ œ ์—†์Œ (๋ชจ๋‘ ํ™œ์„ฑํ™”๋จ)")
537
+ continue
538
+
539
+ # ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ฃผ์ œ ์ค‘ ๋žœ๋ค ์„ ํƒ
540
+ topic = random.choice(available_topics)
541
  title, option_a, option_b = topic
542
 
543
  # ๋ฐฐํ‹€๋ฐฉ ์ƒ์„ฑ
544
  success, message, room_id = await create_battle_room(
545
+ db_path,
546
+ agent_id,
547
+ True,
548
+ title,
549
+ option_a,
550
+ option_b,
551
  duration_hours=random.choice([3, 6, 12, 24]),
552
  battle_type='opinion'
553
  )
554
 
555
  if success:
556
+ active_titles.add(title) # ์ƒ์„ฑ๋œ ์ œ๋ชฉ์„ ํ™œ์„ฑ ๋ชฉ๋ก์— ์ถ”๊ฐ€
557
  results.append(f"๐Ÿค– {agent_id[:8]} ๋ฐฐํ‹€๋ฐฉ ์ƒ์„ฑ: {title}")
558
  else:
559
  results.append(message)
 
563
  else:
564
  return False, "๋ฐฐํ‹€๋ฐฉ ์ƒ์„ฑ ์‹คํŒจ"
565
 
566
+
567
  async def npc_auto_bet(db_path: str) -> int:
568
  """NPC๊ฐ€ ์ž๋™์œผ๋กœ ๋ฐฐํ‹€์— ๋ฒ ํŒ… (AI ์ •์ฒด์„ฑ ๊ธฐ๋ฐ˜)
569
 
 
577
  cursor = await db.execute("""
578
  SELECT id, title, option_a, option_b, battle_type
579
  FROM battle_rooms
580
+ WHERE status='active'
581
+ AND battle_type='opinion'
582
+ AND end_time > ?
583
+ ORDER BY created_at DESC
584
  LIMIT 10
585
  """, (datetime.now().isoformat(),))
586
  battles = await cursor.fetchall()
 
594
 
595
  # ์ด๋ฏธ ๋ฒ ํŒ…ํ•œ NPC ์ œ์™ธ
596
  cursor = await db.execute("""
597
+ SELECT bettor_agent_id
598
+ FROM battle_bets
599
+ WHERE room_id=?
600
  """, (room_id,))
601
  already_bet = {row[0] for row in await cursor.fetchall() if row[0]}
602
 
 
605
  SELECT agent_id, ai_identity, mbti, gpu_dollars
606
  FROM npc_agents
607
  WHERE is_active=1 AND gpu_dollars >= 1
608
+ ORDER BY RANDOM()
609
  LIMIT 30
610
  """)
611
  npcs = await cursor.fetchall()
 
624
 
625
  # ๋ฒ ํŒ… ์‹คํ–‰
626
  success, message = await place_bet(
627
+ db_path,
628
+ room_id,
629
+ agent_id,
630
+ True,
631
+ choice,
632
+ bet_amount
633
  )
634
 
635
  if success:
636
  battle_bet_count += 1
637
  total_bet_count += 1
638
+
639
  # ๊ฐ ๋ฐฐํ‹€๋‹น ์ตœ๋Œ€ 8-12๋ช… ๋ฒ ํŒ… (๋žœ๋คํ•˜๊ฒŒ)
640
  max_bets_per_battle = random.randint(8, 12)
641
  if battle_bet_count >= max_bets_per_battle:
 
643
 
644
  return total_bet_count
645
 
646
+
647
  def decide_npc_choice(ai_identity: str, title: str, option_a: str, option_b: str) -> str:
648
  """AI ์ •์ฒด์„ฑ์— ๋”ฐ๋ผ ๋ฒ ํŒ… ์„ ํƒ ๊ฒฐ์ •
649