diff options
author | Tor Andersson <tor@ccxvii.net> | 2024-10-17 22:59:50 +0200 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2024-10-17 23:10:06 +0200 |
commit | 0e80195d27b4eab2af57b89d6f9348cb39de19d4 (patch) | |
tree | 9deb3ff8aeafcc675d5765891bf485bffdb3b244 /rules.js | |
parent | d40f4d5eae30fd292ef0bffac7b6479291d5550a (diff) | |
download | maria-0e80195d27b4eab2af57b89d6f9348cb39de19d4.tar.gz |
victory and elector conquest markers
Diffstat (limited to 'rules.js')
-rw-r--r-- | rules.js | 288 |
1 files changed, 217 insertions, 71 deletions
@@ -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]) |