|
3 | 3 | <head> |
4 | 4 | <meta charset="utf-8"> |
5 | 5 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
6 | | - <title>Cyber Chess v10: Neural Link</title> |
| 6 | + <title>Cyber Chess v9.0: Gold Master</title> |
7 | 7 | <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"> |
8 | | - <script src="https://unpkg.com/peerjs@1.5.4/dist/peerjs.min.js"></script> |
9 | 8 | <style> |
10 | | - :root { --neon-cyan: #00ffff; --neon-magenta: #ff00ff; --bg-dark: #0a0a0a; } |
11 | | - body { background: #050505; color: #eee; font-family: 'Courier New', monospace; } |
12 | | - #cyberChessSection { min-height: 100vh; padding: 20px 0; } |
13 | | - .chess-title { font-size: 2rem; color: var(--neon-cyan); text-shadow: 0 0 10px var(--neon-cyan); text-align: center; } |
14 | | - .chessboard { width: clamp(300px, 95vw, 500px); aspect-ratio: 1/1; display: grid; grid-template-columns: repeat(8, 1fr); border: 2px solid var(--neon-cyan); margin: auto; } |
| 9 | + :root { --neon-cyan: #00ffff; --neon-magenta: #ff00ff; --bg-dark: #0a0a0a; --alert-red: #ff3333; --glass: rgba(255, 255, 255, 0.05); } |
| 10 | + body { background: #111; color: #eee; font-family: 'Segoe UI', sans-serif; overflow-x: hidden; } |
| 11 | + #cyberChessSection { background: var(--bg-dark); min-height: 100vh; padding: 2rem 0; } |
| 12 | + .chess-title { font-size: 2.5rem; font-weight: 800; text-align: center; background: linear-gradient(45deg, var(--neon-cyan), var(--neon-magenta)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 1rem; text-transform: uppercase; letter-spacing: 2px; } |
| 13 | + .scoreboard { display: flex; justify-content: center; gap: 1rem; margin-bottom: 1rem; } |
| 14 | + .score-box { background: var(--glass); border: 1px solid #333; padding: 0.5rem 1rem; border-radius: 4px; text-align: center; transition: 0.3s; flex: 1; max-width: 150px; } |
| 15 | + .score-box.active { border-color: currentColor; box-shadow: 0 0 15px currentColor; background: rgba(255,255,255,0.1); } |
| 16 | + .score-white { color: #fff; } .score-black { color: var(--neon-magenta); } |
| 17 | + .status-display { text-align: center; font-size: 1rem; color: #888; min-height: 1.5em; margin-bottom: 10px; } |
| 18 | + .chessboard-container { display: flex; justify-content: center; margin: 1rem 0; } |
| 19 | + .chessboard { width: clamp(300px, 95vw, 520px); aspect-ratio: 1/1; display: grid; grid-template-columns: repeat(8, 1fr); border: 4px solid var(--neon-cyan); background: #000; box-shadow: 0 0 20px rgba(0, 255, 255, 0.2); } |
15 | 20 | .square { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; cursor: pointer; position: relative; } |
16 | | - .light { background: #1a1a1a; } .dark { background: #0a0a0a; } |
17 | | - .selected { background: rgba(0, 255, 255, 0.3) !important; } |
18 | | - .last-move { background: rgba(255, 255, 0, 0.1) !important; } |
19 | | - .piece { font-size: 2.5rem; user-select: none; } |
20 | | - .white { color: #fff; } .black { color: var(--neon-magenta); } |
21 | | - #connectionPanel { max-width: 500px; margin: 10px auto; border: 1px solid #333; padding: 15px; border-radius: 8px; } |
| 21 | + .square.light { background: #2a2a2a; } .square.dark { background: #1a1a1a; } |
| 22 | + .square.selected { background: rgba(0, 255, 255, 0.3) !important; } |
| 23 | + .square.last-move { background: rgba(255, 255, 0, 0.15) !important; border: 1px inset rgba(255, 255, 0, 0.3); } |
| 24 | + .square.valid-move::after { content: ''; position: absolute; width: 12px; height: 12px; background: rgba(0, 255, 0, 0.4); border-radius: 50%; } |
| 25 | + .piece { font-size: clamp(2rem, 8vw, 3rem); user-select: none; z-index: 2; cursor: grab; } |
| 26 | + .piece.white { color: #fff; text-shadow: 0 0 8px rgba(255,255,255,0.4); } |
| 27 | + .piece.black { color: var(--neon-magenta); text-shadow: 0 0 8px rgba(255,0,255,0.4); } |
| 28 | + .cyber-btn { background: #000; border: 1px solid var(--neon-cyan); color: var(--neon-cyan); padding: 0.4rem 1rem; text-transform: uppercase; cursor: pointer; transition: 0.3s; font-size: 0.8rem; } |
| 29 | + .cyber-btn:hover { background: var(--neon-cyan); color: #000; box-shadow: 0 0 10px var(--neon-cyan); } |
22 | 30 | </style> |
23 | 31 | </head> |
24 | 32 | <body> |
25 | | - <div id="cyberChessSection"> |
26 | | - <h2 class="chess-title">CYBER CHESS v10 // MULTIPLAYER</h2> |
27 | | - |
28 | | - <div id="connectionPanel"> |
29 | | - <div id="status" class="text-warning mb-2 small">System: Offline</div> |
30 | | - <div id="myIdDisplay" class="text-info mb-2" style="cursor:pointer" onclick="copyId()">ID: Loading...</div> |
31 | | - <div class="input-group input-group-sm"> |
32 | | - <input type="text" id="peerIdInput" class="form-control bg-dark text-white border-secondary" placeholder="Friend's ID"> |
33 | | - <button class="btn btn-outline-cyan text-info" onclick="network.connectToPeer()">LINK</button> |
| 33 | + <section id="cyberChessSection"> |
| 34 | + <div class="container text-center"> |
| 35 | + <h2 class="chess-title">Cyber Chess v9</h2> |
| 36 | + <div class="scoreboard"> |
| 37 | + <div class="score-box score-white" id="scoreWhite">White: 0</div> |
| 38 | + <div class="score-box score-black" id="scoreBlack">Black: 0</div> |
| 39 | + </div> |
| 40 | + <div class="status-display" id="statusDisplay">System Ready</div> |
| 41 | + <div class="mb-3"> |
| 42 | + <button class="cyber-btn" onclick="game.reset()">Reset</button> |
| 43 | + <button class="cyber-btn" onclick="game.undo()">Undo</button> |
| 44 | + <button class="cyber-btn" onclick="game.setMode('ai')" id="btnAI">AI Mode</button> |
| 45 | + </div> |
| 46 | + <div class="chessboard-container"> |
| 47 | + <div class="chessboard" id="chessboard"></div> |
34 | 48 | </div> |
35 | 49 | </div> |
36 | | - |
37 | | - <div class="chessboard" id="chessboard"></div> |
38 | | - </div> |
| 50 | + </section> |
39 | 51 |
|
40 | 52 | <script> |
| 53 | + const SoundSystem = { |
| 54 | + ctx: null, |
| 55 | + init() { if (!this.ctx) this.ctx = new (window.AudioContext || window.webkitAudioContext)(); if(this.ctx.state === 'suspended') this.ctx.resume(); }, |
| 56 | + play(freq, type='sine', dur=0.1) { |
| 57 | + this.init(); const osc = this.ctx.createOscillator(); const g = this.ctx.createGain(); |
| 58 | + osc.type = type; osc.frequency.setValueAtTime(freq, this.ctx.currentTime); |
| 59 | + g.gain.setValueAtTime(0.1, this.ctx.currentTime); g.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + dur); |
| 60 | + osc.connect(g); g.connect(this.ctx.destination); osc.start(); osc.stop(this.ctx.currentTime + dur); |
| 61 | + } |
| 62 | + }; |
| 63 | + |
41 | 64 | const game = { |
42 | | - board: [], turn: 'W', myRole: null, // 'W' or 'B' |
| 65 | + board: [], turn: 'W', history: [], mode: 'pvp', lastMove: null, scores: {W:0, B:0}, |
| 66 | + vals: {P:10, N:30, B:30, R:50, Q:90, K:900}, |
| 67 | + pst: { |
| 68 | + P: [[0,0,0,0,0,0,0,0],[5,5,5,5,5,5,5,5],[1,1,2,3,3,2,1,1],[0.5,0.5,1,2.5,2.5,1,0.5,0.5],[0,0,0,2,2,0,0,0],[0.5,-0.5,-1,0,0,-1,-0.5,0.5],[0.5,1,1,-2,-2,1,1,0.5],[0,0,0,0,0,0,0,0]], |
| 69 | + N: [[-5,-4,-3,-3,-3,-3,-4,-5],[-4,-2,0,0,0,0,-2,-4],[-3,0,1,1.5,1.5,1,0,-3],[-3,0.5,1.5,2,2,1.5,0.5,-3],[-3,0,1.5,2,2,1.5,0,-3],[-3,0.5,1,1.5,1.5,1,0.5,-3],[-4,-2,0,0.5,0.5,0,-2,-4],[-5,-4,-3,-3,-3,-3,-4,-5]] |
| 70 | + }, |
43 | 71 | syms: {W:{K:'♔',Q:'♕',R:'♖',B:'♗',N:'♘',P:'♙'}, B:{K:'♚',Q:'♛',R:'♜',B:'♝',N:'♞',P:'♟'}}, |
44 | 72 |
|
45 | 73 | init() { |
46 | 74 | this.board = [['r','n','b','q','k','b','n','r'],['p','p','p','p','p','p','p','p'],[null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null],['P','P','P','P','P','P','P','P'],['R','N','B','Q','K','B','N','R']]; |
47 | | - this.render(); |
| 75 | + this.turn = 'W'; this.history = []; this.lastMove = null; this.render(); this.updateUI(); |
| 76 | + }, |
| 77 | + |
| 78 | + evaluate() { |
| 79 | + let s = 0; |
| 80 | + for(let r=0; r<8; r++) for(let c=0; c<8; c++) { |
| 81 | + let p = this.board[r][c]; if(!p) continue; |
| 82 | + let v = this.vals[p.toUpperCase()] + (this.pst[p.toUpperCase()] ? this.pst[p.toUpperCase()][p === p.toUpperCase() ? r : 7-r][c] : 0); |
| 83 | + s += (p === p.toUpperCase() ? v : -v); |
| 84 | + } |
| 85 | + return s; |
| 86 | + }, |
| 87 | + |
| 88 | + minimax(depth, alpha, beta, isMax) { |
| 89 | + if(depth === 0) return -this.evaluate(); |
| 90 | + let moves = this.getAllMoves(isMax ? 'B' : 'W'); |
| 91 | + if(!moves.length) return isMax ? -1000 : 1000; |
| 92 | + let best = isMax ? -Infinity : Infinity; |
| 93 | + for(let m of moves) { |
| 94 | + let old = this.board[m.to.r][m.to.c]; |
| 95 | + this.board[m.to.r][m.to.c] = this.board[m.from.r][m.from.c]; |
| 96 | + this.board[m.from.r][m.from.c] = null; |
| 97 | + let score = this.minimax(depth-1, alpha, beta, !isMax); |
| 98 | + this.board[m.from.r][m.from.c] = this.board[m.to.r][m.to.c]; |
| 99 | + this.board[m.to.r][m.to.c] = old; |
| 100 | + if(isMax) { best = Math.max(best, score); alpha = Math.max(alpha, best); } |
| 101 | + else { best = Math.min(best, score); beta = Math.min(beta, best); } |
| 102 | + if(beta <= alpha) break; |
| 103 | + } |
| 104 | + return best; |
| 105 | + }, |
| 106 | + |
| 107 | + triggerAI() { |
| 108 | + setTimeout(() => { |
| 109 | + let moves = this.getAllMoves('B'); |
| 110 | + let bestM = null, bestS = -Infinity; |
| 111 | + moves.sort(() => Math.random() - 0.5); |
| 112 | + for(let m of moves) { |
| 113 | + let old = this.board[m.to.r][m.to.c]; |
| 114 | + this.board[m.to.r][m.to.c] = this.board[m.from.r][m.from.c]; |
| 115 | + this.board[m.from.r][m.from.c] = null; |
| 116 | + let s = this.minimax(2, -Infinity, Infinity, false); |
| 117 | + this.board[m.from.r][m.from.c] = this.board[m.to.r][m.to.c]; |
| 118 | + this.board[m.to.r][m.to.c] = old; |
| 119 | + if(s > bestS) { bestS = s; bestM = m; } |
| 120 | + } |
| 121 | + if(bestM) this.move(bestM.from, bestM.to); |
| 122 | + }, 300); |
| 123 | + }, |
| 124 | + |
| 125 | + getAllMoves(color) { |
| 126 | + let moves = []; |
| 127 | + for(let r=0; r<8; r++) for(let c=0; c<8; c++) { |
| 128 | + let p = this.board[r][c]; |
| 129 | + if(p && (color==='W' ? p===p.toUpperCase() : p===p.toLowerCase())) { |
| 130 | + for(let tr=0; tr<8; tr++) for(let tc=0; tc<8; tc++) { |
| 131 | + if(this.isLegal({r,c}, {r:tr, c:tc})) moves.push({from:{r,c}, to:{r:tr, c:tc}}); |
| 132 | + } |
| 133 | + } |
| 134 | + } |
| 135 | + return moves; |
| 136 | + }, |
| 137 | + |
| 138 | + isLegal(f, t) { |
| 139 | + let p = this.board[f.r][f.c]; if(!p) return false; |
| 140 | + let target = this.board[t.r][t.c]; |
| 141 | + if(target && (p===p.toUpperCase()) === (target===target.toUpperCase())) return false; |
| 142 | + let dx = t.c-f.c, dy = t.r-f.r, type = p.toUpperCase(); |
| 143 | + |
| 144 | + // Basic movement logic |
| 145 | + if(type==='P') { |
| 146 | + let dir = p==='P' ? -1 : 1; |
| 147 | + if(dx===0 && dy===dir && !target) {} |
| 148 | + else if(dx===0 && dy===2*dir && f.r===(p==='P'?6:1) && !target && !this.board[f.r+dir][f.c]) {} |
| 149 | + else if(Math.abs(dx)===1 && dy===dir && target) {} |
| 150 | + else return false; |
| 151 | + } else if(type==='R') { if(dx!==0 && dy!==0 || !this.pathClear(f,t)) return false; } |
| 152 | + else if(type==='B') { if(Math.abs(dx)!==Math.abs(dy) || !this.pathClear(f,t)) return false; } |
| 153 | + else if(type==='Q') { if((dx!==0 && dy!==0 && Math.abs(dx)!==Math.abs(dy)) || !this.pathClear(f,t)) return false; } |
| 154 | + else if(type==='N') { if(!(Math.abs(dx)===2 && Math.abs(dy)===1 || Math.abs(dx)===1 && Math.abs(dy)===2)) return false; } |
| 155 | + else if(type==='K') { if(Math.abs(dx)>1 || Math.abs(dy)>1) return false; } |
| 156 | + |
| 157 | + // King Safety |
| 158 | + let old = this.board[t.r][t.c]; |
| 159 | + this.board[t.r][t.c] = this.board[f.r][f.c]; this.board[f.r][f.c] = null; |
| 160 | + let safe = !this.inDanger(p===p.toUpperCase() ? 'W':'B'); |
| 161 | + this.board[f.r][f.c] = this.board[t.r][t.c]; this.board[t.r][t.c] = old; |
| 162 | + return safe; |
48 | 163 | }, |
49 | 164 |
|
50 | | - move(f, t, remote = false) { |
51 | | - // If it's a remote move, or it's my turn and I'm moving my color |
52 | | - this.board[t.r][t.c] = this.board[f.r][f.c]; |
53 | | - this.board[f.r][f.c] = null; |
54 | | - this.turn = this.turn === 'W' ? 'B' : 'W'; |
55 | | - this.render(); |
56 | | - if(!remote && network.conn) { |
57 | | - network.conn.send({type: 'MOVE', f, t}); |
| 165 | + pathClear(f, t) { |
| 166 | + let dx = Math.sign(t.c-f.c), dy = Math.sign(t.r-f.r); |
| 167 | + let c = f.c+dx, r = f.r+dy; |
| 168 | + while(c!==t.c || r!==t.r) { if(this.board[r][c]) return false; c+=dx; r+=dy; } |
| 169 | + return true; |
| 170 | + }, |
| 171 | + |
| 172 | + inDanger(color) { |
| 173 | + let kr, kc; |
| 174 | + for(let r=0; r<8; r++) for(let c=0; c<8; c++) if(this.board[r][c]===(color==='W'?'K':'k')) { kr=r; kc=c; } |
| 175 | + for(let r=0; r<8; r++) for(let c=0; c<8; c++) { |
| 176 | + let p = this.board[r][c]; |
| 177 | + if(p && (color==='W' ? p===p.toLowerCase() : p===p.toUpperCase())) { |
| 178 | + // Check if this piece can hit the king (Simplified geometry check) |
| 179 | + let dx = kc-c, dy = kr-r, type = p.toUpperCase(); |
| 180 | + let canHit = false; |
| 181 | + if(type==='P') canHit = (Math.abs(dx)===1 && dy===(p==='P'?-1:1)); |
| 182 | + else if(type==='R') canHit = (dx===0 || dy===0) && this.pathClear({r,c},{r:kr,c:kc}); |
| 183 | + else if(type==='B') canHit = (Math.abs(dx)===Math.abs(dy)) && this.pathClear({r,c},{r:kr,c:kc}); |
| 184 | + else if(type==='Q') canHit = (dx===0 || dy===0 || Math.abs(dx)===Math.abs(dy)) && this.pathClear({r,c},{r:kr,c:kc}); |
| 185 | + else if(type==='N') canHit = (Math.abs(dx)===2 && Math.abs(dy)===1 || Math.abs(dx)===1 && Math.abs(dy)===2); |
| 186 | + else if(type==='K') canHit = (Math.abs(dx)<=1 && Math.abs(dy)<=1); |
| 187 | + if(canHit) return true; |
| 188 | + } |
58 | 189 | } |
| 190 | + return false; |
| 191 | + }, |
| 192 | + |
| 193 | + move(f, t) { |
| 194 | + this.history.push(JSON.stringify(this.board)); |
| 195 | + let target = this.board[t.r][t.c]; |
| 196 | + if(target) { this.scores[this.turn] += this.vals[target.toUpperCase()]; SoundSystem.play(200, 'square'); } |
| 197 | + else SoundSystem.play(400); |
| 198 | + |
| 199 | + this.board[t.r][t.c] = this.board[f.r][f.c]; this.board[f.r][f.c] = null; |
| 200 | + // Promotion |
| 201 | + if(this.board[t.r][t.c].toUpperCase()==='P' && (t.r===0 || t.r===7)) this.board[t.r][t.c] = this.turn==='W'?'Q':'q'; |
| 202 | + |
| 203 | + this.lastMove = {f, t}; |
| 204 | + this.turn = this.turn==='W' ? 'B' : 'W'; |
| 205 | + this.render(); this.updateUI(); |
| 206 | + if(this.mode==='ai' && this.turn==='B') this.triggerAI(); |
59 | 207 | }, |
60 | 208 |
|
61 | 209 | render() { |
62 | 210 | const el = document.getElementById('chessboard'); el.innerHTML = ''; |
63 | 211 | for(let r=0; r<8; r++) for(let c=0; c<8; c++) { |
64 | 212 | const sq = document.createElement('div'); |
65 | 213 | sq.className = `square ${(r+c)%2?'dark':'light'}`; |
| 214 | + if(this.selected && this.selected.r===r && this.selected.c===c) sq.classList.add('selected'); |
| 215 | + if(this.lastMove && ((this.lastMove.f.r===r && this.lastMove.f.c===c) || (this.lastMove.t.r===r && this.lastMove.t.c===c))) sq.classList.add('last-move'); |
| 216 | + |
66 | 217 | let p = this.board[r][c]; |
67 | 218 | if(p) { |
68 | 219 | const pEl = document.createElement('div'); |
69 | 220 | pEl.className = `piece ${p===p.toUpperCase()?'white':'black'}`; |
70 | 221 | pEl.textContent = this.syms[p===p.toUpperCase()?'W':'B'][p.toUpperCase()]; |
71 | 222 | sq.appendChild(pEl); |
72 | 223 | } |
| 224 | + if(this.selected && this.isLegal(this.selected, {r,c})) sq.classList.add('valid-move'); |
73 | 225 | sq.onclick = () => { |
74 | | - if(this.myRole && this.turn !== this.myRole) return; // Not your turn |
75 | | - if(p && (this.turn === 'W' ? p===p.toUpperCase() : p===p.toLowerCase())) { |
76 | | - this.selected = {r,c}; this.render(); |
77 | | - } else if(this.selected) { |
78 | | - this.move(this.selected, {r,c}); |
79 | | - this.selected = null; |
80 | | - } |
| 226 | + SoundSystem.init(); |
| 227 | + if(p && (this.turn==='W' ? p===p.toUpperCase() : p===p.toLowerCase())) { this.selected = {r,c}; this.render(); } |
| 228 | + else if(this.selected) { if(this.isLegal(this.selected, {r,c})) this.move(this.selected, {r,c}); this.selected = null; this.render(); } |
81 | 229 | }; |
82 | 230 | el.appendChild(sq); |
83 | 231 | } |
84 | | - } |
85 | | - }; |
86 | | - |
87 | | - const network = { |
88 | | - peer: null, conn: null, |
89 | | - init() { |
90 | | - this.peer = new Peer(); |
91 | | - this.peer.on('open', (id) => { |
92 | | - document.getElementById('myIdDisplay').innerText = "MY ID (Click to Copy): " + id; |
93 | | - document.getElementById('status').innerText = "System: Waiting for peer..."; |
94 | | - }); |
95 | | - this.peer.on('connection', (c) => { |
96 | | - this.conn = c; |
97 | | - game.myRole = 'W'; // Host is White |
98 | | - this.setupHandlers(); |
99 | | - }); |
100 | 232 | }, |
101 | | - connectToPeer() { |
102 | | - const id = document.getElementById('peerIdInput').value; |
103 | | - this.conn = this.peer.connect(id); |
104 | | - game.myRole = 'B'; // Joiner is Black |
105 | | - this.setupHandlers(); |
| 233 | + |
| 234 | + updateUI() { |
| 235 | + document.getElementById('scoreWhite').textContent = `White: ${this.scores.W}`; |
| 236 | + document.getElementById('scoreBlack').textContent = `Black: ${this.scores.B}`; |
| 237 | + document.getElementById('statusDisplay').textContent = this.turn==='W' ? "White's Turn" : "Black's Turn (Analyzing...)"; |
106 | 238 | }, |
107 | | - setupHandlers() { |
108 | | - this.conn.on('open', () => { |
109 | | - document.getElementById('status').innerText = "System: NEURAL LINK ESTABLISHED"; |
110 | | - document.getElementById('status').className = "text-success mb-2 small"; |
111 | | - }); |
112 | | - this.conn.on('data', (data) => { |
113 | | - if(data.type === 'MOVE') game.move(data.f, data.t, true); |
114 | | - }); |
115 | | - } |
116 | | - }; |
117 | 239 |
|
118 | | - function copyId() { |
119 | | - const id = document.getElementById('myIdDisplay').innerText.split(': ')[1]; |
120 | | - navigator.clipboard.writeText(id); |
121 | | - alert("ID Copied! Send this to your friend."); |
122 | | - } |
| 240 | + setMode(m) { this.mode = m; this.init(); }, |
| 241 | + undo() { if(this.history.length) { this.board = JSON.parse(this.history.pop()); this.turn = this.turn==='W'?'B':'W'; this.render(); this.updateUI(); } }, |
| 242 | + reset() { this.init(); } |
| 243 | + }; |
123 | 244 |
|
124 | 245 | game.init(); |
125 | | - network.init(); |
126 | 246 | </script> |
127 | 247 | </body> |
128 | 248 | </html> |
0 commit comments