From c44b4d238ffbb9983a651a2d6385c32dcc964510 Mon Sep 17 00:00:00 2001 From: Mischa Untaga <99098079+MischaU8@users.noreply.github.com> Date: Tue, 7 Nov 2023 14:26:55 +0100 Subject: implementation of add_campaigners, receive/spend_buttons --- events.txt | 15 +-- play.html | 16 +++ play.js | 14 ++- rules.js | 327 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 349 insertions(+), 23 deletions(-) diff --git a/events.txt b/events.txt index 2e0d144..0bea465 100644 --- a/events.txt +++ b/events.txt @@ -2,9 +2,12 @@ CARD 1 - Seneca Falls Convention # Add 1 :purple_campaigner and 1 :yellow_campaigner in the Northeast region. Receive 2 :button and add 2 :purple_or_yellow_cube in New York. - add_campaigner 1 PURPLE NORTHEAST - add_campaigner 1 YELLOW NORTHEAST + prompt "Add 1 :purple_campaigner and 1 :yellow_campaigner in the Northeast region." + add_campaigner PURPLE NORTHEAST + add_campaigner YELLOW NORTHEAST + prompt "Receive 2 :button." receive_buttons 2 + prompt "Add 2 :purple_or_yellow_cube in New York." add_cubes 2 PURPLE_OR_YELLOW us_states("New York") CARD 2 - Property Rights for Women @@ -101,7 +104,7 @@ CARD 18 - National Woman’s Rights Convention CARD 19 - National American Woman Suffrage Association # Add 1 :purple_campaigner in the Atlantic & Appalachia region. Receive 3 :button. - add_campaigner 1 PURPLE ATLANTIC_APPALACHIA + add_campaigner PURPLE ATLANTIC_APPALACHIA receive_buttons 3 CARD 20 - Jeannette Rankin @@ -189,7 +192,7 @@ CARD 35 - Southern Strategy CARD 36 - Women’s Trade Union League # Add 1 :yellow_campaigner in the Atlantic & Appalachia region. Add 1 :congressional_marker in Congress and receive 2 :button. - add_cubes_in_each_of 1 YELLOW region_us_states(ATLANTIC_APPALACHIA) + add_campaigner YELLOW ATLANTIC_APPALACHIA add_congress 1 receive_buttons 2 @@ -277,7 +280,7 @@ CARD 52 - Miss Febb Wins the Last Vote CARD 53 - The Patriarchy # Add 1 :red_campaigner in the South region. Receive 4 :button. Add 1 :red_cube in each state in the Northeast region, the Atlantic & Appalachia region, the South region and the Midwest region. - add_campaigner 1 RED SOUTH + add_campaigner RED SOUTH receive_buttons 4 add_cubes_in_each_of 1 RED region_us_states(NORTHEAST, ATLANTIC_APPALACHIA, SOUTH, MIDWEST) @@ -373,7 +376,7 @@ CARD 70 - Old Dixie CARD 71 - NAOWS Forms # Add 1 :red_campaigner in the Northeast region. Receive 2 :button. - add_campaigner 1 NORTHEAST + add_campaigner NORTHEAST receive_buttons 2 CARD 72 - Woman and the Republic diff --git a/play.html b/play.html index 176bb74..4948e79 100644 --- a/play.html +++ b/play.html @@ -124,6 +124,12 @@ svg { position: absolute; } +path.state.action { + fill: yellow; fill-opacity: 0.8; + stroke: white; + stroke-width: 20; +} + path.state:hover { fill: white; } @@ -139,6 +145,11 @@ div.state { height: 64px; } +div.state.action { + background-color: rgba(251, 186, 0, 0.5); + border-color: white; +} + div.state:hover { background-color: #fff8; } @@ -154,6 +165,11 @@ div.region { height: 65px; } +div.region.action { + background-color: rgba(251, 186, 0, 0.5); + border-color: white; +} + div.region:hover { background-color: #fff8; } diff --git a/play.js b/play.js index 69a7669..eb8d8f2 100644 --- a/play.js +++ b/play.js @@ -301,7 +301,7 @@ function build_user_interface() { for (let s = 1; s <= us_states_count; ++s) { let us_state_css = US_STATES[s].code - elt = ui.regions[s] = document.querySelector(`#map #${us_state_css}`) + elt = ui.us_states[s] = document.querySelector(`#map #${us_state_css}`) elt.my_us_state = s elt.addEventListener("mousedown", on_click_us_state) elt.addEventListener("mouseenter", on_focus_us_state) @@ -427,16 +427,26 @@ function on_update() { // eslint-disable-line no-unused-vars } for (let i = 1; i < ui.cards.length; ++i) { - // ui.cards[i].classList.toggle("action", is_card_action('card', i)) ui.cards[i].classList.toggle("action", is_card_enabled(i)) } + for (let i = 1; i <= region_count; ++i) { + ui.regions[i].classList.toggle("action", is_region_action(i)) + } + + for (let i = 1; i <= us_states_count; ++i) { + ui.us_states[i].classList.toggle("action", is_us_state_action(i)) + } + action_button("commit_1_button", "+1 Button") action_button("defer", "Defer") action_button("match", "Match") action_button("supersede", "Supersede") action_button("draw", "Draw") + action_button("next", "Next") + action_button("purple", "Purple") + action_button("yellow", "Yellow") action_button("end_event", "End Event") action_button("skip", "Skip") diff --git a/rules.js b/rules.js index 182a5a9..ceb072f 100644 --- a/rules.js +++ b/rules.js @@ -50,6 +50,15 @@ const D4 = 4 const D6 = 6 const D8 = 8 +const COLOR_CODE = { + [PURPLE]: "P", + [YELLOW]: "Y", + [PURPLE_OR_YELLOW]: "PY", + [RED]: "R", + [GREEN_CHECK]: "GV", + [RED_X]: "RX", +} + const { CARDS } = require("./cards.js") const { US_STATES } = require("./data.js") @@ -147,6 +156,31 @@ function region_us_states_except(region, excluded) { return region_us_states(region).filter( x => !to_remove.has(x) ) } +function find_campaigners(campaigner) { + if (campaigner === PURPLE) { + return game.purple_campaigner + } else if (campaigner === YELLOW) { + return game.yellow_campaigner + } else { + return game.opposition_campaigner + } +} + +function add_campaigner(campaigner, region) { + const campaigners = find_campaigners(campaigner) + const index = campaigners.indexOf(0) + if (index !== -1) { + campaigners[index] = region + } else { + throw Error("No free campaigners") + } + log(`Placed ${COLOR_CODE[campaigner]}R in R${region}`) +} + +function add_cube(cube, us_state) { + log(`Added ${COLOR_CODE[cube]}C in S${us_state}`) +} + // #endregion // #region PUBLIC FUNCTIONS @@ -164,6 +198,14 @@ function gen_action(action, argument) { } } +function gen_action_region(r) { + gen_action("region", r) +} + +function gen_action_us_state(s) { + gen_action("us_state", s) +} + exports.action = function (state, player, action, arg) { game = state if (states[game.state] && action in states[game.state]) { @@ -555,7 +597,6 @@ function can_play_event(c) { return true } - function count_player_active_campaigners() { if (game.active === SUF) { return game.purple_campaigner.filter(value => value !== 0).length + game.yellow_campaigner.filter(value => value !== 0).length @@ -614,7 +655,6 @@ states.operations_phase = { if (!game.has_played_claimed) { // only one claimed can be played per turn for (let c of player_claimed()) { - // TODO is this the right type of event? gen_action("card_event", c) } } @@ -885,21 +925,21 @@ function vm_case() { // #region EVENTS VfW DSL function vm_add_campaigner() { - game.vm.count = vm_operand(1) - game.vm.campaigner = vm_operand(2) - game.vm.region = vm_operand(3) - goto_vm_add_campaigner() + game.vm.campaigner = vm_operand(1) + game.vm.region = vm_operand(2) + game.state = "vm_add_campaigner" } function vm_receive_buttons() { game.vm.count = vm_operand(1) - goto_vm_receive_buttons() + game.state = "vm_receive_buttons" } function vm_spend_buttons() { - // TODO assert game.vm.count = vm_operand(1) - goto_vm_spend_buttons() + game.state = "vm_spend_buttons" + if (player_buttons() < game.vm.count) + throw Error("ASSERT: Insufficient buttons") } function vm_opponent_loses_buttons() { @@ -1211,6 +1251,91 @@ states.vm_switch = { }, } +states.vm_add_campaigner = { + inactive: "add a campaigner", + prompt() { + event_prompt() + gen_action_region(game.vm.region) + }, + region(r) { + push_undo() + add_campaigner(game.vm.campaigner, r) + vm_next() + } +} + +states.vm_receive_buttons = { + inactive: "receive buttons", + prompt() { + event_prompt() + gen_action("next") + }, + next() { + push_undo() + log(`+${pluralize(game.vm.count, 'button')}.`) + if (game.active === SUF) { + game.support_buttons += game.vm.count + } else { + game.opponent_buttons += game.vm.count + } + vm_next() + } +} + +states.vm_spend_buttons = { + inactive: "spend buttons", + prompt() { + event_prompt() + gen_action("next") + }, + next() { + push_undo() + log(`-${pluralize(game.vm.count, 'button')}.`) + if (game.active === SUF) { + game.support_buttons -= game.vm.count + } else { + game.opponent_buttons -= game.vm.count + } + vm_next() + } +} + +function goto_vm_add_cubes() { + game.state = "vm_add_cubes" + if (game.vm.cubes === PURPLE_OR_YELLOW) { + game.vm.cube_color = 0 + } else { + game.vm.cube_color = game.vm.cubes + } +} + +states.vm_add_cubes = { + inactive: "add a cube", + prompt() { + event_prompt() + if (game.vm.cubes === PURPLE_OR_YELLOW) { + gen_action("purple") + gen_action("yellow") + } + if (game.vm.cube_color) { + for (let s of game.vm.us_states) + gen_action_us_state(s) + } + }, + purple() { + game.vm.cube_color = PURPLE + }, + yellow() { + game.vm.cube_color = YELLOW + }, + us_state(s) { + push_undo() + add_cube(game.vm.cube_color, s) + if (--game.vm.count === 0) + vm_next() + } +} + function can_vm_place() { for (let s of game.vm.spaces) if (can_place_cube(s, game.vm.removed)) @@ -1614,15 +1739,187 @@ function array_remove(array, index) { array.length = n - 1 } + +// insert item at index (faster than splice) +function array_insert(array, index, item) { + for (let i = array.length; i > index; --i) + array[i] = array[i - 1] + array[index] = item + return array +} + +function array_remove_pair(array, index) { + let n = array.length + for (let i = index + 2; i < n; ++i) + array[i - 2] = array[i] + array.length = n - 2 +} + +function array_insert_pair(array, index, key, value) { + for (let i = array.length; i > index; i -= 2) { + array[i] = array[i-2] + array[i+1] = array[i-1] + } + array[index] = key + array[index+1] = value +} + +function set_clear(set) { + set.length = 0 +} + +function set_has(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 + return true + } + return false +} + +function set_add(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 + return set + } + return array_insert(set, a, item) +} + +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 + return array_remove(set, m) + } + return set +} + +function set_toggle(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 + return array_remove(set, m) + } + return array_insert(set, a, item) +} + +function map_clear(map) { + map.length = 0 +} + +function map_has(map, key) { + let a = 0 + let b = (map.length >> 1) - 1 + while (a <= b) { + let m = (a + b) >> 1 + let x = map[m<<1] + if (key < x) + b = m - 1 + else if (key > x) + a = m + 1 + else + return true + } + return false +} + +function map_get(map, key, missing) { + let a = 0 + let b = (map.length >> 1) - 1 + while (a <= b) { + let m = (a + b) >> 1 + let x = map[m<<1] + if (key < x) + b = m - 1 + else if (key > x) + a = m + 1 + else + return map[(m<<1)+1] + } + return missing +} + +function map_set(map, key, value) { + let a = 0 + let b = (map.length >> 1) - 1 + while (a <= b) { + let m = (a + b) >> 1 + let x = map[m<<1] + if (key < x) + b = m - 1 + else if (key > x) + a = m + 1 + else { + map[(m<<1)+1] = value + return + } + } + array_insert_pair(map, a<<1, key, value) +} + +function map_delete(map, item) { + let a = 0 + let b = (map.length >> 1) - 1 + while (a <= b) { + let m = (a + b) >> 1 + let x = map[m<<1] + if (item < x) + b = m - 1 + else if (item > x) + a = m + 1 + else { + array_remove_pair(map, m<<1) + return + } + } +} + +function map_for_each(map, f) { + for (let i = 0; i < map.length; i += 2) + f(map[i], map[i+1]) +} + // #endregion // #region GENERATED EVENT CODE const CODE = [] CODE[1] = [ // Seneca Falls Convention - [ vm_add_campaigner, 1, PURPLE, NORTHEAST ], - [ vm_add_campaigner, 1, YELLOW, NORTHEAST ], + [ vm_prompt, "Add 1 :purple_campaigner and 1 :yellow_campaigner in the Northeast region." ], + [ vm_add_campaigner, PURPLE, NORTHEAST ], + [ vm_add_campaigner, YELLOW, NORTHEAST ], + [ vm_prompt, "Receive 2 :button." ], [ vm_receive_buttons, 2 ], + [ vm_prompt, "Add 2 :purple_or_yellow_cube in New York." ], [ vm_add_cubes, 2, PURPLE_OR_YELLOW, us_states("New York") ], [ vm_return ], ] @@ -1736,7 +2033,7 @@ CODE[18] = [ // National Woman’s Rights Convention ] CODE[19] = [ // National American Woman Suffrage Association - [ vm_add_campaigner, 1, PURPLE, ATLANTIC_APPALACHIA ], + [ vm_add_campaigner, PURPLE, ATLANTIC_APPALACHIA ], [ vm_receive_buttons, 3 ], [ vm_return ], ] @@ -1841,7 +2138,7 @@ CODE[35] = [ // Southern Strategy ] CODE[36] = [ // Women’s Trade Union League - [ vm_add_cubes_in_each_of, 1, YELLOW, region_us_states(ATLANTIC_APPALACHIA) ], + [ vm_add_campaigner, YELLOW, ATLANTIC_APPALACHIA ], [ vm_add_congress, 1 ], [ vm_receive_buttons, 2 ], [ vm_return ], @@ -1944,7 +2241,7 @@ CODE[52] = [ // Miss Febb Wins the Last Vote ] CODE[53] = [ // The Patriarchy - [ vm_add_campaigner, 1, RED, SOUTH ], + [ vm_add_campaigner, RED, SOUTH ], [ vm_receive_buttons, 4 ], [ vm_add_cubes_in_each_of, 1, RED, region_us_states(NORTHEAST, ATLANTIC_APPALACHIA, SOUTH, MIDWEST) ], [ vm_return ], @@ -2058,7 +2355,7 @@ CODE[70] = [ // Old Dixie ] CODE[71] = [ // NAOWS Forms - [ vm_add_campaigner, 1, NORTHEAST ], + [ vm_add_campaigner, NORTHEAST ], [ vm_receive_buttons, 2 ], [ vm_return ], ] -- cgit v1.2.3