@@ -41,3 +41,128 @@ export function clearAllRounds() {
4141 db . exec ( "DELETE FROM rounds;" ) ;
4242 db . exec ( "DELETE FROM sqlite_sequence WHERE name = 'rounds';" ) ;
4343}
44+
45+ // ── Betting tables ──────────────────────────────────────────────────────────
46+
47+ db . exec ( `
48+ CREATE TABLE IF NOT EXISTS users (
49+ id TEXT PRIMARY KEY,
50+ nickname TEXT UNIQUE NOT NULL,
51+ balance INTEGER DEFAULT 1000,
52+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
53+ );
54+ ` ) ;
55+
56+ db . exec ( `
57+ CREATE TABLE IF NOT EXISTS bets (
58+ id INTEGER PRIMARY KEY AUTOINCREMENT,
59+ user_id TEXT NOT NULL,
60+ round_num INTEGER NOT NULL,
61+ contestant TEXT NOT NULL,
62+ amount INTEGER NOT NULL,
63+ won INTEGER,
64+ payout INTEGER DEFAULT 0,
65+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
66+ UNIQUE(user_id, round_num)
67+ );
68+ ` ) ;
69+
70+ // ── Betting functions ───────────────────────────────────────────────────────
71+
72+ export function createUser ( id : string , nickname : string ) {
73+ const stmt = db . prepare ( "INSERT INTO users (id, nickname) VALUES ($id, $nickname)" ) ;
74+ stmt . run ( { $id : id , $nickname : nickname } ) ;
75+ return { id, nickname, balance : 1000 } ;
76+ }
77+
78+ export function getUser ( id : string ) {
79+ return db . query ( "SELECT id, nickname, balance FROM users WHERE id = $id" ) . get ( { $id : id } ) as { id : string ; nickname : string ; balance : number } | null ;
80+ }
81+
82+ export function placeBet ( userId : string , roundNum : number , contestant : string , amount : number ) {
83+ const user = getUser ( userId ) ;
84+ if ( ! user ) throw new Error ( "User not found" ) ;
85+ if ( amount <= 0 ) throw new Error ( "Amount must be positive" ) ;
86+ if ( amount > user . balance ) throw new Error ( "Insufficient balance" ) ;
87+
88+ const existing = db . query ( "SELECT id FROM bets WHERE user_id = $userId AND round_num = $roundNum" ) . get ( { $userId : userId , $roundNum : roundNum } ) ;
89+ if ( existing ) throw new Error ( "Already bet this round" ) ;
90+
91+ db . exec ( "BEGIN" ) ;
92+ try {
93+ db . prepare ( "INSERT INTO bets (user_id, round_num, contestant, amount) VALUES ($userId, $roundNum, $contestant, $amount)" )
94+ . run ( { $userId : userId , $roundNum : roundNum , $contestant : contestant , $amount : amount } ) ;
95+ db . prepare ( "UPDATE users SET balance = balance - $amount WHERE id = $userId" )
96+ . run ( { $amount : amount , $userId : userId } ) ;
97+ db . exec ( "COMMIT" ) ;
98+ } catch ( e ) {
99+ db . exec ( "ROLLBACK" ) ;
100+ throw e ;
101+ }
102+
103+ return {
104+ bet : { userId, roundNum, contestant, amount } ,
105+ balance : user . balance - amount ,
106+ } ;
107+ }
108+
109+ export function resolveBets ( roundNum : number , winnerName : string | null ) {
110+ const bets = db . query ( "SELECT id, user_id, contestant, amount FROM bets WHERE round_num = $roundNum AND won IS NULL" )
111+ . all ( { $roundNum : roundNum } ) as { id : number ; user_id : string ; contestant : string ; amount : number } [ ] ;
112+
113+ if ( bets . length === 0 ) return ;
114+
115+ db . exec ( "BEGIN" ) ;
116+ try {
117+ for ( const bet of bets ) {
118+ if ( winnerName === null ) {
119+ // Tie — refund
120+ db . prepare ( "UPDATE bets SET won = 0, payout = $amount WHERE id = $id" )
121+ . run ( { $amount : bet . amount , $id : bet . id } ) ;
122+ db . prepare ( "UPDATE users SET balance = balance + $amount WHERE id = $userId" )
123+ . run ( { $amount : bet . amount , $userId : bet . user_id } ) ;
124+ } else if ( bet . contestant === winnerName ) {
125+ const payout = bet . amount * 2 ;
126+ db . prepare ( "UPDATE bets SET won = 1, payout = $payout WHERE id = $id" )
127+ . run ( { $payout : payout , $id : bet . id } ) ;
128+ db . prepare ( "UPDATE users SET balance = balance + $payout WHERE id = $userId" )
129+ . run ( { $payout : payout , $userId : bet . user_id } ) ;
130+ } else {
131+ db . prepare ( "UPDATE bets SET won = 0, payout = 0 WHERE id = $id" )
132+ . run ( { $id : bet . id } ) ;
133+ }
134+ }
135+ db . exec ( "COMMIT" ) ;
136+ } catch ( e ) {
137+ db . exec ( "ROLLBACK" ) ;
138+ throw e ;
139+ }
140+ }
141+
142+ export function getLeaderboard ( limit = 10 ) {
143+ return db . query ( "SELECT id, nickname, balance FROM users ORDER BY balance DESC LIMIT $limit" )
144+ . all ( { $limit : limit } ) as { id : string ; nickname : string ; balance : number } [ ] ;
145+ }
146+
147+ export function getBetsForRound ( roundNum : number ) {
148+ const rows = db . query (
149+ "SELECT contestant, COUNT(*) as count, SUM(amount) as total FROM bets WHERE round_num = $roundNum GROUP BY contestant"
150+ ) . all ( { $roundNum : roundNum } ) as { contestant : string ; count : number ; total : number } [ ] ;
151+
152+ const result : Record < string , { count : number ; total : number } > = { } ;
153+ for ( const row of rows ) {
154+ result [ row . contestant ] = { count : row . count , total : row . total } ;
155+ }
156+ return result ;
157+ }
158+
159+ export function getUserBetForRound ( userId : string , roundNum : number ) {
160+ return db . query ( "SELECT contestant, amount, won, payout FROM bets WHERE user_id = $userId AND round_num = $roundNum" )
161+ . get ( { $userId : userId , $roundNum : roundNum } ) as { contestant : string ; amount : number ; won : number | null ; payout : number } | null ;
162+ }
163+
164+ export function clearAllBets ( ) {
165+ db . exec ( "DELETE FROM bets;" ) ;
166+ db . exec ( "DELETE FROM users;" ) ;
167+ db . exec ( "DELETE FROM sqlite_sequence WHERE name = 'bets';" ) ;
168+ }
0 commit comments