diff options
Diffstat (limited to 'rules.js')
-rw-r--r-- | rules.js | 372 |
1 files changed, 196 insertions, 176 deletions
@@ -1,29 +1,29 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.roles = exports.scenarios = void 0; +exports.roles = exports.scenarios = exports.MODERATE = exports.COMMUNIST = exports.ANARCHIST = void 0; exports.action = action; exports.view = game_view; exports.setup = setup; const data_1 = require("./data"); const states = {}; -let game = {}; // = null -var view = {}; // = null -const ANARCHIST = 'Anarchist'; -const COMMUNIST = 'Communist'; -const MODERATE = 'Moderate'; +let game = {}; +var view = {}; +exports.ANARCHIST = 'Anarchist'; +exports.COMMUNIST = 'Communist'; +exports.MODERATE = 'Moderate'; const ANARCHISTS_ID = 'a'; const COMMUNISTS_ID = 'c'; const MODERATES_ID = 'm'; const role_ids = [ANARCHISTS_ID, COMMUNISTS_ID, MODERATES_ID]; const faction_player_map = { - [ANARCHISTS_ID]: ANARCHIST, - [COMMUNISTS_ID]: COMMUNIST, - [MODERATES_ID]: MODERATE, + [ANARCHISTS_ID]: exports.ANARCHIST, + [COMMUNISTS_ID]: exports.COMMUNIST, + [MODERATES_ID]: exports.MODERATE, }; const player_faction_map = { - [ANARCHIST]: ANARCHISTS_ID, - [COMMUNIST]: COMMUNISTS_ID, - [MODERATE]: MODERATES_ID, + [exports.ANARCHIST]: ANARCHISTS_ID, + [exports.COMMUNIST]: COMMUNISTS_ID, + [exports.MODERATE]: MODERATES_ID, }; const front_names = { a: 'the Aragon Front', @@ -40,19 +40,19 @@ const track_names = { [data_1.SOVIET_SUPPORT]: 'Soviet Support', [data_1.FOREIGN_AID]: 'Foreign Aid', }; -const { cards, -// fronts - } = data_1.default; +const { cards, } = data_1.default; const faction_cards = { [ANARCHISTS_ID]: make_list(37, 54), [COMMUNISTS_ID]: make_list(19, 36), [MODERATES_ID]: make_list(1, 18), }; +const medaillons = make_list(0, 8); +console.log('medaillons', medaillons); const fascist_decks = { 1: make_list(55, 62), }; exports.scenarios = ['Standard']; -exports.roles = [ANARCHIST, COMMUNIST, MODERATE]; +exports.roles = [exports.ANARCHIST, exports.COMMUNIST, exports.MODERATE]; function gen_action(action, argument) { if (argument === undefined) { view.actions[action] = 1; @@ -66,15 +66,6 @@ function gen_action(action, argument) { function gen_action_card(card_id) { gen_action('card', card_id); } -function gen_action_space(space) { - gen_action('space', space); -} -// function gen_action_piece(piece) { -// gen_action('piece', piece); -// } -// function gen_action_card(card) { -// gen_action('card', card); -// } function action(state, player, action, arg) { console.log('action', state, player, action, arg); game = state; @@ -87,7 +78,6 @@ function action(state, player, action, arg) { throw new Error('Invalid action: ' + action); return game; } -// #region ENGINE const leaf_node = 'l'; const seq_node = 's'; const function_node = 'f'; @@ -109,9 +99,10 @@ function setup_bag_of_glory() { } function setup_choose_card() { console.log('setup_choose_card'); - game.engine = exports.roles.map((role) => ({ + const player_order = get_player_order(); + game.engine = player_order.map((faction_id) => ({ t: leaf_node, - p: player_faction_map[role], + p: faction_id, s: 'choose_card', })); game.engine.push({ @@ -122,11 +113,8 @@ function setup_choose_card() { } function setup_player_turn() { console.log('setup_player_turn'); - // TODO: reverse order in second round - const first = game.initiative; - const second = get_next_faction(first); - const third = get_next_faction(second); - game.engine = [first, second, third].map((faction_id) => ({ + const player_order = get_player_order(); + game.engine = player_order.map((faction_id) => ({ t: seq_node, c: [ { @@ -154,16 +142,13 @@ const engine_functions = { setup_player_turn, resolve_fascist_test, }; -function get_active_node(engine) { +function get_active(engine) { for (let i of engine) { - if (i.t === leaf_node && i.r !== resolved) { - return i; - } - if (i.t === function_node && i.r !== resolved) { - return i; + if ((i.t === leaf_node || i.t === function_node) && i.r !== resolved) { + return { parent: engine, node: i }; } if (i.t === seq_node) { - const next_child = get_active_node(i.c); + const next_child = get_active(i.c); if (next_child !== null) { return next_child; } @@ -171,6 +156,10 @@ function get_active_node(engine) { } return null; } +function get_active_node(engine) { + const a = get_active(engine); + return a === null ? null : a.node; +} function get_active_node_args() { const node = get_active_node(game.engine); if (node.t === leaf_node) { @@ -178,6 +167,23 @@ function get_active_node_args() { } return null; } +function insert_before_or_after_active_node(node, position, engine = game.engine) { + const a = get_active(engine); + if (a === null) { + return; + } + const i = a.parent.indexOf(a.node); + console.log('insert_before_active_node', i); + if (i >= 0) { + array_insert(a.parent, i + (position == 'after' ? 1 : 0), node); + } +} +function insert_after_active_node(node, engine = game.engine) { + insert_before_or_after_active_node(node, 'after', engine); +} +function insert_before_active_node(node, engine = game.engine) { + insert_before_or_after_active_node(node, 'before', engine); +} function next() { console.log('next'); const node = get_active_node(game.engine); @@ -217,9 +223,12 @@ function game_view(state, player) { prompt: null, location: game.location, selected: game.selected, + bonuses: game.bonuses, current_events: game.current_events, fronts: game.fronts, hand: game.hands[faction_id], + medaillons: game.medaillons, + selected_card: game.cards_in_play[faction_id], tracks: game.tracks, }; if (player !== game.active) { @@ -236,19 +245,17 @@ function game_view(state, player) { } return view; } -// #endregion -// #region SETUP function setup(seed, _scenario, _options) { - // game.seed = seed; game = { seed: seed, state: null, - active: ANARCHIST, + active: exports.ANARCHIST, bag_of_glory: { [ANARCHISTS_ID]: 1, [COMMUNISTS_ID]: 1, [MODERATES_ID]: 1, }, + blank_markers: [[], [], [], [], []], bonuses: [data_1.ON, data_1.ON], current_events: [], engine: [], @@ -267,6 +274,7 @@ function setup(seed, _scenario, _options) { [ANARCHISTS_ID]: 2, [COMMUNISTS_ID]: 2, [MODERATES_ID]: 0, + pool: 14, }, cards_in_play: { [ANARCHISTS_ID]: null, @@ -274,6 +282,13 @@ function setup(seed, _scenario, _options) { [MODERATES_ID]: null, }, initiative: MODERATES_ID, + medaillons: [ + draw_item(medaillons), + draw_item(medaillons), + draw_item(medaillons), + draw_item(medaillons), + draw_item(medaillons), + ], tableaus: { [ANARCHISTS_ID]: [], [COMMUNISTS_ID]: [], @@ -298,7 +313,6 @@ function draw_hand_cards() { } }); } -// #endregion function start_year() { log_h1('Year ' + game.year); draw_hand_cards(); @@ -322,13 +336,7 @@ function start_turn() { f: 'setup_choose_card', }); next(); - // game.state = 'resolve_event'; - // game.active = faction_player_map[game.initiative]; - // game.state_data = { - // current_effect: 0, - // }; } -// region STATES states.add_glory = { inactive: 'add tokens to the Bag of Glory', prompt() { @@ -369,7 +377,13 @@ states.resolve_event = { else if (effect.type === 'track') { gen_action('standee', effect.target); } - // for (let p = 0; p < 5; ++p) gen_action('standee', p); + else if (effect.type === 'hero_points' && + effect.target === data_1.PLAYER_WITH_MOST_HERO_POINTS) { + const factions = get_factions_with_most_hero_poins(); + for (let faction_id of factions) { + gen_action(get_player(faction_id)); + } + } }, front(f) { const card = get_current_event(); @@ -385,6 +399,18 @@ states.resolve_event = { log_h3(`${track_names[effect.target]} decreased by ${Math.abs(value)}`); resolve_active_and_proceed(); }, + Anarchist() { + lose_hero_point(ANARCHISTS_ID, 1); + resolve_active_and_proceed(); + }, + Communist() { + lose_hero_point(ANARCHISTS_ID, 1); + resolve_active_and_proceed(); + }, + Moderate() { + lose_hero_point(ANARCHISTS_ID, 1); + resolve_active_and_proceed(); + }, }; states.choose_card = { inactive: 'choose a card', @@ -406,8 +432,41 @@ states.choose_card = { states.player_turn = { inactive: 'play their turn', prompt() { - view.prompt = 'Play your card or spend Hero points'; - gen_action_card(game.cards_in_play[player_faction_map[game.active]]); + const faction_id = get_faction_id(game.active); + const hero_points = game.hero_points[faction_id]; + view.prompt = + hero_points === 0 + ? 'Play your card' + : 'Play your card or spend Hero points'; + if (game.cards_in_play[faction_id] !== null) { + gen_action('play_for_ap'); + gen_action('play_for_event'); + } + if (hero_points > 0) { + gen_action('spend_hp'); + } + }, + play_for_ap() { + const faction_id = get_faction_id(game.active); + const card = game.cards_in_play[faction_id]; + log_h3(`${game.active} plays ${cards[card].title} for the Action Points`); + resolve_active_and_proceed(); + }, + play_for_event() { + const faction_id = get_faction_id(game.active); + const card = game.cards_in_play[faction_id]; + log_h3(`${game.active} plays ${cards[card].title} for the Event`); + game.cards_in_play[faction_id] = null; + game.tableaus[faction_id].push(card); + resolve_active_and_proceed(); + }, + spend_hp() { + insert_before_active_node({ + t: leaf_node, + p: get_faction_id(game.active), + s: 'spend_hero_points', + }); + next(); }, card(c) { const faction = get_active_faction(); @@ -425,22 +484,24 @@ states.player_turn = { resolve_active_and_proceed(); }, }; -// states.move_to = { -// inactive: 'move', -// prompt() { -// view.prompt = 'Move the piece to a space.'; -// for (let s = 0; s < 64; ++s) gen_action_space(s); -// }, -// // space(to) { -// // game.location[game.selected] = to -// // game.state = "move" -// // if (game.active === PLAYER1) -// // game.active = PLAYER2 -// // else -// // game.active = PLAYER1 -// // }, -// }; -// #endrregion +states.spend_hero_points = { + inactive: 'spend Hero points', + prompt() { + view.prompt = 'Spend your Hero points'; + gen_action('done'); + gen_action('draw_card'); + }, + done() { + resolve_active_and_proceed(); + }, + draw_card() { + game.hero_points[get_active_faction_id()]--; + log(`${game.active} draws a card`); + if (game.hero_points[get_active_faction_id()] === 0) { + resolve_active_and_proceed(); + } + }, +}; function pop_undo() { const save_log = game.log; const save_undo = game.undo; @@ -449,9 +510,7 @@ function pop_undo() { game.log = save_log; game.undo = save_undo; } -// #region GAME FUNCTIONS function end_of_turn() { - // REMOVE playre tplems from the Fronts; log_h2('End of turn'); if (game.turn === 4) { end_of_year(); @@ -467,47 +526,34 @@ function resolve_fascist_test() { log_h2('Fascist test is resolved'); next(); } -// #endregion -// #region CARDS -// function draw_faction_card(faction: Player): CardId { -// return draw_faction_cards(faction, 1)[0]; -// } -// function draw_faction_cards(faction: Player, count: number = 1): CardId[] { -// const drawnCards = []; -// } +function move_track(track, change) { } function draw_card(deck) { - console.log('draw_card_deck', deck); clear_undo(); let i = random(deck.length); - console.log('random ', i); let c = deck[i]; - console.log('draw_card_id', c); set_delete(deck, c); return c; } -// #endregion -// #region EVENTS +function draw_item(ids) { + let i = random(ids.length); + let r = ids[i]; + set_delete(ids, r); + return r; +} +function lose_hero_point(faction, value) { + const points_lost = Math.min(game.hero_points[faction], value); + game.hero_points.pool += points_lost; + game.hero_points[faction] -= points_lost; + if (points_lost === 1) { + log(`${get_player(faction)} loses 1 Hero Point`); + } + else { + log(`${get_player(faction)} loses ${points_lost} Hero Points`); + } +} function get_current_event() { return cards[game.current_events[game.current_events.length - 1]]; } -// function get_front_name(frontId: string) { -// switch (frontId) { -// case 'a': -// return 'the Aragon Front'; -// case 'm': -// return 'the Madrid Front'; -// case 'n': -// return 'the Nothern Front'; -// case 's': -// return 'the Southern Front'; -// case 'd': -// return 'the Front closest to Defeat'; -// case 'v': -// return 'the Front closest to Victory'; -// default: -// return ''; -// } -// } function get_event_prompt(effect) { let prompt = ''; switch (effect.type) { @@ -516,52 +562,17 @@ function get_event_prompt(effect) { case 'bonus': break; case 'hero_points': - break; + return 'Select player with most Hero points'; case 'track': return 'Decrease ' + track_names[effect.target]; } return prompt; } -// function resolve_event_attack(target: string | number, value: number) { -// switch (target) { -// case 'v': -// break; -// case 'd': -// break; -// default: -// game.fronts[target] += value; -// } -// } -// function fascist_event() { -// // log_h1('Year ' + game.year); -// const deck = fascist_decks[game.year]; -// const cardId = draw_card(deck); -// game.current_events.push(cardId); -// const card = cards[cardId] as EventCard; -// card.effects.forEach((effect) => { -// switch (effect.type) { -// case 'attack': -// resolve_event_attack(effect.target, effect.value); -// break; -// case 'bonus': -// break; -// case 'hero_points': -// break; -// case 'track': -// game.tracks[effect.target] += effect.value; -// break; -// } -// }); -// } -// #endregion -// #region FRONTS function get_fronts_closest_to(target) { const values = Object.values(game.fronts); const targetValue = target === 'd' ? Math.min(...values) : Math.max(...values); return Object.keys(game.fronts).filter((frontId) => game.fronts[frontId] === targetValue); } -// #endregion -// #region LOGGING function log_br() { if (game.log.length > 0 && game.log[game.log.length - 1] !== '') game.log.push(''); @@ -569,18 +580,6 @@ function log_br() { function log(msg) { game.log.push(msg); } -// function logevent(cap: Card) { -// game.log.push(`E${cap}.`) -// } -// function logcap(cap: Card) { -// game.log.push(`C${cap}.`) -// } -// function logi(msg: string) { -// game.log.push('>' + msg); -// } -// function logii(msg: string) { -// game.log.push('>>' + msg); -// } function log_h1(msg) { log_br(); log('.h1 ' + msg); @@ -591,26 +590,10 @@ function log_h2(msg) { log('.h2 ' + msg); log_br(); } -// function log_h2_active(msg: string) { -// log_br(); -// log('.h2 ' + msg); -// log_br(); -// } -// function log_h2_common(msg: string) { -// log_br(); -// log('.h2 ' + msg); -// log_br(); -// } function log_h3(msg) { log_br(); log('.h3 ' + msg); } -// function log_h4(msg: string) { -// log_br(); -// log('.h4 ' + msg); -// } -// #endregion LOGGING -// #region UTILITY function clear_undo() { console.log('game clear undo', game?.undo); if (game?.undo && game.undo.length > 0) @@ -619,13 +602,50 @@ function clear_undo() { function get_active_faction() { return player_faction_map[game.active]; } +function get_active_faction_id() { + return player_faction_map[game.active]; +} +function get_faction_id(player) { + return player_faction_map[player]; +} +function get_player(faction_id) { + return faction_player_map[faction_id]; +} +function get_player_order(first_player = game.initiative) { + const order = []; + let faction = first_player; + for (let i = 0; i < 3; ++i) { + order.push(faction); + faction = + game.year === 2 + ? get_previous_faction(faction) + : get_next_faction(faction); + } + return order; +} +function get_previous_faction(faction_id) { + const index = role_ids.indexOf(faction_id); + if (index === 0) { + return role_ids[2]; + } + return role_ids[index - 1]; +} function get_next_faction(faction_id) { const index = role_ids.indexOf(faction_id); - let next_index = index + 1; - if (next_index === role_ids.length) { - next_index = 0; + if (index === 2) { + return role_ids[0]; } - return role_ids[next_index]; + return role_ids[index + 1]; +} +function get_factions_with_most_hero_poins() { + const most_hero_points = Math.max(...Object.values(game.hero_points)); + const faction_ids = []; + Object.entries(game.hero_points).forEach(([faction, hero_points]) => { + if (hero_points === most_hero_points) { + faction_ids.push(faction); + } + }); + return faction_ids; } function make_list(first, last) { let list = []; @@ -634,9 +654,6 @@ function make_list(first, last) { return list; } function random(range) { - // An MLCG using integer arithmetic with doubles. - // https://www.ams.org/journals/mcom/1999-68-225/S0025-5718-99-00996-5/S0025-5718-99-00996-5.pdf - // m = 2**35 − 31 return (game.seed = (game.seed * 200105) % 34359738337) % range; } function set_delete(set, item) { @@ -655,11 +672,14 @@ function set_delete(set, item) { } } } -// #endregion -// #region ARRAY function array_remove(array, index) { let n = array.length; for (let i = index + 1; i < n; ++i) array[i - 1] = array[i]; array.length = n - 1; } +function array_insert(array, index, item) { + for (let i = array.length; i > index; --i) + array[i] = array[i - 1]; + array[index] = item; +} |