diff options
-rw-r--r-- | play.js | 127 | ||||
-rw-r--r-- | rules.js | 293 |
2 files changed, 237 insertions, 183 deletions
@@ -22,19 +22,8 @@ function toggle_shift() { document.body.classList.toggle("shift") } -/* DATA */ - -const P_FRANCE = 0 -const P_BAVARIA = 1 -const P_PRUSSIA = 2 -const P_SAXONY = 3 -const P_PRAGMATIC = 4 -const P_AUSTRIA = 5 - const last_city = data.cities.name.length - 1 -const ELIMINATED = data.cities.name.length -const REMOVED = ELIMINATED + 1 const ELIMINATED_TRAIN_X = 340 const ELIMINATED_TRAIN_Y = 160 const ELIMINATED_TRAIN_DX = 33 @@ -45,8 +34,35 @@ const ELIMINATED_GENERAL_DX = 50 const HUSSAR_X = 2190 const HUSSAR_Y = 1630 +/* DATA (SHARED) */ + +const deck_name = [ "red", "green", "blue", "yellow" ] +const suit_name = [ "\u2660", "\u2663", "\u2665", "\u2666", "R" ] +const suit_class = [ "spades", "clubs", "hearts", "diamonds", "reserve" ] +const suit_letter = [ "S", "C", "H", "D", "R" ] + +const P_FRANCE = 0 +const P_BAVARIA = 1 +const P_PRUSSIA = 2 +const P_SAXONY = 3 +const P_PRAGMATIC = 4 +const P_AUSTRIA = 5 + +const power_name = [ "France", "Bavaria", "Prussia", "Saxony", "Pragmatic Army", "Austria" ] const power_class = [ "france", "bavaria", "prussia", "saxony", "pragmatic", "austria" ] -const power_name = [ "France", "Bavaria", "Prussia", "Saxony", "Pragmatic", "Austria" ] + +const is_major_power = [ 1, 0, 1, 0, 1, 1 ] +const is_minor_power = [ 0, 1, 0, 1, 0, 0 ] + +const SPADES = 0 +const CLUBS = 1 +const HEARTS = 2 +const DIAMONDS = 3 +const RESERVE = 4 + +const IMPERIAL_ELECTION = 25 + +const ELIMINATED = data.cities.name.length const all_powers = [ 0, 1, 2, 3, 4, 5 ] @@ -68,6 +84,10 @@ const all_power_trains = [ [ 27, 28, 29 ], ] +const last_piece = 29 + +const all_hussars = [ 30, 31 ] + const piece_power = [ P_FRANCE, P_FRANCE, P_FRANCE, P_FRANCE, P_FRANCE, P_BAVARIA, @@ -123,6 +143,36 @@ const piece_name = [ "Batthyány", "Neipperg", "Arenberg", + "supply train", "supply train", + "supply train", + "supply train", "supply train", + "supply train", + "supply train", + "supply train", "supply train", "supply train", + "hussar", "hussar", +] + +const piece_log_name = [ + "Moritz", + "Belle-Isle", + "Broglie", + "Maillebois", + "Noailles", + "Törring", + "Friedrich", + "Schwerin", + "Leopold", + "Dessauer", + "Rutowski", + "George II", + "Cumberland", + "Earl of Stair", + "Karl", + "Traun", + "Khevenhüller", + "Batthyány", + "Neipperg", + "Arenberg", "French ST", "French ST", "Bavarian ST", "Prussian ST", "Prussian ST", @@ -132,8 +182,11 @@ const piece_name = [ "Hussars", "Hussars", ] -let suit_class = [ "spades", "clubs", "hearts", "diamonds", "reserve" ] -let suit_letter = [ "S", "C", "H", "D", "R" ] + +const all_pieces = [ ...all_power_generals.flat(), ...all_power_trains.flat() ] +const all_trains = [ ...all_power_trains.flat() ] +const all_generals = [ ...all_power_generals.flat() ] +const all_generals_by_rank = all_generals.slice().sort((a,b)=>piece_rank[a]-piece_rank[b]) function to_deck(c) { return c >> 7 @@ -144,7 +197,7 @@ function to_suit(c) { } function to_value(c) { - if (to_suit(c) === 4) + if (to_suit(c) === RESERVE) return 8 return c & 15 } @@ -442,16 +495,6 @@ function make_political_card(fc) { return e } -function has_removed_all_pieces(pow) { - for (let p of all_power_generals[pow]) - if (view.pos[p] !== REMOVED) - return false - for (let p of all_power_trains[pow]) - if (view.pos[p] !== REMOVED) - return false - return true -} - function on_init() { ui.pieces = [ create_piece("piece", 0, "piece cylinder france france_1"), @@ -533,12 +576,13 @@ function on_init() { ] ui.tc_hand = [ - make_tc_deck_pile("deck_1"), - make_tc_deck_pile("deck_1"), - make_tc_deck_pile("deck_1"), - make_tc_deck_pile("deck_1"), - make_tc_deck_pile("deck_1"), - make_tc_deck_pile("deck_1"), + // TODO: or make_tc_deck_pile ??? + make_tc_deck_hand("deck_1"), + make_tc_deck_hand("deck_1"), + make_tc_deck_hand("deck_1"), + make_tc_deck_hand("deck_1"), + make_tc_deck_hand("deck_1"), + make_tc_deck_hand("deck_1"), ] ui.tc_discard = [ @@ -675,9 +719,9 @@ function on_blur_city() { function on_focus_piece(evt) { let p = evt.target.my_id if (p < 20 && view.troops[p] > 0) - ui.status.textContent = piece_name[evt.target.my_id] + " (" + view.troops[p] + " troops)" + ui.status.textContent = piece_log_name[evt.target.my_id] + " (" + view.troops[p] + " troops)" else - ui.status.textContent = piece_name[evt.target.my_id] + ui.status.textContent = piece_log_name[evt.target.my_id] } function on_blur_piece() { @@ -751,14 +795,6 @@ function layout_hussar(id, s) { function layout_general(id, s) { let x, y, n - if (s === REMOVED) { - if (ui.pieces[id].parentElement === ui.pieces_element) - e.remove() - if (ui.troops[id].parentElement === ui.pieces_element) - e.remove() - return - } - if (s === ELIMINATED) { n = layout_general_offset_elim(id) x = ELIMINATED_GENERAL_X + ELIMINATED_GENERAL_DX * get_cylinder_power(id) @@ -794,11 +830,6 @@ function layout_train(id, s) { let n = layout_train_offset(id, s) let x, y - if (s === REMOVED) { - if (e.parentElement === ui.pieces_element) - e.remove() - return - } if (e.parentElement !== ui.pieces_element) ui.pieces_element.appendChild(e) @@ -1067,7 +1098,7 @@ function on_update() { function sub_piece(match, p1) { let x = p1 | 0 - let n = piece_name[x] + let n = piece_log_name[x] let p = power_class[piece_power[x]] return `<span class="piece_tip ${p}" onclick="on_click_piece_tip(${x})" onmouseenter="on_focus_piece_tip(${x})" onmouseleave="on_blur_piece_tip(${x})">${n}</span>` } @@ -1082,7 +1113,7 @@ function sub_path(pieces_and_spaces) { let ps = pieces_and_spaces[0].split(",") let ss = pieces_and_spaces[1].split(",") let x = ss[ss.length-1] - let ps_name = ps.map(p => piece_name[p]).join(" and ") + let ps_name = ps.map(p => piece_log_name[p]).join(" and ") let ss_name = data.cities.name[x] return `<span onclick="on_click_city_tip(${x})" onmouseenter="on_focus_path_tip([${ps.join(",")}],[${ss.join(",")}])" onmouseleave="on_blur_path_tip()">${ps_name} to ${ss_name}.</span>` } @@ -46,64 +46,26 @@ const R_LOUIS_XV = "Louis XV" const R_FREDERICK = "Frederick" const R_MARIA_THERESA = "Maria Theresa" -exports.roles = [ R_LOUIS_XV, R_FREDERICK, R_MARIA_THERESA ] +exports.roles = [ + R_MARIA_THERESA, + R_FREDERICK, + R_LOUIS_XV +] exports.scenarios = [ "Advanced" ] -/* DATA */ +const data = require("./data") var game var view var states = {} -const data = require("./data") - -function is_bohemia_space(s) { - return s >= 0 && s <= 401 -} - -function is_flanders_space(s) { - return s >= 402 && s <= 618 -} - -function is_map_space(s) { - return s >= 0 && s <= 618 -} - -function is_piece_on_map(p) { - return is_map_space(game.pos[p]) -} - -function is_piece_on_map_or_eliminated(p) { - let s = game.pos[p] - return is_map_space(s) || s === ELIMINATED -} - -function find_city(city) { - let n = data.cities.name.length - let x = -1 - for (let c = 0; c < n; ++c) { - if (data.cities.name[c] === city) { - if (x < 0) - x = c - else - throw "TWO CITIES: " + city - } - } - if (x < 0) - throw "CITY NOT FOUND: " + city - return x -} - -function find_city_list(names) { - let list = [] - for (let n of names) - set_add(list, find_city(n)) - return list -} +/* DATA (SHARED) */ const deck_name = [ "red", "green", "blue", "yellow" ] const suit_name = [ "\u2660", "\u2663", "\u2665", "\u2666", "R" ] +const suit_class = [ "spades", "clubs", "hearts", "diamonds", "reserve" ] +const suit_letter = [ "S", "C", "H", "D", "R" ] const P_FRANCE = 0 const P_BAVARIA = 1 @@ -112,7 +74,8 @@ const P_SAXONY = 3 const P_PRAGMATIC = 4 const P_AUSTRIA = 5 -const POWER_NAME = [ "France", "Bavaria", "Prussia", "Saxony", "Pragmatic Army", "Austria" ] +const power_name = [ "France", "Bavaria", "Prussia", "Saxony", "Pragmatic Army", "Austria" ] +const power_class = [ "france", "bavaria", "prussia", "saxony", "pragmatic", "austria" ] const is_major_power = [ 1, 0, 1, 0, 1, 1 ] const is_minor_power = [ 0, 1, 0, 1, 0, 0 ] @@ -127,24 +90,6 @@ const IMPERIAL_ELECTION = 25 const ELIMINATED = data.cities.name.length -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 all_powers = [ 0, 1, 2, 3, 4, 5 ] const all_power_generals = [ @@ -194,6 +139,15 @@ const piece_rank = [ 1, 2, 3, 4, 5, 6, ] +const piece_abbr = [ + "F1", "F2", "F3", "F4", "F5", + "B1", + "P1", "P2", "P3", "P4", + "S1", + "PA1", "PA2", "PA3", + "A1", "A2", "A3", "A4", "A5", "A6", +] + const piece_name = [ "Moritz", "Belle-Isle", @@ -224,11 +178,117 @@ const piece_name = [ "hussar", "hussar", ] +const piece_log_name = [ + "Moritz", + "Belle-Isle", + "Broglie", + "Maillebois", + "Noailles", + "Törring", + "Friedrich", + "Schwerin", + "Leopold", + "Dessauer", + "Rutowski", + "George II", + "Cumberland", + "Earl of Stair", + "Karl", + "Traun", + "Khevenhüller", + "Batthyány", + "Neipperg", + "Arenberg", + "French ST", "French ST", + "Bavarian ST", + "Prussian ST", "Prussian ST", + "Saxon ST", + "Pragmatic Army ST", + "Austrian ST", "Austrian ST", "Austrian ST", + "Hussars", "Hussars", +] + + const all_pieces = [ ...all_power_generals.flat(), ...all_power_trains.flat() ] const all_trains = [ ...all_power_trains.flat() ] const all_generals = [ ...all_power_generals.flat() ] const all_generals_by_rank = all_generals.slice().sort((a,b)=>piece_rank[a]-piece_rank[b]) +function to_deck(c) { + return c >> 7 +} + +function to_suit(c) { + return (c >> 4) & 7 +} + +function to_value(c) { + if (to_suit(c) === RESERVE) + return 8 + return c & 15 +} + +/* DATA */ + +function is_bohemia_space(s) { + return s >= 0 && s <= 401 +} + +function is_flanders_space(s) { + return s >= 402 && s <= 618 +} + +function is_map_space(s) { + return s >= 0 && s <= 618 +} + +function is_piece_on_map(p) { + return is_map_space(game.pos[p]) +} + +function is_piece_eliminated(p) { + return game.pos[p] === ELIMINATED +} + +function is_piece_on_map_or_eliminated(p) { + let s = game.pos[p] + return is_map_space(s) || s === ELIMINATED +} + +function find_city(city) { + let n = data.cities.name.length + let x = -1 + for (let c = 0; c < n; ++c) { + if (data.cities.name[c] === city) { + if (x < 0) + x = c + else + throw "TWO CITIES: " + city + } + } + if (x < 0) + throw "CITY NOT FOUND: " + city + return x +} + +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 all_france_bavaria_generals = [ ...all_power_generals[P_FRANCE], ...all_power_generals[P_BAVARIA], @@ -490,20 +550,6 @@ function is_hussar(p) { return p >= 30 && p < 32 } -function to_deck(c) { - return c >> 7 -} - -function to_suit(c) { - return (c >> 4) & 7 -} - -function to_value(c) { - if (to_suit(c) === RESERVE) - return 8 - return c & 15 -} - function format_card_prompt(c) { if (is_reserve(c)) return "8R" @@ -583,10 +629,6 @@ function make_protect_range(result, start, here, range) { } } -function is_fortress(s) { - return set_has(all_fortresses, s) -} - function is_home_country(s) { // TODO: Silesia switch (game.power) { @@ -690,18 +732,20 @@ function tc_per_turn() { return n } -const player_from_power_table = [ - R_LOUIS_XV, - R_LOUIS_XV, - R_FREDERICK, - R_FREDERICK, - R_FREDERICK, - R_MARIA_THERESA, -] - function player_from_power(pow) { - // TOOD: saxony allies with austria - return player_from_power_table[pow] + switch (pow) { + case P_FRANCE: + case P_BAVARIA: + return R_LOUIS_XV + case P_PRUSSIA: + return R_FREDERICK + case P_SAXONY: + return R_FREDERICK + case P_PRAGMATIC: + return R_FREDERICK + case P_AUSTRIA: + return R_MARIA_THERESA + } } function set_active_to_power(power) { @@ -783,20 +827,6 @@ function has_friendly_supply_train(to) { return false } -function has_enemy_supply_train(to) { - for (let p of all_enemy_trains(game.power)) - if (game.pos[p] === to) - return true - return false -} - -function has_enemy_general(to) { - for (let p of all_enemy_generals(game.power)) - if (game.pos[p] === to) - return true - return false -} - function has_enemy_piece(to) { for (let p of all_enemy_generals(game.power)) if (game.pos[p] === to) @@ -815,13 +845,6 @@ function has_non_cooperative_general(to) { return false } -function has_own_general(to) { - for (let p of all_power_generals[game.power]) - if (game.pos[p] === to) - return true - return false -} - function count_generals(to) { let n = 0 for (let p of all_generals) @@ -877,8 +900,8 @@ const TURN_NAME = [ const POWER_FROM_ACTION_STAGE = [ P_FRANCE, // and bavaria P_PRUSSIA, // and saxony - P_AUSTRIA, // and pragmatic army -- interleave with pragmatic moves on flanders map P_PRAGMATIC, // interleave with austria moves on flanders map + P_AUSTRIA, // and pragmatic army -- interleave with pragmatic moves on flanders map ] const title_from_action_stage = [ @@ -1122,7 +1145,7 @@ function draw_tc(n) { while (n > 0) { if (game.deck.length === 0) { if (k > 0) - log(POWER_NAME[game.power] + " " + k + " TC.") + log(power_name[game.power] + " " + k + " TC.") k = 0 next_tactics_deck() if (game.deck.length === 0) { @@ -1136,7 +1159,7 @@ function draw_tc(n) { } if (k > 0) - log(POWER_NAME[game.power] + " " + k + " TC.") + log(power_name[game.power] + " " + k + " TC.") } function goto_tactical_cards() { @@ -1358,7 +1381,7 @@ function resume_supply() { goto_supply_suffer() else end_supply() - // TODO: pause + // TODO: pause // game.state = "supply_done" } @@ -2102,7 +2125,7 @@ function goto_re_enter_train() { function can_train_re_enter(p) { return ( - (is_piece_on_map(p) || game.pos[p] === ELIMINATED) && + (is_piece_on_map_or_eliminated(p)) && !set_has(game.moved, p) && has_re_entry_space_for_supply_train(piece_power[p]) ) @@ -2238,7 +2261,7 @@ states.re_enter_train_where = { function end_re_enter_train() { if (game.recruit.used.length > 0) { log_br() - log(POWER_NAME[game.power] + " spent " + game.recruit.used.map(format_card).join(", ") + ".") + log(power_name[game.power] + " spent " + game.recruit.used.map(format_card).join(", ") + ".") map_for_each(game.recruit.pieces, (p, s) => { if (s !== ELIMINATED) log("Re-entered P" + p + " from S" + s + " at S" + game.pos[p] + ".") @@ -2311,7 +2334,7 @@ function has_re_entry_space_for_general() { return false } -function can_re_enter_general_at_city(from, to) { +function can_re_enter_general_at_city(to) { if (is_enemy_controlled_fortress(to)) return false if (has_friendly_supply_train(to)) @@ -2326,7 +2349,7 @@ function can_re_enter_general_at_city(from, to) { function can_recruit_anything() { for (let p of all_power_generals[game.power]) { // can re-enter generals - if (game.pos[p] === ELIMINATED && has_re_entry_space_for_general()) + if (is_piece_eliminated(p) && has_re_entry_space_for_general()) return true // can recruit troops? if (is_piece_on_map(p) && game.troops[p] < 8) @@ -2343,7 +2366,7 @@ states.recruit = { for (let p of all_power_generals[game.power]) { if (is_piece_on_map(p)) av_troops += 8 - game.troops[p] - else if (game.pos[p] === ELIMINATED && has_re_entry_space_for_general()) { + else if (is_piece_eliminated(p) && has_re_entry_space_for_general()) { av_general += 1 av_troops += 8 } @@ -2379,7 +2402,7 @@ states.recruit = { for (let p of all_power_generals[game.power]) { if (game.troops[p] > 0 && game.troops[p] < 8) gen_action_piece(p) - else if (game.pos[p] === ELIMINATED && has_re_entry_space_for_general()) + else if (is_piece_eliminated(p) && has_re_entry_space_for_general()) gen_action_piece(p) } } @@ -2398,7 +2421,7 @@ states.recruit = { spend_card_value(game.recruit.pool, game.recruit.used, 4) - if (game.pos[p] === ELIMINATED) { + if (is_piece_eliminated(p)) { game.selected = p game.state = "re_enter_general_where" } else { @@ -2627,7 +2650,7 @@ function format_combat(value) { let a = format_combat_stack(game.attacker) let d = format_combat_stack(game.defender) let s = signed_number(value) - let p = POWER_NAME[game.power] + let p = power_name[game.power] return `${a} vs ${d}. ${p} is at ${s}.` } @@ -2699,7 +2722,7 @@ function gen_play_reserve() { } function play_card(c, sign) { - let prefix = (sign < 0 ? ">>" : ">") + POWER_NAME[game.power] + let prefix = (sign < 0 ? ">>" : ">") + power_name[game.power] if (sign < 0) game.count -= to_value(c) else @@ -2711,7 +2734,7 @@ function play_card(c, sign) { function play_reserve(v, sign) { let c = game.reserve delete game.reserve - let prefix = (sign < 0 ? ">>" : ">") + POWER_NAME[game.power] + let prefix = (sign < 0 ? ">>" : ">") + power_name[game.power] if (sign < 0) game.count -= v else @@ -2867,7 +2890,7 @@ function goto_retreat() { for (let pow of all_powers) if (lost[pow] > 0) - log(POWER_NAME[pow] + " lost " + (lost[pow]) + " troops.") + log(power_name[pow] + " lost " + (lost[pow]) + " troops.") resume_retreat() } @@ -3100,7 +3123,7 @@ const setup_max_troops = [ 8, 8, 8, 8, 8, 5, 8, 8, 8, 6, - 8, + 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, ] @@ -3270,7 +3293,7 @@ states.setup = { } else { let n_stacks = 0 for (let p of all_power_generals[game.power]) { - if (game.pos[p] !== ELIMINATED && !set_has(game.moved, p)) { + if (!set_has(game.moved, p)) { gen_action_piece(p) n_stacks ++ } @@ -3432,7 +3455,7 @@ exports.view = function (state, player) { if (typeof inactive === "function") view.prompt = inactive() else - view.prompt = `Waiting for ${POWER_NAME[game.power]} to ${inactive}.` + view.prompt = `Waiting for ${power_name[game.power]} to ${inactive}.` } else { view.actions = {} if (states[game.state]) @@ -3463,7 +3486,7 @@ function goto_game_over(result, victory) { } function prompt(str) { - view.prompt = POWER_NAME[game.power] + ": " + str + view.prompt = power_name[game.power] + ": " + str } exports.action = function (state, _player, action, arg) { |