{"id":52794,"student_id":10,"content":"{\"html\":\"\u003c!DOCTYPE html\u003e\\n\u003chtml lang=\\\"en\\\"\u003e\\n\\n\u003chead\u003e\\n \u003cmeta charset=\\\"UTF-8\\\"\u003e\\n \u003cmeta name=\\\"description\\\" content=\\\"使用 JavaScript 實作西洋棋電腦 AI 瞭解背後運作的原理\\\"\u003e\\n \u003cmeta name=\\\"keywords\\\" content=\\\"Minimax, JavaScript, Chess, AI\\\"\u003e\\n \u003cmeta name=\\\"author\\\" content=\\\"JingShiang Yang\\\"\u003e\\n \u003cmeta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1.0\\\"\u003e\\n \u003cmeta http-equiv=\\\"X-UA-Compatible\\\" content=\\\"ie=edge\\\"\u003e\\n \u003ctitle\u003e 西洋棋 AI 是怎麼思考的?| Minimax Algorithm 演算法 | JavaScript\u003c/title\u003e\\n \u003clink rel=\\\"stylesheet\\\" href=\\\"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css\\\"\u003e\\n \u003cstyle\u003e\\n #board {\\n margin: auto;\\n height: 500px;\\n width: 500px;\\n display: grid;\\n grid-template-columns: repeat(8, 1fr);\\n border: 2px solid black;\\n transition: transform 1.5s;\\n }\\n\\n #board \u003e div {\\n border: 2px solid black;\\n position: relative;\\n }\\n\\n #board \u003e div \u003e img {\\n width: 85%;\\n height: 85%;\\n top: 7.5%;\\n left: 7.5%;\\n display: block;\\n position: absolute;\\n transition: transform 1.5s;\\n }\\n \u003c/style\u003e\\n \u003cscript src=\\\"https://cdn.jsdelivr.net/npm/vue/dist/vue.js\\\"\u003e\u003c/script\u003e\\n\u003c/head\u003e\\n\\n\u003cbody\u003e\\n \u003cdiv id=\\\"root\\\" style=\\\"width: 500px;\\\" class=\\\"text-center m-auto py-5\\\"\u003e\\n \u003ch1 class=\\\"py-3 text-center\\\"\u003eAlpha-beta Algorithm\u003c/h1\u003e\\n \u003cdiv id=\\\"board\\\" class=\\\"my-3\\\" v-bind:class=\\\"{reverse: black}\\\"\u003e\\n \u003ctemplate v-for=\\\"(row, x) in grid\\\"\u003e\\n \u003cdiv v-for=\\\"(col, y) in row\\\" v-bind:style=\\\"{backgroundColor: color(x, y)}\\\" @click=\\\"click(x, y)\\\"\u003e\\n \u003cimg src=\\\"b_pawn.svg\\\" v-if=\\\"col == 'b_pawn'\\\"\u003e\\n \u003cimg src=\\\"b_rook.svg\\\" v-if=\\\"col == 'b_rook'\\\"\u003e\\n \u003cimg src=\\\"b_knight.svg\\\" v-if=\\\"col == 'b_knight'\\\"\u003e\\n \u003cimg src=\\\"b_bishop.svg\\\" v-if=\\\"col == 'b_bishop'\\\"\u003e\\n \u003cimg src=\\\"b_king.svg\\\" v-if=\\\"col == 'b_king'\\\"\u003e\\n \u003cimg src=\\\"b_queen.svg\\\" v-if=\\\"col == 'b_queen'\\\"\u003e\\n\\n \u003cimg src=\\\"w_pawn.svg\\\" v-if=\\\"col == 'w_pawn'\\\"\u003e\\n \u003cimg src=\\\"w_rook.svg\\\" v-if=\\\"col == 'w_rook'\\\"\u003e\\n \u003cimg src=\\\"w_knight.svg\\\" v-if=\\\"col == 'w_knight'\\\"\u003e\\n \u003cimg src=\\\"w_bishop.svg\\\" v-if=\\\"col == 'w_bishop'\\\"\u003e\\n \u003cimg src=\\\"w_king.svg\\\" v-if=\\\"col == 'w_king'\\\"\u003e\\n \u003cimg src=\\\"w_queen.svg\\\" v-if=\\\"col == 'w_queen'\\\"\u003e\\n \u003c/div\u003e\\n \u003c/template\u003e\\n \u003c/div\u003e\\n \u003cp class=\\\"font-weight-bold\\\"\u003e{{message}}\u003c/p\u003e\\n \u003cselect class=\\\"mb-2 form-control\\\" v-model=\\\"depth\\\"\u003e\\n \u003coption value=\\\"1\\\"\u003e1\u003c/option\u003e\\n \u003coption value=\\\"2\\\"\u003e2\u003c/option\u003e\\n \u003coption value=\\\"3\\\"\u003e3\u003c/option\u003e\\n \u003coption value=\\\"4\\\"\u003e4\u003c/option\u003e\\n \u003coption value=\\\"5\\\"\u003e5\u003c/option\u003e\\n \u003coption value=\\\"6\\\"\u003e6\u003c/option\u003e\\n \u003coption value=\\\"7\\\"\u003e7\u003c/option\u003e\\n \u003c/select\u003e\\n \u003ctable class=\\\"table table-striped\\\"\u003e\\n \u003ctr\u003e\\n \u003cth\u003e#\u003c/th\u003e\\n \u003cth\u003ecost\u003c/th\u003e\\n \u003cth\u003edepth\u003c/th\u003e\\n \u003c/tr\u003e\\n \u003ctr v-for=\\\"(log, idx) in logs\\\"\u003e\\n \u003ctd\u003e{{idx + 1}}\u003c/td\u003e\\n \u003ctd\u003e{{log.cost/1000}}s\u003c/td\u003e\\n \u003ctd\u003e{{log.depth}}\u003c/td\u003e\\n \u003c/tr\u003e\\n \u003c/table\u003e\\n \u003c/div\u003e\\n\u003c/body\u003e\\n\\n\u003c/html\u003e\",\"css\":\"/* 設定整個頁面內容水平置中、內寬為 30 像素:*/\\nbody {\\n text-align: center;\\n padding: 30px;\\n}\\n\\n/* 設定圖片的寬度為銀幕一半寬 */\\nimg { \\n max-width: 50%; \\n}\\n\\n/* 設定文字的顏色為淡黑色 */\\np {\\n color: #555555;\\n}\",\"js\":\"// noprotect\\nfunction AI(turn, depth) {\\n let virtualChess = chess.clone();\\n let { from, to } = alphabeta(virtualChess, -Infinity, +Infinity, depth, turn);\\n chess.move(from.x, from.y, to.x, to.y);\\n}\\n\\nfunction alphabeta(chess, alpha, beta, depth, turn) {\\n\\n if (depth === 0) return { score: getScore(chess.grid) };\\n\\n let from = {}\\n let to = {}\\n\\n for (let x = 0; x \u003c 8; x++) {\\n for (let y = 0; y \u003c 8; y++) {\\n let arr = chess.getReachedBlock(x, y);\\n for (let i = 0; i \u003c arr.length; i++) {\\n let pos = arr[i];\\n chess.move(x, y, pos.x, pos.y);\\n let { score } = alphabeta(chess, alpha, beta, depth - 1, turn === 'b' ? 'w' : 'b');\\n\\n if (turn === 'b' \u0026\u0026 score \u003e alpha) {\\n alpha = score;\\n from = { x, y };\\n to = pos;\\n }\\n if (turn === 'w' \u0026\u0026 score \u003c beta) {\\n beta = score;\\n from = { x, y };\\n to = pos;\\n }\\n\\n chess.undo();\\n\\n if (alpha \u003e= beta) return { score: (turn === 'b' ? alpha : beta), from, to };\\n }\\n }\\n }\\n return { score: (turn === 'b' ? alpha : beta), from, to };\\n}\\n\\n\\nfunction getScore(grid) {\\n var score = 0;\\n for (let x = 0; x \u003c 8; x++) {\\n for (let y = 0; y \u003c 8; y++) {\\n if (grid[x][y] == 'b_pawn') score += 1;\\n if (grid[x][y] == 'b_rook') score += 10;\\n if (grid[x][y] == 'b_knight') score += 5;\\n if (grid[x][y] == 'b_bishop') score += 8;\\n if (grid[x][y] == 'b_queen') score += 25;\\n if (grid[x][y] == 'b_king') score += 1000;\\n\\n if (grid[x][y] == 'w_pawn') score -= 1;\\n if (grid[x][y] == 'w_rook') score -= 10;\\n if (grid[x][y] == 'w_knight') score -= 5;\\n if (grid[x][y] == 'w_bishop') score -= 8;\\n if (grid[x][y] == 'w_queen') score -= 25;\\n if (grid[x][y] == 'w_king') score -= 1000;\\n }\\n }\\n return score;\\n}\\n\\n\\n\\n\\n\\nclass Chess {\\n constructor() {\\n this.status = 'playing'; // 'playing', 'white win', 'black win'\\n this.turn = 'w'; // 'w', 'b'\\n this.logs = [];\\n this.grid = {\\n 0: { 0: 'b_rook', 1: 'b_knight', 2: 'b_bishop', 3: 'b_king', 4: 'b_queen', 5: 'b_bishop', 6: 'b_knight', 7: 'b_rook' },\\n 1: { 0: 'b_pawn', 1: 'b_pawn', 2: 'b_pawn', 3: 'b_pawn', 4: 'b_pawn', 5: 'b_pawn', 6: 'b_pawn', 7: 'b_pawn' },\\n 2: { 0: '', 1: '', 2: '', 3: '', 4: '', 5: '', 6: '', 7: '' },\\n 3: { 0: '', 1: '', 2: '', 3: '', 4: '', 5: '', 6: '', 7: '' },\\n 4: { 0: '', 1: '', 2: '', 3: '', 4: '', 5: '', 6: '', 7: '' },\\n 5: { 0: '', 1: '', 2: '', 3: '', 4: '', 5: '', 6: '', 7: '' },\\n 6: { 0: 'w_pawn', 1: 'w_pawn', 2: 'w_pawn', 3: 'w_pawn', 4: 'w_pawn', 5: 'w_pawn', 6: 'w_pawn', 7: 'w_pawn' },\\n 7: { 0: 'w_rook', 1: 'w_knight', 2: 'w_bishop', 3: 'w_king', 4: 'w_queen', 5: 'w_bishop', 6: 'w_knight', 7: 'w_rook' },\\n }\\n }\\n\\n clone() {\\n let virtualChess = new Chess();\\n virtualChess.turn = this.turn;\\n virtualChess.logs = JSON.parse(JSON.stringify(this.logs));\\n virtualChess.grid = JSON.parse(JSON.stringify(this.grid));\\n return virtualChess;\\n }\\n\\n move(fromX, fromY, toX, toY) {\\n if (this.status !== 'playing') return;\\n if (this.grid[fromX][fromY][0] !== this.turn) return;\\n\\n if (this.grid[toX][toY] == 'w_king') this.status = 'black win';\\n if (this.grid[toX][toY] == 'b_king') this.status = 'white win';\\n\\n if ((toX === 0 || toX === 7) \u0026\u0026 this.grid[fromX][fromY].split('_')[1] === 'pawn') {\\n this.grid[fromX][fromY] = this.turn + '_queen';\\n }\\n\\n this._move(fromX, fromY, toX, toY);\\n\\n this.turn = this.turn === 'w' ? 'b' : 'w';\\n }\\n\\n getReachedBlock(fromX, fromY) {\\n let from = this.grid[fromX][fromY];\\n if (from === '' || from[0] !== this.turn || this.status !== 'playing') return [];\\n\\n switch (from.split('_')[1]) {\\n case 'rook': return this._rook(fromX, fromY);\\n case 'bishop': return this._bishop(fromX, fromY);\\n case 'queen': return this._queen(fromX, fromY);\\n case 'king': return this._king(fromX, fromY);\\n case 'knight': return this._knight(fromX, fromY);\\n case 'pawn': return this._pawn(fromX, fromY);\\n }\\n }\\n\\n undo() {\\n if (this.logs.length == 0) return;\\n let lastMod = this.logs.pop();\\n while (lastMod.length \u003e 0) {\\n let mod = lastMod.pop();\\n if (mod[0] === 'remove') this.grid[mod[1]][mod[2]] = mod[3];\\n if (mod[0] === 'create') this.grid[mod[1]][mod[2]] = '';\\n }\\n this.turn = this.turn === 'w' ? 'b' : 'w';\\n this.status = 'playing';\\n }\\n\\n _move(fromX, fromY, toX, toY) {\\n this.logs.push([\\n ['remove', fromX, fromY, this.grid[fromX][fromY]],\\n ['remove', toX, toY, this.grid[toX][toY]],\\n ['create', toX, toY, this.grid[fromX][fromY]],\\n ]);\\n this.grid[toX][toY] = this.grid[fromX][fromY];\\n this.grid[fromX][fromY] = '';\\n }\\n\\n _rook(fromX, fromY) {\\n return this._line(fromX, fromY, 0, 1)\\n .concat(this._line(fromX, fromY, 1, 0))\\n .concat(this._line(fromX, fromY, 0, -1))\\n .concat(this._line(fromX, fromY, -1, 0))\\n }\\n\\n _bishop(fromX, fromY) {\\n return this._line(fromX, fromY, 1, 1)\\n .concat(this._line(fromX, fromY, -1, 1))\\n .concat(this._line(fromX, fromY, 1, -1))\\n .concat(this._line(fromX, fromY, -1, -1))\\n }\\n\\n _queen(fromX, fromY) {\\n return this._bishop(fromX, fromY).concat(this._rook(fromX, fromY));\\n }\\n\\n _king(x, y) {\\n let steps = [ \\n {x: x-1,y: y-1}, {x: x+0,y: y-1}, {x: x+1,y: y-1},\\n {x: x-1,y: y+0}, {x: x+1,y: y+0},\\n {x: x-1,y: y+1}, {x: x+0,y: y+1}, {x: x+1,y: y+1},\\n ];\\n return steps.filter(this._isInRange).filter(this._isCover.bind(this));\\n }\\n\\n _knight(fromX, fromY) {\\n let steps = [\\n { x: fromX + 2, y: fromY + 1 },\\n { x: fromX + 2, y: fromY - 1 },\\n { x: fromX - 2, y: fromY + 1 },\\n { x: fromX - 2, y: fromY - 1 },\\n { x: fromX + 1, y: fromY + 2 },\\n { x: fromX + 1, y: fromY - 2 },\\n { x: fromX - 1, y: fromY + 2 },\\n { x: fromX - 1, y: fromY - 2 },\\n ];\\n return steps.filter(this._isInRange).filter(this._isCover.bind(this));\\n }\\n\\n _pawn(fromX, fromY) {\\n if (fromX === 0 || fromX === 7) return [];\\n let steps = [];\\n\\n if (this.turn == 'b') {\\n if (this.grid[fromX + 1][fromY] === '') steps.push({ x: fromX + 1, y: fromY });\\n if (fromY !== 7 \u0026\u0026 this.grid[fromX + 1][fromY + 1][0] === 'w') steps.push({ x: fromX + 1, y: fromY + 1 });\\n if (fromY !== 0 \u0026\u0026 this.grid[fromX + 1][fromY - 1][0] === 'w') steps.push({ x: fromX + 1, y: fromY - 1 });\\n if (fromX === 1 \u0026\u0026 this.grid[fromX + 2][fromY] === '') steps.push({ x: fromX + 2, y: fromY });\\n } else {\\n if (this.grid[fromX - 1][fromY] === '') steps.push({ x: fromX - 1, y: fromY });\\n if (fromY !== 7 \u0026\u0026 this.grid[fromX - 1][fromY + 1][0] === 'b') steps.push({ x: fromX - 1, y: fromY + 1 });\\n if (fromY !== 0 \u0026\u0026 this.grid[fromX - 1][fromY - 1][0] === 'b') steps.push({ x: fromX - 1, y: fromY - 1 });\\n if (fromX === 6 \u0026\u0026 this.grid[fromX - 2][fromY] === '') steps.push({ x: fromX - 2, y: fromY });\\n }\\n return steps;\\n }\\n\\n _isCover(pos) {\\n return this.grid[pos.x][pos.y] === '' || this.grid[pos.x][pos.y][0] !== this.turn;\\n }\\n\\n _isInRange(pos) {\\n return pos.x \u003e= 0 \u0026\u0026 pos.x \u003c= 7 \u0026\u0026 pos.y \u003e= 0 \u0026\u0026 pos.y \u003c= 7;\\n }\\n\\n _line(fromX, fromY, offsetX, offsetY) {\\n let steps = [];\\n for (let i = 1; i \u003c 8; i++) {\\n let x = fromX + offsetX * i;\\n let y = fromY + offsetY * i;\\n if (x \u003c 0 || x \u003e 7 || y \u003c 0 || y \u003e 7) break;\\n if (this.grid[x][y] === '' || this.grid[x][y][0] !== this.turn) {\\n steps.push({ x: x, y: y });\\n }\\n if (this.grid[x][y] !== '') break;\\n }\\n return steps;\\n }\\n}\\n\\nlet chess = new Chess();\\n\\nnew Vue({\\n el: '#root',\\n data: {\\n black: false,\\n depth: 4,\\n message: 'Player first. Press `reverse` button to exchange.',\\n focusOn: {},\\n grid: chess.grid,\\n board: { 0: {}, 1: {}, 2: {}, 3: {}, 4: {}, 5: {}, 6: {}, 7: {} },\\n logs: [],\\n },\\n methods: {\\n click(x, y) {\\n let self = this;\\n x = Number(x);\\n y = Number(y);\\n\\n if (this.focusOn.x === undefined) {\\n let arr = chess.getReachedBlock(x, y);\\n arr.forEach((pos) =\u003e {\\n this.board[pos.x][pos.y] = true;\\n });\\n this.focusOn = { x: x, y: y };\\n } else if (this.board[x][y]) {\\n chess.move(this.focusOn.x, this.focusOn.y, x, y);\\n this.clear();\\n this.message = 'thinking...';\\n \\n setTimeout(() =\u003e {\\n let startTime = Date.now();\\n if(window.AI !== undefined) window.AI(chess.turn, this.depth);\\n let costTime = Date.now() - startTime\\n this.message = 'Cost: ' + (costTime / 1000) + 'sec';\\n this.logs.push({ cost: costTime, depth: this.depth });\\n if (chess.status !== 'playing') this.message = chess.status;\\n });\\n if (chess.status !== 'playing') this.message = chess.status;\\n } else {\\n this.clear();\\n }\\n },\\n color(x, y) {\\n x = Number(x);\\n y = Number(y);\\n let odd = (x + y) % 2 === 0;\\n\\n if (x == this.focusOn.x \u0026\u0026 y == this.focusOn.y) return 'blue';\\n\\n return this.board[x][y] ? 'red' : odd ? '#512a2a' : '#7c4c3e';\\n },\\n\\n clear() {\\n this.focusOn = {};\\n this.board = { 0: {}, 1: {}, 2: {}, 3: {}, 4: {}, 5: {}, 6: {}, 7: {} }\\n },\\n attachAI(func) {\\n this.AI = func;\\n }\\n }\\n})\\n\\n\"}","created_at":"2018-12-28T12:05:03.817+08:00","updated_at":"2019-10-21T07:21:22.268+08:00","name":"test","language":"web","screenshot":{"url":"https://cdn5.koding.school/uploads/project/screenshot/52794/1b3e45568ab7fdcfc243a8f7a462c2b8.jpg"},"parent_id":3,"plugin":"","description":null,"note":null,"status":"public","like_student_ids":[],"is_featured":false,"views":281,"hashid":"zpes3pvd","is_content_changed":false,"review_status":"unsubmitted","submitted_at":null,"reviewed_at":null,"advise":null,"is_deleted":false}
[{"id":941247,"file_name":"b_bishop.svg","project_id":52794,"asset_id":105387,"created_at":"2018-12-28T12:06:28.777+08:00","updated_at":"2018-12-28T12:06:28.777+08:00"},{"id":941248,"file_name":"b_king.svg","project_id":52794,"asset_id":105388,"created_at":"2018-12-28T12:06:28.780+08:00","updated_at":"2018-12-28T12:06:28.780+08:00"},{"id":941249,"file_name":"b_knight.svg","project_id":52794,"asset_id":105389,"created_at":"2018-12-28T12:06:30.024+08:00","updated_at":"2018-12-28T12:06:30.024+08:00"},{"id":941250,"file_name":"b_pawn.svg","project_id":52794,"asset_id":105390,"created_at":"2018-12-28T12:06:30.027+08:00","updated_at":"2018-12-28T12:06:30.027+08:00"},{"id":941251,"file_name":"b_queen.svg","project_id":52794,"asset_id":105391,"created_at":"2018-12-28T12:06:30.784+08:00","updated_at":"2018-12-28T12:06:30.784+08:00"},{"id":941252,"file_name":"b_rook.svg","project_id":52794,"asset_id":105392,"created_at":"2018-12-28T12:06:30.788+08:00","updated_at":"2018-12-28T12:06:30.788+08:00"},{"id":941253,"file_name":"w_bishop.svg","project_id":52794,"asset_id":105393,"created_at":"2018-12-28T12:06:31.518+08:00","updated_at":"2018-12-28T12:06:31.518+08:00"},{"id":941254,"file_name":"w_king.svg","project_id":52794,"asset_id":105394,"created_at":"2018-12-28T12:06:31.521+08:00","updated_at":"2018-12-28T12:06:31.521+08:00"},{"id":941255,"file_name":"w_knight.svg","project_id":52794,"asset_id":105395,"created_at":"2018-12-28T12:06:32.217+08:00","updated_at":"2018-12-28T12:06:32.217+08:00"},{"id":941256,"file_name":"w_pawn.svg","project_id":52794,"asset_id":105396,"created_at":"2018-12-28T12:06:32.220+08:00","updated_at":"2018-12-28T12:06:32.220+08:00"},{"id":941257,"file_name":"w_queen.svg","project_id":52794,"asset_id":105397,"created_at":"2018-12-28T12:06:32.988+08:00","updated_at":"2018-12-28T12:06:32.988+08:00"},{"id":941258,"file_name":"w_rook.svg","project_id":52794,"asset_id":105398,"created_at":"2018-12-28T12:06:32.991+08:00","updated_at":"2018-12-28T12:06:32.991+08:00"}]
橘蘋學習平台
橘蘋學習平台
我的作品
檢視專案頁
匯出
複製
匯入
刪除
下載 Android APP (APK)
截圖
前往網站頁面
1:1:1
1:1
full
用手機掃描下方 QRCode 進行安裝
或您也可以
下載 APK
到這台電腦
用手機掃描下方 QRCode 進行安裝
或您也可以
下載 APK
到這台電腦