Improve automatic move.

This commit is contained in:
yukirij 2024-08-20 00:51:06 -07:00
parent 00a105dd4a
commit d3a238fbd4
2 changed files with 169 additions and 86 deletions

View File

@ -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;
}

View File

@ -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);
}
},
};