diff --git a/www/js/game.js b/www/js/game.js index d94d90a..091a2fc 100644 --- a/www/js/game.js +++ b/www/js/game.js @@ -62,6 +62,19 @@ GAME.Board = class { } } + clone() { + let board = new GAME.Board(); + for(let i = 0; i < this.tiles.length; ++i) { board.tiles[i] = this.tiles[i].clone(); } + for(let i = 0; i < this.pieces.length; ++i) { + if(this.pieces[i] !== null) { + board.pieces[i] = this.pieces[i].clone(); + } else { + board.pieces[i] = null; + } + } + return board; + } + set_piece(piece, player, tile) { let index = 0; while(this.pieces[index] !== null) { index++; } @@ -84,6 +97,12 @@ GAME.Pool = class { constructor() { this.pieces = [ ]; for(let i = 0; i < 7; ++i) { this.pieces.push(0); } } + + clone() { + let pool = new GAME.Pool(); + for(let i = 0; i < pool.pieces.length; ++i) { pool.pieces[i] = this.pieces[i]; } + return pool; + } }; GAME.Column = class { @@ -108,6 +127,13 @@ GAME.Tile = class { this.hex = HEX.tile_to_hex(index); } + clone() { + let tile = new GAME.Tile(0); + tile.piece = this.piece; + tile.hex = this.hex; + return tile; + } + reset() { this.threaten = [0, 0]; this.checking = false; @@ -175,6 +201,13 @@ GAME.Piece = class { this.blocking = 0; } + clone() { + let piece = new GAME.Piece(this.piece, this.player); + piece.promoted = this.promoted; + piece.tile = this.tile; + return piece; + } + moves() { let def = GAME.Const.Piece[this.piece]; let moves = null; @@ -212,6 +245,17 @@ GAME.Game = class { this.update_board(); } + clone() { + let game = new GAME.Game(); + + game.turn = this.turn; + game.board = this.board.clone(); + game.pools = [ this.pools[0].clone(), this.pools[1].clone() ]; + + game.update_board(); + return game; + } + update_board() { // Reset tiles this.board.reset(); @@ -447,7 +491,7 @@ GAME.Game = class { if(pieces_blocking == 1) { // Apply blocking to last . for(let idist = 1; idist < dist; idist++) { - if(GAME_DATA.board.tiles[tiles[tiles.length - idist].tile].piece !== null) { + if(this.board.tiles[tiles[tiles.length - idist].tile].piece !== null) { tiles[tiles.length - idist].block = mask; break; } diff --git a/www/js/interface.js b/www/js/interface.js index 120c283..c5b5a62 100644 --- a/www/js/interface.js +++ b/www/js/interface.js @@ -789,8 +789,6 @@ const INTERFACE = { GAME_DATA.turn = data.turn; INTERFACE_DATA.play = data.play; - console.log(data.play.source); - if(INTERFACE_DATA.play.source == 2) { GAME_DATA.state.code = 2; } @@ -912,105 +910,146 @@ const INTERFACE = { }, auto() { - let moves = [ ]; - let player = GAME_DATA.turn & 1; - let opponent = +(!player); + function state_score(state, player) { + let score = 0; + let opponent = player ^ 1; + let turn = (state.turn & 1); - // Get available placement moves. - for(let p = 0; p < 8; ++p) { - if(GAME_DATA.pools[player].pieces[p] > 0) { - for(let move of GAME_DATA.placement_tiles(p, player)) { - if(move.valid) { - let hex = HEX.tile_to_hex(move.tile); + for(let i = 0; i < state.board.tiles.length; ++i) { + let tile = state.board.tiles[i]; + score += (tile.threaten[player] - tile.threaten[opponent]) / 2; + } - let score = GAME_DATA.board.tiles[move.tile].threaten[player] - GAME_DATA.board.tiles[move.tile].threaten[opponent]; - if(player == 0) { - score += 2 * Math.max(0, GAME_DATA.board.columns[hex.x].extent[player] - hex.y); - } else { - score += 2 * Math.max(0, hex.y - GAME_DATA.board.columns[hex.x].extent[player]); - } - - let moves_from = GAME_DATA.movement_tiles(new GAME.Piece(p, player), move.tile); - for(let next_move of moves_from) { - if(next_move.valid) { - // Add score for taking. - let target_id = GAME_DATA.board.tiles[next_move.tile].piece; - if(target_id !== null) { - let target = GAME_DATA.board.pieces[target_id]; - if(target.player == opponent) { score += (1 + target.piece) * (1 + target.promoted); } - } - - if(next_move.check) { score += 1; } - } - } - - moves.push({ - score: score, - play: new GAME.Play(1, p, move.tile), - }); + for(let i = 0; i < state.board.pieces.length; ++i) { + let piece_id = state.board.pieces[i]; + if(piece_id !== null) { + let piece = state.board.pieces[i]; + let tile = state.board.tiles[piece.tile]; + + if(piece.player == player) { + score += 1 + (4 * piece.piece) + (4 * (piece.promoted + 1)); + score += (1 + piece.piece) * (tile.threaten[player] - tile.threaten[opponent]); + } else { + score -= 1 + (3 * piece.piece) + (4 * (piece.promoted + 1)); + score -= (1 + piece.piece) * (tile.threaten[opponent] - tile.threaten[player]); } } } + + for(let i = 0; i < state.pools[player].pieces.length; ++i) { + score += 2 * state.pools[player].pieces[i]; + } + for(let i = 0; i < state.pools[opponent].pieces.length; ++i) { + score -= 2 * state.pools[opponent].pieces[i]; + } + + for(let i = 0; i < state.board.columns.length; ++i) { + let column = state.board.columns[i]; + if(player == 0) { + score += column.extent[player] / 8; + score -= (8 - column.extent[opponent]) / 8; + } else { + score += (8 - column.extent[player]) / 8; + score -= column.extent[opponent] / 8; + } + } + + if(state.state.check) { + if(turn == player) { score -= 2; } + else { score += 2; } + } + if(state.state.checkmate) { + if(turn == player) { score -= 1000; } + else { score += 1000; } + } + + return score; } - - // Get available piece moves. - for(let i = 0; i < GAME_DATA.board.pieces.length; ++i) { - let piece = GAME_DATA.board.pieces[i]; - if(piece !== null) { - if(piece.player == player) { - let current_hex = HEX.tile_to_hex(piece.tile); - for(let move of GAME_DATA.movement_tiles(piece, piece.tile)) { + function determine_play(state, search_player, depth) { + let moves = [ ]; + let player = state.turn & 1; + + // Get available placement moves. + for(let p = 0; p < 8; ++p) { + if(state.pools[player].pieces[p] > 0) { + for(let move of state.placement_tiles(p, player)) { if(move.valid) { - let hex = HEX.tile_to_hex(move.tile); - - // Calculate base score. - let score = ((piece.piece + 1) * (1 + piece.promoted) * GAME_DATA.board.tiles[piece.tile].threaten[opponent]) - + GAME_DATA.board.tiles[move.tile].threaten[player] - - ((piece.piece + 1) * GAME_DATA.board.tiles[move.tile].threaten[opponent]); - - // Add score for taking. - let target_id = GAME_DATA.board.tiles[move.tile].piece; - if(target_id !== null) { - let target = GAME_DATA.board.pieces[target_id]; - if(target.player == opponent) { score += (2 + target.piece) * (1 + target.promoted); } - else { score -= 1; } - } - - score += +((-MATH.sign_branch(player) * (hex.y - current_hex.y)) > 0); - - // Add score for check. - if(move.check) { score += 1; } - - let moves_from = GAME_DATA.movement_tiles(piece, move.tile); - for(let next_move of moves_from) { - if(next_move.valid) { - // Add score for taking. - let target_id = GAME_DATA.board.tiles[next_move.tile].piece; - if(target_id !== null) { - let target = GAME_DATA.board.pieces[target_id]; - if(target.player == opponent) { score += (1 + target.piece) * (1 + target.promoted); } - } - - if(next_move.check) { score += 1; } - } - } - moves.push({ - score: score, - play: new GAME.Play(0, piece.tile, move.tile), + score: 0, + play: new GAME.Play(1, p, move.tile), }); } } } } + + // Get available piece moves. + for(let i = 0; i < state.board.pieces.length; ++i) { + let piece = state.board.pieces[i]; + if(piece !== null) { + if(piece.player == player) { + for(let move of state.movement_tiles(piece, piece.tile)) { + if(move.valid) { + moves.push({ + score: 0, + play: new GAME.Play(0, piece.tile, move.tile), + }); + } + } + } + } + } + + // Get move scores. + for(let i = 0; i < moves.length; ++i) { + let st = state.clone(); + st.process(moves[i].play); + moves[i].score = state_score(st, player); + } + + // Select move. + if(moves.length > 0) { + moves.sort((a, b) => { return b.score - a.score }); + + if(depth == 0) { + // Get move scores for search player. + for(let i = 0; i < moves.length; ++i) { + let st = state.clone(); + st.process(moves[i].play); + moves[i].score = state_score(st, search_player); + } + } else { + for(let i = 0; i < moves.length && i < 3; ++i) { + let st = state.clone(); + st.process(moves[i].play); + + let result = determine_play(st, search_player, depth - 1); + if(result !== null) { + moves[i].score = result.score; + } + } + } + + // Select random from ties. + let selection = 0; + for(let i = 1; i < moves.length; ++i) { + if(moves[i].score == moves[i-1].score) { + selection++; + } else { + break; + } + } + selection = Math.min(moves.length, selection + 3); + + return moves[Math.floor(Math.random() * selection)]; + } + return null; } - if(moves.length > 0) { - moves = moves.sort((a, b) => { return b.score - a.score; }); - - let select = Math.floor(Math.random() * Math.min(moves.length, 2)); - INTERFACE.process(moves[select].play); + let result = determine_play(GAME_DATA, GAME_DATA.turn & 1, 1); + if(result !== null) { + INTERFACE.process(result.play); } }, };