Add simple auto move to practice.

This commit is contained in:
yukirij 2024-08-19 19:03:50 -07:00
parent 6d22410658
commit f74396f257
3 changed files with 144 additions and 39 deletions

View File

@ -102,14 +102,14 @@ GAME.Tile = class {
constructor(index) {
this.piece = null;
this.threaten = [false, false];
this.threaten = [0, 0];
this.checking = false;
this.hex = HEX.tile_to_hex(index);
}
reset() {
this.threaten = [false, false];
this.threaten = [0, 0];
this.checking = false;
}
};
@ -236,7 +236,7 @@ GAME.Game = class {
// Get threatened tiles.
for(let movement of this.movement_tiles(piece, piece.tile)) {
if(movement.threat) {
this.board.tiles[movement.tile].threaten[piece.player] = true;
this.board.tiles[movement.tile].threaten[piece.player] += 1;
}
if(movement.check) {
this.board.tiles[piece.tile].checking = true;
@ -408,7 +408,7 @@ GAME.Game = class {
}
// King may not move onto threatened tile.
if(piece.piece == GAME.Const.PieceId.Omen && tile_data.threaten[+(!piece.player)]) {
if(piece.piece == GAME.Const.PieceId.Omen && tile_data.threaten[+(!piece.player)] > 0) {
result = false;
}
@ -490,7 +490,7 @@ GAME.Game = class {
// King cannot swap onto tile that is threatened.
// This case should also cover blocking.
if(target.piece == GAME.Const.PieceId.Omen
&& (this.board.tiles[tile].threaten[+(!target.player)] || piece.blocking != 0)) {
&& (this.board.tiles[tile].threaten[+(!target.player)] > 0 || piece.blocking != 0)) {
return false;
}
return ((moves.direction & mask) != 0 && (range == 1 || (moves.stride & mask) != 0));

View File

@ -87,7 +87,7 @@ const INTERFACE = {
if(movement.valid) {
// Show valid/threat hints if piece belongs to player and is player turn.
if(INTERFACE_DATA.player == 2 || (player == INTERFACE_DATA.player && (GAME_DATA.turn & 1) == player)) {
if(GAME_DATA.board.tiles[movement.tile].threaten[+(!player)]) {
if(GAME_DATA.board.tiles[movement.tile].threaten[+(!player)] > 0) {
INTERFACE_DATA.board_state[movement.tile][zone] = INTERFACE.TileStatus.Threat;
} else {
INTERFACE_DATA.board_state[movement.tile][zone] = INTERFACE.TileStatus.Valid;
@ -200,39 +200,8 @@ const INTERFACE = {
// Handle player action.
if(is_valid) {
// Send message to server for online game.
switch(INTERFACE_DATA.mode) {
// Apply action and change turn for local game.
case INTERFACE.Mode.Local: {
let play = new GAME.Play(INTERFACE_DATA.select.source, INTERFACE_DATA.select.tile, INTERFACE_DATA.hover.tile);
INTERFACE_DATA.play = play;
GAME_DATA.process(play);
INTERFACE_DATA.player = +(!INTERFACE_DATA.player);
INTERFACE_DATA.rotate = +(!INTERFACE_DATA.rotate);
INTERFACE.draw();
} break;
// Send action to server for validation.
case INTERFACE.Mode.Online: {
let move_data = INTERFACE_DATA.select.source | (INTERFACE_DATA.select.tile << 1) | (INTERFACE_DATA.hover.tile << 7);
MESSAGE_COMPOSE([
PACK.u16(OpCode.GamePlay),
PACK.u16(0),
PACK.u16(GAME_DATA.turn),
PACK.u16(move_data),
]);
} break;
// Branch into local game from here.
case INTERFACE.Mode.Replay: {
} break;
}
let play = new GAME.Play(INTERFACE_DATA.select.source, INTERFACE_DATA.select.tile, INTERFACE_DATA.hover.tile);
INTERFACE.process(play);
INTERFACE_DATA.select = null;
}
@ -860,6 +829,38 @@ const INTERFACE = {
}
},
process(play) {
// Send message to server for online game.
switch(INTERFACE_DATA.mode) {
// Apply action and change turn for local game.
case INTERFACE.Mode.Local: {
INTERFACE_DATA.play = play;
GAME_DATA.process(play);
INTERFACE_DATA.player = +(!INTERFACE_DATA.player);
INTERFACE_DATA.rotate = +(!INTERFACE_DATA.rotate);
INTERFACE.draw();
} break;
// Send action to server for validation.
case INTERFACE.Mode.Online: {
let move_data = play.source | (play.from << 1) | (play.to << 7);
MESSAGE_COMPOSE([
PACK.u16(OpCode.GamePlay),
PACK.u16(0),
PACK.u16(GAME_DATA.turn),
PACK.u16(move_data),
]);
} break;
// Branch into local game from here.
case INTERFACE.Mode.Replay: {
} break;
}
},
rotate() {
INTERFACE_DATA.rotate ^= 1;
INTERFACE.draw();
@ -909,6 +910,109 @@ const INTERFACE = {
}
}
},
auto() {
let moves = [ ];
let player = GAME_DATA.turn & 1;
let opponent = +(!player);
// 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);
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; }
}
if(next_move.check) { score += 2; }
}
}
moves.push({
score: score,
play: new GAME.Play(1, p, move.tile),
});
}
}
}
}
// 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)) {
if(move.valid) {
let hex = HEX.tile_to_hex(move.tile);
// Calculate base score.
let score = ((piece.piece + 1) * 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 * (1 + target.piece); }
else { score -= 1; }
}
score += +((-MATH.sign_branch(player) * (hex.y - current_hex.y)) > 0);
// Add score for check.
if(move.check) { score += 2; }
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; }
}
if(next_move.check) { score += 2; }
}
}
moves.push({
score: score,
play: new GAME.Play(0, piece.tile, move.tile),
});
}
}
}
}
}
if(moves.length > 0) {
moves = moves.sort((a, b) => { return b.score - a.score; });
let select = Math.floor(Math.random() * Math.min(moves.length, 3));
INTERFACE.process(moves[select].play);
}
},
};
INTERFACE.Radius = 2.0 / Math.sqrt(3.0);

View File

@ -564,6 +564,7 @@ const SCENES = {
UI.nav([
UI.button("Rotate", () => { INTERFACE.rotate(); }),
UI.button("Mirror", () => { INTERFACE.mirror(); }),
UI.button("Auto", () => { INTERFACE.auto(); }),
], buttons_bottom);
let canvas = document.createElement("canvas");