summaryrefslogtreecommitdiff
path: root/rules.js
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2022-07-27 19:12:49 +0200
committerTor Andersson <tor@ccxvii.net>2022-11-17 13:11:26 +0100
commit736097bab6ce3d9b59f791b8dad0d7cd98c9d584 (patch)
tree21726d000750bd818b80ddbf4b385fa9ba2418ac /rules.js
parent330d6dfb5382f4b3b338d88b8a08d4f776f5ee36 (diff)
downloadrommel-in-the-desert-736097bab6ce3d9b59f791b8dad0d7cd98c9d584.tar.gz
Forced march.
Diffstat (limited to 'rules.js')
-rw-r--r--rules.js272
1 files changed, 169 insertions, 103 deletions
diff --git a/rules.js b/rules.js
index 5283d9a..6bff7ab 100644
--- a/rules.js
+++ b/rules.js
@@ -1,18 +1,19 @@
"use strict"
-// TODO: select hexside for engaging
-
// TODO: fortress supply
// TODO: oasis supply
-// TODO: legal pass withdrawal moves (reduce supply net, withdraw from fortress attack)
// TODO: raiders
+// TODO: legal pass withdrawal moves (reduce supply net, withdraw from fortress attack)
-// TODO: MINEFIELDS
// TODO: FORCED MARCHES
+// TODO: MINEFIELDS
// TODO: SUPPLY COMMITMENT
// TODO: BUILDUP
+
// TODO: setup scenario specials
+// TODO: failed forced march hexside control...
+
// TODO: when is "fired" status cleared?
// RULES: disrupted units routed again in second enemy turn, will they still recover?
@@ -1301,34 +1302,6 @@ function can_move_to(to, speed) {
return false
}
-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]
- next_road = 0
- }
- if (road >= 1 && path_cost[1][to] <= speed + 1) {
- if (path_cost[1][to] <= next_cost) {
- next_cost = path_cost[1][to]
- next_road = 1
- }
- }
- if (road >= 2 && path_cost[2][to] <= speed + 2) {
- if (path_cost[2][to] <= next_cost) {
- next_cost = path_cost[2][to]
- next_road = 2
- }
- }
- if (road >= 4 && path_cost[4][to] <= speed + 4) {
- if (path_cost[4][to] <= next_cost) {
- next_cost = path_cost[4][to]
- next_road = 4
- }
- }
- return next_road
-}
-
function max_speed_of_undisrupted_and_unmoved_friendly_unit_in_hex(from) {
let max_speed = 0
for_each_undisrupted_and_unmoved_friendly_unit_in_hex(from, u => {
@@ -1340,13 +1313,12 @@ function max_speed_of_undisrupted_and_unmoved_friendly_unit_in_hex(from) {
}
function find_valid_regroup_destinations(from, rommel) {
- // TODO: forced march
let speed = max_speed_of_undisrupted_and_unmoved_friendly_unit_in_hex(from)
if (speed > 0) {
- search_move(from, speed + rommel)
+ search_move(from, speed + 1 + rommel)
for (let x of all_hexes)
if (!path_valid[x])
- if (can_move_to(x, speed + rommel))
+ if (can_move_to(x, speed + 1 + rommel))
path_valid[x] = 1
}
}
@@ -1798,9 +1770,9 @@ states.move = {
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)
+ search_move(from, speed + 1 + rommel1)
for_each_undisrupted_and_unmoved_friendly_unit_in_hex(from, u => {
- if (can_move_to(game.to1, unit_speed(u) + rommel1))
+ if (can_move_to(game.to1, unit_speed(u) + 1 + rommel1))
gen_action_unit(u)
})
}
@@ -1813,9 +1785,9 @@ states.move = {
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)
+ search_move(from, speed + 1 + rommel2)
for_each_undisrupted_and_unmoved_friendly_unit_in_hex(from, u => {
- if (can_move_to(game.to2, unit_speed(u) + rommel2))
+ if (can_move_to(game.to2, unit_speed(u) + 1 + rommel2))
gen_action_unit(u)
})
}
@@ -1847,9 +1819,9 @@ states.move = {
// Move
if (game.turn_option === 'pass')
- search_withdraw(game.selected, (rommel1 | rommel2))
+ search_withdraw(game.selected, 1 + (rommel1 | rommel2))
else
- search_move(unit_hex(game.selected), unit_speed(game.selected) + (rommel1 | rommel2))
+ search_move(unit_hex(game.selected), unit_speed(game.selected) + 1 + (rommel1 | rommel2))
gen_move()
}
@@ -1857,6 +1829,9 @@ states.move = {
unit(who) {
apply_select(who)
},
+ forced_march(to) {
+ apply_move(to)
+ },
hex(to) {
apply_move(to)
},
@@ -1914,25 +1889,40 @@ function gen_move() {
let from = unit_hex(game.selected)
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)
+ for (let to of all_hexes) {
+ if (to != from) {
+ if (can_move_to(to, speed + rommel1))
+ gen_action_hex(to)
+ else if (can_move_to(to, speed + 1 + rommel1))
+ gen_action_forced_march(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)
+ for (let to of all_hexes) {
+ if (to != from) {
+ if (can_move_to(to, speed + rommel2))
+ gen_action_hex(to)
+ else if (can_move_to(to, speed + 1 + rommel2))
+ gen_action_forced_march(to)
+
+ }
+ }
}
if (game.to1 && is_hex_or_adjacent_to(from, game.from1)) {
if (can_move_to(game.to1, speed + rommel1))
gen_action_hex(game.to1)
+ else if (can_move_to(game.to1, speed + 1 + rommel1))
+ gen_action_forced_march(game.to1)
}
if (game.to2 && is_hex_or_adjacent_to(from, game.from2)) {
if (can_move_to(game.to2, speed + rommel2))
gen_action_hex(game.to2)
+ else if (can_move_to(game.to2, speed + 1 + rommel2))
+ gen_action_forced_march(game.to2)
}
}
@@ -1941,7 +1931,7 @@ function apply_move(to) {
let rommel2 = (game.rommel === 2) ? 1 : 0
let who = pop_selected()
let from = unit_hex(who)
- let speed = unit_speed(who)
+ let speed = unit_speed(who) + 1 // forced march
push_undo()
@@ -1949,19 +1939,19 @@ function apply_move(to) {
if (!game.to1 && game.from1 === from)
if (can_move_to(to, speed + rommel1))
- return move_unit(who, to, speed + rommel1)
+ return move_unit(who, to, speed + rommel1, 1)
if (!game.to2 && game.from2 === from)
if (can_move_to(to, speed + rommel2))
- return move_unit(who, to, speed + rommel2)
+ return move_unit(who, to, speed + rommel2, 2)
if (game.to1 === to && is_hex_or_adjacent_to(from, game.from1))
if (can_move_to(to, speed + rommel1))
- return move_unit(who, to, speed + rommel1)
+ return move_unit(who, to, speed + rommel1, 1)
if (game.to2 === to && is_hex_or_adjacent_to(from, game.from2))
if (can_move_to(to, speed + rommel2))
- return move_unit(who, to, speed + rommel2)
+ return move_unit(who, to, speed + rommel2, 2)
}
// to check usable alternate paths to enter destination hex
@@ -1970,8 +1960,6 @@ function can_move_via(via, to, speed, road) {
let side = to_side_id(via, to)
let max_side = side_limit[side]
- console.log("can_move_via", via, to, speed, road, "=", cost)
-
// too far
if (cost + 1 > speed + road)
return false
@@ -1996,72 +1984,145 @@ function can_move_via(via, to, speed, road) {
return true
}
-function move_unit(who, to, speed) {
+function is_forced_march_move(from, to, speed) {
+ console.log("is_forced_march_move", from, to, speed, "=>",
+ path_cost[4][to],
+ path_cost[2][to],
+ path_cost[1][to],
+ path_cost[0][to]
+ )
+ if (hex_road[from] >= 4 && path_cost[4][to] === speed + 4)
+ return true
+ if (hex_road[from] >= 2 && path_cost[2][to] === speed + 2)
+ return true
+ if (hex_road[from] >= 1 && path_cost[1][to] === speed + 1)
+ return true
+ if (hex_road[from] >= 0 && path_cost[0][to] === speed + 0)
+ return true
+ return false
+}
+
+function move_via(who, to, speed, move) {
let from = unit_hex(who)
- let road = pick_path(to, speed)
-
- if (has_enemy_unit(to)) {
- game.engage_via = []
- for_each_adjacent_hex(to, via => {
- if (
- (hex_road[from] >= 4 && can_move_via(via, to, speed, 4)) ||
- (hex_road[from] >= 2 && can_move_via(via, to, speed, 2)) ||
- (hex_road[from] >= 1 && can_move_via(via, to, speed, 1)) ||
- (hex_road[from] >= 0 && can_move_via(via, to, speed, 0))
- )
- game.engage_via.push(via)
- })
- console.log("ENGAGE", to, "FROM", game.engage_via)
- if (game.engage_via.length === 1) {
- engage_via(who, game.engage_via[0], to)
+ game.hexside = {
+ who: who,
+ via: [],
+ to: to,
+ move: move
+ }
+ for_each_adjacent_hex(to, via => {
+ if (
+ (hex_road[from] >= 4 && can_move_via(via, to, speed, 4)) ||
+ (hex_road[from] >= 2 && can_move_via(via, to, speed, 2)) ||
+ (hex_road[from] >= 1 && can_move_via(via, to, speed, 1)) ||
+ (hex_road[from] >= 0 && can_move_via(via, to, speed, 0))
+ )
+ game.hexside.via.push(via)
+ })
+ return game.hexside.via.length === 1
+}
+
+function move_unit(who, to, speed, move) {
+ let from = unit_hex(who)
+
+ if (is_forced_march_move(from, to, speed + 1)) {
+ if (move_via(who, to, speed, move)) {
+ forced_march_via(who, game.hexside.via[0], to, move)
+ delete game.hexside
} else {
- game.engage_who = who
- game.engage_to = to
- game.state = 'engage'
- return
+ game.state = 'forced_march_via'
}
- } else {
+ }
+
+ else if (has_enemy_unit(to)) {
+ if (move_via(who, to, speed, move)) {
+ engage_via(who, game.hexside.via[0], to)
+ delete game.hexside
+ } else {
+ game.state = 'engage_via'
+ }
+ }
+
+ else {
log(`>from #${from} to #${to}`)
set_unit_moved(who)
set_unit_hex(who, to)
}
}
-states.engage = {
+states.forced_march_via = {
prompt() {
view.prompt = `Move: Select which hex side to cross.`
- view.selected = game.engage_who
- for (let x of game.engage_via)
+ view.selected = game.hexside.who
+ for (let x of game.hexside.via)
gen_action_hex(x)
},
hex(via) {
- engage_via(game.engage_who, via, game.engage_to)
- delete game.engage_via
- delete game.engage_who
- delete game.engage_to
+ forced_march_via(game.hexside.who, via, game.hexside.to, game.hexside.move)
+ delete game.hexside
game.state = 'move'
}
}
-function engage_via(who, via, to) {
- let from = unit_hex(who)
+states.engage_via = {
+ prompt() {
+ view.prompt = `Move: Select which hex side to cross.`
+ view.selected = game.hexside.who
+ for (let x of game.hexside.via)
+ gen_action_hex(x)
+ },
+ hex(via) {
+ engage_via(game.hexside.who, via, game.hexside.to)
+ delete game.hexside
+ game.state = 'move'
+ }
+}
- set_unit_moved(who)
- set_unit_hex(who, to)
+function forced_march_via(who, via, to, move) {
+ let from = unit_hex(who)
+ set_unit_moved(who)
+ set_unit_hex(who, via)
+
+ // remember where we should advance to / return to
+ if ((move === 1 && game.to1) || (move === 2 && game.to2))
+ game.forced[who] = to
+ else
+ game.forced[who] = [ to, from ]
+ // attempted force marches affect hexside limits
+ if (is_enemy_hex(to)) {
let side = to_side_id(via, to)
if (game.side_limit[side])
game.side_limit[side] = 2
else
game.side_limit[side] = 1
+ }
- claim_hexside_control(side)
- if (is_new_battle_hex(to)) {
- claim_hex_control_for_defender(to)
- set_add(game.active_battles, to)
- }
+ log(`>forced march from #${from} via #${via} to #${to}`)
+}
+
+function engage_via(who, via, to) {
+ let from = unit_hex(who)
+
+ set_unit_moved(who)
+ set_unit_hex(who, to)
+ let side = to_side_id(via, to)
+ if (game.side_limit[side])
+ game.side_limit[side] = 2
+ else
+ game.side_limit[side] = 1
+
+ claim_hexside_control(side)
+ if (is_new_battle_hex(to)) {
+ claim_hex_control_for_defender(to)
+ set_add(game.active_battles, to)
+ }
+
+ if (from !== via)
log(`>from #${from} via #${via} to #${to}`)
+ else
+ log(`>from #${from} to #${to}`)
}
// === RETREAT ===
@@ -2119,9 +2180,9 @@ function can_unit_retreat_group_move(who) {
function can_unit_retreat_regroup_move(who, to, rommel) {
if (game.turn_option === 'pass')
- return can_unit_disengage_and_withdraw_to(who, to, rommel)
+ return can_unit_disengage_and_withdraw_to(who, to, 1 + rommel)
else
- return can_unit_disengage_and_move_to(who, to, rommel)
+ return can_unit_disengage_and_move_to(who, to, 1 + rommel)
}
function is_friendly_hexside(side) {
@@ -2155,14 +2216,14 @@ function can_unit_disengage_and_move(who) {
return result
}
-function can_unit_disengage_and_withdraw_to(who, to, rommel) {
- search_withdraw_retreat(who, rommel)
- return can_move_to(to, unit_speed(who) + rommel)
+function can_unit_disengage_and_withdraw_to(who, to, extra) {
+ search_withdraw_retreat(who, extra)
+ return can_move_to(to, unit_speed(who) + extra)
}
-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_unit_disengage_and_move_to(who, to, extra) {
+ search_move_retreat(unit_hex(who), unit_speed(who) + extra)
+ return can_move_to(to, unit_speed(who) + extra)
}
function can_select_retreat_hex() {
@@ -2357,9 +2418,9 @@ states.retreat_move = {
let rommel2 = (game.rommel === 2) ? 1 : 0
gen_action_unit(game.selected)
if (game.turn_option === 'pass')
- search_withdraw_retreat(game.selected, (rommel1 | rommel2))
+ search_withdraw_retreat(game.selected, 1 + (rommel1 | rommel2))
else
- search_move_retreat(unit_hex(game.selected), unit_speed(game.selected) + (rommel1 | rommel2))
+ search_move_retreat(unit_hex(game.selected), unit_speed(game.selected) + 1 + (rommel1 | rommel2))
gen_move()
}
},
@@ -3935,6 +3996,7 @@ exports.setup = function (seed, scenario, options) {
// current turn option and selected moves
turn_option: null,
side_limit: {},
+ forced: {},
rommel: 0,
from1: 0,
to1: 0,
@@ -4020,6 +4082,10 @@ function gen_action_hex(x) {
gen_action('hex', x)
}
+function gen_action_forced_march(x) {
+ gen_action('forced_march', x)
+}
+
// === COMMON TEMPLATE ===
function random(range) {
@@ -4144,7 +4210,7 @@ function pop_undo() {
}
function clear_undo() {
- game.undo = []
+ // game.undo = []
}
function log_br() {