diff options
Diffstat (limited to 'rules.js')
-rw-r--r-- | rules.js | 2624 |
1 files changed, 1312 insertions, 1312 deletions
@@ -1,44 +1,44 @@ -"use strict"; - -let cards = require("./cards.js"); - -const Afghan = 'Afghan'; -const British = 'British'; -const Russian = 'Russian'; - -const Political = 'Political'; -const Intelligence = 'Intelligence'; -const Economic = 'Economic'; -const Military = 'Military'; - -const Persia = 201; -const Transcaspia = 202; -const Herat = 203; -const Kabul = 204; -const Kandahar = 205; -const Punjab = 206; - -const Persia_Transcaspia = 301; -const Persia_Herat = 302; -const Transcaspia_Herat = 303; -const Transcaspia_Kabul = 304; -const Herat_Kabul = 305; -const Herat_Kandahar = 306; -const Kabul_Kandahar = 307; -const Kabul_Punjab = 308; -const Kandahar_Punjab = 309; - -const Gift2 = 400; -const Gift4 = 401; -const Gift6 = 402; -const Safe_House = 500; - -const first_region = 201; -const last_region = 206; - -const PUBLIC_WITHDRAWAL = 111; -const SAFE_HOUSE_1 = 41; -const SAFE_HOUSE_2 = 72; +"use strict" + +let cards = require("./cards.js") + +const Afghan = 'Afghan' +const British = 'British' +const Russian = 'Russian' + +const Political = 'Political' +const Intelligence = 'Intelligence' +const Economic = 'Economic' +const Military = 'Military' + +const Persia = 201 +const Transcaspia = 202 +const Herat = 203 +const Kabul = 204 +const Kandahar = 205 +const Punjab = 206 + +const Persia_Transcaspia = 301 +const Persia_Herat = 302 +const Transcaspia_Herat = 303 +const Transcaspia_Kabul = 304 +const Herat_Kabul = 305 +const Herat_Kandahar = 306 +const Kabul_Kandahar = 307 +const Kabul_Punjab = 308 +const Kandahar_Punjab = 309 + +const Gift2 = 400 +const Gift4 = 401 +const Gift6 = 402 +const Safe_House = 500 + +const first_region = 201 +const last_region = 206 + +const PUBLIC_WITHDRAWAL = 111 +const SAFE_HOUSE_1 = 41 +const SAFE_HOUSE_2 = 72 const player_names = [ "Gray", @@ -47,9 +47,9 @@ const player_names = [ "Red", "Black", "None" -]; +] -const player_index = Object.fromEntries(Object.entries(player_names).map(([k,v])=>[v,k|0])); +const player_index = Object.fromEntries(Object.entries(player_names).map(([k,v])=>[v,k|0])) const region_names = { [Persia]: "Persia", @@ -58,7 +58,7 @@ const region_names = { [Kabul]: "Kabul", [Kandahar]: "Kandahar", [Punjab]: "Punjab", -}; +} const region_index = { "Persia": Persia, @@ -67,15 +67,15 @@ const region_index = { "Kabul": Kabul, "Kandahar": Kandahar, "Punjab": Punjab, -}; +} cards.forEach(card => { if (card) { - card.region = region_index[card.region]; + card.region = region_index[card.region] if (card.name === 'EVENT') - card.name = card.if_discarded + " / " + card.if_purchased; + card.name = card.if_discarded + " / " + card.if_purchased } -}); +}) const border_names = { [Persia_Transcaspia]: "Persia/Transcaspia", @@ -108,519 +108,519 @@ const roads = { } function is_dominance_check(c) { - return c >= 101 && c <= 104; + return c >= 101 && c <= 104 } function is_event_card(c) { - return c > 100; + return c > 100 } -let game = null; -let player = null; -let view = null; +let game = null +let player = null +let view = null -let states = {}; +let states = {} const scenario_player_count = { "2P": 2, "3P": 3, "4P": 4, "5P": 5 } -exports.scenarios = [ "3P", "4P", "5P", "2P" ]; +exports.scenarios = [ "3P", "4P", "5P", "2P" ] exports.roles = function (scenario) { switch (scenario) { - case "2P": return player_names.slice(0, 2); - case "3P": return player_names.slice(0, 3); - case "4P": return player_names.slice(0, 4); - case "5P": return player_names.slice(0, 5); + case "2P": return player_names.slice(0, 2) + case "3P": return player_names.slice(0, 3) + case "4P": return player_names.slice(0, 4) + case "5P": return player_names.slice(0, 5) } } exports.ready = function (scenario, options, players) { switch (scenario) { - case "2P": return players.length === 2; - case "3P": return players.length === 3; - case "4P": return players.length === 4; - case "5P": return players.length === 5; + case "2P": return players.length === 2 + case "3P": return players.length === 3 + case "4P": return players.length === 4 + case "5P": return players.length === 5 } } function random(n) { - return ((game.seed = game.seed * 69621 % 0x7fffffff) / 0x7fffffff) * n | 0; + return ((game.seed = game.seed * 69621 % 0x7fffffff) / 0x7fffffff) * n | 0 } function shuffle(deck) { for (let i = deck.length - 1; i > 0; --i) { - let j = random(i + 1); - let tmp = deck[j]; - deck[j] = deck[i]; - deck[i] = tmp; + let j = random(i + 1) + let tmp = deck[j] + deck[j] = deck[i] + deck[i] = tmp } } function remove_from_array(array, item) { - let i = array.indexOf(item); + let i = array.indexOf(item) if (i >= 0) - array.splice(i, 1); + array.splice(i, 1) } function set_active(new_active) { if (game.active !== new_active) - clear_undo(); - game.active = new_active; - update_aliases(); + clear_undo() + game.active = new_active + update_aliases() } function update_aliases() { - player = game.players[game.active]; + player = game.players[game.active] } function find_card_in_market(c) { for (let row = 0; row < 2; ++row) for (let col = 0; col < 6; ++col) if (c === game.market_cards[row][col]) - return [row, col]; - return null; + return [row, col] + return null } function find_card_in_court(c) { for (let p = 0; p < game.players.length; ++p) { - let court = game.players[p].court; + let court = game.players[p].court for (let i = 0; i < court.length; ++i) if (court[i] === c) - return p; + return p } - return -1; + return -1 } function next_player(current) { - return (current + 1) % game.players.length; + return (current + 1) % game.players.length } function a_coalition(s) { switch (s) { - case Afghan: return "an Afghan"; - case British: return "a British"; - case Russian: return "a Russian"; + case Afghan: return "an Afghan" + case British: return "a British" + case Russian: return "a Russian" } } function logbr() { if (game.log.length > 0 && game.log[game.log.length-1] !== "") - game.log.push(""); + game.log.push("") } function log(msg) { - game.log.push(msg); + game.log.push(msg) } function logi(msg) { - game.log.push(">" + msg); + game.log.push(">" + msg) } function clear_undo() { - game.undo = []; + game.undo = [] } function push_undo() { game.undo.push(JSON.stringify(game, (k,v) => { - if (k === 'undo') return 0; - if (k === 'log') return v.length; - return v; - })); + if (k === 'undo') return 0 + if (k === 'log') return v.length + return v + })) } function pop_undo() { - let save_undo = game.undo; - let save_log = game.log; - game = JSON.parse(save_undo.pop()); - game.undo = save_undo; - save_log.length = game.log; - game.log = save_log; + let save_undo = game.undo + let save_log = game.log + game = JSON.parse(save_undo.pop()) + game.undo = save_undo + save_log.length = game.log + game.log = save_log } function gen_action(action, argument=undefined) { if (argument !== undefined) { if (!(action in view.actions)) { - view.actions[action] = [ argument ]; + view.actions[action] = [ argument ] } else { if (!view.actions[action].includes(argument)) - view.actions[action].push(argument); + view.actions[action].push(argument) } } else { - view.actions[action] = 1; + view.actions[action] = 1 } } // STATE QUERIES function active_has_court_card(c) { - let court = player.court; + let court = player.court for (let i = 0; i < court.length; ++i) if (court[i] === c) - return true; - return false; + return true + return false } function player_has_court_card(p, c) { - let court = game.players[p].court; + let court = game.players[p].court for (let i = 0; i < court.length; ++i) if (court[i] === c) - return true; - return false; + return true + return false } function which_player_has_court_card(c) { for (let p = 0; p < game.players.length; ++p) { - let court = game.players[p].court; + let court = game.players[p].court for (let i = 0; i < court.length; ++i) if (court[i] === c) - return p; + return p } - return -1; + return -1 } -function active_has_russian_influence() { return active_has_court_card(70); } -function active_has_persian_influence() { return active_has_court_card(68); } -function active_has_herat_influence() { return active_has_court_card(66); } -function active_has_claim_of_ancient_lineage() { return active_has_court_card(5); } -function active_has_indian_supplies() { return active_has_court_card(51); } -function active_has_well_connected() { return active_has_court_card(56); } -function active_has_strange_bedfellows() { return active_has_court_card(21); } -function active_has_infrastructure() { return active_has_court_card(78); } -function active_has_civil_service_reforms() { return active_has_court_card(24); } -function active_has_charismatic_courtiers() { return active_has_court_card(42); } -function active_has_blackmail_kandahar() { return active_has_court_card(43); } -function active_has_blackmail_herat() { return active_has_court_card(54); } +function active_has_russian_influence() { return active_has_court_card(70) } +function active_has_persian_influence() { return active_has_court_card(68) } +function active_has_herat_influence() { return active_has_court_card(66) } +function active_has_claim_of_ancient_lineage() { return active_has_court_card(5) } +function active_has_indian_supplies() { return active_has_court_card(51) } +function active_has_well_connected() { return active_has_court_card(56) } +function active_has_strange_bedfellows() { return active_has_court_card(21) } +function active_has_infrastructure() { return active_has_court_card(78) } +function active_has_civil_service_reforms() { return active_has_court_card(24) } +function active_has_charismatic_courtiers() { return active_has_court_card(42) } +function active_has_blackmail_kandahar() { return active_has_court_card(43) } +function active_has_blackmail_herat() { return active_has_court_card(54) } -function player_has_bodyguards(p) { return player_has_court_card(p, 15) || player_has_court_card(p, 83); } -function player_has_indispensable_advisors(p) { return player_has_court_card(p, 1); } -function player_has_citadel_in_kabul(p) { return player_has_court_card(p, 17); } -function player_has_citadel_in_transcaspia(p) { return player_has_court_card(p, 97); } -function player_has_safe_house(p) { return player_has_court_card(p, 41) || player_has_court_card(p, 72); } +function player_has_bodyguards(p) { return player_has_court_card(p, 15) || player_has_court_card(p, 83) } +function player_has_indispensable_advisors(p) { return player_has_court_card(p, 1) } +function player_has_citadel_in_kabul(p) { return player_has_court_card(p, 17) } +function player_has_citadel_in_transcaspia(p) { return player_has_court_card(p, 97) } +function player_has_safe_house(p) { return player_has_court_card(p, 41) || player_has_court_card(p, 72) } -function which_player_has_insurrection() { return which_player_has_court_card(3); } +function which_player_has_insurrection() { return which_player_has_court_card(3) } function player_has_citadel(p, r) { - if (r === Kabul) return player_has_citadel_in_kabul(p); - if (r === Transcaspia) return player_has_citadel_in_transcaspia(p); - return false; + if (r === Kabul) return player_has_citadel_in_kabul(p) + if (r === Transcaspia) return player_has_citadel_in_transcaspia(p) + return false } function player_coalition_blocks(p) { switch (game.players[p].loyalty) { - case Afghan: return 0; - case British: return 12; - case Russian: return 24; + case Afghan: return 0 + case British: return 12 + case Russian: return 24 } } function active_coalition_blocks() { switch (player.loyalty) { - case Afghan: return 0; - case British: return 12; - case Russian: return 24; + case Afghan: return 0 + case British: return 12 + case Russian: return 24 } } function player_cylinders(p) { - return 36 + p * 10; + return 36 + p * 10 } function active_cylinders() { - return 36 + game.active * 10; + return 36 + game.active * 10 } function min_gift_cost() { - let x = active_cylinders(); - let avail_2 = true, avail_4 = true, avail_6 = true; + let x = active_cylinders() + let avail_2 = true, avail_4 = true, avail_6 = true for (let i = x; i < x + 10; ++i) { - let s = game.pieces[i]; - if (s === Gift2) avail_2 = false; - else if (s === Gift4) avail_4 = false; - else if (s === Gift6) avail_6 = false; + let s = game.pieces[i] + if (s === Gift2) avail_2 = false + else if (s === Gift4) avail_4 = false + else if (s === Gift6) avail_6 = false } - if (avail_2) return 2; - if (avail_4) return 4; - if (avail_6) return 6; - return 1000; + if (avail_2) return 2 + if (avail_4) return 4 + if (avail_6) return 6 + return 1000 } function gen_place_gift() { - let x = active_cylinders(); - let avail_2 = true, avail_4 = true, avail_6 = true; + let x = active_cylinders() + let avail_2 = true, avail_4 = true, avail_6 = true for (let i = x; i < x + 10; ++i) { - let s = game.pieces[i]; - if (s === Gift2) avail_2 = false; - else if (s === Gift4) avail_4 = false; - else if (s === Gift6) avail_6 = false; + let s = game.pieces[i] + if (s === Gift2) avail_2 = false + else if (s === Gift4) avail_4 = false + else if (s === Gift6) avail_6 = false } - if (avail_2 && player.coins >= 2) gen_action('place_gift', 2); - if (avail_4 && player.coins >= 4) gen_action('place_gift', 4); - if (avail_6 && player.coins >= 6) gen_action('place_gift', 6); + if (avail_2 && player.coins >= 2) gen_action('place_gift', 2) + if (avail_4 && player.coins >= 4) gen_action('place_gift', 4) + if (avail_6 && player.coins >= 6) gen_action('place_gift', 6) } function ruler_of_region(r) { - let ruler = -1; + let ruler = -1 - let n_afghan = 0; - let n_british = 0; - let n_russian = 0; + let n_afghan = 0 + let n_british = 0 + let n_russian = 0 for (let i = 0; i < 12; ++i) { if (game.pieces[i] === r) - n_afghan ++; + n_afghan ++ if (game.pieces[i+12] === r) - n_british ++; + n_british ++ if (game.pieces[i+24] === r) - n_russian ++; + n_russian ++ } - let max_ruling = Math.max(n_afghan, n_british, n_russian); + let max_ruling = Math.max(n_afghan, n_british, n_russian) for (let p = 0; p < game.players.length; ++p) { - let n_tribes = 0; - let x = player_cylinders(p); + let n_tribes = 0 + let x = player_cylinders(p) for (let i = x; i < x + 10; ++i) if (game.pieces[i] === r) - n_tribes++; + n_tribes++ - let n_ruling = n_tribes; + let n_ruling = n_tribes if (game.players[p].loyalty === Afghan) - n_ruling += n_afghan; + n_ruling += n_afghan if (game.players[p].loyalty === British) - n_ruling += n_british; + n_ruling += n_british if (game.players[p].loyalty === Russian) - n_ruling += n_russian; + n_ruling += n_russian if (n_ruling === max_ruling) { - ruler = -1; + ruler = -1 } else if (n_ruling > max_ruling) { - max_ruling = n_ruling; + max_ruling = n_ruling if (n_tribes > 0) - ruler = p; + ruler = p else - ruler = -1; + ruler = -1 } } - return ruler; + return ruler } function player_rules_region(p, r) { - return ruler_of_region(r) === p; + return ruler_of_region(r) === p } function active_rules_region(r) { - return player_rules_region(game.active, r); + return player_rules_region(game.active, r) } function player_rules_any_region(p) { for (let r = first_region; r <= last_region; ++r) if (player_rules_region(p, r)) - return true; - return false; + return true + return false } function active_rules_any_region() { - return player_rules_any_region(game.active); + return player_rules_any_region(game.active) } function active_can_betray() { - let x = active_cylinders(); + let x = active_cylinders() for (let i = x; i < x + 10; ++i) { if (game.pieces[i] >= 1 && game.pieces[i] <= 100) { - let c = game.pieces[i]; + let c = game.pieces[i] if (cards[c].suit !== Political) - return true; - let p = find_card_in_court(c); + return true + let p = find_card_in_court(c) if (!player_has_bodyguards(p)) - return true; + return true } } - return false; + return false } function card_has_no_spies(c) { for (let p = 0; p < game.players.length; ++p) { - let x = player_cylinders(p); + let x = player_cylinders(p) for (let i = x; i < x + 10; ++i) if (game.pieces[i] === c) - return false; + return false } - return true; + return true } function market_cost(col, c) { if (col === 0) - return 0; + return 0 if (cards[c].patriot === Russian && active_has_russian_influence()) - return 0; + return 0 if (cards[c].region === Persia && active_has_persian_influence()) - return 0; + return 0 if (cards[c].region === Herat && active_has_herat_influence()) - return 0; + return 0 if (game.favored === Military) - return 2 * col; - return col; + return 2 * col + return col } function is_favored_suit(c) { if (cards[c].suit === game.favored) - return true; + return true if (player.events.new_tactics && cards[c].suit === Military) - return true; - if (c === 91) return true; // Savvy Operator - if (c === 99) return true; // Irregulars - return false; + return true + if (c === 91) return true // Savvy Operator + if (c === 99) return true // Irregulars + return false } function rightmost_card(row, i) { while (i >= 0 && game.market_cards[row][i] === 0) - --i; - return i; + --i + return i } function pay_action_cost(cost, offset) { - player.coins -= cost; - let ra = rightmost_card(0, 5); - let rb = rightmost_card(1, 5); + player.coins -= cost + let ra = rightmost_card(0, 5) + let rb = rightmost_card(1, 5) for (let i = 0; i < offset; ++i) { - ra = rightmost_card(0, ra-1); - rb = rightmost_card(1, rb-1); + ra = rightmost_card(0, ra-1) + rb = rightmost_card(1, rb-1) } for (let i = 0; i < cost; i += 2) { - if (ra >= 0) game.market_coins[0][ra] ++; - if (rb >= 0) game.market_coins[1][rb] ++; - ra = rightmost_card(0, ra-1); - rb = rightmost_card(1, rb-1); + if (ra >= 0) game.market_coins[0][ra] ++ + if (rb >= 0) game.market_coins[1][rb] ++ + ra = rightmost_card(0, ra-1) + rb = rightmost_card(1, rb-1) } - check_public_withdrawal(); + check_public_withdrawal() } function remove_all_tribes_and_armies(where) { - logi(`Removed all tribes and armies in ${region_names[where]}.`); + logi(`Removed all tribes and armies in ${region_names[where]}.`) for (let i = 0; i < 36; ++i) if (game.pieces[i] === where) - game.pieces[i] = 0; + game.pieces[i] = 0 for (let p = 0; p < game.players.length; ++p) { - let x = player_cylinders(p); - let maybe_overthrow = false; + let x = player_cylinders(p) + let maybe_overthrow = false for (let i = x; i < x + 10; ++i) { if (game.pieces[i] === where) { - game.pieces[i] = 0; - maybe_overthrow = true; + game.pieces[i] = 0 + maybe_overthrow = true } } if (maybe_overthrow) - check_region_overthrow(p, where); + check_region_overthrow(p, where) } } function player_with_most_spies(c) { - let who = -1; - let max_spies = 0; + let who = -1 + let max_spies = 0 for (let p = 0; p < game.players.length; ++p) { - let n_spies = count_player_cylinders(p, c); + let n_spies = count_player_cylinders(p, c) if (n_spies === max_spies) { - who = -1; + who = -1 } else if (n_spies > max_spies) { - max_spies = n_spies; - who = p; + max_spies = n_spies + who = p } } - return who; + return who } function select_available_cylinder() { - let x = active_cylinders(); + let x = active_cylinders() for (let i = x; i < x + 10; ++i) if (game.pieces[i] === 0) - return i; - return -1; + return i + return -1 } function gen_select_cylinder() { - let x = active_cylinders(); + let x = active_cylinders() for (let i = x; i < x + 10; ++i) if (!game.used_pieces.includes(i)) - gen_action('piece', i); + gen_action('piece', i) } function gen_select_spy_to_move() { - let x = active_cylinders(); + let x = active_cylinders() for (let i = x; i < x + 10; ++i) if (game.pieces[i] > 0 && game.pieces[i] <= 100) - gen_action('piece', i); + gen_action('piece', i) } function gen_select_tribe_to_move() { - let x = active_cylinders(); + let x = active_cylinders() for (let i = x; i < x + 10; ++i) if (game.pieces[i] >= first_region && game.pieces[i] <= last_region) - gen_action('piece', i); + gen_action('piece', i) } function select_available_block() { - let b = active_coalition_blocks(); + let b = active_coalition_blocks() for (let i = b + 11; i >= b; --i) if (game.pieces[i] === 0) - return i; - return -1; + return i + return -1 } function select_afghan_block() { for (let i = 0; i < 12; ++i) if (game.pieces[i] === 0) - return i; - return -1; + return i + return -1 } function gen_select_block() { - let b = active_coalition_blocks(); + let b = active_coalition_blocks() for (let i = b; i < b + 12; ++i) if (!game.used_pieces.includes(i)) - gen_action('piece', i); + gen_action('piece', i) } function gen_select_army_to_move() { - let b = active_coalition_blocks(); + let b = active_coalition_blocks() for (let i = b; i < b + 12; ++i) { - let r = game.pieces[i]; + let r = game.pieces[i] if (r >= first_region && r <= last_region) if (active_can_move_from_region(r)) - gen_action('piece', i); + gen_action('piece', i) } } // DISCARD COURT CARD function discard_court_card(c) { - let pidx = -1; + let pidx = -1 // Remove card from court for (let p = 0; p < game.players.length; ++p) { - let i = game.players[p].court.indexOf(c); + let i = game.players[p].court.indexOf(c) if (i >= 0) { - game.players[p].court.splice(i, 1); - pidx = p; - break; + game.players[p].court.splice(i, 1) + pidx = p + break } } if (pidx !== game.phasing) - log(`Discarded #${c} from ${player_names[pidx]} court.`); + log(`Discarded #${c} from ${player_names[pidx]} court.`) else - log(`Discarded #${c} from court.`); + log(`Discarded #${c} from court.`) // Return all spies on card for (let p = 0; p < game.players.length; ++p) { - let x = player_cylinders(p); + let x = player_cylinders(p) for (let i = x; i < x + 10; ++i) { if (game.pieces[i] === c) { - log(`Returned ${player_names[p]} spy.`); - game.pieces[i] = 0; + log(`Returned ${player_names[p]} spy.`) + game.pieces[i] = 0 } } } @@ -628,155 +628,155 @@ function discard_court_card(c) { // Return rupees for leverage if (cards[c].leveraged) { if (pidx !== game.phasing) - log(`${player_names[pidx]} paid back leverage.`); + log(`${player_names[pidx]} paid back leverage.`) else - log(`Paid back leverage.`); + log(`Paid back leverage.`) - game.players[pidx].coins -= 2; + game.players[pidx].coins -= 2 } if (cards[c].suit === Political) - check_court_overthrow(pidx, cards[c].region); + check_court_overthrow(pidx, cards[c].region) } // CHANGE LOYALTY function change_loyalty(new_loyalty) { - player.loyalty = new_loyalty; + player.loyalty = new_loyalty - log(`Loyalty to ${player.loyalty}.`); + log(`Loyalty to ${player.loyalty}.`) for (let i = 0; i < player.court.length;) { - let c = player.court[i]; - let card = cards[c]; + let c = player.court[i] + let card = cards[c] if (card.patriot && card.patriot !== new_loyalty) { - discard_court_card(c); + discard_court_card(c) } else { - ++i; + ++i } } - let x = active_cylinders(); + let x = active_cylinders() for (let i = x; i < x + 10; ++i) { - let s = game.pieces[i]; + let s = game.pieces[i] if (s === Gift2 || s === Gift4 || s === Gift6) - game.pieces[i] = 0; + game.pieces[i] = 0 } - player.prizes = 0; + player.prizes = 0 } // RETURN LEVERAGE function check_leverage() { - let first = game.phasing; + let first = game.phasing for (let p = first; p < game.players.length; ++p) if (check_player_leverage(p)) - return; + return for (let p = 0; p < first; ++p) if (check_player_leverage(p)) - return; - check_safe_house(); + return + check_safe_house() } function check_player_leverage(p) { if (game.players[p].coins < 0) { if (game.players[p].hand.length + game.players[p].court.length === 0) { - game.players[p].coins = 0; + game.players[p].coins = 0 } else { - logbr(); - set_active(p); - game.state = 'leverage'; - return true; + logbr() + set_active(p) + game.state = 'leverage' + return true } } - return false; + return false } states.leverage = { prompt() { if (player.coins < 0) { - view.prompt = `Discard cards from your hand or court to pay back leverage.`; + view.prompt = `Discard cards from your hand or court to pay back leverage.` for (let i = 0; i < player.hand.length; ++i) - gen_action('card', player.hand[i]); + gen_action('card', player.hand[i]) for (let i = 0; i < player.court.length; ++i) - gen_action('card', player.court[i]); + gen_action('card', player.court[i]) } else { - view.prompt = `Discard cards from your hand or court to pay back leverage \u2014 done.`; - gen_action('next'); + view.prompt = `Discard cards from your hand or court to pay back leverage \u2014 done.` + gen_action('next') } }, card(c) { - push_undo(); - player.coins ++; + push_undo() + player.coins ++ if (player.hand.includes(c)) { - log(`Discarded #${c} from hand.`); - remove_from_array(player.hand, c); + log(`Discarded #${c} from hand.`) + remove_from_array(player.hand, c) } else { - discard_court_card(c); + discard_court_card(c) } if (player.hand.length + player.court.length === 0) - player.coins = 0; + player.coins = 0 }, next() { - push_undo(); - check_leverage(); + push_undo() + check_leverage() } } // OVERTHROW function check_court_overthrow(p, r) { - let court = game.players[p].court; - let x = player_cylinders(p); + let court = game.players[p].court + let x = player_cylinders(p) - let nc = 0; + let nc = 0 for (let i = 0; i < court.length; ++i) { - let info = cards[court[i]]; + let info = cards[court[i]] if (info.suit === Political && info.region === r) - ++nc; + ++nc } - let nt = 0; + let nt = 0 for (let i = x; i < x + 10; ++i) if (game.pieces[i] === r) - ++nt; + ++nt if (nc === 0 && nt > 0) { - log(`${player_names[p]} overthrown in ${region_names[r]}.`); + log(`${player_names[p]} overthrown in ${region_names[r]}.`) for (let i = x; i < x + 10; ++i) { if (game.pieces[i] === r) { - // log(`Removed ${player_names[p]} tribe.`); - game.pieces[i] = 0; + // log(`Removed ${player_names[p]} tribe.`) + game.pieces[i] = 0 } } } } function check_region_overthrow(p, r) { - let court = game.players[p].court; - let x = player_cylinders(p); + let court = game.players[p].court + let x = player_cylinders(p) - let nc = 0; + let nc = 0 for (let i = 0; i < court.length; ++i) { - let info = cards[court[i]]; + let info = cards[court[i]] if (info.suit === Political && info.region === r) - ++nc; + ++nc } - let nt = 0; + let nt = 0 for (let i = x; i < x + 10; ++i) if (game.pieces[i] === r) - ++nt; + ++nt if (nt === 0 && nc > 0) { - log(`${player_names[p]} overthrown in ${region_names[r]}.`); + log(`${player_names[p]} overthrown in ${region_names[r]}.`) for (let i = 0; i < court.length;) { - let info = cards[court[i]]; + let info = cards[court[i]] if (info.suit === Political && info.region === r) - discard_court_card(court[i]); + discard_court_card(court[i]) else - ++i; + ++i } } } @@ -785,174 +785,174 @@ function check_region_overthrow(p, r) { states.bribe = { prompt() { - let p = game.bribe; - view.prompt = `Must pay ${game.count} rupee bribe to ${player_names[p]}.`; + let p = game.bribe + view.prompt = `Must pay ${game.count} rupee bribe to ${player_names[p]}.` if (player.coins - game.reserve >= game.count) - gen_action('pay'); - gen_action('beg'); + gen_action('pay') + gen_action('beg') if (player.events.courtly_manners) - gen_action('courtly_manners'); + gen_action('courtly_manners') if (game.undo.length === 0) - gen_action('refuse'); + gen_action('refuse') }, courtly_manners() { - log(`Did not pay bribe.`); - end_bribe(); + log(`Did not pay bribe.`) + end_bribe() }, pay() { - let p = game.bribe; - log(`Paid ${game.count} to ${player_names[p]}.`); - game.players[p].coins += game.count; - game.players[game.active].coins -= game.count; - end_bribe(); + let p = game.bribe + log(`Paid ${game.count} to ${player_names[p]}.`) + game.players[p].coins += game.count + game.players[game.active].coins -= game.count + end_bribe() }, beg() { - let p = game.bribe; - log(`Asked ${player_names[p]} to waive the bribe for #${game.card}.`); - game.state = 'waive'; - set_active(p); + let p = game.bribe + log(`Asked ${player_names[p]} to waive the bribe for #${game.card}.`) + game.state = 'waive' + set_active(p) }, refuse() { - game.card = 0; - game.where = 0; - resume_actions(); + game.card = 0 + game.where = 0 + resume_actions() }, } states.waive = { prompt() { if (typeof game.where === 'string') - view.prompt = `${player_names[game.phasing]} asks you to waive the bribe to use #${game.card} to ${game.where}.`; + view.prompt = `${player_names[game.phasing]} asks you to waive the bribe to use #${game.card} to ${game.where}.` else - view.prompt = `${player_names[game.phasing]} asks you to waive the bribe to play #${game.card}.`; - let max_cost = Math.min(game.players[game.phasing].coins - game.reserve, game.count); - gen_action('waive'); + view.prompt = `${player_names[game.phasing]} asks you to waive the bribe to play #${game.card}.` + let max_cost = Math.min(game.players[game.phasing].coins - game.reserve, game.count) + gen_action('waive') for (let i = 1; i < max_cost; ++i) - gen_action('offer_' + i); - gen_action('refuse'); + gen_action('offer_' + i) + gen_action('refuse') }, waive() { - log(`${player_names[game.active]} waived the bribe.`); - set_active(game.phasing); - end_bribe(); - }, - offer_1() { do_offer(1); }, - offer_2() { do_offer(2); }, - offer_3() { do_offer(3); }, - offer_4() { do_offer(4); }, - offer_5() { do_offer(5); }, - offer_6() { do_offer(6); }, - offer_7() { do_offer(7); }, - offer_8() { do_offer(8); }, - offer_9() { do_offer(9); }, + log(`${player_names[game.active]} waived the bribe.`) + set_active(game.phasing) + end_bribe() + }, + offer_1() { do_offer(1) }, + offer_2() { do_offer(2) }, + offer_3() { do_offer(3) }, + offer_4() { do_offer(4) }, + offer_5() { do_offer(5) }, + offer_6() { do_offer(6) }, + offer_7() { do_offer(7) }, + offer_8() { do_offer(8) }, + offer_9() { do_offer(9) }, refuse() { - log(`${player_names[game.active]} refused to waive the bribe.`); - game.state = 'bribe'; - set_active(game.phasing); + log(`${player_names[game.active]} refused to waive the bribe.`) + game.state = 'bribe' + set_active(game.phasing) }, } function do_offer(n) { - log(`${player_names[game.active]} reduced the bribe to ${n}.`); - game.count = 1; - game.state = 'bribe'; - set_active(game.phasing); + log(`${player_names[game.active]} reduced the bribe to ${n}.`) + game.count = 1 + game.state = 'bribe' + set_active(game.phasing) } function end_bribe() { if (typeof game.where === 'string') - do_card_action_2(); + do_card_action_2() else - do_play_2(); + do_play_2() } // STARTING LOYALTY states.loyalty = { prompt() { - view.prompt = `Choose your loyalty \u2014 Afghan, British, or Russian.`; - gen_action('loyalty_afghan'); - gen_action('loyalty_british'); - gen_action('loyalty_russian'); + view.prompt = `Choose your loyalty \u2014 Afghan, British, or Russian.` + gen_action('loyalty_afghan') + gen_action('loyalty_british') + gen_action('loyalty_russian') }, loyalty_afghan() { - set_starting_loyalty(Afghan); + set_starting_loyalty(Afghan) }, loyalty_british() { - set_starting_loyalty(British); + set_starting_loyalty(British) }, loyalty_russian() { - set_starting_loyalty(Russian); + set_starting_loyalty(Russian) }, } function set_starting_loyalty(loyalty) { - log(`${player_names[game.active]} loyalty to ${loyalty}.`); - player.loyalty = loyalty; - let next = next_player(game.active); + log(`${player_names[game.active]} loyalty to ${loyalty}.`) + player.loyalty = loyalty + let next = next_player(game.active) if (game.players[next].loyalty) - goto_actions(); + goto_actions() else - set_active(next); + set_active(next) } // ACTION PHASE function goto_actions() { - game.phasing = game.active; - game.actions = 2; - game.used_cards = []; // track cards that have been used - game.used_pieces = []; - game.selected = -1; - game.where = 0; - logbr(); - log(`.turn ${player_names[game.phasing]}`); - logbr(); - - let bmh = active_can_blackmail(Herat); - let bmk = active_can_blackmail(Kandahar); + game.phasing = game.active + game.actions = 2 + game.used_cards = [] // track cards that have been used + game.used_pieces = [] + game.selected = -1 + game.where = 0 + logbr() + log(`.turn ${player_names[game.phasing]}`) + logbr() + + let bmh = active_can_blackmail(Herat) + let bmk = active_can_blackmail(Kandahar) if (bmh || bmk) { - game.state = 'blackmail'; - game.where = (bmh && bmk) ? -1 : (bmh ? Herat : Kandahar); - game.selected = select_available_cylinder(); + game.state = 'blackmail' + game.where = (bmh && bmk) ? -1 : (bmh ? Herat : Kandahar) + game.selected = select_available_cylinder() } else { - resume_actions(); + resume_actions() } } function end_action() { - check_leverage(); + check_leverage() } function resume_actions() { - set_active(game.phasing); - game.selected = -1; - game.where = 0; - game.state = 'actions'; + set_active(game.phasing) + game.selected = -1 + game.where = 0 + game.state = 'actions' } function goto_next_player() { - game.phasing = next_player(game.phasing); - set_active(game.phasing); - goto_actions(); + game.phasing = next_player(game.phasing) + set_active(game.phasing) + goto_actions() } states.actions = { prompt() { // Pass / End turn if (game.actions > 0) { - gen_action('end_turn_pass'); + gen_action('end_turn_pass') } else { - gen_action('end_turn'); + gen_action('end_turn') } // Purchase if (game.actions > 0) { for (let row = 0; row < 2; ++row) { for (let col = 0; col < 6; ++col) { - let c = game.market_cards[row][col]; + let c = game.market_cards[row][col] if (c && market_cost(col, c) <= player.coins && !game.used_cards.includes(c) && c !== PUBLIC_WITHDRAWAL) - gen_action('card', c); + gen_action('card', c) } } } @@ -960,190 +960,190 @@ states.actions = { // Play if (game.actions > 0) { for (let i = 0; i < player.hand.length; ++i) { - let c = player.hand[i]; - gen_action('play_left', c); - gen_action('play_right', c); + let c = player.hand[i] + gen_action('play_left', c) + gen_action('play_right', c) } } // Card-based actions - let bonus = false; + let bonus = false for (let i = 0; i < player.court.length; ++i) { - let c = player.court[i]; - let card = cards[c]; + let c = player.court[i] + let card = cards[c] if (game.used_cards.includes(c)) - continue; + continue - let favored = is_favored_suit(c); + let favored = is_favored_suit(c) if (favored || game.actions > 0) { - let usable = false; + let usable = false if (card.tax && active_can_tax()) { - gen_action('tax', c); - usable = true; + gen_action('tax', c) + usable = true } if (card.gift && player.coins >= min_gift_cost()) { - gen_action('gift', c); - usable = true; + gen_action('gift', c) + usable = true } if (card.build && player.coins >= 2 && active_rules_any_region()) { - gen_action('build', c); - usable = true; + gen_action('build', c) + usable = true } if (card.move && active_can_move()) { - gen_action('move', c); - usable = true; + gen_action('move', c) + usable = true } if (card.betray && player.coins >= 2 && active_can_betray()) { - gen_action('betray', c); - usable = true; + gen_action('betray', c) + usable = true } if (card.battle && active_can_battle()) { - gen_action('battle', c); - usable = true; + gen_action('battle', c) + usable = true } if (usable && favored) - bonus = true; + bonus = true } } if (game.actions === 2) - view.prompt = `You may take two actions.`; + view.prompt = `You may take two actions.` else if (game.actions === 1) - view.prompt = `You may take one more action.`; + view.prompt = `You may take one more action.` else - view.prompt = `You have no more actions.`; + view.prompt = `You have no more actions.` if (bonus) - view.prompt += ` You may take bonus actions.`; + view.prompt += ` You may take bonus actions.` }, // Purchase card from market card(c) { - push_undo(); - logbr(); + push_undo() + logbr() - let [row, col] = find_card_in_market(c); - game.actions --; + let [row, col] = find_card_in_market(c) + game.actions -- - let cost = market_cost(col, c); - let cost_per_card = cost / col; + let cost = market_cost(col, c) + let cost_per_card = cost / col for (let i = 0; i < col; ++i) { if (game.market_cards[row][i] > 0) { - game.market_coins[row][i] += cost_per_card; - game.used_cards.push(game.market_cards[row][i]); + game.market_coins[row][i] += cost_per_card + game.used_cards.push(game.market_cards[row][i]) } else { - game.market_coins[1-row][i] += cost_per_card; - game.used_cards.push(game.market_cards[1-row][i]); + game.market_coins[1-row][i] += cost_per_card + game.used_cards.push(game.market_cards[1-row][i]) } } - check_public_withdrawal(); + check_public_withdrawal() - logbr(); + logbr() if (cost > 0) - player.coins -= cost; + player.coins -= cost - let took = game.market_coins[row][col]; + let took = game.market_coins[row][col] if (took > 0) - player.coins += took; + player.coins += took - game.market_coins[row][col] = 0; - game.market_cards[row][col] = 0; + game.market_coins[row][col] = 0 + game.market_cards[row][col] = 0 - log(`Purchased #${c}.`); + log(`Purchased #${c}.`) if (cost > 0 && took > 0) - logi(`Paid ${cost} and took ${took}.`); + logi(`Paid ${cost} and took ${took}.`) else if (cost > 0) - logi(`Paid ${cost}.`); + logi(`Paid ${cost}.`) else if (took > 0) - logi(`Took ${took}.`); + logi(`Took ${took}.`) if (is_dominance_check(c)) { - do_dominance_check('purchase'); + do_dominance_check('purchase') } else if (is_event_card(c)) { - events_if_purchased[cards[c].if_purchased](); + events_if_purchased[cards[c].if_purchased]() } else { - player.hand.push(c); - resume_actions(); + player.hand.push(c) + resume_actions() } }, // Play card to court - play_left(c) { do_play_1(c, 0); }, - play_right(c) { do_play_1(c, 1); }, + play_left(c) { do_play_1(c, 0) }, + play_right(c) { do_play_1(c, 1) }, // Use card based ability - tax(c) { do_card_action_1(c, "Tax", 0); }, - gift(c) { do_card_action_1(c, "Gift", min_gift_cost()); }, - build(c) { do_card_action_1(c, "Build", 2); }, - move(c) { do_card_action_1(c, "Move", 0); }, - betray(c) { do_card_action_1(c, "Betray", 2); }, - battle(c) { do_card_action_1(c, "Battle", 0); }, + tax(c) { do_card_action_1(c, "Tax", 0) }, + gift(c) { do_card_action_1(c, "Gift", min_gift_cost()) }, + build(c) { do_card_action_1(c, "Build", 2) }, + move(c) { do_card_action_1(c, "Move", 0) }, + betray(c) { do_card_action_1(c, "Betray", 2) }, + battle(c) { do_card_action_1(c, "Battle", 0) }, end_turn_pass() { - logbr(); - log(`Passed.`); - goto_cleanup_court(); + logbr() + log(`Passed.`) + goto_cleanup_court() }, end_turn() { - goto_cleanup_court(); + goto_cleanup_court() }, } // PLAY CARD function do_play_1(c, side) { - push_undo(); - game.card = c; - game.where = side; + push_undo() + game.card = c + game.where = side if (!active_has_charismatic_courtiers() && !game.events.disregard_for_customs) { - let ruler = ruler_of_region(cards[c].region); + let ruler = ruler_of_region(cards[c].region) if (ruler >= 0 && ruler !== game.active) { - logbr(); - game.state = 'bribe'; - game.count = count_player_cylinders(ruler, cards[c].region); - game.reserve = 0; - game.bribe = ruler; - return; + logbr() + game.state = 'bribe' + game.count = count_player_cylinders(ruler, cards[c].region) + game.reserve = 0 + game.bribe = ruler + return } } - do_play_2(); + do_play_2() } function do_play_2() { - let c = game.card; - let side = game.where; - game.actions --; - logbr(); - log(`Played #${c}.`); - let idx = player.hand.indexOf(c); - player.hand.splice(idx, 1); + let c = game.card + let side = game.where + game.actions -- + logbr() + log(`Played #${c}.`) + let idx = player.hand.indexOf(c) + player.hand.splice(idx, 1) if (side) - player.court.push(c); + player.court.push(c) else - player.court.unshift(c); - goto_play_patriot(); + player.court.unshift(c) + goto_play_patriot() } function goto_play_patriot() { - let card = cards[game.card]; + let card = cards[game.card] if (card.patriot && card.patriot !== player.loyalty) - change_loyalty(card.patriot); - goto_play_tribes(); + change_loyalty(card.patriot) + goto_play_tribes() } function goto_play_tribes() { - let card = cards[game.card]; + let card = cards[game.card] if (card.tribes) { - game.count = card.tribes; - game.state = 'place_tribe'; - game.where = card.region; - game.selected = select_available_cylinder(); + game.count = card.tribes + game.state = 'place_tribe' + game.where = card.region + game.selected = select_available_cylinder() } else { - goto_play_roads(); + goto_play_roads() } } @@ -1151,39 +1151,39 @@ states.place_tribe = { inactive: "place tribe", prompt() { if (game.selected < 0) { - view.prompt = `Place tribe in ${region_names[game.where]} \u2014 select a cylinder.`; - gen_select_cylinder(); + view.prompt = `Place tribe in ${region_names[game.where]} \u2014 select a cylinder.` + gen_select_cylinder() } else { - view.prompt = `Place tribe in ${region_names[game.where]}.`; - gen_action('space', game.where); + view.prompt = `Place tribe in ${region_names[game.where]}.` + gen_action('space', game.where) } }, piece(x) { - push_undo(); - game.selected = x; + push_undo() + game.selected = x }, space(s) { - push_undo(); - logi(`Tribe to ${region_names[s]}.`); - game.pieces[game.selected] = s; - game.used_pieces.push(game.selected); - game.selected = -1; + push_undo() + logi(`Tribe to ${region_names[s]}.`) + game.pieces[game.selected] = s + game.used_pieces.push(game.selected) + game.selected = -1 if (--game.count === 0) - goto_play_roads(); + goto_play_roads() else - game.selected = select_available_cylinder(); + game.selected = select_available_cylinder() }, } function goto_play_roads() { - let card = cards[game.card]; + let card = cards[game.card] if (card.roads) { - game.count = card.roads; - game.state = 'place_road'; - game.where = card.region; - game.selected = select_available_block(); + game.count = card.roads + game.state = 'place_road' + game.where = card.region + game.selected = select_available_block() } else { - goto_play_armies(); + goto_play_armies() } } @@ -1191,40 +1191,40 @@ states.place_road = { inactive: "place road", prompt() { if (game.selected < 0) { - view.prompt = `Place ${player.loyalty} road in ${region_names[game.where]} \u2014 select a block to move.`; - gen_select_block(); + view.prompt = `Place ${player.loyalty} road in ${region_names[game.where]} \u2014 select a block to move.` + gen_select_block() } else { - view.prompt = `Place ${player.loyalty} road in ${region_names[game.where]}.`; + view.prompt = `Place ${player.loyalty} road in ${region_names[game.where]}.` for (let s of borders[game.where]) - gen_action('space', s); + gen_action('space', s) } }, piece(x) { - push_undo(); - game.selected = x; + push_undo() + game.selected = x }, space(s) { - push_undo(); - logi(`${player.loyalty} road to ${border_names[s]}.`); - game.pieces[game.selected] = s; - game.used_pieces.push(game.selected); - game.selected = -1; + push_undo() + logi(`${player.loyalty} road to ${border_names[s]}.`) + game.pieces[game.selected] = s + game.used_pieces.push(game.selected) + game.selected = -1 if (--game.count === 0) - goto_play_armies(); + goto_play_armies() else - game.selected = select_available_block(); + game.selected = select_available_block() }, } function goto_play_armies() { - let card = cards[game.card]; + let card = cards[game.card] if (card.armies) { - game.count = card.armies; - game.state = 'place_army'; - game.where = card.region; - game.selected = select_available_block(); + game.count = card.armies + game.state = 'place_army' + game.where = card.region + game.selected = select_available_block() } else { - goto_play_spies(); + goto_play_spies() } } @@ -1232,39 +1232,39 @@ states.place_army = { inactive: "place army", prompt() { if (game.selected < 0) { - view.prompt = `Place ${player.loyalty} army in ${region_names[game.where]} \u2014 select a block to move.`; - gen_select_block(); + view.prompt = `Place ${player.loyalty} army in ${region_names[game.where]} \u2014 select a block to move.` + gen_select_block() } else { - view.prompt = `Place ${player.loyalty} army in ${region_names[game.where]}.`; - gen_action('space', game.where); + view.prompt = `Place ${player.loyalty} army in ${region_names[game.where]}.` + gen_action('space', game.where) } }, piece(x) { - push_undo(); - game.selected = x; + push_undo() + game.selected = x }, space(s) { - push_undo(); - logi(`${player.loyalty} army to ${region_names[s]}.`); - game.pieces[game.selected] = s; - game.used_pieces.push(game.selected); - game.selected = -1; + push_undo() + logi(`${player.loyalty} army to ${region_names[s]}.`) + game.pieces[game.selected] = s + game.used_pieces.push(game.selected) + game.selected = -1 if (--game.count === 0) - goto_play_spies(); + goto_play_spies() else - game.selected = select_available_block(); + game.selected = select_available_block() }, } function goto_play_spies() { - let card = cards[game.card]; + let card = cards[game.card] if (card.spies) { - game.count = card.spies; - game.state = 'place_spy'; - game.where = card.region; - game.selected = select_available_cylinder(); + game.count = card.spies + game.state = 'place_spy' + game.where = card.region + game.selected = select_available_cylinder() } else { - goto_play_leveraged(); + goto_play_leveraged() } } @@ -1272,68 +1272,68 @@ states.place_spy = { inactive: "place spy", prompt() { if (game.selected < 0) { - view.prompt = `Place spy on a court card in ${region_names[game.where]} \u2014 select a cylinder.`; - gen_select_cylinder(); + view.prompt = `Place spy on a court card in ${region_names[game.where]} \u2014 select a cylinder.` + gen_select_cylinder() } else { - view.prompt = `Place spy on a court card in ${region_names[game.where]}.`; + view.prompt = `Place spy on a court card in ${region_names[game.where]}.` for (let p = 0; p < game.players.length; ++p) { - let court = game.players[p].court; + let court = game.players[p].court for (let i = 0; i < court.length; ++i) { - let card = cards[court[i]]; + let card = cards[court[i]] if (card.region === game.where) { - gen_action('card', court[i]); + gen_action('card', court[i]) } } } } }, piece(x) { - push_undo(); - game.selected = x; + push_undo() + game.selected = x }, card(c) { - push_undo(); - logi(`Spy to #${c}.`); - game.pieces[game.selected] = c; - game.used_pieces.push(game.selected); - game.selected = -1; + push_undo() + logi(`Spy to #${c}.`) + game.pieces[game.selected] = c + game.used_pieces.push(game.selected) + game.selected = -1 if (--game.count === 0) - goto_play_leveraged(); + goto_play_leveraged() else - game.selected = select_available_cylinder(); + game.selected = select_available_cylinder() }, } function goto_play_leveraged() { - let card = cards[game.card]; + let card = cards[game.card] if (card.leveraged) { - logi(`Leveraged.`); - player.coins += 2; + logi(`Leveraged.`) + player.coins += 2 } - goto_play_favored_suit_impact(); + goto_play_favored_suit_impact() } function goto_play_favored_suit_impact() { - let card = cards[game.card]; + let card = cards[game.card] if (card.climate && !game.events.pashtunwali_values) { - game.state = 'favored_suit_impact'; - game.where = card.climate; + game.state = 'favored_suit_impact' + game.where = card.climate } else { - end_action(); + end_action() } } states.favored_suit_impact = { inactive: "favored suit", prompt() { - view.prompt = `Change the favored suit to ${game.where}.`; - gen_action('suit', game.where); + view.prompt = `Change the favored suit to ${game.where}.` + gen_action('suit', game.where) }, suit(suit) { - push_undo(); - logi(`Favored suit to ${suit}.`); - game.favored = suit; - end_action(); + push_undo() + logi(`Favored suit to ${suit}.`) + game.favored = suit + end_action() }, } @@ -1341,55 +1341,55 @@ states.favored_suit_impact = { const card_action_table = { Tax() { - game.state = 'tax'; + game.state = 'tax' }, Gift() { - game.selected = select_available_cylinder(); - game.state = 'gift'; + game.selected = select_available_cylinder() + game.state = 'gift' }, Build() { - game.count = Math.min(3, Math.floor(player.coins / 2)); + game.count = Math.min(3, Math.floor(player.coins / 2)) if (player.events.nation_building) - game.count *= 2; - game.selected = select_available_block(); - game.reserve = 0; - game.state = 'build'; + game.count *= 2 + game.selected = select_available_block() + game.reserve = 0 + game.state = 'build' }, Move() { - game.selected = -1; - game.state = 'move'; + game.selected = -1 + game.state = 'move' }, Betray() { - pay_action_cost(2, 0); - game.state = 'betray'; + pay_action_cost(2, 0) + game.state = 'betray' }, Battle() { - game.state = 'battle'; - game.where = 0; + game.state = 'battle' + game.where = 0 }, } function do_card_action_1(c, what, reserve) { - push_undo(); - game.card = c; - game.where = what; + push_undo() + game.card = c + game.where = what if (!active_has_civil_service_reforms() && !game.events.disregard_for_customs) { - let who = player_with_most_spies(c); + let who = player_with_most_spies(c) if (who >= 0 && who !== game.active) { - logbr(); - game.state = 'bribe'; - game.count = count_player_cylinders(who, c); - game.reserve = reserve; - game.bribe = who; - return; + logbr() + game.state = 'bribe' + game.count = count_player_cylinders(who, c) + game.reserve = reserve + game.bribe = who + return } } - do_card_action_2(); + do_card_action_2() } const past_what = { @@ -1402,95 +1402,95 @@ const past_what = { } function do_card_action_2() { - let c = game.card; - let what = game.where; - game.used_cards.push(c); + let c = game.card + let what = game.where + game.used_cards.push(c) if (!is_favored_suit(c)) - game.actions --; - game.count = cards[c].rank; - logbr(); - log(`${past_what[what]} with #${c}.`); - card_action_table[what](); + game.actions -- + game.count = cards[c].rank + logbr() + log(`${past_what[what]} with #${c}.`) + card_action_table[what]() } // CARD-BASED ACTION: TAX function can_tax_player(active, p, claim) { - let okay = claim; - let shelter = 0; - let court = game.players[p].court; + let okay = claim + let shelter = 0 + let court = game.players[p].court for (let i = 0; i < court.length; ++i) { - let c = court[i]; + let c = court[i] if (!okay && player_rules_region(active, cards[c].region)) - okay = true; + okay = true if (cards[c].suit === Economic) - shelter += cards[c].rank; + shelter += cards[c].rank } - return okay && game.players[p].coins > shelter; + return okay && game.players[p].coins > shelter } function do_tax_player(p) { - push_undo(); - logi(`Taxed ${player_names[p]} player.`); - game.players[p].coins --; - player.coins ++; + push_undo() + logi(`Taxed ${player_names[p]} player.`) + game.players[p].coins -- + player.coins ++ if (--game.count === 0) - end_action(); + end_action() } function active_can_tax() { for (let row = 0; row < 2; ++row) for (let col = 0; col < 6; ++col) if (game.market_coins[row][col] > 0) - return true; - let claim = active_has_claim_of_ancient_lineage(); + return true + let claim = active_has_claim_of_ancient_lineage() for (let p = 0; p < game.players.length; ++p) if (p !== game.active && can_tax_player(game.active, p, claim)) - return true; - return false; + return true + return false } states.tax = { prompt() { if (game.count === 1) - view.prompt = `Tax \u2014 take up to ${game.count} rupee from market cards or players.`; + view.prompt = `Tax \u2014 take up to ${game.count} rupee from market cards or players.` else - view.prompt = `Tax \u2014 take up to ${game.count} rupees from market cards or players.`; + view.prompt = `Tax \u2014 take up to ${game.count} rupees from market cards or players.` for (let row = 0; row < 2; ++row) { for (let col = 0; col < 6; ++col) { if (game.market_coins[row][col] > 0) - gen_action('card', game.market_cards[row][col]); + gen_action('card', game.market_cards[row][col]) } } - let claim = active_has_claim_of_ancient_lineage(); + let claim = active_has_claim_of_ancient_lineage() for (let p = 0; p < game.players.length; ++p) { if (p !== game.active && can_tax_player(game.active, p, claim)) { - gen_action('player_' + p); + gen_action('player_' + p) } } - gen_action('pass'); + gen_action('pass') }, pass() { - push_undo(); - end_action(); + push_undo() + end_action() }, card(c) { - push_undo(); - let [row, col] = find_card_in_market(c); - logi(`Taxed market.`); - game.market_coins[row][col] --; - player.coins ++; + push_undo() + let [row, col] = find_card_in_market(c) + logi(`Taxed market.`) + game.market_coins[row][col] -- + player.coins ++ if (--game.count === 0) - end_action(); + end_action() }, - player_0() { do_tax_player(0); }, - player_1() { do_tax_player(1); }, - player_2() { do_tax_player(2); }, - player_3() { do_tax_player(3); }, - player_4() { do_tax_player(4); }, + player_0() { do_tax_player(0) }, + player_1() { do_tax_player(1) }, + player_2() { do_tax_player(2) }, + player_3() { do_tax_player(3) }, + player_4() { do_tax_player(4) }, } // CARD-BASED ACTION: GIFT @@ -1498,30 +1498,30 @@ states.tax = { states.gift = { prompt() { if (game.selected < 0) { - view.prompt = `Select cylinder to place as Gift.`; - gen_select_cylinder(); + view.prompt = `Select cylinder to place as Gift.` + gen_select_cylinder() } else { - view.prompt = `Place cylinder as Gift.`; - gen_place_gift(); + view.prompt = `Place cylinder as Gift.` + gen_place_gift() } }, piece(x) { - push_undo(); - game.selected = x; + push_undo() + game.selected = x }, place_gift(cost) { - push_undo(); - logi(`Paid ${cost}.`); - pay_action_cost(cost, 0); + push_undo() + logi(`Paid ${cost}.`) + pay_action_cost(cost, 0) if (cost === 2) - game.pieces[game.selected] = Gift2; + game.pieces[game.selected] = Gift2 else if (cost === 4) - game.pieces[game.selected] = Gift4; + game.pieces[game.selected] = Gift4 else if (cost === 6) - game.pieces[game.selected] = Gift6; - game.used_pieces.push(game.selected); - game.selected = -1; - end_action(); + game.pieces[game.selected] = Gift6 + game.used_pieces.push(game.selected) + game.selected = -1 + end_action() } } @@ -1529,222 +1529,222 @@ states.gift = { states.build = { prompt() { - view.prompt = `Build up to ${game.count} armies and/or roads.`; - gen_action('next'); - let must_pay = !player.events.nation_building || (game.count & 1) === 0; + view.prompt = `Build up to ${game.count} armies and/or roads.` + gen_action('next') + let must_pay = !player.events.nation_building || (game.count & 1) === 0 if (!must_pay || player.coins >= 2) { if (game.selected < 0) { - gen_select_block(); + gen_select_block() } else { for (let r = first_region; r <= last_region; ++r) { if (active_rules_region(r)) { - gen_action('space', r); + gen_action('space', r) for (let s of borders[r]) - gen_action('space', s); + gen_action('space', s) } } } } }, piece(x) { - push_undo(); - game.selected = x; + push_undo() + game.selected = x }, space(s) { - push_undo(); + push_undo() - let must_pay = !player.events.nation_building || (game.count & 1) === 0; + let must_pay = !player.events.nation_building || (game.count & 1) === 0 if (must_pay) - pay_action_cost(2, game.reserve++); + pay_action_cost(2, game.reserve++) if (s <= last_region) - logi(`${player.loyalty} army to ${region_names[s]}.`); + logi(`${player.loyalty} army to ${region_names[s]}.`) else - logi(`${player.loyalty} road to ${border_names[s]}.`); - game.pieces[game.selected] = s; - game.used_pieces.push(game.selected); - game.selected = -1; - --game.count; + logi(`${player.loyalty} road to ${border_names[s]}.`) + game.pieces[game.selected] = s + game.used_pieces.push(game.selected) + game.selected = -1 + --game.count - must_pay = !player.events.nation_building || (game.count & 1) === 0; + must_pay = !player.events.nation_building || (game.count & 1) === 0 if (game.count === 0 || (must_pay && player.coins < 2)) - end_build_action(); + end_build_action() else - game.selected = select_available_block(); + game.selected = select_available_block() }, next() { - push_undo(); - end_build_action(); + push_undo() + end_build_action() }, } states.infrastructure = { prompt() { - view.prompt = `Place an additional block.`; + view.prompt = `Place an additional block.` if (game.selected < 0) { - gen_select_block(); + gen_select_block() } else { for (let r = first_region; r <= last_region; ++r) { if (active_rules_region(r)) { - gen_action('space', r); + gen_action('space', r) for (let s of borders[r]) - gen_action('space', s); + gen_action('space', s) } } } }, piece(x) { - push_undo(); - game.selected = x; + push_undo() + game.selected = x }, space(s) { - push_undo(); + push_undo() if (s <= last_region) - logi(`${player.loyalty} army to ${region_names[s]}.`); + logi(`${player.loyalty} army to ${region_names[s]}.`) else - logi(`${player.loyalty} road to ${border_names[s]}.`); - game.pieces[game.selected] = s; - game.used_pieces.push(game.selected); - game.selected = -1; - end_action(); + logi(`${player.loyalty} road to ${border_names[s]}.`) + game.pieces[game.selected] = s + game.used_pieces.push(game.selected) + game.selected = -1 + end_action() }, } function end_build_action() { if (active_has_infrastructure()) { - game.state = 'infrastructure'; - game.selected = select_available_block(); + game.state = 'infrastructure' + game.selected = select_available_block() } else { - end_action(); + end_action() } } // CARD-BASED ACTION: MOVE function last_court_position() { - let n = 0; + let n = 0 for (let p = 0; p < game.players.length; ++p) - n += game.players[p].court.length; - return n - 1; + n += game.players[p].court.length + return n - 1 } function court_card_from_position(y) { - let x = 0; + let x = 0 for (let p = 0; p < game.players.length; ++p) { - let court = game.players[p].court; + let court = game.players[p].court if (y < x + court.length) - return court[y - x]; - x += court.length; + return court[y - x] + x += court.length } - return 0; + return 0 } function court_position_from_card(c) { - let x = 0; + let x = 0 for (let p = 0; p < game.players.length; ++p) { - let court = game.players[p].court; + let court = game.players[p].court for (let i = 0; i < court.length; ++i) { if (c === court[i]) - return x + i; + return x + i } - x += court.length; + x += court.length } - return -1; + return -1 } function find_border(a, b) { if (a > b) { - let c = a; a = b; b = c; + let c = a; a = b; b = c } - if (a === Persia && b === Transcaspia) return Persia_Transcaspia; - if (a === Persia && b === Herat) return Persia_Herat; - if (a === Transcaspia && b === Herat) return Transcaspia_Herat; - if (a === Transcaspia && b === Kabul) return Transcaspia_Kabul; - if (a === Herat && b === Kabul) return Herat_Kabul; - if (a === Herat && b === Kandahar) return Herat_Kandahar; - if (a === Kabul && b === Kandahar) return Kabul_Kandahar; - if (a === Kabul && b === Punjab) return Kabul_Punjab; - if (a === Kandahar && b === Punjab) return Kandahar_Punjab; - throw new Error(`bad border ${a} ${b}`); + if (a === Persia && b === Transcaspia) return Persia_Transcaspia + if (a === Persia && b === Herat) return Persia_Herat + if (a === Transcaspia && b === Herat) return Transcaspia_Herat + if (a === Transcaspia && b === Kabul) return Transcaspia_Kabul + if (a === Herat && b === Kabul) return Herat_Kabul + if (a === Herat && b === Kandahar) return Herat_Kandahar + if (a === Kabul && b === Kandahar) return Kabul_Kandahar + if (a === Kabul && b === Punjab) return Kabul_Punjab + if (a === Kandahar && b === Punjab) return Kandahar_Punjab + throw new Error(`bad border ${a} ${b}`) } function can_army_move_across_border(here, next) { - let border = find_border(here, next); - let b = active_coalition_blocks(); + let border = find_border(here, next) + let b = active_coalition_blocks() for (let i = b; i < b + 12; ++i) if (game.pieces[i] === border) - return true; - return false; + return true + return false } function active_can_move_from_region(here) { - let supplies = active_has_indian_supplies(); + let supplies = active_has_indian_supplies() for (let next of roads[here]) if (supplies || can_army_move_across_border(here, next)) - return true; - return false; + return true + return false } function active_can_move() { - let b = active_coalition_blocks(); - let x = active_cylinders(); + let b = active_coalition_blocks() + let x = active_cylinders() for (let i = b; i < b + 12; ++i) { - let r = game.pieces[i]; + let r = game.pieces[i] if (r >= first_region && r <= last_region) if (active_can_move_from_region(r)) - return true; + return true } for (let i = x; i < x + 10; ++i) { - let r = game.pieces[i]; + let r = game.pieces[i] if (r > 0 && r <= 100) - return true; + return true } if (player.events.nationalism) { for (let i = x; i < x + 10; ++i) { - let r = game.pieces[i]; + let r = game.pieces[i] if (r >= first_region && r <= last_region) if (active_can_move_from_region(r)) - return true; + return true } } - return false; + return false } states.move = { prompt() { if (game.selected >= 0) { - let here = game.pieces[game.selected]; + let here = game.pieces[game.selected] // Spy on a court card if (here <= 100) { - let c = here; - here = court_position_from_card(here); - view.prompt = `Move spy from #${c}.`; + let c = here + here = court_position_from_card(here) + view.prompt = `Move spy from #${c}.` - let last = last_court_position(); - let prev = here > 0 ? here - 1 : last; - let next = here < last ? here + 1 : 0; - gen_action('card', court_card_from_position(prev)); - gen_action('card', court_card_from_position(next)); + let last = last_court_position() + let prev = here > 0 ? here - 1 : last + let next = here < last ? here + 1 : 0 + gen_action('card', court_card_from_position(prev)) + gen_action('card', court_card_from_position(next)) if (active_has_well_connected()) { - let pprev = prev > 0 ? prev - 1 : last; - let nnext = next < last ? next + 1 : 0; - gen_action('card', court_card_from_position(pprev)); - gen_action('card', court_card_from_position(nnext)); + let pprev = prev > 0 ? prev - 1 : last + let nnext = next < last ? next + 1 : 0 + gen_action('card', court_card_from_position(pprev)) + gen_action('card', court_card_from_position(nnext)) } if (active_has_strange_bedfellows()) { - let r = cards[c].region; + let r = cards[c].region for (let p = 0; p < game.players.length; ++p) { - let court = game.players[p].court; + let court = game.players[p].court for (let i = 0; i < court.length; ++i) if (cards[court[i]].region === r) - gen_action('card', court[i]); + gen_action('card', court[i]) } } } @@ -1752,63 +1752,63 @@ states.move = { // Army or tribe in a region else { if (game.selected < 36) - view.prompt = `Move ${player.loyalty} army from ${region_names[here]}.`; + view.prompt = `Move ${player.loyalty} army from ${region_names[here]}.` else - view.prompt = `Move ${player_names[game.active]} tribe from ${region_names[here]}.`; - let supplies = active_has_indian_supplies(); + view.prompt = `Move ${player_names[game.active]} tribe from ${region_names[here]}.` + let supplies = active_has_indian_supplies() for (let next of roads[here]) if (supplies || can_army_move_across_border(here, next)) - gen_action('space', next); + gen_action('space', next) } } else { if (player.events.nationalism) { if (game.count === 1) - view.prompt = `Move up to ${game.count} spy, army, or tribe \u2014 select a spy, army, or tribe to move.`; + view.prompt = `Move up to ${game.count} spy, army, or tribe \u2014 select a spy, army, or tribe to move.` else - view.prompt = `Move up to ${game.count} spies, armies, and/or tribes \u2014 select a spy, army, or tribe to move.`; + view.prompt = `Move up to ${game.count} spies, armies, and/or tribes \u2014 select a spy, army, or tribe to move.` } else { if (game.count === 1) - view.prompt = `Move up to ${game.count} spy or army \u2014 select a spy or army to move.`; + view.prompt = `Move up to ${game.count} spy or army \u2014 select a spy or army to move.` else - view.prompt = `Move up to ${game.count} spies and/or armies \u2014 select a spy or army to move.`; + view.prompt = `Move up to ${game.count} spies and/or armies \u2014 select a spy or army to move.` } - gen_action('next'); - gen_select_army_to_move(); - gen_select_spy_to_move(); + gen_action('next') + gen_select_army_to_move() + gen_select_spy_to_move() if (player.events.nationalism) - gen_select_tribe_to_move(); + gen_select_tribe_to_move() } }, piece(x) { - push_undo(); - game.selected = x; + push_undo() + game.selected = x }, card(c) { - push_undo(); - let old = game.pieces[game.selected]; - logi(`Spy from #${old} to #${c}.`); - game.pieces[game.selected] = c; - game.selected = -1; + push_undo() + let old = game.pieces[game.selected] + logi(`Spy from #${old} to #${c}.`) + game.pieces[game.selected] = c + game.selected = -1 if (--game.count === 0) - end_action(); + end_action() }, space(s) { - push_undo(); - let old = game.pieces[game.selected]; - game.pieces[game.selected] = s; + push_undo() + let old = game.pieces[game.selected] + game.pieces[game.selected] = s if (game.selected < 36) { - logi(`${player.loyalty} army from ${region_names[old]} to ${region_names[s]}.`); + logi(`${player.loyalty} army from ${region_names[old]} to ${region_names[s]}.`) } else { - logi(`Tribe from ${region_names[old]} to ${region_names[s]}.`); - check_region_overthrow(game.active, old); + logi(`Tribe from ${region_names[old]} to ${region_names[s]}.`) + check_region_overthrow(game.active, old) } - game.selected = -1; + game.selected = -1 if (--game.count === 0) - end_action(); + end_action() }, next() { - push_undo(); - end_action(); + push_undo() + end_action() }, } @@ -1816,29 +1816,29 @@ states.move = { states.betray = { prompt() { - view.prompt = `Discard one court card where you have a spy.`; - let x = active_cylinders(); + view.prompt = `Discard one court card where you have a spy.` + let x = active_cylinders() for (let i = x; i < x + 10; ++i) { if (game.pieces[i] > 0 && game.pieces[i] <= 100) { - let c = game.pieces[i]; + let c = game.pieces[i] if (cards[c].suit !== Political) { - gen_action('card', c); + gen_action('card', c) } else { - let p = find_card_in_court(c); + let p = find_card_in_court(c) if (!player_has_bodyguards(p)) - gen_action('card', c); + gen_action('card', c) } } } }, card(c) { - push_undo(); - discard_court_card(c); + push_undo() + discard_court_card(c) if (cards[c].prize) { - game.card = c; - game.state = 'accept_prize'; + game.card = c + game.state = 'accept_prize' } else { - end_action(); + end_action() } }, } @@ -1846,19 +1846,19 @@ states.betray = { states.accept_prize = { inactive: "accept prize", prompt() { - view.prompt = `You may accept #${game.card} as ${a_coalition(cards[game.card].prize)} prize.`; - gen_action('accept'); - gen_action('refuse'); + view.prompt = `You may accept #${game.card} as ${a_coalition(cards[game.card].prize)} prize.` + gen_action('accept') + gen_action('refuse') }, accept() { - log(`Accepted ${cards[game.card].prize} prize.`); + log(`Accepted ${cards[game.card].prize} prize.`) if (cards[game.card].prize !== player.loyalty) - change_loyalty(cards[game.card].prize); - player.prizes ++; - end_action(); + change_loyalty(cards[game.card].prize) + player.prizes ++ + end_action() }, refuse() { - end_action(); + end_action() }, } @@ -1868,229 +1868,229 @@ function gen_battle_blocks(where) { if (player.loyalty !== Afghan) for (let i = 0; i < 12; ++i) if (game.pieces[i] === where) - gen_action('piece', i); + gen_action('piece', i) if (player.loyalty !== British) for (let i = 12; i < 24; ++i) if (game.pieces[i] === where) - gen_action('piece', i); + gen_action('piece', i) if (player.loyalty !== Russian) for (let i = 24; i < 36; ++i) if (game.pieces[i] === where) - gen_action('piece', i); + gen_action('piece', i) } function count_active_spies_on_card(where) { - let n = 0; - let x = active_cylinders(); + let n = 0 + let x = active_cylinders() for (let i = x; i < x + 10; ++i) if (game.pieces[i] === where) - ++n; - return n; + ++n + return n } function count_enemy_spies_on_card(where) { - let n = 0; + let n = 0 for (let p = 0; p < game.players.length; ++p) { if (p === game.active) - continue; + continue if (player_has_indispensable_advisors(p)) - continue; - let x = player_cylinders(p); + continue + let x = player_cylinders(p) for (let i = x; i < x + 10; ++i) { if (game.pieces[i] === where) - ++n; + ++n } } - return n; + return n } function count_player_armies_in_region(p, where) { - let n = 0; - let b = player_coalition_blocks(p); + let n = 0 + let b = player_coalition_blocks(p) for (let i = b; i < b + 12; ++i) if (game.pieces[i] === where) - ++n; - return n; + ++n + return n } function count_player_cylinders(p, r) { - let x = player_cylinders(p); - let n = 0; + let x = player_cylinders(p) + let n = 0 for (let i = x; i < x + 10; ++i) if (game.pieces[i] === r) - ++n; - return n; + ++n + return n } function count_active_armies_in_region(where) { - let n = count_player_armies_in_region(game.active, where); + let n = count_player_armies_in_region(game.active, where) if (player.events.nationalism) - n += count_player_cylinders(game.active, where); - return n; + n += count_player_cylinders(game.active, where) + return n } function count_enemy_blocks_on_border(where) { - let n = 0; + let n = 0 if (player.loyalty !== Afghan) for (let i = 0; i < 12; ++i) if (game.pieces[i] === where) - ++n; + ++n if (player.loyalty !== British) for (let i = 12; i < 24; ++i) if (game.pieces[i] === where) - ++n; + ++n if (player.loyalty !== Russian) for (let i = 24; i < 36; ++i) if (game.pieces[i] === where) - ++n; - return n; + ++n + return n } function count_enemy_tribes_and_blocks_in_region(where) { - let n = 0; + let n = 0 for (let p = 0; p < game.players.length; ++p) { if (game.players[p].loyalty === player.loyalty) - continue; + continue if (player_has_citadel(p, where)) - continue; - let x = player_cylinders(p); + continue + let x = player_cylinders(p) for (let i = x; i < x + 10; ++i) if (game.pieces[i] === where) - ++n; + ++n } if (player.loyalty !== Afghan) for (let i = 0; i < 12; ++i) if (game.pieces[i] === where) - ++n; + ++n if (player.loyalty !== British) for (let i = 12; i < 24; ++i) if (game.pieces[i] === where) - ++n; + ++n if (player.loyalty !== Russian) for (let i = 24; i < 36; ++i) if (game.pieces[i] === where) - ++n; + ++n for (let b of borders[where]) - n += count_enemy_blocks_on_border(b); - return n; + n += count_enemy_blocks_on_border(b) + return n } function is_battle_card(where) { - return count_active_spies_on_card(where) > 0 && count_enemy_spies_on_card(where) > 0; + return count_active_spies_on_card(where) > 0 && count_enemy_spies_on_card(where) > 0 } function is_battle_region(where) { - return count_active_armies_in_region(where) > 0 && count_enemy_tribes_and_blocks_in_region(where) > 0; + return count_active_armies_in_region(where) > 0 && count_enemy_tribes_and_blocks_in_region(where) > 0 } function piece_owner(x) { - if (x < 12) return "Afghan"; - if (x < 24) return "British"; - if (x < 36) return "Russian"; - if (x < 36+10) return player_names[0]; - if (x < 36+20) return player_names[1]; - if (x < 36+30) return player_names[2]; - if (x < 36+40) return player_names[3]; - if (x < 36+50) return player_names[4]; - return "undefined"; + if (x < 12) return "Afghan" + if (x < 24) return "British" + if (x < 36) return "Russian" + if (x < 36+10) return player_names[0] + if (x < 36+20) return player_names[1] + if (x < 36+30) return player_names[2] + if (x < 36+40) return player_names[3] + if (x < 36+50) return player_names[4] + return "undefined" } function active_can_battle() { for (let r = first_region; r <= last_region; ++r) { if (is_battle_region(r)) - return true; + return true } for (let p = 0; p < game.players.length; ++p) { - let court = game.players[p].court; + let court = game.players[p].court for (let i = 0; i < court.length; ++i) if (is_battle_card(court[i])) - return true; + return true } - return false; + return false } states.battle = { prompt() { if (game.where <= 0) { - view.prompt = `Start a battle in a single region or on a court card.`; + view.prompt = `Start a battle in a single region or on a court card.` for (let p = 0; p < game.players.length; ++p) { - let court = game.players[p].court; + let court = game.players[p].court for (let i = 0; i < court.length; ++i) { if (is_battle_card(court[i])) - gen_action('card', court[i]); + gen_action('card', court[i]) } } for (let r = first_region; r <= last_region; ++r) { if (is_battle_region(r)) - gen_action('space', r); + gen_action('space', r) } } else { - let where = game.where; + let where = game.where if (where >= first_region && where <= last_region) { - view.prompt = `Remove up to ${game.count} tribes, roads, or armies from ${region_names[where]}.`; - gen_battle_blocks(where); + view.prompt = `Remove up to ${game.count} tribes, roads, or armies from ${region_names[where]}.` + gen_battle_blocks(where) for (let border of borders[where]) - gen_battle_blocks(border); + gen_battle_blocks(border) for (let p = 0; p < game.players.length; ++p) { if (p !== game.active && game.players[p].loyalty !== player.loyalty && !player_has_citadel(p, where)) { - let x = player_cylinders(p); + let x = player_cylinders(p) for (let i = x; i < x + 10; ++i) if (game.pieces[i] === where) - gen_action('piece', i); + gen_action('piece', i) } } } else { - view.prompt = `Remove up to ${game.count} spies on #${where}.`; + view.prompt = `Remove up to ${game.count} spies on #${where}.` for (let p = 0; p < game.players.length; ++p) { if (p !== game.active && !player_has_indispensable_advisors(p)) { - let x = player_cylinders(p); + let x = player_cylinders(p) for (let i = x; i < x + 10; ++i) if (game.pieces[i] === where) - gen_action('piece', i); + gen_action('piece', i) } } } - gen_action('next'); + gen_action('next') } }, card(where) { - push_undo(); - //logi(`Battle on #${where}.`); - game.where = where; - game.count = Math.min(game.count, count_active_spies_on_card(where)); + push_undo() + //logi(`Battle on #${where}.`) + game.where = where + game.count = Math.min(game.count, count_active_spies_on_card(where)) }, space(s) { - push_undo(); - //logi(`Battle in ${region_names[s]}.`); - game.where = s; - game.count = Math.min(game.count, count_active_armies_in_region(s)); + push_undo() + //logi(`Battle in ${region_names[s]}.`) + game.where = s + game.count = Math.min(game.count, count_active_armies_in_region(s)) }, piece(x) { - push_undo(); - let where = game.pieces[x]; - game.pieces[x] = 0; + push_undo() + let where = game.pieces[x] + game.pieces[x] = 0 if (x < 36) { if (where >= first_region && where <= last_region) - logi(`Removed ${piece_owner(x)} army from ${region_names[where]}.`); + logi(`Removed ${piece_owner(x)} army from ${region_names[where]}.`) else - logi(`Removed ${piece_owner(x)} road from ${border_names[where]}.`); + logi(`Removed ${piece_owner(x)} road from ${border_names[where]}.`) } else { - let p = Math.floor((x - 36) / 10); + let p = Math.floor((x - 36) / 10) if (where <= 100) { - logi(`Removed ${piece_owner(x)} spy from #${where}.`); + logi(`Removed ${piece_owner(x)} spy from #${where}.`) if (player_has_safe_house(p)) - game.pieces[x] = Safe_House; + game.pieces[x] = Safe_House } else { - logi(`Removed ${piece_owner(x)} tribe from ${region_names[where]}.`); - check_region_overthrow(p, where); + logi(`Removed ${piece_owner(x)} tribe from ${region_names[where]}.`) + check_region_overthrow(p, where) } } if (--game.count === 0) - end_action(); + end_action() }, next() { - push_undo(); - end_action(); + push_undo() + end_action() } } @@ -2099,142 +2099,142 @@ states.battle = { function active_can_blackmail(r) { if ((r === Herat && active_has_blackmail_herat()) || (r === Kandahar && active_has_blackmail_kandahar())) { for (let p = 0; p < game.players.length; ++p) { - let court = game.players[p].court; + let court = game.players[p].court for (let i = 0; i < court.length; ++i) { - let c = court[i]; + let c = court[i] if (cards[c].region === r && card_has_no_spies(c)) - return true; + return true } } } - return false; + return false } states.blackmail = { prompt() { - let bmh = game.where !== Kandahar && active_can_blackmail(Herat); - let bmk = game.where !== Herat && active_can_blackmail(Kandahar); - let msg = ""; + let bmh = game.where !== Kandahar && active_can_blackmail(Herat) + let bmk = game.where !== Herat && active_can_blackmail(Kandahar) + let msg = "" if (bmh && bmk) - msg = "any Herat and/or Kandahar court card without a spy"; + msg = "any Herat and/or Kandahar court card without a spy" else if (bmh) - msg = "any Herat court card without a spy"; + msg = "any Herat court card without a spy" else - msg = "any Kandahar court card without a spy"; + msg = "any Kandahar court card without a spy" if (game.selected < 0) { - view.prompt = `Blackmail \u2014 select a spy to place on ${msg}.`; - gen_select_cylinder(); + view.prompt = `Blackmail \u2014 select a spy to place on ${msg}.` + gen_select_cylinder() } else { - let bmh = game.where !== Kandahar && active_can_blackmail(Herat); - let bmk = game.where !== Herat && active_can_blackmail(Kandahar); - view.prompt = `Blackmail \u2014 place a spy on ${msg}.`; + let bmh = game.where !== Kandahar && active_can_blackmail(Herat) + let bmk = game.where !== Herat && active_can_blackmail(Kandahar) + view.prompt = `Blackmail \u2014 place a spy on ${msg}.` for (let p = 0; p < game.players.length; ++p) { - let court = game.players[p].court; + let court = game.players[p].court for (let i = 0; i < court.length; ++i) { - let c = court[i]; + let c = court[i] if (bmh && cards[c].region === Herat && card_has_no_spies(c)) - gen_action('card', c); + gen_action('card', c) if (bmk && cards[c].region === Kandahar && card_has_no_spies(c)) - gen_action('card', c); + gen_action('card', c) } } } - gen_action('pass'); + gen_action('pass') }, piece(x) { - push_undo(); - game.selected = x; + push_undo() + game.selected = x }, card(c) { - push_undo(); - log(`Blackmail spy to #${c}.`); - game.pieces[game.selected] = c; - game.used_pieces.push(game.selected); + push_undo() + log(`Blackmail spy to #${c}.`) + game.pieces[game.selected] = c + game.used_pieces.push(game.selected) if (game.where < 0) { - game.where = (cards[c].region === Herat) ? Kandahar : Herat; - game.selected = select_available_cylinder(); + game.where = (cards[c].region === Herat) ? Kandahar : Herat + game.selected = select_available_cylinder() } else { - resume_actions(); + resume_actions() } }, pass() { - push_undo(); - resume_actions(); + push_undo() + resume_actions() }, } // PASSIVE: SAFEHOUSE function check_safe_house() { - let first = game.phasing; + let first = game.phasing for (let p = first; p < game.players.length; ++p) if (check_player_safe_house(p)) - return; + return for (let p = 0; p < first; ++p) if (check_player_safe_house(p)) - return; + return if (game.actions >= 0) - resume_actions(); + resume_actions() else - goto_cleanup_hand(); + goto_cleanup_hand() } function check_player_safe_house(p) { - let x = player_cylinders(p); + let x = player_cylinders(p) for (let i = x; i < x + 10; ++i) { if (game.pieces[i] === Safe_House) { if (player_has_safe_house(p)) { - set_active(p); - logbr(); - game.state = 'safe_house'; - game.selected = i; - return true; + set_active(p) + logbr() + game.state = 'safe_house' + game.selected = i + return true } else { - game.pieces[i] = 0; + game.pieces[i] = 0 } } } - return false; + return false } states.safe_house = { inactive: "safe house", prompt() { - view.prompt = `Safe House \u2014 you may place your killed spy on a Safe House.`; + view.prompt = `Safe House \u2014 you may place your killed spy on a Safe House.` if (player.court.indexOf(SAFE_HOUSE_1) >= 0) - gen_action('card', SAFE_HOUSE_1); + gen_action('card', SAFE_HOUSE_1) if (player.court.indexOf(SAFE_HOUSE_2) >= 0) - gen_action('card', SAFE_HOUSE_2); - gen_action('pass'); + gen_action('card', SAFE_HOUSE_2) + gen_action('pass') }, card(c) { - push_undo(); - log(`${player_names[game.active]} spy to #${c}.`); - game.pieces[game.selected] = c; - game.selected = -1; - check_safe_house(); + push_undo() + log(`${player_names[game.active]} spy to #${c}.`) + game.pieces[game.selected] = c + game.selected = -1 + check_safe_house() }, pass() { - push_undo(); - game.pieces[game.selected] = 0; - game.selected = -1; - check_safe_house(); + push_undo() + game.pieces[game.selected] = 0 + game.selected = -1 + check_safe_house() }, } // PASSIVE: INSURRECTION function check_insurrection() { - let prince = which_player_has_insurrection(); + let prince = which_player_has_insurrection() if (prince >= 0) { - logbr(); - log(`Prince Akbar Khan`); - game.count = 2; - game.selected = select_afghan_block(); - set_active(prince); - game.state = 'insurrection'; + logbr() + log(`Prince Akbar Khan`) + game.count = 2 + game.selected = select_afghan_block() + set_active(prince) + game.state = 'insurrection' } else { - end_dominance_check(); + end_dominance_check() } } @@ -2242,165 +2242,165 @@ states.insurrection = { prompt() { if (game.count > 0) { if (game.selected < 0) { - view.prompt = `Insurrection \u2014 select an Afghan block to move.`; - gen_select_block(); + view.prompt = `Insurrection \u2014 select an Afghan block to move.` + gen_select_block() } else { - view.prompt = `Insurrection \u2014 place Afghan army in Kabul.`; - gen_action('space', Kabul); + view.prompt = `Insurrection \u2014 place Afghan army in Kabul.` + gen_action('space', Kabul) } } else { - view.prompt = `Insurrection \u2014 done.`; - gen_action('next'); + view.prompt = `Insurrection \u2014 done.` + gen_action('next') } }, piece(x) { - push_undo(); - game.selected = x; + push_undo() + game.selected = x }, space() { - push_undo(); - logi(`Afghan army to Kabul.`); - game.pieces[game.selected] = Kabul; - game.selected = -1; + push_undo() + logi(`Afghan army to Kabul.`) + game.pieces[game.selected] = Kabul + game.selected = -1 if (--game.count > 0) - game.selected = select_available_block(); + game.selected = select_available_block() }, next() { - push_undo(); - end_dominance_check(); + push_undo() + end_dominance_check() } } // CLEANUP function player_court_size() { - let stars = 3; + let stars = 3 for (let i = 0; i < player.court.length; ++i) { - let c = player.court[i]; + let c = player.court[i] if (cards[c].suit === Political) - stars += cards[c].rank; + stars += cards[c].rank } - return stars; + return stars } function player_hand_size() { - let stars = 2; + let stars = 2 for (let i = 0; i < player.court.length; ++i) { - let c = player.court[i]; + let c = player.court[i] if (cards[c].suit === Intelligence) - stars += cards[c].rank; + stars += cards[c].rank } - return stars; + return stars } function goto_cleanup_court() { - game.actions = -1; - logbr(); + game.actions = -1 + logbr() if (player.court.length > player_court_size()) { - game.state = 'cleanup_court'; + game.state = 'cleanup_court' } else { - goto_cleanup_hand(); + goto_cleanup_hand() } } states.cleanup_court = { inactive: "cleanup court", prompt() { - let size = player_court_size(); + let size = player_court_size() if (player.court.length <= size) { - view.prompt = `Discard cards in your court until you are within your limit \u2014 done.`; - gen_action('next'); + view.prompt = `Discard cards in your court until you are within your limit \u2014 done.` + gen_action('next') } else { - view.prompt = `Discard cards in your court until you are within your limit (${size}).`; + view.prompt = `Discard cards in your court until you are within your limit (${size}).` for (let i = 0; i < player.court.length; ++i) - gen_action('card', player.court[i]); + gen_action('card', player.court[i]) } }, card(c) { - push_undo(); - discard_court_card(c); + push_undo() + discard_court_card(c) }, next() { - push_undo(); - check_leverage(); + push_undo() + check_leverage() } } function goto_cleanup_hand() { if (player.hand.length > player_hand_size()) { - game.state = 'cleanup_hand'; + game.state = 'cleanup_hand' } else { - goto_discard_events(); + goto_discard_events() } } states.cleanup_hand = { inactive: "cleanup hand", prompt() { - let size = player_hand_size(); + let size = player_hand_size() if (player.hand.length <= size) { - view.prompt = `Discard cards in your hand until you are within your limit \u2014 done.`; - gen_action('next'); + view.prompt = `Discard cards in your hand until you are within your limit \u2014 done.` + gen_action('next') } else { - view.prompt = `Discard cards in your hand until you are within your limit (${size}).`; + view.prompt = `Discard cards in your hand until you are within your limit (${size}).` for (let i = 0; i < player.hand.length; ++i) - gen_action('card', player.hand[i]); + gen_action('card', player.hand[i]) } }, card(c) { - push_undo(); - log(`Discarded #${c} from hand.`); - remove_from_array(player.hand, c); + push_undo() + log(`Discarded #${c} from hand.`) + remove_from_array(player.hand, c) }, next() { - push_undo(); - goto_discard_events(); + push_undo() + goto_discard_events() } } function goto_discard_events() { if (is_event_card(game.market_cards[0][0]) || is_event_card(game.market_cards[1][0])) { - game.state = 'discard_events'; + game.state = 'discard_events' } else { - goto_refill_market(); + goto_refill_market() } } function do_discard_event(row, c) { - game.market_cards[row][0] = 0; - logbr(); - log(`Discarded #${c}.`); + game.market_cards[row][0] = 0 + logbr() + log(`Discarded #${c}.`) if (is_dominance_check(c)) { - do_dominance_check('discard'); + do_dominance_check('discard') } else { - events_if_discarded[cards[c].if_discarded](row); + events_if_discarded[cards[c].if_discarded](row) } } states.discard_events = { inactive: "discard event cards", prompt() { - view.prompt = `Discard any event cards in the leftmost column of the market.`; + view.prompt = `Discard any event cards in the leftmost column of the market.` if (is_event_card(game.market_cards[0][0])) - gen_action('card', game.market_cards[0][0]); + gen_action('card', game.market_cards[0][0]) else if (is_event_card(game.market_cards[1][0])) - gen_action('card', game.market_cards[1][0]); + gen_action('card', game.market_cards[1][0]) }, card(c) { - push_undo(); + push_undo() if (c === game.market_cards[0][0]) - do_discard_event(0, c); + do_discard_event(0, c) else - do_discard_event(1, c); + do_discard_event(1, c) }, } function discard_instability_cards() { for (let row = 0; row < 2; ++row) { for (let col = 0; col < 6; ++col) { - let c = game.market_cards[row][col]; + let c = game.market_cards[row][col] if (is_dominance_check(c)) - game.market_cards[row][col] = 0; + game.market_cards[row][col] = 0 } } } @@ -2408,17 +2408,17 @@ function discard_instability_cards() { function goto_refill_market() { // Move all cards (and their rupees) to the left. for (let row = 0; row < 2; ++row) { - let row_cards = game.market_cards[row]; - let row_coins = game.market_coins[row]; + let row_cards = game.market_cards[row] + let row_coins = game.market_coins[row] for (let to = 0; to < 6; ++to) { if (row_cards[to] === 0) { for (let from = to + 1; from < 6; ++from) { if (row_cards[from] > 0) { - row_cards[to] = row_cards[from]; - row_cards[from] = 0; - row_coins[to] += row_coins[from]; - row_coins[from] = 0; - break; + row_cards[to] = row_cards[from] + row_cards[from] = 0 + row_coins[to] += row_coins[from] + row_coins[from] = 0 + break } } } @@ -2426,52 +2426,52 @@ function goto_refill_market() { } // Instability ... - let instability = 0; + let instability = 0 for (let row = 0; row < 2; ++row) { for (let col = 0; col < 6; ++col) { - let c = game.market_cards[row][col]; + let c = game.market_cards[row][col] if (is_dominance_check(c)) - ++instability; + ++instability } } - clear_undo(); + clear_undo() // Fill with new cards from left (top row in each column first) for (let col = 0; col < 6; ++col) { for (let row = 0; row < 2; ++row) { if (game.deck.length > 0) { if (game.market_cards[row][col] === 0) { - let c = game.deck.pop(); - game.market_cards[row][col] = c; + let c = game.deck.pop() + game.market_cards[row][col] = c if (instability > 0 && is_dominance_check(c)) { - game.state = 'instability'; - return; + game.state = 'instability' + return } } } } } - goto_next_player(); + goto_next_player() } states.instability = { prompt() { - view.prompt = "Instability \u2014 perform a Dominance Check immediately."; + view.prompt = "Instability \u2014 perform a Dominance Check immediately." for (let row = 0; row < 2; ++row) { for (let col = 0; col < 6; ++col) { - let c = game.market_cards[row][col]; + let c = game.market_cards[row][col] if (is_dominance_check(c)) - gen_action('card', c); + gen_action('card', c) } } }, - card(c) { - logbr(); - log(`Instability!`); - discard_instability_cards(); - do_dominance_check('instability'); + card() { + logbr() + log(`Instability!`) + discard_instability_cards() + do_dominance_check('instability') } } @@ -2480,121 +2480,121 @@ states.instability = { const events_if_discarded = { "Military" () { - goto_favored_suit_event(Military); + goto_favored_suit_event(Military) }, "Embarrassment of Riches" () { - game.events.embarrassment_of_riches = 1; - goto_discard_events(); + game.events.embarrassment_of_riches = 1 + goto_discard_events() }, "Disregard for Customs" () { - game.events.disregard_for_customs = 1; - goto_discard_events(); + game.events.disregard_for_customs = 1 + goto_discard_events() }, "Failure to Impress" () { - logi("Discarded all loyalty prizes."); + logi("Discarded all loyalty prizes.") for (let p = 0; p < game.players.length; ++p) - game.players[p].prizes = 0; - goto_discard_events(); + game.players[p].prizes = 0 + goto_discard_events() }, "Riots in Punjab" () { - goto_riots(Punjab); + goto_riots(Punjab) }, "Riots in Herat" () { - goto_riots(Herat); + goto_riots(Herat) }, "No effect" () { - logi("No effect."); - goto_discard_events(); + logi("No effect.") + goto_discard_events() }, "Riots in Kabul" () { - goto_riots(Kabul); + goto_riots(Kabul) }, "Riots in Persia" () { - goto_riots(Persia); + goto_riots(Persia) }, "Confidence Failure" () { - game.state = 'confidence_failure'; + game.state = 'confidence_failure' if (player.hand.length === 0) - next_confidence_failure(); + next_confidence_failure() }, "Intelligence" () { - goto_favored_suit_event(Intelligence); + goto_favored_suit_event(Intelligence) }, "Political" () { - goto_favored_suit_event(Political); + goto_favored_suit_event(Political) }, } function goto_favored_suit_event(suit) { - game.state = 'favored_suit'; - game.where = suit; + game.state = 'favored_suit' + game.where = suit } states.favored_suit = { inactive: "favored suit", prompt() { - view.prompt = `Change the favored suit to ${game.where}.`; - gen_action('suit', game.where); + view.prompt = `Change the favored suit to ${game.where}.` + gen_action('suit', game.where) }, suit(suit) { - logi(`Favored suit to ${suit}.`); - game.favored = suit; - goto_discard_events(); + logi(`Favored suit to ${suit}.`) + game.favored = suit + goto_discard_events() }, } function goto_riots(where) { - game.state = 'riots'; - game.where = where; + game.state = 'riots' + game.where = where } states.riots = { prompt() { - view.prompt = `Riot in ${region_names[game.where]}.`; - gen_action('space', game.where); + view.prompt = `Riot in ${region_names[game.where]}.` + gen_action('space', game.where) }, space(s) { - remove_all_tribes_and_armies(s); - goto_discard_events(); + remove_all_tribes_and_armies(s) + goto_discard_events() }, } states.confidence_failure = { inactive: "confidence failure", prompt() { - view.prompt = "Confidence Failure \u2014 discard a card from your hand."; + view.prompt = "Confidence Failure \u2014 discard a card from your hand." for (let i = 0; i < player.hand.length; ++i) - gen_action('card', player.hand[i]); + gen_action('card', player.hand[i]) }, card(c) { - log(`${player_names[game.active]} discarded #${c} from hand.`); - remove_from_array(player.hand, c); - next_confidence_failure(); + log(`${player_names[game.active]} discarded #${c} from hand.`) + remove_from_array(player.hand, c) + next_confidence_failure() }, } function next_confidence_failure() { - let next = game.active; + let next = game.active for (let i = 0; i < 10; ++i) { - next = next_player(next); + next = next_player(next) if (next === game.phasing) - return goto_discard_events(); + return goto_discard_events() if (game.players[next].hand.length > 0) - return set_active(next); + return set_active(next) } - throw new Error("FAIL"); + throw new Error("FAIL") } // EVENTS: IF PURCHASED @@ -2602,58 +2602,58 @@ function next_confidence_failure() { const events_if_purchased = { "New Tactics" () { - player.events.new_tactics = 1; - end_action(); + player.events.new_tactics = 1 + end_action() }, "Koh-i-noor Recovered" () { - player.events.koh_i_noor = 1; - end_action(); + player.events.koh_i_noor = 1 + end_action() }, "Courtly Manners" () { - player.events.courtly_manners = 1; - end_action(); + player.events.courtly_manners = 1 + end_action() }, "Rumor" () { - game.state = 'rumor'; + game.state = 'rumor' }, "Conflict Fatigue" () { - game.events.conflict_fatigue = 1; - end_action(); + game.events.conflict_fatigue = 1 + end_action() }, "Nationalism" () { - player.events.nationalism = 1; - end_action(); + player.events.nationalism = 1 + end_action() }, "Public Withdrawal" () { - throw new Error("cannot purchase"); + throw new Error("cannot purchase") }, "Nation Building" () { - player.events.nation_building = 1; - end_action(); + player.events.nation_building = 1 + end_action() }, "Backing of Persian Aristocracy" () { - player.coins += 3; - end_action(); + player.coins += 3 + end_action() }, "Other Persuasive Methods" () { - game.state = 'other_persuasive_methods'; + game.state = 'other_persuasive_methods' }, "Pashtunwali Values" () { - game.state = 'pashtunwali_values'; + game.state = 'pashtunwali_values' }, "Rebuke" () { - game.state = 'rebuke'; + game.state = 'rebuke' }, } @@ -2667,375 +2667,375 @@ function check_public_withdrawal() { for (let row = 0; row < 2; ++row) for (let col = 0; col < 6; ++col) if (game.market_cards[row][col] === PUBLIC_WITHDRAWAL) - game.market_coins[row][col] = 0; + game.market_coins[row][col] = 0 } states.rumor = { prompt() { - view.prompt = `Rumor \u2014 choose a player.`; + view.prompt = `Rumor \u2014 choose a player.` for (let p = 0; p < game.players.length; ++p) if (p !== game.active) - gen_action('player_' + p); + gen_action('player_' + p) }, - player_0() { do_rumor(0); }, - player_1() { do_rumor(1); }, - player_2() { do_rumor(2); }, - player_3() { do_rumor(3); }, - player_4() { do_rumor(4); }, + player_0() { do_rumor(0) }, + player_1() { do_rumor(1) }, + player_2() { do_rumor(2) }, + player_3() { do_rumor(3) }, + player_4() { do_rumor(4) }, } function do_rumor(p) { - push_undo(); - log(`${player_names[game.active]} chose ${player_names[p]} for Rumor.`); - game.players[p].events.rumor = 1; - end_action(); + push_undo() + log(`${player_names[game.active]} chose ${player_names[p]} for Rumor.`) + game.players[p].events.rumor = 1 + end_action() } states.other_persuasive_methods = { inactive: "other persuasive methods", prompt() { - view.prompt = `Other Persuasive Methods \u2014 exchange your hand with another player.`; + view.prompt = `Other Persuasive Methods \u2014 exchange your hand with another player.` for (let p = 0; p < game.players.length; ++p) if (p !== game.active) - gen_action('player_' + p); + gen_action('player_' + p) }, - player_0() { do_other_persuasive_methods(0); }, - player_1() { do_other_persuasive_methods(1); }, - player_2() { do_other_persuasive_methods(2); }, - player_3() { do_other_persuasive_methods(3); }, - player_4() { do_other_persuasive_methods(4); }, + player_0() { do_other_persuasive_methods(0) }, + player_1() { do_other_persuasive_methods(1) }, + player_2() { do_other_persuasive_methods(2) }, + player_3() { do_other_persuasive_methods(3) }, + player_4() { do_other_persuasive_methods(4) }, } function do_other_persuasive_methods(p) { if (game.open) - push_undo(); + push_undo() else - clear_undo(); // No undo if closed hands. - log(`${player_names[game.active]} exchanged hands with ${player_names[p]}.`); - let swap = game.players[game.active].hand; - game.players[game.active].hand = game.players[p].hand; - game.players[p].hand = swap; - end_action(); + clear_undo() // No undo if closed hands. + log(`${player_names[game.active]} exchanged hands with ${player_names[p]}.`) + let swap = game.players[game.active].hand + game.players[game.active].hand = game.players[p].hand + game.players[p].hand = swap + end_action() } states.pashtunwali_values = { inactive: "Pastunwali values", prompt() { - view.prompt = `Pashtunwali Values \u2014 choose a suit to favor.`; - gen_action('suit', Political); - gen_action('suit', Intelligence); - gen_action('suit', Economic); - gen_action('suit', Military); + view.prompt = `Pashtunwali Values \u2014 choose a suit to favor.` + gen_action('suit', Political) + gen_action('suit', Intelligence) + gen_action('suit', Economic) + gen_action('suit', Military) }, suit(suit) { - log(`Favored suit to ${suit}.`); - game.favored = suit; - game.events.pashtunwali_values = 1; - end_action(); + log(`Favored suit to ${suit}.`) + game.favored = suit + game.events.pashtunwali_values = 1 + end_action() }, } states.rebuke = { prompt() { - view.prompt = `Rebuke \u2014 remove all tribes and armies in a single region.`; + view.prompt = `Rebuke \u2014 remove all tribes and armies in a single region.` for (let s = first_region; s <= last_region; ++s) // TODO: can pick empty region? - gen_action('space', s); + gen_action('space', s) }, space(s) { - push_undo(); - remove_all_tribes_and_armies(s); - end_action(); + push_undo() + remove_all_tribes_and_armies(s) + end_action() }, } // DOMINANCE CHECK function count_cylinders_in_play(p) { - let n = 0; - let x = player_cylinders(p); + let n = 0 + let x = player_cylinders(p) for (let i = x; i < x + 10; ++i) if (game.pieces[i] > 0) - ++n; - return n; + ++n + return n } function count_influence_points(p) { - let n = 1 + game.players[p].prizes; - let x = player_cylinders(p); + let n = 1 + game.players[p].prizes + let x = player_cylinders(p) if (!game.events.embarrassment_of_riches) { - let gv = game.players[p].events.koh_i_noor ? 2 : 1; + let gv = game.players[p].events.koh_i_noor ? 2 : 1 for (let i = x; i < x + 10; ++i) { - let s = game.pieces[i]; + let s = game.pieces[i] if (s === Gift2 || s === Gift4 || s === Gift6) - n += gv; + n += gv } } if (!game.players[p].events.rumor) { - let court = game.players[p].court; + let court = game.players[p].court for (let i = 0; i < court.length; ++i) if (cards[court[i]].patriot) - ++n; + ++n } - return n; + return n } function assign_vp(points, score, sorted) { - const PLACE = [ "1st", "2nd", "3rd" ]; - let place = 0; - sorted.sort((a,b) => b-a); + const PLACE = [ "1st", "2nd", "3rd" ] + let place = 0 + sorted.sort((a,b) => b-a) while (points.length > 0 && sorted.length > 0) { - let n = 0; + let n = 0 for (let p = 0; p < game.players.length; ++p) if (score[p] === sorted[0]) - ++n; - let v = 0; + ++n + let v = 0 for (let i = 0; i < n; ++i) - v += points[i] | 0; - v = Math.floor(v / n); - let msg = `${PLACE[place]} place:`; + v += points[i] | 0 + v = Math.floor(v / n) + let msg = `${PLACE[place]} place:` for (let p = 0; p < game.players.length; ++p) { if (score[p] === sorted[0]) { - msg += "\n" + player_names[p] + " scored " + v + " vp."; - game.players[p].vp += v; + msg += "\n" + player_names[p] + " scored " + v + " vp." + game.players[p].vp += v } } - log(msg); - points = points.slice(n); - sorted = sorted.slice(n); - place += n; + log(msg) + points = points.slice(n) + sorted = sorted.slice(n) + place += n } } function count_dominance_cards() { - let n = 0; + let n = 0 for (let row = 0; row < 2; ++row) for (let col = 0; col < 6; ++col) if (is_dominance_check(game.market_cards[row][col])) - ++n; + ++n for (let i = 0; i < game.deck.length; ++i) if (is_dominance_check(game.deck[i])) - ++n; - return n; + ++n + return n } function is_final_dominance_check() { for (let row = 0; row < 2; ++row) for (let col = 0; col < 6; ++col) if (is_dominance_check(game.market_cards[row][col])) - return false; + return false for (let i = 0; i < game.deck.length; ++i) if (is_dominance_check(game.deck[i])) - return false; - return true; + return false + return true } function rank_score(a, b) { - return parseInt(b) - parseInt(a); + return parseInt(b) - parseInt(a) } function do_dominance_check(reason) { - let n_afghan = 0; - let n_british = 0; - let n_russian = 0; - let success = null; + let n_afghan = 0 + let n_british = 0 + let n_russian = 0 + let success = null for (let i = 0; i < 12; ++i) { if (game.pieces[i] > 0) - n_afghan ++; + n_afghan ++ if (game.pieces[i+12] > 0) - n_british ++; + n_british ++ if (game.pieces[i+24] > 0) - n_russian ++; + n_russian ++ } logi(n_afghan + " Afghan block" + (n_afghan !== 1 ? "s," : ",")) logi(n_british + " British block" + (n_british !== 1 ? "s," : ",")) logi(n_russian + " Russian block" + (n_russian !== 1 ? "s." : ".")) - let limit = game.events.conflict_fatigue ? 2 : 4; + let limit = game.events.conflict_fatigue ? 2 : 4 if (n_afghan >= n_british+limit && n_afghan >= n_russian+limit) - success = Afghan; + success = Afghan else if (n_british >= n_afghan+limit && n_british >= n_russian+limit) - success = British; + success = British else if (n_russian >= n_british+limit && n_russian >= n_afghan+limit) - success = Russian; + success = Russian - let final = is_final_dominance_check(); + let final = is_final_dominance_check() - let score = new Array(game.players.length).fill(0); + let score = new Array(game.players.length).fill(0) if (success) { - logbr(); - log(`.dc.${success} Dominant ${success} Coalition`); - logbr(); + logbr() + log(`.dc.${success} Dominant ${success} Coalition`) + logbr() if (final) - log(`Final Dominance Check.`); - let list = []; + log(`Final Dominance Check.`) + let list = [] for (let p = 0; p < game.players.length; ++p) { if (game.players[p].loyalty === success) { - score[p] = count_influence_points(p); - list.push(`${score[p]} ${player_names[p]} influence`); + score[p] = count_influence_points(p) + list.push(`${score[p]} ${player_names[p]} influence`) } } - list.sort(rank_score); - log("Ranking:\n" + list.join(",\n") + "."); + list.sort(rank_score) + log("Ranking:\n" + list.join(",\n") + ".") if (final) - assign_vp([10, 6, 2], score, score.filter(x=>x>0)); + assign_vp([10, 6, 2], score, score.filter(x=>x>0)) else - assign_vp([5, 3, 1], score, score.filter(x=>x>0)); + assign_vp([5, 3, 1], score, score.filter(x=>x>0)) } else { - logbr(); - log(`.dc.unsuccessful Unsuccessful Check`); - logbr(); + logbr() + log(`.dc.unsuccessful Unsuccessful Check`) + logbr() if (final) - log(`Final Dominance Check.`); - let list = []; + log(`Final Dominance Check.`) + let list = [] for (let p = 0; p < game.players.length; ++p) { - score[p] = count_cylinders_in_play(p); + score[p] = count_cylinders_in_play(p) if (score[p] !== 1) - list.push(`${score[p]} ${player_names[p]} cylinders`); + list.push(`${score[p]} ${player_names[p]} cylinders`) else - list.push(`${score[p]} ${player_names[p]} cylinder`); + list.push(`${score[p]} ${player_names[p]} cylinder`) } - list.sort(rank_score); - log("Ranking:\n" + list.join(",\n") + "."); + list.sort(rank_score) + log("Ranking:\n" + list.join(",\n") + ".") if (final) - assign_vp([6, 2], score, score.slice()); + assign_vp([6, 2], score, score.slice()) else - assign_vp([3, 1], score, score.slice()); + assign_vp([3, 1], score, score.slice()) } // Clear the board. if (success) { for (let i = 0; i < 36; ++i) - game.pieces[i] = 0; + game.pieces[i] = 0 } // Check instant victory - let vps = game.players.map(pp => pp.vp).sort((a,b)=>b-a); + let vps = game.players.map(pp => pp.vp).sort((a,b)=>b-a) if (vps[0] >= vps[1] + 4) - return goto_pause_game_over(); + return goto_pause_game_over() if (final) - return goto_pause_game_over(); + return goto_pause_game_over() - game.events = {}; + game.events = {} for (let p = 0; p < game.players.length; ++p) - game.players[p].events = {}; + game.players[p].events = {} - game.where = reason; + game.where = reason - check_insurrection(); + check_insurrection() } function end_dominance_check() { - set_active(game.phasing); + set_active(game.phasing) if (game.state === 'game_over') - return; + return switch (game.where) { case 'discard': - return goto_discard_events(); + return goto_discard_events() case 'purchase': - return resume_actions(); + return resume_actions() case 'instability': - return goto_refill_market(); + return goto_refill_market() } } function vp_tie(pp) { - let court = pp.court; - let stars = 0; + let court = pp.court + let stars = 0 for (let i = 0; i < court.length; ++i) { - let c = court[i]; + let c = court[i] if (cards[c].suit === Military) - stars += cards[c].rank; + stars += cards[c].rank } - return pp.vp * 10000 + stars * 100 + pp.coins; + return pp.vp * 10000 + stars * 100 + pp.coins } function compute_victory() { - let vps = game.players.map((pp,i) => [vp_tie(pp),i]).sort((a,b)=>b[0]-a[0]); - let result = []; + let vps = game.players.map((pp,i) => [vp_tie(pp),i]).sort((a,b)=>b[0]-a[0]) + let result = [] for (let i = 0; i < vps.length; ++i) if (vps[i][0] === vps[0][0]) result.push(player_names[vps[i][1]]) - game.result = result.join(", "); - game.victory = result.join(" and ") + " won!"; - logbr(); - log(game.victory); + game.result = result.join(", ") + game.victory = result.join(" and ") + " won!" + logbr() + log(game.victory) } function goto_pause_game_over() { - compute_victory(); + compute_victory() if (game.undo && game.undo.length > 0) - game.state = 'pause_game_over'; + game.state = 'pause_game_over' else - game.state = 'game_over'; + game.state = 'game_over' } function goto_game_over() { - compute_victory(); - game.state = 'game_over'; - game.active = 5; + compute_victory() + game.state = 'game_over' + game.active = 5 } states.pause_game_over = { inactive: "game over", prompt() { - view.prompt = game.victory; - gen_action('end_game'); + view.prompt = game.victory + gen_action('end_game') }, end_game() { - game.state = 'game_over'; - game.active = 5; + game.state = 'game_over' + game.active = 5 } } // SETUP function prepare_deck() { - let court_cards = []; + let court_cards = [] for (let i = 1; i <= 100; ++i) - court_cards.push(i); + court_cards.push(i) - let event_cards = []; + let event_cards = [] for (let i = 105; i <= 116; ++i) - event_cards.push(i); + event_cards.push(i) - let piles = [ [], [], [], [], [], [] ]; + let piles = [ [], [], [], [], [], [] ] - shuffle(court_cards); + shuffle(court_cards) for (let i = 0; i < 6; ++i) for (let k = 0; k < 5 + game.players.length; ++k) - piles[i].push(court_cards.pop()); + piles[i].push(court_cards.pop()) // Leftmost pile is 5, rightmost pile is 0 - piles[3].push(101); - piles[2].push(102); - piles[1].push(103); - piles[0].push(104); + piles[3].push(101) + piles[2].push(102) + piles[1].push(103) + piles[0].push(104) - shuffle(event_cards); + shuffle(event_cards) - piles[4].push(event_cards.pop()); - piles[4].push(event_cards.pop()); - piles[3].push(event_cards.pop()); - piles[2].push(event_cards.pop()); - piles[1].push(event_cards.pop()); - piles[0].push(event_cards.pop()); + piles[4].push(event_cards.pop()) + piles[4].push(event_cards.pop()) + piles[3].push(event_cards.pop()) + piles[2].push(event_cards.pop()) + piles[1].push(event_cards.pop()) + piles[0].push(event_cards.pop()) for (let i = 0; i < 6; ++i) - shuffle(piles[i]); + shuffle(piles[i]) - game.deck = piles.flat(); + game.deck = piles.flat() } exports.setup = function (seed, scenario, options) { - let player_count = scenario_player_count[scenario]; + let player_count = scenario_player_count[scenario] game = { seed: seed, @@ -3071,7 +3071,7 @@ exports.setup = function (seed, scenario, options) { log: [], undo: [], - }; + } for (let i = 0; i < player_count; ++i) { game.players[i] = { @@ -3085,58 +3085,58 @@ exports.setup = function (seed, scenario, options) { } } - prepare_deck(); + prepare_deck() for (let col = 0; col < 6; ++col) for (let row = 0; row < 2; ++row) - game.market_cards[row][col] = game.deck.pop(); + game.market_cards[row][col] = game.deck.pop() // Starting loyalty, starting with a random player. - game.state = 'loyalty'; - game.active = random(player_count); + game.state = 'loyalty' + game.active = random(player_count) - return save_game(); + return save_game() } function load_game(state) { - game = state; - game.active = player_index[game.active]; - update_aliases(); + game = state + game.active = player_index[game.active] + update_aliases() } function save_game() { - game.active = player_names[game.active]; - return game; + game.active = player_names[game.active] + return game } exports.action = function (state, current, action, arg) { - load_game(state); - let S = states[game.state]; + load_game(state) + let S = states[game.state] if (action in S) { - S[action](arg, current); + S[action](arg, current) } else { if (action === 'undo' && game.undo && game.undo.length > 0) - pop_undo(); + pop_undo() else - throw new Error("Invalid action: " + action); + throw new Error("Invalid action: " + action) } - return save_game(); + return save_game() } exports.resign = function (state, current) { - load_game(state); - logbr(); - log(`${current} resigned.`); - goto_game_over(); - game.victory = `${current} resigned.`; - return save_game(); + load_game(state) + logbr() + log(`${current} resigned.`) + goto_game_over() + game.victory = `${current} resigned.` + return save_game() } -exports.is_checkpoint = (a, b) => a.phasing !== b.phasing; +exports.is_checkpoint = (a, b) => a.phasing !== b.phasing -exports.view = function(state, current) { - current = player_index[current]; - load_game(state); +exports.view = function (state, current) { + current = player_index[current] + load_game(state) view = { log: game.log, @@ -3151,23 +3151,23 @@ exports.view = function(state, current) { selected: game.selected, cards: [ game.deck.length, count_dominance_cards() ], open: game.open, - }; + } if (game.state === 'game_over') { - view.prompt = game.victory; + view.prompt = game.victory } else if (current === 'Observer' || game.active !== current) { - let inactive = states[game.state].inactive || game.state; - view.prompt = `Waiting for ${player_names[game.active]} \u2014 ${inactive}...`; + let inactive = states[game.state].inactive || game.state + view.prompt = `Waiting for ${player_names[game.active]} \u2014 ${inactive}...` } else { view.actions = {} - states[game.state].prompt(); - view.prompt = player_names[game.active] + ": " + view.prompt; + states[game.state].prompt() + view.prompt = player_names[game.active] + ": " + view.prompt if (game.undo && game.undo.length > 0) - view.actions.undo = 1; + view.actions.undo = 1 else - view.actions.undo = 0; + view.actions.undo = 0 } - save_game(); - return view; + save_game() + return view } |