summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--play.js140
-rw-r--r--rules.js114
2 files changed, 251 insertions, 3 deletions
diff --git a/play.js b/play.js
index c09402d..da83033 100644
--- a/play.js
+++ b/play.js
@@ -24,6 +24,13 @@ const REGION_NAMES = [
"Northeast"
]
+const PURPLE = 1
+const YELLOW = 2
+const PURPLE_OR_YELLOW = 3
+const RED = 4
+const GREEN_CHECK = 5
+const RED_X = 6
+
const region_count = 6
const us_states_count = region_count * 8
const card_count = 128
@@ -40,6 +47,7 @@ let ui = {
pieces: document.getElementById("pieces"),
support_campaigner: [],
opposition_campaigner: [],
+ cubes: [],
cards: [ null ],
us_states: [ null ],
regions: [ null ],
@@ -106,6 +114,57 @@ const LAYOUT = {
const US_STATES_LAYOUT = [ null ]
const REGIONS_LAYOUT = [ null ]
+// bits
+
+// RED cubes (6 bits), YELLOW cubes (7 bits), PURPLE cubes (7 bits), RED_X (1 bit), GREEN_CHECK (1 bit),
+const GREEN_CHECK_SHIFT = 0
+const GREEN_CHECK_MASK = 1 << GREEN_CHECK_SHIFT
+
+const RED_X_SHIFT = 1
+const RED_X_MASK = 1 << RED_X_SHIFT
+
+const PURPLE_SHIFT = 2
+const PURPLE_MASK = 127 << PURPLE_SHIFT
+
+const YELLOW_SHIFT = 9
+const YELLOW_MASK = 127 << YELLOW_SHIFT
+
+const RED_SHIFT = 16
+const RED_MASK = 63 << RED_SHIFT
+
+function is_green_check(u) {
+ return (view.us_states[u] & GREEN_CHECK_MASK) === GREEN_CHECK_MASK
+}
+
+function is_red_x(u) {
+ return (view.us_states[u] & RED_X_MASK) === RED_X_MASK
+}
+
+function purple_cubes(u) {
+ return (view.us_states[u] & PURPLE_MASK) >> PURPLE_SHIFT
+}
+
+function yellow_cubes(u) {
+ return (view.us_states[u] & YELLOW_MASK) >> YELLOW_SHIFT
+}
+
+function red_cubes(u) {
+ return (view.us_states[u] & RED_MASK) >> RED_SHIFT
+}
+
+function support_cubes(u) {
+ return purple_cubes(u) + yellow_cubes(u)
+}
+
+function color_cubes(cube, u) {
+ if (cube === PURPLE)
+ return purple_cubes(u)
+ else if (cube === YELLOW)
+ return yellow_cubes(u)
+ else
+ return red_cubes(u)
+}
+
// CARD MENU
var card_action_menu = Array.from(document.getElementById("popup").querySelectorAll("li[data-action]")).map(e => e.dataset.action)
@@ -368,6 +427,16 @@ function build_user_interface() {
create_campaigner('red1', 1),
create_campaigner('red2', 2),
]
+
+ for (let i = 0; i < 190; ++i) {
+ // XXX do we need to set the color here?
+ // let color = (i < 65) ? "purple" : (i < 130) ? "yellow" : "red"
+ elt = ui.cubes[i] = create("div", {
+ className: `piece cube`,
+ onmousedown: on_click_cube,
+ })
+ document.getElementById("pieces").appendChild(elt)
+ }
}
function on_focus_card_tip(card_number) { // eslint-disable-line no-unused-vars
@@ -433,6 +502,27 @@ function opposition_info() {
return `${pluralize(view.opposition_buttons, 'button')}, ${pluralize(view.opposition_hand, 'card')} in hand`
}
+function layout_cubes(list, xorig, yorig) {
+ const dx = 12
+ const dy = 8
+ if (list.length > 0) {
+ let ncol = Math.round(Math.sqrt(list.length))
+ let nrow = Math.ceil(list.length / ncol)
+ function place_cube(row, col, e, z) {
+ let x = xorig - (row * dx - col * dx) - 10 + (nrow-ncol) * 6
+ let y = yorig - (row * dy + col * dy) - 13 + (nrow-1) * 8
+ e.style.left = x + "px"
+ e.style.top = y + "px"
+ e.style.zIndex = z
+ }
+ let z = 50
+ let i = 0
+ for (let row = 0; row < nrow; ++row)
+ for (let col = 0; col < ncol && i < list.length; ++col)
+ place_cube(row, col, list[list.length-(++i)], z--)
+ }
+}
+
function on_update() { // eslint-disable-line no-unused-vars
console.log("VIEW", view)
@@ -485,6 +575,7 @@ function on_update() { // eslint-disable-line no-unused-vars
for (let c of view.out_of_play)
document.getElementById("out_of_play").appendChild(ui.cards[c])
+ // TODO Replace with stacked cards
for (let id of ['persistent_turn', 'persistent_game', 'persistent_ballot']) {
document.getElementById(id).replaceChildren()
for (let c of view[id] || []) {
@@ -523,7 +614,6 @@ function on_update() { // eslint-disable-line no-unused-vars
}
for (let i = 0; i < ui.opposition_campaigner.length; ++i) {
// TODO Cleanup
- ui.opposition_campaigner[i].classList.toggle("hide", !view.opposition_campaigner[i])
let campaigner_region = view.opposition_campaigner[i]
if (campaigner_region) {
ui.pieces.appendChild(ui.opposition_campaigner[i])
@@ -535,6 +625,54 @@ function on_update() { // eslint-disable-line no-unused-vars
}
}
+ let ci = 0
+ for (let i = 1; i < ui.us_states.length; ++i) {
+ // TODO Cleanup
+ if (view.us_states[i]) {
+ let state_cubes = []
+ let cube = null
+ console.log("US_STATE", i, purple_cubes(i), yellow_cubes(i), red_cubes(i), is_green_check(i), is_red_x(i))
+ for (let c = 0; c < purple_cubes(i); ++c) {
+ cube = ui.cubes[ci++]
+ // TODO track both state and color
+ cube.my_us_state = i
+ cube.my_cube = PURPLE
+ cube.classList.add("purple")
+ cube.classList.remove("yellow", "red")
+ state_cubes.push(cube)
+ ui.pieces.appendChild(cube)
+ console.log("add purple", cube)
+ }
+ for (let c = 0; c < yellow_cubes(i); ++c) {
+ cube = ui.cubes[ci++]
+ cube.my_us_state = i
+ cube.my_cube = YELLOW
+ cube.classList.add("yellow")
+ cube.classList.remove("purple", "red")
+ state_cubes.push(cube)
+ ui.pieces.appendChild(cube)
+ console.log("add yellow", cube)
+ }
+ for (let c = 0; c < red_cubes(i); ++c) {
+ cube = ui.cubes[ci++]
+ cube.my_us_state = i
+ cube.my_cube = RED
+ cube.classList.add("red")
+ cube.classList.remove("purple", "yellow")
+ state_cubes.push(cube)
+ ui.pieces.appendChild(cube)
+ console.log("add red", cube)
+ }
+ let [x, y] = US_STATES_LAYOUT[i]
+ layout_cubes(state_cubes, x, y)
+ }
+ }
+
+ // remove remaining unused cubes from DOM
+ for (let i = ci; i < ui.cubes.length; ++i) {
+ ui.cubes[i].remove()
+ }
+
action_button("commit_1_button", "+1 Button")
action_button("defer", "Defer")
diff --git a/rules.js b/rules.js
index a39775e..39614cc 100644
--- a/rules.js
+++ b/rules.js
@@ -202,8 +202,114 @@ function add_campaigner(campaigner_color, region) {
log(`Placed ${COLOR_CODE[campaigner_color]}R in R${region}`)
}
+// RED cubes (6 bits), YELLOW cubes (7 bits), PURPLE cubes (7 bits), RED_X (1 bit), GREEN_CHECK (1 bit),
+const GREEN_CHECK_SHIFT = 0
+const GREEN_CHECK_MASK = 1 << GREEN_CHECK_SHIFT
+
+const RED_X_SHIFT = 1
+const RED_X_MASK = 1 << RED_X_SHIFT
+
+const PURPLE_SHIFT = 2
+const PURPLE_MASK = 127 << PURPLE_SHIFT
+
+const YELLOW_SHIFT = 9
+const YELLOW_MASK = 127 << YELLOW_SHIFT
+
+const RED_SHIFT = 16
+const RED_MASK = 63 << RED_SHIFT
+
+function is_green_check(u) {
+ return (game.us_states[u] & GREEN_CHECK_MASK) === GREEN_CHECK_MASK
+}
+
+function set_green_check(u) {
+ game.us_states[u] |= GREEN_CHECK_MASK
+}
+
+function clear_green_check(u) {
+ game.us_states[u] &= ~GREEN_CHECK_MASK
+}
+
+function is_red_x(u) {
+ return (game.us_states[u] & RED_X_MASK) === RED_X_MASK
+}
+
+function set_red_x(u) {
+ game.us_states[u] |= RED_X_MASK
+}
+
+function clear_red_x(u) {
+ game.us_states[u] &= ~RED_X_MASK
+}
+
+function purple_cubes(u) {
+ return (game.us_states[u] & PURPLE_MASK) >> PURPLE_SHIFT
+}
+
+function set_purple_cubes(u, x) {
+ game.us_states[u] = (game.us_states[u] & ~PURPLE_MASK) | (x << PURPLE_SHIFT)
+}
+
+function yellow_cubes(u) {
+ return (game.us_states[u] & YELLOW_MASK) >> YELLOW_SHIFT
+}
+
+function set_yellow_cubes(u, x) {
+ game.us_states[u] = (game.us_states[u] & ~YELLOW_MASK) | (x << YELLOW_SHIFT)
+}
+
+function red_cubes(u) {
+ return (game.us_states[u] & RED_MASK) >> RED_SHIFT
+}
+
+function set_red_cubes(u, x) {
+ game.us_states[u] = (game.us_states[u] & ~RED_MASK) | (x << RED_SHIFT)
+}
+
+function support_cubes(u) {
+ return purple_cubes(u) + yellow_cubes(u)
+}
+
+function color_cubes(cube, u) {
+ if (cube === PURPLE)
+ return purple_cubes(u)
+ else if (cube === YELLOW)
+ return yellow_cubes(u)
+ else
+ return red_cubes(u)
+}
+
+function set_color_cubes(cube, u, x) {
+ if (cube === PURPLE)
+ set_purple_cubes(u, x)
+ else if (cube === YELLOW)
+ set_yellow_cubes(u, x)
+ else
+ set_red_cubes(u, x)
+}
+
function add_cube(cube, us_state) {
log(`Added ${COLOR_CODE[cube]}C in S${us_state}`)
+
+ if ((cube === RED && support_cubes(us_state) > 0) || (cube !== RED && red_cubes(us_state) > 0))
+ throw new Error("Can't add cube when opponent still has cubes there")
+
+ if (is_green_check(us_state) || is_red_x(us_state))
+ throw new Error("Can't add cube with green_check / red_x")
+
+ set_color_cubes(cube, us_state, color_cubes(cube, us_state) + 1)
+}
+
+function remove_cube(cube, us_state) {
+ log(`Removed ${COLOR_CODE[cube]}C from S${us_state}`)
+
+ if ((cube === PURPLE && !purple_cubes(us_state)) || (cube === YELLOW && !yellow_cubes(us_state)) || (cube === RED && !red_cubes(us_state)))
+ throw new Error("Can't remove cube that aint there")
+
+ if (is_green_check(us_state) || is_red_x(us_state))
+ throw new Error("Can't remove cube in us_state with green_check / red_x")
+
+ set_color_cubes(cube, us_state, color_cubes(cube, us_state) - 1)
}
// #endregion
@@ -285,6 +391,7 @@ exports.setup = function (seed, _scenario, _options) {
round: 0,
congress: 0,
us_states: new Array(us_states_count).fill(0),
+ nineteenth_amendment: 0,
strategy_deck: [],
strategy_draw: [],
@@ -379,7 +486,8 @@ exports.view = function(state, player) {
turn: game.turn,
round: game.round,
congress: game.congress,
- states: game.states,
+ us_states: game.us_states,
+ nineteenth_amendment: game.nineteenth_amendment,
strategy_deck: game.strategy_deck.length,
strategy_draw: game.strategy_draw,
@@ -460,7 +568,7 @@ function goto_planning_phase() {
states.planning_phase = {
inactive: "do Planning.",
prompt() {
- view.prompt = "Planning."
+ view.prompt = "Planning. Draw cards."
gen_action("draw")
},
draw() {
@@ -1380,6 +1488,7 @@ states.vm_add_cubes = {
gen_action("yellow")
}
+ // TODO remove cube if opponent has any cubes here
if (game.vm.cube_color) {
for (let s of game.vm.us_states)
gen_action_us_state(s)
@@ -1393,6 +1502,7 @@ states.vm_add_cubes = {
},
us_state(s) {
push_undo()
+ // TODO remove cube if opponent has any cubes here
add_cube(game.vm.cube_color, s)
map_incr(game.vm.added, s, 1)