From cd5a083ea9ade6a7f6ef7e16e336fff9f30343aa Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Mon, 27 May 2024 00:28:00 +0200 Subject: chevert unstack. stay in attack position. no move search. --- rules.js | 335 ++++++--------------------------------------------------------- 1 file changed, 32 insertions(+), 303 deletions(-) (limited to 'rules.js') diff --git a/rules.js b/rules.js index 37520d4..713e234 100644 --- a/rules.js +++ b/rules.js @@ -1,5 +1,8 @@ "use strict" +// TODO: prussian offensive (no capture when not offensive) +// TODO: final score summary at game end (FWC rules) + const R_FREDERICK = "Frederick" const R_ELISABETH = "Elisabeth" const R_MARIA_THERESA = "Maria Theresa" @@ -364,13 +367,6 @@ function clear_fate_effect() { game.fx = 0 } -function may_unstack() { - // TODO: 3-piece stack? - if (game.fx === NEXT_TURN_CHEVERT_MAY_NOT_UNSTACK) - return !set_has(game.selected, GEN_CHEVERT) - return true -} - function forbid_play_value_10_or_more() { if (game.fx === NEXT_TURN_IF_PRUSSIA_AND_FRANCE_FIGHT_EACH_OTHER_THEY_MAY_NOT_USE_TCS_WITH_VALUES_OF_10_OR_MORE) { let a = get_stack_power(game.attacker) @@ -1463,9 +1459,9 @@ states.movement = { game.major = 0 if (is_supply_train(p)) - resume_move_supply_train() + game.state = "move_supply_train" else - resume_move_general() + game.state = "move_general" }, confirm_end_movement() { this.end_movement() @@ -1483,7 +1479,6 @@ function format_move(max) { return ` up to ${n} cities.` } -// TODO: also force moving if in such a position and can move away function forbid_stopping_at(from) { switch (game.fx) { case NEXT_TURN_SOUBISE_AND_HILDBURGHAUSEN_MAY_NOT_ATTACK_WITH_THE_SAME_TC_SYMBOL: @@ -1500,22 +1495,6 @@ function forbid_stopping_at(from) { return false } -function forbid_attack(from, to) { - switch (game.fx) { - case NEXT_TURN_SOUBISE_AND_HILDBURGHAUSEN_MAY_NOT_ATTACK_WITH_THE_SAME_TC_SYMBOL: - return game.pos[GEN_SOUBISE] === from && game.ia_attack === get_space_suit(from) - case NEXT_TURN_NO_GENERAL_MAY_BE_ATTACKED_IN_THE_CITY_OF_HALLE: - return to === HALLE - case NEXT_TURN_CUMBERLAND_MAY_NOT_MOVE_INTO_ATTACK_POSITION: - return game.pos[GEN_CUMBERLAND] === from - case NEXT_TURN_SOUBISE_MAY_NOT_MOVE_INTO_ATTACK_POSITION: - return game.pos[GEN_SOUBISE] === from - case NEXT_TURN_FRIEDRICH_MAY_NOT_MOVE_INTO_ATTACK_POSITION: - return game.pos[GEN_FRIEDRICH] === from - } - return false -} - function forbid_capture(s) { switch (game.fx) { case NEXT_TURN_CUMBERLAND_MAY_NOT_MOVE_INTO_ATTACK_POSITION: @@ -1548,10 +1527,6 @@ function can_move_train_to(to) { return !has_any_piece(to) } -function can_continue_train_from(_) { - return true -} - function can_move_general_in_theory(p, to) { if (has_friendly_supply_train(to)) return false @@ -1595,65 +1570,6 @@ function can_continue_general_from(from) { return true } -function search_move(from, range, road_type, can_move_to, can_continue_from) { - let seen = [ from, -1 ] - let queue = [ from << 4 ] - while (queue.length > 0) { - let item = queue.shift() - let here = item >> 4 - let dist = (item & 15) + 1 - for (let next of data.cities[road_type][here]) { - if (map_has(seen, next)) - continue - if (!can_move_to(next)) - continue - if (dist <= range) { - map_set(seen, next, here) - if (can_continue_from(next)) - queue.push((next << 4) | dist) - } - } - } - return seen -} - -function resume_move_supply_train() { - if (game.count === 2 + game.major) { - end_move_piece() - } else { - let here = game.pos[game.selected[0]] - game.state = "move_supply_train" - if (game.major && game.count < 3) - game.move_major = search_move(here, 3 - game.count, "major_roads", can_move_train_to, can_continue_train_from) - else - game.move_major = [] - if (game.count < 2) - game.move_minor = search_move(here, 2 - game.count, "adjacent", can_move_train_to, can_continue_train_from) - else - game.move_minor = [] - } -} - -function resume_move_general() { - let range = movement_range() - if (game.count === range + game.major) { - end_move_piece() - } else { - game.state = "move_general" - /* NEW - let here = game.pos[game.selected[0]] - if (game.major && game.count < range+1) - game.move_major = search_move(here, range+1 - game.count, "major_roads", can_move_general_to, can_continue_general_from) - else - game.move_major = [] - if (game.count < range) - game.move_minor = search_move(here, range - game.count, "adjacent", can_move_general_to, can_continue_general_from) - else - game.move_minor = [] - */ - } -} - function move_general_to(to) { let pow = game.power let who = game.selected[0] @@ -1738,77 +1654,7 @@ function move_general_immediately(to) { } } -states.move_supply_train_NEW = { - inactive: "move", - prompt() { - prompt("Move supply train" + format_move(2)) - view.selected = game.selected - - let who = game.selected[0] - let here = game.pos[who] - - if (game.move_major) - map_for_each_key(game.move_major, s => { if (s !== here) gen_action_space(s) }) - if (game.move_minor) - map_for_each_key(game.move_minor, s => { if (s !== here) gen_action_space(s) }) - - view.move_major = game.move_major - view.move_minor = game.move_minor - - /* - if (game.count < 2 + game.major) - for (let next of data.cities.major_roads[here]) - if (!has_any_piece(next)) - gen_action_space(next) - if (game.count < 2) - for (let next of data.cities.roads[here]) - if (!has_any_piece(next)) - gen_action_space(next) - */ - - if (game.count > 0) { - gen_action_piece(who) - view.actions.stop = 1 - } - }, - piece(_) { - this.stop() - }, - stop() { - end_move_piece() - }, - space(to) { - let who = game.selected[0] - - set_add(game.moved, who) - game.pos[who] = to - - log("P" + who + " to S" + to) - - let m = map_get(game.move_major, to, 0) - if (m > 0) { - while (m > 0) { - // TODO: reverse - log(">S" + m) - m = map_get(game.move_major, m) - ++ game.count - } - } else { - m = map_get(game.move_minor, to, 0) - while (m > 0) { - // TODO: reverse - log(">S" + m) - m = map_get(game.move_minor, m) - ++ game.count - } - game.major = 0 - } - - resume_move_supply_train() - }, -} - -states.move_supply_train_OLD = { +states.move_supply_train = { inactive: "move", prompt() { prompt("Move supply train" + format_move(2)) @@ -1854,7 +1700,7 @@ states.move_supply_train_OLD = { }, } -states.move_general_NEW = { +states.move_general = { inactive: "move", prompt() { prompt("Move " + format_selected() + format_move(movement_range())) @@ -1864,110 +1710,20 @@ states.move_general_NEW = { let here = game.pos[who] if (game.count === 0) { - if (may_unstack()) { - if (game.selected.length > 1) - view.actions.detach = 1 - else - view.actions.detach = 0 - } - - let s_take = count_stacked_take() - let s_give = count_stacked_give() - let u_take = count_unstacked_take() - let u_give = count_unstacked_give() - if (s_take > 0 && u_give > 0) - view.actions.take = 1 - if (s_give > 0 && u_take > 0) - view.actions.give = 1 - } else { - if (forbid_stopping_at(here)) { - view.actions.stop = 0 + if (game.fx === NEXT_TURN_CHEVERT_MAY_NOT_UNSTACK && game.pos[GEN_CHEVERT] === here) { + view.prompt += " Chevert may not unstack." + if (count_generals(here) === 3) { + // two options: leave alone, or leave with chevert + // to leave with chevert, detach non-chevert + // to leave alone, detach non-chevert, then detach chevert + if (game.selected.length === 3) + for (let p of game.selected) + if (p !== GEN_CHEVERT) + gen_action_piece(p) + if (game.selected.length === 2) + gen_action_piece(GEN_CHEVERT) + } } else { - gen_action_piece(who) - view.actions.stop = 1 - } - } - - view.move_major = game.move_major - view.move_minor = game.move_minor - - if (game.move_major) - map_for_each_key(game.move_major, s => { if (s !== here) gen_action_space_or_piece(s) }) - if (game.move_minor) - map_for_each_key(game.move_minor, s => { if (s !== here) gen_action_space_or_piece(s) }) - }, - take() { - game.state = "move_take" - }, - give() { - game.state = "move_give" - }, - detach() { - game.state = "move_detach" - }, - piece(p) { - if (p === game.selected[0]) - this.stop() - else - this.space(game.pos[p]) - }, - stop() { - for (let p of game.selected) - set_add(game.moved, p) - end_move_piece() - }, - space(to) { - let who = game.selected[0] - let path = [ to ] - - let m = map_get(game.move_major, to, -1) - if (m >= 0) { - while (m >= 0) { - path.unshift(m) - m = map_get(game.move_major, m, -1) - ++game.count - } - } else { - m = map_get(game.move_minor, to, -1) - while (m >= 0) { - path.unshift(m) - m = map_get(game.move_minor, m, -1) - ++game.count - } - game.major = 0 - } - - log("P" + who + " " + path.map(s => "S" + s).join(" > ")) - - let stop = false - path.shift() // skip start space - for (let s of path) - stop ||= move_general_to(s) - - if (stop) - this.stop() - else - resume_move_general() - }, -} - -states.move_general_OLD = { - inactive: "move", - prompt() { - prompt("Move " + format_selected() + format_move(movement_range())) - view.selected = game.selected - - let who = game.selected[0] - let here = game.pos[who] - - if (game.count === 0) { - if (may_unstack()) { - /* - if (game.selected.length > 1) - view.actions.detach = 1 - else - view.actions.detach = 0 - */ if (game.selected.length > 1) for (let p of game.selected) gen_action_piece(p) @@ -2007,9 +1763,6 @@ states.move_general_OLD = { give() { game.state = "move_give" }, - detach() { - game.state = "move_detach" - }, piece(p) { if (game.count === 0) { if (set_has(game.selected, p)) @@ -2042,24 +1795,6 @@ states.move_general_OLD = { }, } -states.move_general = states.move_general_OLD -states.move_supply_train = states.move_supply_train_OLD -//states.move_general = states.move_general_NEW -//states.move_supply_train = states.move_supply_train_NEW - -states.move_detach = { - inactive: "move", - prompt() { - prompt("Move " + format_selected() + ". Detach general from stack.") - for (let p of game.selected) - gen_action_piece(p) - }, - piece(p) { - set_delete(game.selected, p) - game.state = "move_general" - }, -} - states.move_take = { inactive: "move", prompt() { @@ -2332,11 +2067,11 @@ function prompt_combat(value, extra = null) { } function inactive_attack() { - return "attack " + format_combat(game.count) + return "combat " + format_combat(game.count) } function inactive_defend() { - return "defend " + format_combat(-game.count) + return "combat " + format_combat(-game.count) } function goto_combat() { @@ -2357,10 +2092,8 @@ function goto_combat() { for (let a of from) { for (let b of to) { if (set_has(data.cities.adjacent[a], b)) { - if (!forbid_attack(a, b)) { - game.combat.push(a) - game.combat.push(b) - } + game.combat.push(a) + game.combat.push(b) } } } @@ -2897,7 +2630,7 @@ function search_retreat_possible_dfs(result, seen, here, range) { if (has_any_piece(next)) continue if (range === 1) { - map_set(result, next, seen.slice()) + set_add(result, next) } else { seen.push(next) search_retreat_possible_dfs(result, seen, next, range - 1) @@ -2924,12 +2657,9 @@ function search_retreat(loser, winner, range) { } let result = [] - map_for_each(possible, (s, path) => { - if (map_get(distance, s, -1) === max) { + for (let s of possible) + if (map_get(distance, s, -1) === max) result.push(s) - result.push(path) - } - }) return result } @@ -2938,15 +2668,14 @@ states.retreat = { prompt() { prompt("Retreat " + format_selected() + " " + Math.abs(game.count) + " cities.") view.selected = game.selected - view.retreat = game.retreat - map_for_each_key(game.retreat, gen_action_space) + for (let s of game.retreat) + gen_action_space(s) }, space(to) { push_undo() log("Retreated to S" + to + ".") - for (let p of game.selected) { + for (let p of game.selected) game.pos[p] = to - } delete game.retreat game.state = "retreat_done" }, @@ -3578,7 +3307,7 @@ function goto_flip_5_or_6_from_nearest_train(power, list) { } states.flip_5_or_6_from_nearest_train = { - inactive: "flip general out of supply", + inactive: "flip generals out of supply", prompt() { prompt("Flip " + format_selected() + " out of supply.") for (let p of game.selected) @@ -3592,7 +3321,7 @@ states.flip_5_or_6_from_nearest_train = { } states.flip_any_one_prussian_general_or_stack_in_austria_or_saxony = { - inactive: "flip general out of supply", + inactive: "flip any one Prussian general/stack in Austria or Saxony out of supply", prompt() { prompt("Flip any one Prussian general/stack in Austria or Saxony out of supply.") for (let p of game.selected) -- cgit v1.2.3