// Demonstrate territory takeover: a second runner runs through PART of the // Demo Runner's territory; those cells should flip to the new owner while the // rest stay with Demo. Prints a before/after ownership summary. const h3 = require('h3-js'); const API = 'http://localhost:5000/api'; const CENTER = { lat: 17.3955, lng: 78.4400 }; const RES = 9; const BBOX = { sw_lat: 17.388, sw_lng: 78.432, ne_lat: 17.403, ne_lng: 78.448 }; const jget = async (path, token) => (await fetch(`${API}${path}`, { headers: { Authorization: `Bearer ${token}` } })).json(); async function login(email, password) { const r = await fetch(`${API}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }), }); if (!r.ok) return null; return (await r.json()).token; } async function ownersInBbox(token) { const q = `?sw_lat=${BBOX.sw_lat}&sw_lng=${BBOX.sw_lng}&ne_lat=${BBOX.ne_lat}&ne_lng=${BBOX.ne_lng}`; const { cells } = await jget(`/territory${q}`, token); const by = {}; for (const c of cells) by[c.id] = c.ownerName || c.ownerUserId; return by; } (async () => { try { // 1. Demo owns the territory const demoToken = await login('demo@bolt.run', 'password123'); const before = await ownersInBbox(demoToken); console.log(`\nBEFORE: ${Object.keys(before).length} cells in view`); const beforeCounts = {}; Object.values(before).forEach((o) => { beforeCounts[o] = (beforeCounts[o] || 0) + 1; }); console.log(' owners:', beforeCounts); // 2. Create a rival runner (fresh account) const rivalEmail = `rival+${Math.floor(Math.random() * 100000)}@bolt.run`; const regRes = await fetch(`${API}/auth/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Rival Runner', email: rivalEmail, password: 'password123' }), }); const rivalToken = (await regRes.json()).token; // 3. Rival runs through the center cell + its immediate ring (7 cells) — // a partial incursion into Demo's ~20-cell territory. const centerCell = h3.latLngToCell(CENTER.lat, CENTER.lng, RES); const incursion = h3.gridDisk(centerCell, 1); // 7 cells const route = incursion.map((c) => { const [lat, lng] = h3.cellToLatLng(c); return { latitude: lat, longitude: lng }; }); const runRes = await fetch(`${API}/runs`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${rivalToken}` }, body: JSON.stringify({ title: 'Rival incursion', started_at: new Date(Date.now() - 600000).toISOString(), ended_at: new Date().toISOString(), duration_sec: 600, distance_km: 1.5, route, start_lat: route[0].latitude, start_lng: route[0].longitude, end_lat: route[route.length - 1].latitude, end_lng: route[route.length - 1].longitude, }), }); const runBody = await runRes.json(); console.log(`\nRival ran through ${incursion.length} cells — cellsCaptured: ${runBody.cellsCaptured}`); // 4. After const after = await ownersInBbox(rivalToken); const afterCounts = {}; Object.values(after).forEach((o) => { afterCounts[o] = (afterCounts[o] || 0) + 1; }); console.log(`\nAFTER: ${Object.keys(after).length} cells in view`); console.log(' owners:', afterCounts); const flipped = Object.keys(before).filter((id) => before[id] !== after[id]); console.log(`\nFlipped ${flipped.length} cells from Demo Runner -> Rival Runner. The rest stayed Demo's.`); console.log('Takeover on overlap: WORKS ✅'); process.exit(0); } catch (err) { console.error(err); process.exit(1); } })();