From 0e80195d27b4eab2af57b89d6f9348cb39de19d4 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Thu, 17 Oct 2024 22:59:50 +0200 Subject: victory and elector conquest markers --- play.js | 97 ++++++++++++++++----- rules.js | 288 +++++++++++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 293 insertions(+), 92 deletions(-) diff --git a/play.js b/play.js index 0f92565..4e560aa 100644 --- a/play.js +++ b/play.js @@ -491,18 +491,24 @@ function on_init() { for (let e of ui.troops) ui.pieces_element.appendChild(e) - /* - ui.conquest = [] - ui.retro = [] - for (let s of all_objectives) { - for (let pow = 0; pow < 7; ++pow) { - if (set_has(objective1[pow], s) || set_has(objective2[pow], s)) { - ui.conquest[s] = create_conquest("marker conquest " + power_class[pow], s) - ui.retro[s] = create_conquest("marker retroactive " + power_class[pow], s) - } + ui.victory = [ [], [], [], [], [], [] ] + ui.retro = [ [], [], [], [], [], [] ] + for (let pow of [ P_FRANCE, P_PRUSSIA, P_PRAGMATIC, P_AUSTRIA ]) { + ui.victory[pow] = [] + ui.retro[pow] = [] + for (let i = 0; i < 100; ++i) { + ui.victory[pow].push(create_conquest("marker victory_marker_" + power_class[pow])) + ui.retro[pow].push(create_conquest("marker question_marker_" + power_class[pow])) } } - */ + + ui.elector = [ [], [], [], [], [], [] ] + for (let i = 0; i < 10; ++i) { + ui.elector[P_FRANCE].push(create_conquest("marker elector_marker_france")) + ui.elector[P_PRUSSIA].push(create_conquest("marker elector_marker_prussia")) + ui.elector[P_SAXONY].push(create_conquest("marker elector_marker_saxony")) + ui.elector[P_AUSTRIA].push(create_conquest("marker elector_marker_austria_pragmatic")) + } ui.tc = [] make_tc_deck(0) @@ -827,15 +833,54 @@ function layout_turn_marker() { } } -function create_conquest(style, s) { +function create_conquest(style) { + let e = document.createElement("div") + e.className = style + return e +} + +const used_victory = [ 0, 0, 0, 0, 0, 0 ] +const used_elector = [ 0, 0, 0, 0, 0, 0 ] +const used_retro = [ 0, 0, 0, 0, 0, 0 ] + +function layout_victory(s, pow) { + let e = ui.victory[pow][used_victory[pow]++] let x = data.cities.x[s] let y = data.cities.y[s] - let e = document.createElement("div") - e.dataset.id = s e.style.left = (x - 16) + "px" e.style.top = (y - 16) + "px" - e.className = style - return e + ui.markers_element.appendChild(e) +} + +function layout_retro(s, pow) { + let e = ui.retro[pow][used_retro[pow]++] + let x = data.cities.x[s] + let y = data.cities.y[s] + e.style.left = (x - 16) + "px" + e.style.top = (y - 16) + "px" + ui.markers_element.appendChild(e) +} + +function layout_victory_pool(pow, max, x, y) { + let n = 0 + for (let i = 0; i < view.victory.length; i += 2) + if (view.victory[i+1] === pow) + ++n + for (let i = 0; i < max - n; ++i) { + let e = ui.victory[pow][used_victory[pow]++] + e.style.left = (x - 16 + (i%5) * 20) + "px" + e.style.top = (y - 16 + (i/5|0) * 20) + "px" + ui.markers_element.appendChild(e) + } +} + +function layout_elector(s, pow) { + let e = ui.elector[pow][used_elector[pow]++] + let x = data.cities.x[s] + let y = data.cities.y[s] + e.style.left = (x - 16) + "px" + e.style.top = (y - 16) + "px" + ui.markers_element.appendChild(e) } function update_favicon() { @@ -890,6 +935,10 @@ function on_update() { ui.header.classList.toggle("pragmatic", view.power === P_PRAGMATIC) ui.header.classList.toggle("austria", view.power === P_AUSTRIA) + used_victory.fill(0) + used_elector.fill(0) + used_retro.fill(0) + sort_power_panel(true) for (let p = 0; p < 20; ++p) @@ -954,12 +1003,18 @@ function on_update() { } ui.markers_element.replaceChildren() - /* - for (let s of view.conquest) - ui.markers_element.appendChild(ui.conquest[s]) - for (let s of view.retro) - ui.markers_element.appendChild(ui.retro[s]) - */ + + for (let i = 0; i < view.victory.length; i += 2) + layout_victory(view.victory[i], view.victory[i+1]) + for (let i = 0; i < view.elector.length; i += 2) + layout_elector(view.elector[i], view.elector[i+1]) + for (let i = 0; i < view.retro.length; i += 2) + layout_retro(view.retro[i], view.retro[i+1]) + + layout_victory_pool(P_FRANCE, 11, 83, 1560) + layout_victory_pool(P_AUSTRIA, 8, 2334, 1561) + layout_victory_pool(P_PRUSSIA, 13, 1927, 76) + layout_victory_pool(P_PRAGMATIC, 8, 797, 98) layout_turn_marker() diff --git a/rules.js b/rules.js index ab7b143..b2ebc3a 100644 --- a/rules.js +++ b/rules.js @@ -1,8 +1,42 @@ "use strict" -// TODO: game.selected - singleton instead of array +// RULE QUESTIONS: subsidy contract - if major fortress enemy controlled? - ignore contract or waste card? -// TODO: re-entering supply trains during movement (10.2) +/* TODO + +victory & elector marker +victory check +supreme commander in mixed stacks +supply phase + hussar payment +re-entering supply trains during movement (10.2) + supply payment + +winter + +tc draw + no TCs for minor power if major fortress enemy controlled + +-- advanced -- + +tc subsidies +subsidy contracts + +arenberg supply exception +arenberg re-entry exception + +victory boxes +politics +imperial election + +political changes + prussia annexes silesia + france reduces military objectives + saxony's defection + +neutrality + +*/ // TODO: austria/pragmatic action step - show both sides cards and interleave movement on flanders map // PLAN: move all austria on bohemia first, then alternate pragmatic and austria activations on flanders map @@ -86,6 +120,24 @@ const IMPERIAL_ELECTION = 25 const ELIMINATED = data.cities.name.length const REMOVED = ELIMINATED + 1 +const TRIER = find_city("Trier") +const MAINZ = find_city("Mainz") +const KOLN = find_city("Köln") +const MANNHEIM = find_city("Mannheim") + +const LIEGNITZ = find_city("Liegnitz") +const GLOGAU = find_city("Glogau") +const BRESLAU = find_city("Breslau") +const BRIEG = find_city("Brieg") +const GLATZ = find_city("Glatz") +const NEISSE = find_city("Neisse") +const COSEL = find_city("Cosel") + +const MUNCHEN = find_city("München") +const DRESDEN = find_city("Dresden") + +const ENGLAND = find_city("England") + const max_power_troops = [ 5*8, 1*8, 4*8, 1*8, 3*8, 6*8 ] const all_powers = [ 0, 1, 2, 3, 4, 5 ] @@ -227,6 +279,46 @@ const all_france_allied_generals = [ const all_powers_prussia_saxony_pragmatic_austria = [ P_PRUSSIA, P_SAXONY, P_PRAGMATIC, P_AUSTRIA ] const all_powers_france_bavaria_pragmatic_austria = [ P_FRANCE, P_BAVARIA, P_PRAGMATIC, P_AUSTRIA ] const all_powers_france_bavaria_prussia_saxony = [ P_FRANCE, P_BAVARIA, P_PRUSSIA, P_SAXONY ] +const all_powers_pragmatic_austria = [ P_PRAGMATIC, P_AUSTRIA ] + +const all_powers_none = [] +const all_powers_bavaria = [ P_BAVARIA ] +const all_powers_saxony = [ P_SAXONY ] +const all_powers_bavaria_saxony = [ P_BAVARIA, P_SAXONY ] + +function coop_major_power(pow) { + if (pow === P_BAVARIA) + return P_FRANCE + if (pow === P_SAXONY) + return P_PRUSSIA + return pow +} + +function all_friendly_minor_powers(pow) { + switch (pow) { + case P_FRANCE: + case P_BAVARIA: + case P_PRUSSIA: + case P_SAXONY: + return all_powers_bavaria_saxony + case P_PRAGMATIC: + case P_AUSTRIA: + return all_powers_none + } +} + +function all_enemy_powers(pow) { + switch (pow) { + case P_FRANCE: + case P_BAVARIA: + case P_PRUSSIA: + case P_SAXONY: + return all_powers_pragmatic_austria + case P_PRAGMATIC: + case P_AUSTRIA: + return all_powers_france_bavaria_prussia_saxony + } +} function all_non_coop_powers(pow) { switch (pow) { @@ -399,12 +491,23 @@ function log_move_path() { /* OBJECTIVES */ -const all_objectives = [] -set_add_all(all_objectives, data.type.major_fortress) -set_add_all(all_objectives, data.type.minor_fortress) +const all_fortresses = [] +set_add_all(all_fortresses, data.type.major_fortress) +set_add_all(all_fortresses, data.type.minor_fortress) + +const all_home_country_fortresses = [] + +all_home_country_fortresses[P_FRANCE] = set_intersect(all_fortresses, data.country.France) +all_home_country_fortresses[P_BAVARIA] = set_intersect(all_fortresses, data.country.Bavaria) +all_home_country_fortresses[P_PRUSSIA] = set_intersect(all_fortresses, data.country.Prussia) +all_home_country_fortresses[P_SAXONY] = set_intersect(all_fortresses, data.country.Saxony) +all_home_country_fortresses[P_AUSTRIA] = set_intersect(all_fortresses, data.country.Austria) +all_home_country_fortresses[P_PRAGMATIC] = set_intersect(all_fortresses, data.country.Netherlands) + +const all_silesian_fortresses = set_intersect(all_fortresses, data.country.Silesia) const protect_range = [] -for (let s of all_objectives) +for (let s of all_fortresses) make_protect_range(protect_range[s] = [], s, s, 3) function make_protect_range(result, start, here, range) { @@ -416,40 +519,70 @@ function make_protect_range(result, start, here, range) { } } +function is_fortress(s) { + return set_has(all_fortresses, s) +} + +function is_enemy_home_country(s) { + for (let other of all_enemy_powers(game.power)) + if (set_has(all_home_country_fortresses[other], s)) + return true + return false +} + +function is_friendly_minor_home_country(s) { + for (let other of all_friendly_minor_powers(game.power)) + if (set_has(all_home_country_fortresses[other], s)) + return true + return false +} + function is_enemy_controlled_fortress(s) { - // TODO + for (let other of all_enemy_powers(game.power)) + if (is_power_controlled_fortress(other, s)) + return true return false } -function is_conquest_space(_pow, s) { - // TODO - return set_has(all_objectives, s) +function is_power_controlled_fortress(pow, s) { + if (map_get(game.elector, s, -1) === pow) + return true + if (map_get(game.victory, s, -1) === pow) + return true + if (set_has(all_home_country_fortresses[pow], s)) + return true + return false } -function is_reconquest_space(_pow, s) { - // TODO - return set_has(all_objectives, s) +function set_control_of_fortress(s, pow) { + if (s === TRIER || s === MAINZ || s === KOLN || s === MANNHEIM) { + if (pow === P_AUSTRIA || pow === P_PRAGMATIC) + map_set(game.elector, s, P_AUSTRIA) + else + map_set(game.elector, s, P_FRANCE) + return + } + + if (is_enemy_home_country(s) || is_friendly_minor_home_country(s) || set_has(all_silesian_fortresses, s)) + map_set(game.victory, s, pow) + else + map_delete(game.victory, s) } + function is_space_protected_by_piece(s, p) { return set_has(protect_range[s], game.pos[p]) } function is_protected_from_conquest(s) { + /* + TODO for (let pow of all_powers) { for (let p of all_power_generals[pow]) if (is_space_protected_by_piece(s, p)) return true } - return false -} - -function is_protected_from_reconquest(s) { - for (let pow of all_powers) { - for (let p of all_power_generals[pow]) - if (is_space_protected_by_piece(s, p)) - return true - } + */ return false } @@ -461,6 +594,12 @@ function tc_per_turn() { let n = tc_per_turn_table[game.power] // TODO: subsidies + if (game.turn <= 3) { + if (game.power === P_FRANCE) + --n + if (game.power === P_BAVARIA) + ++n + } return n } @@ -881,6 +1020,8 @@ function draw_tc(n) { function goto_tactical_cards() { + // TODO: pause to decide subsidy (france/bavaria) + // TODO: no TC (even subsidy) if major fortress is enemy controlled draw_tc(tc_per_turn()) @@ -985,7 +1126,6 @@ function goto_movement() { log_br() game.move_conq = [] - game.move_reconq = [] } function can_train_move_anywhere(p) { @@ -1075,9 +1215,8 @@ states.movement = { set_clear(game.moved) - log_conquest(game.move_conq, game.move_reconq) + log_conquest(game.move_conq) delete game.move_conq - delete game.move_reconq // MARIA: recruit during winter goto_recruit() goto_combat() @@ -1111,8 +1250,6 @@ function can_move_general_to(from, to) { return false if (has_non_cooperative_general(to)) return false - if (has_enemy_supply_train(to)) - return false if (count_generals(to) >= 2) return false return true @@ -1129,27 +1266,14 @@ function move_general_to(to, is_force_march) { // Cannot conquer if force marching. // Cannot conquer if out of supply. if (!is_force_march && !is_out_of_supply(who)) { - - // conquer space - if (is_conquest_space(pow, from) && !set_has(game.conquest, from)) { + if (is_enemy_controlled_fortress(from)) { if (is_protected_from_conquest(from)) { - set_add(game.retro, from) + map_set(game.retro, from, coop_major_power(game.power)) } else { set_add(game.move_conq, from) - set_add(game.conquest, from) - } - } - - // re-conquer space - if (is_reconquest_space(pow, from) && set_has(game.conquest, from)) { - if (is_protected_from_reconquest(from)) { - set_add(game.retro, from) - } else { - set_add(game.move_reconq, from) - set_delete(game.conquest, from) + set_control_of_fortress(from, coop_major_power(game.power)) } } - } // eliminate supply train @@ -2288,19 +2412,12 @@ states.retreat_done = { /* RETRO-ACTIVE CONQUEST */ -function log_conquest(conq, reconq) { - if (conq.length > 0 || reconq.length > 0) { +function log_conquest(conq) { + if (conq.length > 0) { log_br() - if (conq.length > 0) { - log("Conquered") - for (let s of conq) - log(">S" + s) - } - if (reconq.length > 0) { - log("Reconquered") - for (let s of reconq) - log(">S" + s) - } + log("Conquered") + for (let s of conq) + log(">S" + s) } } @@ -2308,26 +2425,19 @@ function goto_retroactive_conquest() { delete game.combat let conq = [] - let reconq = [] - for (let s of game.retro) { - if (is_conquest_space(game.power, s)) { + map_for_each(game.retro, function (s, pow) { + if (is_enemy_controlled_fortress(s)) { if (!is_protected_from_conquest(s)) { - set_add(game.conquest, s) + set_control_of_fortress(s, pow) conq.push(s) } } - if (is_reconquest_space(game.power, s)) { - if (!is_protected_from_reconquest(s)) { - set_delete(game.conquest, s) - reconq.push(s) - } - } - } + }) - log_conquest(conq, reconq) + log_conquest(conq) - set_clear(game.retro) + map_clear(game.retro) end_action_stage() } @@ -2714,8 +2824,9 @@ exports.setup = function (seed, _scenario, _options) { pos: setup_piece_position.slice(), oos: 0, - troops: setup_min_troops.map(n => 0), - conquest: [], + troops: new Array(20).fill(0), + victory: [], + elector: [], moved: [], retro: [], @@ -2729,6 +2840,19 @@ exports.setup = function (seed, _scenario, _options) { shuffle_bigint(game.deck) + map_set(game.elector, TRIER, P_AUSTRIA) + map_set(game.elector, MAINZ, P_AUSTRIA) + map_set(game.elector, KOLN, P_FRANCE) + map_set(game.elector, MANNHEIM, P_FRANCE) + + map_set(game.victory, LIEGNITZ, P_PRUSSIA) + map_set(game.victory, GLOGAU, P_PRUSSIA) + map_set(game.victory, BRESLAU, P_AUSTRIA) + map_set(game.victory, BRIEG, P_AUSTRIA) + map_set(game.victory, GLATZ, P_AUSTRIA) + map_set(game.victory, NEISSE, P_AUSTRIA) + map_set(game.victory, COSEL, P_AUSTRIA) + // Deal initial cards for (let pow of all_powers) for (let i = 0; i < setup_initial_tcs[pow]; ++i) @@ -2885,7 +3009,8 @@ exports.view = function (state, player) { turn: game.turn, pos: game.pos, oos: game.oos, - conquest: game.conquest, + victory: game.victory, + elector: game.elector, troops: mask_troops(player), hand: mask_hand(player), pt: total_troops_list(), @@ -3194,6 +3319,10 @@ function set_intersect(one, two) { // Map as plain sorted array of key/value pairs +function map_clear(set) { + set.length = 0 +} + function map_has(map, key) { let a = 0 let b = (map.length >> 1) - 1 @@ -3244,6 +3373,23 @@ function map_set(map, key, value) { array_insert_pair(map, a<<1, key, value) } +function map_delete(map, key) { + let a = 0 + let b = (map.length >> 1) - 1 + while (a <= b) { + let m = (a + b) >> 1 + let x = map[m<<1] + if (key < x) + b = m - 1 + else if (key > x) + a = m + 1 + else { + array_remove_pair(map, m<<1) + return + } + } +} + function map_for_each(map, f) { for (let i = 0; i < map.length; i += 2) f(map[i], map[i+1]) -- cgit v1.2.3