From 1ce07e1aa4bb1efd9831dccd3f72d176cac943b4 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 27 Jul 2022 12:58:58 +0200 Subject: Use cache for supply lines. --- rules.js | 261 ++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 140 insertions(+), 121 deletions(-) (limited to 'rules.js') diff --git a/rules.js b/rules.js index 6e9d678..a37f8e0 100644 --- a/rules.js +++ b/rules.js @@ -200,6 +200,14 @@ var presence_invalid = true var presence_axis = new Array(hexcount).fill(0) var presence_allied = new Array(hexcount).fill(0) +var supply_axis_invalid = true +var supply_axis_network = new Array(hexcount).fill(0) +var supply_axis_line = new Array(sidecount).fill(0) + +var supply_allied_invalid = true +var supply_allied_network = new Array(hexcount).fill(0) +var supply_allied_line = new Array(sidecount).fill(0) + var first_friendly_unit, last_friendly_unit var first_enemy_unit, last_enemy_unit @@ -221,6 +229,8 @@ function load_state(state) { if (game !== state) { game = state presence_invalid = true + supply_axis_invalid = true + supply_allied_invalid = true update_aliases() } } @@ -260,11 +270,15 @@ function is_unit_disrupted(u) { function set_unit_disrupted(u) { presence_invalid = true + supply_axis_invalid = true + supply_allied_invalid = true game.units[u] |= UNIT_DISRUPTED_MASK } function clear_unit_disrupted(u) { presence_invalid = true + supply_axis_invalid = true + supply_allied_invalid = true game.units[u] &= ~UNIT_DISRUPTED_MASK } @@ -274,6 +288,8 @@ function unit_hex(u) { function set_unit_hex(u, x) { presence_invalid = true + supply_axis_invalid = true + supply_allied_invalid = true game.units[u] = (game.units[u] & ~UNIT_HEX_MASK) | (x << UNIT_HEX_SHIFT) } @@ -616,7 +632,11 @@ function claim_hex_control_for_defender(a) { function capture_fortress(fortress, capacity, control_prop, captured_prop) { if (game[control_prop] !== game.active) { if (has_undisrupted_friendly_unit(fortress) && !has_enemy_unit(fortress)) { + supply_axis_invalid = true + supply_allied_invalid = true + log(`Captured #${fortress}!`) + game[control_prop] = game.active if (!game[captured_prop]) { game[captured_prop] = 1 @@ -839,37 +859,13 @@ function deal_allied_supply_cards(n) { // === SUPPLY NETWORK === -function is_side_unit(side, u) { - if (side === AXIS) - return is_axis_unit(u) - return is_allied_unit(u) -} - -function list_control_hexes(control, first_unit, last_unit) { - control.fill(0) - for (let u = first_unit; u <= last_unit; ++u) { - let x = unit_hex(u) - if (x >= first_hex && x <= last_hex) - if (is_unit_disrupted(u)) - control[x] |= 1 - else - control[x] |= 2 - } - return control -} - function ind(d, msg, here, ...extra) { console.log(new Array(d).fill("-").join("") + msg, here, "("+hex_name[here]+")", extra.join(" ")) } -var supply_axis = new Array(hexcount) -var supply_allied = new Array(hexcount) -var supply_defender -var supply_defender_sides -var supply_visited = new Array(hexcount) -var supply_src = new Array(hexcount) - -var supply_friendly, supply_enemy, supply_src, supply_net, supply_line +var supply_defender, supply_defender_sides, supply_friendly, supply_enemy, supply_net, supply_line +var supply_visited = new Array(hexcount).fill(0) +var supply_src = new Array(hexcount).fill(0) var trace_highway var trace_chain @@ -1024,9 +1020,6 @@ function trace_supply_chain(here, d, n, range) { } function trace_supply_network(start) { - supply_net = new Array(hexcount) - supply_line = new Array(sidecount) - supply_visited.fill(0) supply_src.fill(0) supply_net.fill(0) @@ -1056,38 +1049,91 @@ function trace_supply_network(start) { debug_hexes("SN", supply_net) } -function update_axis_supply_network() { +function update_axis_supply() { + supply_axis_invalid = false + if (presence_invalid) + update_presence() + + supply_net = supply_axis_network + supply_line = supply_axis_line supply_defender = game.axis_hexes supply_defender_sides = game.axis_sides - supply_friendly = supply_axis - supply_enemy = supply_allied + supply_friendly = presence_axis + supply_enemy = presence_allied + trace_supply_network(EL_AGHEILA) - game.axis_supply = supply_net - game.axis_supply_line = supply_line } -function update_allied_supply_network() { +function update_allied_supply() { + supply_allied_invalid = false + if (presence_invalid) + update_presence() + + supply_net = supply_allied_network + supply_line = supply_allied_line supply_defender = game.allied_hexes supply_defender_sides = game.allied_sides - supply_friendly = supply_allied - supply_enemy = supply_axis + supply_friendly = presence_allied + supply_enemy = presence_axis + trace_supply_network(ALEXANDRIA) - game.allied_supply = supply_net - game.allied_supply_line = supply_line } -function update_supply_networks() { - list_control_hexes(supply_axis, first_axis_unit, last_axis_unit) - list_control_hexes(supply_allied, first_allied_unit, last_allied_unit) - update_axis_supply_network() - update_allied_supply_network() +function update_supply() { + if (supply_axis_invalid) + update_axis_supply() + if (supply_allied_invalid) + update_allied_supply() +} + +function axis_supply_line() { + if (supply_axis_invalid) + update_axis_supply() + return supply_axis_line } -function clear_supply_networks() { - game.axis_supply = null - game.axis_supply_line = null - game.allied_supply = null - game.allied_supply_line = null +function axis_supply_network() { + if (supply_axis_invalid) + update_axis_supply() + return supply_axis_network +} + +function allied_supply_line() { + if (supply_allied_invalid) + update_allied_supply() + return supply_allied_line +} + +function allied_supply_network() { + if (supply_allied_invalid) + update_allied_supply() + return supply_allied_network +} + +function unit_supply_line(who) { + // TODO: fortress supply + if (is_axis_unit(who)) + return axis_supply_line() + return allied_supply_line() +} + +function unit_supply_distance(who) { + // TODO: fortress supply + if (is_axis_unit(who)) + return distance_to[EL_AGHEILA] + return distance_to[ALEXANDRIA] +} + +function friendly_supply_base() { + if (is_axis_player()) + return EL_AGHEILA + return ALEXANDRIA +} + +function friendly_supply_network() { + if (is_axis_player()) + return axis_supply_network() + return allied_supply_network() } // === PATHING === @@ -1095,7 +1141,6 @@ function clear_supply_networks() { const path_from = [ new Array(hexcount), new Array(hexcount), new Array(hexcount), null, new Array(hexcount) ] const path_cost = [ new Array(hexcount), new Array(hexcount), new Array(hexcount), null, new Array(hexcount) ] const path_valid = new Array(hexcount) -const path_enemy = new Array(hexcount) function print_path(who, from, to, road) { let p = [ hex_name[to] ] @@ -1123,56 +1168,50 @@ function search_move(start, speed) { } function search_move_retreat(start, speed) { - search_init() search_move_bfs(path_from[0], path_cost[0], start, 0, speed, true, null, null) search_move_bfs(path_from[1], path_cost[1], start, 1, speed + 1, true, null, null) search_move_bfs(path_from[2], path_cost[2], start, 2, speed + 2, true, null, null) search_move_bfs(path_from[4], path_cost[4], start, 4, speed + 4, true, null, null) } -function search_withdraw(start, speed) { - // TODO: who+bonus instead of start+speed - update_supply_networks() - let sline = is_axis_player() ? game.axis_supply_line : game.allied_supply_line - let sdist = is_axis_player() ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA] +function search_withdraw(who, bonus) { + let sline = unit_supply_line(who) + let sdist = unit_supply_distance(who) + let speed = unit_speed(who) + bonus - search_init() search_move_bfs(path_from[0], path_cost[0], start, 0, speed, false, sline, sdist) search_move_bfs(path_from[1], path_cost[1], start, 1, speed + 1, false, sline, sdist) search_move_bfs(path_from[2], path_cost[2], start, 2, speed + 2, false, sline, sdist) search_move_bfs(path_from[4], path_cost[4], start, 4, speed + 4, false, sline, sdist) } -function search_withdraw_retreat(start, speed) { - // TODO: who+bonus instead of start+speed - update_supply_networks() - let sline = is_axis_player() ? game.axis_supply_line : game.allied_supply_line - let sdist = is_axis_player() ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA] +function search_withdraw_retreat(who, bonus) { + let sline = unit_supply_line(who) + let sdist = unit_supply_distance(who) + let speed = unit_speed(who) + bonus - search_init() search_move_bfs(path_from[0], path_cost[0], start, 0, speed, true, sline, sdist) search_move_bfs(path_from[1], path_cost[1], start, 1, speed + 1, true, sline, sdist) search_move_bfs(path_from[2], path_cost[2], start, 2, speed + 2, true, sline, sdist) search_move_bfs(path_from[4], path_cost[4], start, 4, speed + 4, true, sline, sdist) } -// Cache enemy presence function search_init() { - path_enemy.fill(0) - for_each_enemy_unit(u => { - let x = unit_hex(u) - if (x >= first_hex && x <= last_hex) { - if (is_unit_disrupted(u)) - path_enemy[x] |= 1 - else - path_enemy[x] |= 2 - } - }) } // Breadth First Search function search_move_bfs(from, cost, start, road, max_cost, retreat, sline, sdist) { - let friendly_sides = (is_axis_player()) ? game.axis_sides : game.allied_sides + let path_enemy, friendly_sides + + if (presence_invalid) + update_presence() + if (is_axis_player()) { + path_enemy = presence_axis + friendly_sides = game.axis_sides + } else { + path_enemy = presence_allied + friendly_sides = game.allied_sides + } from.fill(0) cost.fill(15) @@ -1440,9 +1479,8 @@ states.turn_option = { // === INITIAL & FINAL SUPPLY CHECK === function goto_initial_supply_check() { - update_supply_networks() - let snet = game.phasing === AXIS ? game.axis_supply : game.allied_supply - let ssrc = game.phasing === AXIS ? EL_AGHEILA : ALEXANDRIA + let snet = friendly_supply_network() + let ssrc = friendly_supply_base() // TODO: fortress supply // TODO: assign fortress supply @@ -1505,9 +1543,8 @@ function goto_final_supply_check() { capture_fortress(BENGHAZI, 2, "benghazi", "benghazi_captured") capture_fortress(TOBRUK, 5, "tobruk", "tobruk_captured") - update_supply_networks() - let snet = game.phasing === AXIS ? game.axis_supply : game.allied_supply - let ssrc = game.phasing === AXIS ? EL_AGHEILA : ALEXANDRIA + let snet = friendly_supply_network() + let ssrc = friendly_supply_base() // TODO: fortress supply // TODO: assign unused fortress supply @@ -1781,6 +1818,7 @@ states.move = { for_each_hex_and_adjacent_hex(game.from1, from => { let speed = max_speed_of_undisrupted_and_unmoved_friendly_unit_in_hex(from) if (speed > 0 && !has_enemy_unit(from)) { + // TODO: withdraw pass move search_move(from, speed + rommel1) for_each_undisrupted_and_unmoved_friendly_unit_in_hex(from, u => { if (can_move_to(game.to1, unit_speed(u) + rommel1)) @@ -1795,6 +1833,7 @@ states.move = { for_each_hex_and_adjacent_hex(game.from2, from => { let speed = max_speed_of_undisrupted_and_unmoved_friendly_unit_in_hex(from) if (speed > 0 && !has_enemy_unit(from)) { + // TODO: withdraw pass move search_move(from, speed + rommel2) for_each_undisrupted_and_unmoved_friendly_unit_in_hex(from, u => { if (can_move_to(game.to2, unit_speed(u) + rommel2)) @@ -1828,10 +1867,10 @@ states.move = { // Move if (game.turn_option === 'pass') - gen_move(search_withdraw) + search_withdraw(game.selected, (rommel1 | rommel2)) else - gen_move(search_move) - + search_move(unit_hex(game.selected), unit_speed(game.selected) + (rommel1 | rommel2)) + gen_move() } }, unit(who) { @@ -1887,14 +1926,12 @@ function goto_overrun(where) { goto_rout(where, true, null) } -function gen_move(search_fn) { +function gen_move() { let rommel1 = (game.rommel === 1) ? 1 : 0 let rommel2 = (game.rommel === 2) ? 1 : 0 let speed = unit_speed(game.selected) let from = unit_hex(game.selected) - search_fn(from, max(speed + rommel1, speed + rommel2)) - if (!game.to1 && game.from1 === from) { for (let to of all_hexes) if (to != from && can_move_to(to, speed + rommel1)) @@ -1933,7 +1970,7 @@ function apply_move(to) { // TODO: pick hex-side manually when engaging } - search_move(from, max(speed + rommel1, speed + rommel2)) + search_move(from, speed + (rommel1 | rommel2)) if (!game.to1 && game.from1 === from) if (can_move_to(to, speed + rommel1)) @@ -1981,15 +2018,6 @@ function move_unit(who, to, speed) { // === RETREAT === -function friendly_unit_supply_line(who) { - update_supply_networks() - return is_axis_player() ? game.axis_supply_line : game.allied_supply_line -} - -function friendly_unit_supply_distance(who) { - return is_axis_player() ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA] -} - function is_valid_retreat_hex(from) { if (game.turn_option === 'pass') return can_all_retreat(from) @@ -2047,8 +2075,8 @@ function can_unit_retreat_regroup_move(who, to, rommel) { } function can_unit_disengage_and_withdraw(who) { - let sline = friendly_unit_supply_line(who) - let sdist = friendly_unit_supply_distance(who) + let sline = unit_supply_line(who) + let sdist = unit_supply_distance(who) let from = unit_hex(who) let result = false for_each_adjacent_hex(from, to => { @@ -2072,7 +2100,7 @@ function can_unit_disengage_and_move(who) { } function can_unit_disengage_and_withdraw_to(who, to, rommel) { - search_withdraw_retreat(unit_hex(who), unit_speed(who) + rommel) + search_withdraw_retreat(who, rommel) return can_move_to(to, unit_speed(who) + rommel) } @@ -2269,11 +2297,14 @@ states.retreat_move = { if (done) gen_action('end_retreat') } else { + let rommel1 = (game.rommel === 1) ? 1 : 0 + let rommel2 = (game.rommel === 2) ? 1 : 0 gen_action_unit(game.selected) if (game.turn_option === 'pass') - gen_move(search_withdraw_retreat) + search_withdraw_retreat(game.selected, (rommel1 | rommel2)) else - gen_move(search_move_retreat) + search_move_retreat(unit_hex(game.selected), unit_speed(game.selected) + (rommel1 | rommel2)) + gen_move() } }, unit(who) { @@ -2387,7 +2418,7 @@ states.refuse_battle_move = { } else { let speed = unit_speed(game.selected) gen_action_unit(game.selected) - search_withdraw_retreat(game.refuse, speed) + search_withdraw_retreat(game.selected, 0) for (let to of all_hexes) if (to != game.refuse && can_move_to(to, speed)) gen_action_hex(to) @@ -2491,7 +2522,7 @@ states.rout_move = { } else { let speed = unit_speed(game.selected) let eliminate = true - search_withdraw_retreat(game.rout.from, speed) + search_withdraw_retreat(game.selected, 0) for (let to of all_hexes) { if (to != game.rout.from && can_move_to(to, speed)) { gen_action_hex(to) @@ -3796,12 +3827,6 @@ exports.setup = function (seed, scenario, options) { benghazi: ALLIED, tobruk: ALLIED, - // supply networks - axis_supply: null, - axis_supply_line: null, - allied_supply: null, - allied_supply_line: null, - // battle hexes (defender) axis_hexes: [], allied_hexes: [], @@ -3856,10 +3881,6 @@ exports.view = function(state, current) { axis_sides: game.axis_sides, allied_sides: game.allied_sides, selected: game.selected, - // axis_supply: game.axis_supply, - // axis_supply_line: game.axis_supply_line, - // allied_supply: game.allied_supply, - // allied_supply_line: game.allied_supply_line, } if (game.from1) view.from1 = game.from1 @@ -3880,18 +3901,16 @@ exports.view = function(state, current) { exports.query = function (state, current, q) { if (q === 'supply') { load_state(state) - update_supply_networks() return { - axis_supply: game.axis_supply, - axis_supply_line: game.axis_supply_line, - allied_supply: game.allied_supply, - allied_supply_line: game.allied_supply_line, + axis_supply: axis_supply_network(), + axis_supply_line: axis_supply_line(), + allied_supply: allied_supply_network(), + allied_supply_line: allied_supply_line(), } } return null } - function gen_action_next() { gen_action('next') } -- cgit v1.2.3