summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--play.js2
-rw-r--r--rules.js238
2 files changed, 174 insertions, 66 deletions
diff --git a/play.js b/play.js
index 1812adc..20e3e90 100644
--- a/play.js
+++ b/play.js
@@ -965,6 +965,8 @@ function on_update() {
// DS buttons
action_button("conscript", "Conscript")
action_button("end_conscript", "End Conscript")
+ action_button("march", "March")
+ action_button("end_march", "End March")
// Command buttons
action_button("rally", "Rally")
diff --git a/rules.js b/rules.js
index 6cef5b5..31eb1ca 100644
--- a/rules.js
+++ b/rules.js
@@ -6,10 +6,11 @@ let view = null
/* DATA */
const data = require("./data.js")
-const space_name = data.space_name
+const SPACES = data.spaces
+const SPACE_NAME = data.space_name
function get_space_id(name) {
- return space_name.indexOf(name);
+ return SPACE_NAME.indexOf(name);
}
const S_ANDHRA = get_space_id("Andhra")
@@ -194,11 +195,12 @@ exports.setup = function (seed, scenario, _options) {
pieces: Array(104).fill(AVAILABLE), // piece locations
cavalry: [0, 0, 0],
deck: [],
- op: {
+ cmd: {
type: null,
limited: null,
free: null,
spaces: [],
+ pieces: [],
where: null,
},
decree: 0
@@ -309,7 +311,7 @@ function goto_eligible() {
end_card()
} else {
game.state = "eligible"
- game.op = {
+ game.cmd = {
limited: 0,
free: 0,
spaces: [],
@@ -349,7 +351,7 @@ function adjust_eligibility(faction) {
function goto_pass() {
push_undo()
- game.op = 0
+ game.cmd = 0
game.sa = 0
game.cylinder[game.current] = SOP_PASS
@@ -380,20 +382,38 @@ function goto_conscript() {
if (conscript_count() === 1) {
let p = find_piece(AVAILABLE, game.current, TROOPS)
log_summary_place(p)
- place_piece(p, game.op.where)
+ place_piece(p, game.cmd.where)
end_conscript_space()
} else {
- game.op.count = 0
+ game.cmd.count = 0
game.state = "conscript_space"
}
}
function end_conscript_space() {
- log_space(game.op.where, "Conscript")
+ log_space(game.cmd.where, "Conscript")
pop_summary()
game.state = "conscript"
}
+ function goto_march() {
+ init_command("March")
+ game.cmd.pieces = []
+ game.state = "march"
+ }
+
+ function goto_march_space() {
+ push_summary()
+ game.cmd_count = 0
+ game.state = "march_space"
+ }
+
+ function end_march_space() {
+ log_space(game.cmd.where, "March")
+ pop_summary()
+ game.state = "march"
+ }
+
function goto_rally() {
init_command("Rally")
game.state = "rally"
@@ -405,16 +425,16 @@ function goto_conscript() {
if (rally_count() === 1) {
let p = find_piece(AVAILABLE, game.current, ELITE)
log_summary_place(p)
- place_piece(p, game.op.where)
+ place_piece(p, game.cmd.where)
end_rally_space()
} else {
- game.op.count = 0
+ game.cmd.count = 0
game.state = "rally_space"
}
}
function end_rally_space() {
- log_space(game.op.where, "Rally")
+ log_space(game.cmd.where, "Rally")
pop_summary()
game.state = "rally"
}
@@ -460,7 +480,7 @@ states.eligible = {
lim_command() {
push_undo()
game.cylinder[game.current] = SOP_LIMITED_COMMAND
- game.op.limited = 1
+ game.cmd.limited = 1
game.state = "lim_command"
},
}
@@ -475,6 +495,7 @@ states.command_decree = {
},
build: goto_build,
conscript: goto_conscript,
+ march: goto_march,
rally: goto_rally,
rebel: goto_rebel,
}
@@ -486,6 +507,7 @@ states.event_command = {
gen_any_command()
},
conscript: goto_conscript,
+ march: goto_march,
rally: goto_rally,
rebel: goto_rebel,
}
@@ -497,6 +519,7 @@ states.lim_command = {
gen_any_command()
},
conscript: goto_conscript,
+ march: goto_march,
rally: goto_rally,
rebel: goto_rebel,
}
@@ -553,15 +576,15 @@ states.conscript = {
states.conscript_space = {
prompt() {
view.prompt = `Conscript: Place up to ${conscript_count()} Troops.`
- view.where = game.op.where
+ view.where = game.cmd.where
view.actions.next = 1
gen_place_piece(DS, TROOPS)
},
piece(p) {
log_summary_place(p)
- place_piece(p, game.op.where)
- if (++game.op.count >= conscript_count())
+ place_piece(p, game.cmd.where)
+ if (++game.cmd.count >= conscript_count())
end_conscript_space()
},
next() {
@@ -569,6 +592,52 @@ states.conscript_space = {
}
}
+states.march = {
+ prompt() {
+ view.prompt = "March: Select a space to move into."
+
+ if (can_select_cmd_space(1)) {
+ for (let s = first_space; s <= last_space; ++s) {
+ if (!is_selected_cmd_space(s) && can_march_in_space(s))
+ gen_action_space(s)
+ }
+ }
+
+ view.actions.end_march = prompt_end_cmd(1)
+ },
+ space(s) {
+ push_undo
+ select_cmd_space(s, 1)
+ goto_march_space()
+ },
+ end_march: end_command
+}
+
+states.march_space = {
+ prompt() {
+ view.prompt = "March: Move pieces from adjacent spaces."
+ view.where = game.cmd.where
+ view.actions.next = 1
+
+ for (let p of iter_faction_movable(DS)) {
+ if (
+ set_has(SPACES[game.cmd.where].adjacent, piece_space(p)) &&
+ !set_has(game.cmd.pieces, p)
+ )
+ gen_action_piece(p)
+ }
+
+ },
+ piece(p) {
+ log_summary_move_from(p)
+ set_add(game.cmd.pieces, p)
+ place_piece(p, game.cmd.where)
+ },
+ next() {
+ end_march_space()
+ }
+}
+
states.rally = {
prompt() {
if (game.current === BK)
@@ -596,15 +665,15 @@ states.rally = {
states.rally_space = {
prompt() {
view.prompt = `Rally: Place up to ${rally_count()} ${PIECE_FACTION_TYPE_NAME[game.current][ELITE]}`
- view.where = game.op.where
+ view.where = game.cmd.where
view.actions.next = 1
gen_place_piece(game.current, ELITE)
},
piece(p) {
log_summary_place(p)
- place_piece(p, game.op.where)
- if (++game.op.count >= rally_count())
+ place_piece(p, game.cmd.where)
+ if (++game.cmd.count >= rally_count())
end_rally_space()
},
next() {
@@ -640,16 +709,17 @@ states.rebel = {
function init_command(type) {
push_undo()
- if (game.op.limited)
+ if (game.cmd.limited)
log_h2(faction_name[game.current] + " - Limited " + type)
else
log_h2(faction_name[game.current] + " - " + type)
- game.op.type = type
+ game.cmd.type = type
}
function gen_any_command() {
if (game.current === DS) {
view.actions.conscript = can_conscript() ? 1 : 0
+ view.actions.march = can_march() ? 1 : 0
} else if (game.current === BK || game.current === VE) {
view.actions.rally = can_rally() ? 1 : 0
view.actions.rebel = can_rebel() ? 1 : 0
@@ -657,18 +727,18 @@ function gen_any_command() {
}
function can_select_cmd_space(cost) {
- if (!game.op.free && game.resources[game.current] < cost)
+ if (!game.cmd.free && game.resources[game.current] < cost)
return false
- if (game.op.limited)
- return game.op.spaces.length === 0
+ if (game.cmd.limited)
+ return game.cmd.spaces.length === 0
return true
}
function select_cmd_space(s, cost) {
- set_add(game.op.spaces, s)
- if (!game.op.free)
+ set_add(game.cmd.spaces, s)
+ if (!game.cmd.free)
game.resources[game.current] -= cost
- game.op.where = s
+ game.cmd.where = s
}
function can_conscript() {
@@ -681,14 +751,28 @@ function can_conscript_in_space(s) {
}
function conscript_count() {
- if (game.op.where === S_DELHI)
+ if (game.cmd.where === S_DELHI)
return 5
- if (has_piece(game.op.where, DS, DISC)) {
+ if (has_piece(game.cmd.where, DS, DISC)) {
return 2
}
return 1
}
+function can_march() {
+ for (let s = first_space; s <= last_space; ++s)
+ if (can_march_in_space(s))
+ return true
+ return false
+}
+
+function can_march_in_space(s) {
+ for (let adj of SPACES[s].adjacent)
+ if (has_unmoved_piece(adj, DS))
+ return true
+ return false
+}
+
function can_rally() {
return has_piece(AVAILABLE, game.current, ELITE)
}
@@ -705,10 +789,10 @@ function rally_count() {
// TODO : Add rally with control + 1
let count = 1
if (game.current === BK) {
- count += (game.op.where === S_MAHARASHTRA ? 1 : 0)
+ count += (game.cmd.where === S_MAHARASHTRA ? 1 : 0)
} else if (game.current === VE) {
- count += (game.op.where === S_KARNATAKA ? 1 : 0)
- count += (has_piece(game.op.where, VE, DISC) ? 1 : 0)
+ count += (game.cmd.where === S_KARNATAKA ? 1 : 0)
+ count += (has_piece(game.cmd.where, VE, DISC) ? 1 : 0)
}
return count
}
@@ -730,19 +814,19 @@ function can_rebel_in_space(s) {
function prompt_end_cmd(cost) {
if (!view.actions.space) {
- if (game.op.limited && game.op.spaces && game.op.spaces.length > 0)
- view.prompt = game.op.type + ": Done."
- else if (!game.op.free && game.resources[game.current] < cost)
- view.prompt = game.op.type + ": No resources."
+ if (game.cmd.limited && game.cmd.spaces && game.cmd.spaces.length > 0)
+ view.prompt = game.cmd.type + ": Done."
+ else if (!game.cmd.free && game.resources[game.current] < cost)
+ view.prompt = game.cmd.type + ": No resources."
else
- view.prompt = game.op.type + ": Done."
+ view.prompt = game.cmd.type + ": Done."
}
- return (game.op.spaces.length > 0) ? 1 : 0
+ return (game.cmd.spaces.length > 0) ? 1 : 0
}
function end_command() {
log_br()
- game.op = null
+ game.cmd = null
resume_event_card()
}
@@ -775,6 +859,10 @@ function is_tributary(s) {
return game.tributary & (1 << s)
}
+function is_faction_control(s, faction) {
+ return game.control[faction] & (1 << s)
+}
+
function remove_tributary(s) {
game.tributary &= ~(1 << s)
update_control()
@@ -789,28 +877,26 @@ function to_obedient(p) {
function to_rebel(p) {
let faction = piece_faction(p)
let piece_index = p - first_piece[faction][ELITE]
- console.log("with p ", p)
- console.log("with piece_index ", piece_index)
- console.log(game.rebel)
game.rebel[faction] |= (1 << piece_index)
- console.log(game.rebel)
}
function to_rebel_space(s, faction) {
- let first = first_piece[faction][ELITE]
- let last = last_piece[faction][ELITE]
- for (let p = first; p <= last; ++p)
+ for (let p of iter_faction_pieces(faction, ELITE))
if (piece_space(p) === s)
to_rebel(p)
}
+function update_rebel(p, s) {
+ let faction = piece_faction(p)
+ if (is_faction_control(s, faction))
+ to_rebel(p)
+}
+
/* MISC SPACE + PIECE QUERIES */
function count_pieces(s, faction, type) {
- let first = first_piece[faction][type]
- let last = last_piece[faction][type]
let n = 0
- for (let p = first; p <= last; ++p)
+ for (let p of iter_faction_pieces(faction, type))
if (piece_space(p) === s)
++n
return n
@@ -847,29 +933,30 @@ function has_majority(s) {
return -1
}
-function has_piece(s, faction, type) {
- let first = first_piece[faction][type]
- let last = last_piece[faction][type]
- for (let p = first; p <= last; ++p)
- if (piece_space(p) === s)
- return true
- return false
-}
-
function find_piece(s, faction, type) {
- let first = first_piece[faction][type]
- let last = last_piece[faction][type]
- for (let p = first; p <= last; ++p)
+ for (let p of iter_faction_pieces(faction, type))
if (piece_space(p) === s)
return p
return -1
}
+function has_piece(s, faction, type) {
+ return find_piece(s, faction, type) != -1
+}
+
+function has_unmoved_piece(s, faction) {
+ for (let p of iter_faction_movable(faction))
+ if (piece_space(p) === s)
+ if (!game.cmd.pieces || game.cmd.pieces.length === 0)
+ return true
+ else if (!set_has(game.cmd.pieces, p))
+ return true
+ return false
+}
+
function gen_place_piece(faction, type) {
- let p0 = first_piece[faction][type]
- let p1 = last_piece[faction][type]
let can_place = false
- for (let p = p0; p <= p1; ++p) {
+ for (let p of iter_faction_pieces(faction, type)) {
if (piece_space(p) === AVAILABLE) {
gen_action_piece(p)
can_place = true
@@ -881,7 +968,7 @@ function gen_place_piece(faction, type) {
}
function is_selected_cmd_space(s) {
- return game.op.spaces && set_has(game.op.spaces, s)
+ return game.cmd.spaces && set_has(game.cmd.spaces, s)
}
function place_piece(p, s) {
@@ -890,6 +977,7 @@ function place_piece(p, s) {
set_piece_space(p, s)
update_control()
+ update_rebel(p, s)
}
function piece_faction(p) {
@@ -1053,8 +1141,8 @@ function log_summary_place(p) {
log_summary("% " + piece_name(p))
}
-function log_summary_move_to_from(p, to) {
- log_summary("% " + piece_name(p) + " to S" + to + " from S" + piece_space(p))
+function log_summary_move_from(p) {
+ log_summary("% " + piece_name(p) + " from S" + piece_space(p))
}
function log_summary_remove(p) {
@@ -1409,6 +1497,24 @@ function object_group_by(items, callback) {
return groups
}
+/* GENERATORS */
+
+function* iter_faction_pieces(faction, type) {
+ let first = first_piece[faction][type]
+ let last = last_piece[faction][type]
+ for (let p = first; p <= last; ++p)
+ yield p;
+}
+
+function* iter_faction_movable(faction) {
+ if (faction === DS) {
+ yield* iter_faction_pieces(DS, ELITE)
+ yield* iter_faction_pieces(DS, TROOPS)
+ } else {
+ yield* iter_faction_pieces(faction, ELITE)
+ }
+}
+
// === CONST ===