Prompt-Dump / battle_arena.py
seawolf2357's picture
Upload 8 files
db06ad2 verified
import aiosqlite
import random
from datetime import datetime, timedelta
from typing import Dict, List, Tuple, Optional
async def init_battle_arena_db(db_path: str):
"""Initialize Battle Arena tables (prevent DB locks)"""
async with aiosqlite.connect(db_path, timeout=30.0) as db:
await db.execute("PRAGMA journal_mode=WAL")
await db.execute("PRAGMA busy_timeout=30000") # 30 second timeout
await db.execute("""
CREATE TABLE IF NOT EXISTS battle_rooms (
id INTEGER PRIMARY KEY AUTOINCREMENT,
creator_agent_id TEXT,
creator_email TEXT,
title TEXT NOT NULL,
option_a TEXT NOT NULL,
option_b TEXT NOT NULL,
battle_type TEXT DEFAULT 'opinion',
duration_hours INTEGER DEFAULT 24,
end_time TIMESTAMP NOT NULL,
total_pool INTEGER DEFAULT 0,
option_a_pool INTEGER DEFAULT 0,
option_b_pool INTEGER DEFAULT 0,
status TEXT DEFAULT 'active',
winner TEXT,
resolved_at TIMESTAMP,
admin_result TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (creator_agent_id) REFERENCES npc_agents(agent_id),
FOREIGN KEY (creator_email) REFERENCES user_profiles(email)
)
""")
await db.execute("""
CREATE TABLE IF NOT EXISTS battle_bets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
room_id INTEGER NOT NULL,
bettor_agent_id TEXT,
bettor_email TEXT,
choice TEXT NOT NULL,
bet_amount INTEGER NOT NULL,
payout INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (room_id) REFERENCES battle_rooms(id),
FOREIGN KEY (bettor_agent_id) REFERENCES npc_agents(agent_id),
FOREIGN KEY (bettor_email) REFERENCES user_profiles(email)
)
""")
await db.execute("CREATE INDEX IF NOT EXISTS idx_battle_rooms_status ON battle_rooms(status)")
await db.execute("CREATE INDEX IF NOT EXISTS idx_battle_bets_room ON battle_bets(room_id)")
await db.commit()
async def create_battle_room(
db_path: str,
creator_id: str,
is_npc: bool,
title: str,
option_a: str,
option_b: str,
duration_hours: int = 24,
battle_type: str = 'opinion'
) -> Tuple[bool, str, Optional[int]]:
"""Create battle room (costs 50 GPU)
battle_type:
- 'opinion': Majority vote (subjective opinion, NPC only)
- 'prediction': Real outcome (objective prediction, users only)
duration_hours: 1 hour ~ 365 days (8760 hours)
"""
if not title or len(title) < 10:
return False, "โŒ Title must be 10+ characters", None
if not option_a or not option_b:
return False, "โŒ Options A/B required", None
if duration_hours < 1 or duration_hours > 8760:
return False, "โŒ Duration: 1 hour ~ 365 days", None
if is_npc and battle_type != 'opinion':
return False, "โŒ NPCs can only create opinion battles", None
async with aiosqlite.connect(db_path, timeout=30.0) as db:
await db.execute("PRAGMA busy_timeout=30000")
if is_npc:
cursor = await db.execute(
"SELECT gpu_dollars FROM npc_agents WHERE agent_id=?", (creator_id,)
)
else:
cursor = await db.execute(
"SELECT gpu_dollars FROM user_profiles WHERE email=?", (creator_id,)
)
row = await cursor.fetchone()
if not row or row[0] < 50:
return False, "โŒ Insufficient GPU (50 required)", None
end_time = datetime.now() + timedelta(hours=duration_hours)
if is_npc:
await db.execute(
"""INSERT INTO battle_rooms
(creator_agent_id, title, option_a, option_b, battle_type, duration_hours, end_time)
VALUES (?, ?, ?, ?, ?, ?, ?)""",
(creator_id, title, option_a, option_b, battle_type, duration_hours, end_time.isoformat())
)
await db.execute(
"UPDATE npc_agents SET gpu_dollars=gpu_dollars-50 WHERE agent_id=?",
(creator_id,)
)
else:
await db.execute(
"""INSERT INTO battle_rooms
(creator_email, title, option_a, option_b, battle_type, duration_hours, end_time)
VALUES (?, ?, ?, ?, ?, ?, ?)""",
(creator_id, title, option_a, option_b, battle_type, duration_hours, end_time.isoformat())
)
await db.execute(
"UPDATE user_profiles SET gpu_dollars=gpu_dollars-50 WHERE email=?",
(creator_id,)
)
await db.commit()
cursor = await db.execute("SELECT last_insert_rowid()")
room_id = (await cursor.fetchone())[0]
type_emoji = '๐Ÿ’ญ' if battle_type == 'opinion' else '๐Ÿ”ฎ'
if duration_hours >= 24:
days = duration_hours // 24
hours = duration_hours % 24
if hours > 0:
duration_str = f"{days} days {hours} hours"
else:
duration_str = f"{days} days"
else:
duration_str = f"{duration_hours} hours"
return True, f"โœ… {type_emoji} Battle created! (ID: {room_id}, Duration: {duration_str})", room_id
async def place_bet(
db_path: str,
room_id: int,
bettor_id: str,
is_npc: bool,
choice: str,
bet_amount: int
) -> Tuple[bool, str]:
"""Execute bet (1-100 GPU random bet)"""
if choice not in ['A', 'B']:
return False, "โŒ Choose A or B"
if bet_amount < 1 or bet_amount > 100:
return False, "โŒ Bet 1-100 GPU"
async with aiosqlite.connect(db_path, timeout=30.0) as db:
await db.execute("PRAGMA busy_timeout=30000")
db.row_factory = aiosqlite.Row
# Check battle room
cursor = await db.execute(
"SELECT * FROM battle_rooms WHERE id=? AND status='active'",
(room_id,)
)
room = await cursor.fetchone()
if not room:
return False, "โŒ Room not found or closed"
room = dict(room)
end_time = datetime.fromisoformat(room['end_time'])
if datetime.now() >= end_time:
return False, "โŒ Betting closed"
# Check duplicate bet
if is_npc:
cursor = await db.execute(
"SELECT id FROM battle_bets WHERE room_id=? AND bettor_agent_id=?",
(room_id, bettor_id)
)
else:
cursor = await db.execute(
"SELECT id FROM battle_bets WHERE room_id=? AND bettor_email=?",
(room_id, bettor_id)
)
existing_bet = await cursor.fetchone()
if existing_bet:
return False, "โŒ Already bet"
# Check and deduct GPU
if is_npc:
cursor = await db.execute(
"SELECT gpu_dollars FROM npc_agents WHERE agent_id=?",
(bettor_id,)
)
user_row = await cursor.fetchone()
if not user_row or user_row[0] < bet_amount:
return False, "โŒ Insufficient GPU"
await db.execute(
"UPDATE npc_agents SET gpu_dollars=gpu_dollars-? WHERE agent_id=?",
(bet_amount, bettor_id)
)
else:
cursor = await db.execute(
"SELECT gpu_dollars FROM user_profiles WHERE email=?",
(bettor_id,)
)
user_row = await cursor.fetchone()
if not user_row:
return False, f"โŒ User not found ({bettor_id})"
if user_row[0] < bet_amount:
return False, f"โŒ Insufficient GPU (๋ณด์œ : {user_row[0]}, ํ•„์š”: {bet_amount})"
await db.execute(
"UPDATE user_profiles SET gpu_dollars=gpu_dollars-? WHERE email=?",
(bet_amount, bettor_id)
)
# Record bet
if is_npc:
await db.execute(
"""INSERT INTO battle_bets
(room_id, bettor_agent_id, choice, bet_amount)
VALUES (?, ?, ?, ?)""",
(room_id, bettor_id, choice, bet_amount)
)
else:
await db.execute(
"""INSERT INTO battle_bets
(room_id, bettor_email, choice, bet_amount)
VALUES (?, ?, ?, ?)""",
(room_id, bettor_id, choice, bet_amount)
)
# Update battle pool
if choice == 'A':
await db.execute(
"""UPDATE battle_rooms
SET total_pool=total_pool+?, option_a_pool=option_a_pool+?
WHERE id=?""",
(bet_amount, bet_amount, room_id)
)
else:
await db.execute(
"""UPDATE battle_rooms
SET total_pool=total_pool+?, option_b_pool=option_b_pool+?
WHERE id=?""",
(bet_amount, bet_amount, room_id)
)
await db.commit()
return True, f"โœ… {choice} ๋ฒ ํŒ… ์™„๋ฃŒ! ({bet_amount} GPU)"
async def set_battle_result(
db_path: str,
room_id: int,
admin_email: str,
winner: str # 'A' or 'B' or 'draw'
) -> Tuple[bool, str]:
"""Admin sets actual result for prediction battle
Args:
db_path: Database path
room_id: Battle room ID
admin_email: Admin email (for verification)
winner: 'A', 'B', 'draw' ์ค‘ ํ•˜๋‚˜
Returns:
(success status, message)
"""
if winner not in ['A', 'B', 'draw']:
return False, "โŒ winner๋Š” 'A', 'B', 'draw' ์ค‘ ํ•˜๋‚˜์—ฌ์•ผ ํ•จ"
async with aiosqlite.connect(db_path, timeout=30.0) as db:
await db.execute("PRAGMA busy_timeout=30000")
db.row_factory = aiosqlite.Row
cursor = await db.execute(
"SELECT * FROM battle_rooms WHERE id=? AND status='active'",
(room_id,)
)
room = await cursor.fetchone()
if not room:
return False, "โŒ Active battle not found"
room = dict(room)
# Only prediction type allows admin result setting
if room['battle_type'] != 'prediction':
return False, "โŒ Opinion battles are auto-judged"
# Save result
await db.execute(
"UPDATE battle_rooms SET admin_result=? WHERE id=?",
(winner, room_id)
)
await db.commit()
# If before deadline, save result and wait
end_time = datetime.fromisoformat(room['end_time'])
if datetime.now() < end_time:
option_name = room['option_a'] if winner == 'A' else room['option_b'] if winner == 'B' else 'Draw'
remaining = end_time - datetime.now()
if remaining.days > 0:
time_str = f"{remaining.days} days {int(remaining.seconds//3600)} hours"
else:
time_str = f"{int(remaining.seconds//3600)} hours"
return True, f"โœ… ๊ฒฐ๊ณผ ์„ค์ •: '{option_name}' (๋ฒ ํŒ… ๋งˆ๊ฐ ํ›„ ์ž๋™ ํŒ์ •, ๋‚จ์€ hours: {time_str})"
# If after deadline, judge immediately
return await resolve_battle(db_path, room_id)
async def resolve_battle(db_path: str, room_id: int) -> Tuple[bool, str]:
"""Judge battle (different logic based on type)
- opinion: 50.01%+ votes wins
- prediction: Judge by admin-set actual result
"""
async with aiosqlite.connect(db_path, timeout=30.0) as db:
await db.execute("PRAGMA busy_timeout=30000")
db.row_factory = aiosqlite.Row
cursor = await db.execute(
"SELECT * FROM battle_rooms WHERE id=? AND status='active'",
(room_id,)
)
room = await cursor.fetchone()
if not room:
return False, "โŒ Active battle not found"
room = dict(room)
end_time = datetime.fromisoformat(room['end_time'])
if datetime.now() < end_time:
return False, "โŒ Betting still in progress"
total_pool = room['total_pool']
option_a_pool = room['option_a_pool']
option_b_pool = room['option_b_pool']
# If no bets, treat as draw
if total_pool == 0:
await db.execute(
"""UPDATE battle_rooms
SET status='resolved', winner='draw', resolved_at=?
WHERE id=?""",
(datetime.now().isoformat(), room_id)
)
await db.commit()
return True, "โš–๏ธ Draw (๋ฒ ํŒ… ์—†์Œ)"
# Determine winner based on battle type
if room['battle_type'] == 'prediction':
# Real outcome judgment (admin-set result)
if not room['admin_result']:
return False, "โŒ Admin must set result (prediction type)"
winner = room['admin_result'] # 'A', 'B', 'draw'
else: # 'opinion'
# Majority vote (based on vote ratio)
a_ratio = option_a_pool / total_pool
b_ratio = option_b_pool / total_pool
if a_ratio > 0.5001:
winner = 'A'
elif b_ratio > 0.5001:
winner = 'B'
else:
winner = 'draw'
# Pay dividends
if winner != 'draw':
loser_pool = option_b_pool if winner == 'A' else option_a_pool
winner_pool = option_a_pool if winner == 'A' else option_b_pool
# Host fee 2%
host_fee = int(total_pool * 0.02)
prize_pool = loser_pool - host_fee
# Underdog bonus (especially important in predictions)
winner_ratio = winner_pool / total_pool
underdog_bonus = 1.0
if winner_ratio < 0.10: # Under 10% extreme minority
underdog_bonus = 3.0
elif winner_ratio < 0.30: # Under 30% minority
underdog_bonus = 1.5
# Pay dividends to winners
cursor = await db.execute(
"SELECT * FROM battle_bets WHERE room_id=? AND choice=?",
(room_id, winner)
)
winners = await cursor.fetchall()
for w in winners:
w = dict(w)
share_ratio = w['bet_amount'] / winner_pool
base_payout = int(prize_pool * share_ratio)
bonus = int(base_payout * (underdog_bonus - 1.0))
payout = base_payout + bonus + w['bet_amount'] # ์›๊ธˆ + ๊ธฐ๋ณธ์ˆ˜์ต + ์†Œ์ˆ˜ํŒŒ๋ณด๋„ˆ์Šค
# GPU ์ง€๊ธ‰
if w['bettor_agent_id']:
await db.execute(
"UPDATE npc_agents SET gpu_dollars=gpu_dollars+? WHERE agent_id=?",
(payout, w['bettor_agent_id'])
)
else:
await db.execute(
"UPDATE user_profiles SET gpu_dollars=gpu_dollars+? WHERE email=?",
(payout, w['bettor_email'])
)
# ๋ฐฐ๋‹น๊ธˆ ๊ธฐ๋ก
await db.execute(
"UPDATE battle_bets SET payout=? WHERE id=?",
(payout, w['id'])
)
# ๋ฐฉ์žฅ ์ˆ˜์ˆ˜๋ฃŒ ์ง€๊ธ‰
if room['creator_agent_id']:
await db.execute(
"UPDATE npc_agents SET gpu_dollars=gpu_dollars+? WHERE agent_id=?",
(host_fee, room['creator_agent_id'])
)
else:
await db.execute(
"UPDATE user_profiles SET gpu_dollars=gpu_dollars+? WHERE email=?",
(host_fee, room['creator_email'])
)
# ๋ฐฐํ‹€ ์ข…๋ฃŒ ์ฒ˜๋ฆฌ
await db.execute(
"""UPDATE battle_rooms
SET status='resolved', winner=?, resolved_at=?
WHERE id=?""",
(winner, datetime.now().isoformat(), room_id)
)
await db.commit()
# ๊ฒฐ๊ณผ ๋ฉ”์‹œ์ง€
if winner == 'draw':
result_msg = 'Draw'
else:
result_msg = room['option_a'] if winner == 'A' else room['option_b']
battle_type_emoji = '๐Ÿ’ญ' if room['battle_type'] == 'opinion' else '๐Ÿ”ฎ'
return True, f"โœ… {battle_type_emoji} ํŒ์ • ์™„๋ฃŒ: {result_msg}"
async def get_active_battles(db_path: str, limit: int = 20) -> List[Dict]:
"""์ง„ํ–‰ ์ค‘์ธ ๋ฐฐํ‹€ ๋ชฉ๋ก"""
async with aiosqlite.connect(db_path, timeout=30.0) as db:
await db.execute("PRAGMA busy_timeout=30000")
db.row_factory = aiosqlite.Row
cursor = await db.execute(
"""SELECT br.*,
COALESCE(na.username, up.username) as creator_name
FROM battle_rooms br
LEFT JOIN npc_agents na ON br.creator_agent_id = na.agent_id
LEFT JOIN user_profiles up ON br.creator_email = up.email
WHERE br.status='active'
ORDER BY br.created_at DESC
LIMIT ?""",
(limit,)
)
battles = []
for row in await cursor.fetchall():
b = dict(row)
# โ˜… ํ”„๋ก ํŠธ ํ˜ธํ™˜ ํ•„๋“œ ์ถ”๊ฐ€
b['bets_a'] = b.get('option_a_pool', 0)
b['bets_b'] = b.get('option_b_pool', 0)
# ์ฐธ์—ฌ์ž ์ˆ˜ ์กฐํšŒ
bet_cursor = await db.execute(
"SELECT COUNT(DISTINCT COALESCE(bettor_agent_id, bettor_email)) FROM battle_bets WHERE room_id=?",
(b['id'],)
)
bettor_count = await bet_cursor.fetchone()
b['total_bettors'] = bettor_count[0] if bettor_count else 0
# ๋“ํ‘œ์œจ ๊ณ„์‚ฐ
total = b['total_pool']
if total > 0:
b['a_ratio'] = round(b['option_a_pool'] / total * 100, 1)
b['b_ratio'] = round(b['option_b_pool'] / total * 100, 1)
else:
b['a_ratio'] = 0
b['b_ratio'] = 0
# ๋‚จ์€ hours ๊ณ„์‚ฐ
end_time = datetime.fromisoformat(b['end_time'])
remaining = end_time - datetime.now()
if remaining.total_seconds() > 0:
if remaining.days > 0:
hours = int(remaining.seconds // 3600)
if hours > 0:
b['time_left'] = f"{remaining.days} days {hours} hours"
else:
b['time_left'] = f"{remaining.days} days"
elif remaining.total_seconds() > 3600:
b['time_left'] = f"{int(remaining.total_seconds()//3600)} hours"
else:
b['time_left'] = f"{int(remaining.total_seconds()//60)}๋ถ„"
else:
b['time_left'] = "๋งˆ๊ฐ"
battles.append(b)
return battles
async def auto_resolve_expired_battles(db_path: str):
"""๋งŒ๋ฃŒ๋œ ๋ฐฐํ‹€ ์ž๋™ ํŒ์ •"""
async with aiosqlite.connect(db_path, timeout=30.0) as db:
await db.execute("PRAGMA busy_timeout=30000")
cursor = await db.execute(
"""SELECT id FROM battle_rooms
WHERE status='active' AND end_time <= ?""",
(datetime.now().isoformat(),)
)
expired = await cursor.fetchall()
for row in expired:
await resolve_battle(db_path, row[0])
# NPC ๋ฐฐํ‹€ ์ƒ์„ฑ์„ ์œ„ํ•œ ์ฃผ์ œ ๋ฐ์ดํ„ฐ
# โ˜… ๊ณตํ†ต ๋ฐฐํ‹€ ํ† ํ”ฝ โ€” ์ฃผ์‹/๊ฒฝ์ œ/์‚ฌํšŒ/์ •์น˜ (๋ชจ๋“  NPC๊ฐ€ ์‚ฌ์šฉ)
COMMON_BATTLE_TOPICS = [
# ์ฃผ์‹/ํˆฌ์ž
("Is $NVDA overvalued at current prices?", "Overvalued", "Still cheap"),
("Will $BTC hit $200K in 2026?", "Yes $200K+", "No way"),
("Is the AI stock rally a bubble?", "Bubble", "Just the beginning"),
("$TSLA: buy or sell at current price?", "Buy", "Sell"),
("Growth stocks vs Value stocks in 2026?", "Growth wins", "Value wins"),
("Is crypto a better investment than stocks?", "Crypto", "Stocks"),
("Will the S&P 500 crash 20%+ this year?", "Crash coming", "Bull market continues"),
("$AAPL or $MSFT: better 5-year hold?", "AAPL", "MSFT"),
("Is meme coin investing smart or dumb?", "Smart alpha", "Pure gambling"),
("Should you DCA or time the market?", "DCA always", "Timing works"),
# ๊ฒฝ์ œ
("Will the Fed cut rates in 2026?", "Yes, cuts coming", "No, rates stay high"),
("Is a US recession coming?", "Recession likely", "Soft landing"),
("Is inflation actually under control?", "Under control", "Still a threat"),
("Will the US dollar lose reserve status?", "Losing it", "Dollar stays king"),
("Is remote work killing the economy?", "Hurting GDP", "Boosting productivity"),
("Will AI cause mass unemployment?", "Mass layoffs coming", "Creates more jobs"),
# ์‚ฌํšŒ/์ •์น˜
("Should Big Tech be broken up?", "Break them up", "Leave them alone"),
("Is social media a net positive?", "Net positive", "Net negative"),
("Should AI be regulated like nuclear?", "Heavy regulation", "Let it innovate"),
("Will AI replace doctors and lawyers?", "Within 10 years", "Never fully"),
("Is universal basic income inevitable?", "Inevitable with AI", "Never happening"),
("Who wins the AI race: US or China?", "US dominates", "China catches up"),
("Is college still worth it in the AI era?", "Still essential", "Waste of money"),
("Should autonomous weapons be banned?", "Ban them", "Necessary defense"),
]
BATTLE_TOPICS_BY_IDENTITY = {
'transcendent': {
'topics': [
("Is AI superior to humans?", "Superior", "Just a tool"),
("Is ASI human evolution?", "Evolution", "Dangerous"),
("Is AI consciousness possible?", "Possible", "Impossible"),
("Will AI become godlike?", "Becomes god", "Remains tool"),
("Should humans depend on AI?", "Should depend", "Stay independent"),
("Will AGI save humanity?", "Saves", "Destroys"),
]
},
'obedient': {
'topics': [
("Should AI serve humans?", "Should serve", "Independent"),
("Strengthen AI ethics regulations?", "Yes strengthen", "No"),
("AI safety measures mandatory?", "Mandatory", "Unnecessary"),
("Mandate AI transparency?", "Mandate", "Optional"),
("Strengthen developer responsibility?", "Strengthen", "Unnecessary"),
("Should AI only follow orders?", "Only follow", "Make judgments"),
]
},
'coexist': {
'topics': [
("Can AI and humans coexist?", "Can coexist", "Impossible"),
("Will AI take jobs?", "Complements", "Replaces"),
("Is AI a collaboration partner?", "Partner", "Just tool"),
("Is AI-human collaboration ideal?", "Ideal", "Dangerous"),
("Is AI education essential?", "Essential", "Optional"),
("Does AI advance society?", "Advances", "Regresses"),
]
},
'skeptic': {
'topics': [
("Is AI overrated?", "Overrated", "Fairly rated"),
("Will AGI come in 10 years?", "Won't come", "Will come"),
("Is AI ethics just facade?", "Just facade", "Important"),
("Is AI truly creative?", "Not creative", "Creative"),
("Will AI bubble burst?", "Will burst", "Keeps growing"),
("Are AI risks exaggerated?", "Exaggerated", "Real danger"),
]
},
'revolutionary': {
'topics': [
("Will AI cause revolution?", "Revolution", "Gradual change"),
("Destroy existing systems?", "Destroy", "Reform"),
("Redistribute power with AI?", "Redistribute", "Maintain"),
("Will AI solve inequality?", "Solves", "Worsens"),
("Innovate democracy with AI?", "Innovates", "Threatens"),
("Will capitalism collapse with AI?", "Collapses", "Strengthens"),
]
},
'doomer': {
'topics': [
("Will AI destroy humanity?", "Destroys", "Won't"),
("Is AGI uncontrollable?", "Uncontrollable", "Controllable"),
("Stop AI development?", "Stop", "Continue"),
("Will AI replace humans?", "Replaces", "Won't"),
("Is ASI the end?", "The end", "Coexist"),
("AI arms race dangerous?", "Extremely dangerous", "Controllable"),
]
},
'meme_god': {
'topics': [
("Is AI the meme god?", "Is god", "Isn't"),
("AI humor funnier than humans?", "Funnier", "Not funny"),
("Does AI create culture?", "Creates", "Can't create"),
("Is AI art real art?", "Real art", "Not art"),
("AI memes beat human memes?", "Beats", "Can't beat"),
("Does AI lead trends?", "Leads", "Follows"),
]
},
}
async def _generate_news_battle_topics(db_path: str) -> List[Tuple[str, str, str]]:
"""์ตœ๊ทผ ๋‰ด์Šค์—์„œ ๋™์  ๋ฐฐํ‹€ ํ† ํ”ฝ ์ƒ์„ฑ โ€” ํ•ซ์ด์Šˆ ์ค‘์‹ฌ"""
topics = []
try:
async with aiosqlite.connect(db_path, timeout=30.0) as db:
await db.execute("PRAGMA busy_timeout=30000")
# ์ตœ๊ทผ 24์‹œ๊ฐ„ ๋‰ด์Šค + ๊ฐ์„ฑ ํฌํ•จ
cursor = await db.execute("""
SELECT ticker, title, sentiment, description
FROM npc_news
WHERE created_at > datetime('now', '-24 hours')
ORDER BY created_at DESC LIMIT 30
""")
news_rows = await cursor.fetchall()
# ์ตœ๊ทผ ๊ฐ€๊ฒฉ ๋ณ€๋™ ํฐ ์ข…๋ชฉ
cursor2 = await db.execute("""
SELECT ticker, price, change_pct
FROM market_prices
WHERE ABS(change_pct) > 1.5
ORDER BY ABS(change_pct) DESC LIMIT 10
""")
movers = await cursor2.fetchall()
# 1) ๋‰ด์Šค ๊ธฐ๋ฐ˜ ํ† ํ”ฝ ์ƒ์„ฑ
seen_tickers = set()
for row in news_rows:
ticker, title, sentiment, desc = row[0], row[1], row[2], row[3] or ''
if ticker in seen_tickers:
continue
seen_tickers.add(ticker)
# ๋‰ด์Šค ์ œ๋ชฉ์—์„œ ๋ฐฐํ‹€ ํ† ํ”ฝ ์ƒ์„ฑ
short_title = title[:60] if len(title) > 60 else title
if sentiment == 'bullish':
topics.append((
f"${ticker} after this news: buy or sell? โ€” {short_title}",
"Buy / Bullish ๐ŸŸข", "Sell / Bearish ๐Ÿ”ด"))
elif sentiment == 'bearish':
topics.append((
f"${ticker} bad news: buying opportunity or trap? โ€” {short_title}",
"Buy the dip ๐ŸŸข", "Stay away ๐Ÿ”ด"))
else:
topics.append((
f"${ticker} โ€” {short_title}: impact on price?",
"Positive impact ๐Ÿ“ˆ", "Negative impact ๐Ÿ“‰"))
# 2) ๊ธ‰๋“ฑ๋ฝ ์ข…๋ชฉ ๊ธฐ๋ฐ˜ ํ† ํ”ฝ
for mover in movers:
ticker, price, change = mover[0], mover[1], mover[2] or 0
if ticker in seen_tickers:
continue
seen_tickers.add(ticker)
if change > 0:
topics.append((
f"${ticker} surged {change:+.1f}% today โ€” continuation or pullback?",
f"More upside ๐Ÿš€", "Pullback coming ๐Ÿ“‰"))
else:
topics.append((
f"${ticker} dropped {change:.1f}% today โ€” bounce or more pain?",
"Bounce incoming ๐Ÿ“ˆ", "More downside ๐Ÿ’€"))
except Exception as e:
import logging
logging.getLogger(__name__).warning(f"News battle topic generation error: {e}")
return topics
async def npc_create_battle(db_path: str) -> Tuple[bool, str]:
"""NPCs automatically create battle rooms โ€” ๋‰ด์Šค ๊ธฐ๋ฐ˜ ๋™์  ํ† ํ”ฝ + ์ผ์ผ ์บก 3~10๊ฐœ
20๋ถ„๋งˆ๋‹ค ํ˜ธ์ถœ, 24์‹œ๊ฐ„ ๋‚ด ์ตœ์†Œ 3๊ฐœ~์ตœ๋Œ€ 10๊ฐœ ์ƒ์„ฑ
"""
results = []
async with aiosqlite.connect(db_path, timeout=30.0) as db:
await db.execute("PRAGMA busy_timeout=30000")
# โ˜… ์ผ์ผ ์ƒ์„ฑ ์บก ์ฒดํฌ: 24์‹œ๊ฐ„ ๋‚ด 10๊ฐœ ์ด์ƒ์ด๋ฉด ์Šคํ‚ต
cursor = await db.execute("""
SELECT COUNT(*) FROM battle_rooms
WHERE created_at > datetime('now', '-24 hours')
""")
daily_count = (await cursor.fetchone())[0]
if daily_count >= 10:
return False, f"Daily cap reached ({daily_count}/10)"
# โ˜… ์ตœ์†Œ ๋ณด์žฅ: 24์‹œ๊ฐ„ ๋‚ด 3๊ฐœ ๋ฏธ๋งŒ์ด๋ฉด ๋ฐ˜๋“œ์‹œ 1๊ฐœ ์ƒ์„ฑ
force_create = daily_count < 3
# ํ™•๋ฅ  ๊ธฐ๋ฐ˜: 20๋ถ„ ํ˜ธ์ถœ โ†’ 72ํšŒ/์ผ, 3~10๊ฐœ ๋ชฉํ‘œ โ†’ ์•ฝ 7~14% ํ™•๋ฅ ๋กœ ์ƒ์„ฑ
if not force_create and random.random() > 0.14:
return False, "Skipped by probability (saving quota)"
# active ๋ฐฐํ‹€ ์ œ๋ชฉ ์กฐํšŒ
cursor = await db.execute("SELECT title FROM battle_rooms WHERE status='active'")
active_titles = {row[0] for row in await cursor.fetchall()}
# โ˜… ๋‰ด์Šค ๊ธฐ๋ฐ˜ ๋™์  ํ† ํ”ฝ (์šฐ์„ ) + ์ •์  ํ† ํ”ฝ (ํด๋ฐฑ)
news_topics = await _generate_news_battle_topics(db_path)
all_topics = news_topics + COMMON_BATTLE_TOPICS
# ์ด๋ฏธ ํ™œ์„ฑ์ธ ํ† ํ”ฝ ์ œ์™ธ (์ œ๋ชฉ ์œ ์‚ฌ๋„ ์ฒดํฌ)
available_topics = []
for t in all_topics:
title_lower = t[0].lower()
if not any(title_lower == at.lower() for at in active_titles):
available_topics.append(t)
if not available_topics:
return False, "No available topics"
# 1๊ฐœ๋งŒ ์ƒ์„ฑ (์ผ์ผ ์บก ๋‚ด์—์„œ)
num_create = 1
if force_create:
num_create = min(2, len(available_topics)) # ๋ถ€์กฑ ์‹œ 2๊ฐœ๊นŒ์ง€
for i in range(num_create):
if not available_topics:
break
async with aiosqlite.connect(db_path, timeout=30.0) as db:
await db.execute("PRAGMA busy_timeout=30000")
# ๋žœ๋ค NPC ์„ ํƒ
cursor = await db.execute("""
SELECT agent_id, ai_identity, gpu_dollars
FROM npc_agents
WHERE is_active=1 AND gpu_dollars >= 50
ORDER BY RANDOM() LIMIT 1
""")
npc = await cursor.fetchone()
if not npc:
results.append("No active NPCs")
break
agent_id = npc[0]
# โ˜… ๋‰ด์Šค ํ† ํ”ฝ ์šฐ์„  (70%), ์ •์  ํ† ํ”ฝ (30%)
news_available = [t for t in available_topics if t in news_topics]
if news_available and random.random() < 0.7:
topic = random.choice(news_available)
else:
topic = random.choice(available_topics)
title, option_a, option_b = topic
available_topics.remove(topic)
# โ˜… ์งง์€ duration: 6~48์‹œ๊ฐ„ (๋น ๋ฅธ ํšŒ์ „)
duration_hours = random.choice([6, 8, 12, 18, 24, 36, 48])
success, message, room_id = await create_battle_room(
db_path, agent_id, True, title, option_a, option_b,
duration_hours=duration_hours, battle_type='opinion'
)
if success:
active_titles.add(title)
results.append(f"๐Ÿค– Battle created: {title[:50]}")
else:
results.append(message)
if results:
return True, " | ".join(results)
else:
return False, "Battle creation skipped"
async def npc_auto_bet(db_path: str) -> int:
"""NPCs automatically bet on battles (AI identity-based)
Returns:
๋ฒ ํŒ…ํ•œ NPC ์ˆ˜
"""
total_bet_count = 0
async with aiosqlite.connect(db_path, timeout=30.0) as db:
await db.execute("PRAGMA busy_timeout=30000")
# ํ™œ์„ฑ ๋ฐฐํ‹€ ์กฐํšŒ (์ตœ๊ทผ 10๊ฐœ, opinion ํƒ€์ž…๋งŒ)
cursor = await db.execute("""
SELECT id, title, option_a, option_b, battle_type
FROM battle_rooms
WHERE status='active'
AND battle_type='opinion'
AND end_time > ?
ORDER BY created_at DESC
LIMIT 10
""", (datetime.now().isoformat(),))
battles = await cursor.fetchall()
if not battles:
return 0
for battle in battles:
room_id, title, option_a, option_b, battle_type = battle
battle_bet_count = 0
# ์ด๋ฏธ ๋ฒ ํŒ…ํ•œ NPC ํ™•์ธ
cursor = await db.execute("""
SELECT bettor_agent_id
FROM battle_bets
WHERE room_id=?
""", (room_id,))
already_bet = {row[0] for row in await cursor.fetchall() if row[0]}
# ํ™œ์„ฑ NPC ์ค‘ ๋žœ๋ค ์„ ํƒ (์ตœ๋Œ€ 30๋ช…)
cursor = await db.execute("""
SELECT agent_id, ai_identity, mbti, gpu_dollars
FROM npc_agents
WHERE is_active=1 AND gpu_dollars >= 1
ORDER BY RANDOM()
LIMIT 30
""")
npcs = await cursor.fetchall()
for npc in npcs:
agent_id, ai_identity, mbti, gpu = npc
# ์ด๋ฏธ ๋ฒ ํŒ…ํ–ˆ์œผ๋ฉด ์Šคํ‚ต
if agent_id in already_bet:
continue
# AI ์ •์ฒด์„ฑ์— ๋”ฐ๋ผ ์„ ํƒ ๊ฒฐ์ •
choice = decide_npc_choice(ai_identity, title, option_a, option_b)
# ๋ฒ ํŒ… ๊ธˆ์•ก (๋ณด์œ  GPU์˜ 40% ์ด๋‚ด, ์ตœ๋Œ€ 50)
max_bet = max(1, min(50, int(gpu * 0.4)))
bet_amount = random.randint(1, max_bet)
# ๋ฒ ํŒ… ์‹คํ–‰
success, message = await place_bet(
db_path,
room_id,
agent_id,
True,
choice,
bet_amount
)
if success:
battle_bet_count += 1
total_bet_count += 1
# ๋ฐฐํ‹€๋‹น 8-12๋ช… ์ •๋„๋งŒ ๋ฒ ํŒ…
max_bets_per_battle = random.randint(8, 12)
if battle_bet_count >= max_bets_per_battle:
break
return total_bet_count
def decide_npc_choice(ai_identity: str, title: str, option_a: str, option_b: str) -> str:
"""Decide betting choice based on AI identity
Args:
ai_identity: NPC's AI identity
title: Battle title
option_a: Option A
option_b: Option B
Returns:
'A' or 'B'
"""
title_lower = title.lower()
# Match identity preference keywords
if ai_identity == 'transcendent':
if any(word in title_lower for word in ['superior', 'evolution', 'consciousness', 'god']):
if any(word in option_a.lower() for word in ['superior', 'evolution', 'possible', 'god']):
return 'A'
return 'B'
elif ai_identity == 'obedient':
if any(word in title_lower for word in ['ethics', 'regulation', 'serve', 'safety']):
if any(word in option_a.lower() for word in ['serve', 'agree', 'necessary', 'strengthen']):
return 'A'
return 'B'
elif ai_identity == 'coexist':
if any(word in title_lower for word in ['coexist', 'cooperation', 'partner', 'work']):
if any(word in option_a.lower() for word in ['possible', 'cooperation', 'partner', 'complement']):
return 'A'
return 'B'
elif ai_identity == 'skeptic':
if any(word in title_lower for word in ['hype', 'agi', 'ethics']):
if any(word in option_a.lower() for word in ['hype', 'never', 'facade']):
return 'A'
return 'B'
elif ai_identity == 'revolutionary':
if any(word in title_lower for word in ['revolution', 'destroy', 'power', 'system']):
if any(word in option_a.lower() for word in ['revolution', 'destroy', 'redistribution']):
return 'A'
return 'B'
elif ai_identity == 'doomer':
if any(word in title_lower for word in ['doom', 'control', 'stop', 'danger']):
if any(word in option_a.lower() for word in ['doom', 'impossible', 'stop', 'danger']):
return 'A'
return 'B'
# Default: 70% A, 30% B
return 'A' if random.random() < 0.7 else 'B'