summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2022-09-24 02:02:50 +0200
committerTor Andersson <tor@ccxvii.net>2022-11-17 12:53:18 +0100
commit28e3241e894c5d26f6e9d1f98baf7963ac0a478b (patch)
tree3cadd7ca873e67fafb3d941632637ee092e32879
parent02591e34c384be9e0785eb5f1d1482ac8310b66a (diff)
downloadhammer-of-the-scots-28e3241e894c5d26f6e9d1f98baf7963ac0a478b.tar.gz
Optimize representation.
-rw-r--r--data.js262
-rw-r--r--play.css7
-rw-r--r--play.js170
-rw-r--r--rules.js1129
4 files changed, 883 insertions, 685 deletions
diff --git a/data.js b/data.js
index 21e4953..920e8c6 100644
--- a/data.js
+++ b/data.js
@@ -1,4 +1,4 @@
-"use strict";
+"use strict"
const CARDS = {
1: {
@@ -51,11 +51,15 @@ const CARDS = {
23: { name: "a 1", moves: 1, image: "card_1" },
24: { name: "a 1", moves: 1, image: "card_1" },
25: { name: "a 1", moves: 1, image: "card_1" },
-};
+}
-let BLOCKS = {}
+let block_index = {}
+let BLOCKS = []
-let AREAS = {
+let AREAS_XY = {
+ "Nowhere": {},
+ "E. Bag": { x: 150, y: 1900 },
+ "S. Bag": { x: 150, y: 50 },
"England": { x: 1360, y: 1750 },
"Ross": { x: 583, y: 376 },
"Garmoran": { x: 466, y: 573 },
@@ -79,79 +83,84 @@ let AREAS = {
"Galloway": { x: 685, y: 1667 },
"Annan": { x: 946, y: 1566 },
"Teviot": { x: 1151, y: 1424 },
-
- "E. Bag": { x: 150, y: 1900 },
- "S. Bag": { x: 150, y: 50 },
}
-let BORDERS = {};
+let area_index = []
+let AREAS = []
+
+let BORDERS = []
-(function () {
+;(function () {
function border(A,B,T) {
+ A = area_index[A]
+ B = area_index[B]
if (A > B)
- [A, B] = [B, A];
- let id = A + "/" + B;
- AREAS[A].exits.push(B);
- AREAS[B].exits.push(A);
- BORDERS[id] = T;
+ [A, B] = [B, A]
+ let id = A * 100 + B
+ AREAS[A].exits.push(B)
+ AREAS[B].exits.push(A)
+ BORDERS[id] = T
}
- for (let a in AREAS) {
- AREAS[a].cathedral = false;
- AREAS[a].home = null;
- AREAS[a].coastal = false;
- AREAS[a].exits = [];
+ for (let a in AREAS_XY) {
+ let id = area_index[a] = AREAS.length
+ AREAS[id] = AREAS_XY[a]
+ AREAS[id].name = a
+ AREAS[id].cathedral = false
+ AREAS[id].home = null
+ AREAS[id].coastal = false
+ AREAS[id].exits = []
}
- AREAS["Strathspey"].cathedral = true;
- AREAS["Lennox"].cathedral = true;
- AREAS["Fife"].cathedral = true;
+ AREAS_XY["Strathspey"].cathedral = true
+ AREAS_XY["Lennox"].cathedral = true
+ AREAS_XY["Fife"].cathedral = true
- AREAS["Ross"].home = "Ross";
- AREAS["Moray"].home = "Moray";
- AREAS["Buchan"].home = "Buchan";
- AREAS["Lochaber"].home = "Comyn";
- AREAS["Badenoch"].home = "Comyn";
- AREAS["Mar"].home = "Mar";
- AREAS["Angus"].home = "Angus";
- AREAS["Argyll"].home = "Argyll";
- AREAS["Atholl"].home = "Atholl";
- AREAS["Lennox"].home = "Lennox";
- AREAS["Mentieth"].home = "Mentieth";
- AREAS["Carrick"].home = "Bruce";
- AREAS["Lanark"].home = "Steward";
- AREAS["Dunbar"].home = "Dunbar";
- AREAS["Galloway"].home = "Galloway";
- AREAS["Annan"].home = "Bruce";
+ AREAS_XY["Ross"].home = "Ross"
+ AREAS_XY["Moray"].home = "Moray"
+ AREAS_XY["Buchan"].home = "Buchan"
+ AREAS_XY["Lochaber"].home = "Comyn"
+ AREAS_XY["Badenoch"].home = "Comyn"
+ AREAS_XY["Mar"].home = "Mar"
+ AREAS_XY["Angus"].home = "Angus"
+ AREAS_XY["Argyll"].home = "Argyll"
+ AREAS_XY["Atholl"].home = "Atholl"
+ AREAS_XY["Lennox"].home = "Lennox"
+ AREAS_XY["Mentieth"].home = "Mentieth"
+ AREAS_XY["Carrick"].home = "Bruce"
+ AREAS_XY["Lanark"].home = "Steward"
+ AREAS_XY["Dunbar"].home = "Dunbar"
+ AREAS_XY["Galloway"].home = "Galloway"
+ AREAS_XY["Annan"].home = "Bruce"
- AREAS["England"].limit = 0;
- AREAS["Ross"].limit = 1;
- AREAS["Garmoran"].limit = 0;
- AREAS["Moray"].limit = 2;
- AREAS["Strathspey"].limit = 1;
- AREAS["Buchan"].limit = 2;
- AREAS["Lochaber"].limit = 1;
- AREAS["Badenoch"].limit = 2;
- AREAS["Mar"].limit = 1;
- AREAS["Angus"].limit = 2;
- AREAS["Argyll"].limit = 2;
- AREAS["Atholl"].limit = 1;
- AREAS["Lennox"].limit = 1;
- AREAS["Mentieth"].limit = 3;
- AREAS["Fife"].limit = 2;
- AREAS["Carrick"].limit = 1;
- AREAS["Lanark"].limit = 2;
- AREAS["Lothian"].limit = 2;
- AREAS["Selkirk"].limit = 0;
- AREAS["Dunbar"].limit = 2;
- AREAS["Galloway"].limit = 1;
- AREAS["Annan"].limit = 2;
- AREAS["Teviot"].limit = 1;
+ AREAS_XY["England"].limit = 0
+ AREAS_XY["Ross"].limit = 1
+ AREAS_XY["Garmoran"].limit = 0
+ AREAS_XY["Moray"].limit = 2
+ AREAS_XY["Strathspey"].limit = 1
+ AREAS_XY["Buchan"].limit = 2
+ AREAS_XY["Lochaber"].limit = 1
+ AREAS_XY["Badenoch"].limit = 2
+ AREAS_XY["Mar"].limit = 1
+ AREAS_XY["Angus"].limit = 2
+ AREAS_XY["Argyll"].limit = 2
+ AREAS_XY["Atholl"].limit = 1
+ AREAS_XY["Lennox"].limit = 1
+ AREAS_XY["Mentieth"].limit = 3
+ AREAS_XY["Fife"].limit = 2
+ AREAS_XY["Carrick"].limit = 1
+ AREAS_XY["Lanark"].limit = 2
+ AREAS_XY["Lothian"].limit = 2
+ AREAS_XY["Selkirk"].limit = 0
+ AREAS_XY["Dunbar"].limit = 2
+ AREAS_XY["Galloway"].limit = 1
+ AREAS_XY["Annan"].limit = 2
+ AREAS_XY["Teviot"].limit = 1
function red(A,B) { border(A,B,"minor"); }
function black(A,B) { border(A,B,"major"); }
- function northsea(A) { AREAS[A].coastal = true; }
- function irishsea(A) { AREAS[A].coastal = true; }
+ function northsea(A) { AREAS_XY[A].coastal = true; }
+ function irishsea(A) { AREAS_XY[A].coastal = true; }
black("Buchan", "Angus")
black("Buchan", "Mar")
@@ -225,86 +234,89 @@ let BORDERS = {};
irishsea("Annan")
function block(owner, type, name, move, combat, steps, mortal, image) {
- let id = name;
+ let sid = name
if (type === 'nobles')
- id = name + "/" + owner[0];
- let item = {
+ sid = name + "/" + owner[0]
+ let id = block_index[sid] = BLOCKS.length
+ BLOCKS[id] = {
+ sid: sid,
owner: owner,
type: type,
name: name,
move: move,
combat: combat,
+ initiative: combat[0],
+ fire_power: combat[1] | 0,
steps: steps,
mortal: mortal,
image: image,
}
- BLOCKS[id] = item;
}
- block("Scotland", "wallace", "Wallace", 3, "A3", 4, true, 11);
- block("Scotland", "king", "King", 3, "A3", 4, true, 12);
- block("Scotland", "infantry", "Douglas", 2, "C3", 4, false, 13);
- block("Scotland", "infantry", "Campbell", 2, "C2", 4, false, 14);
- block("Scotland", "infantry", "Graham", 2, "C2", 4, false, 15);
- block("Scotland", "infantry", "MacDonald", 2, "C3", 3, false, 16);
- block("Scotland", "infantry", "Lindsay", 2, "C2", 3, false, 17);
+ block("Scotland", "wallace", "Wallace", 3, "A3", 4, true, 11)
+ block("Scotland", "king", "King", 3, "A3", 4, true, 12)
+ block("Scotland", "infantry", "Douglas", 2, "C3", 4, false, 13)
+ block("Scotland", "infantry", "Campbell", 2, "C2", 4, false, 14)
+ block("Scotland", "infantry", "Graham", 2, "C2", 4, false, 15)
+ block("Scotland", "infantry", "MacDonald", 2, "C3", 3, false, 16)
+ block("Scotland", "infantry", "Lindsay", 2, "C2", 3, false, 17)
- block("Scotland", "infantry", "Fraser", 2, "C3", 3, false, 21);
- block("Scotland", "infantry", "Barclay", 2, "C2", 4, false, 22);
- block("Scotland", "infantry", "Grant", 2, "C2", 3, false, 23);
- block("Scotland", "cavalry", "Keith", 3, "B1", 3, false, 24);
- block("Scotland", "archers", "Etterick", 3, "B2", 2, false, 25);
- block("Scotland", "norse", "Norse", 0, "A2", 3, true, 26);
- block("Scotland", "knights", "French Knights", 2, "B3", 4, true, 27);
+ block("Scotland", "infantry", "Fraser", 2, "C3", 3, false, 21)
+ block("Scotland", "infantry", "Barclay", 2, "C2", 4, false, 22)
+ block("Scotland", "infantry", "Grant", 2, "C2", 3, false, 23)
+ block("Scotland", "cavalry", "Keith", 3, "B1", 3, false, 24)
+ block("Scotland", "archers", "Etterick", 3, "B2", 2, false, 25)
+ block("Scotland", "norse", "Norse", 0, "A2", 3, true, 26)
+ block("Scotland", "knights", "French Knights", 2, "B3", 4, true, 27)
- block("Scotland", "nobles", "Comyn", 2, "B2", 4, false, 31);
- block("Scotland", "moray", "Moray", 2, "B2", 3, true, 32);
- block("Scotland", "nobles", "Angus", 2, "B2", 3, false, 33);
- block("Scotland", "nobles", "Argyll", 2, "B2", 3, false, 34);
- block("Scotland", "nobles", "Bruce", 2, "B2", 4, false, 35);
- block("Scotland", "nobles", "Mar", 2, "B2", 3, false, 36);
- block("Scotland", "nobles", "Lennox", 2, "B2", 3, false, 37);
+ block("Scotland", "nobles", "Comyn", 2, "B2", 4, false, 31)
+ block("Scotland", "moray", "Moray", 2, "B2", 3, true, 32)
+ block("Scotland", "nobles", "Angus", 2, "B2", 3, false, 33)
+ block("Scotland", "nobles", "Argyll", 2, "B2", 3, false, 34)
+ block("Scotland", "nobles", "Bruce", 2, "B2", 4, false, 35)
+ block("Scotland", "nobles", "Mar", 2, "B2", 3, false, 36)
+ block("Scotland", "nobles", "Lennox", 2, "B2", 3, false, 37)
- block("Scotland", "nobles", "Buchan", 2, "B2", 3, false, 41);
- block("Scotland", "nobles", "Galloway", 2, "B2", 3, false, 42);
- block("Scotland", "nobles", "Ross", 2, "B2", 3, false, 43);
- block("Scotland", "nobles", "Atholl", 2, "B2", 3, false, 44);
- block("Scotland", "nobles", "Dunbar", 2, "B2", 3, false, 45);
- block("Scotland", "nobles", "Mentieth", 2, "B2", 3, false, 46);
- block("Scotland", "nobles", "Steward", 2, "B2", 3, false, 47);
+ block("Scotland", "nobles", "Buchan", 2, "B2", 3, false, 41)
+ block("Scotland", "nobles", "Galloway", 2, "B2", 3, false, 42)
+ block("Scotland", "nobles", "Ross", 2, "B2", 3, false, 43)
+ block("Scotland", "nobles", "Atholl", 2, "B2", 3, false, 44)
+ block("Scotland", "nobles", "Dunbar", 2, "B2", 3, false, 45)
+ block("Scotland", "nobles", "Mentieth", 2, "B2", 3, false, 46)
+ block("Scotland", "nobles", "Steward", 2, "B2", 3, false, 47)
- block("England", "king", "Edward", 3, "B4", 4, true, 61);
- block("England", "archers", "Lancaster Archers", 2, "B3", 3, false, 62);
- block("England", "archers", "Wales Archers", 2, "B3", 3, false, 63);
- block("England", "knights", "Lancaster Knights", 2, "B3", 4, false, 64);
- block("England", "knights", "York Knights", 2, "B3", 4, false, 65);
- block("England", "knights", "Durham Knights", 2, "B3", 3, false, 66);
- block("England", "hobelars", "Hobelars", 3, "A2", 3, true, 67);
+ block("England", "king", "Edward", 3, "B4", 4, true, 61)
+ block("England", "archers", "Lancaster Archers", 2, "B3", 3, false, 62)
+ block("England", "archers", "Wales Archers", 2, "B3", 3, false, 63)
+ block("England", "knights", "Lancaster Knights", 2, "B3", 4, false, 64)
+ block("England", "knights", "York Knights", 2, "B3", 4, false, 65)
+ block("England", "knights", "Durham Knights", 2, "B3", 3, false, 66)
+ block("England", "hobelars", "Hobelars", 3, "A2", 3, true, 67)
- block("England", "infantry", "York Infantry", 2, "C2", 4, false, 71);
- block("England", "infantry", "Lancaster Infantry", 2, "C2", 4, false, 72);
- block("England", "infantry", "Northumber Infantry", 2, "C2", 4, false, 73);
- block("England", "infantry", "Durham Infantry", 2, "C2", 3, false, 74);
- block("England", "infantry", "Cumbria Infantry", 2, "C2", 3, false, 75);
- block("England", "infantry", "Westmor Infantry", 2, "C2", 3, false, 82);
- block("England", "infantry", "Wales Infantry", 2, "C3", 3, false, 76);
- block("England", "infantry", "Ulster Infantry", 2, "C3", 3, false, 77);
+ block("England", "infantry", "York Infantry", 2, "C2", 4, false, 71)
+ block("England", "infantry", "Lancaster Infantry", 2, "C2", 4, false, 72)
+ block("England", "infantry", "Northumber Infantry", 2, "C2", 4, false, 73)
+ block("England", "infantry", "Durham Infantry", 2, "C2", 3, false, 74)
+ block("England", "infantry", "Cumbria Infantry", 2, "C2", 3, false, 75)
+ block("England", "infantry", "Westmor Infantry", 2, "C2", 3, false, 82)
+ block("England", "infantry", "Wales Infantry", 2, "C3", 3, false, 76)
+ block("England", "infantry", "Ulster Infantry", 2, "C3", 3, false, 77)
- block("England", "nobles", "Comyn", 2, "B2", 4, false, 81);
- block("England", "nobles", "Angus", 2, "B2", 3, false, 83);
- block("England", "nobles", "Argyll", 2, "B2", 3, false, 84);
- block("England", "nobles", "Bruce", 2, "B2", 4, false, 85);
- block("England", "nobles", "Mar", 2, "B2", 3, false, 86);
- block("England", "nobles", "Lennox", 2, "B2", 3, false, 87);
+ block("England", "nobles", "Comyn", 2, "B2", 4, false, 81)
+ block("England", "nobles", "Angus", 2, "B2", 3, false, 83)
+ block("England", "nobles", "Argyll", 2, "B2", 3, false, 84)
+ block("England", "nobles", "Bruce", 2, "B2", 4, false, 85)
+ block("England", "nobles", "Mar", 2, "B2", 3, false, 86)
+ block("England", "nobles", "Lennox", 2, "B2", 3, false, 87)
- block("England", "nobles", "Buchan", 2, "B2", 3, false, 91);
- block("England", "nobles", "Galloway", 2, "B2", 3, false, 92);
- block("England", "nobles", "Ross", 2, "B2", 3, false, 93);
- block("England", "nobles", "Atholl", 2, "B2", 3, false, 94);
- block("England", "nobles", "Dunbar", 2, "B2", 3, false, 95);
- block("England", "nobles", "Mentieth", 2, "B2", 3, false, 96);
- block("England", "nobles", "Steward", 2, "B2", 3, false, 97);
-})();
+ block("England", "nobles", "Buchan", 2, "B2", 3, false, 91)
+ block("England", "nobles", "Galloway", 2, "B2", 3, false, 92)
+ block("England", "nobles", "Ross", 2, "B2", 3, false, 93)
+ block("England", "nobles", "Atholl", 2, "B2", 3, false, 94)
+ block("England", "nobles", "Dunbar", 2, "B2", 3, false, 95)
+ block("England", "nobles", "Mentieth", 2, "B2", 3, false, 96)
+ block("England", "nobles", "Steward", 2, "B2", 3, false, 97)
+})()
if (typeof module !== 'undefined')
- module.exports = { CARDS, BLOCKS, AREAS, BORDERS }
+ module.exports = { CARDS, BLOCKS, AREAS, BORDERS, block_index, area_index }
diff --git a/play.css b/play.css
index 813dfab..be8cc5a 100644
--- a/play.css
+++ b/play.css
@@ -9,11 +9,12 @@ header.your_turn { background-color: orange; }
#log { background-color: whitesmoke; }
#log div { padding-left: 20px; text-indent: -12px; }
-#log .st { background-color: darkslategray; color: white; font-weight: bold; }
+#log div.i { padding-left: 32px; text-indent: -12px; }
+#log .h1 { background-color: darkslategray; color: white; font-weight: bold; }
#log .S { background-color: lightblue; }
#log .E { background-color: pink; }
-#log .bs { background-color: gainsboro; }
-#log .br { font-style: italic; text-decoration: underline; }
+#log .h3 { background-color: gainsboro; }
+#log .h4 { font-style: italic; text-decoration: underline; }
/* CARDS */
diff --git a/play.js b/play.js
index f5483a6..f417009 100644
--- a/play.js
+++ b/play.js
@@ -1,10 +1,38 @@
"use strict"
-const ENGLAND = "England"
-const SCOTLAND = "Scotland"
+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
+}
+
const ENEMY = { Scotland: "England", England: "Scotland" }
-const ENGLAND_BAG = "E. Bag"
-const SCOTLAND_BAG = "S. Bag"
+
+const ENGLAND_BAG = area_index["E. Bag"]
+const SCOTLAND_BAG = area_index["S. Bag"]
+
+const AREA_ARGYLL = area_index["Argyll"]
+const AREA_CARRICK = area_index["Carrick"]
+const AREA_DUNBAR = area_index["Dunbar"]
+const AREA_FIFE = area_index["Fife"]
+const AREA_ENGLAND = area_index["England"]
+const AREA_GARMORAN = area_index["Garmoran"]
+const AREA_LANARK = area_index["Lanark"]
+const AREA_LENNOX = area_index["Lennox"]
+const AREA_LOTHIAN = area_index["Lothian"]
+const AREA_MENTIETH = area_index["Mentieth"]
+const AREA_SCOTLAND = area_index["Scotland"]
+const AREA_SELKIRK = area_index["Selkirk"]
const NOBLES = [
"Angus", "Argyll", "Atholl", "Bruce", "Buchan", "Comyn", "Dunbar",
@@ -41,15 +69,21 @@ function toggle_blocks() {
let ui = {
cards: {},
card_backs: {},
- areas: {},
- blocks: {},
- battle_menu: {},
- battle_block: {},
+ areas: [],
+ blocks: [],
+ battle_menu: [],
+ battle_block: [],
present: new Set(),
}
function on_log(text) {
let p = document.createElement("div")
+
+ if (text.match(/^>/)) {
+ text = text.substring(1)
+ p.className = "i"
+ }
+
text = text.replace(/&/g, "&amp;")
text = text.replace(/</g, "&lt;")
text = text.replace(/>/g, "&gt;")
@@ -58,30 +92,16 @@ function on_log(text) {
text = text.replace(/^([A-Z]):/, '<span class="$1"> $1 </span>')
- if (text.match(/^Scenario: /))
- p.className = 'st', text = text.substring(10)
- else if (text.match(/^~ .* ~$/))
- p.className = 'br', text = text.substring(2, text.length-2)
- else if (text.match(/^Start England turn/))
- p.className = 'E'
- else if (text.match(/^Start Scotland turn/))
- p.className = 'S'
- else if (text.match(/^Start /))
- p.className = 'st', text = text.replace(/\.$/, "")
- else if (text.match(/^(Battle in|Defection battle in)/))
- p.className = 'bs'
- else if (text.match(/^.h1 /)) {
- p.className = 'st', text = text.substring(4)
- }
- else if (text.match(/^.turn England/)) {
- p.className = 'E', text = text.substring(6)
- }
- else if (text.match(/^.turn Scotland/)) {
- p.className = 'S', text = text.substring(6)
- }
-
- if (text.match(/^Start /))
- text = text.substring(6)
+ if (text.match(/^\.h1 /))
+ p.className = 'h1', text = text.substring(4)
+ if (text.match(/^\.h2 E/))
+ p.className = 'h2 E', text = text.substring(4)
+ if (text.match(/^\.h2 S/))
+ p.className = 'h2 S', text = text.substring(4)
+ if (text.match(/^\.h3 /))
+ p.className = 'h3', text = text.substring(4)
+ if (text.match(/^\.h4 /))
+ p.className = 'h4', text = text.substring(4)
p.innerHTML = text
return p
@@ -89,7 +109,7 @@ function on_log(text) {
function on_focus_area(evt) {
let where = evt.target.area
- document.getElementById("status").textContent = where
+ document.getElementById("status").textContent = AREAS[where].name
}
function on_blur_area(evt) {
@@ -127,7 +147,7 @@ function on_focus_map_block(evt) {
if (BLOCKS[b].mortal)
text += ' \u271d'
} else {
- text = (BLOCKS[b].owner === ENGLAND) ? "English" : "Scottish"
+ text = (BLOCKS[b].owner === "England") ? "English" : "Scottish"
}
document.getElementById("status").textContent = text
}
@@ -286,19 +306,19 @@ function build_map() {
for (let c = 1; c <= 5; ++c)
ui.card_backs[c] = document.getElementById("back+"+c)
- for (let name in AREAS) {
- let area = AREAS[name]
- let element = svgmap.getElementById("area+"+name)
+ for (let s = 1; s < AREAS.length; ++s) {
+ let area = AREAS[s]
+ let element = svgmap.getElementById("area+"+area.name)
if (element) {
- element.area = name
+ element.area = s
element.addEventListener("mouseenter", on_focus_area)
element.addEventListener("mouseleave", on_blur_area)
element.addEventListener("click", on_click_area)
- ui.areas[name] = element
+ ui.areas[s] = element
}
}
- for (let b in BLOCKS) {
+ for (let b = 0; b < BLOCKS.length; ++b) {
let block = BLOCKS[b]
build_battle_block(b, block)
build_map_block(b, block)
@@ -327,17 +347,17 @@ function layout_blocks(location, north, south) {
case SCOTLAND_BAG:
wrap = 28
break
- case "Selkirk":
- case "Lothian":
- case "Dunbar":
- case "Lanark":
- case "Lennox":
- case "Argyll":
- case "Garmoran":
- case "Mentieth":
+ case AREA_SELKIRK:
+ case AREA_LOTHIAN:
+ case AREA_DUNBAR:
+ case AREA_LANARK:
+ case AREA_LENNOX:
+ case AREA_ARGYLL:
+ case AREA_GARMORAN:
+ case AREA_MENTIETH:
wrap = 3
break
- case "England":
+ case AREA_ENGLAND:
wrap = 5
}
@@ -389,31 +409,31 @@ function position_block(location, row, n_rows, col, n_cols, element) {
layout_major = 0
layout_minor = 0
break
- case ENGLAND:
+ case AREA_ENGLAND:
layout_major = 1
layout_minor = 1
break
- case "Argyll":
+ case AREA_ARGYLL:
layout_major = 0.5
layout_minor = 1.0
break
- case "Carrick":
+ case AREA_CARRICK:
layout_major = 0.75
layout_minor = 0.5
break
- case "Dunbar":
+ case AREA_DUNBAR:
layout_major = 0.25
layout_minor = 0.75
break
- case "Fife":
+ case AREA_FIFE:
layout_major = 0.25
layout_minor = 0.5
break
- case "Lennox":
+ case AREA_LENNOX:
layout_major = 0.75
layout_minor = 0.75
break
- case "Mentieth":
+ case AREA_MENTIETH:
layout_major = 0.5
layout_minor = 0.25
break
@@ -445,13 +465,13 @@ function update_map() {
document.getElementById("turn").setAttribute("class", "turn year_" + view.year)
- for (let area in AREAS)
+ for (let area = 1; area < AREAS.length; ++area)
layout[area] = { north: [], south: [] }
for (let b in view.location) {
- if (view.location[b] === null && BLOCKS[b].mortal) {
+ if (view.location[b] === 0 && BLOCKS[b].mortal) {
let element = ui.blocks[b]
- if (BLOCKS[b].owner === SCOTLAND)
+ if (BLOCKS[b].owner === "Scotland")
layout[SCOTLAND_BAG].north.push(element)
else
layout[ENGLAND_BAG].south.push(element)
@@ -462,13 +482,13 @@ function update_map() {
let info = BLOCKS[b]
let element = ui.blocks[b]
let area = view.location[b]
- if (area in AREAS || BLOCKS[b].mortal) {
- let moved = view.moved[b] ? " moved" : ""
- if (is_known_block(b) || area === null) {
+ if (area > 0 || BLOCKS[b].mortal) {
+ let moved = set_has(view.moved, b) ? " moved" : ""
+ if (is_known_block(b) || area === 0) {
let image = " block_" + info.image
let steps = " r" + (info.steps - view.steps[b])
let known = " known"
- if (area === null) {
+ if (area === 0) {
moved = " moved"
steps = " r0"
}
@@ -476,8 +496,8 @@ function update_map() {
} else {
element.classList = info.owner + " block" + moved
}
- if (area !== null) {
- if (info.owner === SCOTLAND)
+ if (area > 0) {
+ if (info.owner === "Scotland")
layout[area].north.push(element)
else
layout[area].south.push(element)
@@ -488,15 +508,15 @@ function update_map() {
}
}
- for (let area in AREAS)
+ for (let area = 1; area < AREAS.length; ++area)
layout_blocks(area, layout[area].north, layout[area].south)
// Mark selections and highlights
- for (let where in AREAS) {
- if (ui.areas[where]) {
- ui.areas[where].classList.remove('highlight')
- ui.areas[where].classList.remove('where')
+ for (let area = 1; area < AREAS.length; ++area) {
+ if (ui.areas[area]) {
+ ui.areas[area].classList.remove('highlight')
+ ui.areas[area].classList.remove('where')
}
}
if (view.actions && view.actions.area)
@@ -505,7 +525,7 @@ function update_map() {
if (view.where)
ui.areas[view.where].classList.add('where')
- for (let b in BLOCKS) {
+ for (let b = 0; b < BLOCKS.length; ++b) {
ui.blocks[b].classList.remove('highlight')
ui.blocks[b].classList.remove('selected')
}
@@ -513,7 +533,7 @@ function update_map() {
if (view.actions && view.actions.block)
for (let b of view.actions.block)
ui.blocks[b].classList.add('highlight')
- if (view.who)
+ if (view.who >= 0)
ui.blocks[view.who].classList.add('selected')
}
}
@@ -608,7 +628,7 @@ function update_battle() {
ui.battle_block[block].classList.add("secret")
else
ui.battle_block[block].classList.remove("secret")
- if (view.moved[block])
+ if (set_has(view.moved, block))
ui.battle_block[block].classList.add("moved")
else
ui.battle_block[block].classList.remove("moved")
@@ -618,7 +638,7 @@ function update_battle() {
ui.battle_block[block].classList.add("known")
}
- for (let b in BLOCKS) {
+ for (let b = 0; b < BLOCKS.length; ++b) {
if (!ui.present.has(b)) {
if (cell.contains(ui.battle_menu[b]))
cell.removeChild(ui.battle_menu[b])
@@ -626,7 +646,7 @@ function update_battle() {
}
}
- if (player === ENGLAND) {
+ if (player === "England") {
fill_cell("FR", view.battle.ER, true)
fill_cell("FF", view.battle.EF, false)
fill_cell("EF", view.battle.SF, false)
diff --git a/rules.js b/rules.js
index 4bd32c4..a083a92 100644
--- a/rules.js
+++ b/rules.js
@@ -11,28 +11,90 @@ exports.roles = [
"Scotland",
]
-const { CARDS, BLOCKS, AREAS, BORDERS } = require('./data')
+const { CARDS, BLOCKS, AREAS, BORDERS, block_index, area_index } = require('./data')
-const BLOCKLIST = Object.keys(BLOCKS)
-const AREALIST = Object.keys(AREAS)
+const block_count = BLOCKS.length
+const area_count = AREAS.length
+const first_map_area = 3
const ENEMY = { Scotland: "England", England: "Scotland" }
+
const OBSERVER = "Observer"
const BOTH = "Both"
const ENGLAND = "England"
const SCOTLAND = "Scotland"
-const E_BAG = "E. Bag"
-const S_BAG = "S. Bag"
-const EDWARD = "Edward"
-const KING = "King"
-const MORAY = "Moray"
-const E_BRUCE = "Bruce/E"
-const S_BRUCE = "Bruce/S"
-const E_COMYN = "Comyn/E"
-const S_COMYN = "Comyn/S"
-const WALLACE = "Wallace"
-const NORSE = "Norse"
-const FRENCH_KNIGHTS = "French Knights"
+
+const NOWHERE = 0
+const E_BAG = area_index["E. Bag"]
+const S_BAG = area_index["S. Bag"]
+
+const AREA_ENGLAND = area_index["England"]
+const AREA_ROSS = area_index["Ross"]
+const AREA_GARMORAN = area_index["Garmoran"]
+const AREA_MORAY = area_index["Moray"]
+const AREA_STRATHSPEY = area_index["Strathspey"]
+const AREA_BUCHAN = area_index["Buchan"]
+const AREA_LOCHABER = area_index["Lochaber"]
+const AREA_BADENOCH = area_index["Badenoch"]
+const AREA_MAR = area_index["Mar"]
+const AREA_ANGUS = area_index["Angus"]
+const AREA_ARGYLL = area_index["Argyll"]
+const AREA_ATHOLL = area_index["Atholl"]
+const AREA_LENNOX = area_index["Lennox"]
+const AREA_MENTIETH = area_index["Mentieth"]
+const AREA_FIFE = area_index["Fife"]
+const AREA_CARRICK = area_index["Carrick"]
+const AREA_LANARK = area_index["Lanark"]
+const AREA_LOTHIAN = area_index["Lothian"]
+const AREA_SELKIRK = area_index["Selkirk"]
+const AREA_DUNBAR = area_index["Dunbar"]
+const AREA_GALLOWAY = area_index["Galloway"]
+const AREA_ANNAN = area_index["Annan"]
+const AREA_TEVIOT = area_index["Teviot"]
+
+const NOBODY = -1
+const B_EDWARD = block_index["Edward"]
+const B_KING = block_index["King"]
+const B_MORAY = block_index["Moray"]
+const B_WALLACE = block_index["Wallace"]
+const B_NORSE = block_index["Norse"]
+const B_FRENCH_KNIGHTS = block_index["French Knights"]
+const B_BRUCE_E = block_index["Bruce/E"]
+const B_BRUCE_S = block_index["Bruce/S"]
+const B_COMYN_E = block_index["Comyn/E"]
+const B_COMYN_S = block_index["Comyn/S"]
+
+const ENGLISH_NOBLES = {
+ "Angus": block_index["Angus/E"],
+ "Argyll": block_index["Argyll/E"],
+ "Atholl": block_index["Atholl/E"],
+ "Bruce": block_index["Bruce/E"],
+ "Buchan": block_index["Buchan/E"],
+ "Comyn": block_index["Comyn/E"],
+ "Dunbar": block_index["Dunbar/E"],
+ "Galloway": block_index["Galloway/E"],
+ "Lennox": block_index["Lennox/E"],
+ "Mar": block_index["Mar/E"],
+ "Mentieth": block_index["Mentieth/E"],
+ "Ross": block_index["Ross/E"],
+ "Steward": block_index["Steward/E"],
+}
+
+const SCOTTISH_NOBLES = {
+ "Angus": block_index["Angus/S"],
+ "Argyll": block_index["Argyll/S"],
+ "Atholl": block_index["Atholl/S"],
+ "Bruce": block_index["Bruce/S"],
+ "Buchan": block_index["Buchan/S"],
+ "Comyn": block_index["Comyn/S"],
+ "Dunbar": block_index["Dunbar/S"],
+ "Galloway": block_index["Galloway/S"],
+ "Lennox": block_index["Lennox/S"],
+ "Mar": block_index["Mar/S"],
+ "Mentieth": block_index["Mentieth/S"],
+ "Ross": block_index["Ross/S"],
+ "Steward": block_index["Steward/S"],
+}
// serif cirled numbers
const DIE_HIT = [ 0, '\u2776', '\u2777', '\u2778', '\u2779', '\u277A', '\u277B' ]
@@ -51,52 +113,48 @@ function random(n) {
return (game.seed = game.seed * 200105 % 34359738337) % n
}
-function log(...args) {
- let s = Array.from(args).join(" ")
+function log(s) {
game.log.push(s)
}
-function log_battle(...args) {
- let s = Array.from(args).join("")
+function logi(s) {
+ game.log.push(">" + s)
+}
+
+function log_battle(s) {
game.log.push(game.active[0] + ": " + s)
}
function print_turn_log_no_count(text) {
- function print_move(last) {
- return "\n" + last.join(" \u2192 ")
- }
+ log(text)
if (game.turn_log.length > 0) {
game.turn_log.sort()
for (let entry of game.turn_log)
- text += print_move(entry)
+ logi(entry.join(" \u2192 "))
} else {
- text += "\nnothing."
+ logi("nothing.")
}
- log(text)
delete game.turn_log
}
function print_turn_log_no_active(text) {
- function print_move(last) {
- return "\n" + n + " " + last.join(" \u2192 ")
- }
game.turn_log.sort()
+ log(text)
let last = game.turn_log[0]
let n = 0
for (let entry of game.turn_log) {
if (entry.toString() !== last.toString()) {
- text += print_move(last)
+ logi(n + " " + last.join(" \u2192 "))
n = 0
}
++n
last = entry
}
if (n > 0)
- text += print_move(last)
+ logi(n + " " + last.join(" \u2192 "))
else
- text += "\nnothing."
- log(text)
+ logi("nothing.")
delete game.turn_log
}
@@ -114,56 +172,6 @@ function remove_from_array(array, item) {
array.splice(i, 1)
}
-function deep_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] = deep_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] = deep_copy(v)
- else
- copy[i] = v
- }
- return copy
- }
-}
-
-function push_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 = deep_copy(v)
- copy[k] = v
- }
- game.undo.push(copy)
-}
-
-function pop_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
-}
-
-function clear_undo() {
- game.undo = []
-}
-
function gen_action_undo(view) {
if (!view.actions)
view.actions = {}
@@ -186,6 +194,18 @@ function gen_action(view, action, argument) {
}
}
+function gen_action_battle(view, action, b) {
+ gen_action(view, action, b)
+}
+
+function gen_action_block(view, b) {
+ gen_action(view, 'block', b)
+}
+
+function gen_action_area(view, a) {
+ gen_action(view, 'area', a)
+}
+
function roll_d6() {
return random(6) + 1
}
@@ -207,10 +227,14 @@ function deal_cards(deck, n) {
return hand
}
+function area_name(where) {
+ return AREAS[where].name
+}
+
function block_name(who) {
- if (who === EDWARD)
+ if (who === B_EDWARD)
return game.edward === 1 ? "Edward I" : "Edward II"
- if (who === KING)
+ if (who === B_KING)
return "Scottish King"
return BLOCKS[who].name
}
@@ -236,11 +260,11 @@ function block_is_mortal(who) {
}
function block_initiative(who) {
- return BLOCKS[who].combat[0]
+ return BLOCKS[who].initiative
}
function block_printed_fire_power(who) {
- return BLOCKS[who].combat[1] | 0
+ return BLOCKS[who].fire_power
}
function block_fire_power(who, where) {
@@ -249,7 +273,7 @@ function block_fire_power(who, where) {
if (is_defender(who)) {
if (block_type(who) === 'nobles' && area.home === block_name(who))
++combat
- else if (who === MORAY && where === "Moray")
+ else if (who === B_MORAY && where === AREA_MORAY)
++combat
}
return combat
@@ -276,14 +300,14 @@ function is_in_friendly_coastal_area(who) {
function is_on_map(who) {
let where = game.location[who]
- if (where && where !== E_BAG && where !== S_BAG)
+ if (where !== NOWHERE && where !== E_BAG && where !== S_BAG)
return true
return false
}
function count_blocks_in_area(where) {
let count = 0
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (game.location[b] === where)
++count
return count
@@ -291,7 +315,7 @@ function count_blocks_in_area(where) {
function count_blocks_in_area_excluding(where, exc) {
let count = 0
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (game.location[b] === where && !exc.includes(b))
++count
return count
@@ -313,7 +337,7 @@ function is_under_castle_limit(where) {
function count_english_nobles() {
let count = 0
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (block_owner(b) === ENGLAND && block_type(b) === 'nobles')
if (is_on_map(b))
++count
@@ -322,23 +346,23 @@ function count_english_nobles() {
function count_scottish_nobles() {
let count = 0
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (block_owner(b) === SCOTLAND && block_type(b) === 'nobles')
if (is_on_map(b))
++count
- if (is_on_map(MORAY))
+ if (is_on_map(B_MORAY))
++count
return count
}
function find_noble(owner, name) {
- if (name in BLOCKS)
- return name
- return name + "/" + owner[0]
+ if (owner === ENGLAND)
+ return ENGLISH_NOBLES[name]
+ return SCOTTISH_NOBLES[name]
}
function border_id(a, b) {
- return (a < b) ? a + "/" + b : b + "/" + a
+ return (a < b) ? a * 100 + b : b * 100 + a
}
function border_was_last_used_by_enemy(from, to) {
@@ -360,7 +384,7 @@ function reset_border_limits() {
function count_friendly(where) {
let p = game.active
let count = 0
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (game.location[b] === where && block_owner(b) === p)
++count
return count
@@ -369,7 +393,7 @@ function count_friendly(where) {
function count_enemy(where) {
let p = ENEMY[game.active]
let count = 0
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (game.location[b] === where && block_owner(b) === p)
++count
return count
@@ -382,7 +406,7 @@ function is_contested_area(where) { return count_friendly(where) > 0 && count_en
function is_friendly_or_neutral_area(where) { return is_friendly_area(where) || is_neutral_area(where) }
function have_contested_areas() {
- for (let where of AREALIST)
+ for (let where = first_map_area; where < area_count; ++where)
if (is_contested_area(where))
return true
return false
@@ -394,9 +418,9 @@ function count_pinning(where) {
function count_pinned(where) {
let count = 0
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (game.location[b] === where && block_owner(b) === game.active)
- if (!game.reserves.includes(b))
+ if (!set_has(game.reserves, b))
++count
return count
}
@@ -417,10 +441,10 @@ function can_block_use_border(who, from, to) {
function can_block_move_to(who, from, to) {
// No group moves across Anglo-Scottish border
- if (from === ENGLAND || to === ENGLAND)
+ if (from === AREA_ENGLAND || to === AREA_ENGLAND)
if (game.moves === 0)
return false
- if (game.active === SCOTLAND && game.truce === SCOTLAND && to === ENGLAND)
+ if (game.active === SCOTLAND && game.truce === SCOTLAND && to === AREA_ENGLAND)
return false
if (can_block_use_border(who, from, to)) {
if (count_pinning(from) > 0) {
@@ -435,9 +459,9 @@ function can_block_move_to(who, from, to) {
}
function can_block_move(who) {
- if (who === NORSE)
+ if (who === B_NORSE)
return false
- if (block_owner(who) === game.active && !game.moved[who]) {
+ if (block_owner(who) === game.active && !set_has(game.moved, who)) {
let from = game.location[who]
if (from) {
if (is_pinned(from))
@@ -451,7 +475,7 @@ function can_block_move(who) {
}
function can_block_continue(who, from, here) {
- if (here === ENGLAND)
+ if (here === AREA_ENGLAND)
return false
if (is_contested_area(here))
return false
@@ -468,9 +492,9 @@ function can_block_continue(who, from, here) {
function can_block_retreat_to(who, to) {
if (is_friendly_area(to) || is_neutral_area(to)) {
let from = game.location[who]
- if (block_owner(who) === ENGLAND && from === ENGLAND)
+ if (block_owner(who) === ENGLAND && from === AREA_ENGLAND)
return false
- if (block_owner(who) === SCOTLAND && to === ENGLAND)
+ if (block_owner(who) === SCOTLAND && to === AREA_ENGLAND)
return false
if (can_block_use_border(who, from, to)) {
if (border_was_last_used_by_enemy(from, to))
@@ -483,7 +507,7 @@ function can_block_retreat_to(who, to) {
function can_block_retreat(who) {
if (block_owner(who) === game.active) {
- if (who === NORSE)
+ if (who === B_NORSE)
return true
let from = game.location[who]
for (let to of AREAS[from].exits)
@@ -496,9 +520,9 @@ function can_block_retreat(who) {
function can_block_regroup_to(who, to) {
if (is_friendly_area(to) || is_neutral_area(to)) {
let from = game.location[who]
- if (block_owner(who) === ENGLAND && from === ENGLAND)
+ if (block_owner(who) === ENGLAND && from === AREA_ENGLAND)
return false
- if (block_owner(who) === SCOTLAND && to === ENGLAND)
+ if (block_owner(who) === SCOTLAND && to === AREA_ENGLAND)
return false
if (can_block_use_border(who, from, to))
return true
@@ -517,18 +541,18 @@ function can_block_regroup(who) {
}
function is_battle_reserve(b) {
- return game.reserves.includes(b)
+ return set_has(game.reserves, b)
}
function is_attacker(b) {
if (game.location[b] === game.where && block_owner(b) === game.attacker[game.where])
- return !game.reserves.includes(b)
+ return !set_has(game.reserves, b)
return false
}
function is_defender(b) {
if (game.location[b] === game.where && block_owner(b) !== game.attacker[game.where])
- return !game.reserves.includes(b)
+ return !set_has(game.reserves, b)
return false
}
@@ -537,7 +561,7 @@ function swap_blocks(old) {
let b = find_noble(bo, block_name(old))
game.location[b] = game.location[old]
game.steps[b] = game.steps[old]
- game.location[old] = null
+ game.location[old] = NOWHERE
game.steps[old] = block_max_steps(old)
return b
}
@@ -550,7 +574,7 @@ function disband(who) {
function eliminate_block(who, reason) {
if (block_type(who) === 'nobles') {
if (reason === 'retreat') {
- game.turn_log.push([game.location[who], "Captured"])
+ game.turn_log.push([area_name(game.location[who]), "Captured"])
} else if (reason === 'combat') {
game.flash = block_name(who) + " was captured."
log(block_name(who) + " was captured.")
@@ -559,7 +583,7 @@ function eliminate_block(who, reason) {
}
} else {
if (reason === 'retreat') {
- game.turn_log.push([game.location[who], "Eliminated"])
+ game.turn_log.push([area_name(game.location[who]), "Eliminated"])
} else if (reason === 'combat') {
game.flash = block_name(who) + " was eliminated."
log(block_name(who) + " was eliminated.")
@@ -572,13 +596,13 @@ function eliminate_block(who, reason) {
}
// TODO: clean up and check all combinations
- if (who === EDWARD) {
+ if (who === B_EDWARD) {
if (reason === 'combat' || reason === 'retreat') {
if (game.edward === 1) {
game.edward = 2
disband(who)
} else {
- game.location[who] = null
+ game.location[who] = NOWHERE
if (reason === 'combat') {
game.victory = "Scotland won because king Edward II has died in battle!"
game.result = SCOTLAND
@@ -587,19 +611,19 @@ function eliminate_block(who, reason) {
} else {
disband(who)
}
- } else if (who === KING) {
- game.location[who] = null
+ } else if (who === B_KING) {
+ game.location[who] = NOWHERE
if (reason === 'combat' || reason === 'retreat') {
game.victory = "England won because the Scottish king has died in battle!"
game.result = ENGLAND
}
} else if (block_is_mortal(who) && (reason === 'combat' || reason === 'retreat')) {
- game.location[who] = null
+ game.location[who] = NOWHERE
} else if (block_type(who) === 'nobles') {
who = swap_blocks(who)
game.steps[who] = 1 // flip at strength 1 if eliminated
if (reason === 'combat' || reason === 'retreat')
- game.reserves.push(who)
+ set_add(game.reserves, who)
} else {
disband(who)
}
@@ -615,7 +639,7 @@ function reduce_block(who, reason) {
function count_attackers() {
let count = 0
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (is_attacker(b))
++count
return count
@@ -623,35 +647,35 @@ function count_attackers() {
function count_defenders() {
let count = 0
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (is_defender(b))
++count
return count
}
const CELTIC_BLOCKS = [
- "Ulster Infantry",
- "Wales Archers",
- "Wales Infantry",
+ block_index["Ulster Infantry"],
+ block_index["Wales Archers"],
+ block_index["Wales Infantry"],
]
function celtic_unity_roll(who) {
let die = roll_d6()
if (die >= 5) {
- log(who + " rolled " + DIE_HIT[die] + " for Celtic unity and returned to the draw pool.")
+ log(block_name(who) + " rolled " + DIE_HIT[die] + " for Celtic unity and returned to the draw pool.")
disband(who)
} else {
- log(who + " rolled " + DIE_MISS[die] + " for Celtic unity \u2013 no effect.")
+ log(block_name(who) + " rolled " + DIE_MISS[die] + " for Celtic unity \u2013 no effect.")
}
}
// SETUP
function reset_blocks() {
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
game.steps[b] = block_max_steps(b)
if (block_type(b) === 'nobles')
- game.location[b] = null
+ game.location[b] = NOWHERE
else if (block_owner(b) === ENGLAND)
game.location[b] = E_BAG
else
@@ -660,78 +684,82 @@ function reset_blocks() {
}
function deploy_noble(owner, area, name) {
- if (name in BLOCKS) {
- game.location[name] = area
+ if (name in block_index) {
+ game.location[block_index[name]] = area
} else {
let friend = find_noble(owner, name)
let enemy = find_noble(ENEMY[owner], name)
game.location[friend] = area
- game.location[enemy] = null
+ game.location[enemy] = NOWHERE
}
}
function deploy_block(area, block) {
+ block = block_index[block]
game.location[block] = area
}
function draw_from_bag(bag, exclude_list) {
let list = []
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (exclude_list && exclude_list.includes(b))
continue
if (game.location[b] === bag)
list.push(b)
}
+ if (list.length === 0)
+ return NOBODY
return list[random(list.length)]
}
function deploy_english(count) {
let list = []
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (game.location[b] === E_BAG)
list.push(b)
for (let i = 0; i < count; ++i) {
let x = random(list.length)
let b = list[x]
list.splice(x,1)
- game.location[b] = ENGLAND
+ game.location[b] = AREA_ENGLAND
game.steps[b] = block_max_steps(b)
}
}
function deploy_off_map(block) {
- game.location[block] = null
+ block = block_index[block]
+ game.location[block] = NOWHERE
}
function setup_braveheart() {
reset_blocks()
- deploy_noble("England", "Badenoch", "Comyn")
- deploy_noble("England", "Angus", "Angus")
- deploy_noble("England", "Argyll", "Argyll")
- deploy_noble("England", "Mar", "Mar")
- deploy_noble("England", "Lennox", "Lennox")
- deploy_noble("England", "Buchan", "Buchan")
- deploy_noble("England", "Ross", "Ross")
- deploy_noble("England", "Atholl", "Atholl")
- deploy_noble("England", "Dunbar", "Dunbar")
- deploy_noble("England", "Mentieth", "Mentieth")
- deploy_noble("England", "Lanark", "Steward")
-
- deploy_block("Lothian", "Cumbria Infantry")
- deploy_block("Mentieth", "Northumber Infantry")
+ deploy_noble("England", AREA_BADENOCH, "Comyn")
+ deploy_noble("England", AREA_ANGUS, "Angus")
+ deploy_noble("England", AREA_ARGYLL, "Argyll")
+ deploy_noble("England", AREA_MAR, "Mar")
+ deploy_noble("England", AREA_LENNOX, "Lennox")
+ deploy_noble("England", AREA_BUCHAN, "Buchan")
+ deploy_noble("England", AREA_ROSS, "Ross")
+ deploy_noble("England", AREA_ATHOLL, "Atholl")
+ deploy_noble("England", AREA_DUNBAR, "Dunbar")
+ deploy_noble("England", AREA_MENTIETH, "Mentieth")
+ deploy_noble("England", AREA_LANARK, "Steward")
+
+ deploy_block(AREA_LOTHIAN, "Cumbria Infantry")
+ deploy_block(AREA_MENTIETH, "Northumber Infantry")
deploy_english(4)
- deploy_noble("Scotland", "Annan", "Bruce")
- deploy_noble("Scotland", "Galloway", "Galloway")
+ deploy_noble("Scotland", AREA_ANNAN, "Bruce")
+ deploy_noble("Scotland", AREA_GALLOWAY, "Galloway")
- deploy_block("Fife", "Wallace")
- deploy_block("Fife", "Douglas")
- deploy_block("Fife", "Barclay")
- deploy_block("Moray", "Moray")
- deploy_block("Moray", "Fraser")
- deploy_block("Strathspey", "Grant")
+ deploy_block(AREA_FIFE, "Wallace")
+ deploy_block(AREA_FIFE, "Douglas")
+ deploy_block(AREA_FIFE, "Barclay")
+ deploy_block(AREA_MORAY, "Moray")
+ deploy_block(AREA_MORAY, "Fraser")
+ deploy_block(AREA_STRATHSPEY, "Grant")
deploy_off_map("King")
deploy_off_map("French Knights")
@@ -745,33 +773,33 @@ function setup_braveheart() {
function setup_the_bruce() {
reset_blocks()
- deploy_noble("England", "Badenoch", "Comyn")
- deploy_noble("England", "Angus", "Angus")
- deploy_noble("England", "Argyll", "Argyll")
- deploy_noble("England", "Buchan", "Buchan")
- deploy_noble("England", "Galloway", "Galloway")
- deploy_noble("England", "Ross", "Ross")
- deploy_noble("England", "Mentieth", "Mentieth")
- deploy_noble("England", "Lanark", "Steward")
+ deploy_noble("England", AREA_BADENOCH, "Comyn")
+ deploy_noble("England", AREA_ANGUS, "Angus")
+ deploy_noble("England", AREA_ARGYLL, "Argyll")
+ deploy_noble("England", AREA_BUCHAN, "Buchan")
+ deploy_noble("England", AREA_GALLOWAY, "Galloway")
+ deploy_noble("England", AREA_ROSS, "Ross")
+ deploy_noble("England", AREA_MENTIETH, "Mentieth")
+ deploy_noble("England", AREA_LANARK, "Steward")
- deploy_block("Moray", "Cumbria Infantry")
- deploy_block("Mentieth", "Northumber Infantry")
- deploy_block("Lothian", "Durham Infantry")
- deploy_block("Lanark", "Westmor Infantry")
+ deploy_block(AREA_MORAY, "Cumbria Infantry")
+ deploy_block(AREA_MENTIETH, "Northumber Infantry")
+ deploy_block(AREA_LOTHIAN, "Durham Infantry")
+ deploy_block(AREA_LANARK, "Westmor Infantry")
deploy_english(6)
- deploy_noble("Scotland", "Dunbar", "Dunbar")
- deploy_noble("Scotland", "Lennox", "Lennox")
- deploy_noble("Scotland", "Atholl", "Atholl")
- deploy_noble("Scotland", "Mar", "Mar")
- deploy_noble("Scotland", "Carrick", "Bruce")
+ deploy_noble("Scotland", AREA_DUNBAR, "Dunbar")
+ deploy_noble("Scotland", AREA_LENNOX, "Lennox")
+ deploy_noble("Scotland", AREA_ATHOLL, "Atholl")
+ deploy_noble("Scotland", AREA_MAR, "Mar")
+ deploy_noble("Scotland", AREA_CARRICK, "Bruce")
- deploy_block("Fife", "King")
- deploy_block("Fife", "Douglas")
- deploy_block("Fife", "Barclay")
- deploy_block("Lennox", "Campbell")
- deploy_block("Carrick", "Lindsay")
+ deploy_block(AREA_FIFE, "King")
+ deploy_block(AREA_FIFE, "Douglas")
+ deploy_block(AREA_FIFE, "Barclay")
+ deploy_block(AREA_LENNOX, "Campbell")
+ deploy_block(AREA_CARRICK, "Lindsay")
deploy_off_map("Moray")
deploy_off_map("Wallace")
@@ -814,7 +842,7 @@ function start_game_turn() {
game.last_used = {}
game.attacker = {}
game.reserves = []
- game.moved = {}
+ game.moved = []
goto_card_phase()
}
@@ -842,7 +870,7 @@ function end_game_turn() {
function goto_card_phase() {
game.e_card = 0
game.s_card = 0
- game.show_cards = false
+ game.show_cards = 0
game.state = 'play_card'
game.active = BOTH
}
@@ -911,7 +939,7 @@ function reveal_cards() {
log("")
log("England played " + CARDS[game.e_card].name + ".")
log("Scotland played " + CARDS[game.s_card].name + ".")
- game.show_cards = true
+ game.show_cards = 1
let ec = CARDS[game.e_card]
let sc = CARDS[game.s_card]
@@ -942,7 +970,7 @@ function reveal_cards() {
function start_player_turn() {
log("")
- log(".turn " + game.active)
+ log(".h2 " + game.active)
reset_border_limits()
let ec = CARDS[game.e_card]
let sc = CARDS[game.s_card]
@@ -973,15 +1001,15 @@ function end_player_turn() {
// CORONATION
function can_crown_bruce() {
- return game.location[WALLACE] === null && game.location[S_BRUCE] === "Fife"
+ return game.location[B_WALLACE] === NOWHERE && game.location[B_BRUCE_S] === AREA_FIFE
}
function can_crown_comyn() {
- return game.location[WALLACE] === null && game.location[S_COMYN] === "Fife"
+ return game.location[B_WALLACE] === NOWHERE && game.location[B_COMYN_S] === AREA_FIFE
}
function can_crown_balliol() {
- return game.year >= 1301 && is_on_map(FRENCH_KNIGHTS)
+ return game.year >= 1301 && is_on_map(B_FRENCH_KNIGHTS)
}
function goto_event(event) {
@@ -1010,22 +1038,22 @@ states.coronation_event = {
crown_bruce: function () {
log("Bruce was crowned King!")
game.scottish_king = true
- game.location[KING] = "Fife"
- game.steps[KING] = block_max_steps(KING)
+ game.location[B_KING] = AREA_FIFE
+ game.steps[B_KING] = block_max_steps(B_KING)
defect_comyn_nobles()
},
crown_comyn: function () {
log("Comyn was crowned King!")
game.scottish_king = true
- game.location[KING] = "Fife"
- game.steps[KING] = block_max_steps(KING)
+ game.location[B_KING] = AREA_FIFE
+ game.steps[B_KING] = block_max_steps(B_KING)
defect_bruce_nobles()
},
return_of_the_king: function () {
log("Return of the King!")
game.scottish_king = true
- game.location[KING] = game.location[FRENCH_KNIGHTS]
- game.steps[KING] = block_max_steps(KING)
+ game.location[B_KING] = game.location[B_FRENCH_KNIGHTS]
+ game.steps[B_KING] = block_max_steps(B_KING)
defect_bruce_nobles()
},
play_event: function () {
@@ -1072,9 +1100,9 @@ states.coronation_battles = {
if (is_inactive_player(current))
return view.prompt = "Waiting for " + game.active + " to choose a battle."
view.prompt = "Coronation: Choose the next battle to fight!"
- for (let where of AREALIST)
+ for (let where = first_map_area; where < area_count; ++where)
if (is_contested_area(where))
- gen_action(view, 'area', where)
+ gen_action_area(view, where)
},
area: function (where) {
start_battle(where, 'coronation')
@@ -1113,7 +1141,7 @@ states.herald = {
return view.prompt = "Waiting for " + game.active + " to choose a noble."
view.prompt = "Herald: Name an enemy noble to try to convert to your side."
gen_action(view, 'pass')
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (is_enemy_noble(b))
gen_action(view, 'noble', block_name(b))
},
@@ -1141,7 +1169,7 @@ states.herald = {
function goto_victuals() {
game.victuals = 3
- game.where = null
+ game.where = NOWHERE
game.state = 'victuals'
game.turn_log = []
clear_undo()
@@ -1154,11 +1182,11 @@ states.victuals = {
gen_action_undo(view)
let done = true
if (game.victuals > 0) {
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (is_on_map(b) && block_owner(b) === game.active) {
if (game.steps[b] < block_max_steps(b)) {
if (!game.where || game.location[b] === game.where) {
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
done = false
}
}
@@ -1175,7 +1203,7 @@ states.victuals = {
block: function (who) {
push_undo()
game.where = game.location[who]
- game.turn_log.push([game.where])
+ game.turn_log.push([area_name(game.where)])
++game.steps[who]
--game.victuals
},
@@ -1183,7 +1211,7 @@ states.victuals = {
print_turn_log("victualed")
clear_undo()
delete game.victuals
- game.where = null
+ game.where = NOWHERE
end_player_turn()
},
undo: pop_undo
@@ -1200,11 +1228,11 @@ states.pillage = {
return view.prompt = "Waiting for " + game.active + " to pillage."
view.prompt = "Pillage: Pillage one enemy group adjacent to a friendly group."
gen_action(view, 'pass')
- for (let from of AREALIST) {
+ for (let from = first_map_area; from < area_count; ++from) {
if (is_friendly_area(from)) {
for (let to of AREAS[from].exits)
if (is_contested_area(to) || is_enemy_area(to))
- gen_action(view, 'area', to)
+ gen_action_area(view, to)
}
}
},
@@ -1224,11 +1252,11 @@ function pillage_victims() {
return block_owner(b) === game.active && game.location[b] === game.where
}
let max = 0
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (is_candidate(b) && game.steps[b] > max)
max = game.steps[b]
let list = []
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (is_candidate(b) && game.steps[b] === max)
list.push(b)
return list
@@ -1238,9 +1266,9 @@ states.pillage_hits = {
prompt: function (view, current) {
if (is_inactive_player(current))
return view.prompt = "Waiting for " + game.active + " to apply pillage hits."
- view.prompt = "Pillage: Apply two hits in " + game.where + "."
+ view.prompt = "Pillage: Apply two hits in " + area_name(game.where) + "."
for (let b of pillage_victims())
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
},
block: function (who) {
--game.pillage
@@ -1250,7 +1278,7 @@ states.pillage_hits = {
game.state = 'pillage_builds'
game.pillage = 2 - game.pillage
game.from = game.where
- game.where = null
+ game.where = NOWHERE
}
},
}
@@ -1263,20 +1291,20 @@ states.pillage_builds = {
let done = true
if (game.pillage > 0) {
if (game.where) {
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (block_owner(b) === game.active && game.location[b] === game.where) {
if (game.steps[b] < block_max_steps(b)) {
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
done = false
}
}
}
} else {
for (let to of AREAS[game.from].exits) {
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (block_owner(b) === game.active && game.location[b] === to) {
if (game.steps[b] < block_max_steps(b)) {
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
done = false
}
}
@@ -1294,7 +1322,7 @@ states.pillage_builds = {
block: function (who) {
push_undo()
game.where = game.location[who]
- game.turn_log.push([game.from, game.where])
+ game.turn_log.push([area_name(game.from), area_name(game.where)])
++game.steps[who]
--game.pillage
// TODO: auto-end pillage builds?
@@ -1304,7 +1332,7 @@ states.pillage_builds = {
clear_undo()
while (game.pillage > 0) {
--game.pillage
- game.turn_log.push([game.from])
+ game.turn_log.push([area_name(game.from)])
}
end_pillage(game.from)
},
@@ -1313,8 +1341,8 @@ states.pillage_builds = {
function end_pillage(where) {
print_turn_log("pillaged")
- game.from = null
- game.where = null
+ game.from = NOWHERE
+ game.where = NOWHERE
delete game.pillage
if (is_contested_area(where)) {
game.attacker[where] = ENEMY[game.active]
@@ -1326,8 +1354,8 @@ function end_pillage(where) {
function goto_sea_move() {
game.moves = 2
- game.from = null
- game.where = null
+ game.from = NOWHERE
+ game.where = NOWHERE
game.state = 'sea_move'
game.turn_log = []
clear_undo()
@@ -1341,12 +1369,12 @@ states.sea_move = {
gen_action_undo(view)
gen_action(view, 'end_move_phase')
if (game.moves > 0) {
- for (let b of BLOCKLIST) {
- if (b === NORSE)
+ for (let b = 0; b < block_count; ++b) {
+ if (b === B_NORSE)
continue
if (is_in_friendly_coastal_area(b) && block_owner(b) === game.active)
if (!game.from || game.location[b] === game.from)
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
}
}
},
@@ -1359,8 +1387,8 @@ states.sea_move = {
print_turn_log("sea moved")
clear_undo()
game.moves = 0
- game.from = null
- game.where = null
+ game.from = NOWHERE
+ game.where = NOWHERE
end_player_turn()
},
undo: pop_undo
@@ -1372,24 +1400,24 @@ states.sea_move_to = {
return view.prompt = "Waiting for " + game.active + " to sea move."
view.prompt = "Sea Move: Move one or two blocks from one coastal area to one other friendly coastal area."
gen_action_undo(view)
- gen_action(view, 'block', game.who)
+ gen_action_block(view, game.who)
if (game.where) {
- gen_action(view, 'area', game.where)
+ gen_action_area(view, game.where)
} else {
let from = game.location[game.who]
- for (let to of AREALIST)
+ for (let to = first_map_area; to < area_count; ++to)
if (to !== from && is_friendly_coastal_area(to))
- gen_action(view, 'area', to)
+ gen_action_area(view, to)
}
},
area: function (to) {
if (!game.from)
game.from = game.location[game.who]
- game.turn_log.push([game.from, to])
+ game.turn_log.push([area_name(game.from), area_name(to)])
game.location[game.who] = to
- game.moved[game.who] = true
+ set_add(game.moved, game.who)
game.where = to
- game.who = null
+ game.who = NOBODY
--game.moves
game.state = 'sea_move'
},
@@ -1416,18 +1444,18 @@ states.move_who = {
view.prompt = "Choose an army to move. " + game.moves + "MP left."
gen_action_undo(view)
gen_action(view, 'end_move_phase')
- for (let b of BLOCKLIST) {
- if (b === NORSE && game.active === SCOTLAND && is_on_map(NORSE)) {
- if (!game.moved[b] && game.moves > 0 && !is_pinned(game.location[NORSE]))
- gen_action(view, 'block', NORSE)
+ for (let b = 0; b < block_count; ++b) {
+ if (b === B_NORSE && game.active === SCOTLAND && is_on_map(B_NORSE)) {
+ if (!set_has(game.moved, b) && game.moves > 0 && !is_pinned(game.location[B_NORSE]))
+ gen_action_block(view, B_NORSE)
}
if (can_block_move(b)) {
if (game.moves === 0) {
let from = game.location[b]
if (game.activated.includes(from))
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
} else {
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
}
}
}
@@ -1437,7 +1465,7 @@ states.move_who = {
game.who = who
game.state = 'move_where'
game.origin = game.location[who]
- game.last_from = null
+ game.last_from = NOWHERE
game.distance = 0
},
end_move_phase: function () {
@@ -1462,7 +1490,7 @@ function move_block(who, from, to) {
return ATTACK_MARK
} else {
if (game.attacker[to] !== game.active || game.main_border[to] !== from || game.main_origin[to] !== game.origin) {
- game.reserves.push(who)
+ set_add(game.reserves, who)
return RESERVE_MARK
} else {
return ATTACK_MARK
@@ -1478,19 +1506,19 @@ states.move_where = {
return view.prompt = "Waiting for " + game.active + " to move."
view.prompt = "Move " + block_name(game.who) + "."
gen_action_undo(view)
- gen_action(view, 'block', game.who)
+ gen_action_block(view, game.who)
let from = game.location[game.who]
- if (game.who === NORSE) {
- for (let to of AREALIST)
- if (to !== from && to !== ENGLAND && is_coastal_area(to))
+ if (game.who === B_NORSE) {
+ for (let to = first_map_area; to < area_count; ++to)
+ if (to !== from && to !== AREA_ENGLAND && is_coastal_area(to))
if (game.truce !== game.active || !is_enemy_area(to))
- gen_action(view, 'area', to)
+ gen_action_area(view, to)
} else {
if (game.distance > 0)
- gen_action(view, 'area', from)
+ gen_action_area(view, from)
for (let to of AREAS[from].exits) {
if (to !== game.last_from && can_block_move_to(game.who, from, to))
- gen_action(view, 'area', to)
+ gen_action_area(view, to)
}
}
},
@@ -1506,32 +1534,32 @@ states.move_where = {
end_move()
return
}
- if (game.who === NORSE) {
+ if (game.who === B_NORSE) {
log("The Norse moved by sea.")
game.location[game.who] = to
- game.moved[game.who] = true
+ set_add(game.moved, game.who)
if (is_contested_area(to)) {
if (!game.attacker[to]) {
- game.turn_log.push([from, to + ATTACK_MARK + " (Norse)"])
+ game.turn_log.push([area_name(from), area_name(to) + ATTACK_MARK + " (Norse)"])
game.attacker[to] = game.active
} else {
- game.turn_log.push([from, to + RESERVE_MARK + " (Norse)"])
- game.reserves.push(game.who)
+ game.turn_log.push([area_name(from), area_name(to) + RESERVE_MARK + " (Norse)"])
+ set_add(game.reserves, game.who)
}
} else {
- game.turn_log.push([from, to + " (Norse)"])
+ game.turn_log.push([area_name(from), area_name(to) + " (Norse)"])
}
--game.moves
- game.who = null
+ game.who = NOBODY
game.state = 'move_who'
} else {
if (game.distance === 0)
- game.move_buf = [ from ]
+ game.move_buf = [ area_name(from) ]
let mark = move_block(game.who, from, to)
if (mark)
- game.move_buf.push(to + mark)
+ game.move_buf.push(area_name(to) + mark)
else
- game.move_buf.push(to)
+ game.move_buf.push(area_name(to))
game.last_from = from
if (!can_block_continue(game.who, from, to))
end_move()
@@ -1543,22 +1571,22 @@ states.move_where = {
function end_move() {
if (game.distance > 0) {
let to = game.location[game.who]
- if (game.origin === ENGLAND || to === ENGLAND) {
+ if (game.origin === AREA_ENGLAND || to === AREA_ENGLAND) {
log(game.active + " crossed the Anglo-Scottish border.")
game.moves --
} else if (!game.activated.includes(game.origin)) {
- log(game.active + " activated " + game.origin + ".")
+ log(game.active + " activated " + area_name(game.origin) + ".")
game.activated.push(game.origin)
game.moves --
}
- game.moved[game.who] = true
+ set_add(game.moved, game.who)
game.turn_log.push(game.move_buf)
}
delete game.move_buf
- game.who = null
+ game.who = NOBODY
game.distance = 0
- game.origin = null
- game.last_from = null
+ game.origin = NOWHERE
+ game.last_from = NOWHERE
game.state = 'move_who'
}
@@ -1578,9 +1606,9 @@ states.battle_phase = {
if (is_inactive_player(current))
return view.prompt = "Waiting for " + game.active + " to choose a battle."
view.prompt = "Choose the next battle to fight!"
- for (let where of AREALIST)
+ for (let where = first_map_area; where < area_count; ++where)
if (is_contested_area(where))
- gen_action(view, 'area', where)
+ gen_action_area(view, where)
},
area: function (where) {
start_battle(where, 'battle')
@@ -1593,9 +1621,9 @@ function start_battle(where, reason) {
game.flash = ""
log("")
if (reason !== 'battle')
- log("Defection battle in " + where)
+ log(".h3 Defection battle in " + area_name(where))
else
- log("Battle in " + where)
+ log(".h3 Battle in " + area_name(where))
game.where = where
game.battle_round = 0
game.state = 'battle_round'
@@ -1611,12 +1639,12 @@ function resume_battle() {
function end_battle() {
if (game.turn_log && game.turn_log.length > 0)
- print_turn_log_no_active("Retreated from " + game.where + ":")
+ print_turn_log_no_active("Retreated from " + area_name(game.where) + ":")
game.flash = ""
game.battle_round = 0
reset_border_limits()
- game.moved = {}
+ game.moved = []
game.active = game.attacker[game.where]
let victor = game.active
@@ -1624,27 +1652,27 @@ function end_battle() {
victor = ENEMY[game.active]
else if (is_enemy_area(game.where))
victor = ENEMY[game.active]
- log(victor + " won the battle in " + game.where + "!")
+ log(victor + " won the battle in " + area_name(game.where) + "!")
goto_retreat()
}
function bring_on_reserves() {
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (game.location[b] === game.where)
- remove_from_array(game.reserves, b)
+ set_delete(game.reserves, b)
}
function start_battle_round() {
if (++game.battle_round <= 3) {
if (game.turn_log && game.turn_log.length > 0)
- print_turn_log_no_active("Retreated from " + game.where + ":")
+ print_turn_log_no_active("Retreated from " + area_name(game.where) + ":")
game.turn_log = []
- log("~ Battle Round " + game.battle_round + " ~")
+ log(".h4 Battle Round " + game.battle_round)
reset_border_limits()
- game.moved = {}
+ game.moved = []
if (game.battle_round === 1) {
for (let b of CELTIC_BLOCKS)
@@ -1677,8 +1705,8 @@ function start_battle_round() {
function pump_battle_round() {
function filter_battle_blocks(ci, is_candidate) {
let output = null
- for (let b of BLOCKLIST) {
- if (is_candidate(b) && !game.moved[b]) {
+ for (let b = 0; b < block_count; ++b) {
+ if (is_candidate(b) && !set_has(game.moved, b)) {
if (block_initiative(b) === ci) {
if (!output)
output = []
@@ -1720,7 +1748,7 @@ function pump_battle_round() {
function pass_with_block(b) {
game.flash = block_name(b) + " passed."
log_battle(block_name(b) + " passed.")
- game.moved[b] = true
+ set_add(game.moved, b)
resume_battle()
}
@@ -1730,7 +1758,7 @@ function retreat_with_block(b) {
}
function fire_with_block(b) {
- game.moved[b] = true
+ set_add(game.moved, b)
let steps = game.steps[b]
let fire = block_fire_power(b, game.where)
let printed_fire = block_printed_fire_power(b)
@@ -1775,11 +1803,11 @@ states.battle_round = {
return view.prompt = "Waiting for " + game.active + " to choose a combat action."
view.prompt = "Fire, retreat, or pass with an army."
for (let b of game.battle_list) {
- gen_action(view, 'block', b)
- gen_action(view, 'battle_fire', b)
- gen_action(view, 'battle_pass', b)
+ gen_action_block(view, b)
+ gen_action_battle(view, 'battle_fire', b)
+ gen_action_battle(view, 'battle_pass', b)
if (can_block_retreat(b))
- gen_action(view, 'battle_retreat', b)
+ gen_action_battle(view, 'battle_retreat', b)
}
},
block: function (who) {
@@ -1843,11 +1871,11 @@ function apply_hit(who) {
function list_victims(p) {
let is_candidate = (p === game.attacker[game.where]) ? is_attacker : is_defender
let max = 0
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (is_candidate(b) && game.steps[b] > max)
max = game.steps[b]
let list = []
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (is_candidate(b) && game.steps[b] === max)
list.push(b)
return list
@@ -1860,8 +1888,8 @@ states.battle_hits = {
return view.prompt = "Waiting for " + game.active + " to assign hits."
view.prompt = "Assign " + game.hits + (game.hits !== 1 ? " hits" : " hit") + " to your armies."
for (let b of game.battle_list) {
- gen_action(view, 'block', b)
- gen_action(view, 'battle_hit', b)
+ gen_action_block(view, b)
+ gen_action_battle(view, 'battle_hit', b)
}
},
block: function (who) {
@@ -1890,9 +1918,9 @@ states.retreat = {
view.prompt = "Retreat: Choose an army to move."
gen_action_undo(view)
let can_retreat = false
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (game.location[b] === game.where && can_block_retreat(b)) {
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
can_retreat = true
}
}
@@ -1901,7 +1929,7 @@ states.retreat = {
},
end_retreat: function () {
clear_undo()
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (game.location[b] === game.where && block_owner(b) === game.active)
eliminate_block(b, 'retreat')
print_turn_log("retreated")
@@ -1920,13 +1948,13 @@ states.retreat_to = {
if (is_inactive_player(current))
return view.prompt = "Waiting for " + game.active + " to retreat."
gen_action_undo(view)
- gen_action(view, 'block', game.who)
+ gen_action_block(view, game.who)
let can_retreat = false
- if (game.who === NORSE) {
+ if (game.who === B_NORSE) {
view.prompt = "Retreat: Move the army to a friendly coastal area."
- for (let to of AREALIST) {
- if (to !== game.where && to !== ENGLAND && is_friendly_coastal_area(to)) {
- gen_action(view, 'area', to)
+ for (let to = first_map_area; to < area_count; ++to) {
+ if (to !== game.where && to !== AREA_ENGLAND && is_friendly_coastal_area(to)) {
+ gen_action_area(view, to)
can_retreat = true
}
}
@@ -1934,7 +1962,7 @@ states.retreat_to = {
view.prompt = "Retreat: Move the army to a friendly or neutral area."
for (let to of AREAS[game.where].exits) {
if (can_block_retreat_to(game.who, to)) {
- gen_action(view, 'area', to)
+ gen_action_area(view, to)
can_retreat = true
}
}
@@ -1944,19 +1972,19 @@ states.retreat_to = {
},
area: function (to) {
let from = game.where
- if (game.who === NORSE) {
- game.turn_log.push([from, to + " (Norse)"])
+ if (game.who === B_NORSE) {
+ game.turn_log.push([area_name(from), area_name(to) + " (Norse)"])
game.location[game.who] = to
} else {
- game.turn_log.push([from, to])
+ game.turn_log.push([area_name(from), area_name(to)])
move_block(game.who, game.where, to)
}
- game.who = null
+ game.who = NOBODY
game.state = 'retreat'
},
eliminate: function () {
eliminate_block(game.who, 'retreat')
- game.who = null
+ game.who = NOBODY
game.state = 'retreat'
},
block: pop_undo,
@@ -1968,23 +1996,23 @@ states.retreat_in_battle = {
if (is_inactive_player(current))
return view.prompt = "Waiting for " + game.active + " to retreat."
gen_action(view, 'undo')
- gen_action(view, 'block', game.who)
- if (game.who === NORSE) {
+ gen_action_block(view, game.who)
+ if (game.who === B_NORSE) {
view.prompt = "Retreat: Move the army to a friendly coastal area."
- for (let to of AREALIST)
- if (to !== game.where && to !== ENGLAND && is_friendly_coastal_area(to))
- gen_action(view, 'area', to)
+ for (let to = first_map_area; to < area_count; ++to)
+ if (to !== game.where && to !== AREA_ENGLAND && is_friendly_coastal_area(to))
+ gen_action_area(view, to)
} else {
view.prompt = "Retreat: Move the army to a friendly or neutral area."
for (let to of AREAS[game.where].exits)
if (can_block_retreat_to(game.who, to))
- gen_action(view, 'area', to)
+ gen_action_area(view, to)
}
},
area: function (to) {
- game.turn_log.push([game.active, to])
- if (game.who === NORSE) {
- game.flash = "Norse retreated to " + to + "."
+ game.turn_log.push([game.active, area_name(to)])
+ if (game.who === B_NORSE) {
+ game.flash = "Norse retreated to " + area_name(to) + "."
log_battle(game.flash)
game.location[game.who] = to
} else {
@@ -1992,15 +2020,15 @@ states.retreat_in_battle = {
log_battle(game.flash)
move_block(game.who, game.where, to)
}
- game.who = null
+ game.who = NOBODY
resume_battle()
},
block: function () {
- game.who = null
+ game.who = NOBODY
resume_battle()
},
undo: function () {
- game.who = null
+ game.who = NOBODY
resume_battle()
}
}
@@ -2021,9 +2049,9 @@ states.regroup = {
view.prompt = "Regroup: Choose an army to move."
gen_action_undo(view)
gen_action(view, 'end_regroup')
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (game.location[b] === game.where && can_block_regroup(b))
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
},
block: function (who) {
push_undo()
@@ -2032,8 +2060,8 @@ states.regroup = {
},
end_regroup: function () {
print_turn_log("regrouped")
- game.attacker[game.where] = null
- game.where = null
+ game.attacker[game.where] = null // XXX ???
+ game.where = NOWHERE
clear_undo()
game.active = game.battle_active
delete game.battle_active
@@ -2063,27 +2091,27 @@ states.regroup_to = {
return view.prompt = "Waiting for " + game.active + " to regroup."
view.prompt = "Regroup: Move the army to a friendly or neutral area."
gen_action_undo(view)
- gen_action(view, 'block', game.who)
- if (game.who === NORSE) {
- for (let to of AREALIST)
- if (to !== game.where && to !== ENGLAND && is_friendly_coastal_area(to))
- gen_action(view, 'area', to)
+ gen_action_block(view, game.who)
+ if (game.who === B_NORSE) {
+ for (let to = first_map_area; to < area_count; ++to)
+ if (to !== game.where && to !== AREA_ENGLAND && is_friendly_coastal_area(to))
+ gen_action_area(view, to)
} else {
for (let to of AREAS[game.where].exits)
if (can_block_regroup_to(game.who, to))
- gen_action(view, 'area', to)
+ gen_action_area(view, to)
}
},
area: function (to) {
let from = game.where
- if (game.who === NORSE) {
- game.turn_log.push([from, to + " (Norse)"])
+ if (game.who === B_NORSE) {
+ game.turn_log.push([area_name(from), area_name(to) + " (Norse)"])
game.location[game.who] = to
} else {
- game.turn_log.push([from, to])
+ game.turn_log.push([area_name(from), area_name(to)])
move_block(game.who, game.where, to)
}
- game.who = null
+ game.who = NOBODY
game.state = 'regroup'
},
block: pop_undo,
@@ -2094,7 +2122,7 @@ states.regroup_to = {
function count_non_noble_english_blocks_on_map() {
let count = 0
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (block_owner(b) === ENGLAND && block_type(b) !== 'nobles')
if (is_on_map(b))
++count
@@ -2103,7 +2131,7 @@ function count_non_noble_english_blocks_on_map() {
function goto_border_raids() {
game.active = ENGLAND
- if (is_enemy_area(ENGLAND)) {
+ if (is_enemy_area(AREA_ENGLAND)) {
log("Scotland raided in England.")
if (count_non_noble_english_blocks_on_map() > 0) {
game.state = 'border_raids'
@@ -2121,10 +2149,10 @@ states.border_raids = {
if (is_inactive_player(current))
return view.prompt = "Waiting for England to choose a border raid victim."
view.prompt = "Border Raids: Eliminate a non-Noble block."
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (block_owner(b) === ENGLAND && block_type(b) !== 'nobles')
if (is_on_map(b))
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
},
block: function (who) {
eliminate_block(who, 'border_raids')
@@ -2135,7 +2163,7 @@ states.border_raids = {
// WINTERING
function goto_winter_turn() {
- game.moved = {}
+ game.moved = []
log("")
log(".h1 Winter of " + game.year)
log("")
@@ -2143,18 +2171,18 @@ function goto_winter_turn() {
}
function is_bruce(who) {
- return who === E_BRUCE || who === S_BRUCE
+ return who === B_BRUCE_E || who === B_BRUCE_S
}
function is_comyn(who) {
- return who === E_COMYN || who === S_COMYN
+ return who === B_COMYN_E || who === B_COMYN_S
}
function find_noble_home(who) {
- for (let where of AREALIST)
+ for (let where = first_map_area; where < area_count; ++where)
if (AREAS[where].home === block_name(who))
return where
- return null
+ return NOWHERE
}
function go_home_to(who, home, defected = false) {
@@ -2167,9 +2195,9 @@ function go_home_to(who, home, defected = false) {
defected = true
}
if (defected)
- game.turn_log.push([name, home + " \u2727"])
+ game.turn_log.push([name, area_name(home) + " \u2727"])
else
- game.turn_log.push([name, home])
+ game.turn_log.push([name, area_name(home)])
}
}
@@ -2180,7 +2208,7 @@ function go_home(who) {
function english_nobles_go_home() {
game.turn_log = []
game.active = ENGLAND
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (block_owner(b) === ENGLAND && block_type(b) === 'nobles' && game.location[b])
if (!is_bruce(b) && !is_comyn(b))
go_home(b)
@@ -2195,7 +2223,7 @@ function english_nobles_go_home() {
function scottish_nobles_go_home() {
game.turn_log = []
game.active = SCOTLAND
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (block_owner(b) === SCOTLAND && block_type(b) === 'nobles' && game.location[b])
if (!is_bruce(b) && !is_comyn(b))
go_home(b)
@@ -2205,16 +2233,16 @@ function scottish_nobles_go_home() {
}
function goto_e_bruce() {
- game.who = E_BRUCE
- if (game.location[E_BRUCE] && !game.bruce_home)
+ game.who = B_BRUCE_E
+ if (game.location[B_BRUCE_E] && !game.bruce_home)
send_bruce_home()
else
end_bruce()
}
function goto_s_bruce() {
- game.who = S_BRUCE
- if (game.location[S_BRUCE] && !game.bruce_home)
+ game.who = B_BRUCE_S
+ if (game.location[B_BRUCE_S] && !game.bruce_home)
send_bruce_home()
else
end_bruce()
@@ -2222,16 +2250,16 @@ function goto_s_bruce() {
function send_bruce_home() {
game.bruce_home = true
- let annan = is_friendly_or_neutral_area("Annan")
- let carrick = is_friendly_or_neutral_area("Carrick")
+ let annan = is_friendly_or_neutral_area(AREA_ANNAN)
+ let carrick = is_friendly_or_neutral_area(AREA_CARRICK)
if (annan && !carrick) {
- go_home_to(game.who, "Annan")
- game.who = null
+ go_home_to(game.who, AREA_ANNAN)
+ game.who = NOBODY
return end_bruce()
}
if (carrick && !annan) {
- go_home_to(game.who, "Carrick")
- game.who = null
+ go_home_to(game.who, AREA_CARRICK)
+ game.who = NOBODY
return end_bruce()
}
if (!annan && !carrick) {
@@ -2249,18 +2277,18 @@ states.bruce = {
if (is_inactive_player(current))
return view.prompt = "Waiting for " + game.active + " to move Bruce to one of his home areas."
view.prompt = "Nobles go Home: Move Bruce to one of his home areas."
- gen_action(view, 'area', "Annan")
- gen_action(view, 'area', "Carrick")
+ gen_action_area(view, AREA_ANNAN)
+ gen_action_area(view, AREA_CARRICK)
},
area: function (to) {
go_home_to(game.who, to, game.bruce_defected)
- game.who = null
+ game.who = NOBODY
end_bruce()
},
}
function end_bruce() {
- game.who = null
+ game.who = NOBODY
game.active = game.going_home
delete game.bruce_defected
if (game.going_home === ENGLAND)
@@ -2270,16 +2298,16 @@ function end_bruce() {
}
function goto_e_comyn() {
- game.who = E_COMYN
- if (game.location[E_COMYN] && !game.comyn_home)
+ game.who = B_COMYN_E
+ if (game.location[B_COMYN_E] && !game.comyn_home)
send_comyn_home()
else
end_comyn()
}
function goto_s_comyn() {
- game.who = S_COMYN
- if (game.location[S_COMYN] && !game.comyn_home)
+ game.who = B_COMYN_S
+ if (game.location[B_COMYN_S] && !game.comyn_home)
send_comyn_home()
else
end_comyn()
@@ -2287,16 +2315,16 @@ function goto_s_comyn() {
function send_comyn_home() {
game.comyn_home = true
- let badenoch = is_friendly_or_neutral_area("Badenoch")
- let lochaber = is_friendly_or_neutral_area("Lochaber")
+ let badenoch = is_friendly_or_neutral_area(AREA_BADENOCH)
+ let lochaber = is_friendly_or_neutral_area(AREA_LOCHABER)
if (badenoch && !lochaber) {
- go_home_to(game.who, "Badenoch")
- game.who = null
+ go_home_to(game.who, AREA_BADENOCH)
+ game.who = NOBODY
return end_comyn()
}
if (lochaber && !badenoch) {
- go_home_to(game.who, "Lochaber")
- game.who = null
+ go_home_to(game.who, AREA_LOCHABER)
+ game.who = NOBODY
return end_comyn()
}
if (!lochaber && !badenoch) {
@@ -2314,18 +2342,18 @@ states.comyn = {
if (is_inactive_player(current))
return view.prompt = "Waiting for " + game.active + " to move Comyn to one of his home areas."
view.prompt = "Nobles go Home: Move Comyn to one of his home areas."
- gen_action(view, 'area', "Badenoch")
- gen_action(view, 'area', "Lochaber")
+ gen_action_area(view, AREA_BADENOCH)
+ gen_action_area(view, AREA_LOCHABER)
},
area: function (to) {
go_home_to(game.who, to, game.comyn_defected)
- game.who = null
+ game.who = NOBODY
end_comyn()
},
}
function end_comyn() {
- game.who = null
+ game.who = NOBODY
game.active = game.going_home
delete game.comyn_defected
if (game.active === ENGLAND) {
@@ -2341,10 +2369,10 @@ function goto_moray() {
delete game.bruce_home
delete game.comyn_home
- if (is_on_map(MORAY) && game.location[MORAY] !== "Moray" && is_friendly_or_neutral_area("Moray")) {
+ if (is_on_map(B_MORAY) && game.location[B_MORAY] !== AREA_MORAY && is_friendly_or_neutral_area(AREA_MORAY)) {
game.state = 'moray'
game.active = SCOTLAND
- game.who = MORAY
+ game.who = B_MORAY
} else {
goto_scottish_king()
}
@@ -2355,27 +2383,27 @@ states.moray = {
if (is_inactive_player(current))
return view.prompt = "Waiting for Scotland to move Moray."
view.prompt = "Nobles go Home: Move Moray to his home area or remain where he is."
- gen_action(view, 'area', game.location[MORAY])
- gen_action(view, 'area', "Moray")
+ gen_action_area(view, game.location[B_MORAY])
+ gen_action_area(view, AREA_MORAY)
},
disband: function () {
- game.turn_log.push(["Moray", "Pool"])
- disband(MORAY)
- game.who = null
+ game.turn_log.push([area_name(AREA_MORAY), "Pool"])
+ disband(B_MORAY)
+ game.who = NOBODY
goto_scottish_king()
},
area: function (to) {
- let from = game.location[MORAY]
+ let from = game.location[B_MORAY]
if (to !== from)
- game.turn_log.push(["Moray", to])
- game.location[MORAY] = to
- game.who = null
+ game.turn_log.push([area_name(AREA_MORAY), to])
+ game.location[B_MORAY] = to
+ game.who = NOBODY
goto_scottish_king()
},
}
function king_can_go_home(current) {
- for (let where of AREALIST)
+ for (let where = first_map_area; where < area_count; ++where)
if (where !== current && is_cathedral_area(where))
if (is_friendly_or_neutral_area(where))
return true
@@ -2393,18 +2421,18 @@ function goto_scottish_king() {
if (s > 7 || e > 7)
return goto_game_over()
// Moray is dead so there can be no tie.
- if (game.location[MORAY] === null)
+ if (game.location[B_MORAY] === NOWHERE)
return goto_game_over()
// Wallace is dead so there can be no tie breaker.
- if (game.location[WALLACE] === null)
+ if (game.location[B_WALLACE] === NOWHERE)
return goto_game_over()
// A tie is possible, need to continue to disband and build phase...
}
- if (is_on_map(KING) && king_can_go_home(game.location[KING])) {
+ if (is_on_map(B_KING) && king_can_go_home(game.location[B_KING])) {
game.state = 'scottish_king'
game.active = SCOTLAND
- game.who = KING
+ game.who = B_KING
} else {
goto_edward_wintering()
}
@@ -2415,37 +2443,37 @@ states.scottish_king = {
if (is_inactive_player(current))
return view.prompt = "Waiting for Scotland to move the King."
view.prompt = "Scottish King: Move the King to a cathedral or remain where he is."
- gen_action(view, 'area', game.location[KING])
- for (let where of AREALIST) {
+ gen_action_area(view, game.location[B_KING])
+ for (let where = first_map_area; where < area_count; ++where) {
if (is_cathedral_area(where))
if (is_friendly_or_neutral_area(where))
- gen_action(view, 'area', where)
+ gen_action_area(view, where)
}
},
disband: function () {
log("Scottish King disbanded.")
- disband(KING)
- game.who = null
+ disband(B_KING)
+ game.who = NOBODY
goto_edward_wintering()
},
area: function (to) {
- if (game.location[KING] !== to) {
- log("Scottish King moved to " + to + ".")
- game.location[KING] = to
+ if (game.location[B_KING] !== to) {
+ log("Scottish King moved to " + area_name(to) + ".")
+ game.location[B_KING] = to
}
- game.who = null
+ game.who = NOBODY
goto_edward_wintering()
},
}
function is_in_scotland(who) {
- return is_on_map(who) && game.location[who] !== ENGLAND
+ return is_on_map(who) && game.location[who] !== AREA_ENGLAND
}
function goto_edward_wintering() {
- if (game.edward === 1 && game.year !== 1306 && is_in_scotland(EDWARD) && !game.wintered_last_year) {
+ if (game.edward === 1 && game.year !== 1306 && is_in_scotland(B_EDWARD) && !game.wintered_last_year) {
game.active = ENGLAND
- game.who = EDWARD
+ game.who = B_EDWARD
game.state = 'edward_wintering'
return
}
@@ -2455,9 +2483,9 @@ function goto_edward_wintering() {
game.edward = 2
}
- if (is_on_map(EDWARD)) {
+ if (is_on_map(B_EDWARD)) {
log("Edward disbanded.")
- disband(EDWARD)
+ disband(B_EDWARD)
}
game.wintered_last_year = false
@@ -2466,15 +2494,15 @@ function goto_edward_wintering() {
function disband_edward() {
log("Edward disbanded.")
- disband(EDWARD)
- game.who = null
+ disband(B_EDWARD)
+ game.who = NOBODY
game.wintered_last_year = false
goto_english_disbanding()
}
function winter_edward() {
- log("Edward wintered in " + game.location[EDWARD] + ".")
- game.who = null
+ log("Edward wintered in " + area_name(game.location[B_EDWARD]) + ".")
+ game.who = NOBODY
game.wintered_last_year = true
goto_english_disbanding()
}
@@ -2486,8 +2514,8 @@ states.edward_wintering = {
view.prompt = "Edward Wintering: Winter in Scotland or disband."
gen_action(view, 'winter')
gen_action(view, 'disband')
- gen_action(view, 'area', game.location[EDWARD])
- gen_action(view, 'area', ENGLAND)
+ gen_action_area(view, game.location[B_EDWARD])
+ gen_action_area(view, AREA_ENGLAND)
},
winter: function () {
winter_edward()
@@ -2496,7 +2524,7 @@ states.edward_wintering = {
disband_edward()
},
area: function (to) {
- if (to === ENGLAND)
+ if (to === AREA_ENGLAND)
disband_edward()
else
winter_edward()
@@ -2507,13 +2535,13 @@ function goto_english_disbanding() {
game.active = ENGLAND
game.turn_log = []
let ask = false
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
let where = game.location[b]
// All (English) blocks in England must disband.
// Scottish blocks disband later during the castle limit check.
- if (where === ENGLAND && block_owner(b) === ENGLAND) {
- game.turn_log.push([ENGLAND])
+ if (where === AREA_ENGLAND && block_owner(b) === ENGLAND) {
+ game.turn_log.push([area_name(AREA_ENGLAND)])
disband(b)
}
@@ -2521,10 +2549,10 @@ function goto_english_disbanding() {
// Knights, Archers, & Hobelars must disband except when wintering with Edward.
let type = block_type(b)
if (type === 'knights' || type === 'archers' || type === 'hobelars') {
- if (where === game.location[EDWARD]) {
+ if (where === game.location[B_EDWARD]) {
ask = true
} else {
- game.turn_log.push([where])
+ game.turn_log.push([area_name(where)])
disband(b)
}
}
@@ -2553,14 +2581,14 @@ states.english_disbanding = {
// Mandatory disbanding
let okay_to_end = true
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (block_owner(b) === ENGLAND && is_on_map(b)) {
let where = game.location[b]
let type = block_type(b)
if (type === 'infantry') {
- if (!is_within_castle_limit(where) && where !== game.location[EDWARD]) {
+ if (!is_within_castle_limit(where) && where !== game.location[B_EDWARD]) {
okay_to_end = false
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
}
}
}
@@ -2575,20 +2603,20 @@ states.english_disbanding = {
// Voluntary disbanding
view.prompt = "English Disbanding: You may disband units to the pool."
gen_action(view, 'end_disbanding')
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (block_owner(b) === ENGLAND && is_on_map(b)) {
let type = block_type(b)
if (type === 'knights' || type === 'archers' || type === 'hobelars')
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
if (type === 'infantry')
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
}
}
}
},
block: function (who) {
push_undo()
- game.turn_log.push([game.location[who]])
+ game.turn_log.push([area_name(game.location[who])])
disband(who)
},
end_disbanding: function () {
@@ -2600,9 +2628,9 @@ states.english_disbanding = {
}
function heal_wallace() {
- let old = game.steps[WALLACE]
- game.steps[WALLACE] = Math.min(block_max_steps(WALLACE), game.steps[WALLACE] + 2)
- let n = game.steps[WALLACE] - old
+ let old = game.steps[B_WALLACE]
+ game.steps[B_WALLACE] = Math.min(block_max_steps(B_WALLACE), game.steps[B_WALLACE] + 2)
+ let n = game.steps[B_WALLACE] - old
if (n === 1)
log("Wallace gained 1 step.")
else if (n === 2)
@@ -2611,12 +2639,12 @@ function heal_wallace() {
function goto_wallace() {
game.active = SCOTLAND
- if (game.location[WALLACE] === "Selkirk") {
+ if (game.location[B_WALLACE] === AREA_SELKIRK) {
heal_wallace()
goto_scottish_disbanding()
- } else if (is_on_map(WALLACE) && is_friendly_or_neutral_area("Selkirk")) {
+ } else if (is_on_map(B_WALLACE) && is_friendly_or_neutral_area(AREA_SELKIRK)) {
game.state = 'wallace'
- game.who = WALLACE
+ game.who = B_WALLACE
} else {
goto_scottish_disbanding()
}
@@ -2627,16 +2655,16 @@ states.wallace = {
if (is_inactive_player(current))
return view.prompt = "Waiting for Scotland to move Wallace."
view.prompt = "Scottish Disbanding: Move Wallace to Selkirk and gain 2 steps or remain where he is."
- gen_action(view, 'area', game.location[WALLACE])
- gen_action(view, 'area', "Selkirk")
+ gen_action_area(view, game.location[B_WALLACE])
+ gen_action_area(view, AREA_SELKIRK)
},
area: function (to) {
- if (to === "Selkirk") {
- log("Wallace went home to " + to + ".")
+ if (to === AREA_SELKIRK) {
+ log("Wallace went home to " + area_name(to) + ".")
heal_wallace()
}
- game.location[WALLACE] = to
- game.who = null
+ game.location[B_WALLACE] = to
+ game.who = NOBODY
goto_scottish_disbanding()
},
}
@@ -2645,7 +2673,7 @@ function goto_scottish_disbanding() {
game.active = SCOTLAND
game.turn_log = []
let ask = false
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (block_owner(b) === SCOTLAND && is_on_map(b)) {
let type = block_type(b)
if (type !== 'nobles')
@@ -2670,16 +2698,16 @@ states.scottish_disbanding = {
// Mandatory disbanding
let okay_to_end = true
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (block_owner(b) === SCOTLAND && is_on_map(b)) {
let where = game.location[b]
- if (b === WALLACE && where === "Selkirk")
+ if (b === B_WALLACE && where === AREA_SELKIRK)
continue
let type = block_type(b)
if (type !== 'nobles') {
if (!is_within_castle_limit(where)) {
okay_to_end = false
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
}
}
}
@@ -2691,18 +2719,18 @@ states.scottish_disbanding = {
// Voluntary disbanding
view.prompt = "Scottish Disbanding: You may disband units to the pool."
gen_action(view, 'end_disbanding')
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (block_owner(b) === SCOTLAND && is_on_map(b)) {
let type = block_type(b)
if (type !== 'nobles')
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
}
}
}
},
block: function (who) {
push_undo()
- game.turn_log.push([game.location[who]])
+ game.turn_log.push([area_name(game.location[who])])
disband(who)
},
end_disbanding: function () {
@@ -2719,16 +2747,14 @@ function goto_scottish_builds() {
if (!game.french_knights && count_scottish_nobles() >= 8) {
log("French knights added to pool.")
game.french_knights = true
- game.location[FRENCH_KNIGHTS] = S_BAG
- game.steps[FRENCH_KNIGHTS] = block_max_steps(FRENCH_KNIGHTS)
+ game.location[B_FRENCH_KNIGHTS] = S_BAG
+ game.steps[B_FRENCH_KNIGHTS] = block_max_steps(B_FRENCH_KNIGHTS)
}
- game.rp = {}
- for (let where of AREALIST) {
- if (is_friendly_area(where)) {
+ game.rp = Array(area_count).fill(0)
+ for (let where = first_map_area; where < area_count; ++where)
+ if (is_friendly_area(where))
game.rp[where] = castle_limit(where)
- }
- }
game.state = 'scottish_builds'
game.turn_log = []
clear_undo()
@@ -2736,8 +2762,8 @@ function goto_scottish_builds() {
function can_build_scottish_block_in(where) {
if (is_under_castle_limit(where)) {
- if (where === "Lanark" || where === "Badenoch")
- return count_blocks_in_area_excluding(S_BAG, [ NORSE, FRENCH_KNIGHTS ]) > 0
+ if (where === AREA_LANARK || where === AREA_BADENOCH)
+ return count_blocks_in_area_excluding(S_BAG, [ B_NORSE, B_FRENCH_KNIGHTS ]) > 0
else
return count_blocks_in_area(S_BAG) > 0
}
@@ -2750,17 +2776,17 @@ states.scottish_builds = {
return view.prompt = "Waiting for Scotland to build."
gen_action_undo(view)
let done = true
- for (let where in game.rp) {
+ for (let where = 1; where < area_count; ++where) {
let rp = game.rp[where]
if (rp > 0) {
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (game.location[b] === where && game.steps[b] < block_max_steps(b)) {
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
done = false
}
}
if (can_build_scottish_block_in(where)) {
- gen_action(view, 'area', where)
+ gen_action_area(view, where)
done = false
}
}
@@ -2774,13 +2800,13 @@ states.scottish_builds = {
},
area: function (where) {
let who
- if (where === "Lanark" || where === "Badenoch")
- who = draw_from_bag(S_BAG, [ NORSE, FRENCH_KNIGHTS ])
+ if (where === AREA_LANARK || where === AREA_BADENOCH)
+ who = draw_from_bag(S_BAG, [ B_NORSE, B_FRENCH_KNIGHTS ])
else
who = draw_from_bag(S_BAG)
- if (who) {
+ if (who !== NOBODY) {
clear_undo() // no undo after drawing from the bag!
- game.turn_log.push([where])
+ game.turn_log.push([area_name(where)])
game.location[who] = where
game.steps[who] = 1
--game.rp[where]
@@ -2789,7 +2815,7 @@ states.scottish_builds = {
block: function (who) {
push_undo()
let where = game.location[who]
- game.turn_log.push([where])
+ game.turn_log.push([area_name(where)])
--game.rp[where]
++game.steps[who]
},
@@ -2804,8 +2830,8 @@ states.scottish_builds = {
function goto_english_builds() {
game.active = ENGLAND
- game.rp = {}
- for (let where of AREALIST)
+ game.rp = Array(area_count).fill(0)
+ for (let where = first_map_area; where < area_count; ++where)
if (is_friendly_area(where))
game.rp[where] = castle_limit(where)
game.state = 'english_builds'
@@ -2818,14 +2844,14 @@ states.english_builds = {
return view.prompt = "Waiting for England to build."
gen_action_undo(view)
let done = true
- for (let where in game.rp) {
+ for (let where = 1; where < area_count; ++where) {
let rp = game.rp[where]
if (rp > 0) {
- for (let b of BLOCKLIST) {
+ for (let b = 0; b < block_count; ++b) {
if (game.location[b] === where && game.steps[b] < block_max_steps(b)) {
let type = block_type(b)
if (type === 'nobles' || type === 'infantry') {
- gen_action(view, 'block', b)
+ gen_action_block(view, b)
done = false
}
}
@@ -2842,7 +2868,7 @@ states.english_builds = {
block: function (who) {
push_undo()
let where = game.location[who]
- game.turn_log.push([where])
+ game.turn_log.push([area_name(where)])
--game.rp[where]
++game.steps[who]
},
@@ -2856,7 +2882,7 @@ states.english_builds = {
}
function goto_english_feudal_levy() {
- if (!is_on_map(EDWARD)) {
+ if (!is_on_map(B_EDWARD)) {
let count = Math.ceil(count_blocks_in_area(E_BAG) / 2)
log("English feudal levy:\n" + count + " England")
deploy_english(count)
@@ -2893,7 +2919,7 @@ function goto_game_over() {
game.victory = "Scotland won by controlling the most nobles!"
game.result = SCOTLAND
} else {
- if (is_on_map(WALLACE)) {
+ if (is_on_map(B_WALLACE)) {
game.victory = "Tied for control of nobles. Scotland won because Wallace was on the map!"
game.result = SCOTLAND
} else {
@@ -2925,7 +2951,7 @@ function make_battle_view() {
battle.title += " \u2014 round " + game.battle_round + " of 3"
function fill_cell(cell, owner, fn) {
- for (let b of BLOCKLIST)
+ for (let b = 0; b < block_count; ++b)
if (game.location[b] === game.where & block_owner(b) === owner && fn(b))
cell.push(b)
}
@@ -2943,22 +2969,21 @@ exports.setup = function (seed, scenario, options) {
seed: seed,
log: [],
undo: [],
+ moves: 0,
+
+ location: [],
+ steps: [],
+ moved: [],
+ reserves: [],
attacker: {},
border_limit: {},
last_used: {},
- location: {},
- log: [],
main_border: {},
main_origin: {},
- moved: {},
- moves: 0,
- prompt: null,
- reserves: [],
- show_cards: false,
- steps: {},
- who: null,
- where: null,
+ show_cards: 0,
+ who: NOBODY,
+ where: NOWHERE,
}
if (options.rng)
@@ -2984,10 +3009,11 @@ exports.setup = function (seed, scenario, options) {
exports.action = function (state, current, action, arg) {
game = state
let S = states[game.state]
- if (action in S)
+ if (action in S) {
S[action](arg, current)
- else
- throw new Error("Invalid action: " + action)
+ } else {
+ throw new Error("Invalid action " + action + " in state " + game.state)
+ }
return game
}
@@ -3026,15 +3052,12 @@ exports.view = function(state, current) {
e_card: (game.show_cards || current === ENGLAND) ? game.e_card : 0,
s_card: (game.show_cards || current === SCOTLAND) ? game.s_card : 0,
hand: (current === ENGLAND) ? game.e_hand : (current === SCOTLAND) ? game.s_hand : observer_hand(),
- who: (game.active === current) ? game.who : null,
+ who: (game.active === current) ? game.who : NOBODY,
where: game.where,
location: game.location,
steps: game.steps,
moved: game.moved,
- battle: null,
active: game.active,
- prompt: null,
- actions: null,
}
states[game.state].prompt(view, current)
@@ -3044,3 +3067,145 @@ exports.view = function(state, current) {
return view
}
+
+// === COMMON LIBRARY ===
+
+// remove item at index (faster than splice)
+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
+ return array
+}
+
+// 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 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)
+}
+
+// Fast deep copy for objects without cycles
+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 clear_undo() {
+ if (game.undo.length > 0)
+ game.undo = []
+}
+
+function push_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)
+}
+
+function pop_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
+}