From 5bedb1a547dbd70240d8a59ec4a84e57517d4918 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sat, 15 Jul 2023 15:08:55 +0200 Subject: Lotsa stuff. --- create.html | 0 favicon.svg | 3 + play.html | 95 ++++--- play.js | 140 +++++++--- rules.js | 817 ++++++++++++++++++++++++++++++++++++++++++++++++--------- tools/color.js | 4 +- 6 files changed, 859 insertions(+), 200 deletions(-) create mode 100644 create.html create mode 100644 favicon.svg diff --git a/create.html b/create.html new file mode 100644 index 0000000..e69de29 diff --git a/favicon.svg b/favicon.svg new file mode 100644 index 0000000..f3b2c0a --- /dev/null +++ b/favicon.svg @@ -0,0 +1,3 @@ + + + diff --git a/play.html b/play.html index 5b114c6..2311ab0 100644 --- a/play.html +++ b/play.html @@ -5,28 +5,59 @@ THE FIELD OF THE CLOTH OF GOLD - + @@ -169,6 +199,7 @@ X#hand { background-color: #666 }
+
@@ -194,14 +225,12 @@ X#hand { background-color: #666 }
-
+
- -
+
Rival Court
-
-
- +
Court
+
Hand
diff --git a/play.js b/play.js index 0cced7e..c4eae91 100644 --- a/play.js +++ b/play.js @@ -33,40 +33,40 @@ 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], + [156,165,46,46], + [262,165,46,46], + [369,165,46,46], + [475,165,46,46], + [582,165,46,46], + [688,165,46,46], + [66,164,46,46], // dragon at home - [66,105,46,45], + [67,106,46,46], // off-board - [75,400,62,62], - [175,400,62,62], - [50,500,62,62], - [150,500,62,62], + [10,638-50], + [110,638-50], + [60,638-50], + [160,638-50], ] 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], + [147,224,62,62], + [253,224,62,62], + [360,225,62,62], + [466,225,62,62], + [573,225,62,62], + [679,225,62,62], ] -const LAYOUT_SCORE_0 = [358,332,45,45] -const LAYOUT_SCORE_39 = [748,547,45,45] +const LAYOUT_SCORE_0 = [358,332,46,46] +const LAYOUT_SCORE_39 = [748,547,46,46] 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 +const SCORE_DX = (LAYOUT_SCORE_39[0] - LAYOUT_SCORE_0[0] + 4) / 7 +const SCORE_DY = (LAYOUT_SCORE_39[1] - LAYOUT_SCORE_0[1] + 0) / 4 let ui = { board: document.getElementById("map"), @@ -75,6 +75,7 @@ let ui = { red_score: null, blue_score: null, oval_spaces: [], + square_spaces: [], tokens: [], tiles: [ null ], } @@ -127,7 +128,7 @@ function on_init() { return on_init_once = true - for (let i = 0; i < 7; ++i) { + for (let i = 0; i < 8; ++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" @@ -136,8 +137,17 @@ function on_init() { ui.board.append(ui.oval_spaces[i]) } - ui.red_score = create_token({ className: "token red" }) - ui.blue_score = create_token({ className: "token blue" }) + for (let i = 0; i < 6; ++i) { + ui.square_spaces[i] = create("div", { className: "square space", my_action: "square", my_id: i }) + ui.square_spaces[i].style.left = (BOARD_X + LAYOUT_SQUARE[i][0]) + "px" + ui.square_spaces[i].style.top = (BOARD_Y + LAYOUT_SQUARE[i][1]) + "px" + ui.square_spaces[i].style.width = LAYOUT_SQUARE[i][2] + "px" + ui.square_spaces[i].style.height = LAYOUT_SQUARE[i][3] + "px" + ui.board.append(ui.square_spaces[i]) + } + + ui.red_score = create_token({ className: "token red", my_action: "score", my_id: 1 }) + ui.blue_score = create_token({ className: "token blue", my_action: "score", my_id: 2 }) 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 }) @@ -171,7 +181,7 @@ 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)) + if ((view.hand && 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]) @@ -196,9 +206,22 @@ function on_update() { 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 court1, court2 + if (player !== "Blue") { + ui.court1.className = "blue court" + ui.court2.className = "red court" + court1 = view.blue_court + court2 = view.red_court + } else { + ui.court1.className = "red court" + ui.court2.className = "blue court" + court1 = view.red_court + court2 = view.blue_court + } + + let tile_space = calc_tile_spacing(court1.length) + for (let i = 0; i < court1.length; ++i) { + let k = court1[i] let x = COURT1_X + tile_space * i let y = COURT1_Y ui.tiles[k].style.left = x + "px" @@ -206,9 +229,9 @@ function on_update() { 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] + tile_space = calc_tile_spacing(court2.length) + for (let i = 0; i < court2.length; ++i) { + let k = court2[i] let x = COURT2_X + tile_space * i let y = COURT2_Y ui.tiles[k].style.left = x + "px" @@ -216,14 +239,16 @@ function on_update() { 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 + if (view.hand) { + 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 @@ -246,8 +271,43 @@ function on_update() { for (let e of action_register) e.classList.toggle("action", is_action(e.my_action, e.my_id)) + action_button("darkness", "Darkness") action_button("done", "Done") action_button("undo", "Undo") } +function on_log(text) { + let p = document.createElement("div") + if (text.match(/^\.r /)) { + text = text.substring(3) + p.className = 'h1 r' + } + else if (text.match(/^\.b /)) { + text = text.substring(3) + p.className = 'h1 b' + } + else if (text.match(/^\.x /)) { + text = text.substring(3) + p.className = 'h1 x' + } + + if (text.match(/^Dragon$/)) + p.classList.add("dragon") + if (text.match(/^Secrecy$/)) + p.classList.add("secrecy") + if (text.match(/^Cloth of Gold$/)) + p.classList.add("gold") + if (text.match(/^Banquets/)) + p.classList.add("blue") + if (text.match(/^Godliness/)) + p.classList.add("white") + if (text.match(/^Tournaments$/)) + p.classList.add("red") + if (text.match(/^Collections$/)) + p.classList.add("purple") + + p.textContent = text + return p +} + scroll_with_middle_mouse("main") diff --git a/rules.js b/rules.js index c809044..8659e2d 100644 --- a/rules.js +++ b/rules.js @@ -11,6 +11,9 @@ exports.scenarios = [ "Standard" ] exports.roles = [ RED, BLUE ] +const SECRECY_PER_ROW = [ 1, 2, 2, 3, 0 ] +const GOLD_PER_ROW = [ 3, 3, 2, 1, 1 ] + const TOKEN_DRAGON = 0 const TOKEN_RED_1 = 1 const TOKEN_RED_2 = 2 @@ -43,6 +46,59 @@ const TILE_WHITE = 37 const TILE_GREEN = 49 const LAST_TILE = 54 +const COUNT_NAME = [ + "no", + "one", + "two", + "three", + "four", + "five", + "six", + "seven", + "eight", + "nine", + "ten", + "eleven", + "twelve", +] + +function count_name(n) { + if (n > 12) + return String(n) + return COUNT_NAME[n] +} + +function space_name(s) { + switch (s) { + case S_DRAGON_1: return "Dragon" + case S_DARKNESS: return "Secrecy" + case S_GOLD: return "Cloth of Gold" + case S_BLUE: return "Banquets & Feasts" + case S_WHITE: return "Godliness & Piety" + case S_RED: return "Tournaments" + case S_PURPLE: return "Collections" + default: return "off board" + } +} + +function tile_name(tile) { + if (tile >= TILE_BLUE && tile < TILE_BLUE + 12) return "Blue" + if (tile >= TILE_RED && tile < TILE_RED + 12) return "Red" + if (tile >= TILE_GOLD && tile < TILE_GOLD + 12) return "Gold" + if (tile >= TILE_WHITE && tile < TILE_WHITE + 12) return "White" + if (tile >= TILE_GREEN && tile < TILE_GREEN + 12) return "Green" + return "None" +} + +function tile_type_name(tile) { + if (tile >= TILE_BLUE && tile < TILE_BLUE + 12) return "Beasts" + if (tile >= TILE_RED && tile < TILE_RED + 12) return "Tournaments" + if (tile >= TILE_GOLD && tile < TILE_GOLD + 12) return "Cloth of Gold" + if (tile >= TILE_WHITE && tile < TILE_WHITE + 12) return "Piety" + if (tile >= TILE_GREEN && tile < TILE_GREEN + 12) return "Jewels" + return "None" +} + function gen_action(action, argument) { if (!(action in view.actions)) view.actions[action] = [] @@ -61,6 +117,33 @@ function gen_action_space(space) { gen_action("space", space) } +function gen_action_square(space) { + gen_action("square", space) +} + +function gen_action_score() { + if (game.active === RED) + gen_action("score", 1) + else + gen_action("score", 2) +} + +function gen_action_score_rival() { + if (game.active !== RED) + gen_action("score", 1) + else + gen_action("score", 2) +} + +function prompt_score(space, score, tail = ".") { + if (score === 0) + view.prompt = space + ": Score no points" + tail + else if (score === 1) + view.prompt = space + ": Score one point" + tail + else + view.prompt = space + ": Score " + score + " points" + tail +} + exports.action = function (state, player, action, arg) { game = state let S = states[game.state] @@ -100,7 +183,7 @@ exports.view = function(state, player) { view.prompt = game.victory } else if (player !== game.active) { let inactive = states[game.state].inactive || game.state - view.prompt = `Waiting for ${game.active} \u2014 ${inactive}...` + view.prompt = `Waiting for ${game.active} \u2014 ${inactive}.` } else { view.actions = {} states[game.state].prompt() @@ -129,14 +212,12 @@ function goto_game_over(result, victory) { game.active = "None" game.result = result game.victory = victory - log_br() + log("") log(game.victory) + return false } states.game_over = { - get inactive() { - return game.victory - }, prompt() { view.prompt = game.victory }, @@ -205,35 +286,71 @@ function own_court() { return game.blue_court } +function rival_hand() { + if (game.active === RED) + return game.blue_hand + return game.red_hand +} + function own_hand() { if (game.active === RED) return game.red_hand return game.blue_hand } +function rival_score() { + if (game.active === RED) + return game.blue_score + return game.red_score +} + function own_score() { if (game.active === RED) return game.red_score return game.blue_score } +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 +} + function gift_tile_in_space(to) { // Gift associated tile to rival. if (to <= LAST_TILE_SPACE) { let tile = game.squares[to] - log("Gifted " + tile) + log("Gifted " + tile_name(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) + if (n > 0) { + if (n === 1) + log("Scored " + count_name(n) + " point.") + else + log("Scored " + count_name(n) + " points.") + if (game.active === RED) + game.red_score += n + if (game.active === BLUE) + game.blue_score += n + } +} + +function score_rival_points(n) { + if (n > 0) { + if (n === 1) + log("Rival scored " + count_name(n) + " point.") + else + log("Rival scored " + count_name(n) + " points.") + if (game.active === RED) + game.blue_score += n + if (game.active === BLUE) + game.red_score += n + } } function count_tiles(list, type) { @@ -244,44 +361,78 @@ function count_tiles(list, type) { 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 has_tile_in_list(type, list) { + for (let tile of list) + if (tile >= type && tile < type + 12) + return true + return false } -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 gen_tile_in_list(type, list) { + for (let tile of list) + if (tile >= type && tile < type + 12) + gen_action_tile(tile) } -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 +function log_reveal_tiles_into_court(type) { + let n = count_tiles(own_hand(), type) + if (n > 0) + log("Revealed " + count_name(n) + " " + tile_name(type) + ".") +} + +function log_remove_tiles_from_court(type) { + let n = count_tiles(own_court(), type) + if (n > 0) + log("Removed " + count_name(n) + " " + tile_name(type) + ".") +} + +function log_remove_tiles_from_rival_court(type) { + let n = count_tiles(rival_court(), type) + if (n > 0) + log("Rival removed " + count_name(n) + " " + tile_name(type) + ".") +} + +function can_reveal_tiles_into_court(type) { + return has_tile_in_list(type, own_hand()) +} + +function gen_reveal_tiles_into_court(type) { + gen_tile_in_list(type, own_hand()) +} + +function can_remove_tiles_from_court(type) { + return has_tile_in_list(type, own_court()) +} + +function gen_remove_tiles_from_court(type) { + gen_tile_in_list(type, own_court()) +} + +function can_remove_tiles_from_rival_court(type) { + return has_tile_in_list(type, rival_court()) +} + +function gen_remove_tiles_from_rival_courts(type) { + gen_tile_in_list(type, rival_court()) +} + +function reveal_tile_into_court(tile) { + array_remove_item(own_hand(), tile) + own_court().push(tile) +} + +function remove_tile_from_court(tile) { + array_remove_item(own_court(), tile) +} + +function remove_tile_from_rival_court(tile) { + array_remove_item(rival_court(), tile) } // === FLOW OF PLAY === states.move_token = { + inactive: "to move one of their tokens", prompt() { view.prompt = "Move one of your tokens to an oval space." if (game.active === RED) { @@ -312,8 +463,9 @@ states.move_token = { } states.move_token_to = { + inactive: "to move one of their tokens", prompt() { - view.prompt = "Move your token to an oval space." + view.prompt = "Move one of your tokens to an oval space." view.selected_token = game.selected_token for (let i = FIRST_SPACE; i <= LAST_OVAL_SPACE; ++i) if (is_oval_space_empty(i)) @@ -324,7 +476,12 @@ states.move_token_to = { // Remember whence we came. game.from = game.tokens[game.selected_token] - log_h2(game.active + " MOVED TO " + to) + if (game.active === RED) + log(".r " + space_name(to)) + else + log(".b " + space_name(to)) + + log("From " + space_name(game.from) + ".") // Move the token. game.tokens[game.selected_token] = to @@ -334,20 +491,20 @@ states.move_token_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() + 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_collections() } }, token(t) { @@ -355,23 +512,80 @@ states.move_token_to = { }, } +function is_end_of_the_contest() { + if (game.darkness.length === 0) + return true + if ((game.red_score >= 30) || (game.blue_score >= 30)) + return true + return false +} + +// TODO: manual return dragon +// TODO: manual refill + function end_turn() { clear_undo() + // Game End triggered? + if (is_end_of_the_contest()) + return goto_end_of_the_contest() + // Return Dragon if needed. - if (is_oval_space_empty(S_DRAGON_1)) - game.tokens[TOKEN_DRAGON] = S_DRAGON_2 + if (is_oval_space_empty(S_DRAGON_1) && game.tokens[TOKEN_DRAGON] !== S_DRAGON_2) { + game.state = "return_dragon" + return + } - // 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() - } + goto_refill_tiles() +} + +states.return_dragon = { + inactive: "End Turn", + prompt() { + view.prompt = "End Turn: Move the Dragon back to its space." + view.selected_token = TOKEN_DRAGON + gen_action_space(S_DRAGON_2) + }, + space(_) { + log("Returned Dragon.") + game.tokens[TOKEN_DRAGON] = S_DRAGON_2 + goto_refill_tiles() } +} + +function must_refill_tiles() { + for (let i = FIRST_SPACE; i <= LAST_TILE_SPACE; ++i) + if (is_oval_space_empty(i) && game.squares[i] === 0) + return true + return false +} + +function goto_refill_tiles() { + if (must_refill_tiles()) + game.state = "refill_tiles" + else + pass_play_to_rival() +} +states.refill_tiles = { + inactive: "End Turn", + prompt() { + view.prompt = "End Turn: Place a new tile." + for (let i = FIRST_SPACE; i <= LAST_TILE_SPACE; ++i) + if (is_oval_space_empty(i) && game.squares[i] === 0) + gen_action_square(i) + }, + square(i) { + let tile = game.darkness.pop() + log("Placed " + tile_name(tile) + " at " + space_name(i) + ".") + game.squares[i] = tile + if (game.darkness.length === 0) + return goto_end_of_the_contest() + goto_refill_tiles() + }, +} + +function pass_play_to_rival() { // Play passes to the rival player. if (game.active === RED) game.active = BLUE @@ -383,18 +597,20 @@ function end_turn() { // === THE ACTIONS: DRAGON === function goto_dragon() { - log("Dragon") game.state = "dragon" } states.dragon = { + inactive: "Dragon", prompt() { + view.prompt = "Dragon: Move the Dragon to an empty oval space." 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) { + log("Dragon to " + space_name(to) + ".") game.tokens[TOKEN_DRAGON] = to gift_tile_in_space(to) end_turn() @@ -403,106 +619,457 @@ states.dragon = { // === THE ACTIONS: SECRECY === -const SECRECY_PER_ROW = [ 1, 2, 2, 3, 0 ] +function gain_tiles_from_darkness(n) { + for (let i = 0; i < n; ++i) + own_hand().push(game.darkness.pop()) +} + +function gain_tiles_from_darkness_rival(n) { + for (let i = 0; i < n; ++i) + rival_hand().push(game.darkness.pop()) +} + +function gain_secrecy_tiles() { + let n = Math.min(SECRECY_PER_ROW[own_score() / 8 | 0], game.darkness.length) + log("Gained " + count_name(n) + ".") + gain_tiles_from_darkness(n) +} -function gain_tile_from_darkness() { - let tile = game.darkness.pop() - own_hand().push(tile) - return game.darkness.length === 0 +function gain_secrecy_tiles_rival() { + let n = Math.min(SECRECY_PER_ROW[rival_score() / 8 | 0], game.darkness.length) + log("Rival gained " + count_name(n) + ".") + gain_tiles_from_darkness_rival(n) } 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() + game.state = "secrecy" +} + +states.secrecy = { + inactive: "Secrecy", + prompt() { + view.prompt = "Secrecy: Gain tiles from the Darkness to your Hand." + view.actions.darkness = 1 + }, + darkness() { + gain_secrecy_tiles() + end_turn() + }, } // === THE ACTIONS: CLOTH OF GOLD === -function goto_cloth_of_gold() { - log("Cloth of Gold") - reveal_tiles_into_court(TILE_GOLD) +function calc_cloth_of_gold_score() { 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() + if (own > rival) + return 2 + return 0 +} + +function goto_cloth_of_gold() { + log_reveal_tiles_into_court(TILE_GOLD) + if (can_reveal_tiles_into_court(TILE_GOLD)) + game.state = "cloth_of_gold_reveal" + else + game.state = "cloth_of_gold_score" +} + +states.cloth_of_gold_reveal = { + inactive: "Cloth of Gold", + prompt() { + view.prompt = "Cloth of Gold: Reveal all Gold tiles from your Hand." + gen_reveal_tiles_into_court(TILE_GOLD) + }, + tile(tile) { + reveal_tile_into_court(tile) + if (!can_reveal_tiles_into_court(TILE_GOLD)) + game.state = "cloth_of_gold_score" + }, +} + +states.cloth_of_gold_score = { + inactive: "Cloth of Gold", + prompt() { + prompt_score("Cloth of Gold", calc_cloth_of_gold_score()) + gen_action_score() + }, + score() { + score_own_points(calc_cloth_of_gold_score()) + end_turn() } - end_turn() } // === THE ACTIONS: BANQUETS AND FEASTS === +function calc_banquets_and_feasts_score() { + let n = count_tiles(own_court(), TILE_BLUE) + if (n === 0) + return 0 + if (n === 1) + return 1 + if (n === 2) + return 3 + return 6 +} + 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() + log_reveal_tiles_into_court(TILE_BLUE) + if (can_reveal_tiles_into_court(TILE_BLUE)) + game.state = "banquets_and_feasts_reveal" + else + game.state = "banquets_and_feasts_score" +} + +states.banquets_and_feasts_reveal = { + inactive: "Banquets & Feasts", + prompt() { + view.prompt = "Banquets & Feasts: Reveal all Blue tiles from your Hand." + gen_reveal_tiles_into_court(TILE_BLUE) + }, + tile(tile) { + reveal_tile_into_court(tile) + if (!can_reveal_tiles_into_court(TILE_BLUE)) + game.state = "banquets_and_feasts_score" + }, +} + +states.banquets_and_feasts_score = { + inactive: "Banquets & Feasts", + prompt() { + prompt_score("Banquets & Feasts", calc_banquets_and_feasts_score()) + gen_action_score() + }, + score() { + score_own_points(calc_banquets_and_feasts_score()) + if (is_end_of_the_contest()) + return goto_end_of_the_contest() + log_remove_tiles_from_court(TILE_BLUE) + if (can_remove_tiles_from_court(TILE_BLUE)) + game.state = "banquets_and_feasts_remove" + else + end_turn() + }, +} + +states.banquets_and_feasts_remove = { + inactive: "Banquets & Feasts", + prompt() { + view.prompt = "Banquets & Feasts: Remove all Blue tiles from your Court." + gen_remove_tiles_from_court(TILE_BLUE) + }, + tile(tile) { + remove_tile_from_court(tile) + if (!can_remove_tiles_from_court(TILE_BLUE)) + end_turn() + }, } // === THE ACTIONS: GODLINESS AND PIETY === +function calc_godliness_and_piety_score() { + return count_tiles(own_court(), TILE_WHITE) +} + function goto_godliness_and_piety() { - end_turn() + log_reveal_tiles_into_court(TILE_WHITE) + if (can_reveal_tiles_into_court(TILE_WHITE)) + game.state = "godliness_and_piety_reveal" + else + game.state = "godliness_and_piety_score" +} + +states.godliness_and_piety_reveal = { + inactive: "Godliness & Piety", + prompt() { + view.prompt = "Godliness & Piety: Reveal all White tiles from your Hand." + gen_reveal_tiles_into_court(TILE_WHITE) + }, + tile(tile) { + reveal_tile_into_court(tile) + if (!can_reveal_tiles_into_court(TILE_WHITE)) + game.state = "godliness_and_piety_score" + }, +} + +states.godliness_and_piety_score = { + inactive: "Godliness & Piety", + prompt() { + view.prompt = "Godliness & Piety: Score " + calc_godliness_and_piety_score() + " points." + prompt_score("Godliness & Piety", calc_godliness_and_piety_score()) + gen_action_score() + }, + score() { + score_own_points(calc_godliness_and_piety_score()) + if (is_end_of_the_contest()) + return goto_end_of_the_contest() + log_remove_tiles_from_court(TILE_WHITE) + if (can_remove_tiles_from_court(TILE_WHITE)) + game.state = "godliness_and_piety_remove" + else + end_turn() + }, +} + +states.godliness_and_piety_remove = { + inactive: "Godliness & Piety", + prompt() { + view.prompt = "Godliness & Piety: Remove all White tiles from your Court." + gen_remove_tiles_from_court(TILE_WHITE) + }, + tile(tile) { + remove_tile_from_court(tile) + if (!can_remove_tiles_from_court(TILE_WHITE)) + end_turn() + }, } // === THE ACTIONS: TOURNAMENTS === +function calc_tournaments_score() { + return count_tiles(own_court(), TILE_RED) +} + +function calc_tournaments_rival_score() { + return count_tiles(rival_court(), TILE_RED) +} + function goto_tournaments() { - end_turn() + goto_tournaments_reveal() +} + +function goto_tournaments_reveal() { + log_reveal_tiles_into_court(TILE_RED) + if (can_reveal_tiles_into_court(TILE_RED)) + game.state = "tournaments_reveal" + else + game.state = "tournaments_score_own" +} + +states.tournaments_reveal = { + inactive: "Tournaments", + prompt() { + view.prompt = "Tournaments: Reveal all Red tiles from your Hand." + gen_reveal_tiles_into_court(TILE_RED) + }, + tile(tile) { + reveal_tile_into_court(tile) + goto_tournaments_reveal() + }, +} + +states.tournaments_score_own = { + inactive: "Tournaments", + prompt() { + view.prompt = "Tournaments: Score " + calc_tournaments_score() + " points." + prompt_score("Tournaments", calc_tournaments_score()) + gen_action_score() + }, + score() { + score_own_points(calc_tournaments_score()) + if (is_end_of_the_contest()) + return goto_end_of_the_contest() + game.state = "tournaments_score_rival" + }, +} + +states.tournaments_score_rival = { + inactive: "Tournaments", + prompt() { + prompt_score("Tournaments", calc_tournaments_rival_score(), " for your rival.") + gen_action_score_rival() + }, + score() { + score_rival_points(calc_tournaments_rival_score()) + if (is_end_of_the_contest()) + return goto_end_of_the_contest() + log_remove_tiles_from_court(TILE_RED) + goto_tournaments_remove_own() + }, +} + +function goto_tournaments_remove_own() { + if (can_remove_tiles_from_court(TILE_RED)) + game.state = "tournaments_remove_own" + else { + log_remove_tiles_from_rival_court(TILE_RED) + goto_tournaments_remove_rival() + } +} + +states.tournaments_remove_own = { + inactive: "Tournaments", + prompt() { + view.prompt = "Tournaments: Remove all Red tiles from your Court." + gen_remove_tiles_from_court(TILE_RED) + }, + tile(tile) { + remove_tile_from_court(tile) + goto_tournaments_remove_own() + }, +} + +function goto_tournaments_remove_rival() { + if (can_remove_tiles_from_rival_court(TILE_RED)) + game.state = "tournaments_remove_rival" + else + game.state = "tournaments_secrecy" +} + +states.tournaments_remove_rival = { + inactive: "Tournaments", + prompt() { + view.prompt = "Tournaments: Remove all Red tiles from rival Court." + gen_remove_tiles_from_rival_courts(TILE_RED) + }, + tile(tile) { + remove_tile_from_rival_court(tile) + goto_tournaments_remove_rival() + }, +} + +states.tournaments_secrecy = { + inactive: "Tournaments", + prompt() { + view.prompt = "Tournaments: Gain tiles from the Darkness." + view.actions.darkness = 1 + }, + darkness() { + gain_secrecy_tiles() + gain_secrecy_tiles_rival() + end_turn() + }, } // === THE ACTIONS: COLLECTIONS === -function goto_purple() { - end_turn() +function calc_collections_score() { + let x1 = count_tiles(own_court(), TILE_GOLD) + let x2 = count_tiles(own_court(), TILE_BLUE) + let x3 = count_tiles(own_court(), TILE_WHITE) + let x4 = count_tiles(own_court(), TILE_RED) + return Math.min(x1, x2, x3, x4) * 2 +} + +function goto_collections() { + log_reveal_tiles_into_court(TILE_GOLD) + log_reveal_tiles_into_court(TILE_BLUE) + log_reveal_tiles_into_court(TILE_WHITE) + log_reveal_tiles_into_court(TILE_RED) + log_reveal_tiles_into_court(TILE_GREEN) + if (own_hand().length > 0) + game.state = "collections_reveal" + else + game.state = "collections_score" +} + +states.collections_reveal = { + inactive: "Collections", + prompt() { + view.prompt = "Collections: Reveal all tiles from your Hand." + for (let tile of own_hand()) + gen_action_tile(tile) + }, + tile(tile) { + reveal_tile_into_court(tile) + if (own_hand().length === 0) + game.state = "collections_score" + }, +} + +states.collections_score = { + inactive: "Collections", + prompt() { + view.prompt = "Collections: Score " + calc_collections_score() + " points." + prompt_score("Collections", calc_collections_score()) + gen_action_score() + }, + score() { + score_own_points(calc_collections_score()) + end_turn() + } } // === END OF THE CONTEST === -// === COMMON LIBRARY === +function calc_jewel_score(court) { + let n = count_tiles(court, TILE_GREEN) + return n * n +} -function log(msg) { - game.log.push(msg) +function calc_gold_score(score, court) { + let n = count_tiles(court, TILE_GOLD) + return n * GOLD_PER_ROW[score / 8 | 0] +} + +function goto_end_of_the_contest() { + log(".x End of the Contest") + game.state = "end_of_the_contest_jewels" +} + +states.end_of_the_contest_jewels = { + prompt() { + prompt_score("End of the Contest", calc_jewel_score(own_court()), " for Jewels.") + gen_action_score() + }, + score() { + score_own_points(calc_jewel_score(own_court())) + game.state = "end_of_the_contest_jewels_rival" + }, +} + +states.end_of_the_contest_jewels_rival = { + prompt() { + prompt_score("End of the Contest", calc_jewel_score(rival_court()), " to your rival for Jewels.") + gen_action_score_rival() + }, + score() { + score_rival_points(calc_jewel_score(rival_court())) + game.state = "end_of_the_contest_gold" + }, } -function log_br() { - if (game.log.length > 0 && game.log[game.log.length - 1] !== "") - game.log.push("") +states.end_of_the_contest_gold = { + prompt() { + prompt_score("End of the Contest", calc_gold_score(own_score(), own_court()), " for Gold.") + gen_action_score() + }, + score() { + score_own_points(calc_gold_score(own_score(), own_court())) + game.state = "end_of_the_contest_gold_rival" + }, } -function logi(msg) { - game.log.push(">" + msg) +states.end_of_the_contest_gold_rival = { + prompt() { + prompt_score("End of the Contest", calc_gold_score(rival_score(), rival_court()), " to your rival for Gold.") + gen_action_score_rival() + }, + score() { + score_rival_points(calc_gold_score(rival_score(), rival_court())) + goto_victory() + }, +} + +function victory_check(red, blue) { + if (red > blue) + return goto_game_over(RED, "Red is the winner!") + if (blue > red) + return goto_game_over(BLUE, "Blue is the winner!") + return true } -function log_h1(msg) { - log_br() - log(".h1 " + msg) - log_br() +function goto_victory() { + if (victory_check(game.red_score, game.blue_score)) + if (victory_check(count_tiles(game.red_court, TILE_WHITE), count_tiles(game.blue_court, TILE_WHITE))) + if (victory_check(game.red_court.length), game.blue_court.length) + goto_game_over("Shared", "The two majesties do share victory.") } -function log_h2(msg) { - log_br() - log(".h2 " + msg) - log_br() +// === COMMON LIBRARY === + +function log(msg) { + game.log.push(msg) } function clear_undo() { diff --git a/tools/color.js b/tools/color.js index d645df7..82b9db7 100644 --- a/tools/color.js +++ b/tools/color.js @@ -41,8 +41,8 @@ function rgb_to_hsl(rgb, add=0, scale=1) { function foo(sel, rgb) { let bg = rgb_to_hsl(rgb, 0.00, 1.00) - let hi = rgb_to_hsl(rgb, 0.10, 1.00) - let sh = rgb_to_hsl(rgb, -0.10, 1.00) + let hi = rgb_to_hsl(rgb, 0.15, 1.00) + let sh = rgb_to_hsl(rgb, -0.15, 1.00) let bd = rgb_to_hsl(rgb, 0.00, 0.33) console.log(sel + ` { background-color: ${bg}; border-color: ${hi} ${sh} ${sh} ${hi}; box-shadow: 0 0 0 1px ${bd}, 0px 1px 4px #0008; }`) } -- cgit v1.2.3