From 357cf90cb0e56060dc3ebac92325f3792502e757 Mon Sep 17 00:00:00 2001 From: Frans Bongers Date: Tue, 24 Dec 2024 21:21:42 +0100 Subject: enable undo --- rules.js | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 167 insertions(+), 40 deletions(-) (limited to 'rules.js') diff --git a/rules.js b/rules.js index 3b085a9..f68af28 100644 --- a/rules.js +++ b/rules.js @@ -71,7 +71,10 @@ function gen_action_standee(track_id) { gen_action('standee', track_id); } function action(state, player, action, arg) { - console.log('action', state, player, action, arg); + console.log('action', player, action, arg); + if (action !== 'undo') { + state.undo = push_undo(); + } game = state; let S = states[game.state]; if (action in S) @@ -212,17 +215,27 @@ function next() { } else if (node.t === 'l') { game.state = node.s; - game.active = faction_player_map[node.p]; + const current_active = game.active; + const next_active = faction_player_map[node.p]; + if (next_active !== current_active && game.undo.length > 0) { + insert_before_active_node(create_leaf_node('confirm_turn', get_active_faction())); + game.state = 'confirm_turn'; + return; + } + game.active = next_active; } } -function resolve_active_node() { +function resolve_active_node(checkpoint = false) { const next_node = get_active_node(game.engine); if (next_node !== null) { next_node.r = resolved; } + if (checkpoint) { + clear_undo(); + } } -function resolve_active_and_proceed() { - resolve_active_node(); +function resolve_active_and_proceed(checkpoint = false) { + resolve_active_node(checkpoint); next(); } function game_view(state, player) { @@ -248,6 +261,7 @@ function game_view(state, player) { tableaus: game.tableaus, tracks: game.tracks, triggered_track_effects: game.triggered_track_effects, + undo: game.undo, used_medallions: game.used_medallions, year: game.year, }; @@ -791,6 +805,34 @@ states.choose_medallion = { resolve_active_and_proceed(); }, }; +states.confirm_turn = { + inactive: 'confirm their turn', + prompt() { + view.prompt = 'Confirm your actions or undo'; + gen_action('confirm'); + }, + confirm() { + resolve_active_and_proceed(true); + }, +}; +states.draw_card = { + inactive: 'draw a card', + prompt() { + const { v } = get_active_node_args(); + view.prompt = v === 1 ? 'Draw a card' : `Draw ${v} cards`; + gen_action(v === 1 ? 'draw_card' : 'draw_cards'); + }, + draw_card() { + const { v } = get_active_node_args(); + draw_hand_cards(get_active_faction(), v); + resolve_active_and_proceed(); + }, + draw_cards() { + const { v } = get_active_node_args(); + draw_hand_cards(get_active_faction(), v); + resolve_active_and_proceed(); + }, +}; states.end_of_year_discard = { inactive: 'discard cards from hand and tableau', prompt() { @@ -947,7 +989,7 @@ states.player_turn = { } }, done() { - resolve_active_and_proceed(); + resolve_active_and_proceed(true); }, play_for_ap() { const faction_id = get_faction_id(game.active); @@ -1137,14 +1179,6 @@ states.use_strategy_medallion = { resolve_active_and_proceed(); }, }; -function pop_undo() { - const save_log = game.log; - const save_undo = game.undo; - game = save_undo.pop(); - save_log.length = game.log; - game.log = save_log; - game.undo = save_undo; -} function add_glory(faction, amount, indent = false) { for (let i = 0; i < amount; ++i) { game.bag_of_glory.push(get_active_faction()); @@ -1560,7 +1594,14 @@ function resolve_effect(effect) { if (effect.type === 'hero_points' && effect.target === data_1.SELF) { state = 'gain_hero_points'; } - return state === undefined ? null : create_leaf_node(state, faction, args); + if (effect.type === 'draw_card' && effect.target === data_1.SELF) { + state = 'draw_card'; + } + if (state === undefined) { + console.log('----UNRESOLVED EFFECT----', effect); + return null; + } + return create_leaf_node(state, faction, args); } function win_final_bid(faction_id) { log_br(); @@ -1625,11 +1666,6 @@ function log_h3(msg) { log_br(); log('.h3 ' + msg); } -function clear_undo() { - console.log('game clear undo', game?.undo); - if (game?.undo && game.undo.length > 0) - game.undo = []; -} function get_active_faction() { return player_faction_map[game.active]; } @@ -1724,25 +1760,6 @@ function make_list(first, last) { list.push(i); return list; } -function random(range) { - return (game.seed = (game.seed * 200105) % 34359738337) % range; -} -function set_delete(set, item) { - let a = 0; - let b = set.length - 1; - while (a <= b) { - let m = (a + b) >> 1; - let x = set[m]; - if (item < x) - b = m - 1; - else if (item > x) - a = m + 1; - else { - array_remove(set, m); - return; - } - } -} function list_deck(id) { const deck = []; const card_list = id === 'fascist' ? fascist_decks[game.year] : faction_cards[id]; @@ -1754,7 +1771,8 @@ function list_deck(id) { else if (id !== 'fascist' && (game.hands[id].includes(card) || game.discard[id].includes(card) || - game.tableaus[id].includes(card))) { + game.tableaus[id].includes(card) || + game.trash[id].includes(card))) { return; } deck.push(card); @@ -1770,6 +1788,67 @@ function draw_medallions() { game.medallions.pool.push(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; + } + next(); +} +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(array, index) { let n = array.length; for (let i = index + 1; i < n; ++i) @@ -1781,3 +1860,51 @@ function array_insert(array, index, item) { array[i] = array[i - 1]; array[index] = item; } +function set_clear(set) { + set.length = 0; +} +function set_has(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 true; + } + return false; +} +function set_add(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 set; + } + return array_insert(set, a, 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; +} -- cgit v1.2.3