diff --git a/server/database.js b/server/database.js index f2ea8ab..050acf3 100644 --- a/server/database.js +++ b/server/database.js @@ -88,6 +88,51 @@ export function initializeDatabase() { ) `); + // Mining statistics table + db.exec(` + CREATE TABLE IF NOT EXISTS mining_stats ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + turtle_id INTEGER NOT NULL, + block_type TEXT NOT NULL, + count INTEGER DEFAULT 1, + session_start INTEGER NOT NULL, + last_mined INTEGER NOT NULL + ) + `); + + // Turtle groups/teams table + db.exec(` + CREATE TABLE IF NOT EXISTS turtle_groups ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + group_name TEXT NOT NULL UNIQUE, + color TEXT DEFAULT '#3b82f6', + created_at INTEGER NOT NULL + ) + `); + + // Turtle group membership table + db.exec(` + CREATE TABLE IF NOT EXISTS turtle_group_members ( + turtle_id INTEGER NOT NULL, + group_id INTEGER NOT NULL, + joined_at INTEGER NOT NULL, + PRIMARY KEY (turtle_id, group_id), + FOREIGN KEY (group_id) REFERENCES turtle_groups(id) ON DELETE CASCADE + ) + `); + + // Session tracking table (for time-based statistics) + db.exec(` + CREATE TABLE IF NOT EXISTS turtle_sessions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + turtle_id INTEGER NOT NULL, + started_at INTEGER NOT NULL, + ended_at INTEGER, + blocks_mined INTEGER DEFAULT 0, + distance_traveled INTEGER DEFAULT 0 + ) + `); + // Create indexes for better performance db.exec(` CREATE INDEX IF NOT EXISTS idx_world_blocks_discovered @@ -278,6 +323,138 @@ export function closeMiningArea(areaId) { stmt.run(Date.now(), areaId); } +// Mining Statistics +export function recordBlockMined(turtleId, blockType) { + const stmt = db.prepare(` + INSERT INTO mining_stats (turtle_id, block_type, count, session_start, last_mined) + VALUES (?, ?, 1, ?, ?) + ON CONFLICT(turtle_id, block_type, session_start) + DO UPDATE SET count = count + 1, last_mined = ? + `); + const now = Date.now(); + const sessionStart = now - (now % (24 * 60 * 60 * 1000)); // Start of day + stmt.run(turtleId, blockType, sessionStart, now, now); +} + +export function getMiningStats(turtleId = null, days = 7) { + const cutoff = Date.now() - (days * 24 * 60 * 60 * 1000); + + if (turtleId) { + const stmt = db.prepare(` + SELECT block_type, SUM(count) as total_count + FROM mining_stats + WHERE turtle_id = ? AND session_start >= ? + GROUP BY block_type + ORDER BY total_count DESC + `); + return stmt.all(turtleId, cutoff); + } else { + const stmt = db.prepare(` + SELECT turtle_id, block_type, SUM(count) as total_count + FROM mining_stats + WHERE session_start >= ? + GROUP BY turtle_id, block_type + ORDER BY turtle_id, total_count DESC + `); + return stmt.all(cutoff); + } +} + +export function getTopMiners(limit = 10) { + const stmt = db.prepare(` + SELECT turtle_id, SUM(count) as total_blocks + FROM mining_stats + GROUP BY turtle_id + ORDER BY total_blocks DESC + LIMIT ? + `); + return stmt.all(limit); +} + +// Turtle Groups/Teams +export function createGroup(groupName, color = '#3b82f6') { + const stmt = db.prepare(` + INSERT INTO turtle_groups (group_name, color, created_at) + VALUES (?, ?, ?) + `); + const result = stmt.run(groupName, color, Date.now()); + return result.lastInsertRowid; +} + +export function getAllGroups() { + const stmt = db.prepare('SELECT * FROM turtle_groups ORDER BY created_at DESC'); + return stmt.all(); +} + +export function deleteGroup(groupId) { + const stmt = db.prepare('DELETE FROM turtle_groups WHERE id = ?'); + stmt.run(groupId); +} + +export function addTurtleToGroup(turtleId, groupId) { + const stmt = db.prepare(` + INSERT OR IGNORE INTO turtle_group_members (turtle_id, group_id, joined_at) + VALUES (?, ?, ?) + `); + stmt.run(turtleId, groupId, Date.now()); +} + +export function removeTurtleFromGroup(turtleId, groupId) { + const stmt = db.prepare(` + DELETE FROM turtle_group_members + WHERE turtle_id = ? AND group_id = ? + `); + stmt.run(turtleId, groupId); +} + +export function getGroupMembers(groupId) { + const stmt = db.prepare(` + SELECT turtle_id, joined_at + FROM turtle_group_members + WHERE group_id = ? + `); + return stmt.all(groupId); +} + +export function getTurtleGroups(turtleId) { + const stmt = db.prepare(` + SELECT g.*, m.joined_at + FROM turtle_groups g + JOIN turtle_group_members m ON g.id = m.group_id + WHERE m.turtle_id = ? + `); + return stmt.all(turtleId); +} + +// Session Tracking +export function startSession(turtleId) { + const stmt = db.prepare(` + INSERT INTO turtle_sessions (turtle_id, started_at) + VALUES (?, ?) + `); + const result = stmt.run(turtleId, Date.now()); + return result.lastInsertRowid; +} + +export function endSession(sessionId, blocksMined, distanceTraveled) { + const stmt = db.prepare(` + UPDATE turtle_sessions + SET ended_at = ?, blocks_mined = ?, distance_traveled = ? + WHERE id = ? + `); + stmt.run(Date.now(), blocksMined, distanceTraveled, sessionId); +} + +export function getSessionStats(turtleId, limit = 10) { + const stmt = db.prepare(` + SELECT * FROM turtle_sessions + WHERE turtle_id = ? + ORDER BY started_at DESC + LIMIT ? + `); + return stmt.all(turtleId, limit); +} + // Cleanup function export function closeDatabase() { db.close();