summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2023-06-05 13:31:15 +0200
committerTor Andersson <tor@ccxvii.net>2023-07-16 13:06:00 +0200
commit51f8e9aefed01f36127246b15563d7e9cc1801b8 (patch)
tree28d0c16b9247d1f33f49d99a874514114b6637d8
parentfa3fc7fd0184fc8500cbe21cee7440433d1d1c4c (diff)
downloadfield-cloth-gold-51f8e9aefed01f36127246b15563d7e9cc1801b8.tar.gz
All in one.
-rw-r--r--play.html188
-rw-r--r--play.js253
-rw-r--r--rules.js395
3 files changed, 700 insertions, 136 deletions
diff --git a/play.html b/play.html
index bc33c84..5b114c6 100644
--- a/play.html
+++ b/play.html
@@ -12,64 +12,141 @@
<script defer src="play.js"></script>
<style>
-main { background-color: dimgray }
+main {
+ background-color: #666;
+ background-image: url(background.png);
+ box-shadow: inset 0 0 8px #0008;
+}
+
+#log {
+ background-color: oldlace;
+}
-#role_Red .role_name { background-color: salmon; }
-#role_Blue .role_name { background-color: skyblue; }
+#role_Red .role_name { background-color: hsl(359,100%,75%); }
+#role_Blue .role_name { background-color: hsl(220,100%,80%); }
#mapwrap {
- width: 1104px;
- height: 854px;
- box-shadow: 0px 1px 10px #0008;
- margin: 12px auto;
+ width: 827px;
+ margin: 0px auto;
}
#map {
- width: 1100px;
- height: 850px;
- background-size: 1100px 850px;
- background-image: url(map100.jpg);
- border: 2px solid #333;
+ width: 825px;
+ height: calc(12px + 80px + 12px + 638px + 12px + 80px + 12px + 80px + 12px);
+}
+
+#board, #hand, .court {
+ box-sizing: border-box;
+ position: absolute;
+ width: 825px;
+ border: 2px solid #0004;
+ border-color: #fff4 #0004 #0004 #fff4;
+ box-shadow: 0 0 0 1px #222, 1px 1px 8px -2px #000;
+ border-radius: 8px;
+}
+
+#hand, .court {
+ height: 80px;
+}
+
+#board {
+ height: 638px;
+ background-size: 825px 638px;
+ background-position: center;
+ background-image: url(map75.jpg);
+ border-color: #fff8 #2228 #2228 #fff8;
}
+#court1 { top: 12px }
+#board { top: calc(12px + 80px + 12px) }
+#court2 { top: calc(12px + 80px + 12px + 638px + 12px) }
+#hand { top: calc(12px + 80px + 12px + 638px + 12px + 80px + 12px) }
+#mapwrap { height: calc(12px + 80px + 12px + 638px + 12px + 80px + 12px + 80px) }
+
+.red.court { background-color: hsl(359,35%,45%) }
+.blue.court { background-color: hsl(220,35%,45%) }
+
+X#hand { background-color: hsl(46, 63%, 56%) }
+#hand { background-color: hsl(46, 35%, 45%) }
+X#hand { background-image: url(bg_gold3.png); background-size: 256px 256px; }
+X#hand { background-color: #666 }
+
@media (min-resolution: 97dpi) {
- #map { background-image: url(map200.jpg) }
+ #board { background-image: url(map150.jpg) }
}
#map div {
position: absolute;
transition-property: top, left;
- transition-duration: 1s;
+ transition-duration: 500ms;
transition-timing-function: ease;
}
+.action {
+ cursor: pointer;
+}
+
.token {
- width: 58px;
- height: 64px;
- background-size: 58px 64px;
+ width: 41px;
+ height: 44px;
+ background-size: 41px 44px;
filter: drop-shadow(0px 2px 4px #0008);
}
+.token.action {
+ filter:
+ drop-shadow(0px 2px 0px white)
+ drop-shadow(2px 0px 0px white)
+ drop-shadow(-2px 0px 0px white)
+ drop-shadow(0px -2px 0px white)
+}
+
+.token.selected {
+ filter:
+ drop-shadow(0px 2px 0px yellow)
+ drop-shadow(2px 0px 0px yellow)
+ drop-shadow(-2px 0px 0px yellow)
+ drop-shadow(0px -2px 0px yellow)
+}
+
.tile {
- width: 75px;
- height: 75px;
+ width: 52px;
+ height: 52px;
background-repeat: no-repeat;
- background-size: 50px 50px;
+ background-size: 38px 38px;
background-position: center;
border-width: 2px;
border-style: solid;
box-shadow: 0 0 0 1px #222, 1px 2px 4px #0008;
}
+.tile.action {
+ box-shadow: 0 0 0 1px #222, 0 0 0 4px white;
+}
+
+.tile.selected {
+ box-shadow: 0 0 0 1px #222, 0 0 0 4px yellow;
+}
+
+.space {
+ box-sizing: border-box;
+ border-radius: 50%;
+ border: 3px solid transparent;
+}
+
+.space.action {
+ border: 3px solid white;
+}
+
.token.white { background-image: url(images/token_white.svg) }
.token.red { background-image: url(images/token_red.svg) }
.token.blue { background-image: url(images/token_blue.svg) }
-.tile.white { background-image: url(images/tile_white.png) }
-.tile.red { background-image: url(images/tile_red.png) }
-.tile.blue { background-image: url(images/tile_blue.png) }
-.tile.gold { background-image: url(images/tile_gold.png) }
-.tile.green { background-image: url(images/tile_green.png) }
+.tile.white { background-image: url(images/white.png) }
+.tile.red { background-image: url(images/red.png) }
+.tile.blue { background-image: url(images/blue.png) }
+.tile.gold { background-image: url(images/gold.png) }
+.tile.green { background-image: url(images/green.png) }
.tile.red { background-color: hsl(0,90%,49%); border-color: hsl(0,90%,59%) hsl(0,90%,39%) hsl(0,90%,39%) hsl(0,90%,59%); }
.tile.white { background-color: hsl(0,0%,94%); border-color: hsl(0,0%,100%) hsl(0,0%,84%) hsl(0,0%,84%) hsl(0,0%,100%); }
@@ -77,38 +154,6 @@ main { background-color: dimgray }
.tile.gold { background-color: hsl(50,81%,59%); border-color: hsl(50,81%,69%) hsl(50,81%,49%) hsl(50,81%,49%) hsl(50,81%,69%); }
.tile.green { background-color: hsl(125,21%,33%); border-color: hsl(125,21%,43%) hsl(125,21%,23%) hsl(125,21%,23%) hsl(125,21%,43%); }
-.panel {
- max-width: 1100px;
- margin: 24px auto;
- background-color: #555;
- border: 2px solid #333;
- box-shadow: 0 1px 10px #0008;
-}
-
-#red_court_header { background-color: #644 }
-#red_court { background-color: #655 }
-
-#blue_court_header { background-color: #446 }
-#blue_court { background-color: #556 }
-
-.panel_header {
- background-color: #444;
- color: white;
- user-select: none;
- font-weight: bold;
- text-align: center;
- padding: 3px 1em;
-}
-
-.panel_body {
- display: flex;
- justify-content: start;
- flex-wrap: wrap;
- padding: 16px;
- gap: 16px;
- min-height: 80px;
-}
-
</style>
</head>
<body>
@@ -149,36 +194,17 @@ main { background-color: dimgray }
<main>
-<div id="mapwrap" class="">
+<div id="mapwrap">
<div id="map">
-<div id="token_white" class="token white" style="left:90px;top:135px;"></div>
-<div id="token_red1" class="token red" style="left:200px"></div>
-<div id="token_red2" class="token red" style="left:300px"></div>
-<div id="token_red3" class="token red" style="left:400px"></div>
-<div id="token_blue1" class="token blue" style="left:500px"></div>
-<div id="token_blue2" class="token blue" style="left:600px"></div>
-<div id="token_blue3" class="token blue" style="left:700px"></div>
+<div id="court1" class="red court"></div>
+<div id="board"></div>
+<div id="court2" class="blue court"></div>
+<div id="hand" class="blue hand"></div>
</div>
</div>
-<div id="hand_panel" class="panel">
-<div id="hand_header" class="panel_header">Hand</div>
-<div id="hand" class="panel_body">
-</div>
-</div>
-
-<div id="red_court_panel" class="panel">
-<div id="red_court_header" class="panel_header">Red Court</div>
-<div id="red_court" class="panel_body">
-</div></div>
-
-<div id="blue_court_panel" class="panel">
-<div id="blue_court_header" class="panel_header">Blue Court</div>
-<div id="blue_court" class="panel_body">
-</div></div>
-
</main>
<footer id="status"></footer>
diff --git a/play.js b/play.js
new file mode 100644
index 0000000..0cced7e
--- /dev/null
+++ b/play.js
@@ -0,0 +1,253 @@
+"use strict"
+
+const FIRST_TILE = 1
+const TILE_BLUE = 1
+const TILE_RED = 13
+const TILE_GOLD = 25
+const TILE_WHITE = 37
+const TILE_GREEN = 49
+const LAST_TILE = 54
+
+const TILE_W = 56
+const TILE_SPACE = TILE_W + 12
+
+function calc_tile_spacing(n) {
+ if (n > 12)
+ return 744 / (n-1)
+ return TILE_SPACE
+}
+
+const BOARD_X = 0
+const BOARD_Y = 0 + (12 + 80 + 12)
+
+const COURT1_X = 12
+const COURT1_Y = 12 + (12)
+const COURT2_X = 12
+const COURT2_Y = 12 + (12 + 80 + 12 + 638 + 12)
+const HAND_X = 12
+const HAND_Y = 12 + (12 + 80 + 12 + 638 + 12 + 80 + 12)
+
+const TOKEN_DX = 3
+const TOKEN_DY = -5
+const TILE_DX = 3
+const TILE_DY = 3
+
+const LAYOUT_OVAL = [
+ [156,165,45,45],
+ [262,165,45,45],
+ [369,165,45,45],
+ [475,165,45,45],
+ [582,165,45,45],
+ [688,165,45,45],
+ [66,164,45,45],
+
+ // dragon at home
+ [66,105,46,45],
+
+ // off-board
+ [75,400,62,62],
+ [175,400,62,62],
+ [50,500,62,62],
+ [150,500,62,62],
+]
+
+const LAYOUT_SQUARE = [
+ [147,224,61,61],
+ [253,224,61,61],
+ [360,225,61,61],
+ [466,225,61,61],
+ [573,225,61,61],
+ [679,225,61,61],
+]
+
+const LAYOUT_SCORE_0 = [358,332,45,45]
+const LAYOUT_SCORE_39 = [748,547,45,45]
+
+const SCORE_X0 = LAYOUT_SCORE_0[0] + TOKEN_DX + BOARD_X
+const SCORE_Y0 = LAYOUT_SCORE_0[1] + TOKEN_DY + BOARD_Y
+const SCORE_DX = (LAYOUT_SCORE_39[0] - LAYOUT_SCORE_0[0]) / 7
+const SCORE_DY = (LAYOUT_SCORE_39[1] - LAYOUT_SCORE_0[1]) / 4
+
+let ui = {
+ board: document.getElementById("map"),
+ court1: document.getElementById("court1"),
+ court2: document.getElementById("court2"),
+ red_score: null,
+ blue_score: null,
+ oval_spaces: [],
+ tokens: [],
+ tiles: [ null ],
+}
+
+let action_register = []
+
+function register_action(e, action, id) {
+ e.my_action = action
+ e.my_id = id
+ e.onmousedown = on_click_action
+ action_register.push(e)
+}
+
+function on_click_action(evt) {
+ if (evt.button === 0)
+ if (send_action(evt.target.my_action, evt.target.my_id))
+ evt.stopPropagation()
+}
+
+function is_action(action, arg) {
+ if (arg === undefined)
+ return !!(view.actions && view.actions[action] === 1)
+ return !!(view.actions && view.actions[action] && view.actions[action].includes(arg))
+}
+
+function create(t, p, ...c) {
+ let e = document.createElement(t)
+ Object.assign(e, p)
+ e.append(c)
+ if (p.my_action)
+ register_action(e, p.my_action, p.my_id)
+ return e
+}
+
+function create_token(p) {
+ let e = create("div", p)
+ ui.board.appendChild(e)
+ return e
+}
+
+function create_tile(p) {
+ let e = create("div", p)
+ return e
+}
+
+let on_init_once = false
+
+function on_init() {
+ if (on_init_once)
+ return
+ on_init_once = true
+
+ for (let i = 0; i < 7; ++i) {
+ ui.oval_spaces[i] = create("div", { className: "oval space", my_action: "space", my_id: i })
+ ui.oval_spaces[i].style.left = (BOARD_X + LAYOUT_OVAL[i][0]) + "px"
+ ui.oval_spaces[i].style.top = (BOARD_Y + LAYOUT_OVAL[i][1]) + "px"
+ ui.oval_spaces[i].style.width = LAYOUT_OVAL[i][2] + "px"
+ ui.oval_spaces[i].style.height = LAYOUT_OVAL[i][3] + "px"
+ ui.board.append(ui.oval_spaces[i])
+ }
+
+ ui.red_score = create_token({ className: "token red" })
+ ui.blue_score = create_token({ className: "token blue" })
+
+ ui.tokens[0] = create_token({ className: "token white", my_action: "token", my_id: 0 })
+ ui.tokens[1] = create_token({ className: "token red", my_action: "token", my_id: 1 })
+ ui.tokens[2] = create_token({ className: "token red", my_action: "token", my_id: 2 })
+ ui.tokens[3] = create_token({ className: "token blue", my_action: "token", my_id: 3 })
+ ui.tokens[4] = create_token({ className: "token blue", my_action: "token", my_id: 4 })
+
+ for (let i = TILE_BLUE; i < TILE_BLUE + 12; ++i)
+ ui.tiles[i] = create_tile({ className: "tile blue", my_action: "tile", my_id: i })
+ for (let i = TILE_RED; i < TILE_RED + 12; ++i)
+ ui.tiles[i] = create_tile({ className: "tile red", my_action: "tile", my_id: i })
+ for (let i = TILE_GOLD; i < TILE_GOLD + 12; ++i)
+ ui.tiles[i] = create_tile({ className: "tile gold", my_action: "tile", my_id: i })
+ for (let i = TILE_WHITE; i < TILE_WHITE + 12; ++i)
+ ui.tiles[i] = create_tile({ className: "tile white", my_action: "tile", my_id: i })
+ for (let i = TILE_GREEN; i < TILE_GREEN + 6; ++i)
+ ui.tiles[i] = create_tile({ className: "tile green", my_action: "tile", my_id: i })
+}
+
+function show(elt) {
+ if (elt.parentElement !== ui.board)
+ ui.board.appendChild(elt)
+}
+
+function hide(elt) {
+ if (elt.parentElement === ui.board)
+ elt.remove()
+}
+
+function on_update() {
+ on_init()
+
+ for (let i = 1; i <= 54; ++i) {
+ if (view.hand.includes(i) || view.red_court.includes(i) || view.blue_court.includes(i) || view.squares.includes(i))
+ show(ui.tiles[i])
+ else
+ hide(ui.tiles[i])
+ }
+
+ for (let i = 0; i < 6; ++i) {
+ let k = view.squares[i]
+ if (k > 0) {
+ let x = LAYOUT_SQUARE[i][0] + TILE_DX + BOARD_X
+ let y = LAYOUT_SQUARE[i][1] + TILE_DY + BOARD_Y
+ ui.tiles[k].style.left = x + "px"
+ ui.tiles[k].style.top = y + "px"
+ }
+ }
+
+ for (let i = 0; i < 5; ++i) {
+ let s = view.tokens[i]
+ let x = LAYOUT_OVAL[s][0] + TOKEN_DX + BOARD_X
+ let y = LAYOUT_OVAL[s][1] + TOKEN_DY + BOARD_Y
+ ui.tokens[i].style.left = x + "px"
+ ui.tokens[i].style.top = y + "px"
+ ui.tokens[i].classList.toggle("selected", i === view.selected_token)
+ }
+
+ let tile_space = calc_tile_spacing(view.red_court.length)
+ for (let i = 0; i < view.red_court.length; ++i) {
+ let k = view.red_court[i]
+ let x = COURT1_X + tile_space * i
+ let y = COURT1_Y
+ ui.tiles[k].style.left = x + "px"
+ ui.tiles[k].style.top = y + "px"
+ ui.tiles[k].style.zIndex = i
+ }
+
+ tile_space = calc_tile_spacing(view.blue_court.length)
+ for (let i = 0; i < view.blue_court.length; ++i) {
+ let k = view.blue_court[i]
+ let x = COURT2_X + tile_space * i
+ let y = COURT2_Y
+ ui.tiles[k].style.left = x + "px"
+ ui.tiles[k].style.top = y + "px"
+ ui.tiles[k].style.zIndex = i
+ }
+
+ tile_space = calc_tile_spacing(view.hand.length)
+ for (let i = 0; i < view.hand.length; ++i) {
+ let k = view.hand[i]
+ let x = HAND_X + tile_space * i
+ let y = HAND_Y
+ ui.tiles[k].style.left = x + "px"
+ ui.tiles[k].style.top = y + "px"
+ ui.tiles[k].style.zIndex = i
+ }
+
+ let rs = view.red_score
+ let bs = view.blue_score
+ let rs_x = rs % 8
+ let bs_x = bs % 8
+ let rs_y = rs >> 3
+ let bs_y = bs >> 3
+
+ ui.red_score.style.left = (SCORE_X0 + SCORE_DX * rs_x) + "px"
+ ui.blue_score.style.left = (SCORE_X0 + SCORE_DX * bs_x) + "px"
+ if (rs === bs) {
+ ui.red_score.style.top = (SCORE_Y0 + SCORE_DY * rs_y - 21) + "px"
+ ui.blue_score.style.top = (SCORE_Y0 + SCORE_DY * bs_y + 8) + "px"
+ } else {
+ ui.red_score.style.top = (SCORE_Y0 + SCORE_DY * rs_y) + "px"
+ ui.blue_score.style.top = (SCORE_Y0 + SCORE_DY * bs_y) + "px"
+ }
+
+ for (let e of action_register)
+ e.classList.toggle("action", is_action(e.my_action, e.my_id))
+
+ action_button("done", "Done")
+ action_button("undo", "Undo")
+}
+
+scroll_with_middle_mouse("main")
diff --git a/rules.js b/rules.js
index e6461e1..c809044 100644
--- a/rules.js
+++ b/rules.js
@@ -3,9 +3,9 @@
const RED = "Red"
const BLUE = "Blue"
-const states = {}
-
-var game, view
+var states = {}
+var game = null
+var view = null
exports.scenarios = [ "Standard" ]
@@ -17,23 +17,31 @@ const TOKEN_RED_2 = 2
const TOKEN_BLUE_1 = 3
const TOKEN_BLUE_2 = 4
+const FIRST_SPACE = 0
const S_DARKNESS = 0
const S_GOLD = 1
const S_BLUE = 2
const S_WHITE = 3
const S_RED = 4
const S_PURPLE = 5
-const S_DRAGON = 6
-const S_OFF_BOARD = 7
+const LAST_TILE_SPACE = 5
+const S_DRAGON_1 = 6
+const LAST_OVAL_SPACE = 6
+
+const S_DRAGON_2 = 7
+const S_OFF_BOARD_1 = 8
+const S_OFF_BOARD_2 = 9
+const S_OFF_BOARD_3 = 10
+const S_OFF_BOARD_4 = 11
-const TILE_NONE = 0
const FIRST_TILE = 1
+const TILE_NONE = 0
const TILE_BLUE = 1
const TILE_RED = 13
const TILE_GOLD = 25
const TILE_WHITE = 37
const TILE_GREEN = 49
-const LAST_TILE = 55
+const LAST_TILE = 54
function gen_action(action, argument) {
if (!(action in view.actions))
@@ -58,22 +66,13 @@ exports.action = function (state, player, action, arg) {
let S = states[game.state]
if (action in S)
S[action](arg, player)
+ else if (action === "undo" && game.undo && game.undo.length > 0)
+ pop_undo()
else
throw new Error("Invalid action: " + action)
return game
}
-exports.resign = function (state, player) {
- game = state
- if (game.state !== 'game_over') {
- if (player === RED)
- goto_game_over(BLUE, "Red resigned.")
- if (player === BLUE)
- goto_game_over(RED, "Blue resigned.")
- }
- return game
-}
-
exports.view = function(state, player) {
game = state
@@ -114,6 +113,37 @@ exports.view = function(state, player) {
return view;
}
+exports.resign = function (state, player) {
+ game = state
+ if (game.state !== 'game_over') {
+ if (player === RED)
+ goto_game_over(BLUE, "Red resigned.")
+ if (player === BLUE)
+ goto_game_over(RED, "Blue resigned.")
+ }
+ return game
+}
+
+function goto_game_over(result, victory) {
+ game.state = "game_over"
+ game.active = "None"
+ game.result = result
+ game.victory = victory
+ log_br()
+ log(game.victory)
+}
+
+states.game_over = {
+ get inactive() {
+ return game.victory
+ },
+ prompt() {
+ view.prompt = game.victory
+ },
+}
+
+// === PREPARATION ===
+
exports.setup = function (seed, scenario, options) {
game = {
seed: seed,
@@ -123,8 +153,8 @@ exports.setup = function (seed, scenario, options) {
red_score: 0,
blue_score: 0,
- tokens: [ S_DRAGON, S_OFF_BOARD, S_OFF_BOARD, S_OFF_BOARD, S_OFF_BOARD ],
- squares: [ 0, 0, 0, 0, 0, 0 ],
+ tokens: [ S_DRAGON_2, S_OFF_BOARD_1, S_OFF_BOARD_2, S_OFF_BOARD_3, S_OFF_BOARD_4 ],
+ squares: [ TILE_NONE, TILE_NONE, TILE_NONE, TILE_NONE, TILE_NONE, TILE_NONE ],
darkness: [],
red_court: [],
blue_court: [],
@@ -148,7 +178,7 @@ exports.setup = function (seed, scenario, options) {
shuffle(game.darkness)
- for (let i = 0; i < 6; ++i)
+ for (let i = FIRST_SPACE; i <= LAST_TILE_SPACE; ++i)
game.squares[i] = game.darkness.pop()
if (random(2) === 0)
@@ -161,24 +191,112 @@ exports.setup = function (seed, scenario, options) {
return game
}
+// === HANDS AND COURTS ===
+
+function rival_court() {
+ if (game.active === RED)
+ return game.blue_court
+ return game.red_court
+}
+
+function own_court() {
+ if (game.active === RED)
+ return game.red_court
+ return game.blue_court
+}
+
+function own_hand() {
+ if (game.active === RED)
+ return game.red_hand
+ return game.blue_hand
+}
+
+function own_score() {
+ if (game.active === RED)
+ return game.red_score
+ return game.blue_score
+}
+
+function gift_tile_in_space(to) {
+ // Gift associated tile to rival.
+ if (to <= LAST_TILE_SPACE) {
+ let tile = game.squares[to]
+ log("Gifted " + tile)
+ game.squares[to] = TILE_NONE
+ rival_court().push(tile)
+ }
+}
+
+function score_own_points(n) {
+ log("Scored " + n)
+ if (game.active === RED)
+ game.red_score += n
+ if (game.active === BLUE)
+ game.blue_score += n
+ return (game.red_score >= 30) || (game.blue_score >= 30)
+}
+
+function count_tiles(list, type) {
+ let n = 0
+ for (let tile of list)
+ if (tile >= type && tile < type + 12)
+ ++n
+ return n
+}
+
+function reveal_tiles_into_court(type) {
+ let hand = own_hand()
+ let court = own_court()
+ for (let i = 0; i < hand.length;) {
+ let tile = hand[i]
+ if (tile >= type && tile < type + 12) {
+ logi("Revealed " + tile)
+ array_remove(hand, i)
+ court.push(tile)
+ } else {
+ ++i
+ }
+ }
+}
+
+function remove_tiles_from_court(type) {
+ let court = own_court()
+ for (let i = 0; i < court.length;) {
+ let tile = court[i]
+ if (tile >= type && tile < type + 12) {
+ logi("Removed " + tile)
+ array_remove(court, i)
+ } else {
+ ++i
+ }
+ }
+}
+
+function is_oval_space_empty(s) {
+ for (let i = FIRST_SPACE; i <= LAST_OVAL_SPACE; ++i)
+ if (game.tokens[i] === s)
+ return false
+ return true
+}
+
// === FLOW OF PLAY ===
states.move_token = {
prompt() {
view.prompt = "Move one of your tokens to an oval space."
if (game.active === RED) {
- if (game.tokens[TOKEN_RED_1] === S_OFF_BOARD) {
+ if (game.tokens[TOKEN_RED_1] >= S_OFF_BOARD_1) {
gen_action_token(TOKEN_RED_1)
- } else if (game.tokens[TOKEN_RED_2] === S_OFF_BOARD) {
+ } else if (game.tokens[TOKEN_RED_2] >= S_OFF_BOARD_1) {
gen_action_token(TOKEN_RED_2)
} else {
gen_action_token(TOKEN_RED_1)
gen_action_token(TOKEN_RED_2)
}
} else {
- if (game.tokens[TOKEN_BLUE_1] === S_OFF_BOARD) {
+ if (game.tokens[TOKEN_BLUE_1] >= S_OFF_BOARD_1) {
gen_action_token(TOKEN_BLUE_1)
- } else if (game.tokens[TOKEN_BLUE_2] === S_OFF_BOARD) {
+ } else if (game.tokens[TOKEN_BLUE_2] >= S_OFF_BOARD_1) {
gen_action_token(TOKEN_BLUE_2)
} else {
gen_action_token(TOKEN_BLUE_1)
@@ -193,65 +311,232 @@ states.move_token = {
},
}
-function is_oval_space_empty(s) {
- for (let i = 0; i < 5; ++i)
- if (game.tokens[i] === s)
- return false
- return true
-}
-
states.move_token_to = {
prompt() {
view.prompt = "Move your token to an oval space."
view.selected_token = game.selected_token
- for (let i = 0; i < 7; ++i)
+ for (let i = FIRST_SPACE; i <= LAST_OVAL_SPACE; ++i)
if (is_oval_space_empty(i))
gen_action_space(i)
+ gen_action_token(game.selected_token)
+ },
+ space(to) {
+ // Remember whence we came.
+ game.from = game.tokens[game.selected_token]
+
+ log_h2(game.active + " MOVED TO " + to)
+
+ // Move the token.
+ game.tokens[game.selected_token] = to
+
+ // Gift associated tile to rival.
+ gift_tile_in_space(to)
+
+ // Take action.
+ switch (to) {
+ case S_DRAGON_1:
+ return goto_dragon()
+ case S_DARKNESS:
+ return goto_secrecy()
+ case S_GOLD:
+ return goto_cloth_of_gold()
+ case S_BLUE:
+ return goto_banquets_and_feasts()
+ case S_WHITE:
+ return goto_godliness_and_piety()
+ case S_RED:
+ return goto_tournaments()
+ case S_PURPLE:
+ return goto_purple()
+ }
+ },
+ token(t) {
+ pop_undo()
},
- space(space) {
- game.tokens[game.selected_token] = space
- // take action!
+}
+
+function end_turn() {
+ clear_undo()
+
+ // Return Dragon if needed.
+ if (is_oval_space_empty(S_DRAGON_1))
+ game.tokens[TOKEN_DRAGON] = S_DRAGON_2
+
+ // Refill Tiles if needed.
+ for (let i = FIRST_SPACE; i <= LAST_TILE_SPACE; ++i) {
+ if (is_oval_space_empty(i) && game.squares[i] === 0) {
+ let tile = game.darkness.pop()
+ game.squares[i] = tile
+ if (game.darkness.length === 0)
+ return goto_end_of_the_contest()
+ }
}
+
+ // Play passes to the rival player.
+ if (game.active === RED)
+ game.active = BLUE
+ else
+ game.active = RED
+ game.state = "move_token"
}
// === THE ACTIONS: DRAGON ===
+
+function goto_dragon() {
+ log("Dragon")
+ game.state = "dragon"
+}
+
+states.dragon = {
+ prompt() {
+ view.selected_token = TOKEN_DRAGON
+ for (let i = FIRST_SPACE; i <= LAST_OVAL_SPACE; ++i)
+ if (i !== game.from && is_oval_space_empty(i))
+ gen_action_space(i)
+ },
+ space(to) {
+ game.tokens[TOKEN_DRAGON] = to
+ gift_tile_in_space(to)
+ end_turn()
+ },
+}
+
// === THE ACTIONS: SECRECY ===
+
+const SECRECY_PER_ROW = [ 1, 2, 2, 3, 0 ]
+
+function gain_tile_from_darkness() {
+ let tile = game.darkness.pop()
+ own_hand().push(tile)
+ return game.darkness.length === 0
+}
+
+function goto_secrecy() {
+ log("Secrecy")
+ let score = own_score()
+ let row = (score >> 8)
+ let n = SECRECY_PER_ROW[row]
+ for (let i = 0; i < n; ++i) {
+ log("Gained Tile from Darkness.")
+ if (gain_tile_from_darkness()) {
+ return goto_end_of_the_contest()
+ }
+ }
+ end_turn()
+}
+
// === THE ACTIONS: CLOTH OF GOLD ===
+
+function goto_cloth_of_gold() {
+ log("Cloth of Gold")
+ reveal_tiles_into_court(TILE_GOLD)
+ let own = count_tiles(own_court(), TILE_GOLD)
+ let rival = count_tiles(rival_court(), TILE_GOLD)
+ if (own > rival) {
+ if (score_own_points(2))
+ return goto_end_of_the_contest()
+ }
+ end_turn()
+}
+
// === THE ACTIONS: BANQUETS AND FEASTS ===
+
+function goto_banquets_and_feasts() {
+ log("Banquets & Feasts")
+ reveal_tiles_into_court(TILE_BLUE)
+ let own = count_tiles(own_court(), TILE_BLUE)
+ let score = 0
+ if (own === 1)
+ score = 1
+ else if (own === 2)
+ score = 3
+ else if (own >= 3)
+ score = 6
+ remove_tiles_from_court(TILE_BLUE)
+ if (score_own_points(score))
+ return goto_end_of_the_contest()
+ end_turn()
+}
+
// === THE ACTIONS: GODLINESS AND PIETY ===
+
+function goto_godliness_and_piety() {
+ end_turn()
+}
+
// === THE ACTIONS: TOURNAMENTS ===
+
+function goto_tournaments() {
+ end_turn()
+}
+
// === THE ACTIONS: COLLECTIONS ===
+
+function goto_purple() {
+ end_turn()
+}
+
// === END OF THE CONTEST ===
// === COMMON LIBRARY ===
+function log(msg) {
+ game.log.push(msg)
+}
+
+function log_br() {
+ if (game.log.length > 0 && game.log[game.log.length - 1] !== "")
+ game.log.push("")
+}
+
+function logi(msg) {
+ game.log.push(">" + msg)
+}
+
+function log_h1(msg) {
+ log_br()
+ log(".h1 " + msg)
+ log_br()
+}
+
+function log_h2(msg) {
+ log_br()
+ log(".h2 " + msg)
+ log_br()
+}
+
function clear_undo() {
- if (game.undo.length > 0)
- game.undo = []
+ if (game.undo) {
+ game.undo.length = 0
+ }
}
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
+ if (game.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)
}
- 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
+ if (game.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 random(range) {