diff options
Diffstat (limited to 'rules.js')
-rw-r--r-- | rules.js | 390 |
1 files changed, 128 insertions, 262 deletions
@@ -957,52 +957,63 @@ function print_path(who, from, to, road) { log(">" + p.join(" - ") + ".") } -function search_move(start, start_cost, start_road) { - // recon=4, forced march=+1, rommel bonus=+1 - let limit = 6 - +function search_init() { path_enemy.fill(0) - for (let u = 0; u < units.length; ++u) { - if (is_enemy_unit(u)) { - let x = unit_hex(u) - if (x >= first_hex && x <= last_hex) - path_enemy[x] = 1 - } - } - - search_move_bfs(path_from[0], path_cost[0], start, start_cost, 0, limit) - if (start_road >= 1) - search_move_bfs(path_from[1], path_cost[1], start, start_cost, 1, limit + 1) - if (start_road >= 2) - search_move_bfs(path_from[2], path_cost[2], start, start_cost, 2, limit + 2) - if (start_road >= 4) - search_move_bfs(path_from[4], path_cost[4], start, start_cost, 4, limit + 4) - - let grid = new Array(hexcount).fill('-') - for (let x = first_hex; x <= last_hex; ++x) { - for (let speed = 4; speed >= 1; --speed) { - if (path_cost[0][x] <= speed) - grid[x] = speed - if (path_cost[1][x] <= speed + 1) - grid[x] = speed - if (path_cost[2][x] <= speed + 2) - grid[x] = speed - if (path_cost[4][x] <= speed + 4) - grid[x] = speed - } - } - grid[start] = '@' + for_each_enemy_unit(u => { + let x = unit_hex(u) + if (x >= first_hex && x <= last_hex) + path_enemy[x] = 1 + }) +} - debug_hexes2("reach", path_cost[0]) +function search_move(start, limit) { + // Normal moves. + search_init() + search_move_bfs(path_from[0], path_cost[0], start, 0, limit) + if (hex_road[start] >= 1) + search_move_bfs(path_from[1], path_cost[1], start, 1, limit + 1) + if (hex_road[start] >= 2) + search_move_bfs(path_from[2], path_cost[2], start, 2, limit + 2) + if (hex_road[start] >= 4) + search_move_bfs(path_from[4], path_cost[4], start, 4, limit + 4) +} + +function search_retreat(start, limit) { + // (Non-withdrawal) Retreat moves may not cross enemy hex-sides, and may not engage. + search_init() + search_retreat_bfs(path_from[0], path_cost[0], start, 0, limit) + if (hex_road[start] >= 1) + search_retreat_bfs(path_from[1], path_cost[1], start, 1, limit + 1) + if (hex_road[start] >= 2) + search_retreat_bfs(path_from[2], path_cost[2], start, 2, limit + 2) + if (hex_road[start] >= 4) + search_retreat_bfs(path_from[4], path_cost[4], start, 4, limit + 4) +} + +function search_withdraw(start, limit) { + // Withdrawal moves may not cross enemy hex-sides, + // and must follow a supply line, + // and must move closer to the supply source, + // and may not engage. + let sline = game.active === AXIS ? game.axis_supply_line : game.allied_supply_line + let sdist = game.active === AXIS ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA] + search_init() + search_withdraw_bfs(path_from[0], path_cost[0], start, 0, limit, sline, sdist) + if (hex_road[start] >= 1) + search_withdraw_bfs(path_from[1], path_cost[1], start, 1, limit + 1, sline, sdist) + if (hex_road[start] >= 2) + search_withdraw_bfs(path_from[2], path_cost[2], start, 2, limit + 2, sline, sdist) + if (hex_road[start] >= 4) + search_withdraw_bfs(path_from[4], path_cost[4], start, 4, limit + 4, sline, sdist) } // Breadth First Search -function search_move_bfs(from, cost, start, start_cost, road, max_cost) { - let queue = [ start << 4 | start_cost ] +function search_move_bfs(from, cost, start, road, max_cost) { + let queue = [ start << 4 ] from.fill(0) cost.fill(15) - cost[start] = start_cost + cost[start] = 0 while (queue.length > 0) { let item = queue.shift() @@ -1050,45 +1061,6 @@ function search_move_bfs(from, cost, start, start_cost, road, max_cost) { } } -function search_withdraw(start, is_pass_move) { - // recon=4, forced march=+1, rommel bonus=+1 - let limit = is_pass_move ? 6 : 4 - - path_enemy.fill(0) - for (let u = 0; u < units.length; ++u) { - if (is_enemy_unit(u)) { - let x = unit_hex(u) - if (x >= first_hex && x <= last_hex) - path_enemy[x] = 1 - } - } - - let sline = game.active === AXIS ? game.axis_supply_line : game.allied_supply_line - let sdist = game.active === AXIS ? distance_to[EL_AGHEILA] : distance_to[ALEXANDRIA] - - search_withdraw_bfs(path_from[0], path_cost[0], start, 0, limit, sline, sdist) - search_withdraw_bfs(path_from[1], path_cost[1], start, 1, limit + 1, sline, sdist) - search_withdraw_bfs(path_from[2], path_cost[2], start, 2, limit + 2, sline, sdist) - search_withdraw_bfs(path_from[4], path_cost[4], start, 4, limit + 4, sline, sdist) - - let grid = new Array(hexcount).fill('-') - for (let x = first_hex; x <= last_hex; ++x) { - for (let speed = 4; speed >= 1; --speed) { - if (path_cost[0][x] <= speed) - grid[x] = speed - if (path_cost[1][x] <= speed + 1) - grid[x] = speed - if (path_cost[2][x] <= speed + 2) - grid[x] = speed - if (path_cost[4][x] <= speed + 4) - grid[x] = speed - } - } - grid[start] = '@' - - debug_hexes2("reach", path_cost[0]) -} - // Breadth First Search function search_withdraw_bfs(from, cost, start, road, max_cost, sline, sdist) { let queue = [ start << 4 ] @@ -1143,39 +1115,24 @@ function search_withdraw_bfs(from, cost, start, road, max_cost, sline, sdist) { } } -// TODO: search_retreat where first hexside exit is restricted to friendly - -function can_move_to(to, road, speed) { - // TODO: engagement & hexside limit - if (road >= 4 && path_cost[4][to] <= speed + 4) +function can_move_to(to, speed) { + if (path_cost[4][to] <= speed + 4) return true - if (road >= 2 && path_cost[2][to] <= speed + 2) + if (path_cost[2][to] <= speed + 2) return true - if (road >= 1 && path_cost[1][to] <= speed + 1) + if (path_cost[1][to] <= speed + 1) return true if (path_cost[0][to] <= speed) return true return false } -function can_move_from(from, road, speed) { - // TODO: engagement & hexside limit - return can_move_to(from, road, speed) +function can_move_from(from, speed) { + return can_move_to(from, speed) } -function can_force_march_to(to, road, speed) { - if (road >= 4 && path_cost[4][to] <= speed + 5) - return true - if (road >= 2 && path_cost[2][to] <= speed + 3) - return true - if (road >= 1 && path_cost[1][to] <= speed + 2) - return true - if (path_cost[0][to] <= speed + 1) - return true - return false -} - -function pick_path(to, road, speed) { +function pick_path(to, speed) { + let road = 4 let next_cost = 15, next_road = 0 if (path_cost[0][to] <= speed) { next_cost = path_cost[0][to] @@ -1223,12 +1180,13 @@ function max_speed_of_undisrupted_friendly_unit_in_hex(from) { } function find_valid_regroup_destinations(from, rommel) { + // TODO: forced march let speed = max_speed_of_undisrupted_friendly_unit_in_hex(from) if (speed > 0) { - search_move(from, 0, 4) - for (let x = first_hex; x <= last_hex; ++x) + search_move(from, speed + rommel) + for (let x of all_hexes) if (!path_valid[x]) - if (can_move_to(x, 4, speed + rommel)) + if (can_move_to(x, speed + rommel)) path_valid[x] = 1 } } @@ -1302,7 +1260,7 @@ function goto_initial_supply_check() { } function clear_all_unit_moved() { - game.moved.length = 0 + set_clear(game.moved) } function goto_turn_option() { @@ -1464,11 +1422,12 @@ states.regroup_move_destination = { cp = game.from1, rommel = (game.rommel === 1 ? 1 : 0) else cp = game.from2, rommel = (game.rommel === 2 ? 1 : 0) + path_valid.fill(0) for_each_hex_and_adjacent_hex(cp, x => { find_valid_regroup_destinations(x, rommel) }) - for (let x = first_hex; x <= last_hex; ++x) + for (let x of all_hexes) if (path_valid[x]) gen_action_hex(x) }, @@ -1492,6 +1451,47 @@ states.regroup_move_destination = { }, } +function end_move_phase() { + game.side_limit = {} + game.rommel = 0 + game.from1 = game.from2 = game.to1 = game.to2 = 0 + // TODO: forced marches + goto_refuse_battle() +} + +// === GROUP AND REGROUP MOVEMENT === + +function unit_speed_1(who) { + return unit_speed(who) + (game.rommel === 1 ? 1 : 0) +} + +function unit_speed_2(who) { + return unit_speed(who) + (game.rommel === 2 ? 1 : 0) +} + +function gen_group_move_who(may_retreat, from) { + if (may_retreat !== is_battle_hex(from)) + return + for_each_undisrupted_friendly_unit_in_hex(from, u => { + if (!is_unit_moved(u)) + gen_action_unit(u) + }) +} + +function gen_regroup_move_who(may_retreat, command_point, destination, rommel) { + search_move(destination, 4 + rommel) + for_each_hex_and_adjacent_hex(command_point, x => { + if (x !== destination) { + if (may_retreat !== is_battle_hex(x)) + return + for_each_undisrupted_friendly_unit_in_hex(x, u => { + if (!is_unit_moved(u) && can_move_from(x, unit_speed(u) + rommel)) + gen_action_unit(u) + }) + } + }) +} + function goto_move_who() { if (game.rommel === 1) { if (game.from1 && game.to1) @@ -1516,33 +1516,9 @@ function goto_move_who() { log(`Group move from #${game.from2}.`) } log_br() - log("Moved:") game.state = 'move_who' } -function gen_group_move_who(may_retreat, from) { - if (may_retreat !== is_battle_hex(from)) - return - for_each_undisrupted_friendly_unit_in_hex(from, u => { - if (!is_unit_moved(u)) - gen_action_unit(u) - }) -} - -function gen_regroup_move_who(may_retreat, command_point, destination, rommel) { - search_move(destination, 0, 4) - for_each_hex_and_adjacent_hex(command_point, x => { - if (x !== destination) { - if (may_retreat !== is_battle_hex(x)) - return - for_each_undisrupted_friendly_unit_in_hex(x, u => { - if (!is_unit_moved(u) && can_move_from(x, 4, unit_speed(u) + rommel)) - gen_action_unit(u) - }) - } - }) -} - states.move_who = { inactive: "move (who)", prompt() { @@ -1567,8 +1543,6 @@ states.move_who = { push_undo() game.selected = [ who ] game.state = 'move_to' - game.move_used = 0 - game.move_road = 4 }, retreat() { push_undo() @@ -1582,26 +1556,15 @@ states.move_who = { } } -function end_move_phase() { - game.side_limit = {} - game.rommel = 0 - game.from1 = game.from2 = game.to1 = game.to2 = 0 - // TODO: forced marches - goto_refuse_battle() -} - function apply_move(move, who, from, to) { - let speed = unit_speed(who) + (move === game.rommel ? 1 : 0) - let road = pick_path(to, game.move_road, speed) - - print_path(who, unit_hex(who), to, road) - - game.move_road = road - game.move_used = path_cost[road][to] + let rommel = (game.rommel === move ? 1 : 0) + let road = pick_path(to, unit_speed(who) + rommel) set_unit_moved(who) set_unit_hex(who, to) + log(`>from #${from} to #${to}`) + if (is_battle_hex(to)) { let side = to_side_id(to, path_from[road][to]) @@ -1617,19 +1580,6 @@ function apply_move(move, who, from, to) { } return true } - - if (game.move_used === speed + game.move_road) - return true - - return false -} - -function unit_speed_1(who) { - return unit_speed(who) + (game.rommel === 1 ? 1 : 0) -} - -function unit_speed_2(who) { - return unit_speed(who) + (game.rommel === 2 ? 1 : 0) } function can_move_regroup_1(who, from, to) { @@ -1648,14 +1598,14 @@ function can_move_regroup_2(who, from, to) { function can_move_group_1(who, from, to) { if (from === game.from1 && !game.to1) - if (can_move_to(to, game.move_road, unit_speed_1(who))) + if (can_move_to(to, unit_speed_1(who))) return true return false } function can_move_group_2(who, from, to) { if (from === game.from2 && !game.to2) - if (can_move_to(to, game.move_road, unit_speed_2(who))) + if (can_move_to(to, unit_speed_2(who))) return true return false } @@ -1666,8 +1616,9 @@ states.move_to = { view.prompt = `Move: Select where to move.` let who = game.selected[0] let from = unit_hex(who) + let maybe_rommel = 1 - search_move(from, 0, 4) + search_move(from, unit_speed(who) + maybe_rommel) if (from === game.from1 && !game.to1) for (let to of all_hexes) @@ -1694,102 +1645,26 @@ states.move_to = { push_undo() let who = game.selected[0] let from = unit_hex(who) + let maybe_rommel = 1 - search_move(from, 0, 4) + search_move(from, unit_speed(who) + maybe_rommel) - if (can_move_group_1(who, from, to)) { - game.move_from = from - if (apply_move(1, who, from, to)) - stop_move(who) - else - game.state = 'group_move_to' - return - } - - if (can_move_group_2(who, from, to)) { - game.move_from = from - if (apply_move(2, who, from, to)) - stop_move(who) - else - game.state = 'group_move_to' - return + if (has_enemy_unit(to)) { + log("TODO: pick hex-side") } - if (can_move_regroup_1(who, from, to)) { + if (can_move_group_1(who, from, to)) apply_move(1, who, from, to) - stop_move(who) - game.state = 'move_who' - return - } - - if (can_move_regroup_2(who, from, to)) { + if (can_move_group_2(who, from, to)) + apply_move(2, who, from, to) + if (can_move_regroup_1(who, from, to)) + apply_move(1, who, from, to) + if (can_move_regroup_2(who, from, to)) apply_move(2, who, from, to) - stop_move(who) - game.state = 'move_who' - return - } - }, -} - -states.group_move_to = { - inactive: "group move (to)", - prompt() { - view.prompt = `Group Move: Select where to move.` - let who = game.selected[0] - let from = unit_hex(who) - if (game.move_used > 0) - gen_action('stop') - - search_move(from, game.move_used, game.move_road) - - if (game.move_from === game.from1 && !game.to1) - for (let to of all_hexes) - if (to != from && can_move_group_1(who, game.move_from, to)) - gen_action_hex(to) - - if (game.move_from === game.from2 && !game.to2) - for (let to of all_hexes) - if (to != from && can_move_group_2(who, game.move_from, to)) - gen_action_hex(to) - - gen_action_unit(who) - }, - unit(who) { - this.stop() - }, - hex(to) { - let who = game.selected[0] - let from = unit_hex(who) - - search_move(from, game.move_used, game.move_road) - - if (can_move_group_1(who, game.move_from, to)) { - log(`group continued ${who} to ${to}`) - if (apply_move(1, who, from, to)) - stop_move(who) - return - } - if (can_move_group_2(who, game.move_from, to)) { - log(`group continued ${who} to ${to}`) - if (apply_move(2, who, from, to)) - stop_move(who) - return - } + game.state = 'move_who' + game.selected.length = 0 }, - stop() { - let who = game.selected[0] - stop_move(who) - } -} - -function stop_move(who) { - set_unit_moved(who) - game.move_from = 0 - game.move_road = 4 - game.move_used = 0 - game.selected = [] - game.state = 'move_who' } // === RETREAT === @@ -1948,13 +1823,14 @@ states.retreat_to = { prompt() { view.prompt = `Retreat: Select destination.` let who = game.selected[0] + let from = game.retreat gen_action_unit(who) if (game.turn_option === 'pass') { - search_withdraw(game.retreat, true) + search_withdraw(from, true) } else { - search_retreat(game.retreat) + search_retreat(from) } if (from === game.from1 && !game.to1) @@ -2382,11 +2258,6 @@ function end_battle() { end_combat_phase() } -function end_probe() { - game.battle = 0 - resume_retreat() -} - // === PURSUIT FIRE === // Refuse battle @@ -3144,11 +3015,6 @@ exports.setup = function (seed, scenario, options) { from2: 0, to2: 0, - // current group move state - move_from: 0, - move_used: 0, - move_road: 4, - // retreat partial_retreats: [], // remember partial retreats to forbid initiating combat retreat: 0, |