{"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":"/uploads/project/screenshot/52794/1b3e45568ab7fdcfc243a8f7a462c2b8.jpg"},"parent_id":3,"plugin":"","description":null,"note":null,"status":"public","like_student_ids":[],"is_featured":false,"views":471,"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"}]
Orange Apple Learning Platform
My Projects
View Project Page
Export
Remix
Import
Delete
Download Android APP (APK)
Screenshot
Open Web Site
繁中
简中
English
日本語
1:1:1
1:1
Full Width
Scan the QRCode below with your phone to install
Or you can
download APK
to this computer
Scan the QRCode below with your phone to install
Or you can
download APK
to this computer