summaryrefslogtreecommitdiff
path: root/rules.js
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2024-10-17 22:59:50 +0200
committerTor Andersson <tor@ccxvii.net>2024-10-17 23:10:06 +0200
commit0e80195d27b4eab2af57b89d6f9348cb39de19d4 (patch)
tree9deb3ff8aeafcc675d5765891bf485bffdb3b244 /rules.js
parentd40f4d5eae30fd292ef0bffac7b6479291d5550a (diff)
downloadmaria-0e80195d27b4eab2af57b89d6f9348cb39de19d4.tar.gz
victory and elector conquest markers
Diffstat (limited to 'rules.js')
-rw-r--r--rules.js288
1 files changed, 217 insertions, 71 deletions
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])