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