diff options
Diffstat (limited to 'rules.ts')
-rw-r--r-- | rules.ts | 190 |
1 files changed, 68 insertions, 122 deletions
@@ -12,7 +12,7 @@ import { FunctionNode, Game, Icon, - LeafNode, + StateNode, PlayCardArgs, Player, PlayerCard, @@ -29,14 +29,6 @@ import data, { ANARCHISTS_ID, COMMUNISTS_ID, MODERATES_ID, - // LIBERTY, - // COLLECTIVIZATION, - // GOVERNMENT, - // SOVIET_SUPPORT, - // FOREIGN_AID, - // MORALE_BONUS, - // TEAMWORK_BONUS, - // OFF, LIBERTY, CLOSEST_TO_DEFEAT, CLOSEST_TO_VICTORY, @@ -60,7 +52,6 @@ import data, { TOWARDS_CENTER, ARCHIVES_MEDALLION_ID, INTELLIGENCE_MEDALLION_ID, - // VOLUNTEERS_MEDALLION_ID, ORGANIZATION_MEDALLION_ID, STRATEGY_MEDALLION_ID, PROPAGANDA_MEDALLION_ID, @@ -75,24 +66,14 @@ import data, { ARAGON, FASCIST_ID, NORTHERN, - // StaticData, - // PLAYER_WITH_MOST_HERO_POINTS, } from './data'; -// interface State { -// inactive: string; -// prompt: () => void; -// } const OBSERVER = 'Observer'; const states = {} as States; let game = {} as Game; // = null var view = {} as View; // = null -// export const ANARCHIST = 'Anarchist' as Player; -// export const COMMUNIST = 'Communist' as Player; -// export const MODERATE = 'Moderate' as Player; - const role_ids: FactionId[] = [ANARCHISTS_ID, COMMUNISTS_ID, MODERATES_ID]; const faction_player_map: Record<FactionId, Player> = { @@ -185,18 +166,6 @@ function gen_spend_hero_points() { } } -// 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); -// } - export function action( state: Game, player: Player, @@ -217,18 +186,18 @@ export function action( // #region ENGINE -const leaf_node = 'l'; +const state_node = 'l'; const seq_node = 's'; const function_node = 'f'; const resolved = 1; -function create_leaf_node<T = any>( +function create_state_node<T = any>( state: string, faction: FactionId | 'None' | 'all' | FactionId[], args?: T -): LeafNode { +): StateNode { return { - t: leaf_node, + t: state_node, s: state, p: faction, a: args, @@ -256,7 +225,7 @@ function create_seq_node(children: EngineNode[]): SeqNode { function checkpoint() { if (game.undo.length > 0) { insert_after_active_node( - create_leaf_node('confirm_turn', get_active_faction()) + create_state_node('confirm_turn', get_active_faction()) ); } resolve_active_and_proceed(); @@ -265,22 +234,15 @@ function checkpoint() { function setup_bag_of_glory() { log_h1('Bag of Glory'); game.engine = [ - create_leaf_node('add_glory', game.initiative), + create_state_node('add_glory', game.initiative), create_function_node('end_of_turn'), ]; next(); } function setup_choose_card() { - game.engine = [create_leaf_node('choose_card', 'all')]; - - // const player_order = get_player_order(); - // game.engine = player_order.map((faction_id) => - // create_leaf_node('choose_card', faction_id) - // ); - // Add extra confirm, otherwise first players card will be revealed - // before confirm - // game.engine.push(create_leaf_node('confirm_turn', player_order[2])); + game.engine = [create_state_node('choose_card', 'all')]; + game.engine.push(create_function_node('setup_player_turn')); next(); } @@ -289,7 +251,7 @@ function setup_final_bid() { log_h1('Final Bid'); const player_order = get_player_order(); game.engine = player_order.map((faction_id) => - create_leaf_node('choose_final_bid', faction_id) + create_state_node('choose_final_bid', faction_id) ); game.engine.push(create_function_node('checkpoint')); game.engine.push(create_function_node('resolve_final_bid')); @@ -309,14 +271,14 @@ function setup_player_turn() { game.engine = [ create_function_node('start_of_player_turn', { f: next_faction }), - create_leaf_node('player_turn', next_faction), + create_state_node('player_turn', next_faction), create_function_node('end_of_player_turn', { f: next_faction }), ]; next(); } -// Check if player needs to discard cards. If so inserts leaf node +// Check if player needs to discard cards. If so inserts state node function check_end_of_year_discard() { const { f: faction } = get_active_node_args(); @@ -324,7 +286,7 @@ function check_end_of_year_discard() { game.hands[faction].length > get_hand_limit(faction) || game.tableaus[faction].length > game.year ) { - insert_after_active_node(create_leaf_node('end_of_year_discard', faction)); + insert_after_active_node(create_state_node('end_of_year_discard', faction)); } resolve_active_and_proceed(); @@ -334,7 +296,7 @@ function end_of_player_turn() { const { f: faction } = get_active_node_args(); if (get_next_faction_in_player_order(faction) === game.first_player) { game.engine = [ - create_leaf_node('change_active_player', game.initiative), + create_state_node('change_active_player', game.initiative), create_function_node('resolve_fascist_test'), create_function_node('setup_bag_of_glory'), ]; @@ -391,9 +353,9 @@ const engine_functions: Record<string, Function> = { function get_active( engine: EngineNode[] -): { parent: EngineNode[]; node: FunctionNode | LeafNode } | null { +): { parent: EngineNode[]; node: FunctionNode | StateNode } | null { for (let i of engine) { - if ((i.t === leaf_node || i.t === function_node) && i.r !== resolved) { + if ((i.t === state_node || i.t === function_node) && i.r !== resolved) { return { parent: engine, node: i }; } if (i.t === seq_node) { @@ -408,7 +370,7 @@ function get_active( function get_active_node( engine: EngineNode[] = game.engine -): FunctionNode | LeafNode | null { +): FunctionNode | StateNode | null { const a = get_active(engine); return a === null ? null : a.node; } @@ -416,10 +378,10 @@ function get_active_node( function get_nodes_for_state( state: string, engine: EngineNode[] = game.engine -): LeafNode[] { +): StateNode[] { let nodes = []; for (let i of engine) { - if (i.t === leaf_node && i.s === state) { + if (i.t === state_node && i.s === state) { nodes.push(i); } if (i.t === seq_node) { @@ -431,7 +393,7 @@ function get_nodes_for_state( function get_active_node_args<T = any>(): T { const node = get_active_node(game.engine); - if (node.t === leaf_node || node.t === function_node) { + if (node.t === state_node || node.t === function_node) { return node.a ?? {}; } return null; @@ -439,7 +401,7 @@ function get_active_node_args<T = any>(): T { function update_active_node_args<T = any>(args: Partial<T>) { const node = get_active_node(game.engine); - if (node.t === leaf_node || node.t === function_node) { + if (node.t === state_node || node.t === function_node) { node.a = { ...node.a, ...args, @@ -476,7 +438,7 @@ function insert_before_active_node( insert_before_or_after_active_node(node, 'before', engine); } -function get_next_active(p: LeafNode['p']): Player | Player[] | 'None' { +function get_next_active(p: StateNode['p']): Player | Player[] | 'None' { if (Array.isArray(p)) { return p.map((faction) => faction_player_map[faction]); } @@ -513,7 +475,7 @@ function next(checkpoint = false) { if (next_active !== current_active && game.undo.length > 0) { insert_before_active_node( - create_leaf_node('confirm_turn', get_active_faction()) + create_state_node('confirm_turn', get_active_faction()) ); game.state = 'confirm_turn'; return; @@ -592,7 +554,9 @@ function game_view(state: Game, current: Player | 'Observer') { !game.active.includes(current as Player) ) { let inactive = states[game.state].inactive || game.state; - view.prompt = Array.isArray(game.active) ? `Waiting for other players to ${inactive}.` : `Waiting for ${game.active} to ${inactive}.`; + view.prompt = Array.isArray(game.active) + ? `Waiting for other players to ${inactive}.` + : `Waiting for ${game.active} to ${inactive}.`; } else { view.actions = {}; if (game.undo && game.undo.length > 0) view.actions.undo = 1; @@ -771,11 +735,6 @@ function start_turn() { game.engine.push(create_function_node('setup_choose_card')); } next(); - // game.state = 'resolve_event'; - // game.active = faction_player_map[game.initiative]; - // game.state_data = { - // current_effect: 0, - // }; } // region STATES @@ -805,7 +764,7 @@ states.activate_icon = { }, add_to_front() { insert_after_active_node( - create_leaf_node('add_to_front', get_active_faction(), { + create_state_node('add_to_front', get_active_faction(), { t: ANY, v: get_icon_count_in_tableau('add_to_front'), }) @@ -1155,15 +1114,12 @@ states.choose_area_ap = { resolve_active_and_proceed(); }, standee(track_id: number) { - insert_after_active_node({ - t: leaf_node, - p: get_active_faction(), - s: 'move_track_up_or_down', - a: { + insert_after_active_node( + create_state_node('move_track_up_or_down', get_active_faction(), { track_id, strength: get_active_node()?.a.strength, - }, - }); + }) + ); resolve_active_and_proceed(); }, }; @@ -1328,7 +1284,7 @@ function setup_momentum() { // right away or not. Depends on whether card for this turn has been fully resolved or not // Get player turn node - const node: LeafNode<PlayerTurnArgs> = get_nodes_for_state('player_turn')[0]; + const node: StateNode<PlayerTurnArgs> = get_nodes_for_state('player_turn')[0]; const player_needs_to_play_card = game.selected_cards[faction].length > 0; const { use_ap, use_morale_bonus, resolving_event } = @@ -1348,7 +1304,7 @@ function setup_momentum() { } else { // Player can resolve choosing a new card insert_after_active_node( - create_leaf_node<PlayCardArgs>('play_card', faction, { + create_state_node<PlayCardArgs>('play_card', faction, { src: 'momentum', }) ); @@ -1785,7 +1741,7 @@ function resolve_spend_hp() { // insert spend hero points node before current node // so it will return to current node after resolving insert_before_active_node( - create_leaf_node('spend_hero_points', get_active_faction()) + create_state_node('spend_hero_points', get_active_faction()) ); log('Spends Hero Points'); @@ -1911,7 +1867,7 @@ states.player_turn = { use_ap: false, }); insert_before_active_node( - create_leaf_node('choose_area_ap', faction, { + create_state_node('choose_area_ap', faction, { strength, }) ); @@ -1935,7 +1891,7 @@ states.player_turn = { use_morale_bonus: false, }); insert_before_active_node( - create_leaf_node('activate_icon', get_active_faction()) + create_state_node('activate_icon', get_active_faction()) ); next(); }, @@ -2029,7 +1985,7 @@ states.remove_attack_from_fronts = { resolve_active_and_proceed(); } else if (card_id === 39 || card_id === 16) { insert_after_active_node( - create_leaf_node('attack_front', get_active_faction(), { + create_state_node('attack_front', get_active_faction(), { t: ANY, v: card_id === 39 ? -2 : -1 * removed_value, n: card_id === 16 ? id : undefined, @@ -2043,7 +1999,7 @@ states.remove_attack_from_fronts = { const values: number[] = Object.values(f ?? {}); if (card_id === 39 && values.length > 0) { insert_after_active_node( - create_leaf_node('attack_front', get_active_faction(), { + create_state_node('attack_front', get_active_faction(), { t: ANY, v: -2, }) @@ -2161,11 +2117,11 @@ states.spend_hero_points = { pay_hero_points(faction, 1); insert_after_active_node( create_seq_node([ - create_leaf_node('add_to_front', faction, { + create_state_node('add_to_front', faction, { t: ANY, v: 1, }), - create_leaf_node('spend_hero_points', faction), + create_state_node('spend_hero_points', faction), ]) ); resolve_active_and_proceed(); @@ -2202,8 +2158,8 @@ states.spend_hero_points = { } insert_after_active_node( create_seq_node([ - create_leaf_node('remove_blank_marker', faction), - create_leaf_node('spend_hero_points', faction), + create_state_node('remove_blank_marker', faction), + create_state_node('spend_hero_points', faction), ]) ); resolve_active_and_proceed(); @@ -2222,11 +2178,11 @@ states.spend_hero_points = { pay_hero_points(faction, amount); insert_after_active_node( create_seq_node([ - create_leaf_node('move_track_up_or_down', faction, { + create_state_node('move_track_up_or_down', faction, { track_id, strength: 1, }), - create_leaf_node('spend_hero_points', faction), + create_state_node('spend_hero_points', faction), ]) ); resolve_active_and_proceed(); @@ -2637,7 +2593,7 @@ function determine_winner() { win_game(faction_player_map[winners[0]], highest_glory); } else { insert_after_active_node( - create_leaf_node('break_tie_winner', game.initiative, { + create_state_node('break_tie_winner', game.initiative, { winners, glory: highest_glory, }) @@ -2689,7 +2645,7 @@ function end_of_year() { const engine = []; for (let i = 0; i < glory_to_draw[game.year]; ++i) { - engine.push(create_leaf_node('draw_glory', player_order[i % 3])); + engine.push(create_state_node('draw_glory', player_order[i % 3])); } engine.push(create_function_node('end_of_year_cleanup')); @@ -2726,7 +2682,7 @@ function end_of_year_cleanup() { function end_resolving_event_effects() { // Get player turn node - const node: LeafNode<PlayerTurnArgs> = get_nodes_for_state('player_turn')[0]; + const node: StateNode<PlayerTurnArgs> = get_nodes_for_state('player_turn')[0]; // Update args node.a = { @@ -2784,9 +2740,7 @@ function gain_hero_points( } function game_over(result: Player | 'None', victory: string) { - insert_after_active_node(create_leaf_node('game_over', 'None')); - // game.state = 'game_over'; - // game.active = 'None'; + insert_after_active_node(create_state_node('game_over', 'None')); game.result = result; game.victory = victory; game.undo = []; @@ -2896,7 +2850,7 @@ function resolve_final_bid() { win_final_bid(winners[0]); } else { insert_after_active_node( - create_leaf_node('break_tie_final_bid', game.initiative, { winners }) + create_state_node('break_tie_final_bid', game.initiative, { winners }) ); } @@ -3025,7 +2979,7 @@ function insert_use_organization_medallion_node( const faction = get_active_faction(); insert_after_active_node( - create_leaf_node('use_organization_medallion', faction, { + create_state_node('use_organization_medallion', faction, { t: track_id, v: value, }) @@ -3085,7 +3039,7 @@ function update_front( can_use_medallion(STRATEGY_MEDALLION_ID) ) { insert_after_active_node( - create_leaf_node('use_strategy_medallion', get_active_faction(), { + create_state_node('use_strategy_medallion', get_active_faction(), { f: front_id, }) ); @@ -3170,7 +3124,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { return create_function_node(effect.target as string); } if (effect.type === 'state') { - return create_leaf_node(effect.target as string, faction, { + return create_state_node(effect.target as string, faction, { v: effect.value, src: source, }); @@ -3178,7 +3132,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { // Default cases where effect type is mapped to a state let state = effect_type_state_map[effect.type]; if (state !== undefined) { - return create_leaf_node(state, faction, args); + return create_state_node(state, faction, args); } // Specific mapping based on target @@ -3188,7 +3142,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { effect.type === 'hero_points' && effect.target === PLAYER_WITH_MOST_HERO_POINTS, resolve: () => { - return create_leaf_node( + return create_state_node( 'select_player_with_most_hero_points', faction, args @@ -3200,7 +3154,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { resolve: () => { return create_seq_node( get_player_order().map((faction) => - create_leaf_node('hero_points', faction, args) + create_state_node('hero_points', faction, args) ) ); }, @@ -3208,7 +3162,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { { condition: effect.type === 'hero_points' && effect.target === SELF, resolve: () => { - return create_leaf_node('hero_points', faction, args); + return create_state_node('hero_points', faction, args); }, }, { @@ -3216,7 +3170,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { effect.type === 'hero_points' && role_ids.includes(effect.target as FactionId), resolve: () => { - return create_leaf_node( + return create_state_node( 'hero_points', effect.target as FactionId, args @@ -3227,20 +3181,20 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { condition: effect.type === 'hero_points' && effect.target === INITIATIVE_PLAYER, resolve: () => { - return create_leaf_node('hero_points', game.initiative, args); + return create_state_node('hero_points', game.initiative, args); }, }, { condition: effect.type === 'draw_card' && effect.target === SELF, resolve: () => { - return create_leaf_node('draw_card', faction, args); + return create_state_node('draw_card', faction, args); }, }, { condition: effect.type === 'draw_card' && effect.target === INITIATIVE_PLAYER, resolve: () => { - return create_leaf_node('draw_card', game.initiative, args); + return create_state_node('draw_card', game.initiative, args); }, }, { @@ -3248,7 +3202,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { effect.type === 'draw_card' && role_ids.includes(effect.target as FactionId), resolve: () => { - return create_leaf_node('draw_card', effect.target as FactionId, args); + return create_state_node('draw_card', effect.target as FactionId, args); }, }, { @@ -3256,7 +3210,7 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { resolve: () => { return create_seq_node( get_player_order(get_active_faction()).map((faction) => - create_leaf_node('draw_card', faction, args) + create_state_node('draw_card', faction, args) ) ); }, @@ -3264,19 +3218,19 @@ function resolve_effect(effect: Effect, source?: EffectSource): EngineNode { { condition: effect.type === 'draw_card' && effect.target === OTHER_PLAYERS, resolve: () => { - const leaf_nodes = get_player_order(get_active_faction()).map( - (faction) => create_leaf_node('draw_card', faction, args) + const state_nodes = get_player_order(get_active_faction()).map( + (faction) => create_state_node('draw_card', faction, args) ); - array_remove(leaf_nodes, 0); // Remove current player - return create_seq_node(leaf_nodes); + array_remove(state_nodes, 0); // Remove current player + return create_seq_node(state_nodes); }, }, { condition: effect.type === 'play_card', resolve: () => { return create_seq_node([ - create_leaf_node('play_card', faction, { src: source }), - create_leaf_node('player_turn', faction, { src: source }), + create_state_node('play_card', faction, { src: source }), + create_state_node('player_turn', faction, { src: source }), ]); }, }, @@ -3305,14 +3259,6 @@ function win_game(player: Player, glory: number) { // #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 draw_card(deck: CardId[]): CardId { clear_undo(); |