From 5ee7d467b1cca6467ec4fa3c69c37dbf7baf2132 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 27 Jul 2022 12:24:30 +0200 Subject: Use cache for presence queries. --- rules.js | 239 +++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 119 insertions(+), 120 deletions(-) diff --git a/rules.js b/rules.js index 48d5a62..6e9d678 100644 --- a/rules.js +++ b/rules.js @@ -14,14 +14,8 @@ // TODO: BUILDUP // TODO: setup scenario specials -// TODO: clean up "can retreat" checks // TODO: when is "fired" status cleared? -// TODO: cache_valid, cache_axis, cache_allied (presence and disruption per hex) -// instead of iterating units in is_axis_hex, etc. -// invalidate when loading state -// invalidate when disrupting, recovering, eliminating or moving units - // TODO: cache supply lines // same as presence cache @@ -41,33 +35,6 @@ var view = null const { all_hexes, hex_exists, hex_road, side_road, side_limit, hex_name, units, regions } = require("./data") -const first_axis_unit = 0 -const first_allied_unit = units.findIndex(item => item.nationality === 'allied') -const last_axis_unit = first_allied_unit - 1 -const last_allied_unit = units.length - 1 - -var first_friendly_unit, last_friendly_unit -var first_enemy_unit, last_enemy_unit - -function update_aliases() { - if (game.active === AXIS) { - first_friendly_unit = first_axis_unit - last_friendly_unit = last_axis_unit - first_enemy_unit = first_allied_unit - last_enemy_unit = last_allied_unit - } else { - first_friendly_unit = first_allied_unit - last_friendly_unit = last_allied_unit - first_enemy_unit = first_axis_unit - last_enemy_unit = last_axis_unit - } -} - -function load_state(state) { - game = state - update_aliases() -} - function debug_hexes3(n, list) { console.log("--", n, "--") list = list.map((x,i) => hex_exists[i] ? x : "") @@ -89,26 +56,19 @@ function debug_hexes(n, list) { console.log("".padStart(y," ") + list.slice(y*hexw, (y+1)*hexw).map(x=>String(x).padStart(2, ' ')).join("")) } -// Card deck has 42 cards, of which 28 are supply cards, and 14 are dummy cards. -// Represent draw pile and hands as [ dummy_supply_count, real_supply_count ] -const REAL_SUPPLY_COUNT = 28 -const DUMMY_SUPPLY_COUNT = 14 +const AXIS = 'Axis' +const ALLIED = 'Allied' const hexw = 25 const hexh = 9 - const first_hex = 7 const last_hex = 215 - const hexdeploy = hexw * hexh const hexnext = [ 1, hexw, hexw-1, -1, -hexw, -(hexw-1) ] - const hexcount = last_hex + 1 const sidecount = hexcount * 3 -const AXIS = 'Axis' -const ALLIED = 'Allied' - +const class_name = [ "armor", "infantry", "anti-tank", "artillery" ] const firepower_name = [ "0", "1", "2", "3", "TF", "DF", "SF" ] const speed_name = [ "zero", "leg", "motorized", "mechanized", "recon" ] @@ -116,13 +76,17 @@ const SF = 6 const DF = 5 const TF = 4 -const class_name = [ "armor", "infantry", "anti-tank", "artillery" ] - const ARMOR = 0 const INFANTRY = 1 const ANTITANK = 2 const ARTILLERY = 3 +const TRAIL = 1 +const TRACK = 2 +const HIGHWAY = 4 + +const SUPPLY_RANGE = [ 1, 2, 3, -1, 3 ] + const FIREPOWER_MATRIX = [ [ SF, DF, SF, TF ], [ SF, SF, SF, TF ], @@ -130,26 +94,26 @@ const FIREPOWER_MATRIX = [ [ SF, DF, DF, SF ], ] -const CLEAR = 2 -const PASS = 1 -const ROUGH = 0 - -const NO_ROAD = 0 -const TRAIL = 1 -const TRACK = 2 -const HIGHWAY = 4 - -const SUPPLY_RANGE = [ 1, 2, 3, -1, 3 ] - const EL_AGHEILA = 151 const ALEXANDRIA = 74 const BENGHAZI = 54 const TOBRUK = 37 const BARDIA = 40 -const FT_CAPUZZO = 64 const MERSA_BREGA = 152 +const JALO_OASIS = 204 +const JARABUB_OASIS = 187 +const SIWA_OASIS = 213 + const BARDIA_FT_CAPUZZO = 122 +const SS_NONE = 0 +const SS_EL_AGHEILA = 1 +const SS_ALEXANDRIA = 2 +const SS_BARDIA = 3 +const SS_BENGHAZI = 4 +const SS_TOBRUK = 5 +const SS_OASIS = 6 + const hex_from_supply_source = [ 0, EL_AGHEILA, ALEXANDRIA, BARDIA, BENGHAZI, TOBRUK ] function supply_source_from_hex(hex) { @@ -164,8 +128,6 @@ function supply_source_from_hex(hex) { } const region_egypt = regions["Egypt"] -const region_el_agheila = regions["El Agheila"] -const region_tobruk = regions["Tobruk"] const region_egypt_and_libya = regions["Libya"].concat(regions["Egypt"]) const region_libya_and_sidi_omar = regions["Libya"].concat(regions["Sidi Omar"]) const region_libya_and_sidi_omar_and_sollum = regions["Libya"].concat(regions["Sidi Omar"]).concat(regions["Sollum"]) @@ -227,6 +189,42 @@ function is_hex_or_adjacent_to(x, where) { return false } +// === STATE CACHES === + +const first_axis_unit = 0 +const first_allied_unit = units.findIndex(item => item.nationality === 'allied') +const last_axis_unit = first_allied_unit - 1 +const last_allied_unit = units.length - 1 + +var presence_invalid = true +var presence_axis = new Array(hexcount).fill(0) +var presence_allied = new Array(hexcount).fill(0) + +var first_friendly_unit, last_friendly_unit +var first_enemy_unit, last_enemy_unit + +function update_aliases() { + if (game.active === AXIS) { + first_friendly_unit = first_axis_unit + last_friendly_unit = last_axis_unit + first_enemy_unit = first_allied_unit + last_enemy_unit = last_allied_unit + } else { + first_friendly_unit = first_allied_unit + last_friendly_unit = last_allied_unit + first_enemy_unit = first_axis_unit + last_enemy_unit = last_axis_unit + } +} + +function load_state(state) { + if (game !== state) { + game = state + presence_invalid = true + update_aliases() + } +} + // === UNIT STATE === // location (8 bits), supply source (3 bits), steps lost (2 bits), disrupted (1 bit) @@ -261,10 +259,12 @@ function is_unit_disrupted(u) { } function set_unit_disrupted(u) { + presence_invalid = true game.units[u] |= UNIT_DISRUPTED_MASK } function clear_unit_disrupted(u) { + presence_invalid = true game.units[u] &= ~UNIT_DISRUPTED_MASK } @@ -273,6 +273,7 @@ function unit_hex(u) { } function set_unit_hex(u, x) { + presence_invalid = true game.units[u] = (game.units[u] & ~UNIT_HEX_MASK) | (x << UNIT_HEX_SHIFT) } @@ -418,94 +419,93 @@ function is_axis_unit(u) { // === MAP STATE === -function has_axis_unit(x) { +function update_presence() { + console.log("UPDATE PRESENCE") + presence_invalid = false + presence_axis.fill(0) for (let u = first_axis_unit; u <= last_axis_unit; ++u) - if (unit_hex(u) === x) - return true - return false + if (is_unit_disrupted(u)) + presence_axis[unit_hex(u)] |= 1 + else + presence_axis[unit_hex(u)] |= 2 + presence_allied.fill(0) + for (let u = first_allied_unit; u <= last_allied_unit; ++u) + if (is_unit_disrupted(u)) + presence_allied[unit_hex(u)] |= 1 + else + presence_allied[unit_hex(u)] |= 2 +} + +function has_axis_unit(x) { + if (presence_invalid) + update_presence() + return presence_axis[x] !== 0 } function has_allied_unit(x) { - for (let u = first_allied_unit; u <= last_allied_unit; ++u) - if (unit_hex(u) === x) - return true - return false + if (presence_invalid) + update_presence() + return presence_allied[x] !== 0 } function has_undisrupted_axis_unit(x) { - for (let u = first_axis_unit; u <= last_axis_unit; ++u) - if (!is_unit_disrupted(u) && unit_hex(u) === x) - return true - return false + if (presence_invalid) + update_presence() + return (presence_axis[x] & 2) !== 0 } function has_disrupted_axis_unit(x) { - for (let u = first_axis_unit; u <= last_axis_unit; ++u) - if (is_unit_disrupted(u) && unit_hex(u) === x) - return true - return false + if (presence_invalid) + update_presence() + return (presence_axis[x] & 1) !== 0 } function has_undisrupted_allied_unit(x) { - for (let u = first_allied_unit; u <= last_allied_unit; ++u) - if (!is_unit_disrupted(u) && unit_hex(u) === x) - return true - return false + if (presence_invalid) + update_presence() + return (presence_allied[x] & 2) !== 0 } function has_disrupted_allied_unit(x) { - for (let u = first_allied_unit; u <= last_allied_unit; ++u) - if (is_unit_disrupted(u) && unit_hex(u) === x) - return true - return false + if (presence_invalid) + update_presence() + return (presence_allied[x] & 1) !== 0 } function has_unshielded_disrupted_axis_unit(x) { - let undisrupted = false - let disrupted = false - for (let u = first_axis_unit; u <= last_axis_unit; ++u) - if (unit_hex(u) === x) - if (is_unit_disrupted(u)) - disrupted = true - else - undisrupted = true - return disrupted && !undisrupted + if (presence_invalid) + update_presence() + return presence_axis[x] === 1 } function has_unshielded_disrupted_allied_unit(x) { - let undisrupted = false - let disrupted = false - for (let u = first_allied_unit; u <= last_allied_unit; ++u) - if (unit_hex(u) === x) - if (is_unit_disrupted(u)) - disrupted = true - else - undisrupted = true - return disrupted && !undisrupted + if (presence_invalid) + update_presence() + return presence_allied[x] === 1 } function is_axis_hex(x) { - let has_axis = has_axis_unit(x) - let has_allied = has_allied_unit(x) - return has_axis && !has_allied + if (presence_invalid) + update_presence() + return (presence_axis[x] !== 0) && (presence_allied[x] === 0) } function is_allied_hex(x) { - let has_axis = has_axis_unit(x) - let has_allied = has_allied_unit(x) - return !has_axis && has_allied + if (presence_invalid) + update_presence() + return (presence_axis[x] === 0) && (presence_allied[x] !== 0) } function is_battle_hex(x) { - let has_axis = has_axis_unit(x) - let has_allied = has_allied_unit(x) - return has_axis && has_allied + if (presence_invalid) + update_presence() + return (presence_axis[x] !== 0) && (presence_allied[x] !== 0) } function is_empty_hex(x) { - let has_axis = has_axis_unit(x) - let has_allied = has_allied_unit(x) - return !has_axis && !has_allied + if (presence_invalid) + update_presence() + return (presence_axis[x] === 0) && (presence_allied[x] === 0) } function has_friendly_unit(x) { @@ -2444,9 +2444,7 @@ function goto_rout(from, enemy, after) { if (enemy) set_enemy_player() - game.pursuit = from game.state = 'rout_attrition' - game.flash = "Rout attrition!" } states.rout_attrition = { @@ -3780,7 +3778,7 @@ exports.setup = function (seed, scenario, options) { scenario: scenario, month: 0, - draw_pile: [ DUMMY_SUPPLY_COUNT, REAL_SUPPLY_COUNT ], + draw_pile: [ 14, 28 ], // 14 dummy supply + 28 real supply axis_hand: [ 0, 0 ], allied_hand: [ 0, 0 ], @@ -4022,10 +4020,11 @@ function push_undo() { function pop_undo() { let save_log = game.log let save_undo = game.undo - game = save_undo.pop() - save_log.length = game.log - game.log = save_log - game.undo = save_undo + let state = save_undo.pop() + save_log.length = state.log + state.log = save_log + state.undo = save_undo + load_state(state) } function clear_undo() { -- cgit v1.2.3