From 11b2796d2ce2e3067b6507997df6ba61119a78a3 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Mon, 29 Aug 2022 00:29:59 +0200 Subject: Pass withdrawal permutations. --- rules.js | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 146 insertions(+), 18 deletions(-) diff --git a/rules.js b/rules.js index 090918d..007264d 100644 --- a/rules.js +++ b/rules.js @@ -828,7 +828,7 @@ function fortress_src(fortress) { } function is_fortress_allied_controlled(fortress) { - return (game.fortress & fortress_bit(fortress)) === 1 + return (game.fortress & fortress_bit(fortress)) !== 0 } function is_fortress_axis_controlled(fortress) { @@ -1605,6 +1605,15 @@ function query_friendly_supply_network_from_to(src, x, y) { return supply_temp_network } +function query_friendly_supply_network(src) { + if (is_axis_player()) + init_trace_supply(supply_temp_network, supply_temp_line, AXIS) + else + init_trace_supply(supply_temp_network, supply_temp_line, ALLIED) + trace_supply_network(src) + return supply_temp_network +} + // === PATHING === const path_from = [ new Array(hexcount), new Array(hexcount), new Array(hexcount), null, new Array(hexcount) ] @@ -2074,25 +2083,138 @@ function is_valid_withdrawal_regroup_move_from(x) { return 0 } +function is_valid_withdrawal_regroup_permutation(src) { + let net = src_supply_network(src) + let new_net = query_friendly_supply_network(src) + return is_network_reduced(net, new_net) +} + function list_valid_withdrawal_regroup_command_points() { let always = [] - let maybe = [] + let test = [] + let maybe = null for (let x of all_hexes) { let status = is_valid_withdrawal_regroup_move_from(x) if (status === 2) always.push(x) else if (status === 1) - maybe.push(x) + test.push(x) } - console.log("WITHDRAW REGROUP CANDIDATES", always, maybe) - // TODO: list valid permutations of 'maybe' hexes + console.log("WITHDRAW REGROUP CANDIDATES", always, test) + if (is_axis_player()) + maybe = list_withdrawal_permutations(EL_AGHEILA, test) + else + maybe = list_withdrawal_permutations(ALEXANDRIA, test) + // TODO: fortress supply return { always, maybe, to: null, evacuate: null } } +function count_bits(v) { + let c = 0 + for (; v; ++c) + v &= v - 1 + return c +} + +function list_withdrawal_permutations(src, maybe) { + let rommel1 = (game.rommel === 1) ? 1 : 0 + let result = {} + let hexes + + // impossible... + if (maybe.length < 2) + return + + console.log("LIST WITHDRAWAL REGROUPS", maybe) + + // List all possible command points with more than one 'maybe' hex + let cmd_maybe = {} + let cmd_points = {} + for (let here of all_hexes) { + if (!is_enemy_hex(here)) { + hexes = null + for_each_hex_and_adjacent_hex(here, x => { + if (set_has(maybe, x) && hex_supply_source(x) === src) { + if (!hexes) + hexes = [] + set_add(hexes, x) + } + }) + if (hexes && hexes.length > 1) { + let key = hexes.join(",") + cmd_maybe[key] = hexes + if (!(key in cmd_points)) + cmd_points[key] = [] + cmd_points[key].push(here) + } + } + } + + // For each unique set of "maybe" command points, + // find all permutations of removing more than one "maybe" hex. + if (presence_invalid) + update_presence() + let presence_friendly = is_axis_player() ? presence_axis : presence_allied + + for (let key in cmd_maybe) { + hexes = cmd_maybe[key] + let n = hexes.length + let n2 = 1 << n + path_valid.fill(1) + for (let bits = 3; bits < n2; ++bits) { + if (count_bits(bits) >= 2) { + // Evacuate presence for testing + for (let i = 0; i < n; ++i) + if (bits & (1 << i)) + presence_friendly[hexes[i]] &= 1 // clear 'undisrupted' + + // Find the hexes that all units in the evacuated hexes can reach + for (let i = 0; i < n; ++i) { + if (bits & (1 << i)) { + let from = hexes[i] + let who = slowest_undisrupted_friendly_unit(from) + let speed = unit_speed[who] + search_withdraw(who, 1 + rommel1) + for (let to = 0; to < hexcount; ++to) + if (!can_move_to(to, speed + 1 + rommel1)) + path_valid[to] = 0 + } + } + + // Test supply net for all possible destinations + for (let x of all_hexes) { + if (path_valid[x]) { + let save_x = presence_friendly[x] + presence_friendly[x] = 2 + if (is_valid_withdrawal_regroup_permutation(src)) { + for (let here of cmd_points[key]) { + if (!(here in result)) + result[here] = [] + set_add(result[here], x) + } + } + presence_friendly[x] = save_x + } + } + + // Restore presence + for (let i = 0; i < n; ++i) + if (bits & (1 << i)) + presence_friendly[hexes[i]] |= 2 // set 'undisrupted' + } + } + } + + console.log("MAYBE", result) + return result +} + function gen_withdrawal_regroup_command_point() { var m, n for (let here of all_hexes) { - if (!is_enemy_hex(here)) { + if (here in game.withdraw.maybe) + gen_action_hex(here) + else if (!is_enemy_hex(here)) { m = n = 0 for_each_hex_and_adjacent_hex(here, x => { // Must include at least one valid withdrawal hex to evacuate fully @@ -2112,9 +2234,6 @@ function gen_withdrawal_regroup_command_point() { function list_valid_withdrawal_regroup_destinations() { let rommel1 = (game.rommel === 1) ? 1 : 0 - // TODO: list hexes that can be reached by ALL units of one valid permutation of maybe-hexes - // remember - // Find hexes that can be reached by ALL units of ONE always-hex // ... that also reduces the network (either by full retreat, or checking move) let result = [] @@ -2134,7 +2253,7 @@ function list_valid_withdrawal_regroup_destinations() { let who = slowest_undisrupted_friendly_unit(from) let speed = unit_speed[who] let src = unit_supply_source(who) - let net = unit_supply_network(who) // TODO: remembered network + let net = unit_supply_network(who) search_withdraw(who, 1 + rommel1) for (let to of all_hexes) { if (to != from && can_move_to(to, speed + 1 + rommel1)) { @@ -2146,13 +2265,16 @@ function list_valid_withdrawal_regroup_destinations() { } }) + // List hexes that can be reached by ALL units of one valid permutation of maybe-hexes. + if (game.from1 in game.withdraw.maybe) { + for (let to of game.withdraw.maybe[game.from1]) + set_add(result, to) + } + game.withdraw.to = result } function gen_withdrawal_regroup_destination() { - // XXX - list_valid_withdrawal_regroup_destinations() - // XXX for (let x of game.withdraw.to) gen_action_hex(x) } @@ -2505,6 +2627,7 @@ const xxx_fortress_supply = { unit(who) { let ix = game.assign let ss = FORTRESS_SRC_LIST[ix] + let fortress = FORTRESS_HEX_LIST[ix] push_undo() game.capacity[ix]-- log(`Assigned #${fortress} supply.`) @@ -2556,7 +2679,7 @@ function assign_oasis_supply() { if (n === 1) { for_each_friendly_unit_in_hex(oasis, u => { game.oasis[ix] = 0 - log(`Assigned #${fortress} supply.`) + log(`Assigned #${oasis} supply.`) set_unit_supply(u, SS_OASIS) }) } @@ -2577,9 +2700,10 @@ const xxx_oasis_supply = { }, unit(who) { let ix = game.assign + let oasis = OASIS_HEX_LIST[ix] push_undo() game.oasis[ix] = 0 - log(`Assigned #${fortress} supply.`) + log(`Assigned #${oasis} supply.`) set_unit_supply(who, SS_OASIS) game.assign++ resume_oasis_supply() @@ -2768,7 +2892,9 @@ function push_move_summary(from, to, via, forced) { } function flush_move_summary() { - if (!game.from2 && !game.to1) { + if (!game.from1 && !game.from2) { + log(`Passed.`) + } else if (!game.from2 && !game.to1) { log(`Moved from #${game.from1}`) } else if (!game.from2 && game.to1) { log(`Moved to #${game.to1}`) @@ -4427,7 +4553,7 @@ function is_fortress_defensive_fire() { function is_minefield_offensive_fire() { if ((game.state === 'battle_fire' && is_active_player()) || (game.state === 'probe_fire' && is_passive_player())) { - if (set_has(game.minefields[MF_VISIBLE])) { + if (set_has(game.minefields[MF_VISIBLE], game.battle)) { // DD advantage is lost if the defender initiated combat if (is_axis_player()) return set_has(game.allied_hexes, game.battle) @@ -5283,7 +5409,7 @@ function goto_buildup_point_determination() { log(`Allied rolled ${allied_a} + ${allied_b}.`) } - log(`Receive ${axis + allied} BPs.`) + log(`Received ${axis + allied} BPs.`) game.axis_bps += axis + allied game.allied_bps += axis + allied @@ -5614,6 +5740,8 @@ states.spending_bps = { pay_bps(n - 20) } + log(`Saved ${available_bps()} BPs.`) + end_buildup_spending() } } -- cgit v1.2.3