diff options
-rw-r--r-- | rules.js | 198 |
1 files changed, 109 insertions, 89 deletions
@@ -1111,6 +1111,8 @@ function print_path(who, from, to, road) { // retreat move: must leave battle hex via friendly side. may ignore disrupted enemy. may move freely. // retreat withdrawal: must leave battle hex via friendly side. may ignore disrupted enemy. must follow supply lines. +// TODO: cache search results from previous invocation + function search_move(start, speed) { // Normal moves. search_init() @@ -1893,32 +1895,26 @@ function gen_move(search_fn) { search_fn(from, max(speed + rommel1, speed + rommel2)) - if (!game.to1 && game.from1) { - if (from === game.from1) { - for (let to of all_hexes) - if (to != from && can_move_to(to, speed + rommel1)) - gen_action_hex(to) - } + if (!game.to1 && game.from1 === from) { + for (let to of all_hexes) + if (to != from && can_move_to(to, speed + rommel1)) + gen_action_hex(to) } - if (!game.to2 && game.from2) { - if (from === game.from2) { - for (let to of all_hexes) - if (to != from && can_move_to(to, speed + rommel2)) - gen_action_hex(to) - } + if (!game.to2 && game.from2 === from) { + for (let to of all_hexes) + if (to != from && can_move_to(to, speed + rommel2)) + gen_action_hex(to) } - if (game.to1) { - if (is_hex_or_adjacent_to(from, game.from1)) - if (can_move_to(game.to1, speed + rommel1)) - gen_action_hex(game.to1) + if (game.to1 && is_hex_or_adjacent_to(from, game.from1)) { + if (can_move_to(game.to1, speed + rommel1)) + gen_action_hex(game.to1) } - if (game.to2) { - if (is_hex_or_adjacent_to(from, game.from2)) - if (can_move_to(game.to2, speed + rommel2)) - gen_action_hex(game.to2) + if (game.to2 && is_hex_or_adjacent_to(from, game.from2)) { + if (can_move_to(game.to2, speed + rommel2)) + gen_action_hex(game.to2) } } @@ -1994,93 +1990,110 @@ function friendly_unit_supply_distance(who) { return is_axis_player() ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA] } -function can_unit_disengage_and_withdraw(who) { - let sline = friendly_unit_supply_line(who) - let sdist = friendly_unit_supply_distance(who) - let from = unit_hex(who) +function is_valid_retreat_hex(from) { + if (game.turn_option === 'pass') + return can_all_retreat(from) + else + return can_any_retreat(from) +} + +function can_any_retreat(from) { let result = false - for_each_adjacent_hex(from, to => { - let side = to_side_id(from, to) - if (is_friendly_hexside(side) && !has_enemy_unit(to)) - if (sline[side] && sdist[to] <= sdist[from]) + for_each_undisrupted_friendly_unit_in_hex(from, u => { + if (result === false) { + if (can_unit_retreat(u)) result = true + } }) return result } -function can_all_disengage_and_withdraw(from) { +function can_all_retreat(from) { let result = true for_each_undisrupted_friendly_unit_in_hex(from, u => { - if (result && !can_unit_disengage_and_withdraw(u)) + if (result === true && !can_unit_retreat(u)) result = false }) return result } -function can_disengage_and_move(from) { - let result = false - for_each_adjacent_hex(from, to => { - let side = to_side_id(from, to) - if (is_friendly_hexside(side) && !has_enemy_unit(to)) - result = true - }) - return result +function can_unit_retreat(who) { + let from = unit_hex(who) + if (!game.to1 && game.from1 === from) + return can_unit_retreat_group_move(from) + if (!game.to2 && game.from2 === from) + return can_unit_retreat_group_move(from) + if (game.to1 && is_hex_or_adjacent_to(from, game.from1)) + if (can_unit_retreat_regroup_move(u, game.to1, rommel1)) + return true + if (game.to2 && is_hex_or_adjacent_to(from, game.from2)) + if (can_unit_retreat_regroup_move(u, game.to2, rommel2)) + return true + return false } -function can_retreat_with_group_move(from) { - if (!is_battle_hex(from) || set_has(game.partial_retreats, from)) - return false +function can_unit_retreat_group_move(who) { + if (game.turn_option === 'pass') + return can_unit_disengage_and_withdraw(who) + else + return can_unit_disengage_and_move(who) +} +function can_unit_retreat_regroup_move(who, to, rommel) { if (game.turn_option === 'pass') - return can_all_disengage_and_withdraw(from) + return can_unit_disengage_and_withdraw_to(who, to, rommel) else - return can_disengage_and_move(from) + return can_unit_disengage_and_move_to(who, to, rommel) } -function can_all_disengage_and_move_to(from) { - let result = true - for_each_undisrupted_friendly_unit_in_hex(from, u => { - if (result && !can_unit_disengage_and_withdraw(u)) - result = false +function can_unit_disengage_and_withdraw(who) { + let sline = friendly_unit_supply_line(who) + let sdist = friendly_unit_supply_distance(who) + let from = unit_hex(who) + let result = false + for_each_adjacent_hex(from, to => { + let side = to_side_id(from, to) + if (is_friendly_hexside(side) && !has_enemy_unit(to)) + if (sline[side] && sdist[to] <= sdist[from]) + result = true }) return result } -function can_retreat_with_regroup_move(from, to, rommel) { - if (!is_battle_hex(from) || set_has(game.partial_retreats, from)) - return false +function can_unit_disengage_and_move(who) { + let from = unit_hex(who) + let result = false + for_each_adjacent_hex(from, to => { + let side = to_side_id(from, to) + if (is_friendly_hexside(side) && !has_enemy_unit(to)) + result = true + }) + return result +} - let speed = max_speed_of_undisrupted_and_unmoved_friendly_unit_in_hex(from) - if (game.turn_option === 'pass') - search_withdraw_retreat(from, speed + rommel) - else - search_move_retreat(from, speed + rommel) +function can_unit_disengage_and_withdraw_to(who, to, rommel) { + search_withdraw_retreat(unit_hex(who), unit_speed(who) + rommel) + return can_move_to(to, unit_speed(who) + rommel) +} - if (game.turn_option === 'pass') - return can_all_disengage_and_move_to(from, to, rommel) - else - return can_any_disengage_and_move_to(from, to, rommel) +function can_unit_disengage_and_move_to(who, to, rommel) { + search_move_retreat(unit_hex(who), unit_speed(who) + rommel) + return can_move_to(to, unit_speed(who) + rommel) } function can_select_retreat_hex() { - 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] - let rommel1 = (game.rommel === 1) ? 1 : 0 - let rommel2 = (game.rommel === 2) ? 1 : 0 - if (!game.to1 && game.from1) - if (can_retreat_with_group_move(game.from1) + if (is_valid_retreat_hex(game.from1)) return true if (!game.to2 && game.from2) - if (can_retreat_with_group_move(game.from2) + if (is_valid_retreat_hex(game.from2)) return true if (game.to1) { let result = false for_each_hex_and_adjacent_hex(game.from1, x => { - if (can_retreat_with_regroup_move(x, game.to1, rommel1)) + if (result === false && is_valid_retreat_hex(x)) result = true }) if (result) @@ -2090,7 +2103,7 @@ function can_select_retreat_hex() { if (game.to2) { let result = false for_each_hex_and_adjacent_hex(game.from2, x => { - if (can_retreat_with_regroup_move(x, game.to2, rommel2)) + if (result === false && is_valid_retreat_hex(x)) result = true }) if (result) @@ -2104,32 +2117,26 @@ states.retreat_from = { prompt() { view.prompt = `Retreat: Select hex to retreat from.` - 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] - let rommel1 = (game.rommel === 1) ? 1 : 0 - let rommel2 = (game.rommel === 2) ? 1 : 0 - if (!game.to1 && game.from1) { - if (can_retreat_with_group_move(game.from1, sline, sdist)) + if (is_valid_retreat_hex(game.from1)) gen_action_hex(game.from1) } if (!game.to2 && game.from2) { - if (can_retreat_with_group_move(game.from2, sline, sdist)) + if (is_valid_retreat_hex(game.from2)) gen_action_hex(game.from2) } if (game.to1) { for_each_hex_and_adjacent_hex(game.from1, x => { - if (can_retreat_with_regroup_move(x, game.to1, rommel1)) + if (is_valid_retreat_hex(x)) gen_action_hex(x) }) } if (game.to2) { for_each_hex_and_adjacent_hex(game.from2, x => { - if (can_retreat_with_regroup_move(x, game.to2, rommel2)) + if (is_valid_retreat_hex(x)) gen_action_hex(x) }) } @@ -2156,10 +2163,10 @@ states.retreat_who = { for_each_undisrupted_friendly_unit_in_hex(game.retreat, u => { if (!set_has(game.retreat_units, u)) full_retreat = false - // TODO: has available retreat path XXX - // TODO: can reach regroup destination? - gen_action_unit(u) + if (can_unit_retreat(u)) + gen_action_unit(u) }) + if (full_retreat) { view.actions.retreat = 1 } else { @@ -2176,9 +2183,9 @@ states.retreat_who = { }, select_all() { for_each_undisrupted_friendly_unit_in_hex(game.retreat, u => { - // TODO: has available retreat path XXX - // TODO: can reach regroup destination? - set_add(game.retreat_units, u) + if (!set_has(game.retreat_units, u)) + if (can_unit_retreat(u)) + set_add(game.retreat_units, u) }) }, retreat() { @@ -2323,12 +2330,25 @@ function goto_refuse_battle() { } } +function can_all_refuse_battle(from) { + let result = true + for_each_undisrupted_friendly_unit_in_hex(from, u => { + if (result === true && !can_unit_refuse_battle(u)) + result = false + }) + return result +} + +function can_unit_refuse_battle(who) { + return can_unit_disengage_and_withdraw(who) +} + states.refuse_battle = { inactive: "refuse battle", prompt() { view.prompt = `You may Refuse Battle.` for (let x of game.active_battles) - if (can_all_disengage_and_withdraw(x)) + if (can_all_refuse_battle(x)) gen_action_hex(x) gen_action('next') }, @@ -2653,9 +2673,9 @@ 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.revealed_minefields) { + if (set_has(game.revealed_minefields)) { // DD advantage is lost if the defender initiated combat - if (is_axis_player() + if (is_axis_player()) return set_has(game.allied_hexes, game.battle) else return set_has(game.axis_hexes, game.battle) |