From d5c4bb1f834a9b88599681a5ed66b78c15e96a70 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sat, 22 Jun 2024 14:18:42 +0200 Subject: manual winter attrition, place pc, and isolation segments. --- rules.js | 516 +++++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 352 insertions(+), 164 deletions(-) diff --git a/rules.js b/rules.js index f216bdf..15a2e14 100644 --- a/rules.js +++ b/rules.js @@ -976,9 +976,9 @@ function disperse_continental_congress() { set_flag(F_CONGRESS_WAS_DISPERSED) } -/* MOVE GENERATORS */ +/* PLACE/REMOVE PC IN COLONY */ -function gen_remove_british_pc_from(list_of_colonies) { +function gen_remove_british_pc_in_colony(list_of_colonies) { for (let colony of list_of_colonies) { for (let space of COLONIES[colony]) { if (has_british_pc(space) && has_no_british_cu(space)) { @@ -988,64 +988,7 @@ function gen_remove_british_pc_from(list_of_colonies) { } } -function gen_remove_american_pc() { - for (let space of all_spaces) { - if (has_american_pc(space) && has_no_american_unit(space)) { - gen_action_space(space) - } - } -} - -function gen_remove_american_pc_from(list_of_colonies) { - for (let colony of list_of_colonies) { - for (let space of COLONIES[colony]) { - if (has_american_pc(space) && has_no_american_unit(space)) { - gen_action_space(space) - } - } - } -} - -function gen_remove_american_pc_from_non_port(list_of_colonies) { - for (let colony of list_of_colonies) { - for (let space of COLONIES[colony]) { - if (!is_port(space)) { - if (has_american_pc(space) && has_no_american_unit(space)) { - gen_action_space(space) - } - } - } - } -} - -function gen_remove_american_pc_within_two_spaces_of_a_british_general() { - let candidates = [] - for (let g of BRITISH_GENERALS) { - let a = location_of_general(g) - if (is_map_space(a)) { - set_add(candidates, a) - for (let b of SPACES[a].adjacent) { - set_add(candidates, b) - for (let c of SPACES[b].adjacent) { - set_add(candidates, c) - } - } - } - } - for (let space of candidates) - if (has_american_pc(space) && has_no_american_unit(space)) - gen_action_space(space) -} - -function gen_place_american_pc() { - for (let space of all_spaces) { - if (has_no_pc(space) && has_no_british_playing_piece(space)) { - gen_action_space(space) - } - } -} - -function gen_place_american_pc_in(list_of_colonies) { +function gen_place_american_pc_in_colony(list_of_colonies) { for (let colony of list_of_colonies) { for (let space of COLONIES[colony]) { if (has_no_pc(space) && has_no_british_playing_piece(space)) { @@ -1071,7 +1014,7 @@ states.committees_of_correspondence = { view.prompt = "Committees of Correspondence: Place 1 PC marker in each of the 13 colonies." if (game.colonies.length > 0) { view.prompt += " " + game.colonies.length + " left." - gen_place_american_pc_in(game.colonies) + gen_place_american_pc_in_colony(game.colonies) } else { view.prompt += " Done." view.actions.next = 1 @@ -1752,14 +1695,13 @@ states.ops_general_who = { gen_activate_general() view.actions.pass = 1 }, - place_british_pc(where) { - clear_flag(F_LANDING_PARTY) - place_british_pc(where) - end_strategy_card() - }, - flip_pc(where) { + space(where) { + push_undo() clear_flag(F_LANDING_PARTY) - flip_pc(where) + if (has_american_pc()) + flip_pc(where) + else + place_british_pc(where) end_strategy_card() }, general(g) { @@ -1777,9 +1719,9 @@ function gen_landing_party() { for (let space of all_spaces) { if (!is_fortified_port(space) && is_non_blockaded_port(space)) { if (has_american_pc(space) && has_no_american_unit(space)) - gen_action("flip_pc", space) + gen_action_space(space) if (has_no_pc(space) && has_no_american_unit(space) && has_no_british_playing_piece(space)) - gen_action("place_british_pc", space) + gen_action_space(space) } } } @@ -2818,23 +2760,38 @@ function lose_regular_advantage() { events.baron_von_steuben_trains_the_continental_army = function (c, card) { play_card(c) - if (is_general_on_map(WASHINGTON)) { + if (is_general_on_map(WASHINGTON)) + game.state = "baron_von_steuben_trains_the_continental_army" + else + end_strategy_card() +} + +states.baron_von_steuben_trains_the_continental_army = { + prompt() { + view.prompt = "Baron von Steuben. Place 2 CU with Washington." + gen_action_general(WASHINGTON) + // TODO: lose regulars manual? + }, + general(_) { let where = location_of_general(WASHINGTON) logp("placed 2 CU with Washington in " + where) place_american_cu(where, 2) lose_regular_advantage() - } - end_strategy_card() + end_strategy_card() + }, } events.advance_french_alliance = function (c, card) { play_card(c) + // TODO: advance alliance manual? + // game.state = "advance_french_alliance" advance_french_alliance(card.count) end_strategy_card() } events.remove_french_navy = function (c, card) { play_card(c) + // TODO: remove french navy manual? game.french_navy = game.year + 1 end_strategy_card() } @@ -2850,9 +2807,10 @@ states.remove_british_pc_from = { prompt() { view.prompt = "Remove British PC markers from " + game.where.map(x=>data.colony_name[x]).join(", ") + ". " + game.count + " left." view.actions.pass = 1 - gen_remove_british_pc_from(game.where) + gen_remove_british_pc_in_colony(game.where) }, space(where) { + push_undo() remove_pc(where) if (--game.count === 0) { delete game.where @@ -2860,6 +2818,7 @@ states.remove_british_pc_from = { } }, pass() { + push_undo() delete game.where end_strategy_card() }, @@ -2875,15 +2834,19 @@ states.remove_american_pc = { prompt() { view.prompt = "Remove American PC markers. " + game.count + " left." view.actions.pass = 1 - gen_remove_american_pc() + for (let space of all_spaces) + if (has_american_pc(space) && has_no_american_unit(space)) + gen_action_space(space) }, space(where) { + push_undo() remove_pc(where) if (--game.count === 0) { end_strategy_card() } }, pass() { + push_undo() end_strategy_card() }, } @@ -2898,10 +2861,14 @@ events.remove_american_pc_from = function (c, card) { states.remove_american_pc_from = { prompt() { view.prompt = "Remove American PC markers from " + game.where.join(", ") + ". " + game.count + " left." + for (let colony of game.where) + for (let space of COLONIES[colony]) + if (has_american_pc(space) && has_no_american_unit(space)) + gen_action_space(space) view.actions.pass = 1 - gen_remove_american_pc_from(game.where) }, space(where) { + push_undo() remove_pc(where) if (--game.count === 0) { delete game.where @@ -2909,6 +2876,7 @@ states.remove_american_pc_from = { } }, pass() { + push_undo() delete game.where end_strategy_card() }, @@ -2923,12 +2891,20 @@ events.remove_american_pc_from_non_port = function (c, card) { states.remove_american_pc_from_non_port = { prompt() { - view.prompt = - "Remove American PC markers from non-Port space in " + game.where.join(", ") + ". " + game.count + " left." + view.prompt = "Remove American PC markers from non-Port space in " + game.where.join(", ") + ". " + game.count + " left." + for (let colony of game.where) { + for (let space of COLONIES[colony]) { + if (!is_port(space)) { + if (has_american_pc(space) && has_no_american_unit(space)) { + gen_action_space(space) + } + } + } + } view.actions.pass = 1 - gen_remove_american_pc_from_non_port(game.where) }, space(where) { + push_undo() remove_pc(where) if (--game.count === 0) { delete game.where @@ -2936,6 +2912,7 @@ states.remove_american_pc_from_non_port = { } }, pass() { + push_undo() delete game.where end_strategy_card() }, @@ -2947,6 +2924,25 @@ events.remove_american_pc_within_two_spaces_of_a_british_general = function (c, game.state = "remove_american_pc_within_two_spaces_of_a_british_general" } +function gen_remove_american_pc_within_two_spaces_of_a_british_general() { + let candidates = [] + for (let g of BRITISH_GENERALS) { + let a = location_of_general(g) + if (is_map_space(a)) { + set_add(candidates, a) + for (let b of SPACES[a].adjacent) { + set_add(candidates, b) + for (let c of SPACES[b].adjacent) { + set_add(candidates, c) + } + } + } + } + for (let space of candidates) + if (has_american_pc(space) && has_no_american_unit(space)) + gen_action_space(space) +} + states.remove_american_pc_within_two_spaces_of_a_british_general = { prompt() { view.prompt = "Remove American PC markers within two spaces of a British general. " + game.count + " left." @@ -2954,6 +2950,7 @@ states.remove_american_pc_within_two_spaces_of_a_british_general = { gen_remove_american_pc_within_two_spaces_of_a_british_general() }, space(where) { + push_undo() remove_pc(where) if (--game.count === 0) { delete game.where @@ -2961,6 +2958,7 @@ states.remove_american_pc_within_two_spaces_of_a_british_general = { } }, pass() { + push_undo() delete game.where end_strategy_card() }, @@ -2975,16 +2973,19 @@ events.place_american_pc = function (c, card) { states.place_american_pc = { prompt() { view.prompt = "Place American PC markers. " + game.count + " left." + for (let space of all_spaces) + if (has_no_pc(space) && has_no_british_playing_piece(space)) + gen_action_space(space) view.actions.pass = 1 - gen_place_american_pc() }, - place_american_pc(where) { + space(where) { + push_undo() place_american_pc(where) - if (--game.count === 0) { + if (--game.count === 0) end_strategy_card() - } }, pass() { + push_undo() end_strategy_card() }, } @@ -3000,9 +3001,9 @@ states.place_american_pc_in = { prompt() { view.prompt = "Place American PC markers in " + game.where.join(", ") + ". " + game.count + " left." view.actions.pass = 1 - gen_place_american_pc_in(game.where) + gen_place_american_pc_in_colony(game.where) }, - place_american_pc(where) { + space(where) { place_american_pc(where) if (--game.count === 0) { delete game.where @@ -3062,10 +3063,12 @@ events.remove_american_cu = function (c, card) { states.remove_american_cu = { prompt() { view.prompt = "Remove one American CU from any space." + for (let space of all_spaces) + if (has_american_or_french_cu(space)) + gen_action_space(space) view.actions.pass = 1 - gen_remove_american_cu() }, - remove_cu(where) { + space(where) { if (count_american_cu(where) > 0) remove_american_cu(where, 1) else @@ -3077,13 +3080,6 @@ states.remove_american_cu = { }, } -function gen_remove_american_cu() { - for (let space of all_spaces) { - if (has_american_or_french_cu(space)) - gen_action("remove_cu", space) - } -} - events.remove_british_cu = function (c, card) { play_card(c) game.state = "remove_british_cu" @@ -3093,10 +3089,12 @@ events.remove_british_cu = function (c, card) { states.remove_british_cu = { prompt() { view.prompt = "Remove " + game.count + " British CU from any space." + for (let space of all_spaces) + if (has_british_cu(space)) + gen_action_space(space) view.actions.pass = 1 - gen_remove_british_cu() }, - remove_cu(where) { + space(where) { remove_british_cu(where, 1) if (--game.count === 0) end_strategy_card() @@ -3106,13 +3104,6 @@ states.remove_british_cu = { }, } -function gen_remove_british_cu() { - for (let space of all_spaces) { - if (has_british_cu(space)) - gen_action("remove_cu", space) - } -} - events.pennsylvania_and_new_jersey_line_mutinies = function (c, card) { play_card(c) set_flag(F_MUTINIES) @@ -3178,9 +3169,9 @@ states.declaration_of_independence = { view.prompt = "Declaration of Independence: Place 1 PC marker in each of the 13 colonies. " view.prompt += game.colonies.length + " left." view.actions.pass = 1 - gen_place_american_pc_in(game.colonies) + gen_place_american_pc_in_colony(game.colonies) }, - place_american_pc(space) { + space(space) { let colony = SPACES[space].colony set_delete(game.colonies, colony) place_american_pc(space) @@ -3217,8 +3208,10 @@ function goto_george_washington_captured() { states.george_washington_captured = { prompt() { view.prompt = "George Washington is captured! Remove American PC markers. " + game.count + " left." + for (let space of all_spaces) + if (has_american_pc(space) && has_no_american_unit(space)) + gen_action_space(space) view.actions.pass = 1 - gen_remove_american_pc() }, space(where) { remove_pc(where) @@ -3249,49 +3242,96 @@ function do_event(c) { /* WINTER ATTRITION PHASE */ -function apply_single_winter_attrition(owner, space) { +function apply_single_winter_attrition(remove_cu, space) { let die = roll_d6() - log(owner[0] + " attrition roll " + die + " in " + space) + log("Attrition roll " + die + " in " + space) if (die <= 3) { - log(owner[0] + " lost 1 CU in " + space) - remove_cu(owner, space, 1) + log("Lost 1 CU in " + space) + remove_cu(space, 1) } } -function apply_winter_attrition(owner, space, n) { +function apply_winter_attrition(remove_cu, space, n) { let half = Math.floor(n / 2) - log(owner[0] + " lost " + half + " CU in " + space) - remove_cu(owner, space, half) + log("Lost " + half + " CU in " + space) + remove_cu(space, half) } function goto_winter_attrition_phase() { - logbr() - log("Winter Attrition") + goto_american_winter_attrition() +} +function goto_american_winter_attrition() { + log("=a Winter Attrition") + game.active = P_AMERICA + game.attrition = [] for (let space of all_spaces) { let wq = is_winter_quarter_space(space) - let n_british = count_british_cu(space) let n_american = count_american_cu(space) let n_french = count_french_cu(space) let has_washington = is_general_at_location(WASHINGTON, space) + if (n_american > 0 || (n_french > 0 && !wq)) + game.attrition.push(space) + } + if (game.attrition.length > 0) + game.state = "american_winter_attrition" + else + end_american_winter_attrition() +} - if (n_british === 1 && !wq) - apply_single_winter_attrition(BRITISH, space, has_british_general(space)) - if (n_british > 1 && !wq) - apply_winter_attrition(BRITISH, space, n_british) +function end_american_winter_attrition() { + delete game.attrition + goto_british_winter_attrition() +} + +function goto_british_winter_attrition() { + log("=b Winter Attrition") + game.active = P_BRITAIN + game.attrition = [] + for (let space of all_spaces) { + let wq = is_winter_quarter_space(space) + let n_british = count_british_cu(space) + if (n_british > 0 && !wq) + game.attrition.push(space) + } + if (game.attrition.length > 0) + game.state = "british_winter_attrition" + else + end_british_winter_attrition() +} + +function end_british_winter_attrition() { + delete game.attrition + if (automatic_victory()) + return + goto_french_naval_phase() +} + +states.american_winter_attrition = { + prompt() { + view.prompt = "Winter Attrition." + for (let s of game.attrition) + gen_action_space(s) + if (game.attrition.length === 0) + view.actions.next = 1 + }, + space(s) { + let wq = is_winter_quarter_space(s) + let n_american = count_american_cu(s) + let n_french = count_french_cu(s) + let has_washington = is_general_at_location(WASHINGTON, s) if (n_american === 0 && n_french === 1 && !wq) - apply_single_winter_attrition(FRENCH, space, has_american_or_french_general(space)) + apply_single_winter_attrition(remove_french_cu, s, has_american_or_french_general(s)) if (n_american === 0 && n_french > 1 && !wq) - apply_winter_attrition(FRENCH, space, n_french) - + apply_winter_attrition(remove_french_cu, s, n_french) if (n_american === 1 && n_french === 0) - apply_single_winter_attrition(AMERICAN, space, has_american_or_french_general(space)) + apply_single_winter_attrition(remove_american_cu, s, has_american_or_french_general(s)) if (n_american > 1 && n_french === 0) { let n = n_american if (has_washington && wq) n = Math.max(0, n - 5) - apply_winter_attrition(AMERICAN, space, n) + apply_winter_attrition(remove_american_cu, s, n) } if (n_american > 0 && n_french > 0) { @@ -3302,28 +3342,53 @@ function goto_winter_attrition_phase() { // TODO: let player choose (but why would he ever choose the french?) let lose_american = Math.min(half, n_american) - log(owner[0] + " lost " + lose_american + " American CU in " + space) - remove_american_cu(space, n_american) + log("Lost " + lose_american + " American CU in " + s) + remove_american_cu(s, n_american) half -= lose_american n_american -= lose_american if (half > 0) { - log(owner[0] + " lost " + half + " French CU in " + space) - remove_french_cu(space, half) + log("Lost " + half + " French CU in " + s) + remove_french_cu(s, half) } } - } - if (automatic_victory()) - return + set_delete(game.attrition, s) + }, + next() { + end_american_winter_attrition() + }, +} - goto_french_naval_phase() +states.british_winter_attrition = { + prompt() { + view.prompt = "Winter Attrition." + for (let s of game.attrition) + gen_action_space(s) + if (game.attrition.length === 0) + view.actions.next = 1 + }, + space(s) { + let wq = is_winter_quarter_space(s) + let n_british = count_british_cu(s) + + if (n_british === 1 && !wq) + apply_single_winter_attrition(remove_british_cu, s, has_british_general(s)) + if (n_british > 1 && !wq) + apply_winter_attrition(remove_british_cu, s, n_british) + + set_delete(game.attrition, s) + }, + next() { + end_british_winter_attrition() + } } /* FRENCH NAVAL PHASE */ function goto_french_naval_phase() { if (game.french_navy !== -1) { + log("=a French Naval Phase") game.save = game.active game.active = P_AMERICA game.state = "place_french_navy" @@ -3342,6 +3407,7 @@ states.place_french_navy_trigger = { gen_place_french_navy() }, sea(zone) { + push_undo() logp("placed French Navy.") game.french_navy = zone goto_place_rochambeau() @@ -3380,9 +3446,20 @@ states.place_rochambeau = { } }, space(space) { + push_undo() logp("placed Rochambeau .") move_general(ROCHAMBEAU, space) move_cu(FRENCH, FRENCH_REINFORCEMENTS, space, 5) + game.state = "end_place_rochambeau" + }, +} + +states.end_place_rochambeau = { + prompt() { + view.prompt = "Done." + view.actions.next = 1 + }, + next() { end_place_rochambeau() }, } @@ -3408,11 +3485,12 @@ states.place_french_navy = { /* POLITICAL CONTROL PHASE: RETURN CONGRESS */ function goto_political_control_phase() { + log("# Political Control Phase") if (game.congress === CONTINENTAL_CONGRESS_DISPERSED) { game.active = P_AMERICA game.state = "return_continental_congress" } else { - goto_political_control_phase_2() + goto_place_pc_markers_segment() } } @@ -3437,42 +3515,99 @@ states.return_continental_congress = { }, place_continental_congress(where) { game.congress = where - goto_political_control_phase_2() + goto_place_pc_markers_segment() }, pass() { - goto_political_control_phase_2() + goto_place_pc_markers_segment() }, } /* POLITICAL CONTROL PHASE: PLACE PC MARKERS */ -function goto_political_control_phase_2() { - // TODO: manually place and remove - place_pc_markers_segment() - remove_isolated_american_pc_segment() - remove_isolated_british_pc_segment() - goto_end_phase() +function has_american_place_pc_markers_segment() { + for (let space of all_spaces) + if (has_american_army(space)) + if (has_no_pc(space) || has_british_pc(space)) + return true + return false } -function place_pc_markers_segment() { - for (let space of all_spaces) { - if (has_american_army(space)) { - if (has_no_pc(space)) - place_american_pc(space) - else if (has_british_pc(space)) - flip_pc(space) - } - if (has_british_army(space)) { - if (has_no_pc(space)) - place_british_pc(space) - else if (has_american_pc(space)) - flip_pc(space) - } +function has_british_place_pc_markers_segment() { + for (let space of all_spaces) + if (has_british_army(space)) + if (has_no_pc(space) || has_american_pc(space)) + return true + return false +} + +function gen_american_place_pc_markers_segment() { + for (let space of all_spaces) + if (has_american_army(space)) + if (has_no_pc(space) || has_british_pc(space)) + gen_action_space(space) + if (!view.actions.space) + view.actions.next = 1 +} + +function gen_british_place_pc_markers_segment() { + for (let space of all_spaces) + if (has_british_army(space)) + if (has_no_pc(space) || has_american_pc(space)) + gen_action_space(space) + if (!view.actions.space) + view.actions.next = 1 +} + +function goto_place_pc_markers_segment() { + if (has_american_place_pc_markers_segment()) { + game.active = P_AMERICA + game.state = "place_american_pc_markers_segment" + } else if (has_british_place_pc_markers_segment()) { + game.active = P_BRITAIN + game.state = "place_british_pc_markers_segment" + } else { + goto_remove_isolated_pc_marker_segment() } } +states.place_american_pc_markers_segment = { + prompt() { + view.prompt = "Place American PC markers." + gen_american_place_pc_markers_segment() + }, + space(s) { + if (has_no_pc(s)) + place_american_pc(s) + else if (has_british_pc(s)) + flip_pc(s) + }, + next() { + goto_place_pc_markers_segment() + }, +} + +states.place_british_pc_markers_segment = { + prompt() { + view.prompt = "Place British PC markers." + gen_british_place_pc_markers_segment() + }, + space(s) { + if (has_no_pc(s)) + place_british_pc(s) + else if (has_american_pc(s)) + flip_pc(s) + }, + next() { + goto_place_pc_markers_segment() + }, +} + /* POLITICAL CONTROL PHASE: REMOVE ISOLATED PC MARKERS */ +function goto_remove_isolated_pc_marker_segment() { + goto_remove_isolated_american_pc_segment() +} + function is_american_pc_root(space) { if (has_no_pc(space) && has_no_british_cu(space)) return true @@ -3535,8 +3670,8 @@ function spread_british_path(seen, from) { } } -function remove_isolated_american_pc_segment() { - log("Removed isolated American PC") +function goto_remove_isolated_american_pc_segment() { + game.active = P_AMERICA let seen = [] for (let space of all_spaces) { if (is_american_pc_root(space)) { @@ -3544,13 +3679,21 @@ function remove_isolated_american_pc_segment() { spread_american_path(seen, space) } } + + game.isolated = [] for (let space of all_spaces) if (has_american_pc(space) && !set_has(seen, space)) - remove_pc(space) + set_add(game.isolated, space) + + if (game.isolated.length > 0) { + game.active = P_AMERICA + game.state = "remove_isolated_pc_segment" + } else { + end_remove_isolated_american_pc_segment() + } } -function remove_isolated_british_pc_segment() { - log("Removed isolated British PC") +function goto_remove_isolated_british_pc_segment() { let seen = [] for (let space of all_spaces) { if (is_british_pc_root(space)) { @@ -3558,9 +3701,52 @@ function remove_isolated_british_pc_segment() { spread_british_path(seen, space) } } + + game.isolated = [] for (let space of all_spaces) if (has_british_pc(space) && !set_has(seen, space)) - remove_pc(space) + set_add(game.isolated, space) + + if (game.isolated.length > 0) { + game.active = P_BRITAIN + game.state = "remove_isolated_pc_segment" + } else { + end_remove_isolated_british_pc_segment() + } +} + +states.remove_isolated_pc_segment = { + prompt() { + if (game.isolated.length > 0) { + view.prompt = "Remove isolated PC markers. " + game.isolated.length + " left." + for (let s of game.isolated) + gen_action_space(s) + } else { + view.prompt = "Remove isolated PC markers. Done." + view.actions.next = 1 + } + }, + space(s) { + log("Removed American PC from " + s) + set_delete(s) + remove_pc(s) + }, + next() { + if (game.active === P_AMERICA) + end_remove_isolated_american_pc_segment() + else + end_remove_isolated_british_pc_segment() + }, +} + +function end_remove_isolated_american_pc_segment() { + delete game.isolated + goto_remove_isolated_british_pc_segment() +} + +function end_remove_isolated_british_pc_segment() { + delete game.isolated + goto_end_phase() } /* END PHASE */ @@ -3592,10 +3778,12 @@ function goto_end_phase() { states.european_war = { prompt() { view.prompt = "European War: Remove 2 British CU from any spaces. " + game.count + " left." + for (let space of all_spaces) + if (has_british_cu(space)) + gen_action_space(space) view.actions.pass = 1 - gen_remove_british_cu() }, - remove_cu(where) { + space(where) { remove_british_cu(where, 1) if (--game.count === 0) goto_end_phase() -- cgit v1.2.3