From 6cab873fa90a25a5247c3ce9c6c04bb8f895848b Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 20 Aug 2023 16:17:36 +0200 Subject: Angst. --- rules.js | 134 +++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 78 insertions(+), 56 deletions(-) (limited to 'rules.js') diff --git a/rules.js b/rules.js index 2477dbc..b187dd9 100644 --- a/rules.js +++ b/rules.js @@ -1,34 +1,37 @@ "use strict" +// TODO - auto-update ZOC + // TODO: recall grand battery if alone // TODO: rain effect on movement // TODO goto_british_line_of_communication_angst -const FRENCH = "French" -const COALITION = "Coalition" +const P1 = "French" +const P2 = "Coalition" -const P1 = FRENCH -const P2 = COALITION +var game = null +var view = null +var states = {} exports.roles = [ P1, P2 ] -exports.scenarios = [ "June 16", "June 15", "June 15 (no special rules)" ] +exports.scenarios = [ "June 16", "June 15" ] const data = require("./data") -const { max, abs } = Math - const last_hex = 1000 + (data.map.rows - 1) * 100 + (data.map.cols - 1) -var game = null -var view = null -var states = {} +var move_seen = new Array(last_hex - 999).fill(0) +var move_cost = new Array(last_hex - 999).fill(0) +var zoc_valid = false +var zoc_cache = new Array(data.map.rows * 100).fill(0) const ELIMINATED = 0 const REINFORCEMENTS = 100 const AVAILABLE_P1 = 101 const AVAILABLE_P2 = 102 const BLOWN = 103 +const SWAPPED = 200 function find_piece(name) { let id = data.pieces.findIndex(pc => pc.name === name) @@ -45,7 +48,6 @@ const OLD_GUARD = find_piece("Old Guard") const GRAND_BATTERY = find_piece("Grand Battery") const HILL_1 = find_piece("II Corps (Hill*)") const HILL_2 = find_piece("II Corps (Hill**)") - const IMPERIAL_GUARD = find_piece("Guard Corps (Drouot)") const IMPERIAL_GUARD_CAV = find_piece("Guard Cav Corps (Guyot)") @@ -64,11 +66,7 @@ function calc_distance(a, b) { let bx = bc - (by >> 1) let az = -ax - ay let bz = -bx - by - return max(abs(bx-ax), abs(by-ay), abs(bz-az)) -} - -function is_adjacent(a, b) { - return is_map_hex(a) && is_map_hex(b) && calc_distance(a, b) === 1 && !is_river(a, b) + return Math.max(Math.abs(bx-ax), Math.abs(by-ay), Math.abs(bz-az)) } const adjacent_x1 = [ @@ -76,14 +74,6 @@ const adjacent_x1 = [ [-100,-99,-1,1,100,101] ] -function for_each_adjacent(x, f) { - for (let dx of adjacent_x1[x / 100 & 1]) { - let nx = x + dx - if (is_map_hex(nx)) - f(nx) - } -} - const within_x3 = [ [ -302,-301,-300,-299, @@ -105,6 +95,14 @@ const within_x3 = [ ] ] +function for_each_adjacent(x, f) { + for (let dx of adjacent_x1[x / 100 & 1]) { + let nx = x + dx + if (is_map_hex(nx)) + f(nx) + } +} + function for_each_within_x3(x, f) { for (let dx of within_x3[x / 100 & 1]) { let nx = x + dx @@ -120,6 +118,7 @@ for (let a of data.map.brussels_couillet_road) const data_rivers = [] const data_bridges = [] const data_road_hexsides = [] +const data_roads = [] for (let [a, b] of data.map.rivers) { set_add(data_rivers, a * 10000 + b) @@ -133,7 +132,6 @@ for (let [a, b] of data.map.bridges) { set_add(data_bridges, b * 10000 + a) } -const data_roads = [] for (let row = 0; row < data.map.rows; ++row) { for (let col = 0; col < data.map.cols; ++col) { let x = 1000 + row * 100 + col @@ -141,15 +139,6 @@ for (let row = 0; row < data.map.rows; ++row) { } } -function make_road(id, road, i, d) { - let list = [] - while (i >= 0 && i < road.length) { - list.push(road[i]) - i += d - } - return list -} - for (let road_id = 0; road_id < data.map.roads.length; ++road_id) { let road = data.map.roads[road_id] for (let k = 0; k < road.length; ++k) { @@ -179,6 +168,7 @@ const p1_inf = make_piece_list(p => p.side === P1 && p.type === "inf") const p2_inf = make_piece_list(p => p.side !== P1 && p.type === "inf") const p1_det = make_piece_list(p => p.side === P1 && p.type === "det") const p2_det = make_piece_list(p => p.side !== P1 && p.type === "det") +const aa_det = make_piece_list(p => p.side === "Anglo" && p.type === "det") const p1_corps = make_piece_list(p => p.side === P1 && (p.type === "inf" || p.type === "cav")) const p2_corps = make_piece_list(p => p.side !== P1 && (p.type === "inf" || p.type === "cav")) const p1_units = make_piece_list(p => p.side === P1 && (p.type === "inf" || p.type === "cav" || p.type === "det")) @@ -256,6 +246,10 @@ function piece_stars(p) { return data.pieces[p].stars } +function is_adjacent(a, b) { + return is_map_hex(a) && is_map_hex(b) && calc_distance(a, b) === 1 && !is_river(a, b) +} + function is_empty_hex(x) { for (let p = 0; p < data.pieces.length; ++p) if (piece_hex(p) === x) @@ -353,15 +347,25 @@ function eliminate_detachments_stacked_with_corps(c) { eliminate_unit(p) } +function recall_grand_battery_alone() { + let x = piece_hex(GRAND_BATTERY) + if (is_map_hex(x) && !hex_has_any_piece(x, friendly_corps())) + recall_detachment(GRAND_BATTERY) +} + +function recall_detachment(p) { + if (set_has(p1_det, p)) + set_piece_hex(p, AVAILABLE_P1) + else + set_piece_hex(p, AVAILABLE_P2) +} + function prompt(str) { view.prompt = str } // === ZONE OF CONTROL / INFLUENCE === -var zoc_valid = false -var zoc_cache = new Array(data.map.rows * 100).fill(0) - // ANY_ZOC=1, CAV_ZOC=2, ANY_ZOI=4, CAV_ZOI=8 function is_p1_zoc(x) { return zoc_cache[x-1000] & (1|2) } @@ -785,13 +789,6 @@ function end_detachment_recall_step() { } } -function recall_detachment(p) { - if (set_has(p1_det, p)) - set_piece_hex(p, AVAILABLE_P1) - else - set_piece_hex(p, AVAILABLE_P2) -} - states.detachment_recall_step = { prompt() { prompt("Detachment Recall Step.") @@ -816,13 +813,35 @@ states.detachment_recall_step = { function goto_organization_phase() { update_zoc() + // British Line of Communication Angst + let n = 0 + for (let p of aa_det) + if (piece_is_on_map(p) || piece_hex(p) === ELIMINATED) { + console.log("P" + p, piece_name(p), piece_hex(p), piece_is_on_map(p)) + ++n + } + console.log("aa dets=" + n) + if (n < 3) { + if (piece_hex(HILL_2) === SWAPPED && piece_hex(HILL_1) !== ELIMINATED) { + log("Substituted P" + HILL_2 + ".") + set_piece_hex(HILL_2, piece_hex(HILL_1)) + set_piece_mode(HILL_2, piece_mode(HILL_1)) + set_piece_hex(HILL_1, SWAPPED) + set_piece_mode(HILL_1, 0) + } + } else { + if (piece_hex(HILL_1) === SWAPPED && piece_hex(HILL_2) !== ELIMINATED) { + log("Substituted P" + HILL_1 + ".") + set_piece_hex(HILL_1, piece_hex(HILL_2)) + set_piece_mode(HILL_1, piece_mode(HILL_2)) + set_piece_hex(HILL_2, SWAPPED) + set_piece_mode(HILL_2, 0) + } + } + log("") log("Organization Phase") - // British Line of Communication Angst - // TODO - log("TODO: British Line of Communication Angst") - // F: ADVANCE FORMATION game.active = P1 for (let p of friendly_infantry_corps()) @@ -936,11 +955,13 @@ states.withdrawal_to = { blow() { blow_unit(game.who, 2) game.who = -1 + recall_grand_battery_alone() next_withdrawal() }, hex(x) { set_piece_hex(game.who, x) game.who = -1 + recall_grand_battery_alone() next_withdrawal() }, } @@ -951,7 +972,8 @@ function bring_on_reinforcements() { for (let info of data.reinforcements) if (info.turn === game.turn) for (let p of info.list) - set_piece_hex(p, REINFORCEMENTS) + if (piece_hex(p) !== SWAPPED) + set_piece_hex(p, REINFORCEMENTS) for (let p of friendly_units()) if (piece_hex(p) === BLOWN + game.turn) set_piece_hex(p, BLOWN) @@ -990,7 +1012,7 @@ states.movement = { for (let info of data.reinforcements) { if (info.turn === game.turn && info.side === game.active) { for (let p of info.list) { - if (!piece_is_on_map(p)) { + if (piece_hex(p) === REINFORCEMENTS) { has_reinf = true gen_action_piece(p) break @@ -1084,6 +1106,7 @@ states.movement_to = { set_piece_mode(p, 1) game.who = -1 + recall_grand_battery_alone() next_movement() }, } @@ -1147,9 +1170,6 @@ function must_flip_zoc(here, next, is_cav) { return false } -const move_seen = new Array(last_hex - 999).fill(0) -const move_cost = new Array(last_hex - 999).fill(0) - function find_reinforcement_hex(who) { for (let info of data.reinforcements) for (let p of info.list) @@ -1288,7 +1308,6 @@ function search_move_road(start, ma, hq_hex, hq_range, is_cav) { let queue = [ start ] while (queue.length > 0) { let here = queue.shift() - let mp = move_cost[here-1000] for (let [road_id, k] of data_roads[here-1000]) { let road = data.map.roads[road_id] if (k + 1 < road.length) @@ -1588,8 +1607,8 @@ function goto_resolve_attack() { // Grand battery stacked with attacking or supporting corps let gb_hex = piece_hex(GRAND_BATTERY) for (let p of friendly_corps()) - if (gb_hex === piece_hex(p) && (p === who || (game.count & (1<