summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rules.js261
1 files changed, 140 insertions, 121 deletions
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')
}