summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--play.js62
-rw-r--r--rules.js249
2 files changed, 191 insertions, 120 deletions
diff --git a/play.js b/play.js
index a39588c..6e6c9f3 100644
--- a/play.js
+++ b/play.js
@@ -73,24 +73,72 @@ let ui = {
const AXIS = 'Axis'
const ALLIED = 'Allied'
+// === UNIT STATE ===
+
+const UNIT_DISRUPTED_SHIFT = 0
+const UNIT_DISRUPTED_MASK = 1 << UNIT_DISRUPTED_SHIFT
+
+const UNIT_STEPS_SHIFT = 1
+const UNIT_STEPS_MASK = 3 << UNIT_STEPS_SHIFT
+
+const UNIT_SUPPLY_SHIFT = 3
+const UNIT_SUPPLY_MASK = 7 << UNIT_SUPPLY_SHIFT
+
+const UNIT_HEX_SHIFT = 6
+const UNIT_HEX_MASK = 255 << UNIT_HEX_SHIFT
+
+function is_unit_disrupted(u) {
+ return (view.units[u] & UNIT_DISRUPTED_MASK) === UNIT_DISRUPTED_MASK
+}
+
+function set_unit_disrupted(u) {
+ view.units[u] |= UNIT_DISRUPTED_MASK
+}
+
+function clear_unit_disrupted(u) {
+ view.units[u] &= ~UNIT_DISRUPTED_MASK
+}
+
function unit_hex(u) {
- return view.units[u] >>> 5
+ return (view.units[u] & UNIT_HEX_MASK) >> UNIT_HEX_SHIFT
}
-function unit_lost_steps(u) {
- return view.units[u] & 3
+function set_unit_hex(u, x) {
+ view.units[u] = (view.units[u] & ~UNIT_HEX_MASK) | (x << UNIT_HEX_SHIFT)
}
function is_unit_supplied(u) {
- return (view.units[u] & 4) === 4
+ return ((view.units[u] & UNIT_SUPPLY_MASK) >> UNIT_SUPPLY_SHIFT) !== 0
}
-function is_unit_disrupted(u) {
- return (view.units[u] & 8) === 8
+function unit_supply(u) {
+ let src = (view.units[u] & UNIT_SUPPLY_MASK) >> UNIT_SUPPLY_SHIFT
+ return hex_from_supply_source[src]
+}
+
+function set_unit_supply(u, hex) {
+ let src = supply_source_from_hex(hex)
+ view.units[u] = (view.units[u] & ~UNIT_SUPPLY_MASK) | (src << UNIT_SUPPLY_SHIFT)
+}
+
+function unit_lost_steps(u) {
+ return (view.units[u] & UNIT_STEPS_MASK) >> UNIT_STEPS_SHIFT
+}
+
+function set_unit_lost_steps(u, n) {
+ view.units[u] = (view.units[u] & ~UNIT_STEPS_MASK) | (n << UNIT_STEPS_SHIFT)
+}
+
+function unit_steps(u) {
+ return units[u].steps - unit_lost_steps(u)
+}
+
+function set_unit_steps(u, n) {
+ set_unit_lost_steps(u, units[u].steps - n)
}
function is_unit_moved(u) {
- return (view.units[u] & 16) === 16
+ return set_has(view.moved, u)
}
function is_unit_fired(u) {
diff --git a/rules.js b/rules.js
index 54647b8..ff66875 100644
--- a/rules.js
+++ b/rules.js
@@ -2,7 +2,7 @@
// TODO: partial moves during regroup (to allow deciding entry hex-side)
-// unit state: location (8 bits), steps (2 bits), supplied, disrupted, moved
+// unit state: location (8 bits), supply source (3 bits), steps (2 bits), disrupted (1 bit)
const max = Math.max
const min = Math.min
@@ -96,6 +96,19 @@ const FT_CAPUZZO = 64
const MERSA_BREGA = 152
const BARDIA_FT_CAPUZZO = 122
+const hex_from_supply_source = [ 0, EL_AGHEILA, ALEXANDRIA, BARDIA, BENGHAZI, TOBRUK ]
+
+function supply_source_from_hex(hex) {
+ switch (hex) {
+ case 0: return 0
+ case EL_AGHEILA: return 1
+ case ALEXANDRIA: return 2
+ case BARDIA: return 3
+ case BENGHAZI: return 4
+ case TOBRUK: return 5
+ }
+}
+
const region_egypt = regions["Egypt"]
const region_el_agheila = regions["El Agheila"]
const region_tobruk = regions["Tobruk"]
@@ -126,32 +139,25 @@ const distance_to = {
[BARDIA]: calc_distance_map(BARDIA),
}
-function draw_supply_card(pile) {
- let x = random(pile[0] + pile[1])
- if (x < pile[0]) {
- pile[0] --
- return 0
- } else {
- pile[1] --
- return 1
- }
-}
-
-function deal_axis_supply_cards(n) {
- for (let i = 0; i < n; ++i)
- game.axis_hand[draw_supply_card(game.draw_pile)]++
-}
-
-function deal_allied_supply_cards(n) {
- for (let i = 0; i < n; ++i)
- game.allied_hand[draw_supply_card(game.draw_pile)]++
+function to_side(a, b, s) {
+ if (s < 3)
+ return a * 3 + s
+ return b * 3 + s - 3
}
-function find_unit(name) {
- for (let u = 0; u < units.length; ++u)
- if (units[u].name === name)
- return u
- throw new Error("cannot find named block: " + name)
+function to_side_id(a, b) {
+ if (a > b) {
+ let c = b
+ b = a
+ a = c
+ }
+ if (a + hexnext[0] === b)
+ return a * 3 + 0
+ else if (a + hexnext[1] === b)
+ return a * 3 + 1
+ else if (a + hexnext[2] === b)
+ return a * 3 + 2
+ throw new Error("not a hexside " + a + " to " + b);
}
function is_map_hex(x) {
@@ -166,52 +172,84 @@ function is_hex_or_adjacent_to(x, where) {
return false
}
-function find_units(list) {
- return list.map(name => find_unit(name))
-}
+// === UNIT STATE ===
-function unit_name(u) {
- return units[u].name
-}
+const UNIT_DISRUPTED_SHIFT = 0
+const UNIT_DISRUPTED_MASK = 1 << UNIT_DISRUPTED_SHIFT
-function unit_speed(u) {
- return units[u].speed
+const UNIT_STEPS_SHIFT = 1
+const UNIT_STEPS_MASK = 3 << UNIT_STEPS_SHIFT
+
+const UNIT_SUPPLY_SHIFT = 3
+const UNIT_SUPPLY_MASK = 7 << UNIT_SUPPLY_SHIFT
+
+const UNIT_HEX_SHIFT = 6
+const UNIT_HEX_MASK = 255 << UNIT_HEX_SHIFT
+
+function is_unit_disrupted(u) {
+ return (game.units[u] & UNIT_DISRUPTED_MASK) === UNIT_DISRUPTED_MASK
}
-function unit_class(u) {
- return units[u].class
+function set_unit_disrupted(u) {
+ game.units[u] |= UNIT_DISRUPTED_MASK
}
-function is_artillery_unit(u) {
- return units[u].class === ARTILLERY
+function clear_unit_disrupted(u) {
+ game.units[u] &= ~UNIT_DISRUPTED_MASK
}
-function is_armor_unit(u) {
- return units[u].class === ARMOR
+function unit_hex(u) {
+ return (game.units[u] & UNIT_HEX_MASK) >> UNIT_HEX_SHIFT
}
-function is_infantry_unit(u) {
- return units[u].class === INFANTRY
+function set_unit_hex(u, x) {
+ game.units[u] = (game.units[u] & ~UNIT_HEX_MASK) | (x << UNIT_HEX_SHIFT)
}
-function is_antitank_unit(u) {
- return units[u].class === ANTITANK
+function is_unit_supplied(u) {
+ return ((game.units[u] & UNIT_SUPPLY_MASK) >> UNIT_SUPPLY_SHIFT) !== 0
}
-function unit_hex(u) {
- return game.units[u] >>> 5
+function unit_supply(u) {
+ let src = (game.units[u] & UNIT_SUPPLY_MASK) >> UNIT_SUPPLY_SHIFT
+ return hex_from_supply_source[src]
}
-function set_unit_hex(u, x) {
- game.units[u] = (game.units[u] & 31) | (x << 5)
+function set_unit_supply(u, hex) {
+ let src = supply_source_from_hex(hex)
+ game.units[u] = (game.units[u] & ~UNIT_SUPPLY_MASK) | (src << UNIT_SUPPLY_SHIFT)
}
function unit_lost_steps(u) {
- return game.units[u] & 3
+ return (game.units[u] & UNIT_STEPS_MASK) >> UNIT_STEPS_SHIFT
}
function set_unit_lost_steps(u, n) {
- game.units[u] = (game.units[u] & ~3) | n
+ game.units[u] = (game.units[u] & ~UNIT_STEPS_MASK) | (n << UNIT_STEPS_SHIFT)
+}
+
+function unit_steps(u) {
+ return units[u].steps - unit_lost_steps(u)
+}
+
+function set_unit_steps(u, n) {
+ set_unit_lost_steps(u, units[u].steps - n)
+}
+
+function is_unit_moved(u) {
+ return set_has(game.moved, u)
+}
+
+function set_unit_moved(u) {
+ set_add(game.moved, u)
+}
+
+function is_unit_fired(u) {
+ return set_has(game.fired, u)
+}
+
+function set_unit_fired(u) {
+ set_add(game.fired, u)
}
function eliminate_unit(u) {
@@ -229,52 +267,41 @@ function reduce_unit(u) {
return hp
}
-function is_unit_supplied(u) {
- return (game.units[u] & 4) === 4
-}
-
-function set_unit_supplied(u) {
- game.units[u] |= 4
-}
-
-function clear_unit_supplied(u) {
- game.units[u] &= ~4
-}
+// === UNIT DATA ===
-function is_unit_disrupted(u) {
- return (game.units[u] & 8) === 8
-}
-
-function set_unit_disrupted(u) {
- game.units[u] |= 8
+function find_unit(name) {
+ for (let u = 0; u < units.length; ++u)
+ if (units[u].name === name)
+ return u
+ throw new Error("cannot find named block: " + name)
}
-function clear_unit_disrupted(u) {
- game.units[u] &= ~8
+function unit_name(u) {
+ return units[u].name
}
-function is_unit_moved(u) {
- return (game.units[u] & 16) === 16
+function unit_speed(u) {
+ return units[u].speed
}
-function set_unit_moved(u) {
- game.units[u] |= 16
+function unit_class(u) {
+ return units[u].class
}
-function clear_unit_moved(u) {
- game.units[u] &= ~16
+function is_artillery_unit(u) {
+ return units[u].class === ARTILLERY
}
-function is_unit_fired(u) {
- return set_has(game.fired, u)
+function is_armor_unit(u) {
+ return units[u].class === ARMOR
}
-function set_unit_fired(u) {
- set_add(game.fired, u)
+function is_infantry_unit(u) {
+ return units[u].class === INFANTRY
}
-function unit_steps(u) {
- return units[u].steps - unit_lost_steps(u)
+function is_antitank_unit(u) {
+ return units[u].class === ANTITANK
}
function is_unit_elite(u) {
@@ -296,10 +323,6 @@ function unit_hp(u) {
return unit_steps(u) * unit_hp_per_step(u)
}
-function set_unit_steps(u, n) {
- set_unit_lost_steps(u, units[u].steps - n)
-}
-
function is_friendly_unit(u) {
if (game.active === AXIS)
return is_axis_unit(u)
@@ -332,6 +355,8 @@ function is_axis_unit(u) {
return units[u].nationality !== 'allied'
}
+// === MAP STATE ===
+
function is_axis_hex(x) {
if (!hex_exists[x])
return false
@@ -475,10 +500,27 @@ function claim_hex_control_for_defender(a) {
})
}
-function claim_stuff() {
- for (let x of all_hexes)
- if (is_new_battle_hex(x))
- claim_hex_control_for_defender(x)
+// === SUPPLY CARDS ===
+
+function draw_supply_card(pile) {
+ let x = random(pile[0] + pile[1])
+ if (x < pile[0]) {
+ pile[0] --
+ return 0
+ } else {
+ pile[1] --
+ return 1
+ }
+}
+
+function deal_axis_supply_cards(n) {
+ for (let i = 0; i < n; ++i)
+ game.axis_hand[draw_supply_card(game.draw_pile)]++
+}
+
+function deal_allied_supply_cards(n) {
+ for (let i = 0; i < n; ++i)
+ game.allied_hand[draw_supply_card(game.draw_pile)]++
}
// === SUPPLY NETWORK ===
@@ -501,27 +543,6 @@ function list_control_hexes(side) {
return control
}
-function to_side(a, b, s) {
- if (s < 3)
- return a * 3 + s
- return b * 3 + s - 3
-}
-
-function to_side_id(a, b) {
- if (a > b) {
- let c = b
- b = a
- a = c
- }
- if (a + hexnext[0] === b)
- return a * 3 + 0
- else if (a + hexnext[1] === b)
- return a * 3 + 1
- else if (a + hexnext[2] === b)
- return a * 3 + 2
- throw new Error("not a hexside " + a + " to " + b);
-}
-
function ind(d, msg, here, ...extra) {
console.log(new Array(d).fill("-").join("") + msg, here, "("+hex_name[here]+")", extra.join(" "))
}
@@ -981,8 +1002,7 @@ function goto_supply_check() {
}
function clear_all_unit_moved() {
- for (let u = 0; u < units.length; ++u)
- clear_unit_moved(u)
+ game.moved.length = 0
}
function goto_turn_option() {
@@ -1551,6 +1571,7 @@ states.refuse_battle_to = {
hex(to) {
let who = game.selected[0]
set_unit_hex(who, to)
+ set_unit_disrupted(who)
game.selected = []
game.state = 'refuse_battle_who'
},
@@ -2576,6 +2597,7 @@ exports.setup = function (seed, scenario, options) {
// current turn option and selected moves
turn_option: null,
+ moved: [],
side_limit: {},
rommel: 0,
from1: 0,
@@ -2612,6 +2634,7 @@ exports.view = function(state, current) {
view = {
month: game.month,
units: game.units,
+ moved: game.moved,
axis_hexes: game.axis_hexes,
allied_hexes: game.allied_hexes,
axis_sides: game.axis_sides,
@@ -2652,9 +2675,9 @@ function gen_action_hex(x) {
// === COMMON TEMPLATE ===
-function random(n) {
+function random(range) {
// https://www.ams.org/journals/mcom/1999-68-225/S0025-5718-99-00996-5/S0025-5718-99-00996-5.pdf
- return (game.seed = game.seed * 200105 % 34359738337) % n
+ return (game.seed = game.seed * 200105 % 34359738337) % range
}
function shuffle(deck) {