'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.roles = exports.scenarios = void 0;
exports.action = action;
exports.view = game_view;
exports.setup = setup;
const data_1 = require("./data");
const OBSERVER = 'Observer';
const states = {};
let game = {};
var view = {};
const POOL_ID = 3;
const role_ids = [data_1.ANARCHISTS_ID, data_1.COMMUNISTS_ID, data_1.MODERATES_ID];
const faction_player_map = [
    data_1.ANARCHIST,
    data_1.COMMUNIST,
    data_1.MODERATE,
];
const player_faction_map = {
    [data_1.ANARCHIST]: data_1.ANARCHISTS_ID,
    [data_1.COMMUNIST]: data_1.COMMUNISTS_ID,
    [data_1.MODERATE]: data_1.MODERATES_ID,
};
const front_names = [
    'Aragon Front',
    'Madrid Front',
    'Northern Front',
    'Southern Front',
    'the Front closest to Defeat',
    'the Front closest to Victory',
];
const bonus_names = ['Morale Bonus', 'Teamwork Bonus'];
const { cards, tracks, } = data_1.default;
const bonuses = [data_1.MORALE_BONUS, data_1.TEAMWORK_BONUS];
const faction_cards = [
    make_list(37, 54),
    make_list(19, 36),
    make_list(1, 18),
];
const fascist_decks = {
    1: make_list(55, 72),
    2: make_list(73, 90),
    3: make_list(91, 108),
};
exports.scenarios = ['Standard'];
exports.roles = [data_1.ANARCHIST, data_1.COMMUNIST, data_1.MODERATE];
function gen_action(action, argument) {
    if (argument === undefined) {
        view.actions[action] = 1;
    }
    else {
        if (!(action in view.actions))
            view.actions[action] = [];
        view.actions[action].push(argument);
    }
}
function gen_action_blank_marker(marker_id) {
    gen_action('blank_marker', marker_id);
}
function gen_action_bonus(bonus_id) {
    gen_action('bonus', bonus_id);
}
function gen_action_card(card_id) {
    gen_action('card', card_id);
}
function gen_action_front(front_id) {
    gen_action('front', front_id);
}
function gen_action_medallion(medallion_id) {
    gen_action('medallion', medallion_id);
}
function gen_spend_hero_points() {
    const faction = get_active_faction();
    const can_spend_hp = game.faction_turn === faction && game.hero_points[faction] > 0;
    if (can_spend_hp) {
        gen_action('spend_hp');
    }
}
const multiactive_states = ['choose_card', 'end_of_year_discard'];
function action(state, player, action, arg) {
    game = state;
    if (action !== 'undo' && !multiactive_states.includes(game.state)) {
        push_undo();
    }
    let S = states[game.state];
    if (action in S)
        S[action](arg, player);
    else if (action === 'undo' && game.undo && game.undo.length > 0)
        pop_undo();
    else
        throw new Error('Invalid action: ' + action);
    return game;
}
const state_node = 'l';
const seq_node = 's';
const function_node = 'f';
const resolved = 1;
function create_state_node(state, faction, args) {
    return {
        t: state_node,
        s: state,
        p: faction,
        a: args,
        r: 0,
    };
}
function create_function_node(func_name, args) {
    return {
        t: function_node,
        f: func_name,
        a: args,
        r: 0,
    };
}
function create_seq_node(children) {
    return {
        t: seq_node,
        c: children,
    };
}
function checkpoint() {
    if (game.undo.length > 0) {
        insert_after_active_node(create_state_node('confirm_turn', get_active_faction()));
    }
    resolve_active_and_proceed();
}
function setup_bag_of_glory() {
    game.engine = [
        create_state_node('add_glory', game.initiative),
        create_function_node('end_of_turn'),
    ];
    next();
}
function setup_choose_card() {
    game.fascist = 0;
    game.engine = [create_state_node('choose_card', 'all')];
    game.engine.push(create_function_node('setup_player_turn'));
    next();
}
function setup_final_bid() {
    game.fascist = 0;
    log_header('Final Bid', 't');
    const player_order = get_player_order();
    game.engine = player_order.map((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'));
    game.engine.push(create_function_node('setup_choose_card'));
    next();
}
function setup_player_turn(faction_id) {
    game.fascist = 0;
    game.card_played = 0;
    const next_faction = game.first_player === null
        ? get_player_order()[0]
        : faction_id;
    if (game.first_player === null) {
        game.first_player = next_faction;
    }
    if (game.selected_cards[next_faction].length > 0) {
        game.engine = [
            create_function_node('start_of_player_turn', { f: next_faction }),
            create_state_node('player_turn', next_faction),
        ];
        next();
    }
    else {
        log_header("Skipped Turn", next_faction);
        log("No cards in hand.");
        game.engine = [
            create_function_node('end_of_player_turn', { f: next_faction })
        ];
        next();
    }
}
function end_of_player_turn() {
    const { f: faction } = get_active_node_args();
    const next_faction = get_next_faction_in_player_order(faction);
    if (next_faction === game.first_player) {
        game.engine = [
            create_state_node('change_active_player', game.initiative),
            create_function_node('resolve_fascist_test'),
            create_function_node('setup_bag_of_glory'),
        ];
    }
    else {
        game.engine = [create_function_node('setup_player_turn', next_faction)];
    }
    next();
}
function start_of_player_turn() {
    const args = get_active_node_args();
    game.faction_turn = args.f;
    game.played_card = game.selected_cards[args.f][0];
    log_header("C" + game.played_card, args.f);
    resolve_active_and_proceed();
}
const engine_functions = {
    checkpoint,
    end_of_player_turn,
    end_of_turn,
    end_of_year_cleanup,
    setup_bag_of_glory,
    setup_choose_card,
    setup_final_bid,
    setup_player_turn,
    start_of_player_turn,
    start_year,
    resolve_fascist_test,
    resolve_final_bid,
    log_trigger,
    card1_event2,
    card3_event2,
    card10_event2,
    card16_event2,
    card17_event3,
    card20_event3,
    card22_event3,
    card23_event1,
    card26_event1,
    card29_event2,
    card35_event2,
    card42_event3,
    card45_event2,
    card46_event3,
    card50_event2,
    card53_event2,
    card54_event1,
    setup_return_card_from_trash,
    trash_card,
};
function get_active(engine) {
    for (let i of engine) {
        if ((i.t === state_node || i.t === function_node) && i.r !== resolved) {
            return { parent: engine, node: i };
        }
        if (i.t === seq_node) {
            const next_child = get_active(i.c);
            if (next_child !== null) {
                return next_child;
            }
        }
    }
    return null;
}
function get_active_node(engine = game.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 === state_node || node.t === function_node) {
        return node.a ?? {};
    }
    return null;
}
function update_active_node_args(args) {
    const node = get_active_node(game.engine);
    if (node.t === state_node || node.t === function_node) {
        node.a = {
            ...node.a,
            ...args,
        };
    }
}
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);
    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 get_next_active(p) {
    if (Array.isArray(p)) {
        return p.map((faction) => faction_player_map[faction]);
    }
    if (p === 'all') {
        return exports.roles.slice();
    }
    if (p === 'None') {
        return 'None';
    }
    else {
        return faction_player_map[p];
    }
}
function next(checkpoint = false) {
    if (checkpoint) {
        clear_undo();
    }
    const node = get_active_node(game.engine);
    if (node.t === function_node && engine_functions[node.f]) {
        const args = node.a;
        if (args !== undefined) {
            engine_functions[node.f](args);
        }
        else {
            engine_functions[node.f]();
        }
    }
    else if (node.t === 'l') {
        game.state = node.s;
        const current_active = game.active;
        const next_active = get_next_active(node.p);
        if (next_active !== current_active && game.undo.length > 0) {
            insert_before_active_node(create_state_node('confirm_turn', get_active_faction()));
            game.state = 'confirm_turn';
            return;
        }
        game.active = next_active;
        if (states[game.state].auto_resolve && states[game.state].auto_resolve()) {
            resolve_active_and_proceed();
        }
    }
}
function resolve_active_node() {
    const next_node = get_active_node(game.engine);
    if (next_node !== null) {
        next_node.r = resolved;
    }
}
function resolve_active_and_proceed(checkpoint = false) {
    resolve_active_node();
    next(checkpoint);
}
function game_view(state, current) {
    game = state;
    const faction = current === OBSERVER ? null : player_faction_map[current];
    view = {
        log: game.log,
        prompt: null,
        bonuses: game.bonuses,
        current_events: game.current_events,
        first_player: game.first_player,
        fronts: game.fronts,
        glory: game.glory,
        hero_points: game.hero_points,
        initiative: game.initiative,
        medallions: game.medallions,
        played_card: game.played_card,
        player_order: current === OBSERVER
            ? get_player_order()
            : get_player_order_in_game(faction),
        selected_cards: current === OBSERVER ? [] : game.selected_cards[faction],
        tableaus: game.tableaus,
        tracks: game.tracks,
        triggered_track_effects: game.triggered_track_effects,
        used_medallions: game.used_medallions,
        year: game.year,
        fascist: game.fascist,
    };
    if (faction !== null) {
        view.hand = game.hands[faction];
        view.discard = game.discard[faction];
        view.trash = game.trash[faction];
    }
    if (!game.hidden_bag)
        view.bag_of_glory = game.bag_of_glory;
    if (game.state === 'game_over') {
        view.prompt = game.victory;
    }
    else if (current !== game.active &&
        !game.active.includes(current)) {
        let inactive = states[game.state].inactive || game.state;
        view.prompt = Array.isArray(game.active)
            ? `Waiting for ${game.active.join(' and ')} to ${inactive}.`
            : `Waiting for ${game.active} to ${inactive}.`;
    }
    else {
        view.actions = {};
        if (game.undo && game.undo.length > 0)
            view.actions.undo = 1;
        else
            view.actions.undo = 0;
        states[game.state].prompt(current);
        let node = get_active_node();
        if (node && node.a && node.a.src)
            view.prompt = get_source_name(node.a.src) + ": " + view.prompt;
    }
    return view;
}
function setup(seed, _scenario, options) {
    game = {
        seed: seed,
        state: null,
        active: data_1.ANARCHIST,
        active_abilities: [],
        bag_of_glory: [data_1.ANARCHISTS_ID, data_1.COMMUNISTS_ID, data_1.MODERATES_ID],
        bonuses: [data_1.ON, data_1.ON],
        current_events: [],
        discard: [
            [],
            [],
            [],
            [],
        ],
        engine: [],
        faction_turn: null,
        fronts: [
            {
                value: -2,
                contributions: [],
                status: null,
            },
            {
                value: -2,
                contributions: [],
                status: null,
            },
            {
                value: -2,
                contributions: [],
                status: null,
            },
            {
                value: -2,
                contributions: [],
                status: null,
            },
        ],
        glory: [],
        first_player: null,
        hands: [
            [],
            [],
            [],
        ],
        hero_points: [2, 2, 0, 14],
        initiative: data_1.MODERATES_ID,
        medallions: [[], [], [], []],
        momentum: null,
        played_card: null,
        player_order: [data_1.MODERATE],
        selected_cards: [
            [],
            [],
            [],
        ],
        tableaus: [
            [],
            [],
            [],
        ],
        tracks: [5, 5, 6, 3, 3],
        trash: [
            [],
            [],
            [],
        ],
        triggered_track_effects: [],
        log: [],
        undo: [],
        used_medallions: [],
        top_of_events_deck: null,
        turn: 0,
        year: 0,
        glory_current_year: null,
        fascist: 0,
        card_played: 0,
        can_use_ap: 0,
        can_use_mb: 0,
    };
    log_header('Land and Freedom', 't');
    if (options.hidden_bag)
        game.hidden_bag = 1;
    game.player_order.push(exports.roles[random(2)]);
    game.player_order.push(game.player_order[1] === data_1.ANARCHIST ? data_1.COMMUNIST : data_1.ANARCHIST);
    draw_medallions();
    start_year();
    return game;
}
function draw_hand_cards(faction_id, count, indent = true) {
    const deck = list_deck(faction_id);
    if (game.medallions[faction_id].includes(data_1.INTELLIGENCE_MEDALLION_ID)) {
        count++;
    }
    let drawn_cards = 0;
    if (deck.length < count) {
        count = count - deck.length;
        drawn_cards += deck.length;
        game.hands[faction_id] = game.hands[faction_id].concat(deck);
        game.discard[faction_id] = [];
    }
    for (let i = 0; i < count; i++) {
        const deck = list_deck(faction_id);
        if (deck.length > 0) {
            game.hands[faction_id].push(draw_card(deck));
            drawn_cards++;
        }
    }
    if (indent)
        logi(`${get_player(faction_id)} +${drawn_cards} cards`);
    else
        log(`${get_player(faction_id)} +${drawn_cards} cards.`);
}
function start_year() {
    game.year++;
    log_header(`Year ${game.year}`, 't');
    game.turn = 1;
    game.current_events = [];
    role_ids.forEach((role) => {
        draw_hand_cards(role, 5, false);
    });
    start_turn();
}
function start_turn() {
    log_header(`Turn ${game.turn}`, 't');
    const cardId = draw_fascist_card();
    game.current_events.push(cardId);
    const card = cards[cardId];
    log_header("C" + cardId, 'f');
    log("Fascist Event:");
    game.fascist = 1;
    game.engine = card.effects.map((effect) => resolve_effect(effect, 'fascist_event'));
    game.engine.push(create_state_node('confirm_fascist_turn', game.initiative, { src: 'fascist_event' }));
    if (game.year === 3 && game.turn === 4) {
        game.engine.push(create_function_node('setup_final_bid'));
    }
    else {
        game.engine.push(create_function_node('setup_choose_card'));
    }
    next();
}
function player_can_resolve_icon(icon) {
    if (icon === 'teamwork_on' && game.bonuses[data_1.TEAMWORK_BONUS] === data_1.ON) {
        return false;
    }
    return true;
}
const track_icon_to_track_id_map = {
    collectivization: data_1.COLLECTIVIZATION,
    d_collectivization: data_1.COLLECTIVIZATION,
    foreign_aid: data_1.FOREIGN_AID,
    d_foreign_aid: data_1.FOREIGN_AID,
    government: data_1.GOVERNMENT,
    d_government: data_1.GOVERNMENT,
    government_to_center: data_1.GOVERNMENT,
    liberty: data_1.LIBERTY,
    d_liberty: data_1.LIBERTY,
    soviet_support: data_1.SOVIET_SUPPORT,
    d_soviet_support: data_1.SOVIET_SUPPORT,
};
states.activate_icon = {
    inactive: 'activate an icon',
    prompt() {
        gen_spend_hero_points();
        const c = cards[game.played_card];
        view.prompt = 'Morale Bonus: ';
        view.prompt += join_oxford_comma(c.icons.map(get_icon_name), "or");
        view.prompt += '.';
        let can_activate_icon = false;
        for (const i of c.icons) {
            const count = get_icon_count_in_tableau(i);
            let direction = 0;
            switch (i) {
                case 'add_to_front':
                    const possible_fronts = get_fronts_to_add_to(data_1.ANY);
                    for (let f of possible_fronts) {
                        gen_action_front(f);
                    }
                    if (possible_fronts.length > 0) {
                        can_activate_icon = true;
                    }
                    break;
                case 'collectivization':
                case 'foreign_aid':
                case 'liberty':
                case 'soviet_support':
                    const can_move_ss = gen_move_track(track_icon_to_track_id_map[i], game.tracks[track_icon_to_track_id_map[i]] + count);
                    can_activate_icon = can_activate_icon || can_move_ss;
                    break;
                case 'government':
                    direction = game.active === data_1.COMMUNIST ? -1 : 1;
                    const can_move_g = gen_move_track(track_icon_to_track_id_map[i], game.tracks[track_icon_to_track_id_map[i]] + direction * count);
                    can_activate_icon = can_activate_icon || can_move_g;
                    break;
                case 'd_collectivization':
                case 'd_foreign_aid':
                case 'd_government':
                case 'd_liberty':
                case 'd_soviet_support':
                    const can_move_t = gen_move_track(track_icon_to_track_id_map[i], game.tracks[track_icon_to_track_id_map[i]] - count);
                    can_activate_icon = can_activate_icon || can_move_t;
                    break;
                case 'government_to_center':
                    direction = game.tracks[data_1.GOVERNMENT] >= 6 ? -1 : 1;
                    const can_move_gtoc = gen_move_track(track_icon_to_track_id_map[i], game.tracks[track_icon_to_track_id_map[i]] + direction * count);
                    can_activate_icon = can_activate_icon || can_move_gtoc;
                    break;
                case 'teamwork_on':
                    if (game.bonuses[data_1.TEAMWORK_BONUS] === data_1.OFF) {
                        gen_action_bonus(data_1.TEAMWORK_BONUS);
                        can_activate_icon = true;
                    }
                    break;
                case 'draw_card':
                    gen_action('draw_card');
                    can_activate_icon = true;
                    break;
                default:
                    gen_action(i);
            }
            if (!player_can_resolve_icon(i)) {
                view.actions[i] = 0;
            }
        }
        if (!can_activate_icon) {
            gen_action('skip');
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    front(f) {
        update_front(f, get_icon_count_in_tableau('add_to_front'), get_active_faction());
        resolve_active_and_proceed();
    },
    tr0(x) {
        if (can_use_medallion(data_1.ORGANIZATION_MEDALLION_ID)) {
            insert_use_organization_medallion_node(data_1.LIBERTY, x);
        }
        else {
            move_track_to(0, x);
        }
        resolve_active_and_proceed();
    },
    tr1(x) {
        if (can_use_medallion(data_1.ORGANIZATION_MEDALLION_ID)) {
            insert_use_organization_medallion_node(data_1.COLLECTIVIZATION, x);
        }
        else {
            move_track_to(1, x);
        }
        resolve_active_and_proceed();
    },
    tr2(x) {
        if (can_use_medallion(data_1.ORGANIZATION_MEDALLION_ID)) {
            insert_use_organization_medallion_node(data_1.GOVERNMENT, x);
        }
        else {
            move_track_to(2, x);
        }
        resolve_active_and_proceed();
    },
    tr3(x) {
        if (can_use_medallion(data_1.ORGANIZATION_MEDALLION_ID)) {
            insert_use_organization_medallion_node(data_1.SOVIET_SUPPORT, x);
        }
        else {
            move_track_to(3, x);
        }
        resolve_active_and_proceed();
    },
    tr4(x) {
        if (can_use_medallion(data_1.ORGANIZATION_MEDALLION_ID)) {
            insert_use_organization_medallion_node(data_1.FOREIGN_AID, x);
        }
        else {
            move_track_to(4, x);
        }
        resolve_active_and_proceed();
    },
    draw_card() {
        draw_hand_cards(get_active_faction(), get_icon_count_in_tableau('draw_card'));
        resolve_active_and_proceed();
    },
    bonus(b) {
        update_bonus(b, data_1.ON);
        resolve_active_and_proceed();
    },
    skip() {
        resolve_active_and_proceed();
    },
};
states.add_card_to_tableau = {
    inactive: 'add a card to their tableau',
    prompt() {
        gen_spend_hero_points();
        view.prompt = 'Add a card to your tableau.';
        const faction = get_active_faction();
        for (const c of game.hands[faction]) {
            if (!game.selected_cards[faction].includes(c))
                gen_action_card(c);
        }
        if (game.hands[faction].length === game.selected_cards[faction].length) {
            gen_action('skip');
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    card(c) {
        const faction_id = get_active_faction();
        array_remove(game.hands[faction_id], game.hands[faction_id].indexOf(c));
        game.tableaus[faction_id].push(c);
        log(">C" + c + " to tableau");
        resolve_active_and_proceed();
    },
    skip() {
        resolve_active_and_proceed();
    },
};
states.add_glory = {
    inactive: 'add tokens to the Bag of Glory',
    prompt() {
        gen_spend_hero_points();
        view.prompt = 'Add tokens to the Bag of Glory.';
        gen_action('add_glory');
    },
    spend_hp() {
        resolve_spend_hp();
    },
    add_glory() {
        let number = 1;
        if (game.turn === 4) {
            number++;
        }
        log_br();
        log("Bag of Glory:");
        add_glory(get_active_faction(), number);
        resolve_active_and_proceed();
    },
};
states.add_to_front = {
    inactive: 'support a Front',
    prompt() {
        gen_spend_hero_points();
        const args = get_active_node_args();
        const possible_fronts = get_fronts_to_add_to(args.t);
        if (possible_fronts.length === 0) {
            view.prompt = 'No valid front to add strength to.';
            gen_action('skip');
        }
        else if (possible_fronts.length === 4) {
            view.prompt = `Support any Front.`;
        }
        else {
            view.prompt = `Support ${join_oxford_comma(possible_fronts.map(x => front_names[x]), 'or')}.`;
        }
        for (let f of possible_fronts) {
            gen_action_front(f);
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    front(f) {
        const value = get_active_node_args().v;
        update_front(f, value, get_active_faction());
        resolve_active_and_proceed();
    },
    skip() {
        resolve_active_and_proceed();
    },
};
states.attack_front = {
    inactive: 'attack a Front',
    prompt() {
        gen_spend_hero_points();
        const { t: target, n } = get_active_node_args();
        const possible_fronts = get_fronts_to_add_to(target, n);
        const number_of_fronts = possible_fronts.length;
        if (number_of_fronts === 0) {
            view.prompt = 'No valid front to attack.';
            gen_action('skip');
        }
        else if (possible_fronts.length === 4) {
            view.prompt = `Attack any Front.`;
        }
        else {
            view.prompt = `Attack ${join_oxford_comma(possible_fronts.map(x => front_names[x]), 'or')}.`;
        }
        possible_fronts.forEach((id) => gen_action('front', id));
    },
    spend_hp() {
        resolve_spend_hp();
    },
    front(f) {
        const value = get_active_node_args().v;
        update_front(f, value);
        resolve_active_and_proceed();
    },
    skip() {
        resolve_active_and_proceed();
    },
};
states.break_tie_final_bid = {
    inactive: 'break tie for Final Bid',
    prompt() {
        view.prompt = 'Choose the winner of the Final Bid';
        const { winners } = get_active_node_args();
        for (const f of winners) {
            gen_action(faction_player_map[f]);
        }
    },
    Anarchist() {
        win_final_bid(data_1.ANARCHISTS_ID);
        resolve_active_and_proceed();
    },
    Communist() {
        win_final_bid(data_1.COMMUNISTS_ID);
        resolve_active_and_proceed();
    },
    Moderate() {
        win_final_bid(data_1.MODERATES_ID);
        resolve_active_and_proceed();
    },
};
states.break_tie_winner = {
    inactive: 'break tie for winner of the game',
    prompt() {
        view.prompt = 'Choose the winner of the game';
        const { winners } = get_active_node_args();
        for (const f of winners) {
            gen_action(faction_player_map[f]);
        }
    },
    Anarchist() {
        const { glory } = get_active_node_args();
        win_game(data_1.ANARCHIST, glory);
        resolve_active_and_proceed();
    },
    Communist() {
        const { glory } = get_active_node_args();
        win_game(data_1.COMMUNIST, glory);
        resolve_active_and_proceed();
    },
    Moderate() {
        const { glory } = get_active_node_args();
        win_game(data_1.MODERATE, glory);
        resolve_active_and_proceed();
    },
};
states.change_active_player = {
    inactive: '',
    auto_resolve() {
        return true;
    },
    prompt() {
        view.prompt = '';
    },
};
states.choose_area_ap = {
    inactive: 'choose area to use Action Points',
    prompt() {
        gen_spend_hero_points();
        const use_morale_bonus = game.can_use_mb === 1 && game.bonuses[data_1.MORALE_BONUS] === data_1.ON;
        if (use_morale_bonus) {
            view.prompt = "Use Action Points and Morale Bonus.";
        }
        else {
            view.prompt = 'Use Action Points.';
        }
        const strength = get_active_node_args().strength;
        if (use_morale_bonus) {
            gen_action('use_morale_bonus');
        }
        let can_use_ap = false;
        for (const track of tracks) {
            can_use_ap = gen_move_track_change(track.id, strength) || can_use_ap;
        }
        const fronts = get_fronts_to_add_to(data_1.ANY);
        if (fronts.length > 0) {
            can_use_ap = true;
        }
        for (const front of fronts) {
            gen_action_front(front);
        }
        for (const bonus of bonuses) {
            if (game.bonuses[bonus] === data_1.OFF) {
                gen_action_bonus(bonus);
                can_use_ap = true;
            }
        }
        if (!can_use_ap) {
            gen_action('skip');
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    bonus(b) {
        log("Action Points:");
        game.can_use_ap = 0;
        update_bonus(b, data_1.ON);
        const s = get_active_node_args().strength;
        const other_bonus = b === data_1.TEAMWORK_BONUS ? data_1.MORALE_BONUS : data_1.TEAMWORK_BONUS;
        if (s > 1 && game.bonuses[other_bonus] === data_1.OFF) {
            insert_after_active_node(resolve_effect((0, data_1.create_effect)('bonus', other_bonus, data_1.ON)));
        }
        resolve_active_and_proceed();
    },
    front(f) {
        log("Action Points:");
        game.can_use_ap = 0;
        const s = get_active_node_args().strength;
        update_front(f, s, get_active_faction());
        resolve_active_and_proceed();
    },
    tr0(x) {
        log("Action Points:");
        game.can_use_ap = 0;
        move_track_to(0, x);
        resolve_active_and_proceed();
    },
    tr1(x) {
        log("Action Points:");
        game.can_use_ap = 0;
        move_track_to(1, x);
        resolve_active_and_proceed();
    },
    tr2(x) {
        log("Action Points:");
        game.can_use_ap = 0;
        move_track_to(2, x);
        resolve_active_and_proceed();
    },
    tr3(x) {
        log("Action Points:");
        game.can_use_ap = 0;
        move_track_to(3, x);
        resolve_active_and_proceed();
    },
    tr4(x) {
        log("Action Points:");
        game.can_use_ap = 0;
        move_track_to(4, x);
        resolve_active_and_proceed();
    },
    skip() {
        game.can_use_ap = 0;
        resolve_active_and_proceed();
    },
    use_morale_bonus() {
        log(`Morale Bonus:`);
        game.can_use_mb = 0;
        insert_before_active_node(create_state_node('activate_icon', get_active_faction()));
        next();
    },
};
states.change_bonus = {
    inactive: 'select Bonus',
    prompt() {
        gen_spend_hero_points();
        const args = get_active_node_args();
        if ((args.v === data_1.ON &&
            game.bonuses[data_1.TEAMWORK_BONUS] === data_1.ON &&
            game.bonuses[data_1.MORALE_BONUS] === data_1.ON) ||
            (args.v === data_1.OFF && game.bonuses[args.t] === data_1.OFF)) {
            view.prompt = `${bonus_names[args.t]} is already ${args.v === data_1.OFF ? 'off' : 'on'}.`;
            gen_action('skip');
        }
        else if (args.t === data_1.ANY && args.v === data_1.ON) {
            view.prompt = 'Turn on a Bonus.';
            for (const bonus of bonuses) {
                if (game.bonuses[bonus] === data_1.OFF) {
                    gen_action_bonus(bonus);
                }
            }
        }
        else {
            view.prompt = `Turn ${args.v === data_1.OFF ? 'off' : 'on'} ${bonus_names[args.t]}.`;
            gen_action_bonus(args.t);
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    bonus(b) {
        const value = get_active_node_args().v;
        update_bonus(b, value);
        resolve_active_and_proceed();
    },
    skip() {
        const args = get_active_node_args();
        logi(`${bonus_names[args.t]} ${args.v === data_1.OFF ? 'off' : 'on'}`);
        resolve_active_and_proceed();
    },
};
states.play_card = {
    inactive: 'play a card',
    prompt() {
        gen_spend_hero_points();
        view.prompt = 'Play a card.';
        const faction = get_active_faction();
        const hand = game.hands[faction];
        for (let c of hand) {
            if (!game.selected_cards[faction].includes(c)) {
                gen_action_card(c);
            }
        }
        if (hand.length === 0) {
            gen_action('skip');
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    card(c) {
        const faction = get_active_faction();
        game.selected_cards[faction].push(c);
        game.card_played = 0;
        game.played_card = game.selected_cards[faction][game.selected_cards[faction].length - 1];
        const args = get_active_node_args();
        if (args && args.src === 'momentum') {
            log_header("~ Momentum ~\nC" + game.played_card, faction);
        }
        else {
            log_header("~ Play Card ~\nC" + game.played_card, faction);
        }
        insert_after_active_node(create_state_node('player_turn', faction, { src: get_active_node_args().src }));
        resolve_active_and_proceed();
    },
    skip() {
        resolve_active_and_proceed();
    },
};
states.choose_card = {
    inactive: 'choose a card',
    prompt(player) {
        gen_spend_hero_points();
        view.prompt = 'Choose a card to play this turn.';
        const faction = player_faction_map[player];
        if (game.selected_cards[faction].length === 0) {
            view.actions.undo = 0;
            const hand = game.hands[faction];
            for (let c of hand) {
                if (!game.selected_cards[faction].includes(c)) {
                    gen_action_card(c);
                }
            }
        }
        else {
            view.actions.undo = 1;
            view.actions.confirm = 1;
        }
        if (game.selected_cards[faction].length === 0 && game.hands[faction].length === 0) {
            gen_action('skip');
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    card(c, player) {
        const faction = player_faction_map[player];
        game.selected_cards[faction] = [c];
    },
    undo(_, player) {
        const faction = player_faction_map[player];
        game.selected_cards[faction] = [];
    },
    confirm(_, player) {
        set_delete(game.active, player);
        if (game.active.length === 0) {
            resolve_active_and_proceed();
        }
    },
    skip(_, player) {
        set_delete(game.active, player);
        if (game.active.length === 0) {
            resolve_active_and_proceed();
        }
    },
};
states.choose_final_bid = {
    inactive: 'choose Final Bid',
    prompt() {
        view.prompt = 'Add a card to the Final Bid.';
        const faction = get_active_faction();
        for (let c of game.hands[faction]) {
            if (!game.selected_cards[faction].includes(c)) {
                gen_action_card(c);
            }
        }
        gen_action('done');
    },
    card(c) {
        const faction = get_active_faction();
        game.selected_cards[faction].push(c);
        const number_selected = game.selected_cards[faction].length;
        const number_hand = game.hands[faction].length;
        if (number_selected === 3 ||
            (number_hand < 4 && number_selected === number_hand - 1)) {
            resolve_active_and_proceed();
        }
        else {
            next();
        }
    },
    done() {
        resolve_active_and_proceed(true);
    },
};
function setup_momentum() {
    const faction = get_active_faction();
    game.momentum = faction;
    const momentum_nodes = resolve_effect((0, data_1.create_effect)('play_card', faction, 1), data_1.MOMENTUM);
    if (game.faction_turn !== null) {
        game.engine.push(momentum_nodes);
        return;
    }
    else {
        insert_after_active_node(momentum_nodes);
    }
}
states.choose_medallion = {
    inactive: 'claim a medallion',
    prompt() {
        gen_spend_hero_points();
        view.prompt = 'Claim a Medallion.';
        const faction = get_active_faction();
        var skip = true;
        for (let m of game.medallions[POOL_ID]) {
            if (m !== null) {
                if (m === data_1.MOMENTUM_MEDALLION_ID) {
                    if (game.hands[faction].some(c => !game.selected_cards[faction].includes(c))) {
                        gen_action_medallion(m);
                        skip = false;
                    }
                }
                else {
                    gen_action_medallion(m);
                    skip = false;
                }
            }
        }
        if (skip) {
            gen_action('skip');
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    medallion(m) {
        const faction = get_active_faction();
        logp("claimed M" + m);
        const index = game.medallions[POOL_ID].indexOf(m);
        game.medallions[POOL_ID][index] = null;
        switch (m) {
            case 0:
                add_glory(faction, 1);
                break;
            case 1:
                gain_hero_points(faction, 7);
                break;
            case 2:
                setup_momentum();
                break;
            default:
                game.medallions[faction].push(m);
        }
        resolve_active_and_proceed();
    },
    skip() {
        resolve_active_and_proceed();
    },
};
states.confirm_turn = {
    inactive: 'confirm their turn',
    prompt() {
        view.prompt = 'You will not be able to undo this action.';
        gen_action('confirm');
    },
    confirm() {
        resolve_active_and_proceed(true);
    },
};
states.confirm_fascist_turn = {
    inactive: 'confirm fascist turn',
    prompt() {
        view.prompt = "Done.";
        gen_action('confirm');
    },
    confirm() {
        resolve_active_and_proceed(true);
    },
};
states.draw_card = {
    inactive: 'draw a card',
    auto_resolve() {
        const { src, v } = get_active_node_args();
        if (src !== 'fascist_test') {
            return false;
        }
        draw_hand_cards(get_active_faction(), v);
        return true;
    },
    prompt() {
        gen_spend_hero_points();
        const { v } = get_active_node_args();
        view.prompt = v === 1 ? 'Draw a card.' : `Draw ${v} cards.`;
        gen_action(v === 1 ? 'draw_card' : 'draw_cards');
    },
    spend_hp() {
        resolve_spend_hp();
    },
    draw_card() {
        const { v } = get_active_node_args();
        draw_hand_cards(get_active_faction(), v);
        resolve_active_and_proceed(true);
    },
    draw_cards() {
        const { v } = get_active_node_args();
        draw_hand_cards(get_active_faction(), v);
        resolve_active_and_proceed(true);
    },
};
states.draw_glory = {
    inactive: 'draw from the Bag of Glory',
    prompt() {
        gen_spend_hero_points();
        view.prompt = 'Draw from the Bag of Glory.';
        gen_action('draw_glory');
    },
    draw_glory() {
        const index = random(game.bag_of_glory.length);
        const faction = game.bag_of_glory[index];
        game.glory.push(faction);
        game.glory_current_year = game.glory_current_year = [
            false,
            false,
            false,
        ];
        game.glory_current_year[faction] = true;
        array_remove(game.bag_of_glory, index);
        logi(`Pulled T${faction} from the Bag`);
        resolve_active_and_proceed(true);
    },
};
states.end_of_year_discard = {
    inactive: 'discard cards from hand and tableau',
    prompt(player) {
        const faction_id = player_faction_map[player];
        const { d: discarded } = get_active_node_args();
        const hand = game.hands[faction_id];
        const hand_limit = get_hand_limit(faction_id);
        const needs_to_discard_from_hand = hand.length > hand_limit;
        if (needs_to_discard_from_hand) {
            for (let c of hand)
                gen_action_card(c);
        }
        const tableau = game.tableaus[faction_id];
        const needs_to_discard_from_tableau = tableau.length > game.year;
        if (needs_to_discard_from_tableau) {
            for (let c of tableau)
                gen_action_card(c);
        }
        if (needs_to_discard_from_hand && needs_to_discard_from_tableau) {
            view.prompt = 'Discard a card from your hand or tableau';
        }
        else if (needs_to_discard_from_hand || needs_to_discard_from_tableau) {
            view.prompt = `Discard a card from your ${needs_to_discard_from_hand ? 'hand' : 'tableau'}`;
        }
        else {
            view.prompt = 'Confirm discard';
            view.actions.confirm = 1;
        }
        if (discarded[faction_id].h.length > 0 || discarded[faction_id].t.length > 0) {
            view.actions.undo = 1;
        }
    },
    card(c, player) {
        const faction_id = player_faction_map[player];
        const { d: discarded } = get_active_node_args();
        if (game.hands[faction_id].includes(c)) {
            game.hands[faction_id] = game.hands[faction_id].filter((id) => id !== c);
            discarded[faction_id].h.push(c);
        }
        else if (game.tableaus[faction_id].includes(c)) {
            game.tableaus[faction_id] = game.tableaus[faction_id].filter((id) => id !== c);
            discarded[faction_id].t.push(c);
        }
        game.discard[faction_id].push(c);
    },
    undo(_, player) {
        const faction_id = player_faction_map[player];
        const last_discarded = game.discard[faction_id].pop();
        const { d: discarded } = get_active_node_args();
        if (discarded[faction_id].h.includes(last_discarded)) {
            game.hands[faction_id].push(last_discarded);
            discarded[faction_id].h.pop();
        }
        else {
            game.tableaus[faction_id].push(last_discarded);
            discarded[faction_id].t.pop();
        }
    },
    confirm(_, player) {
        const faction_id = player_faction_map[player];
        const { d: discarded } = get_active_node_args();
        log_br();
        log(player + " discarded:");
        for (let c of discarded[faction_id].t)
            log(">C" + c);
        const n = discarded[faction_id].h.length;
        if (n === 1)
            log(">" + n + " card from hand");
        else if (n > 1)
            log(">" + n + " cards from hand");
        else
            log(">No cards from hand");
        set_delete(game.active, player);
        if (game.active.length === 0) {
            resolve_active_and_proceed();
        }
    },
};
states.hero_points = {
    inactive: 'gain Hero points',
    auto_resolve() {
        const { src, v } = get_active_node_args();
        if (src !== 'fascist_test') {
            return false;
        }
        if (v < 0) {
            lose_hero_points(get_active_faction(), v);
        }
        else {
            gain_hero_points(get_active_faction(), v);
        }
        return true;
    },
    prompt() {
        gen_spend_hero_points();
        const value = get_active_node_args().v;
        if (value < 0) {
            view.prompt =
                value < -1
                    ? `Lose ${Math.abs(value)} Hero points`
                    : 'Lose 1 Hero point';
            gen_action('lose_hp');
            return;
        }
        if (game.hero_points[POOL_ID] > 0) {
            view.prompt =
                value > 1 ? `Gain ${value} Hero points.` : 'Gain 1 Hero point.';
            gen_action('gain_hp');
        }
        else {
            view.prompt = 'No Hero points available in pool.';
            gen_action('skip');
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    gain_hp() {
        const value = get_active_node_args().v;
        gain_hero_points(get_active_faction(), value);
        resolve_active_and_proceed();
    },
    lose_hp() {
        const value = get_active_node_args().v;
        lose_hero_points(get_active_faction(), value);
        resolve_active_and_proceed();
    },
    skip() {
        resolve_active_and_proceed();
    },
};
states.game_over = {
    get inactive() {
        return game.victory;
    },
    prompt() {
        view.prompt = game.victory;
    },
};
function resolve_player_with_most_hero_points(faction) {
    const value = get_active_node_args().v;
    if (value < 0) {
        lose_hero_points(faction, value);
    }
    else {
        gain_hero_points(faction, value);
    }
    resolve_active_and_proceed();
}
states.select_player_with_most_hero_points = {
    inactive: 'choose a Player',
    prompt() {
        gen_spend_hero_points();
        const { v } = get_active_node_args();
        view.prompt =
            v < 0
                ? 'Choose player to lose Hero points.'
                : 'Choose player to gain Hero points.';
        const factions = get_factions_with_most_hero_poins();
        for (let faction_id of factions) {
            gen_action(faction_player_map[faction_id]);
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    Anarchist() {
        resolve_player_with_most_hero_points(data_1.ANARCHISTS_ID);
    },
    Communist() {
        resolve_player_with_most_hero_points(data_1.COMMUNISTS_ID);
    },
    Moderate() {
        resolve_player_with_most_hero_points(data_1.MODERATES_ID);
    },
};
states.move_track = {
    inactive: 'move a Track',
    prompt() {
        gen_spend_hero_points();
        const node = get_active_node();
        const track = node.a.t;
        const value = node.a.v;
        const name = track === data_1.LIBERTY_OR_COLLECTIVIZATION
            ? 'Liberty OR Collectivization'
            : tracks[track].name;
        if (value === 1)
            view.prompt = `Move ${name} one step up.`;
        else if (value == -1)
            view.prompt = `Move ${name} one step down.`;
        else if (value > 0)
            view.prompt = `Move ${name} ${value} steps up.`;
        else
            view.prompt = `Move ${name} ${-value} steps down.`;
        if (track === data_1.GOVERNMENT && value === data_1.TOWARDS_CENTER) {
            view.prompt = `Move ${name} towards center.`;
        }
        else if (track === data_1.GOVERNMENT && value === data_1.AWAY_FROM_CENTER) {
            view.prompt = `Move ${name} away from center.`;
        }
        let can_move_track = false;
        if (track === data_1.LIBERTY_OR_COLLECTIVIZATION) {
            can_move_track = gen_move_track(data_1.LIBERTY, game.tracks[data_1.LIBERTY] + value) || can_move_track;
            can_move_track = gen_move_track(data_1.COLLECTIVIZATION, game.tracks[data_1.COLLECTIVIZATION] + value) || can_move_track;
        }
        else if (track === data_1.GOVERNMENT &&
            (value === data_1.TOWARDS_CENTER || value === data_1.AWAY_FROM_CENTER)) {
            const direction = get_government_track_direction(value);
            can_move_track = gen_move_track(track, game.tracks[track] + direction) || can_move_track;
        }
        else {
            can_move_track = gen_move_track(track, game.tracks[track] + value) || can_move_track;
        }
        if (!can_move_track) {
            gen_action('skip');
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    tr0(x) {
        move_track_to(0, x);
        resolve_active_and_proceed();
    },
    tr1(x) {
        move_track_to(1, x);
        resolve_active_and_proceed();
    },
    tr2(x) {
        move_track_to(2, x);
        resolve_active_and_proceed();
    },
    tr3(x) {
        move_track_to(3, x);
        resolve_active_and_proceed();
    },
    tr4(x) {
        move_track_to(4, x);
        resolve_active_and_proceed();
    },
    skip() {
        resolve_active_and_proceed();
    },
};
function can_move_track_up(track_id) {
    const faction = get_active_faction();
    return game.faction_turn === data_1.COMMUNISTS_ID &&
        faction === data_1.COMMUNISTS_ID &&
        track_id === data_1.GOVERNMENT
        ? false
        : true;
}
function can_move_track_down(track_id) {
    const faction = get_active_faction();
    if (game.faction_turn === data_1.ANARCHISTS_ID &&
        faction === data_1.ANARCHISTS_ID &&
        (track_id === data_1.LIBERTY || track_id === data_1.COLLECTIVIZATION)) {
        return false;
    }
    if (game.faction_turn === data_1.COMMUNISTS_ID &&
        faction === data_1.COMMUNISTS_ID &&
        track_id === data_1.SOVIET_SUPPORT) {
        return false;
    }
    if (game.faction_turn === data_1.MODERATES_ID &&
        faction === data_1.MODERATES_ID &&
        (track_id === data_1.GOVERNMENT || track_id === data_1.FOREIGN_AID)) {
        return false;
    }
    return true;
}
states.move_track_up_or_down = {
    inactive: 'move a track',
    auto_resolve() {
        const { track_id, strength } = get_active_node_args();
        const can_move_up = can_move_track_up(track_id);
        const can_move_down = can_move_track_down(track_id);
        if (can_move_up && can_move_down) {
            return false;
        }
        if (can_move_up) {
            move_track(track_id, strength);
        }
        else if (can_move_down) {
            move_track(track_id, -1 * strength);
        }
        return true;
    },
    prompt() {
        gen_spend_hero_points();
        const { track_id, strength } = get_active_node_args();
        const can_move_up = can_move_track_up(track_id);
        const can_move_down = can_move_track_down(track_id);
        const track_name = get_track_name(track_id);
        if (can_move_up)
            gen_move_track(track_id, strength);
        if (can_move_down)
            gen_move_track(track_id, -strength);
        view.prompt = `Move ${track_name}.`;
    },
    spend_hp() {
        resolve_spend_hp();
    },
    tr0(x) {
        move_track_to(0, x);
        resolve_active_and_proceed();
    },
    tr1(x) {
        move_track_to(1, x);
        resolve_active_and_proceed();
    },
    tr2(x) {
        move_track_to(2, x);
        resolve_active_and_proceed();
    },
    tr3(x) {
        move_track_to(3, x);
        resolve_active_and_proceed();
    },
    tr4(x) {
        move_track_to(4, x);
        resolve_active_and_proceed();
    },
};
states.peek_fascist_cards = {
    inactive: 'peek at Fascist cards',
    prompt() {
        gen_spend_hero_points();
        view.prompt = 'Return one card to the top of the Fascist deck.';
        view.fascist_cards = game.fascist_cards;
        for (const c of game.fascist_cards) {
            gen_action_card(c);
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    card(c) {
        game.top_of_events_deck = c;
        for (const ec of game.fascist_cards) {
            if (ec !== c) {
                game.discard[data_1.FASCIST_ID].push(ec);
            }
        }
        delete game.fascist_cards;
        resolve_active_and_proceed();
    },
};
function resolve_spend_hp() {
    log("Hero points:");
    insert_before_active_node(create_state_node('spend_hero_points', get_active_faction()));
    next();
}
function set_player_turn_prompt({ can_play_card, use_ap, use_momentum, use_morale_bonus, }) {
    if (can_play_card)
        view.prompt = "Play card for Action Points or for the Event.";
    else if (use_ap && use_morale_bonus)
        view.prompt = "Use Action Points and Morale Bonus.";
    else if (use_morale_bonus)
        view.prompt = "Use Morale Bonus.";
    else if (use_ap)
        view.prompt = "Use Action Points.";
    else if (use_momentum)
        view.prompt = "Play a second card.";
    else
        view.prompt = "Player Turn: Done.";
}
states.player_turn = {
    inactive: 'play their turn',
    prompt() {
        gen_spend_hero_points();
        const faction_id = get_active_faction();
        let { src } = get_active_node_args();
        const use_morale_bonus = game.can_use_mb === 1 && game.bonuses[data_1.MORALE_BONUS] === data_1.ON;
        const can_spend_hp = game.faction_turn === faction_id && game.hero_points[faction_id] > 0;
        const can_play_card = !game.card_played;
        const use_momentum = game.momentum === faction_id && src !== data_1.MOMENTUM;
        if (use_momentum) {
            gen_action('use_momentum');
            if (use_morale_bonus || can_play_card) {
                view.actions['use_momentum'] = 0;
            }
        }
        set_player_turn_prompt({
            can_play_card,
            can_spend_hp,
            use_ap: game.can_use_ap === 1,
            use_momentum,
            use_morale_bonus,
        });
        if (can_play_card) {
            gen_action('play_to_tableau');
            gen_action('play_for_event');
        }
        if (use_morale_bonus) {
            gen_action('use_morale_bonus');
        }
        if (!(can_play_card || use_morale_bonus || use_momentum)) {
            gen_action('end_turn');
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    end_turn() {
        const { src } = get_active_node_args();
        const faction_id = get_active_faction();
        if (game.faction_turn === faction_id) {
            game.faction_turn = null;
            game.played_card = null;
            game.can_use_ap = 0;
            game.can_use_mb = 0;
            game.engine.push(create_function_node('end_of_player_turn', { f: faction_id }));
        }
        game.selected_cards[faction_id].pop();
        if (src === data_1.MOMENTUM) {
            game.momentum = null;
        }
        resolve_active_and_proceed(true);
    },
    play_to_tableau() {
        game.card_played = 1;
        const faction = get_active_faction();
        const { strength } = play_card_to_tableau(faction);
        game.can_use_ap = 1;
        game.can_use_mb = 1;
        insert_before_active_node(create_state_node('choose_area_ap', faction, {
            strength,
        }));
        next();
    },
    play_for_event() {
        game.card_played = 1;
        const faction = get_active_faction();
        log('Played for Event:');
        const { effects } = play_card_for_event(faction);
        const node = create_effects_node(effects, 'player_event');
        node.c.push(create_function_node('trash_card', faction));
        insert_before_active_node(node);
        next();
    },
    use_momentum() {
        const faction_id = get_active_faction();
        game.selected_cards[faction_id].pop();
        game.momentum = null;
        game.can_use_ap = 0;
        game.can_use_mb = 0;
        game.engine.push(create_function_node('end_of_player_turn', { f: faction_id }));
        resolve_active_and_proceed();
    },
    use_morale_bonus() {
        log(`Morale Bonus:`);
        game.can_use_mb = 0;
        insert_before_active_node(create_state_node('activate_icon', get_active_faction()));
        next();
    },
};
states.remove_blank_marker = {
    inactive: 'remove a Blank marker',
    prompt() {
        gen_spend_hero_points();
        view.prompt = 'Remove a Blank marker';
        for (const b of game.triggered_track_effects) {
            gen_action_blank_marker(b);
        }
        if (game.triggered_track_effects.length === 0) {
            view.prompt = 'No Blank marker to remove.';
            gen_action('skip');
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    blank_marker(b) {
        const faction = get_active_faction();
        pay_hero_points(faction, 1);
        const track_id = Math.floor(b / 11);
        const space_id = b % 11;
        logp(`removed blank marker from ${get_track_name(track_id)} ${space_id}`);
        game.triggered_track_effects = game.triggered_track_effects.filter((id) => id !== b);
        game.used_medallions.push(data_1.ARCHIVES_MEDALLION_ID);
        resolve_active_and_proceed();
    },
    skip() {
        resolve_active_and_proceed();
    },
};
states.remove_attack_from_fronts = {
    inactive: 'remove attacks',
    prompt() {
        gen_spend_hero_points();
        const { f, v: card_id } = get_active_node_args();
        view.prompt =
            card_id === 6
                ? 'Remove an attack from a Front.'
                : 'Remove attacks from a Front.';
        const front_data = f ?? [];
        let is_front_with_attacks = false;
        data_1.FRONTS.forEach((id) => {
            if (game.fronts[id].value >= 0 || game.fronts[id].status !== null) {
                return;
            }
            if (card_id === 6 && front_data.includes(id)) {
                return;
            }
            is_front_with_attacks = true;
            gen_action_front(id);
        });
        if (!is_front_with_attacks) {
            view.prompt = 'No valid Front to remove attacks from.';
            gen_action('skip');
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    front(id) {
        const { f, v: card_id } = get_active_node_args();
        const removed_value = card_id === 6 ? 1 : Math.min(3, Math.abs(game.fronts[id].value));
        update_front(id, removed_value, get_active_faction());
        const fronts = f ?? [];
        fronts.push(id);
        update_active_node_args({ f: fronts });
        if (card_id === 6 && fronts.length === 3) {
            resolve_active_and_proceed();
        }
        else if (card_id === 39 || card_id === 16) {
            insert_after_active_node(create_state_node('attack_front', get_active_faction(), {
                t: data_1.ANY,
                v: card_id === 39 ? -2 : -1 * removed_value,
                n: card_id === 16 ? id : undefined,
                src: 'player_event'
            }));
            resolve_active_and_proceed();
        }
    },
    skip() {
        const { f, v: card_id } = get_active_node_args();
        const values = f ?? [];
        if (card_id === 39 && values.length > 0) {
            insert_after_active_node(create_state_node('attack_front', get_active_faction(), {
                t: data_1.ANY,
                v: -2,
                src: 'player_event'
            }));
        }
        resolve_active_and_proceed();
    },
};
states.return_card = {
    inactive: 'return a card to their hand',
    prompt() {
        const faction = get_active_faction();
        gen_spend_hero_points();
        view.prompt = 'Return a card to your hand.';
        let possible = false;
        for (let c of game.trash[faction]) {
            if (c !== game.played_card) {
                gen_action_card(c);
                possible = true;
            }
        }
        if (!possible) {
            view.prompt = 'No card in trash to return to your hand.';
            gen_action('skip');
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    card(c) {
        const faction = get_active_faction();
        array_remove(game.trash[faction], game.trash[faction].indexOf(c));
        game.hands[faction].push(c);
        logp(`returned a card to their hand`);
        resolve_active_and_proceed();
    },
    skip() {
        resolve_active_and_proceed();
    },
};
function gen_spend_hero_points_move_track(track_id, change) {
    for (let i = 1; i <= change; ++i) {
        gen_move_track_change(track_id, i);
    }
}
function pay_hero_points_to_move_track(track_id, new_value) {
    const cost_per_step = [3, 3, 4, 2, 2];
    const change = Math.abs(game.tracks[track_id] - new_value);
    pay_hero_points(get_active_faction(), cost_per_step[track_id] * change);
}
states.spend_hero_points = {
    inactive: 'spend Hero points',
    prompt() {
        const hero_points = game.hero_points[get_active_faction()];
        view.prompt = `Spend up to ${hero_points} Hero points.`;
        const faction = get_active_faction();
        if (hero_points === 0) {
            return;
        }
        gen_action('draw_card');
        if (can_use_medallion(data_1.ARCHIVES_MEDALLION_ID, faction)) {
            gen_action('remove_blank_marker');
            if (game.triggered_track_effects.length === 0) {
                view.actions['remove_blank_marker'] = 0;
            }
        }
        if (can_use_medallion(data_1.VOLUNTEERS_MEDALLION_ID, faction)) {
            gen_action('add_to_front');
        }
        if (hero_points < 2) {
            return;
        }
        for (const bonus of bonuses) {
            if (game.bonuses[bonus] === data_1.OFF) {
                gen_action_bonus(bonus);
            }
        }
        gen_spend_hero_points_move_track(data_1.FOREIGN_AID, Math.floor(hero_points / 2));
        gen_spend_hero_points_move_track(data_1.SOVIET_SUPPORT, Math.floor(hero_points / 2));
        if (hero_points < 3) {
            return;
        }
        gen_spend_hero_points_move_track(data_1.COLLECTIVIZATION, Math.floor(hero_points / 3));
        gen_spend_hero_points_move_track(data_1.LIBERTY, Math.floor(hero_points / 3));
        if (hero_points < 4) {
            return;
        }
        gen_spend_hero_points_move_track(data_1.GOVERNMENT, Math.floor(hero_points / 4));
    },
    add_to_front() {
        const faction = get_active_faction();
        pay_hero_points(faction, 1);
        insert_after_active_node(create_state_node('add_to_front', faction, {
            t: data_1.ANY,
            v: 1,
        }));
        resolve_active_and_proceed();
    },
    bonus(b) {
        update_active_node_args({
            turn_on_bonus: false,
        });
        update_bonus(b, data_1.ON);
        pay_hero_points(get_active_faction(), 2);
        resolve_active_and_proceed();
    },
    draw_card() {
        const faction = get_active_faction();
        pay_hero_points(faction, 1);
        draw_hand_cards(faction, 1);
        resolve_active_and_proceed();
    },
    remove_blank_marker() {
        const faction = get_active_faction();
        if (game.used_medallions) {
            game.used_medallions.push(data_1.ARCHIVES_MEDALLION_ID);
        }
        else {
            game.used_medallions = [data_1.ARCHIVES_MEDALLION_ID];
        }
        insert_after_active_node(create_state_node('remove_blank_marker', faction));
        resolve_active_and_proceed();
    },
    tr0(x) {
        pay_hero_points_to_move_track(data_1.LIBERTY, x);
        move_track_to(0, x);
        resolve_active_and_proceed();
    },
    tr1(x) {
        pay_hero_points_to_move_track(data_1.COLLECTIVIZATION, x);
        move_track_to(1, x);
        resolve_active_and_proceed();
    },
    tr2(x) {
        pay_hero_points_to_move_track(data_1.GOVERNMENT, x);
        move_track_to(2, x);
        resolve_active_and_proceed();
    },
    tr3(x) {
        pay_hero_points_to_move_track(data_1.SOVIET_SUPPORT, x);
        move_track_to(3, x);
        resolve_active_and_proceed();
    },
    tr4(x) {
        pay_hero_points_to_move_track(data_1.FOREIGN_AID, x);
        move_track_to(4, x);
        resolve_active_and_proceed();
    },
};
states.swap_card_tableau_hand = {
    inactive: 'swap cards',
    prompt() {
        gen_spend_hero_points();
        view.prompt = 'Swap a card in your tableau with a card in your hand.';
        const faction = get_active_faction();
        const selected_cards = game.selected_cards[faction];
        const hand = game.hands[faction];
        const tableau = game.tableaus[faction];
        const { s: selected_at_start } = get_active_node_args();
        gen_action('skip');
        if (tableau.length === 0) {
            view.prompt = 'No card in your tableau to swap.';
            return;
        }
        if (hand.length === 1) {
            view.prompt = 'No card in your hand to swap.';
            return;
        }
        if (selected_cards.length === selected_at_start) {
            for (const c of hand) {
                if (!selected_cards.includes(c)) {
                    gen_action_card(c);
                }
            }
        }
        if (selected_cards.length === selected_at_start + 1) {
            for (const c of tableau) {
                gen_action_card(c);
            }
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    card(c) {
        const faction = get_active_faction();
        const selected_cards = game.selected_cards[faction];
        selected_cards.push(c);
        const { s: selected_at_start } = get_active_node_args();
        if (selected_cards.length === selected_at_start + 2) {
            const hand = game.hands[faction];
            const tableau = game.tableaus[faction];
            array_remove_item(hand, selected_cards[selected_at_start + 0]);
            array_remove_item(tableau, selected_cards[selected_at_start + 1]);
            hand.push(selected_cards[selected_at_start + 1]);
            tableau.push(selected_cards[selected_at_start + 0]);
            log(">C" + selected_cards[selected_at_start + 0] + " to tableau");
            log(">C" + selected_cards[selected_at_start + 1] + " to hand");
            game.selected_cards[faction].length = selected_at_start;
            resolve_active_and_proceed();
        }
    },
    skip() {
        const faction = get_active_faction();
        const { s: selected_at_start } = get_active_node_args();
        game.selected_cards[faction].length = selected_at_start;
        ;
        resolve_active_and_proceed();
    },
};
function resolve_take_hero_points(faction) {
    const { v } = get_active_node_args();
    const amount = Math.min(v, game.hero_points[faction]);
    lose_hero_points(faction, amount);
    gain_hero_points(get_active_faction(), amount);
    resolve_active_and_proceed();
}
states.take_hero_points = {
    inactive: 'take Hero points',
    prompt() {
        gen_spend_hero_points();
        const { v } = get_active_node_args();
        view.prompt =
            v === 1
                ? 'Take a Hero point from any player.'
                : `Take ${v} Hero points from any player.`;
        const active_faction = get_active_faction();
        let target_exists = false;
        for (const faction of role_ids) {
            if (faction !== active_faction && game.hero_points[faction] > 0) {
                gen_action(faction_player_map[faction]);
                target_exists = true;
            }
        }
        if (!target_exists) {
            view.prompt =
                'No Hero points to take from another player.';
            gen_action('skip');
        }
    },
    spend_hp() {
        resolve_spend_hp();
    },
    Anarchist() {
        resolve_take_hero_points(data_1.ANARCHISTS_ID);
    },
    Communist() {
        resolve_take_hero_points(data_1.COMMUNISTS_ID);
    },
    Moderate() {
        resolve_take_hero_points(data_1.MODERATES_ID);
    },
    skip() {
        resolve_active_and_proceed();
    },
};
function trash_card(faction) {
    const index = game.selected_cards[faction].length - 1;
    const card_id = game.selected_cards[faction][index];
    array_remove(game.hands[faction], game.hands[faction].indexOf(card_id));
    game.trash[faction].push(card_id);
    resolve_active_and_proceed();
}
states.use_organization_medallion = {
    inactive: 'use Organization Medallion',
    prompt() {
        gen_spend_hero_points();
        view.prompt = 'Use Organization Medallion?';
        gen_action('yes');
        gen_action('no');
    },
    spend_hp() {
        resolve_spend_hp();
    },
    yes() {
        const faction = get_active_faction();
        pay_hero_points(faction, 1);
        game.used_medallions.push(data_1.ORGANIZATION_MEDALLION_ID);
        let { t, v } = get_active_node_args();
        if (v > game.tracks[t]) {
            v++;
        }
        else {
            v--;
        }
        move_track(t, v - game.tracks[t]);
        resolve_active_and_proceed();
    },
    no() {
        const { t, v } = get_active_node_args();
        move_track(t, v);
        resolve_active_and_proceed();
    },
};
states.use_strategy_medallion = {
    inactive: 'use Strategy Medallion',
    prompt() {
        gen_spend_hero_points();
        view.prompt = 'Use Strategy Medallion?';
        gen_action('yes');
        gen_action('no');
    },
    spend_hp() {
        resolve_spend_hp();
    },
    yes() {
        game.used_medallions.push(data_1.STRATEGY_MEDALLION_ID);
        const { f } = get_active_node_args();
        const faction = get_active_faction();
        update_front(f, 1, faction);
        resolve_active_and_proceed();
    },
    no() {
        resolve_active_and_proceed();
    },
};
function card1_event2() {
    const value = game.tracks[data_1.FOREIGN_AID] >= 6 ? 3 : 2;
    insert_after_active_node(resolve_effect((0, data_1.create_effect)('front', data_1.NORTHERN, value), 'player_event'));
    resolve_active_and_proceed();
}
function card3_event2() {
    const value = game.tracks[data_1.FOREIGN_AID] >= 8 ? 2 : 1;
    insert_after_active_node(resolve_effect((0, data_1.create_effect)('track', data_1.GOVERNMENT, value), 'player_event'));
    resolve_active_and_proceed();
}
function card10_event2() {
    if (game.tracks[data_1.FOREIGN_AID] >= 6) {
        insert_after_active_node(resolve_effect((0, data_1.create_effect)('draw_card', data_1.SELF, 2), 'player_event'));
    }
    resolve_active_and_proceed();
}
function card16_event2() {
    const value = game.tracks[data_1.GOVERNMENT] >= 6 ? 4 : 3;
    insert_after_active_node(resolve_effect((0, data_1.create_effect)('track', data_1.FOREIGN_AID, value), 'player_event'));
    resolve_active_and_proceed();
}
function card17_event3() {
    const value = game.tracks[data_1.GOVERNMENT] >= 6 ? -4 : -3;
    insert_after_active_node(resolve_effect((0, data_1.create_effect)('track', data_1.COLLECTIVIZATION, value), 'player_event'));
    resolve_active_and_proceed();
}
function card20_event3() {
    const value = game.tracks[data_1.SOVIET_SUPPORT] >= 6 ? 2 : 1;
    insert_after_active_node(create_seq_node([
        resolve_effect((0, data_1.create_effect)('front', data_1.MADRID, value), 'player_event'),
        resolve_effect((0, data_1.create_effect)('front', data_1.SOUTHERN, value), 'player_event'),
    ]));
    resolve_active_and_proceed();
}
function card22_event3() {
    const value = game.tracks[data_1.SOVIET_SUPPORT] >= 8 ? -3 : -3;
    insert_after_active_node(resolve_effect((0, data_1.create_effect)('track', data_1.GOVERNMENT, value), 'player_event'));
    resolve_active_and_proceed();
}
function card23_event1() {
    const value = game.tracks[data_1.SOVIET_SUPPORT] >= 6 ? 4 : 3;
    insert_after_active_node(resolve_effect((0, data_1.create_effect)('front', data_1.ANY, value), 'player_event'));
    resolve_active_and_proceed();
}
function card26_event1() {
    game.active_abilities.push(data_1.COMMUNIST_EXTRA_HERO_POINT);
    resolve_active_and_proceed();
}
function card29_event2() {
    const value = game.tracks[data_1.GOVERNMENT] <= 5 ? -3 : -2;
    insert_after_active_node(resolve_effect((0, data_1.create_effect)('track', data_1.LIBERTY, value), 'player_event'));
    resolve_active_and_proceed();
}
function card35_event2() {
    const value = game.tracks[data_1.GOVERNMENT] <= 5 ? 2 : 1;
    insert_after_active_node(resolve_effect((0, data_1.create_effect)('track', data_1.SOVIET_SUPPORT, value), 'player_event'));
    resolve_active_and_proceed();
}
function card42_event3() {
    game.active_abilities.push(data_1.ANARCHIST_EXTRA_HERO_POINT);
    resolve_active_and_proceed();
}
function card45_event2() {
    if (game.tracks[data_1.LIBERTY] >= 6) {
        insert_after_active_node(resolve_effect((0, data_1.create_effect)('track', data_1.COLLECTIVIZATION, 1), 'player_event'));
    }
    resolve_active_and_proceed();
}
function card46_event3() {
    game.fascist_cards = [];
    for (let i = 0; i < 3; ++i) {
        game.fascist_cards.push(draw_fascist_card());
    }
    resolve_active_and_proceed();
}
function card50_event2() {
    const value = game.tracks[data_1.COLLECTIVIZATION] >= 8 ? 3 : 2;
    insert_after_active_node(resolve_effect((0, data_1.create_effect)('front', data_1.ARAGON, value), 'player_event'));
    resolve_active_and_proceed();
}
function card53_event2() {
    const value = game.tracks[data_1.LIBERTY] >= 8 ? 3 : 2;
    insert_after_active_node(resolve_effect((0, data_1.create_effect)('front', data_1.ANY, value), 'player_event'));
    resolve_active_and_proceed();
}
function card54_event1() {
    const value = game.tracks[data_1.COLLECTIVIZATION] >= 8 ? 3 : 2;
    insert_after_active_node(resolve_effect((0, data_1.create_effect)('track', data_1.LIBERTY, value), 'player_event'));
    resolve_active_and_proceed();
}
function setup_return_card_from_trash() {
    resolve_active_and_proceed();
}
function add_glory(faction, amount) {
    for (let i = 0; i < amount; ++i)
        game.bag_of_glory.push(faction);
    if (game.hidden_bag) {
        if (amount > 1)
            logi(`Added ${amount} tokens to the Bag`);
        else
            logi(`Added token to the Bag`);
    }
    else {
        if (amount > 1)
            logi(`Added ${amount} T${faction} to the Bag`);
        else
            logi(`Added T${faction} to the Bag`);
    }
}
function check_initiative() {
    let initiative;
    if (game.tracks[data_1.LIBERTY] >= 6 && game.tracks[data_1.COLLECTIVIZATION] >= 6) {
        initiative = data_1.ANARCHISTS_ID;
    }
    else if (game.tracks[data_1.GOVERNMENT] <= 5) {
        initiative = data_1.COMMUNISTS_ID;
    }
    else {
        initiative = data_1.MODERATES_ID;
    }
    if (game.initiative === initiative) {
        return;
    }
    game.initiative = initiative;
    logi(`${faction_player_map[initiative]} claimed Initiative`);
}
function war_is_won() {
    let won_fronts = 0;
    for (const f of data_1.FRONTS) {
        if (game.fronts[f].value >= 1) {
            won_fronts++;
        }
    }
    return won_fronts >= 3;
}
function determine_winner() {
    const glory = {
        [data_1.ANARCHISTS_ID]: 0,
        [data_1.COMMUNISTS_ID]: 0,
        [data_1.MODERATES_ID]: 0,
    };
    for (const g of game.glory) {
        glory[g]++;
    }
    let highest_glory = 0;
    let winners = [];
    for (let f of role_ids) {
        if (glory[f] === highest_glory) {
            winners.push(f);
        }
        else if (glory[f] > highest_glory) {
            highest_glory = glory[f];
            winners = [f];
        }
    }
    if (winners.length === 1) {
        win_game(faction_player_map[winners[0]], highest_glory);
    }
    else {
        insert_after_active_node(create_state_node('break_tie_winner', game.initiative, {
            winners,
            glory: highest_glory,
        }));
    }
    resolve_active_and_proceed();
}
function end_of_turn() {
    game.fronts.forEach(front => {
        front.contributions = [];
    });
    game.active_abilities = [];
    game.used_medallions = [];
    game.first_player = null;
    if (game.turn === 4) {
        end_of_year();
    }
    else {
        game.turn++;
        start_turn();
    }
}
function end_of_year() {
    log_header('End of the Year', 't');
    if (game.year === 3) {
        const is_won = war_is_won();
        if (is_won) {
            log('The war is won!');
        }
        else {
            game_over('Fascist', 'The war is lost. All players lose the game!');
            resolve_active_and_proceed();
            return;
        }
    }
    const glory_to_draw = [0, 1, 2, 5];
    game.glory_current_year = [
        false,
        false,
        false,
    ];
    const player_order = get_player_order();
    const engine = [];
    for (let i = 0; i < glory_to_draw[game.year]; ++i) {
        engine.push(create_state_node('draw_glory', player_order[i % 3]));
    }
    engine.push(create_function_node('end_of_year_cleanup'));
    game.engine = engine;
    next(true);
}
function end_of_year_cleanup() {
    if (game.year === 3) {
        determine_winner();
        return;
    }
    const players_to_gain_hero_points = role_ids.filter((f) => !game.glory_current_year?.[f]);
    gain_hero_points_in_player_order(players_to_gain_hero_points, game.year);
    game.engine = [];
    const factions_that_must_discard = [];
    role_ids.forEach((factionId) => {
        if (game.hands[factionId].length > get_hand_limit(factionId) ||
            game.tableaus[factionId].length > game.year) {
            factions_that_must_discard.push(factionId);
        }
    });
    if (factions_that_must_discard.length > 0) {
        game.engine.push(create_state_node('end_of_year_discard', factions_that_must_discard, { d: [{ h: [], t: [] }, { h: [], t: [] }, { h: [], t: [] }] }));
    }
    game.engine.push(create_function_node('checkpoint'));
    game.engine.push(create_function_node('start_year'));
    game.top_of_events_deck = null;
    game.glory_current_year = null;
    next();
}
function gain_hero_points_in_player_order(factions, value) {
    for (const f of get_player_order()) {
        if (factions.includes(f)) {
            gain_hero_points(f, value);
        }
    }
}
function gain_hero_points(faction_id, value, skip_abilities = false) {
    if (game.hero_points[POOL_ID] === 0) {
        return;
    }
    if (!skip_abilities &&
        faction_id === data_1.ANARCHISTS_ID &&
        game.active_abilities.includes(data_1.ANARCHIST_EXTRA_HERO_POINT)) {
        value++;
        array_remove(game.active_abilities, game.active_abilities.indexOf(data_1.ANARCHIST_EXTRA_HERO_POINT));
    }
    if (!skip_abilities &&
        faction_id === data_1.COMMUNISTS_ID &&
        (game.active_abilities || []).includes(data_1.COMMUNIST_EXTRA_HERO_POINT)) {
        value++;
        game.active_abilities = (game.active_abilities || []).filter((ability) => ability !== data_1.COMMUNIST_EXTRA_HERO_POINT);
    }
    const gain = Math.min(game.hero_points[POOL_ID], value);
    game.hero_points[POOL_ID] -= gain;
    game.hero_points[faction_id] += gain;
    logi(`${get_player(faction_id)} +${gain} HP`);
}
function game_over(result, victory) {
    insert_after_active_node(create_state_node('game_over', 'None'));
    game.result = result;
    game.victory = victory;
    game.undo = [];
    log_br();
    log(game.victory);
}
function get_hand_limit(faction) {
    let hand_limit = game.year;
    if (game.medallions[faction].includes(data_1.INTELLIGENCE_MEDALLION_ID)) {
        hand_limit++;
    }
    return hand_limit;
}
function play_card_for_event(faction) {
    const index = game.selected_cards[faction].length - 1;
    const card_id = game.selected_cards[faction][index];
    const card = cards[card_id];
    game.played_card = card_id;
    return card;
}
function play_card_to_tableau(faction) {
    const index = game.selected_cards[faction].length - 1;
    const card_id = game.selected_cards[faction][index];
    const card = cards[card_id];
    game.played_card = card_id;
    array_remove(game.hands[faction], game.hands[faction].indexOf(card_id));
    game.tableaus[faction].push(card_id);
    return card;
}
function resolve_fascist_test() {
    game.fascist = 2;
    const test = get_current_event().test;
    const front = test.front;
    const status = game.fronts[front].status;
    const test_passed = status === data_1.VICTORY ||
        (status !== data_1.DEFEAT && game.fronts[front].value >= test.value);
    const hero_point_actions = [];
    log_header(front_names[front] + ' Test', 'f');
    if (test_passed) {
        log('Test successful:');
        for (const faction of get_player_order()) {
            let hero_points_gain = game.fronts[front].contributions.includes(faction)
                ? 2
                : 0;
            if (can_use_medallion(data_1.PROPAGANDA_MEDALLION_ID, faction)) {
                hero_points_gain += 2;
            }
            if (hero_points_gain > 0) {
                const node = resolve_effect((0, data_1.create_effect)('hero_points', faction, hero_points_gain), 'fascist_test');
                hero_point_actions.push(node);
            }
        }
        if (hero_point_actions.length > 0) {
            insert_after_active_node(create_seq_node(hero_point_actions));
        }
    }
    else {
        log('Test failed:');
    }
    const effect = test_passed ? test.pass : test.fail;
    const node = resolve_effect(effect, 'fascist_test');
    if (node !== null) {
        insert_after_active_node(node);
    }
    resolve_active_and_proceed();
}
function resolve_final_bid() {
    let highest_bid = 0;
    let winners = [];
    for (const f of get_player_order()) {
        let player_bid = 0;
        for (const c of game.selected_cards[f]) {
            player_bid += cards[c].strength;
        }
        log(`${faction_player_map[f]} bid ${player_bid} cards`);
        if (player_bid === highest_bid) {
            winners.push(f);
        }
        else if (player_bid > highest_bid) {
            highest_bid = player_bid;
            winners = [f];
        }
        game.hands[f] = game.hands[f].filter((c) => !game.selected_cards[f].includes(c));
        game.discard[f].concat(game.selected_cards[f]);
        game.selected_cards[f] = [];
    }
    if (winners.length === 1) {
        win_final_bid(winners[0]);
    }
    else {
        insert_after_active_node(create_state_node('break_tie_final_bid', game.initiative, { winners }));
    }
    resolve_active_and_proceed();
}
function get_fronts_to_add_to(target, not) {
    if (target === data_1.CLOSEST_TO_DEFEAT || target === data_1.CLOSEST_TO_VICTORY) {
        return get_fronts_closest_to(target);
    }
    else if (target === data_1.ANY) {
        return data_1.FRONTS.filter((id) => game.fronts[id].status === null && id !== not);
    }
    else if (game.fronts[target].status === data_1.DEFEAT) {
        return get_fronts_closest_to(data_1.CLOSEST_TO_DEFEAT);
    }
    else if (game.fronts[target].status === data_1.VICTORY) {
        return get_fronts_to_add_to(data_1.ANY);
    }
    else {
        return [target];
    }
}
function get_max_value_for_track(track_id) {
    switch (track_id) {
        case data_1.LIBERTY:
            const max_lib = game.tracks[data_1.COLLECTIVIZATION] >= 8 ? 10 : 7;
            return Math.max(max_lib, game.tracks[data_1.LIBERTY]);
        case data_1.GOVERNMENT:
            const max_gov = game.tracks[data_1.FOREIGN_AID] >= 8 ? 10 : 7;
            return Math.max(max_gov, game.tracks[data_1.GOVERNMENT]);
        case data_1.COLLECTIVIZATION:
        case data_1.SOVIET_SUPPORT:
        case data_1.FOREIGN_AID:
        default:
            return 10;
    }
}
function get_min_value_for_track(track_id) {
    switch (track_id) {
        case data_1.GOVERNMENT:
            const min_gov = game.tracks[data_1.SOVIET_SUPPORT] >= 8 ? 1 : 4;
            return Math.min(min_gov, game.tracks[data_1.GOVERNMENT]);
        case data_1.LIBERTY:
        case data_1.COLLECTIVIZATION:
        case data_1.SOVIET_SUPPORT:
        case data_1.FOREIGN_AID:
        default:
            return 0;
    }
}
function get_government_track_direction(direction) {
    const value = game.tracks[data_1.GOVERNMENT];
    if ((direction === data_1.TOWARDS_CENTER && value >= 6) ||
        (direction === data_1.AWAY_FROM_CENTER && value <= 5)) {
        return -1;
    }
    else {
        return 1;
    }
}
const track_action_name = ['tr0', 'tr1', 'tr2', 'tr3', 'tr4'];
function gen_move_track(track_id, new_value) {
    new_value = Math.max(new_value, get_min_value_for_track(track_id));
    new_value = Math.min(new_value, get_max_value_for_track(track_id));
    if (new_value === game.tracks[track_id]) {
        return false;
    }
    gen_action(track_action_name[track_id], new_value);
    return true;
}
function gen_move_track_change(track_id, change) {
    const current_value = game.tracks[track_id];
    let can_move_track = false;
    if (can_move_track_up(track_id)) {
        can_move_track = gen_move_track(track_id, current_value + change) || can_move_track;
    }
    if (can_move_track_down(track_id)) {
        can_move_track = gen_move_track(track_id, current_value - change) || can_move_track;
    }
    return can_move_track;
}
function move_track(track_id, change) {
    const current_value = game.tracks[track_id];
    let new_value = current_value + change;
    new_value = Math.max(new_value, get_min_value_for_track(track_id));
    new_value = Math.min(new_value, get_max_value_for_track(track_id));
    move_track_to(track_id, new_value);
}
function move_track_to(track_id, new_value) {
    const current_value = game.tracks[track_id];
    let change = new_value - current_value;
    game.tracks[track_id] = new_value;
    if (change > 0)
        logi(`${get_track_name(track_id)} +${change} to ${new_value}`);
    else if (change < 0)
        logi(`${get_track_name(track_id)} ${change} to ${new_value}`);
    check_initiative();
    const triggered_spaces = change > 0
        ? make_list(current_value + 1, new_value).reverse()
        : make_list(new_value, current_value - 1);
    triggered_spaces.forEach((space_id) => {
        const trigger = tracks[track_id].triggers[space_id];
        if (trigger !== null &&
            !game.triggered_track_effects.includes(get_blank_marker_id(track_id, space_id))) {
            if (space_id !== 0) {
                game.triggered_track_effects.push(get_blank_marker_id(track_id, space_id));
            }
            const node = resolve_effect(trigger, tracks[track_id].action);
            if (node !== null) {
                insert_after_active_node(node);
                insert_after_active_node(create_function_node('log_trigger', [track_id, space_id]));
            }
        }
    });
}
function pay_hero_points(faction, amount) {
    game.hero_points[faction] -= amount;
    game.hero_points[POOL_ID] += amount;
}
function can_use_medallion(medallion_id, faction) {
    faction = faction === undefined ? get_active_faction() : faction;
    const can_use = game.medallions[faction].includes(medallion_id) &&
        !game.used_medallions.includes(medallion_id);
    if (medallion_id === data_1.ORGANIZATION_MEDALLION_ID) {
        return can_use && game.hero_points[faction] > 0;
    }
    else {
        return can_use;
    }
}
function insert_use_organization_medallion_node(track_id, value) {
    const faction = get_active_faction();
    insert_after_active_node(create_state_node('use_organization_medallion', faction, {
        t: track_id,
        v: value,
    }));
}
function update_bonus(bonus_id, status) {
    game.bonuses[bonus_id] = status;
    if (status === data_1.ON)
        logi(`${bonus_names[bonus_id]} on`);
    else
        logi(`${bonus_names[bonus_id]} off`);
}
function update_front(front_id, change, faction_id = null) {
    const player_token_on_front = faction_id !== null &&
        game.fronts[front_id].contributions.includes(faction_id);
    if (game.bonuses[data_1.TEAMWORK_BONUS] === data_1.ON &&
        change > 0 &&
        faction_id !== null &&
        !player_token_on_front &&
        game.fronts[front_id].contributions.length > 0) {
        change += 1;
    }
    const value_before = game.fronts[front_id].value;
    game.fronts[front_id].value += change;
    if (change > 0)
        logi(`${front_names[front_id]} +${change}`);
    else if (change < 0)
        logi(`${front_names[front_id]} ${change}`);
    if (faction_id !== null &&
        value_before <= 0 &&
        game.fronts[front_id].value > 0) {
        gain_hero_points(faction_id, 1);
    }
    if (faction_id !== null &&
        !game.fronts[front_id].contributions.includes(faction_id)) {
        game.fronts[front_id].contributions.push(faction_id);
    }
    if (change > 0 &&
        faction_id !== undefined &&
        game.fronts[front_id].value < 10 &&
        can_use_medallion(data_1.STRATEGY_MEDALLION_ID)) {
        insert_after_active_node(create_state_node('use_strategy_medallion', get_active_faction(), {
            f: front_id,
        }));
    }
    if (game.fronts[front_id].value >= 10) {
        victory_on_a_front(front_id);
    }
    else if (game.fronts[front_id].value <= -10) {
        defeat_on_a_front(front_id);
    }
}
function defeat_on_a_front(front_id) {
    game.fronts[front_id].status = data_1.DEFEAT;
    log_br();
    log('Defeat on ' + get_front_name(front_id) + '!');
    log_br();
    if (front_id === data_1.MADRID || get_defeated_front_count() == 2) {
        game_over('Fascist', 'All players lose the game!');
        return;
    }
    insert_after_active_node(create_effects_node([
        (0, data_1.create_effect)('bonus', data_1.MORALE_BONUS, data_1.OFF),
        (0, data_1.create_effect)('track', data_1.COLLECTIVIZATION, -1),
        (0, data_1.create_effect)('track', data_1.SOVIET_SUPPORT, -1),
        (0, data_1.create_effect)('track', data_1.FOREIGN_AID, -1),
    ]));
}
function victory_on_a_front(front_id) {
    game.fronts[front_id].status = data_1.VICTORY;
    log_br();
    log('Victory on ' + get_front_name(front_id) + '!');
    log_br();
    gain_hero_points_in_player_order(game.fronts[front_id].contributions, 3);
}
function create_effects_node(effects, source) {
    const nodes = effects.reduce((accrued, current) => {
        const node = resolve_effect(current, source);
        if (node !== null) {
            accrued.push(node);
        }
        return accrued;
    }, []);
    return create_seq_node(nodes);
}
function get_faction_to_resolve_effect(effect) {
    if (effect.faction === undefined || effect.faction === null) {
        return get_active_faction();
    }
    if (effect.faction === data_1.INITIATIVE_PLAYER) {
        return game.initiative;
    }
    return effect.faction;
}
const effect_type_state_map = {
    add_card_to_tableau: 'add_card_to_tableau',
    attack: 'attack_front',
    bonus: 'change_bonus',
    front: 'add_to_front',
    medallion: 'choose_medallion',
    remove_blank_marker: 'remove_blank_marker',
    return_card: 'return_card',
    swap_card_tableau_hand: 'swap_card_tableau_hand',
    take_hero_points: 'take_hero_points',
    track: 'move_track',
};
function resolve_effect(effect, source) {
    const args = {
        t: effect.target,
        v: effect.value,
        src: source,
    };
    const faction = get_faction_to_resolve_effect(effect);
    if (effect.type === 'function') {
        return create_function_node(effect.target);
    }
    if (effect.type === 'state') {
        return create_state_node(effect.target, faction, {
            v: effect.value,
            src: source,
        });
    }
    if (effect.type === 'swap_card_tableau_hand') {
        return create_state_node('swap_card_tableau_hand', faction, {
            ...args,
            s: game.selected_cards[get_active_faction()].length
        });
    }
    let state = effect_type_state_map[effect.type];
    if (state !== undefined) {
        return create_state_node(state, faction, args);
    }
    const strategies = [
        {
            condition: effect.type === 'hero_points' &&
                effect.target === data_1.PLAYER_WITH_MOST_HERO_POINTS,
            resolve: () => {
                return create_state_node('select_player_with_most_hero_points', faction, args);
            },
        },
        {
            condition: effect.type === 'hero_points' && effect.target === data_1.ALL_PLAYERS,
            resolve: () => {
                return create_seq_node(get_player_order().map((faction) => create_state_node('hero_points', faction, args)));
            },
        },
        {
            condition: effect.type === 'hero_points' && effect.target === data_1.SELF,
            resolve: () => {
                return create_state_node('hero_points', faction, args);
            },
        },
        {
            condition: effect.type === 'hero_points' &&
                role_ids.includes(effect.target),
            resolve: () => {
                return create_state_node('hero_points', effect.target, args);
            },
        },
        {
            condition: effect.type === 'hero_points' && effect.target === data_1.INITIATIVE_PLAYER,
            resolve: () => {
                return create_state_node('hero_points', game.initiative, args);
            },
        },
        {
            condition: effect.type === 'draw_card' && effect.target === data_1.SELF,
            resolve: () => {
                return create_state_node('draw_card', faction, args);
            },
        },
        {
            condition: effect.type === 'draw_card' && effect.target === data_1.INITIATIVE_PLAYER,
            resolve: () => {
                return create_state_node('draw_card', game.initiative, args);
            },
        },
        {
            condition: effect.type === 'draw_card' &&
                role_ids.includes(effect.target),
            resolve: () => {
                return create_state_node('draw_card', effect.target, args);
            },
        },
        {
            condition: effect.type === 'draw_card' && effect.target === data_1.ALL_PLAYERS,
            resolve: () => {
                return create_seq_node(get_player_order(get_active_faction()).map((faction) => create_state_node('draw_card', faction, args)));
            },
        },
        {
            condition: effect.type === 'draw_card' && effect.target === data_1.OTHER_PLAYERS,
            resolve: () => {
                const state_nodes = get_player_order(get_active_faction()).map((faction) => create_state_node('draw_card', faction, args));
                array_remove(state_nodes, 0);
                return create_seq_node(state_nodes);
            },
        },
        {
            condition: effect.type === 'play_card',
            resolve: () => {
                return create_seq_node([
                    create_state_node('play_card', faction, { src: source }),
                ]);
            },
        },
    ];
    const strategy = strategies.find((strategy) => strategy.condition);
    if (strategy) {
        return strategy.resolve();
    }
    else {
        throw new Error('Unresolved effect: ' + effect);
    }
}
function win_final_bid(faction_id) {
    log_br();
    log(`${faction_player_map[faction_id]} won the Final Bid`);
    game.glory.push(faction_id);
}
function win_game(player, glory) {
    game_over(player, `${player} won the game with a total of ${glory} Glory!`);
}
function draw_card(deck) {
    clear_undo();
    let i = random(deck.length);
    let c = deck[i];
    set_delete(deck, c);
    return c;
}
function draw_fascist_card() {
    if (game.top_of_events_deck !== null) {
        const card_id = game.top_of_events_deck;
        game.top_of_events_deck = null;
        return card_id;
    }
    return draw_card(list_deck(data_1.FASCIST_ID));
}
function lose_hero_points(faction, value) {
    const points_lost = Math.min(game.hero_points[faction], Math.abs(value));
    game.hero_points[POOL_ID] += points_lost;
    game.hero_points[faction] -= points_lost;
    if (points_lost !== 0)
        logi(`${get_player(faction)} -${points_lost} HP`);
}
function get_fronts_closest_to(target) {
    let minValue = 100;
    let maxValue = -100;
    for (let front of game.fronts) {
        if (front.status === null) {
            minValue = Math.min(minValue, front.value);
            maxValue = Math.max(maxValue, front.value);
        }
    }
    if (minValue === 100)
        return [];
    const targetValue = target === data_1.CLOSEST_TO_DEFEAT ? minValue : maxValue;
    const closest = [];
    for (let i = 0; i < game.fronts.length; ++i)
        if (game.fronts[i].value === targetValue)
            closest.push(i);
    return closest;
}
function log_br() {
    if (game.log.length > 0 && game.log[game.log.length - 1] !== '')
        game.log.push('');
}
function log(msg) {
    game.log.push(msg);
}
function log_header(msg, prefix) {
    log_br();
    log(`#${prefix} ${msg}`);
    log_br();
}
function log_trigger(args) {
    let [track_id, space_id] = args;
    log(`Trigger ${get_track_name(track_id)} ${space_id}:`);
    resolve_active_and_proceed();
}
function logi(msg) {
    log(">" + msg);
}
function logp(msg) {
    log(">" + game.active + " " + msg);
}
function get_active_faction() {
    return player_faction_map[game.active];
}
function get_blank_marker_id(track_id, space_id) {
    return track_id * 11 + space_id;
}
function get_front_name(id) {
    return front_names[id];
}
function get_current_event_id() {
    return game.current_events[game.current_events.length - 1];
}
function get_current_event() {
    return cards[get_current_event_id()];
}
function get_defeated_front_count() {
    let count = 0;
    for (const front_id of data_1.FRONTS) {
        if (game.fronts[front_id].status === data_1.DEFEAT) {
            count++;
        }
    }
    return count;
}
function get_icon_count_in_tableau(icon, faction = get_active_faction()) {
    let count = 0;
    for (const c of game.tableaus[faction]) {
        const card = cards[c];
        for (const i of card.icons) {
            if (i === icon) {
                ++count;
            }
        }
    }
    return count;
}
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_next_faction_in_player_order(faction_id) {
    return get_player_order(faction_id)[1];
}
function get_previous_faction(faction_id) {
    const index = game.player_order.indexOf(faction_player_map[faction_id]);
    if (index === 0) {
        return player_faction_map[game.player_order[2]];
    }
    return player_faction_map[game.player_order[index - 1]];
}
function get_next_faction(faction_id) {
    const index = game.player_order.indexOf(faction_player_map[faction_id]);
    if (index === 2) {
        return player_faction_map[game.player_order[0]];
    }
    return player_faction_map[game.player_order[index + 1]];
}
function get_player_order_in_game(first_player = game.initiative) {
    const order = [];
    let faction = first_player;
    for (let i = 0; i < 3; ++i) {
        order.push(faction);
        faction = get_next_faction(faction);
    }
    return order;
}
function join_oxford_comma(list, conjunction) {
    let n = list.length;
    if (n == 0)
        return "nothing";
    if (n == 1)
        return list[0];
    if (n == 2)
        return list[0] + " " + conjunction + " " + list[1];
    let result = list[0];
    for (let i = 1; i < n; ++i) {
        result += ", ";
        if (i == n - 1)
            result += conjunction + " ";
        result += list[i];
    }
    return result;
}
const icon_names = {
    add_to_front: "+1 to Front",
    collectivization: "Increase Collectivization",
    foreign_aid: "Increase Foreign Aid",
    liberty: "Increase Liberty",
    soviet_support: "Increase Soviet Support",
    government: "Increase Government",
    d_collectivization: "Decrease Collectivization",
    d_foreign_aid: "Decrease Foreign Aid",
    d_government: "Decrease Government",
    d_liberty: "Decrease Liberty",
    d_soviet_support: "Decrease Soviet Support",
    government_to_center: "Move Government to Center",
    teamwork_on: "Turn on Teamwork Bonus",
    draw_card: "Draw a Card",
};
function get_icon_name(icon) {
    return icon_names[icon];
}
function get_source_name(source) {
    switch (source) {
        case 'player_event':
            return cards[game.played_card].title;
        case 'fascist_event':
            return cards[game.current_events[game.current_events.length - 1]].title;
        case 'fascist_test':
            return 'Fascist Test';
        case 'tr0': return tracks[0].name + ' Trigger';
        case 'tr1': return tracks[1].name + ' Trigger';
        case 'tr2': return tracks[2].name + ' Trigger';
        case 'tr3': return tracks[3].name + ' Trigger';
        case 'tr4': return tracks[4].name + ' Trigger';
        case 'track_icon':
            return 'Track Trigger';
        case data_1.MOMENTUM:
            return 'Momentum';
    }
    return source;
}
function get_factions_with_most_hero_poins() {
    let most_hero_points = null;
    let faction_ids = [];
    game.hero_points.forEach((value, id) => {
        if (id === POOL_ID) {
            return;
        }
        if (most_hero_points === null || value > most_hero_points) {
            most_hero_points = value;
            faction_ids = [id];
        }
        else if (most_hero_points === value) {
            faction_ids.push(id);
        }
    });
    return faction_ids;
}
function get_track_name(track_id) {
    return tracks[track_id].name;
}
function make_list(first, last) {
    let list = [];
    for (let i = first; i <= last; i++)
        list.push(i);
    return list;
}
function list_deck(id) {
    const deck = [];
    const card_list = id === data_1.FASCIST_ID ? fascist_decks[game.year] : faction_cards[id];
    card_list.forEach((card) => {
        if (id === data_1.FASCIST_ID) {
            if (game.current_events.includes(card))
                return;
            if (game.discard[id].includes(card))
                return;
        }
        else if (game.hands[id].includes(card) ||
            game.discard[id].includes(card) ||
            game.tableaus[id].includes(card) ||
            game.trash[id].includes(card)) {
            return;
        }
        deck.push(card);
    });
    return deck;
}
function draw_medallions() {
    const medallion_ids = make_list(0, 8);
    log("Medallions:");
    for (let m = 0; m < 5; ++m) {
        let i = random(medallion_ids.length);
        let r = medallion_ids[i];
        set_delete(medallion_ids, r);
        game.medallions[POOL_ID].push(r);
        logi("M" + r);
    }
}
function clear_undo() {
    if (game.undo) {
        game.undo.length = 0;
    }
}
function push_undo() {
    if (game.undo) {
        let copy = {};
        for (let k in game) {
            let v = game[k];
            if (k === 'undo')
                continue;
            else if (k === 'log')
                v = v.length;
            else if (typeof v === 'object' && v !== null)
                v = object_copy(v);
            copy[k] = v;
        }
        game.undo.push(copy);
    }
    return game.undo;
}
function pop_undo() {
    if (game.undo) {
        let save_log = game.log;
        let save_undo = game.undo;
        game = save_undo.pop();
        save_log.length = game.log;
        game.log = save_log;
        game.undo = save_undo;
    }
}
function random(range) {
    return (game.seed = (game.seed * 200105) % 34359738337) % range;
}
function object_copy(original) {
    if (Array.isArray(original)) {
        let n = original.length;
        let copy = new Array(n);
        for (let i = 0; i < n; ++i) {
            let v = original[i];
            if (typeof v === 'object' && v !== null)
                copy[i] = object_copy(v);
            else
                copy[i] = v;
        }
        return copy;
    }
    else {
        let copy = {};
        for (let i in original) {
            let v = original[i];
            if (typeof v === 'object' && v !== null)
                copy[i] = object_copy(v);
            else
                copy[i] = v;
        }
        return copy;
    }
}
function array_remove_item(array, item) {
    let n = array.length;
    for (let i = 0; i < n; ++i)
        if (array[i] === item)
            return array_remove(array, i);
}
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;
}
function set_delete(set, item) {
    let a = 0;
    let b = set.length - 1;
    while (a <= b) {
        const m = (a + b) >> 1;
        const x = set[m];
        if (item < x)
            b = m - 1;
        else if (item > x)
            a = m + 1;
        else
            return array_remove(set, m);
    }
    return set;
}