From e8b747dc47259943612e68b99db1f05ba22144fb Mon Sep 17 00:00:00 2001 From: yukirij Date: Thu, 23 Jan 2025 22:23:55 -0800 Subject: [PATCH] Improve memory handling for auto cpu. --- www/js/game.js | 126 +++++++++++++++++++++++++++++++++----------- www/js/interface.js | 74 ++++++++++++++------------ www/js/util.js | 68 ++++++++++++++---------- 3 files changed, 174 insertions(+), 94 deletions(-) diff --git a/www/js/game.js b/www/js/game.js index dcfbb17..54db2db 100644 --- a/www/js/game.js +++ b/www/js/game.js @@ -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; } } diff --git a/www/js/interface.js b/www/js/interface.js index 2046b43..c4adeeb 100644 --- a/www/js/interface.js +++ b/www/js/interface.js @@ -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"); } }, diff --git a/www/js/util.js b/www/js/util.js index 3333c90..c57c1a4 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -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]); }, };