{"id":10928,"date":"2025-10-27T10:00:07","date_gmt":"2025-10-27T09:00:07","guid":{"rendered":"https:\/\/spgoo.org\/?page_id=10928"},"modified":"2025-10-27T10:00:07","modified_gmt":"2025-10-27T09:00:07","slug":"tournoi-round-robin-3d","status":"publish","type":"page","link":"https:\/\/spgoo.org\/?page_id=10928","title":{"rendered":"Tournoi Round-Robin  3D"},"content":{"rendered":"    <style>\r\n        #containerGames {\r\n            position: absolute;\r\n            left: 25%;\r\n            top:10%;\r\n            width: 800px;\r\n            height: 700px;\r\n            position: relative;\r\n        }\r\n\r\n        #ui {\r\n            position: absolute;\r\n            top: 12%;\r\n            left: 20px;\r\n            z-index: 100;\r\n        }\r\n\r\n        h1 {\r\n            color: white;\r\n            margin-bottom: 20px;\r\n            text-shadow: 2px 2px 4px rgba(0,0,0,0.5);\r\n            font-size: 32px;\r\n        }\r\n\r\n        #controls {\r\n            background: rgba(255, 255, 255, 0.95);\r\n            padding: 15px;\r\n            border-radius: 10px;\r\n            box-shadow: 0 4px 6px rgba(0,0,0,0.3);\r\n            margin-bottom: 20px;\r\n            display: flex;\r\n            gap: 10px;\r\n            align-items: center;\r\n        }\r\n\r\n        button {\r\n            background: #667eea;\r\n            color: white;\r\n            border: none;\r\n            padding: 10px 20px;\r\n            border-radius: 5px;\r\n            cursor: pointer;\r\n            font-size: 16px;\r\n            transition: background 0.3s;\r\n        }\r\n\r\n        button:hover {\r\n            background: #5568d3;\r\n        }\r\n\r\n        button:disabled {\r\n            background: #ccc;\r\n            cursor: not-allowed;\r\n        }\r\n\r\n        #status {\r\n\t\t\tmargin:20px;\r\n            background: rgba(255, 255, 255, 0.95);\r\n            padding: 15px 20px;\r\n            border-radius: 10px;\r\n            box-shadow: 0 4px 6px rgba(0,0,0,0.3);\r\n            margin-bottom: 20px;\r\n            font-size: 18px;\r\n            color: #667eea;\r\n            font-weight: bold;\r\n            min-width: 200px;\r\n        }\r\n\r\n        #scoreboard {\r\n            background: rgba(255, 255, 255, 0.95);\r\n            padding: 20px;\r\n            border-radius: 10px;\r\n            box-shadow: 0 4px 6px rgba(0,0,0,0.3);\r\n            max-height: 600px;\r\n            overflow-y: auto;\r\n            min-width: 200px;\r\n        }\r\n\r\n        #scoreboard h2 {\r\n            margin-bottom: 15px;\r\n            color: #667eea;\r\n        }\r\n\r\n        .score-item {\r\n            display: flex;\r\n            justify-content: space-between;\r\n            padding: 8px;\r\n            margin: 5px 0;\r\n            border-radius: 5px;\r\n            background: #f5f5f5;\r\n        }\r\n\r\n        .score-item.top {\r\n            background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%);\r\n            font-weight: bold;\r\n        }\r\n\r\n        .color-indicator {\r\n            width: 20px;\r\n            height: 20px;\r\n            border-radius: 50%;\r\n            display: inline-block;\r\n            margin-right: 8px;\r\n            vertical-align: middle;\r\n        }\r\n    <\/style>\r\n<h1>Tournoi Round-Robin &#8211; D\u00e9mo 3D<\/h1>\r\n<div id=\"containerGames\"><\/div>\r\n<div id=\"ui\">\r\n\t<div id=\"controls\">\r\n\t\t<button id=\"startBtn\" onclick=\"startTournament()\">D\u00e9marrer<\/button>\r\n\t\t<button id=\"resetBtn\" onclick=\"resetTournament()\">R\u00e9initialiser<\/button>\r\n\t<\/div>\r\n\t<div id=\"scoreboard\">\r\n\t\t<h2> Classement<\/h2>\r\n\t\t<div id=\"scores\"><\/div>\r\n\t<\/div>\r\n<\/div>\r\n<div id=\"status\">Pr\u00eat \u00e0 commencer<br><small>Clic gauche: rotation | Molette: zoom | Clic droit: d\u00e9placement<\/small><\/div>\r\n\r\n    <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/three.js\/r128\/three.min.js\"><\/script>\r\n    <script>\r\n        let scene, camera, renderer, controls;\r\n        let sphere, characters = [];\r\n        let lines = [];\r\n        let autoRotate = false;\r\n        const  innerWidth=800,innerHeight=700;\r\n\r\n        const numCharacters = 12;\r\n        const colors = [\r\n            0xFF6B6B, 0x4ECDC4, 0x45B7D1, 0xFFA07A, \r\n            0x98D8C8, 0xF7DC6F, 0xBB8FCE, 0x85C1E2,\r\n            0xFF8C94, 0x6BCF7F, 0xF39C12, 0xE74C3C\r\n        ];\r\n        \/\/ Images d'avatars pour les personnages\r\n        const avatarEmojis = ['\ud83d\ude00', '\ud83d\ude0e', '\ud83e\udd16', '\ud83d\udc7d', '\ud83d\udc31', '\ud83d\udc36', '\ud83e\udd8a', '\ud83d\udc3c', '\ud83e\udd81', '\ud83d\udc2f', '\ud83e\udd84', '\ud83d\udc09'];\r\n        let matchups = [];\r\n        let currentMatch = 0;\r\n        let isRunning = false;\r\n\r\n        class Character {\r\n            constructor(id, color, position) {\r\n                this.id = id;\r\n                this.color = color;\r\n                this.wins = 0;\r\n                this.homePosition = position.clone();\r\n                this.mesh = null;\r\n                this.emoji = avatarEmojis[id];\r\n                this.createMesh();\r\n            }\r\n\r\n            createMesh() {\r\n                \/\/ Cr\u00e9er une sph\u00e8re avec l'image\r\n                const geometry = new THREE.SphereGeometry(0.4, 32, 32);\r\n                const material = new THREE.MeshPhongMaterial({ \r\n                    color: this.color,\r\n                    emissive: this.color,\r\n                    emissiveIntensity: 0.3,\r\n                    shininess: 100\r\n                });\r\n                this.mesh = new THREE.Mesh(geometry, material);\r\n                this.mesh.position.copy(this.homePosition);\r\n                \r\n                \/\/ Cr\u00e9er une texture avec l'emoji\r\n                const canvas = document.createElement('canvas');\r\n                canvas.width = 256;\r\n                canvas.height = 256;\r\n                const ctx = canvas.getContext('2d');\r\n                \r\n                \/\/ Fond circulaire avec couleur du personnage\r\n                \/*ctx.fillStyle = '#' + this.color.toString(16).padStart(6, '0');\r\n                ctx.beginPath();\r\n                ctx.arc(128, 128, 120, 0, Math.PI * 2);\r\n                ctx.fill();*\/\r\n                \r\n                \/\/ Bordure blanche\r\n                \/*ctx.strokeStyle = 'white';\r\n                ctx.lineWidth = 8;\r\n                ctx.beginPath();\r\n                ctx.arc(128, 128, 120, 0, Math.PI * 2);\r\n                ctx.stroke();\r\n                \r\n                \/\/ Emoji au centre\r\n                ctx.font = 'bold 120px Arial';\r\n                ctx.textAlign = 'center';\r\n                ctx.textBaseline = 'middle';\r\n                ctx.fillText(this.emoji, 128, 138);*\/\r\n                \r\n                \/\/ Num\u00e9ro en petit\r\n                ctx.font = 'bold 40px Arial';\r\n                ctx.fillStyle = 'white';\r\n                ctx.strokeStyle = 'black';\r\n                ctx.lineWidth = 3;\r\n                ctx.strokeText(this.id + 1, 128, 220);\r\n                ctx.fillText(this.id + 1, 128, 220);\r\n                \r\n                const texture = new THREE.CanvasTexture(canvas);\r\n                const spriteMaterial = new THREE.SpriteMaterial({ \r\n                    map: texture,\r\n                    transparent: true\r\n                });\r\n                const sprite = new THREE.Sprite(spriteMaterial);\r\n                sprite.scale.set(1, 1, 1);\r\n                \/\/this.mesh.add(sprite);\r\n            }\r\n\r\n            moveTo(position, duration = 1000) {\r\n                return new Promise(resolve => {\r\n                    const start = this.mesh.position.clone();\r\n                    const startTime = Date.now();\r\n                    \r\n                    const animate = () => {\r\n                        const elapsed = Date.now() - startTime;\r\n                        const progress = Math.min(elapsed \/ duration, 1);\r\n                        const eased = progress < 0.5 \r\n                            ? 2 * progress * progress \r\n                            : 1 - Math.pow(-2 * progress + 2, 2) \/ 2;\r\n                        \r\n                        this.mesh.position.lerpVectors(start, position, eased);\r\n                        \r\n                        if (progress < 1) {\r\n                            requestAnimationFrame(animate);\r\n                        } else {\r\n                            resolve();\r\n                        }\r\n                    };\r\n                    animate();\r\n                });\r\n            }\r\n\r\n            scale(factor, duration = 500) {\r\n                return new Promise(resolve => {\r\n                    const startScale = this.mesh.scale.x;\r\n                    const startTime = Date.now();\r\n                    \r\n                    const animate = () => {\r\n                        const elapsed = Date.now() - startTime;\r\n                        const progress = Math.min(elapsed \/ duration, 1);\r\n                        const scale = startScale + (factor - startScale) * progress;\r\n                        \r\n                        this.mesh.scale.set(scale, scale, scale);\r\n                        \r\n                        if (progress < 1) {\r\n                            requestAnimationFrame(animate);\r\n                        } else {\r\n                            resolve();\r\n                        }\r\n                    };\r\n                    animate();\r\n                });\r\n            }\r\n\r\n            getBaseScale() {\r\n                \/\/ Taille de base augmente avec le nombre de victoires\r\n                \/\/ 1.0 pour 0 victoires, jusqu'\u00e0 2.0 pour 11 victoires\r\n                return 1.0 + (this.wins * 0.1);\r\n            }\r\n\r\n            updateSize() {\r\n                const baseScale = this.getBaseScale();\r\n                this.mesh.scale.set(baseScale, baseScale, baseScale);\r\n            }\r\n\r\n            glow(intensity = 2) {\r\n                this.mesh.material.emissiveIntensity = intensity;\r\n            }\r\n        }\r\n\r\n        function init() {\r\n            \/\/ Scene\r\n            scene = new THREE.Scene();\r\n            scene.background =  new THREE.Color(0x444488)  ;\/\/ new THREE.Color(0x1a1a2e);\r\n            \/\/scene.fog = new THREE.Fog(0x1a1a2e, 10, 50);\r\n\r\n            \/\/ Camera\r\n            camera = new THREE.PerspectiveCamera(\r\n                75,\r\n                innerWidth \/ innerHeight,\r\n                0.1,\r\n                1000\r\n            );\r\n            camera.position.z = 8;\r\n\r\n            \/\/ Renderer\r\n            renderer = new THREE.WebGLRenderer({ antialias: true });\r\n            renderer.setSize(innerWidth, innerHeight);\r\n            document.getElementById('containerGames').appendChild(renderer.domElement);\r\n\r\n            \/\/ Contr\u00f4les de cam\u00e9ra personnalis\u00e9s\r\n            initControls();\r\n\r\n            \/\/ Lights\r\n            const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);\r\n            scene.add(ambientLight);\r\n\r\n            const pointLight = new THREE.PointLight(0xffffff, 1);\r\n            pointLight.position.set(5, 5, 5);\r\n            scene.add(pointLight);\r\n\r\n            const pointLight2 = new THREE.PointLight(0x667eea, 0.5);\r\n            pointLight2.position.set(-5, -5, 5);\r\n            scene.add(pointLight2);\r\n\r\n            \/\/ Sphere transparente\r\n            const sphereGeometry = new THREE.SphereGeometry(3, 32, 32);\r\n            const sphereMaterial = new THREE.MeshPhongMaterial({\r\n                color: 0x667eea,\r\n                transparent: true,\r\n                opacity: 0.4,\r\n                wireframe: true\r\n            });\r\n            sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);\r\n            scene.add(sphere);\r\n\r\n            \/\/ Cr\u00e9er les personnages sur la sph\u00e8re\r\n            createCharacters();\r\n            generateMatchups();\r\n            updateScoreboard();\r\n\r\n            \/\/ Animation loop\r\n            animate();\r\n\r\n            \/\/ Resize handler\r\n            window.addEventListener('resize', onWindowResize);\r\n        }\r\n\r\n        \/\/ Syst\u00e8me de contr\u00f4les personnalis\u00e9 pour la cam\u00e9ra\r\n        function initControls() {\r\n            controls = {\r\n                isDragging: false,\r\n                isPanning: false,\r\n                previousMousePosition: { x: 0, y: 0 },\r\n                rotation: { x: 0, y: 0 },\r\n                distance: 8,\r\n                target: new THREE.Vector3(0, 0, 0),\r\n                panOffset: new THREE.Vector3(0, 0, 0)\r\n            };\r\n\r\n            renderer.domElement.addEventListener('mousedown', onMouseDown);\r\n            renderer.domElement.addEventListener('mousemove', onMouseMove);\r\n            renderer.domElement.addEventListener('mouseup', onMouseUp);\r\n            renderer.domElement.addEventListener('wheel', onMouseWheel);\r\n            renderer.domElement.addEventListener('contextmenu', (e) => e.preventDefault());\r\n\r\n            \/\/ Touch events pour mobile\r\n            renderer.domElement.addEventListener('touchstart', onTouchStart);\r\n            renderer.domElement.addEventListener('touchmove', onTouchMove);\r\n            renderer.domElement.addEventListener('touchend', onTouchEnd);\r\n        }\r\n\r\n        function onMouseDown(e) {\r\n            if (e.button === 0) { \/\/ Clic gauche - rotation\r\n                controls.isDragging = true;\r\n                controls.isPanning = false;\r\n            } else if (e.button === 2) { \/\/ Clic droit - pan\r\n                controls.isPanning = true;\r\n                controls.isDragging = false;\r\n            }\r\n            controls.previousMousePosition = { x: e.clientX, y: e.clientY };\r\n        }\r\n\r\n        function onMouseMove(e) {\r\n            if (!controls.isDragging && !controls.isPanning) return;\r\n\r\n            const deltaX = e.clientX - controls.previousMousePosition.x;\r\n            const deltaY = e.clientY - controls.previousMousePosition.y;\r\n\r\n            if (controls.isDragging) {\r\n                \/\/ Rotation\r\n                controls.rotation.y += deltaX * 0.005;\r\n                controls.rotation.x += deltaY * 0.005;\r\n                controls.rotation.x = Math.max(-Math.PI \/ 2, Math.min(Math.PI \/ 2, controls.rotation.x));\r\n            } else if (controls.isPanning) {\r\n                \/\/ Pan\r\n                const factor = controls.distance * 0.001;\r\n                controls.panOffset.x -= deltaX * factor;\r\n                controls.panOffset.y += deltaY * factor;\r\n            }\r\n\r\n            controls.previousMousePosition = { x: e.clientX, y: e.clientY };\r\n            updateCamera();\r\n        }\r\n\r\n        function onMouseUp() {\r\n            controls.isDragging = false;\r\n            controls.isPanning = false;\r\n        }\r\n\r\n        function onMouseWheel(e) {\r\n            e.preventDefault();\r\n            controls.distance += e.deltaY * 0.01;\r\n            controls.distance = Math.max(3, Math.min(20, controls.distance));\r\n            updateCamera();\r\n        }\r\n\r\n        \/\/ Touch support\r\n        let touchStartDistance = 0;\r\n        function onTouchStart(e) {\r\n            if (e.touches.length === 1) {\r\n                controls.isDragging = true;\r\n                controls.previousMousePosition = { x: e.touches[0].clientX, y: e.touches[0].clientY };\r\n            } else if (e.touches.length === 2) {\r\n                controls.isPanning = true;\r\n                const dx = e.touches[0].clientX - e.touches[1].clientX;\r\n                const dy = e.touches[0].clientY - e.touches[1].clientY;\r\n                touchStartDistance = Math.sqrt(dx * dx + dy * dy);\r\n            }\r\n        }\r\n\r\n        function onTouchMove(e) {\r\n            e.preventDefault();\r\n            if (e.touches.length === 1 && controls.isDragging) {\r\n                const deltaX = e.touches[0].clientX - controls.previousMousePosition.x;\r\n                const deltaY = e.touches[0].clientY - controls.previousMousePosition.y;\r\n                controls.rotation.y += deltaX * 0.005;\r\n                controls.rotation.x += deltaY * 0.005;\r\n                controls.rotation.x = Math.max(-Math.PI \/ 2, Math.min(Math.PI \/ 2, controls.rotation.x));\r\n                controls.previousMousePosition = { x: e.touches[0].clientX, y: e.touches[0].clientY };\r\n                updateCamera();\r\n            } else if (e.touches.length === 2) {\r\n                const dx = e.touches[0].clientX - e.touches[1].clientX;\r\n                const dy = e.touches[0].clientY - e.touches[1].clientY;\r\n                const distance = Math.sqrt(dx * dx + dy * dy);\r\n                const delta = touchStartDistance - distance;\r\n                controls.distance += delta * 0.01;\r\n                controls.distance = Math.max(3, Math.min(20, controls.distance));\r\n                touchStartDistance = distance;\r\n                updateCamera();\r\n            }\r\n        }\r\n\r\n        function onTouchEnd() {\r\n            controls.isDragging = false;\r\n            controls.isPanning = false;\r\n        }\r\n\r\n        function updateCamera() {\r\n            const x = controls.distance * Math.cos(controls.rotation.x) * Math.sin(controls.rotation.y);\r\n            const y = controls.distance * Math.sin(controls.rotation.x);\r\n            const z = controls.distance * Math.cos(controls.rotation.x) * Math.cos(controls.rotation.y);\r\n            \r\n            camera.position.set(x, y, z);\r\n            camera.position.add(controls.panOffset);\r\n            \r\n            const lookAtTarget = new THREE.Vector3().copy(controls.target).add(controls.panOffset);\r\n            camera.lookAt(lookAtTarget);\r\n        }\r\n\r\n        function toggleAutoRotate() {\r\n            autoRotate = !autoRotate;\r\n            document.getElementById('autoRotateBtn').textContent = \r\n                `\ud83d\udd04 Auto-rotation: ${autoRotate ? 'ON' : 'OFF'}`;\r\n        }\r\n\r\n        function createCharacters() {\r\n            const radius = 3;\r\n            const phi = Math.PI * (3 - Math.sqrt(5)); \/\/ Golden angle\r\n\r\n            for (let i = 0; i < numCharacters; i++) {\r\n                const y = 1 - (i \/ (numCharacters - 1)) * 2;\r\n                const radiusAtY = Math.sqrt(1 - y * y);\r\n                const theta = phi * i;\r\n                \r\n                const x = Math.cos(theta) * radiusAtY;\r\n                const z = Math.sin(theta) * radiusAtY;\r\n                \r\n                const position = new THREE.Vector3(x, y, z).multiplyScalar(radius);\r\n                const char = new Character(i, colors[i%12], position);\r\n                characters.push(char);\r\n                scene.add(char.mesh);\r\n            }\r\n        }\r\n\r\n        function generateMatchups() {\r\n            matchups = [];\r\n            for (let i = 0; i < numCharacters; i++) {\r\n                for (let j = i + 1; j < numCharacters; j++) {\r\n                    matchups.push([i, j]);\r\n                }\r\n            }\r\n        }\r\n\r\n        function createLine(char1, char2, color) {\r\n            const material = new THREE.LineBasicMaterial({ \r\n                color: color,\r\n                transparent: true,\r\n                opacity: 0.6,\r\n                linewidth: 2\r\n            });\r\n            const geometry = new THREE.BufferGeometry().setFromPoints([\r\n                char1.mesh.position.clone(),\r\n                char2.mesh.position.clone()\r\n            ]);\r\n            const line = new THREE.Line(geometry, material);\r\n            scene.add(line);\r\n            lines.push(line);\r\n            return line;\r\n        }\r\n\r\n        async function startTournament() {\r\n            if (isRunning) return;\r\n            \r\n            isRunning = true;\r\n            document.getElementById('startBtn').disabled = true;\r\n            currentMatch = 0;\r\n\r\n            for (const [id1, id2] of matchups) {\r\n                currentMatch++;\r\n                const char1 = characters[id1];\r\n                const char2 = characters[id2];\r\n\r\n                document.getElementById('status').textContent = \r\n                    `Duel ${currentMatch}\/${matchups.length}: ${char1.emoji} vs ${char2.emoji}`;\r\n\r\n                \/\/ Cr\u00e9er une ligne entre les deux personnages\r\n                const tempLine = createLine(char1, char2, 0xff6b6b);\r\n\r\n                await sleep(300);\r\n\r\n                \/\/ D\u00e9placer vers le centre\r\n                const center = new THREE.Vector3(0, 0, 0);\r\n                const offset1 = new THREE.Vector3(-0.5, 0, 0);\r\n                const offset2 = new THREE.Vector3(0.5, 0, 0);\r\n                \r\n                const char1BaseScale = char1.getBaseScale();\r\n                const char2BaseScale = char2.getBaseScale();\r\n                \r\n                await Promise.all([\r\n                    char1.moveTo(center.clone().add(offset1), 800),\r\n                    char2.moveTo(center.clone().add(offset2), 800),\r\n                    char1.scale(char1BaseScale * 1.5, 800),\r\n                    char2.scale(char2BaseScale * 1.5, 800)\r\n                ]);\r\n\r\n                await sleep(500);\r\n\r\n                \/\/ D\u00e9terminer le gagnant\r\n                const winner = Math.random() < 0.5 ? char1 : char2;\r\n                winner.wins++;\r\n                \r\n                const newWinnerScale = winner.getBaseScale();\r\n                \r\n                \/\/ Animation de victoire\r\n                winner.glow(3);\r\n                await winner.scale(newWinnerScale * 2, 300);\r\n                await winner.scale(newWinnerScale * 1.5, 300);\r\n\r\n                await sleep(500);\r\n\r\n                \/\/ Retour \u00e0 la position initiale avec la nouvelle taille\r\n                winner.glow(0.3);\r\n                const loser = winner === char1 ? char2 : char1;\r\n                await Promise.all([\r\n                    char1.moveTo(char1.homePosition, 800),\r\n                    char2.moveTo(char2.homePosition, 800),\r\n                    char1.scale(char1.getBaseScale(), 800),\r\n                    char2.scale(char2.getBaseScale(), 800)\r\n                ]);\r\n\r\n                \/\/ Mettre \u00e0 jour la ligne avec la couleur du gagnant\r\n                scene.remove(tempLine);\r\n                createLine(char1, char2, winner.color);\r\n\r\n                updateScoreboard();\r\n                await sleep(200);\r\n            }\r\n\r\n            document.getElementById('status').textContent = '\ud83c\udf89 Tournoi termin\u00e9 !';\r\n            highlightWinner();\r\n            isRunning = false;\r\n            document.getElementById('startBtn').disabled = false;\r\n        }\r\n\r\n        function highlightWinner() {\r\n            const maxWins = Math.max(...characters.map(c => c.wins));\r\n            const winner = characters.find(c => c.wins === maxWins);\r\n            if (winner) {\r\n                winner.glow(3);\r\n                const finalScale = winner.getBaseScale() * 1.3;\r\n                winner.scale(finalScale, 1000);\r\n            }\r\n        }\r\n\r\n        function updateScoreboard() {\r\n            const sorted = [...characters].sort((a, b) => b.wins - a.wins);\r\n            const scoresDiv = document.getElementById('scores');\r\n            scoresDiv.innerHTML = '';\r\n\r\n            sorted.forEach((char, index) => {\r\n                const div = document.createElement('div');\r\n                div.className = index === 0 && char.wins > 0 ? 'score-item top' : 'score-item';\r\n                const colorHex = '#' + char.color.toString(16).padStart(6, '0');\r\n                div.innerHTML = `\r\n                    <span>\r\n                        <span class=\"color-indicator\" style=\"background-color: ${colorHex}\"><\/span>\r\n                        ${char.emoji} Candidat ${char.id + 1}\r\n                    <\/span>\r\n                    <span>${char.wins} victoire${char.wins > 1 ? 's' : ''}<\/span>\r\n                `;\r\n                scoresDiv.appendChild(div);\r\n            });\r\n        }\r\n\r\n        function resetTournament() {\r\n            \/\/ Supprimer toutes les lignes\r\n            lines.forEach(line => scene.remove(line));\r\n            lines = [];\r\n\r\n            \/\/ R\u00e9initialiser les personnages\r\n            characters.forEach(char => {\r\n                char.wins = 0;\r\n                char.mesh.position.copy(char.homePosition);\r\n                char.mesh.scale.set(1, 1, 1);\r\n                char.glow(0.3);\r\n            });\r\n\r\n            currentMatch = 0;\r\n            updateScoreboard();\r\n            document.getElementById('status').textContent = 'Pr\u00eat \u00e0 commencer';\r\n            document.getElementById('startBtn').disabled = false;\r\n        }\r\n\r\n        function animate() {\r\n            requestAnimationFrame(animate);\r\n            \r\n            \/\/ Rotation de la sph\u00e8re\r\n            \/\/sphere.rotation.y += 0.002;\r\n            \r\n            \/\/ Auto-rotation optionnelle de la cam\u00e9ra\r\n            if (autoRotate && !controls.isDragging && !controls.isPanning) {\r\n                controls.rotation.y += 0.002;\r\n                updateCamera();\r\n            }\r\n            \r\n            renderer.render(scene, camera);\r\n        }\r\n\r\n        function onWindowResize() {\r\n            camera.aspect = innerWidth \/ innerHeight;\r\n            camera.updateProjectionMatrix();\r\n            renderer.setSize(innerWidth, innerHeight);\r\n        }\r\n\r\n        function sleep(ms) {\r\n            return new Promise(resolve => setTimeout(resolve, ms));\r\n        }\r\n\r\n        init();\r\n    <\/script>\r\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-10928","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/spgoo.org\/index.php?rest_route=\/wp\/v2\/pages\/10928","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/spgoo.org\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/spgoo.org\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/spgoo.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/spgoo.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10928"}],"version-history":[{"count":1,"href":"https:\/\/spgoo.org\/index.php?rest_route=\/wp\/v2\/pages\/10928\/revisions"}],"predecessor-version":[{"id":10929,"href":"https:\/\/spgoo.org\/index.php?rest_route=\/wp\/v2\/pages\/10928\/revisions\/10929"}],"wp:attachment":[{"href":"https:\/\/spgoo.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10928"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}