From ca8a901cb4ef47356e6e82784aea0a3e8762e7b6 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Mon, 18 Jul 2022 14:53:35 +0200 Subject: state cleanup --- play.js | 62 ++++++++++++++-- rules.js | 249 ++++++++++++++++++++++++++++++++++----------------------------- 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) { -- cgit v1.2.3