<!DOCTYPE html><html><head><title>P2P Notation Battle</title><linkrel="stylesheet"href="https://unpkg.com/@chrisoakman/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.css"><script src="https://unpkg.com/peerjs@1.5.2/dist/peerjs.min.js"></script><style>body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;display:flex;flex-direction:column;align-items:center;background-color:#f4f4f4;padding:10px;}/* Connection Panel */#connection-panel{background:white;padding:15px;border-radius:8px;box-shadow:02px5pxrgba(0,0,0,0.1);margin-bottom:20px;width:90%;max-width:500px;text-align:center;}#myId{font-weight:bold;color:#2196F3;font-family:monospace;font-size:1.2em;}/* Responsive Board */#myBoard{width:100%;max-width:450px;margin:0auto20pxauto;}/* Input Controls */.input-container{display:flex;gap:10px;width:90%;max-width:500px;margin-bottom:10px;}#moveInput{flex-grow:1;padding:12px;font-size:18px;border:2pxsolid#ccc;border-radius:4px;}button{padding:10px15px;cursor:pointer;border:none;border-radius:4px;font-size:14px;color:white;transition:background0.2s;}#submitBtn{background-color:#4CAF50;}#submitBtn:hover{background-color:#45a049;}#connectBtn{background-color:#2196F3;}.nav-btn{background-color:#555;flex:1;}.nav-btn:hover{background-color:#333;}/* Navigation Row */.navigation-controls{display:flex;gap:10px;width:90%;max-width:500px;margin-top:10px;}#status{font-weight:bold;margin-bottom:10px;color:#333;}#pgn-output{background:#fff;padding:15px;border:1pxsolid#ddd;width:90%;max-width:500px;min-height:40px;font-family:monospace;margin-top:10px;font-size:0.9em;}</style></head><body><divid="connection-panel"><div>My ID: <spanid="myId">Generating...</span></div><divstyle="margin-top: 10px; display: flex; gap: 5px; justify-content: center;"><inputtype="text"id="connId"placeholder="Enter Friend's ID"style="padding: 5px;"><buttonid="connectBtn">Connect</button></div><divid="connStatus"style="margin-top: 5px; font-size: 0.9em; color: #666;">Status: Waiting for peer...</div></div><divid="status">White to move</div><divid="myBoard"></div><divclass="input-container"><inputtype="text"id="moveInput"placeholder="Type move (e.g. e4)..."autofocus><buttonid="submitBtn">Move</button></div><divclass="navigation-controls"><buttonid="prevBtn"class="nav-btn">⬅️ Back</button><buttonid="resetBtn"class="nav-btn">↩️ Reset</button><buttonid="nextBtn"class="nav-btn">Forward ➡️</button></div><divid="pgn-output"></div><script src="https://code.jquery.com/jquery-3.5.1.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.3/chess.min.js"></script><script src="https://unpkg.com/@chrisoakman/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.js"></script><script>// --- 1. GAME SETUP ---varboard=null;vargame=newChess();varredoStack=[];// PeerJS Variablesvarpeer=newPeer();varconn=null;varmyColor='w';// Default to White (Host)varconfig={position:'start',draggable:false,// Input Trainer ModepieceTheme:'https://chessboardjs.com/img/chesspieces/wikipedia/{piece}.png'};board=Chessboard('myBoard',config);window.addEventListener('resize',board.resize);// --- 2. PEERJS LOGIC ---peer.on('open',function(id){document.getElementById('myId').innerText=id;});// Handle Incoming Connection (I am Host)peer.on('connection',function(c){conn=c;myColor='w';// I remain Whitedocument.getElementById('connStatus').innerText="Connected! You are WHITE.";setupConnectionHandlers();});// Connect to Friend (I am Joiner)document.getElementById('connectBtn').addEventListener('click',function(){vardestId=document.getElementById('connId').value;if(!destId)returnalert("Enter an ID first");conn=peer.connect(destId);conn.on('open',function(){myColor='b';// I become Blackdocument.getElementById('connStatus').innerText="Connected! You are BLACK.";board.orientation('black');// Flip board for Black playersetupConnectionHandlers();});});functionsetupConnectionHandlers(){// Listen for data from opponentconn.on('data',function(data){console.log("Received:",data);if(data.type==='move'){// Opponent movedgame.move(data.san);board.position(game.fen());redoStack=[];// Clear redo on new moveupdateStatus();speak(data.san);}elseif(data.type==='reset'){// Opponent reset the gameresetLocalGame();alert("Opponent reset the game.");}});}// --- 3. GAMEPLAY LOGIC ---functionhandleMove(){constmoveText=document.getElementById('moveInput').value.trim();if(!moveText)return;// RULE: Can I move?// 1. If connected, is it my turn?if(conn&&game.turn()!==myColor){speak("Not your turn");flashError("It is "+(game.turn()==='w'?"White":"Black")+"'s turn!");return;}// 2. Attempt Moveconstmove=game.move(moveText);if(move===null){speak("Invalid move");flashError();return;}// 3. Successboard.position(game.fen());redoStack=[];updateStatus();speak(move.san);document.getElementById('moveInput').value='';// 4. Send to Peer (if connected)if(conn&&conn.open){conn.send({type:'move',san:move.san});}}// --- 4. NAVIGATION & UTILS ---functionresetGame(){// Reset locallyresetLocalGame();// Notify peerif(conn&&conn.open){conn.send({type:'reset'});}}functionresetLocalGame(){game.reset();board.start();redoStack=[];updateStatus();document.getElementById('moveInput').value='';}functionundoMove(){// Disable Undo in multiplayer for simplicity (prevents desync)if(conn)returnalert("Undo disabled in multiplayer mode.");constmove=game.undo();if(move){redoStack.push(move);board.position(game.fen());updateStatus();}}functionredoMove(){if(conn)returnalert("Redo disabled in multiplayer mode.");constmove=redoStack.pop();if(move){game.move(move);board.position(game.fen());updateStatus();}}functionupdateStatus(){letstatus='';letmoveColor=(game.turn()==='b')?'Black':'White';if(game.in_checkmate()){status='Game over, '+moveColor+' is in checkmate.';}elseif(game.in_draw()){status='Game over, drawn position';}else{status=moveColor+' to move';if(game.in_check())status+=', '+moveColor+' is in check';}document.getElementById('status').innerText=status;document.getElementById('pgn-output').innerHTML=game.pgn();}functionspeak(text){if('speechSynthesis'inwindow){letut=newSpeechSynthesisUtterance(text);window.speechSynthesis.speak(ut);}}functionflashError(msg){constinput=document.getElementById('moveInput');input.style.borderColor="red";if(msg)document.getElementById('status').innerText=msg;setTimeout(()=>{input.style.borderColor="#ccc";updateStatus();},1000);}// Listenersdocument.getElementById('submitBtn').addEventListener('click',handleMove);document.getElementById('moveInput').addEventListener('keydown',(e)=>{if(e.key==='Enter')handleMove();});document.getElementById('resetBtn').addEventListener('click',resetGame);document.getElementById('prevBtn').addEventListener('click',undoMove);document.getElementById('nextBtn').addEventListener('click',redoMove);updateStatus();</script></body></html>