summaryrefslogtreecommitdiff
path: root/rules.js
diff options
context:
space:
mode:
Diffstat (limited to 'rules.js')
-rw-r--r--rules.js390
1 files changed, 128 insertions, 262 deletions
diff --git a/rules.js b/rules.js
index 1da88b5..ad38289 100644
--- a/rules.js
+++ b/rules.js
@@ -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,