Improve memory handling for auto cpu.

This commit is contained in:
yukirij 2025-01-23 22:23:55 -08:00
parent 2c7854f2d2
commit e8b747dc47
3 changed files with 174 additions and 94 deletions

View File

@ -15,22 +15,34 @@ GAME.Board = class {
lay.piece,
lay.player,
lay.promote,
HEX.hex_to_tile(lay.hex)
HEX.hex_to_tile(lay.hex.x, lay.hex.y)
);
}
}
}
from(state) {
for(let i = 0; i < state.tiles.length; ++i) { this.tiles[i].from(state.tiles[i]); }
for(let i = 0; i < state.columns.length; ++i) { this.columns[i].from(state.columns[i]); }
this.pieces = [ ];
for(let i = 0; i < state.pieces.length; ++i) {
this.pieces.push(null);
if(state.pieces[i] !== null) {
this.pieces[i] = state.pieces[i].clone();
}
}
}
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.columns.length; ++i) { board.columns[i] = this.columns[i].clone(); }
for(let i = 0; i < this.pieces.length; ++i) { board.pieces.push(null); }
for(let i = 0; i < this.pieces.length; ++i) {
board.pieces.push(null);
if(this.pieces[i] !== null) {
board.pieces[i] = this.pieces[i].clone();
} else {
board.pieces[i] = null;
}
}
@ -61,6 +73,12 @@ GAME.Pool = class {
this.pieces = [ ]; for(let i = 0; i < 7; ++i) { this.pieces.push(0); }
}
from(state) {
for(let i = 0; i < this.pieces.length; ++i) {
this.pieces[i] = state.pieces[i];
}
}
clone() {
let pool = new GAME.Pool();
for(let i = 0; i < pool.pieces.length; ++i) { pool.pieces[i] = this.pieces[i]; }
@ -74,6 +92,18 @@ GAME.Column = class {
this.extent = [0, 8];
}
from(state) {
this.militia = state.militia;
this.extent = state.extent;
}
clone() {
let col = new GAME.Column();
col.militia = this.militia;
col.extent = this.extent;
return col;
}
reset() {
this.militia = [false, false];
this.extent = [0, 8];
@ -87,13 +117,28 @@ GAME.Tile = class {
this.threaten = [0, 0];
this.checking = false;
this.hex = HEX.tile_to_hex(index);
let [hx, hy] = HEX.tile_to_hex(index);
this.hex = new MATH.Vec2(hx, hy);
}
from(state) {
if(state.piece !== null) {
this.piece = state.piece;
} else {
this.piece = null;
}
this.hex = new MATH.Vec2(state.hex.x, state.hex.y);
this.threaten = state.threaten;
this.checking = state.checking;
}
clone() {
let tile = new GAME.Tile(0);
tile.piece = this.piece;
tile.hex = this.hex;
tile.threaten = this.threaten;
tile.checking = this.checking;
return tile;
}
@ -176,6 +221,7 @@ GAME.Piece = class {
let piece = new GAME.Piece(this.piece, this.player);
piece.promoted = this.promoted;
piece.tile = this.tile;
piece.blocking = this.blocking;
return piece;
}
@ -198,7 +244,7 @@ GAME.Piece = class {
};
GAME.Game = class {
constructor(config) {
constructor(config, update=true) {
this.config = config;
this.turn = 0;
@ -221,17 +267,34 @@ GAME.Game = class {
check:false, //[false, false],
};
this.update_board();
if(update) {
this.update_board();
}
}
from(state) {
this.config = state.config;
this.turn = state.turn;
this.board.from(state.board);
this.pools[0].from(state.pools[0]);
this.pools[1].from(state.pools[1]);
this.state = {
code:state.state.code,
check:state.state.check,
};
}
clone() {
let game = new GAME.Game(this.config);
let game = new GAME.Game(this.config, false);
game.turn = this.turn;
game.board = this.board.clone();
game.pools = [ this.pools[0].clone(), this.pools[1].clone() ];
game.update_board();
game.state = {
code:this.state.code,
check:this.state.check,
};
return game;
}
@ -344,9 +407,9 @@ GAME.Game = class {
this.board.tiles[play.from].piece = target_id;
// Check if swap is promoted.
let hex = HEX.tile_to_hex(target.tile);
hex.y -= MATH.sign_branch(target.player);
if(!target.promoted && target.has_promotion() && !HEX.is_valid_board(hex)) {
let [hx, hy] = HEX.tile_to_hex(target.tile);
hy -= MATH.sign_branch(target.player);
if(!target.promoted && target.has_promotion() && !HEX.is_valid_board(hx, hy)) {
target.promoted = true;
}
}
@ -361,9 +424,9 @@ GAME.Game = class {
this.board.tiles[play.to].piece = piece_id;
// Check if piece is promoted.
let hex = HEX.tile_to_hex(piece.tile);
hex.y -= MATH.sign_branch(piece.player);
if(!piece.promoted && piece.has_promotion() && !HEX.is_valid_board(hex)) {
let [hx, hy] = HEX.tile_to_hex(piece.tile);
hy -= MATH.sign_branch(piece.player);
if(!piece.promoted && piece.has_promotion() && !HEX.is_valid_board(hx, hy)) {
piece.promoted = true;
}
@ -497,8 +560,8 @@ GAME.Game = class {
let swap = false;
let tile_occupied = false;
if(HEX.is_valid_board(move_hex)) {
let tile_id = HEX.hex_to_tile(move_hex);
if(HEX.is_valid_board(move_hex.x, move_hex.y)) {
let tile_id = HEX.hex_to_tile(move_hex.x, move_hex.y);
let tile_data = this.board.tiles[tile_id];
let target_id = tile_data.piece;
@ -647,8 +710,8 @@ GAME.Game = class {
for(let dist = 1; dist <= 9; ++dist) {
tile_hex.add(direction);
if(HEX.is_valid_board(tile_hex)) {
let tile_id = HEX.hex_to_tile(tile_hex);
if(HEX.is_valid_board(tile_hex.x, tile_hex.y)) {
let tile_id = HEX.hex_to_tile(tile_hex.x, tile_hex.y);
if(this.placable_tile(piece, tile_id, {check:false, extent:false})) {
tiles.push(new GAME.MovementTile(tile_id, true, false, 0, 0));
@ -700,8 +763,8 @@ GAME.Game = class {
for(let dist = 1; dist <= 9; ++dist) {
tile_hex.add(direction);
if(HEX.is_valid_board(tile_hex)) {
let tile_id = HEX.hex_to_tile(tile_hex);
if(HEX.is_valid_board(tile_hex.x, tile_hex.y)) {
let tile_id = HEX.hex_to_tile(tile_hex.x, tile_hex.y);
let valid = true;
if(piece.player == 0) {
@ -736,14 +799,15 @@ GAME.Game = class {
placement_tiles(piece_id, player) {
let tiles = [ ];
if(this.state.code != 0) { return tiles; }
let piece = new GAME.Piece(piece_id, player);
if(this.state.code == 0 && this.pools[player].pieces[piece_id] > 0) {
let piece = new GAME.Piece(piece_id, player);
// Get tiles onto which piece may be placed.
for(let i = 0; i < this.board.tiles.length; ++i) {
if(this.placable_tile(piece, i)) {
tiles.push(new GAME.MovementTile(i, true, false, false));
// Get tiles onto which piece may be placed.
for(let i = 0; i < this.board.tiles.length; ++i) {
if(this.placable_tile(piece, i)) {
tiles.push(new GAME.MovementTile(i, true, false, false));
}
}
}
@ -753,7 +817,7 @@ GAME.Game = class {
placable_tile(piece, tile_id, params={}) {
let valid = false;
let hex = HEX.tile_to_hex(tile_id);
let [hx, hy] = HEX.tile_to_hex(tile_id);
let tile = this.board.tiles[tile_id];
// Check if tile is occupied.
@ -774,15 +838,15 @@ GAME.Game = class {
// Check off-sides.
if(params.extent !== false) {
if(piece.player == 0) {
position_valid = position_valid && (hex.y <= this.board.columns[hex.x].extent[1]);
position_valid = position_valid && (hy <= this.board.columns[hx].extent[1]);
} else {
position_valid = position_valid && (hex.y >= this.board.columns[hex.x].extent[0]);
position_valid = position_valid && (hy >= this.board.columns[hx].extent[0]);
}
}
// Check militia stacking.
if(params.stack !== false) {
if(piece.piece == GAME.Const.PieceId.Militia && this.board.columns[hex.x].militia[piece.player]) {
if(piece.piece == GAME.Const.PieceId.Militia && this.board.columns[hx].militia[piece.player]) {
position_valid = false;
}
}

View File

@ -228,8 +228,8 @@ const INTERFACE = {
}
let hex = new MATH.Vec2(hx, hy);
if(HEX.is_valid_board(hex)) {
let tile = HEX.hex_to_tile(hex);
if(HEX.is_valid_board(hx, hy)) {
let tile = HEX.hex_to_tile(hx, hy);
INTERFACE_DATA.hover = new INTERFACE.Selection(0, tile, hex);
}
}
@ -260,7 +260,7 @@ const INTERFACE = {
}
let hex = new MATH.Vec2(hx, hy);
if(HEX.is_valid_pool(hex)) {
if(HEX.is_valid_pool(hx, hy)) {
let tx = (2 * (hx > 0)) + (2 * (hx > 1));
let tile = tile_set + tx + hy;
INTERFACE_DATA.hover = new INTERFACE.Selection(1, tile, hex);
@ -676,14 +676,14 @@ const INTERFACE = {
let show_hints = !(is_hover || is_select) && piece !== null && draw_piece;
let coord = HEX.tile_to_hex(i);
let [cx, cy] = HEX.tile_to_hex(i);
if((INTERFACE_DATA.player & 1) ^ INTERFACE_DATA.rotate == 1) {
coord.x = 8 - coord.x;
coord.y = 8 - coord.y;
cx = 8 - cx;
cy = 8 - cy;
}
let gui_x = basis_x + (1.5 * radius * coord.x);
let gui_y = basis_y - (2 * gui_scale * coord.y) + (gui_scale * coord.x);
let gui_x = basis_x + (1.5 * radius * cx);
let gui_y = basis_y - (2 * gui_scale * cy) + (gui_scale * cx);
ctx.save();
ctx.translate(gui_x, gui_y);
@ -749,7 +749,7 @@ const INTERFACE = {
background_color = INTERFACE.Color.DuskDarkest;
}
} else {
switch(MATH.mod(coord.x + coord.y, 3)) {
switch(MATH.mod(cx + cy, 3)) {
case 0: background_color = INTERFACE.Color.TileMedium; break;
case 1: background_color = INTERFACE.Color.TileLight; break;
case 2: background_color = INTERFACE.Color.TileDark; break;
@ -1027,13 +1027,13 @@ const INTERFACE = {
let target = INTERFACE_DATA.Animation.piece.target;
// Get to and from coordinates.
let coord_to = HEX.tile_to_hex(play.to);
let [cdx, cdy] = HEX.tile_to_hex(play.to);
if((INTERFACE_DATA.player & 1) ^ INTERFACE_DATA.rotate == 1) {
coord_to.x = 8 - coord_to.x;
coord_to.y = 8 - coord_to.y;
cdx = 8 - cdx;
cdy = 8 - cdy;
}
let to_x = basis_x + (1.5 * radius * coord_to.x);
let to_y = basis_y - (2 * gui_scale * coord_to.y) + (gui_scale * coord_to.x);
let to_x = basis_x + (1.5 * radius * cdx);
let to_y = basis_y - (2 * gui_scale * cdy) + (gui_scale * cdx);
let from_x = 0;
let from_y = 0;
@ -1041,13 +1041,13 @@ const INTERFACE = {
// Lerp between board positions.
case 0:
case 2: {
let coord_from = HEX.tile_to_hex(play.from);
let [cfx, cfy] = HEX.tile_to_hex(play.from);
if((INTERFACE_DATA.player & 1) ^ INTERFACE_DATA.rotate == 1) {
coord_from.x = 8 - coord_from.x;
coord_from.y = 8 - coord_from.y;
cfx = 8 - cfx;
cfy = 8 - cfy;
}
from_x = basis_x + (1.5 * radius * coord_from.x);
from_y = basis_y - (2 * gui_scale * coord_from.y) + (gui_scale * coord_from.x);
from_x = basis_x + (1.5 * radius * cfx);
from_y = basis_y - (2 * gui_scale * cfy) + (gui_scale * cfx);
} break;
// Lerp between pool and board positions.
@ -1896,32 +1896,36 @@ const INTERFACE = {
let turn = (state.turn & 1);
if(state.state.checkmate) {
if(turn == player) { score -= 1000; }
else { score += 1000; }
if(turn == player) { score = -100000; }
else { score = 100000; }
} else {
// Increase score for check, decrease for checked.
if(state.state.check != 0) {
if(turn == player) { score -= 20; }
else { score += 1; }
if(turn == player) { score -= 4; }
else { score += 2; }
}
// Increase score for each threatening tile, decrease for threatened tile.
for(let i = 0; i < state.board.tiles.length; ++i) {
let tile = state.board.tiles[i];
score += Math.floor((tile.threaten[player] - tile.threaten[opponent]) / 2);
score += tile.threaten[player] - tile.threaten[opponent];
}
// Increase score for pieces on board.
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];
let hex = HEX.tile_to_hex(tile);
let [hx, hy] = HEX.tile_to_hex(tile);
let piece_score = 3 + (4 * (piece.piece + piece.promoted));
if(piece.player == player) {
if(tile.threaten[opponent] == 0) { score += piece_score; }
else { score -= piece_score; }
if(hex.y == state.board.columns[hex.x].extent[player]) {
if(hy == state.board.columns[hx].extent[player]) {
score += piece_score * tile.threaten[player];
}
} else {
@ -1930,11 +1934,12 @@ const INTERFACE = {
}
}
// Increase for player pool, decrease for opponent pool.
for(let i = 0; i < state.pools[player].pieces.length; ++i) {
score += 3 * (2 + i) * state.pools[player].pieces[i];
score += 2;
}
for(let i = 0; i < state.pools[opponent].pieces.length; ++i) {
score -= 2 * (1 + i) * state.pools[opponent].pieces[i];
score -= 3;
}
for(let i = 0; i < state.board.columns.length; ++i) {
@ -1990,8 +1995,9 @@ const INTERFACE = {
}
// Get move scores.
let st = state.clone();
for(let mv = 0; mv < moves.length; ++mv) {
let st = state.clone();
st.from(state);
st.process(moves[mv].play);
moves[mv].score = state_score(st, player);
}
@ -2000,16 +2006,15 @@ const INTERFACE = {
if(moves.length > 0) {
moves.sort((a, b) => { return b.score - a.score });
/*if(depth == 0) {
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);
console.log("B");
moves[i].score = state_score(st, search_player);
}
} else {
for(let i = 0; i < moves.length && i < Math.ceil(Math.log2(moves.length)); ++i) {
for(let i = 0; i < moves.length && i < 8; ++i) {
let st = state.clone();
st.process(moves[i].play);
@ -2018,7 +2023,7 @@ const INTERFACE = {
moves[i].score = result.score;
}
}
}*/
}
// Select random from ties.
let selection = 0;
@ -2037,9 +2042,10 @@ const INTERFACE = {
let result = determine_play(GAME_DATA, GAME_DATA.turn & 1, 1);
if(result !== null) {
console.log("PL" + result.play.from + " " + result.play.to);
INTERFACE.process(result.play);
} else {
console.log("warn: autoplay move was null.");
console.log("MOVES NULL");
}
},

View File

@ -241,46 +241,56 @@ const COLOR = {
}
const HEX = {
hex_to_tile(hex) {
let a = ((hex.x + 4) * (hex.x + 5) / 2) - 10;
let b = (hex.x > 4) * ((hex.x - 4) + ((hex.x - 5) * (hex.x - 4)));
return a - b + hex.y;
COLUMNS_START: [
0, 5, 11, 18, 26, 35, 43, 50, 56,
],
COLUMNS_EXTENT: [
[0, 4],
[0, 5],
[0, 6],
[0, 7],
[0, 8],
[1, 8],
[2, 8],
[3, 8],
[4, 8],
],
POOL_EXTENT: [
[0, 1],
[0, 2],
[1, 2],
],
hex_to_tile(x, y) {
let a = ((x + 4) * (x + 5) / 2) - 10;
let b = (x > 4) * ((x - 4) + ((x - 5) * (x - 4)));
return a - b + y;
},
tile_to_hex(tile) {
const ROWS = [ 0, 5, 11, 18, 26, 35, 43, 50, 56, 61 ];
let column = 0;
while(tile >= ROWS[column + 1]) { column += 1; }
let row = tile - ROWS[column] + ((column > 4) * (column - 4));
return new MATH.Vec2(column, row);
let column = (tile >= this.COLUMNS_START[1])
+ (tile >= this.COLUMNS_START[2])
+ (tile >= this.COLUMNS_START[3])
+ (tile >= this.COLUMNS_START[4])
+ (tile >= this.COLUMNS_START[5])
+ (tile >= this.COLUMNS_START[6])
+ (tile >= this.COLUMNS_START[7])
+ (tile >= this.COLUMNS_START[8]);
let row = tile - this.COLUMNS_START[column] + ((column > 4) * (column - 4));
return [column, row];
},
is_valid_board(hex) {
is_valid_board(x, y) {
// x = minimum
// y = maximum
const COLUMNS = [
new MATH.Vec2(0, 4),
new MATH.Vec2(0, 5),
new MATH.Vec2(0, 6),
new MATH.Vec2(0, 7),
new MATH.Vec2(0, 8),
new MATH.Vec2(1, 8),
new MATH.Vec2(2, 8),
new MATH.Vec2(3, 8),
new MATH.Vec2(4, 8),
];
return (hex.x >= 0 && hex.x < 9 && hex.y >= COLUMNS[hex.x].x && hex.y <= COLUMNS[hex.x].y);
return (x >= 0 && x < 9 && y >= this.COLUMNS_EXTENT[x][0] && y <= this.COLUMNS_EXTENT[x][1]);
},
is_valid_pool(hex) {
is_valid_pool(x, y) {
// x = minimum
// y = maximum
const COLUMNS = [
new MATH.Vec2(0, 1),
new MATH.Vec2(0, 2),
new MATH.Vec2(1, 2),
];
return (hex.x >= 0 && hex.x < 3 && hex.y >= COLUMNS[hex.x].x && hex.y <= COLUMNS[hex.x].y);
return (x >= 0 && x < 3 && y >= this.POOL_EXTENT[x][0] && y <= this.POOL_EXTENT[x][1]);
},
};