Skip to content

Commit

Permalink
Add NumberController for Sic Bo game
Browse files Browse the repository at this point in the history
  • Loading branch information
hanshino committed Jan 19, 2024
1 parent 4ce4947 commit 88b2127
Show file tree
Hide file tree
Showing 4 changed files with 346 additions and 1 deletion.
5 changes: 4 additions & 1 deletion app/locales/zh_tw.json
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,10 @@
"add_start_analyze_success": "成功分析開始時間...",
"add_end_now": "未指定結束時間,設定為現在之後一小時...",
"add_end_analyze_success": "成功分析結束時間...",
"admin_now": "正在舉辦的遊戲:\n{{ id }}\n{{ name }}"
"admin_now": "正在舉辦的遊戲:\n{{ id }}\n{{ name }}",
"sic_bo_rolled": "骰出了 {{ dice1 }}、{{ dice2 }}、{{ dice3 }},總和為 {{ sum }}",
"sic_bo_win": "你選擇的是 {{ option }},獲得 {{ chips }} * {{ payout }} = {{ total }} 顆女神石",
"sic_bo_lose": "你選擇的是 {{ option }},失去 {{ chips }} 顆女神石"
},
"trade": {
"no_mention": "請標記要交易的對象",
Expand Down
3 changes: 3 additions & 0 deletions app/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const LotteryController = require("./controller/application/LotteryController");
const BullshitController = require("./controller/application/BullshitController");
const SubscribeController = require("./controller/application/SubscribeController");
const ScratchCardController = require("./controller/application/ScratchCardController");
const NumberController = require("./controller/application/NumberController");
const { transfer } = require("./middleware/dcWebhook");
const redis = require("./util/redis");
const traffic = require("./util/traffic");
Expand Down Expand Up @@ -97,6 +98,7 @@ async function HandlePostback(context, { next }) {
() => action === "exchangeScratchCard",
withProps(ScratchCardController.exchange, { payload })
),
route(() => action === "sicBoGuess", withProps(NumberController.postbackDecide, { payload })),
route("*", next),
]);
} catch (e) {
Expand Down Expand Up @@ -134,6 +136,7 @@ async function OrderBased(context, { next }) {
...BullshitController.router,
...SubscribeController.router,
...ScratchCardController.router,
...NumberController.router,
text(/^[/#.](使用說明|help)$/, welcome),
text(/^[/#.]抽(\*(?<times>\d+))?(\s*(?<tag>[\s\S]+))?$/, gacha.play),
text(/^[/#.]消耗抽(\*(?<times>\d+))?(\s*(?<tag>[\s\S]+))?$/, (context, props) =>
Expand Down
141 changes: 141 additions & 0 deletions app/src/controller/application/NumberController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
const { text } = require("bottender/router");
const { sample, get } = require("lodash");
const NumberTemplate = require("../../templates/application/Number");
const i18n = require("../../util/i18n");

exports.router = [
text(/^[.#/](猜大小) (?<chips>\d{1,7})$/, privateSicBoHolding),
text(/^[.#/](猜) (?<option>\S+) (?<chips>\d{1,7})$/, userDecide),
];

const optionMapping = {
big: ["大"],
small: ["小"],
double: ["兩顆"],
triple: ["三顆"],
};

exports.postbackDecide = async (context, { payload }) => {
const { option, chips } = payload;
await userDecide(context, { match: { groups: { option: optionMapping[option][0], chips } } });
};

/**
* Searches for an option in the optionMapping object and returns the corresponding key.
* @param {string} option - The option to search for.
* @returns {string} - The corresponding key for the option, or null if not found.
*/
function searchOption(option) {
return Object.keys(optionMapping).find(key => optionMapping[key].includes(option));
}

/**
* Handles the user's decision in a game.
* @param {Object} context - The context object.
* @param {Object} props - The props object containing the user's decision.
* @param {string} props.option - The user's selected option.
* @param {string} props.chips - The amount of chips the user has bet.
* @returns {Promise<void>} - A promise that resolves when the function is done.
*/
async function userDecide(context, props) {
const { option, chips } = props.match.groups;
const quoteToken = get(context, "event.message.quoteToken");

const key = searchOption(option);
if (!key) {
await context.replyText("請選擇正確的選項,例如:大、小、雙、三", { quoteToken });
return;
}

if (!chips) {
await context.replyText("請先下注", { quoteToken });
return;
}

const dice = rollDice(3);
const sum = dice.reduce((acc, cur) => acc + cur, 0);
const isSmall = sum >= 3 && sum <= 10;
const isBig = sum >= 11 && sum <= 18;
const isDouble = dice[0] === dice[1] || dice[0] === dice[2] || dice[1] === dice[2];
const isTriple = dice[0] === dice[1] && dice[0] === dice[2];

let result = false;
switch (key) {
case "big":
result = isBig;
break;
case "small":
result = isSmall;
break;
case "double":
result = isDouble;
break;
case "triple":
result = isTriple;
break;
}

let payout = 1;
if (result) {
switch (key) {
case "big":
case "small":
payout += 1;
break;
case "double":
payout += 8;
break;
case "triple":
payout += 180;
break;
}
}

const messages = [
i18n.__("message.gamble.sic_bo_rolled", {
dice1: dice[0],
dice2: dice[1],
dice3: dice[2],
sum,
}),
];

const total = parseInt(chips) * payout;
if (result) {
messages.push(i18n.__("message.gamble.sic_bo_win", { option, chips, payout, total }));
} else {
messages.push(i18n.__("message.gamble.sic_bo_lose", { option, chips }));
}

const replyOption = {};
if (quoteToken) {
replyOption.quoteToken = quoteToken;
}
await context.replyText(messages.join("\n"), replyOption);
}

/**
* Handles the private Sic Bo holding logic.
*
* @param {import("bottender").LineContext} context - The context object.
* @param {import("bottender").Props} props - The props object.
* @returns {Promise<void>} - A promise that resolves when the logic is handled.
*/
async function privateSicBoHolding(context, props) {
const { chips } = props.match.groups;
await context.replyFlex("猜大小", NumberTemplate.generatePanel({ chips }));
}

/**
* Rolls a dice a specified number of times.
* @param {number} times - The number of times to roll the dice.
* @returns {number[]} - An array containing the results of each dice roll.
*/
function rollDice(times) {
const dice = [1, 2, 3, 4, 5, 6];
const result = [];
for (let i = 0; i < times; i++) {
result.push(sample(dice));
}
return result;
}
198 changes: 198 additions & 0 deletions app/src/templates/application/Number.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
exports.generatePanel = ({ chips }) => ({
type: "bubble",
body: {
type: "box",
layout: "vertical",
contents: [
{
type: "text",
text: "猜大小",
weight: "bold",
align: "center",
size: "lg",
color: "#009688",
},
{
type: "box",
layout: "vertical",
contents: [
{
type: "text",
text: "按下按鈕後,布丁將會骰出三顆最大值為六的骰子,你可以決定要猜哪一個",
wrap: true,
size: "sm",
color: "#666666",
},
{
type: "text",
text: "● 3~10為小,賠率1",
size: "sm",
color: "#666666",
},
{
type: "text",
text: "● 11~18為大,賠率1",
size: "sm",
color: "#666666",
},
{
type: "text",
text: "● 猜其中兩顆骰子同數字,賠率8",
size: "sm",
color: "#666666",
},
{
type: "text",
text: "● 猜其中三顆骰子同數字,賠率180",
size: "sm",
color: "#666666",
},
],
spacing: "sm",
paddingAll: "sm",
},
],
spacing: "md",
paddingAll: "12px",
},
footer: {
type: "box",
layout: "vertical",
spacing: "sm",
contents: [
{
type: "text",
align: "center",
size: "sm",
color: "#FF0000",
contents: [
{
type: "span",
text: "注意:此面板點擊一次扣除",
},
{
type: "span",
text: ` ${chips} `,
weight: "bold",
color: "#FF6513",
},
{
type: "span",
text: "女神石",
},
],
},
{
type: "box",
layout: "horizontal",
contents: [
{
type: "box",
layout: "vertical",
contents: [
{
type: "text",
text: "大",
align: "center",
size: "sm",
weight: "bold",
color: "#009688",
},
],
borderWidth: "2px",
cornerRadius: "md",
borderColor: "#009688",
paddingAll: "md",
action: {
type: "postback",
data: JSON.stringify({
action: "sicBoGuess",
option: "big",
chips,
}),
},
},
{
type: "box",
layout: "vertical",
contents: [
{
type: "text",
text: "小",
align: "center",
size: "sm",
weight: "bold",
color: "#009688",
},
],
borderWidth: "2px",
cornerRadius: "md",
borderColor: "#009688",
paddingAll: "md",
action: {
type: "postback",
data: JSON.stringify({
action: "sicBoGuess",
option: "small",
chips,
}),
},
},
{
type: "box",
layout: "vertical",
contents: [
{
type: "text",
text: "兩顆",
align: "center",
size: "sm",
weight: "bold",
color: "#009688",
},
],
borderWidth: "2px",
cornerRadius: "md",
borderColor: "#009688",
paddingAll: "md",
action: {
type: "postback",
data: JSON.stringify({
action: "sicBoGuess",
option: "double",
chips,
}),
},
},
{
type: "box",
layout: "vertical",
contents: [
{
type: "text",
text: "三顆",
align: "center",
size: "sm",
weight: "bold",
color: "#009688",
},
],
borderWidth: "2px",
cornerRadius: "md",
borderColor: "#009688",
paddingAll: "md",
action: {
type: "postback",
data: JSON.stringify({
action: "sicBoGuess",
option: "triple",
chips,
}),
},
},
],
spacing: "sm",
},
],
},
});

0 comments on commit 88b2127

Please sign in to comment.