diff --git a/.gitignore b/.gitignore index c186538..094b6fa 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,7 @@ nbdist/ ###################################################################### # Docker docker/backup/ + +###################################################################### +# workbuddy +.workbuddy \ No newline at end of file diff --git a/bin/logtool.js b/bin/logtool.js index 7cd667e..b472159 100644 --- a/bin/logtool.js +++ b/bin/logtool.js @@ -1,4 +1,4 @@ -// 只读查询/受控清库小工具(连 docker 里的 mysql_db) +// 只读查询/受控清库小工具(连 docker 里的 henan_mysql_db) // 用法: // node bin/logtool.js q "select count(*) from sys_oper_log" // node bin/logtool.js clear-operlog 清理操作日志(先备份到 bin/backup/) @@ -7,7 +7,7 @@ const fs = require('fs'); const path = require('path'); const { execFileSync } = require('child_process'); -const MYSQL = ['exec', 'mysql_db', 'mysql', '-uchangzhou', '-piTSJSPPZM3LSGAPC', '--default-character-set=utf8mb4', '-D', 'changzhou']; +const MYSQL = ['exec', 'henan_mysql_db', 'mysql', '-uhenan', '-piTSJSPPZM3LSGAPC', '--default-character-set=utf8mb4', '-D', 'henan']; function sql(statement) { return execFileSync('docker', [...MYSQL, '-N', '-B', '-e', statement], diff --git a/docker/WINDOWS_QUICKSTART.md b/docker/WINDOWS_QUICKSTART.md index 1888044..cddb67b 100644 --- a/docker/WINDOWS_QUICKSTART.md +++ b/docker/WINDOWS_QUICKSTART.md @@ -11,13 +11,13 @@ 1. 创建 MySQL 数据卷: ```powershell -docker volume create changzhou_mysql_data +docker volume create henan_mysql_data ``` 2. 启动容器(⚠️ 必须先 cd 到 compose 目录,且必须带两个 -f 参数): ```powershell -cd d:\changzhou_app\changzhou_backend\docker\compose +cd D:\henan_background\henan-backend\docker\compose docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d ``` @@ -30,9 +30,9 @@ docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d ```powershell docker ps -# 三个容器都应为 Up 状态:mysql_db / redis_cache / rabbitmq_server +# 三个容器都应为 Up 状态:henan_mysql_db / henan_redis_cache / henan_rabbitmq_server -docker exec redis_cache redis-cli ping +docker exec henan_redis_cache redis-cli ping # 返回 NOAUTH Authentication required. 是正常的(Redis 已启动且带密码) ``` @@ -40,7 +40,7 @@ docker exec redis_cache redis-cli ping | 服务 | 本机地址 | 容器端口 | 账号信息 | |------|---------|---------|---------| -| MySQL | 127.0.0.1:23306 | 3306 | root/123456,业务库 changzhou | +| MySQL | 127.0.0.1:23306 | 3306 | root/123456,业务库 henan | | Redis | 127.0.0.1:26379 | 6379 | 密码见 redis.dev.conf | | RabbitMQ | 127.0.0.1:25672 | 5672 | skins/mk,vhost /skins | | RabbitMQ 管理台 | http://127.0.0.1:35672 | 15672 | skins/mk | @@ -49,7 +49,7 @@ docker exec redis_cache redis-cli ping ```powershell # 都在 docker\compose 目录下执行 -cd d:\changzhou_app\changzhou_backend\docker\compose +cd D:\henan_background\henan-backend\docker\compose # 启动 docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d @@ -61,7 +61,7 @@ docker-compose -f docker-compose.yml -f docker-compose.dev.yml stop docker ps # 查看某个容器日志 -docker logs redis_cache --tail 50 +docker logs henan_redis_cache --tail 50 # 重建某个容器(比如改了配置) docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d --force-recreate redis @@ -74,20 +74,20 @@ docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d --force-rec 启动命令少了 `-f docker-compose.dev.yml`,重建即可: ```powershell -cd d:\changzhou_app\changzhou_backend\docker\compose +cd D:\henan_background\henan-backend\docker\compose docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d --force-recreate redis ``` -### 2. 提示 `external volume "changzhou_mysql_data" not found` +### 2. 提示 `external volume "henan_mysql_data" not found` ```powershell -docker volume create changzhou_mysql_data +docker volume create henan_mysql_data ``` ### 3. 提示 `cannot find the file specified` 没有 cd 到 `docker\compose` 目录就执行了命令,`-f` 的相对路径按当前目录解析。 -### 4. 和 chengdu 项目切换 +### 4. 和 hunan 项目切换 端口可能冲突,先到另一个项目的 compose 目录执行 `docker-compose stop`,再启动本项目。 diff --git a/docker/compose/deploy.sh b/docker/compose/deploy.sh index dbb0ae6..d8e05f5 100644 --- a/docker/compose/deploy.sh +++ b/docker/compose/deploy.sh @@ -2,7 +2,7 @@ set -e # 自动创建 external 数据卷(首次部署时) -for vol in changzhou_mysql_data; do +for vol in henan_mysql_data; do docker volume inspect "$vol" >/dev/null 2>&1 || docker volume create "$vol" >/dev/null done diff --git a/docker/compose/docker-compose.yml b/docker/compose/docker-compose.yml index 0e23938..780e6e4 100644 --- a/docker/compose/docker-compose.yml +++ b/docker/compose/docker-compose.yml @@ -7,13 +7,13 @@ services: args: MYSQL_BASE_IMAGE: mysql:8.0.24 MYSQL_CONF: my.cnf.16c32g - container_name: mysql_db + container_name: henan_mysql_db restart: unless-stopped environment: TZ: Asia/Shanghai MYSQL_ROOT_PASSWORD: 123456 - MYSQL_DATABASE: changzhou - MYSQL_USER: changzhou + MYSQL_DATABASE: henan + MYSQL_USER: henan MYSQL_PASSWORD: iTSJSPPZM3LSGAPC ports: - "127.0.0.1:23306:3306" @@ -31,12 +31,12 @@ services: retries: 5 start_period: 30s networks: - - changzhou_app_network + - henan_app_network # Redis 服务 redis: image: redis:latest - container_name: redis_cache + container_name: henan_redis_cache restart: unless-stopped command: redis-server /usr/local/etc/redis/redis.conf ports: @@ -47,12 +47,12 @@ services: max-size: "50m" max-file: "3" networks: - - changzhou_app_network + - henan_app_network # RabbitMQ 3.9 服务 rabbitmq: image: rabbitmq:3.9-management - container_name: rabbitmq_server + container_name: henan_rabbitmq_server restart: unless-stopped environment: RABBITMQ_DEFAULT_USER: skins @@ -75,15 +75,15 @@ services: max-size: "50m" max-file: "3" networks: - - changzhou_app_network + - henan_app_network # 定义数据卷(external 固定卷名,不随目录变化,防止换目录丢数据) volumes: mysql_data: external: true - name: changzhou_mysql_data + name: henan_mysql_data # 定义网络 networks: - changzhou_app_network: + henan_app_network: driver: bridge diff --git a/docker/compose/migrate-mysql-volume.sh b/docker/compose/migrate-mysql-volume.sh index 668f8f6..8589cd0 100644 --- a/docker/compose/migrate-mysql-volume.sh +++ b/docker/compose/migrate-mysql-volume.sh @@ -1,19 +1,19 @@ #!/bin/bash # MySQL 数据卷迁移脚本 -# 将旧自动命名卷的数据复制到 external 卷 changzhou_mysql_data +# 将旧自动命名卷的数据复制到 external 卷 henan_mysql_data # 用法:bash migrate-mysql-volume.sh [旧卷名] -# 不传参数时自动检测 docker_changzhou_mysql_data 和 docker-cn_changzhou_mysql_data +# 不传参数时自动检测 docker_henan_mysql_data 和 docker-cn_henan_mysql_data set -e -NEW_VOL="changzhou_mysql_data" +NEW_VOL="henan_mysql_data" IMAGE="mysql:8.0.24" # 自动检测旧卷名 if [ -n "$1" ]; then OLD_VOL="$1" else - for candidate in docker_changzhou_mysql_data docker-cn_changzhou_mysql_data; do + for candidate in docker_henan_mysql_data docker-cn_henan_mysql_data; do if docker volume inspect "$candidate" >/dev/null 2>&1; then OLD_VOL="$candidate" break @@ -39,9 +39,9 @@ OLD_SIZE=$(docker run --rm -v "${OLD_VOL}:/data" "$IMAGE" sh -c "du -sh /data" 2 echo "旧卷 $OLD_VOL 数据量: $OLD_SIZE" # 停掉 MySQL 容器(如果在运行) -if docker inspect mysql_db >/dev/null 2>&1; then +if docker inspect henan_mysql_db >/dev/null 2>&1; then echo "停止 MySQL 容器..." - docker stop mysql_db 2>/dev/null || true + docker stop henan_mysql_db 2>/dev/null || true fi # 清空新卷(避免数据叠加) diff --git a/docs/修改记录.md b/docs/修改记录.md new file mode 100644 index 0000000..9b0370c --- /dev/null +++ b/docs/修改记录.md @@ -0,0 +1,32 @@ +# 修改记录 + +> **只记大改动**:一个需求 / 一个模块算一条;同一件事的多轮微调合并成一条,不逐轮记。 +> **排序:最新在最上面。** +> 展开细节看 `docs/updata_sql.md`(数据库改动,含完整可执行 SQL 与回滚)与 `.workbuddy/memory/YYYY-MM-DD.md`(过程与踩坑)。 +> **维护**:完成一个大改动后,在顶部按日期加一条;同一天追加在该日期块末尾。 + +--- + +## 2026-09-22 + +- **docker 配置命名统一:`changzhou` → `henan`(全套 `henan_` 前缀)** + + **起因**:`docker/` 下的编排文件里到处是 `changzhou`,是**从同源项目拷贝模板时的命名残留**。查实本机 `docker volume ls` / `docker ps -a`:**从未跑过 changzhou 这套**(只存在 hunan 项目的容器与卷 `hunan_mysql_db` / `hunan_redis_cache` / `hunan_rabbitmq_server`,3 个月前创建、均已 Exited)⇒ 改名**无数据迁移包袱**。统一为 `henan` 全套前缀,与实跑的 hunan 命名风格对齐。 + + **改动 7 个文件**: + + 1. `docker/compose/docker-compose.yml` —— `MYSQL_DATABASE` / `MYSQL_USER` → `henan`;三个 `container_name` → `henan_mysql_db` / `henan_redis_cache` / `henan_rabbitmq_server`;网络 → `henan_app_network`;external 卷 → `henan_mysql_data` + 2. `docker/compose/deploy.sh` —— 自动建卷的卷名 + 3. `docker/compose/migrate-mysql-volume.sh` —— `NEW_VOL` 与旧卷候选列表;`docker stop henan_mysql_db` + 4. `docker/WINDOWS_QUICKSTART.md` —— 卷名 / 库名 / 容器名;顺带修正 3 处**本身就是错的**路径 `d:\changzhou_app\changzhou_backend\...` → `D:\henan_background\henan-backend\docker\compose`;第 4 节「和 chengdu 项目切换」→ hunan + 5. `ruoyi-admin/src/main/resources/application-dev.yml` —— jdbc url 库名 + `username` + 6. `ruoyi-admin/src/main/resources/application-pro.yml` —— 同上 + 7. `bin/logtool.js` —— `-uhenan`、`-D henan`、容器名 `henan_mysql_db` + + **验证**:`docker compose -f docker-compose.yml config` 解析通过、零报错,`name` 段全部已生效(库 `henan`、三个 `henan_*` 容器名、网络 `henan_app_network`、external 卷 `henan_mysql_data`)。**零 SQL 变更、零后端代码改动。** + + **刻意没改**:① `chengduhipay` —— 第三方**支付通道名**不是地名,`application-dev/pro.yml` 与 `updata_sql.md` 里几十处,动了支付会挂;② `docs/updata_sql.md` 里历史条目的 `USE changzhou;` —— 台账回改会失真,需要留痕就**新加一条**;③ 各 `target/` `dist*/` `.lst` `stats.html` —— 里面是 `D:/changzhou_app/...` 的**编译期绝对路径**,属产物,重新构建自然消失;④ `docs/DEPLOYMENT.md`、`docs/README.md`、`docs/麻将胡了设计方案.md`(`D:\changzhou-cocos\...` 是客户端仓库路径)—— 旧项目文档。 + + **上线前必做两件事**:① 卷是 `external: true`,**必须先 `docker volume create henan_mysql_data`**,否则报 `external volume not found` 直接起不来(`deploy.sh up -d` 会自动建,手敲 `docker-compose up` 不会);② 新卷首次初始化会建出**空**的 `henan` 库 —— `MYSQL_DATABASE` / `MYSQL_USER` / `MYSQL_PASSWORD` **只在数据卷首次初始化时生效**,原有数据需另行导入,否则应用连上的是空库。 + + **遗留待定**:网络没固定 `name:`,实际名会随执行目录变(在 `docker/compose` 下执行时是 `compose_henan_app_network`)。卷已用 `external` + 固定 `name` 规避「换目录丢数据」,网络是否同样钉死成 `henan_app_network` 待定。 diff --git a/olympus-py/README.md b/olympus-py/README.md deleted file mode 100644 index 79146f3..0000000 --- a/olympus-py/README.md +++ /dev/null @@ -1,516 +0,0 @@ -# Olympus 电子游戏 — Python 端结构文档 - -> match-3 消除类电子游戏(皇冠/宝石主题)的**规则核心 + 离线回合生成器**。 -> Python 端负责:可执行的规则规范(引擎)、回合数据预生成、落库。 -> 在线播放接口、结算发奖由后续的 Java 服务端消费本模块产出的 `round/step` 数据实现。 - ---- - -## 一、目录结构 - -``` -olympus-py/ -├── olympus/ 规则核心包(纯逻辑,无第三方依赖) -│ ├── __init__.py 包导出:引擎/模型/关键配置 -│ ├── config.py 全部规则配置 + 3 个查询函数 -│ ├── engine.py OlympusEngine 结算状态机(规则的可执行规范) -│ └── models.py 不可变数据模型(Bomb/Elimination/Cascade/Spin/Session) -├── tests/ -│ ├── __init__.py -│ └── test_engine.py 25 项单元测试(规则回归) -├── olympus_simulator.py 回合生成器核心(网格/级联/炸弹/免费旋转/落桶/截断) -├── generate_rounds.py 生成入口(--per-box 每箱总回合 / box_id 从1顺序) -├── olympus_db.py 建表 + 批量写库(连接参数走环境变量) -├── schema.sql 全量 DDL(12 张表)+ 饰品桶种子数据 -└── pyproject.toml 包元信息(name=olympus, py>=3.9) -``` - -> 辅助脚本:`_reset_boxes.py`(重置——清空 box/box_ornament/round/step/box_template)、`_create_reward_log.py`(建奖池调整日志表 game_olympus_reward_log)。 - -分两层: -- **规则核心 `olympus/`**:定义"游戏应该怎么算",被引擎和生成器共同依赖,是唯一事实来源。 -- **生成/落库工具(根目录)**:调用规则核心批量产出回合数据并写入 MySQL。 - ---- - -## 二、核心游戏规则总览 - -| 规则 | 值 / 说明 | -|---|---| -| 网格 | 5 行 × 6 列 = 30 格 | -| 中奖门槛 | 屏幕任意位置同符号 **≥8 个**即赔付(pay-anywhere) | -| 普通符号 | 9 种:皇冠/沙漏/环/杯赛/红/紫/黄/绿/蓝宝石,各有 **8-9 / 10-11 / 12-30** 三档区间赔率(12 个及以上至全屏 30 格归最高档) | -| SCATTER | 4/5/6 个赔 3x/5x/100x,且 **≥4 个触发免费旋转** | -| 免费旋转 | 首次赠送 **15 次**;FS 中 **≥3 个 SCATTER 重触发 +5 次**(4/5/6 个同时赔 3x/5x/100x 并入本旋赢额;恰好3个只+5不赔),上限 30,**到上限后不再落 3+ 个 SCATTER**(否则会写出"extraSpins=5 但加不上旋"的 retrigger_info) | -| 炸弹(乘数符号) | 值 2x~1000x,分 5 种品质色;**只在免费旋转内出现**(normal/ante 的基础旋、含进 FS 的触发盘一律不出) | -| 消除→引爆 | 一次旋转内多波消除,**全部消完后末帧统一引爆炸弹** | -| 单局封顶 | 累计赢奖 **≤ 1000 倍**基础下注,超出截断 | - ---- - -## 三、三种游戏模式(互斥) - -| 模式 | code | 扣费倍率 | 实际扣费档位 | 底注效果 | 可购买 FS | 保证进 FS | -|---|---|---|---|---|---|---| -| 普通旋转 | `normal` | 1× | 1,3,5,10,20,30,50,100,200,300 | ✗ | ✓ | ✗ | -| 底注 | `ante` | 2× | 2,6,10,20,40,60,100,200,400,600 | ✓ | ✗ | ✗ | -| 购买免费旋转 | `buy_fs` | 50× | 50,150,250,500,1000,1500,2500,5000,10000,15000 | ✗ | ✗ | ✓ | - -**互斥关系**(双向):底注开启 → 禁止购买免费旋转;购买免费旋转 → 强制关闭底注效果。三种模式基础注档位完全一致(均 10 档)。 - -- `normal` vs `ante`:ante 花双倍钱,换 **FS 触发概率翻倍**(0.5%→1%)+ SCATTER 出现率更高 + 桶顶更高(150x→200x)。 -- `buy_fs`:花 50 倍直接进 15 次免费旋转,是累计器滚雪球的主要场景。**首盘固定 4 个 SCATTER**(直接赔 3x),且该盘不出炸弹(炸弹只在免费旋转内出现,触发盘炸弹本来也不带入累计器)。 -- **normal/ante 也能进 FS**:基础旋摇出 4~6 个 SCATTER 就免费进 15 旋,进 FS 后与 buy_fs **完全同一套规则**(炸弹、累计器、每旋保底消除),见第三点五 / 第四节。 - ---- - -## 三点五、基础旋为什么没有炸弹(normal/ante) - -> 规则:**炸弹只存在于免费旋转内**。`_spin()` 里 `if not is_free_spin: allow_bomb = False` —— 基础旋(含进 FS 的触发盘、含补桶植入的大奖旋)整盘不会有 `M`。 - -随之而来的物理限制:没有炸弹,基础旋最多只能凑到 `A12(50) + B10(10) ≈ 60x`(自然多组 12 连到 75x 已属极罕见),**100x / 150x / 200x 这些桶基础旋根本赔不出来**。所以: - -| 目标倍率 | 谁来装这个桶 | -|---|---| -| < 80x(`FS_ONLY_TARGET_MUL`) | 基础旋(`--alloc config` 补桶时用 `_plan_base_only` 植基础组合) | -| ≥ 80x(100x/150x/200x 桶) | **自然触发 FS 的回合**:`generate_round(box, bias≥80)` 直接生成"4~6 S 触发盘 + 15 旋 FS",FS 预算 `= 桶目标 − 触发盘赢额` | - -这正是这两个模式里"能进免费旋转"的现实来源:玩家在后台配比里抽中 100x/150x(/200x) 桶时,看到的就是一次免费旋转(box1:桶 12/13 的池子 100% 是 FS 回合)。 - -实测(2026-09 用当前生成器重跑全部 30 箱之后;全库 39,431 回合 / 358,234 步): - -| 检查项 | 结果 | -|---|---| -| 盘面 | 5×6、30 格全满、只有 A~I/S/M —— **空白格 0、非法格 0** | -| 掉落复刻 | 每一步都能用「上一波盘面 − 上一波新增连线 → `drop_data` 下落 → `fill_data` 补位」**精确**复刻(不符 0 条) | -| 金额 | 连线 `base_score` = 赔付表、单旋赢额 = 基础分 × max(1, 累计+本旋炸弹) + scatter、累计器、`spin_win` 连续 —— 全对(只有 2 步在 1000x 封顶截断局里 `accumulator_after` 记的是未并入值,该字段已下发为接口 `accumulator`) | -| 免费旋转多样性 | 13,542 个 FS 回合,**整局盘面完全重复 = 0** | -| 基础旋炸弹 | normal/ante 基础旋炸弹格 **0**;触发盘 S 数:normal 519 局 / ante 818 局都是 4~6,买 FS 12,205 局固定 4 | -| 100x+ 桶 | normal/ante 的桶 12/13/14 池子 200/200/200 条**全部**是 FS 回合(每箱 49~55 / 76~88 个 FS 回合) | -| RTP | 30 个箱**后台配比反推 = 池内实际口径 = 79.99%~80.03%**(见第六点五;重算后不再有 ±1.7pt 偏差) | -| 池子覆盖率 | 池内回合数 / 配比 count 最小 **1.36x**(≥1 → 一个 cycle 内不会重复发) | -| 抽回合模拟 | `simulate_cycle.py` 复刻 Java 链路(含 payoutCap/水位/抽干按模板重填)各 3~4 个 cycle 都收敛到 **80.0%** | - ---- - -## 四、炸弹累计器(关键机制) - -> **累计器只存在于免费旋转模式**,基础旋转没有累计器。三种模式只要进入 FS(normal/ante 摇出 4+S 或 buy_fs 购买)均启用累计器,与模式无关。 - -| 场景 | 炸弹行为 | 累计器 | -|---|---|---| -| 基础旋转(normal/ante 未进 FS) | **整盘不出炸弹**(`allow_bomb=False`) | 不存在(`multiplier_after` 不变) | -| 免费旋转(三模式一致:≥4 S 进入 / buy_fs 购买) | 每屏最多 1 颗(`FS_SCREEN_BOMB_PROB=0.08` 每空格);有消除时并入累计器,跨旋传递 | **从 0 起累**,永不归零直到 FS 结束 | - -**计算公式**(`engine.end_spin`): -- 基础旋转:`本旋赢额 = 基础分 × max(1, 本旋炸弹和)` -- 免费旋转:`本旋赢额 = 基础分 × max(1, 累计值 + 本旋新炸弹)`,之后 `累计值 += 本旋炸弹和` - (**累计倍数作用到每一次消除** —— 0.4×2 + 0.5×4 = 2.8 这种滚雪球,这是这个玩法的核心爽点) - -三条性质:① 免费旋转内累计器只涨不跌;② 无消除的旋转炸弹作废、累计值保持;③ 触发盘(进 FS 那一旋)的炸弹**不带入** FS 累计器。 - -> **推论(决定了"每局能演几次消除")**:n 次"消除+炸弹"的累计花费 ≈ `0.125 × 平均炸弹值 × n²` 倍底注, -> 所以**炸弹越小,买得起的消除次数越多**(单个球值 b、n 次连爆 ≈ 0.125·b·n² ≤ 该局 FS 预算)。 -> 实测(买FS 售价 50 倍、RTP 80%、桶 7~17): -> -> | 桶(抽中率) | FS 预算 | 每局消除旋数 | 每局炸弹并入 | 局末累计中位 | -> |---|---|---|---|---| -> | 10x(35.6%) | 7 | 5.3 旋 | 3.92 次 | 15 | -> | 16x(23.4%) | 13 | 5.2 旋 | 3.97 次 | 16 | -> | 25x(16.1%) | 22 | 5.3 旋 | 4.21 次 | 20 | -> | 40x(9.2%) | 37 | 4.9 旋 | 3.62 次 | 16 | -> | 100x(3.8%) | 97 | 4.7 旋 | 3.44 次 | 17 | -> | 150x~1000x(4.5%) | 147+ | 3~6 旋 | 2~5 次 | 26~258 | -> -> **炸弹值按档位配**(`FS_SMALL_BOMB_MAX=8` / `FS_BIG_BOMB_TARGET=100`):低倍档(<100x)只出 2~8 的小球 -> ——大球一合并,后面每次消除都被放大几十倍,低倍档预算立刻见底;高倍档(≥100x)用全量 2~1000, -> 所以"大炸弹"照样存在,只是它出现的那些局自然落在高倍桶。 -> 每屏最多 1 颗(`FS_MAX_BOMBS_PER_SPIN`),玩家才看得出"消一次爆一颗、倍数一颗颗涨"。 -> > 2026-09 试过一版"按桶分档"(低倍局只用 2~4、自然路径放开到 1000):低倍桶消除 3.65→4.29 次/局, -> > 但高倍桶出现大球后**消除次数反而掉**(1000x 桶 13→8.7 次/局)—— 一局赔付 ≈ 系数×k²、桶目标固定, -> > 大球把后面每次消除都放大,同样的钱只够买更少的消除。用户定调"消除多优先",故已回退; -> > 实测数据与折中做法(如"每局最多一颗大球""大球只给 300x+")留在 `docs/玫瑰女郎炸弹倍数分档方案.md`。 - -**推论(改生成器时必须知道)**:累计器只在"本旋有消除"时才吃炸弹,而炸弹均值 ≈25 倍(`w = 1/√v` 权重)→ **一次带炸弹的消除就把累计倍数推到几十上百**,此后任何一次小消除都被放大成 7.5~50。所以"某局 FS 能容纳多少次消除"完全由该局的原始分预算决定: - -| buy_fs 桶 | 触发盘白送 | FS 可用预算 | 保底上限 | -|---|---|---|---| -| 4x(3.00~5.00) | 3.00 | ~1.0 | 约 2~4 次消除 | -| 6.5x(5.00~8.00) | 3.00 | ~3.5 | 约 8~14 次 | -| 10x(8.00~12.00) | 3.00 | ~7.0 | 每旋都能消 + 连消 | - -> **buy_fs 的可用桶已改为 10x~1000x(桶 7~17)**:4x/6.5x 桶整局预算只有 1~3.5 倍底注, -> 演不出"15 旋里十来旋都在消 + 炸弹累计",玩家看到的一直是空转动画。 -> 改动点:Java `OlympusModeType.getOrnamentRange()` 的 `BUY_FS` → `{7,17}`; -> py `MODE_ORNAMENT_IDS['buy_fs']` → `range(7,18)`;后台「一键重算」重配 count -> (py 侧可跑 `python _recalc_boxes.py --mode buy_fs` 复刻同一算法)。 -> 实测观感(box21,玩家加权):每局 **4.5 旋**有消除、**1.16 次**炸弹并入累计器、低倍局累计倍数中位 **9**(改前分别约 3.2 旋 / 0.4 次 / 0)。 -> 注意:新配比下 buy_fs 的实际 RTP 会落在 **~81.5%**(10x 桶占 69%,该桶实际均值 10.97 > 名义 10.0);要压回 80% 就把这 10 个箱的 rtp 设成 0.785 再重算。 -> **(已修正,见第六节"配比口径")** 现在配比不再按名义 expect_mul 算,而是按每个箱子池内 `AVG(actual_mul)` 算, -> 「设 80% → 实际就赔 80%」,不再需要手改 rtp 这种补偿手段。 - ---- - -## 四点五、免费旋转"每旋保底消除"(三模式统一) - -> 动机:旧实现 30 格里自然凑出 8 连的概率只有 **17%**,再叠 45% 强制空转,导致 **86.8% 的 FS 旋完全空转**(一局 15 旋里平均只有 2.4 旋有消除),观感"每轮都不消"。 -> 现在 normal/ante/buy_fs **只要进了 FS 走的就是同一条代码路径**(`_free_spin_session`),区别只在"这一局值多少钱"。 - -| 参数 | 值 | 作用 | -|---|---|---| -| `FS_SPIN_WIN_PROB` | 0.60 | 不限额时每旋"至少消一波"的基础概率 | -| `FS_CASCADE_CONTINUE_PROB` | 0.10 | 消完一波后继续连消的概率(越低,同样预算能铺到越多旋) | -| `FS_CASCADE_MAX` | 5 | 单旋最大消除波数 | -| `FS_GUARANTEE_COMBOS` | 8 连 I/H/G | 保底用最小档消除(0.25/0.4/0.5 倍底注),不覆盖已有 S/M 格 | -| `FS_GUARANTEE_COST` | 0.45 | 每多一次"保底消除+炸弹累计"的边际赔付(≈0.45×k,见下) | -| `FS_DRY_FORCE_PROB` | {5:0.5, 6:0.85} | 连续空转到第 5/6 旋时"按概率补保底"(软兜底,避免长空尾) | -| `FS_DRY_FORCE_HARD` | 8 | 连续空转到这个旋数无条件补 | -| `FS_FLOOR_SPEND` | ≈0.28 | 保底预算的换算口径(0.25 / (1−0.10)) | -| `FS_FLOOR_BUDGET_CAP` | ≈4.17 | 一局的保底预算上限(15 旋 × 0.28) | - -**预算规则**(`generate_round`): - -| 场景 | 预算 | 说明 | -|---|---|---| -| buy_fs / 高倍桶补桶(`bias` = 桶目标) | `保底预算 = min(上限, 目标 − 触发盘赢额)`,差额给"一旋大奖"(`_force_win`) | 保证每旋在消又不打穿低倍桶 | -| buy_fs 自然阶段(bias=None) | 不限额 → 每旋 60% 保底 | 15 旋全在消(累计器会把整局抬到几百倍,所以只出现在高倍桶) | -| **normal/ante 自然触发 FS** | 固定 `FS_FLOOR_BUDGET_CAP`(无大奖旋) | 免费摇出来的没有价格可拆,保底预算 + 炸弹累计自然结算 | - -- **保底消除"配额 + 随机间隔"(2026-09 修正)**:一局能买几次消除由预算反推 - `k = max(1, √(保底预算 / FS_GUARANTEE_COST))`;每次消除(自然或保底)后**重抽下一次的位置** - (`_fs_next_target`:间隔 = `round(剩余旋数/剩余配额 + δ)`,δ∈{-2..2} 权重 {1,2,3,2,1}, - 均值 0 → 整局消除次数不变、位置随机);空转太久再按 `FS_DRY_FORCE_PROB` 软兜底(第 5/6 旋 - 0.5/0.85,第 8 旋必补),防止配额提前用完留下长空尾。 - > **为什么要改成随机**:先是"等距铺"(`due += k/总局数`,到点必消),虽然没有长空尾,但间隔被 - > 压在 1~5 旋、**87% 的局"最长连空正好 4 旋"**,玩家几局就看穿"到第 4 旋肯定消"。随机化后 - > 相邻两次消除的间隔散成 1~7 旋(35/20/14/11/8/8/3),每局最长连空 1~8 旋散开(单值最高 41%); - > 硬闸版本(到第 N 旋必消)会留下新的固定天花板(硬顶 6 时 42% 的局正好卡在 6),所以兜底必须是 - > **概率递增**而不是硬上限。 - > **为什么要改(上一版)**:旧写法 `p = min(1, 剩余预算 / (FS_FLOOR_SPEND × 剩余旋数))` 里扣预算 - > 扣的是**已被累计倍数放大后的赢额**(`win = 基础分 × max(1, 累计+本旋炸弹)`),累计器一涨,几次 - > 保底就把预算烧穿 → `p = 0` → 后半局再也不会消。全库实测(`python _regen.py fsdry`):买 FS - > **87.9%** 的局出现 ≥4 旋连空、**51.7%** 的局一直空转到结束、单局最长 **29 旋**;natural 触发 - > FS 更高(96~97%)。修完同一份预算下:买 FS 有消除 **4.2 → 4.8 旋/局**、最长连空 ≥7 旋的局 - > **61% → 2%**、单局最长 29 → 11 旋、平均最长连空 9.6 → 4.5 旋。 -- **物理上限(调生成器前必须知道)**:k 次消除的整局赔付 ≈ `FS_GUARANTEE_COST × k²`(每次消除都把炸弹并进 - 累计器,倍数作用到之后每一次消除 → 平方增长)。所以 10x 桶(预算≈7)只买得起 **~4 次消除**; - 想再多消就得靠"少出炸弹",但那样赔付地板会抬高、这些回合不再落在低倍桶(实测 10x 桶直接填不满)。 -- **炸弹值按档位配**(`spin_bomb_max`):目标 < `FS_BIG_BOMB_TARGET`(100) 用 2~8 的小球(预算买得起多次"消除+爆"),目标 ≥100 用全量 2~1000。 - > 想给低倍桶更多消除**不能靠"把球做小"**:球小确实省钱,但低倍桶变成"小球多消"后赔付地板抬高、 - > 命中率下降(实测 10x 桶只填到 117/292,且高倍桶一旦出大球消除次数反掉)—— 详见 `docs/玫瑰女郎炸弹倍数分档方案.md`。 -- **保底消除种在"刚落下/刚补位"的格子上**(`_force_small_win(grid, prefer=补位格)`):这些格子本来就有 `drop_data`/`fill_data` 记录,前端按动画数据落地就能与 `grid_state` 完全一致;`drop_data`/`fill_data` 里的 `symbol` 也统一按**最终盘面**回填(`_spin` 里补位、保底之后统一同步一次)。 - > 改前的老数据里,保底消除会随机改写**没移动过**的格子 → 那类格子没有任何动画记录,前端只能靠 `grid_state` 兜底(不会出现空格,但符号要等这一步渲染完才对齐)。全库体检:`grid_state` 空白格 0、`fill_data` 漏格 0、`drop_data` 与重力不符 0、drop/fill 符号与盘面不符 0;老数据剩 10,603 格属于上述"原地改写"。 - -**验收口径**:`python _regen.py fsdry [box_id]` 打印每模式/每桶的"有消除旋数、最长连空分布、≥4 连空占比" -(买 FS 的 ≥4 连空一列按后台配比加权 = 玩家实际遇到的概率),并列出最差局;单测 -`tests/test_simulator.py::TestFreeSpinFloorSpread` 锁住"平均最长连空 < 8、不出现十几旋死尾巴"。 - -实测(box1:natural 触发 FS 池内 49 个回合,`_regen.py fsstats 1`):每局 **5.1~5.5 旋**有消除、**3.9~4.0 次**炸弹并入累计器、局末累计倍数中位 **15~16**,落桶与名义值基本贴合(桶 12 实测 115.3 vs 名义 100、桶 13 实测 147.8 vs 150)。 -买 FS(`_regen.py fsstats 21`,按配比加权):每局 **4.6 旋**有消除、**3.16 次**炸弹并入;连空体检 `_regen.py fsdry` 见四点五。 - ---- - -## 五、爆奖动画(小/中/大 3 档) - -> 判定口径:**只在免费旋转里判档**(normal/ante 靠自然触发 FS、buy_fs 本身即 FS), -> **按单旋赢额独立判档**,达标即在该旋末帧盖章,旋与旋互不影响,**同一档一局可多次触发**; -> 基础旋(`spinNum=0`)永远是 `null`,不播大奖动画。 - -档位编码:`small < middle < big`(小 / 中 / 大)。 - -阈值表(单位:单旋赢额 ÷ 基础下注,配置于 `BIGWIN_TIERS`)——**两种情况两套阈值**: - -| 情况 | 模式 | small | middle | big | -|---|---|---|---|---| -| ① 自然免费旋转 | `normal` / `ante` | ≥5 | ≥25 | ≥100 | -| ② 购买免费旋转 | `buy_fs` | ≥20 | ≥60 | ≥200 | - -为什么两套:normal/ante 的自然 FS 预算与旋数完全一致(整局约 80~130x),所以共用一套; -buy_fs 花 50 倍买来的赢额分散在 15+ 旋里,单旋阈值整体下调(否则连"小"都很难出现)。 - -实测出现频率(2026-09 重跑后的池子,逐局统计): - -| 情况 | small | middle | big | -|---|---|---|---| -| ① 自然 FS(normal 519 局 / ante 818 局) | 91.7% / 93.5% 的局出现(每局 2.3~2.5 次) | 49.3% / 48.8%(每局 0.6 次) | 31.4% / 39.0%(每局 0.3~0.4 次) | -| ② 买 FS(12205 局) | 19.5% 的局出现(每局 0.47 次) | 6.3%(每局 0.13 次) | **1.5%**(每局 0.02 次) | - -> 买 FS 的"小"只覆盖 19.5% 的局:它门槛是"单旋 ≥20 倍"(= 售价的 0.4 倍),而 10x/16x 桶整局才 10~20 倍,所以那些局不会有任何爆奖动画。 -> 想让"小"更常见就把 `BIGWIN_TIERS['buy_fs']` 的第一档调低(比如 10),改完重跑数据即可。 - ---- - -## 六、饰品桶(17 档)与匹配/发放分离 - -`ORNAMENTS` 定义 17 个倍率桶(0x ~ 1000x),`expect_mul` 严格 = `(range_low + range_high) / 2`,区间连续无缝。 - -**匹配 vs 发放分离**(顶桶特例): -- 17 号桶(展示名 `1000x`)匹配区间 **630~1370**,中点/期望 = 1000; -- 但实际发放**封顶 1000 倍**:回合理论总分落在 1000~1370 仍归此桶,发钱时截断到 1000 并标记 `win_capped`。 -- 即:**落桶用理论分(≤1370),发钱用截断值(≤1000)**,两层各司其职。 - -### 六点五、配比口径:按"池内实际会赔多少"算(2026-09 修正) - -> 以前的配比(`count`)是按**名义** `expect_mul`(区间中点)反推的,于是「设 80% → 实际赔 81.8%」这种系统性偏差: -> 桶内实际均值跟区间中点差很多(实测 **-18% ~ +11%**),桶 17 又受 1000x 封顶影响实际只有 **~835**(不是 1000)。 - -现在 Java `OlympusDistributionService` 与 py `_recalc_boxes.py` 都改成: - -| 环节 | 口径 | -|---|---| -| 配比 `calcRounds` | 期望值取 **该箱池内 `AVG(actual_mul)`**(`loadPoolExpect(boxId)`;某桶池子里一条回合都没有时才回退名义值) | -| 后台「目标RTP」列 | 同一个口径反推 = `Σ(count × 池内均值) / Σcount / cm` —— 也就是**这个箱子真实会赔多少** | -| 抽回合的 payoutCap 过滤 | 仍用名义 `expect_mul`(`OlympusRoundPicker` 读 DB,偏保守,保留安全余量) | -| 微调 | 从"只在最大的 3 个桶之间搬 1 个"改成**任意两个桶搬 1 个**(保持单调),否则池内均值口径下根本走不到目标,会差 0.5~1 个百分点 | - -效果(30 个箱全部重算后实测):每个箱的期望赔付 **79.97% ~ 80.02%**(目标 0.8),`simulate_cycle.py` 整轮收敛 80.0%。 -注意:池子重新生成过之后要**再跑一次重算**(配比是跟着池子里的这批回合走的);重算会把 `used` 归零, -Redis 的本轮账本要一起清(后台「一键重算」会清;py 侧 `_recalc_boxes.py` 只动库,跑完手动清, -`ruoyi:olympus:cycle:{boxId}:{audience}`),否则「实时RTP」显示的是旧抽数+新抽数混在一起的值。 - ---- - -## 七、多身份爆率(audience) - -参照现有宝箱的"普通/主播/机器人"爆率,olympus 同样支持多身份差异化 RTP。但实现方式不同: - -| | 现有宝箱 | olympus | -|---|---|---| -| 爆率载体 | 每饰品配多列 odds(real/anchor/robot) | 每饰品桶的 `count`(按 audience 分套) | -| 开奖方式 | 开箱实时按身份 roll 饰品 | 开局按身份桶分布抽一个**预生成回合** | - -**核心:回合库存池(round/step)完全共享,仅“抽哪个桶”的概率按身份区分。** 四档身份(对齐 `tt_user`): - -| audience | 对应身份 | 识别字段 | -|---|---|---| -| `common` | 普通用户(及招商/卡商/超管等) | user_type=02 及其他 | -| `anchor` | 主播 | user_type=01 | -| `robot` | 机器人 | user_type=03 | -| `white` | 白号 | is_blank_account=1(**压过 user_type**) | - -**身份解析优先级(Java 开局)**:白号最高优先——`is_blank_account==1 → white`(不管哪个 user_type);否则 01→anchor / 03→robot / 其余→common。 - -实现: -- `game_olympus_box_ornament` 带 `audience` 列,唯一键 `(box_id, ornament_id, audience)`,每身份一套 `count`/`used` 分布; -- 生成器写库时四档各写一套(首版相同,后台可差异化调各桶 count); -- Java 开局时按优先级选 audience 分布抽回合(回合池共享,无需重生)。 - -**白号池(white)专项规则**(消耗隔离——白号不碰其他人奖池): -- 独立有限库存:白号抽取只递减 `white` 自己的 `used`、抽干独立补桶,**永不消耗 common/anchor/robot 库存**(由 audience 维度天然隔离); -- white 桶范围与 common 一致,默认分布相同,运营可单独调; -- 水位隔离:白号 bet/win **不计入水位**(对齐 bullet); -- 白号赢奖不进真实经济(提货虚假成功、不计充值/主播收益,与现有白号语义一致)。 -- 机器人暂不细分幸运/非幸运,后续视 2v2 需求再扩展。 - ---- - -## 八、模块 API 说明 - -### 8.1 `olympus/config.py` — 规则配置(唯一事实来源) - -| 名称 | 类型 | 说明 | -|---|---|---| -| `SymbolRule` | dataclass | 符号赔付规则,`payout_for(count)` 按 8/10/12 档取倍率 | -| `SYMBOL_RULES` | dict | 9 种普通符号赔付表 | -| `SCATTER_PAYOUTS` | dict | `{6:100, 5:5, 4:3}` | -| `SCATTER_FREE_SPIN_TRIGGER` / `SCATTER_RETRIGGER_TRIGGER` | int | 4 / 3 | -| `FREE_SPINS_AWARDED` / `FREE_SPIN_RETRIGGER_COUNT` / `MAX_FREE_SPINS` | int | 15 / 5 / 60 | -| `MULTIPLIER_VALUES` | frozenset | 16 个合法炸弹倍数 | -| `MAX_WIN_MULTIPLIER` | Decimal | 1000(封顶) | -| `GameMode` / `MODES` / `DEFAULT_MODE` | dataclass/dict | 三模式定义,`price_for(bet)` 算扣费 | -| `ORNAMENTS` | list | 17 档饰品桶 | -| `GRID_ROWS/COLS/SIZE` | int | 5 / 6 / 30 | -| `SYMBOL_SHORT` / `SHORT_TO_SYMBOL` | dict | 符号↔短码(A~I, S, M)互转 | -| `SYMBOL_WEIGHTS` / `BOMB_*` | - | 生成器随机权重、炸弹概率/品质 | -| `BOMB_QUALITIES` / `bomb_quality(v)` | list/func | 炸弹 5 档品质,值→品质编码 | -| `BASE_FS_TRIGGER_PROB` / `*_SCATTER` | dict | FS 触发与 SCATTER 散布分布 | -| `MODE_ORNAMENT_IDS` | dict | 各模式可用桶范围 | -| `BIGWIN_TIERS` / `bigwin_level(mode, mul)` | dict/func | 爆奖小/中/大 3 档阈值与判档(两种情况两套阈值) | - -### 8.2 `olympus/models.py` — 数据模型(全部 frozen dataclass) - -| 模型 | 关键字段 | -|---|---| -| `Bomb` | `bomb_id, value, position` | -| `Elimination` | `symbol, count, positions, payout_multiplier` | -| `CascadeResult` | `cascade_index, eliminations, bombs, base_win, accumulated_symbols` | -| `SpinResult` | `spin_index, cascades, base_win, multiplier_before/after, bombs_added, effective_multiplier, total_win, accumulated_symbols, win_capped` | -| `SessionState` | `next_spin_index, accumulated_multiplier, total_win, spins`;`reset()` | - -### 8.3 `olympus/engine.py` — `OlympusEngine` 结算状态机 - -规则的**可执行规范**(一局 = 一次付费;一 spin 内可多次 cascade): - -```python -engine = OlympusEngine(bet=10, mode="normal") # 校验模式与档位 -engine.begin_spin(is_free_spin=False) # 开旋(FS 需传 True 才启用累计器) -engine.record_cascade(symbols={"CROWN": [...]}, bombs=[Bomb("m1", 5)]) # 逐波消除 -result = engine.end_spin() # 末帧统一引爆 + 封顶 → SpinResult -``` - -关键方法:`buy_free_spins_allowed()` / `ante_boost_active()` / `reset_session()`;非法操作抛 `RuleViolation`。 - ---- - -## 九、数据库表(前缀 `game_olympus_`,共 12 张) - -| 表 | 用途 | -|---|---| -| `box` | 箱子配置(模式×档位,`box_type/bet/price`) | -| `ornament` | 17 档饰品桶定义(种子随 schema 写入) | -| `box_ornament` | 箱子-桶数量分布(`count/used`,后台调此控 RTP) | -| `round` | 预生成回合(`ornament_id/actual_mul/free_spin_count/total_cascades`) | -| `step` | 级联步骤明细(见下方字段) | -| `record` | 玩家游戏记录(自带 `settled/结算/补发`字段) | -| `user` / `win` | 用户统计 / 每日盈亏 | -| `prize_ornament` / `prize_range` | 返奖饰品定义与区间 | -| `box_template` | 箱子分布模板(抽干补桶依据) | -| `reward_log` | 奖池水位调整日志(注水/抽水/重置审计,含操作后总池余额) | - -**`step` 表核心字段**(前端播放 + 审计): - -| 字段 | 含义 | -|---|---| -| `grid_state` | 本步 6×5 网格最终状态 | -| `multiplier_detail` | 炸弹 `[{pos,val,quality}]` | -| `symbol_links` | **本旋累积**连线 `[{symbol,count,loc,base_score,tumble}]`;`tumble` = 旋内第几波,**接口只下发这个字段**(本波新增 = `tumble` 等于本步 `cascade_seq` 的那些;获胜者面板按 symbol 汇总) | -| `drop_data/fill_data` | 下落/填充动画数据 | -| `scatter_trigger/retrigger_info` | 触发/重触发免费旋转信息(仅末帧) | -| `accumulator_before/after` | 炸弹累计器前后值(跨旋传递);`after` **已下发**为接口字段 `accumulator`(未 ×bet,断线重连接回累计用),`before` 仍仅落库留档 | -| `win_amount` | **本旋累计到本步的赢额**(该旋末帧即这一旋的总额;不是本步增量) | -| `spin_win` | **本局累计到本步的赢额**(最后一步 = 整局实付 = 回合 `actual_mul`) | -| `win_capped` | 本步是否被 1000x 截断 | -| `bigwin_level` | 爆奖档位 `small/middle/big`(按单旋赢额,只在免费旋转里判档,盖在该旋末帧;基础旋恒为 null) | -| `is_final` | 本旋末帧标记 | -| `spin_winners` / `accumulated_symbols` / `removed_positions` / `base_score` | **仅落库留档,接口已不下发**(都能由 `symbol_links` 推出:面板=按 symbol 汇总、消除格位=上一步 `loc` 并集、基础分=`base_score` 之和) | - ---- - -## 十、回合生成器(`olympus_simulator.py` + `generate_rounds.py`) - -流程:`generate_round(box, bias)` → 生成 cascade 链 → 自然累计 → 落桶 → 发放截断。 - -- **职责边界**:生成器只负责"库存多样性"(每桶预生成足量各异回合),**不做 RTP 收敛**——RTP 由后台配 `box_ornament.count` 控制。 -- `fill_distribution(box, counts)`:两阶段填桶(自由生成 + 缺额加权 bias 补桶),保证每桶命中目标数量。 -- 关键实现:炸弹位置随重力下落同步重映射;免费旋转会话 15 起、≥3S 重触发 +5;`bias ≥ FS_ONLY_TARGET_MUL(80x)` 时改用"自然触发 FS"的回合来装该桶(见第三点五)。 - ---- - -## 十一、运行方式 - -> 环境:Python ≥ 3.9,依赖 `pymysql`(仅写库时需要)。DB 连接走环境变量 `OLY_DB_*`,默认对齐 `docker-compose.yml`(`127.0.0.1:23306 / changzhou`)。 - -```bash -# 建表 + 写入饰品桶种子 -python olympus_db.py - -# 干跑:三模式各桶抽样,验证多样性/封顶(不写库) -python generate_rounds.py --per-bucket 20 - -# 全部 30 箱写库(推荐:池子按后台配比×1.5 分配,见下节"池子怎么分配") -python generate_rounds.py --all --alloc config --write - -# 备用:按每箱固定总回合跨桶均分(旧行为,正式发布可用 --per-box 2000) -python generate_rounds.py --all --per-box 2000 --write - -# 单箱写库 -python generate_rounds.py --box-type normal --bet 1 --alloc config --write - -# 重跑前的安全流程(快照→清空→生成→校验;--alloc config 会自动保留配比,快照只作兜底) -python _regen.py snapshot && python _regen.py clear -python generate_rounds.py --all --alloc config --write # 保底消除改"随机间隔"后低倍桶命中率约减半, - # fill_distribution 尝试上限已从 ×30 提到 ×60, - # 全库重跑耗时约翻倍(30 箱 ≈45 分钟~1 小时) -python _regen.py verify -python _regen.py fsdry # 免费旋转连空体检(见四点五) -python _recalc_boxes.py --mode normal && python _recalc_boxes.py --mode ante && python _recalc_boxes.py --mode buy_fs -# 重算只动库里的 used,不清 Redis 账本 → 手动清 ruoyi:olympus:cycle:{boxId}:{audience}, -# 或后台对每个箱点一次「一键重算分布」;再跑 python _make_templates.py 刷新模板 - -# 重置(改 box_id 方案前用):清空 box/box_ornament/round/step/box_template -python _reset_boxes.py - -# 建奖池水位调整日志表(对齐 bullet 审计) -python _create_reward_log.py - -# 规则回归测试(25 项) -python tests/test_engine.py -``` - -**box_id 规则**:`--all` 按模式顺序×档位顺序**从 1 顺序编号**(normal 1~10、ante 11~20、buy_fs 21~30,共 30 箱)。 - -**池子怎么分配(重要)**:用 `--alloc config` —— **每桶池子 = 后台配比 × 1.5**(下限 20 条),而不是跨桶均分: - -| 方式 | 效果 | -|---|---| -| `--per-box N`(旧,均分) | 每桶 N/桶数 条;normal 0x 桶只有 77 条,而配比一个 cycle 要发 548 次 → 同一条回合被反复发给玩家(**7.1×**) | -| `--alloc config`(推荐) | 每桶 ≥ 配比;normal 0x 桶 837 条(配比 563)、buy_fs 低倍桶 447/280/174 条(配比 297/186/118)→ 每个回合一个 cycle 内最多发一次 | - -`--alloc config` 还会把**现有配比原样写回**(不会把池子形状覆盖成 count),所以不需要再手工还原配比。 -池子形状与配比的对应关系写死在 `generate_rounds.py` 的 `ALLOC_K=1.5` / `ALLOC_FLOOR=60`,要调就改这两个常量。 - -> **为什么下限是 60(2026-09 调)**:池子 = `max(ALLOC_FLOOR, 配比×1.5)`,下限只作用在**配比 ≤13 的桶(6.5x 以上)**。 -> 这几个桶条数少、盘面又显眼(有赢奖/大奖动画),是玩家真正会"看着眼熟"的;0x~4x 虽然复现间隔最短 -> (约 4,500 抽一次),但空转/小赔的盘面没人记得住,所以不值得为它们整体加大池子(那就得动 `ALLOC_K`, -> 池子 +64%、重跑时间 ×1.7)。下限 20→60 只让池子 +8%,把 6.5x 以上的复现间隔拉长 3 倍: -> 10x 桶 5,455→16,364 抽、100x 桶 6 万→18 万抽(按全服每天 5,000 抽算,10x 从"几乎每天重"变成"三天重一次")。 -> 复现间隔 = `池子条数 ÷ 该桶配比 × 一轮抽数`,调这两个常量前建议照这个式子算一遍。 - -> 端侧配合:Java `OlympusRoundPicker` 已改为**同一玩家最近 30 条回合不再重发**(Redis List `ruoyi:olympus:seen:{userId}:{boxId}`,`LPUSH`+`LTRIM`),抽回合时从 40 条候选里过滤。 -> 两者叠加的效果:玩家在池子耗完前**不会看到重复盘面**;只靠加大池子做不到(要 ~1.4GB 数据且仍有 30% 重复)。 - -### 抽完补池(refill)与"实时 RTP 是否达标"仿真 - -一个 cycle 抽满后(`drawn ≥ N = fixedSum/(1−收敛比例)`),Java `OlympusGameService` 会 `resetCycle` + `refillFromConfig(boxId, audience)`: - -| 情况 | 行为 | -|---|---| -| 该 (box, audience) 有**生效模板**(`is_active=1`,取 id 最大一条) | 按模板 `items_json` 重写 count(used 归 0),并按固定池反推回写 `total_ornaments` | -| 没有模板 | 只把 `used` 归零(配比不变,等价于"填满"但形状不重置) | - -建模板:`python _make_templates.py`(30 箱 × 4 身份 = 120 条生效模板,内容=当前配比;会先把旧的测试模板置为不生效)。 - -仿真(复刻 Java:固定阶段按 remaining 加权 + 收敛阶段 `desiredNextMul` + 水位 `payoutCap` + 抽完按模板重填): - -``` -python simulate_cycle.py # 全部 30 箱 × common,3 个 cycle(水位跨 cycle 持久) -python simulate_cycle.py --audience white # 白号(payoutCap=null,不进水位) -python simulate_cycle.py --box 21 --cycles 5 -python simulate_cycle.py --cr 0.1 # 试不同收敛比例(只在仿真里覆盖,不写库) -``` - -实测(rtp=80%、收敛比例=20%,30 箱 × 3 轮 × 1000 抽): - -| 收敛比例 | 固定池末偏差 max/均值 | 整轮末偏差 max/均值 | 最差单轮 | -|---|---|---|---| -| 5% | 4.93% / 1.53% | 2.98% / 0.24% | 83.0% | -| 10% | 6.55% / 1.65% | 1.78% / 0.08% | 81.8% | -| 15% | 7.20% / 1.79% | 0.50% / 0.04% | 80.5% | -| **20%(当前)** | 6.36% / 1.58% | **0.13% / 0.03%** | 79.9% | -| 30% | 6.01% / 1.65% | 0.09% / 0.02% | 79.9% | - -结论:固定阶段自身 ±6% 波动("配比即爆率"),全靠收敛阶段拉回目标;**收敛比例不要低于 15%**。 -水位只在冷启动时起作用(水位 0 → `payoutCap=(0+成本)×rtp`,首轮大奖被压住,导致第 1 轮固定池末偏低 77~78%,但整轮仍达标)。 - ---- - -## 十二、端到端数据流 - -``` -config.py(规则配置,唯一事实来源) - │ - ├─► engine.py 被 test_engine.py 回归验证(规则正确性) - │ - └─► olympus_simulator.py 生成 round + step - │ - ▼ - generate_rounds.py 按桶批量生成 - │ - ▼ - olympus_db.py → MySQL(game_olympus_round / _step / _box_ornament) - │ - ▼ - (后续)Java 服务端消费步骤数据 → dospin 逐步回放 + 结算发奖 -``` - -两层职责:**Python 预生成回合/步骤数据落库**,**Java 服务端实时回放与结算**(对齐 sweet 体系)。 diff --git a/olympus-py/_backup_box_config.json b/olympus-py/_backup_box_config.json deleted file mode 100644 index d1e4d45..0000000 --- a/olympus-py/_backup_box_config.json +++ /dev/null @@ -1,13889 +0,0 @@ -{ - "ts": "2026-09-18T09:44:47", - "game_olympus_box": [ - { - "box_id": "1", - "game_type": "OLYMPUS", - "box_type": "normal", - "bet": "1.00", - "name": "普通旋转-bet1", - "price": "1.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:15", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "2", - "game_type": "OLYMPUS", - "box_type": "normal", - "bet": "3.00", - "name": "普通旋转-bet3", - "price": "3.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:17", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "3", - "game_type": "OLYMPUS", - "box_type": "normal", - "bet": "5.00", - "name": "普通旋转-bet5", - "price": "5.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:18", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "4", - "game_type": "OLYMPUS", - "box_type": "normal", - "bet": "10.00", - "name": "普通旋转-bet10", - "price": "10.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:20", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "5", - "game_type": "OLYMPUS", - "box_type": "normal", - "bet": "20.00", - "name": "普通旋转-bet20", - "price": "20.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:22", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "6", - "game_type": "OLYMPUS", - "box_type": "normal", - "bet": "30.00", - "name": "普通旋转-bet30", - "price": "30.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:24", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "7", - "game_type": "OLYMPUS", - "box_type": "normal", - "bet": "50.00", - "name": "普通旋转-bet50", - "price": "50.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:26", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "8", - "game_type": "OLYMPUS", - "box_type": "normal", - "bet": "100.00", - "name": "普通旋转-bet100", - "price": "100.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:28", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "9", - "game_type": "OLYMPUS", - "box_type": "normal", - "bet": "200.00", - "name": "普通旋转-bet200", - "price": "200.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:29", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "10", - "game_type": "OLYMPUS", - "box_type": "normal", - "bet": "300.00", - "name": "普通旋转-bet300", - "price": "300.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:31", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "11", - "game_type": "OLYMPUS", - "box_type": "ante", - "bet": "1.00", - "name": "底注-bet1", - "price": "2.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:33", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "12", - "game_type": "OLYMPUS", - "box_type": "ante", - "bet": "3.00", - "name": "底注-bet3", - "price": "6.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:35", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "13", - "game_type": "OLYMPUS", - "box_type": "ante", - "bet": "5.00", - "name": "底注-bet5", - "price": "10.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:37", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "14", - "game_type": "OLYMPUS", - "box_type": "ante", - "bet": "10.00", - "name": "底注-bet10", - "price": "20.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:39", - "update_time": "2026-09-15 14:49:22" - }, - { - "box_id": "15", - "game_type": "OLYMPUS", - "box_type": "ante", - "bet": "20.00", - "name": "底注-bet20", - "price": "40.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:42", - "update_time": "2026-09-15 14:49:23" - }, - { - "box_id": "16", - "game_type": "OLYMPUS", - "box_type": "ante", - "bet": "30.00", - "name": "底注-bet30", - "price": "60.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:44", - "update_time": "2026-09-15 14:49:23" - }, - { - "box_id": "17", - "game_type": "OLYMPUS", - "box_type": "ante", - "bet": "50.00", - "name": "底注-bet50", - "price": "100.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:46", - "update_time": "2026-09-15 14:49:23" - }, - { - "box_id": "18", - "game_type": "OLYMPUS", - "box_type": "ante", - "bet": "100.00", - "name": "底注-bet100", - "price": "200.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:48", - "update_time": "2026-09-15 14:49:23" - }, - { - "box_id": "19", - "game_type": "OLYMPUS", - "box_type": "ante", - "bet": "200.00", - "name": "底注-bet200", - "price": "400.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:50", - "update_time": "2026-09-15 14:49:23" - }, - { - "box_id": "20", - "game_type": "OLYMPUS", - "box_type": "ante", - "bet": "300.00", - "name": "底注-bet300", - "price": "600.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:35:52", - "update_time": "2026-09-15 14:49:23" - }, - { - "box_id": "21", - "game_type": "OLYMPUS", - "box_type": "buy_fs", - "bet": "1.00", - "name": "购买免费旋转-bet1", - "price": "50.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:36:01", - "update_time": "2026-09-17 15:16:51" - }, - { - "box_id": "22", - "game_type": "OLYMPUS", - "box_type": "buy_fs", - "bet": "3.00", - "name": "购买免费旋转-bet3", - "price": "150.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:36:11", - "update_time": "2026-09-15 16:23:00" - }, - { - "box_id": "23", - "game_type": "OLYMPUS", - "box_type": "buy_fs", - "bet": "5.00", - "name": "购买免费旋转-bet5", - "price": "250.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:36:21", - "update_time": "2026-09-15 16:24:00" - }, - { - "box_id": "24", - "game_type": "OLYMPUS", - "box_type": "buy_fs", - "bet": "10.00", - "name": "购买免费旋转-bet10", - "price": "500.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:36:32", - "update_time": "2026-09-15 16:25:02" - }, - { - "box_id": "25", - "game_type": "OLYMPUS", - "box_type": "buy_fs", - "bet": "20.00", - "name": "购买免费旋转-bet20", - "price": "1000.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:36:42", - "update_time": "2026-09-15 16:25:59" - }, - { - "box_id": "26", - "game_type": "OLYMPUS", - "box_type": "buy_fs", - "bet": "30.00", - "name": "购买免费旋转-bet30", - "price": "1500.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:36:52", - "update_time": "2026-09-15 16:27:05" - }, - { - "box_id": "27", - "game_type": "OLYMPUS", - "box_type": "buy_fs", - "bet": "50.00", - "name": "购买免费旋转-bet50", - "price": "2500.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:37:03", - "update_time": "2026-09-15 16:28:12" - }, - { - "box_id": "28", - "game_type": "OLYMPUS", - "box_type": "buy_fs", - "bet": "100.00", - "name": "购买免费旋转-bet100", - "price": "5000.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:37:13", - "update_time": "2026-09-15 16:29:17" - }, - { - "box_id": "29", - "game_type": "OLYMPUS", - "box_type": "buy_fs", - "bet": "200.00", - "name": "购买免费旋转-bet200", - "price": "10000.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:37:23", - "update_time": "2026-09-15 16:30:22" - }, - { - "box_id": "30", - "game_type": "OLYMPUS", - "box_type": "buy_fs", - "bet": "300.00", - "name": "购买免费旋转-bet300", - "price": "15000.00", - "total_ornaments": "3000", - "rtp": "0.8000", - "rtp_converge_ratio": "0.200", - "sort": "0", - "status": "1", - "del_flag": "0", - "remark": null, - "create_time": "2026-08-01 14:37:32", - "update_time": "2026-09-15 16:31:36" - } - ], - "game_olympus_box_ornament": [ - { - "id": "77350", - "box_id": "1", - "ornament_id": "1", - "audience": "common", - "count": "1352", - "used": "0" - }, - { - "id": "77351", - "box_id": "1", - "ornament_id": "2", - "audience": "common", - "count": "586", - "used": "0" - }, - { - "id": "77352", - "box_id": "1", - "ornament_id": "3", - "audience": "common", - "count": "256", - "used": "0" - }, - { - "id": "77353", - "box_id": "1", - "ornament_id": "4", - "audience": "common", - "count": "112", - "used": "0" - }, - { - "id": "77354", - "box_id": "1", - "ornament_id": "5", - "audience": "common", - "count": "48", - "used": "0" - }, - { - "id": "77355", - "box_id": "1", - "ornament_id": "6", - "audience": "common", - "count": "22", - "used": "0" - }, - { - "id": "77356", - "box_id": "1", - "ornament_id": "7", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "77357", - "box_id": "1", - "ornament_id": "8", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "77358", - "box_id": "1", - "ornament_id": "9", - "audience": "common", - "count": "4", - "used": "0" - }, - { - "id": "77359", - "box_id": "1", - "ornament_id": "10", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77360", - "box_id": "1", - "ornament_id": "11", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77361", - "box_id": "1", - "ornament_id": "12", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77362", - "box_id": "1", - "ornament_id": "13", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77363", - "box_id": "1", - "ornament_id": "1", - "audience": "anchor", - "count": "1352", - "used": "0" - }, - { - "id": "77364", - "box_id": "1", - "ornament_id": "2", - "audience": "anchor", - "count": "586", - "used": "0" - }, - { - "id": "77365", - "box_id": "1", - "ornament_id": "3", - "audience": "anchor", - "count": "256", - "used": "0" - }, - { - "id": "77366", - "box_id": "1", - "ornament_id": "4", - "audience": "anchor", - "count": "112", - "used": "0" - }, - { - "id": "77367", - "box_id": "1", - "ornament_id": "5", - "audience": "anchor", - "count": "48", - "used": "0" - }, - { - "id": "77368", - "box_id": "1", - "ornament_id": "6", - "audience": "anchor", - "count": "22", - "used": "0" - }, - { - "id": "77369", - "box_id": "1", - "ornament_id": "7", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "77370", - "box_id": "1", - "ornament_id": "8", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "77371", - "box_id": "1", - "ornament_id": "9", - "audience": "anchor", - "count": "4", - "used": "0" - }, - { - "id": "77372", - "box_id": "1", - "ornament_id": "10", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "77373", - "box_id": "1", - "ornament_id": "11", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77374", - "box_id": "1", - "ornament_id": "12", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77375", - "box_id": "1", - "ornament_id": "13", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77376", - "box_id": "1", - "ornament_id": "1", - "audience": "robot", - "count": "1352", - "used": "0" - }, - { - "id": "77377", - "box_id": "1", - "ornament_id": "2", - "audience": "robot", - "count": "586", - "used": "0" - }, - { - "id": "77378", - "box_id": "1", - "ornament_id": "3", - "audience": "robot", - "count": "256", - "used": "0" - }, - { - "id": "77379", - "box_id": "1", - "ornament_id": "4", - "audience": "robot", - "count": "112", - "used": "0" - }, - { - "id": "77380", - "box_id": "1", - "ornament_id": "5", - "audience": "robot", - "count": "48", - "used": "0" - }, - { - "id": "77381", - "box_id": "1", - "ornament_id": "6", - "audience": "robot", - "count": "22", - "used": "0" - }, - { - "id": "77382", - "box_id": "1", - "ornament_id": "7", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "77383", - "box_id": "1", - "ornament_id": "8", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "77384", - "box_id": "1", - "ornament_id": "9", - "audience": "robot", - "count": "4", - "used": "0" - }, - { - "id": "77385", - "box_id": "1", - "ornament_id": "10", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "77386", - "box_id": "1", - "ornament_id": "11", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77387", - "box_id": "1", - "ornament_id": "12", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77388", - "box_id": "1", - "ornament_id": "13", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77389", - "box_id": "1", - "ornament_id": "1", - "audience": "white", - "count": "1352", - "used": "0" - }, - { - "id": "77390", - "box_id": "1", - "ornament_id": "2", - "audience": "white", - "count": "586", - "used": "0" - }, - { - "id": "77391", - "box_id": "1", - "ornament_id": "3", - "audience": "white", - "count": "256", - "used": "0" - }, - { - "id": "77392", - "box_id": "1", - "ornament_id": "4", - "audience": "white", - "count": "112", - "used": "0" - }, - { - "id": "77393", - "box_id": "1", - "ornament_id": "5", - "audience": "white", - "count": "48", - "used": "0" - }, - { - "id": "77394", - "box_id": "1", - "ornament_id": "6", - "audience": "white", - "count": "22", - "used": "0" - }, - { - "id": "77395", - "box_id": "1", - "ornament_id": "7", - "audience": "white", - "count": "10", - "used": "0" - }, - { - "id": "77396", - "box_id": "1", - "ornament_id": "8", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "77397", - "box_id": "1", - "ornament_id": "9", - "audience": "white", - "count": "4", - "used": "0" - }, - { - "id": "77398", - "box_id": "1", - "ornament_id": "10", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "77399", - "box_id": "1", - "ornament_id": "11", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77400", - "box_id": "1", - "ornament_id": "12", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77401", - "box_id": "1", - "ornament_id": "13", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77402", - "box_id": "2", - "ornament_id": "1", - "audience": "common", - "count": "1351", - "used": "0" - }, - { - "id": "77403", - "box_id": "2", - "ornament_id": "2", - "audience": "common", - "count": "587", - "used": "0" - }, - { - "id": "77404", - "box_id": "2", - "ornament_id": "3", - "audience": "common", - "count": "255", - "used": "0" - }, - { - "id": "77405", - "box_id": "2", - "ornament_id": "4", - "audience": "common", - "count": "112", - "used": "0" - }, - { - "id": "77406", - "box_id": "2", - "ornament_id": "5", - "audience": "common", - "count": "49", - "used": "0" - }, - { - "id": "77407", - "box_id": "2", - "ornament_id": "6", - "audience": "common", - "count": "22", - "used": "0" - }, - { - "id": "77408", - "box_id": "2", - "ornament_id": "7", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "77409", - "box_id": "2", - "ornament_id": "8", - "audience": "common", - "count": "6", - "used": "0" - }, - { - "id": "77410", - "box_id": "2", - "ornament_id": "9", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "77411", - "box_id": "2", - "ornament_id": "10", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77412", - "box_id": "2", - "ornament_id": "11", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77413", - "box_id": "2", - "ornament_id": "12", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77414", - "box_id": "2", - "ornament_id": "13", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77415", - "box_id": "2", - "ornament_id": "1", - "audience": "anchor", - "count": "1351", - "used": "0" - }, - { - "id": "77416", - "box_id": "2", - "ornament_id": "2", - "audience": "anchor", - "count": "587", - "used": "0" - }, - { - "id": "77417", - "box_id": "2", - "ornament_id": "3", - "audience": "anchor", - "count": "255", - "used": "0" - }, - { - "id": "77418", - "box_id": "2", - "ornament_id": "4", - "audience": "anchor", - "count": "112", - "used": "0" - }, - { - "id": "77419", - "box_id": "2", - "ornament_id": "5", - "audience": "anchor", - "count": "49", - "used": "0" - }, - { - "id": "77420", - "box_id": "2", - "ornament_id": "6", - "audience": "anchor", - "count": "22", - "used": "0" - }, - { - "id": "77421", - "box_id": "2", - "ornament_id": "7", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "77422", - "box_id": "2", - "ornament_id": "8", - "audience": "anchor", - "count": "6", - "used": "0" - }, - { - "id": "77423", - "box_id": "2", - "ornament_id": "9", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "77424", - "box_id": "2", - "ornament_id": "10", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "77425", - "box_id": "2", - "ornament_id": "11", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77426", - "box_id": "2", - "ornament_id": "12", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77427", - "box_id": "2", - "ornament_id": "13", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77428", - "box_id": "2", - "ornament_id": "1", - "audience": "robot", - "count": "1351", - "used": "0" - }, - { - "id": "77429", - "box_id": "2", - "ornament_id": "2", - "audience": "robot", - "count": "587", - "used": "0" - }, - { - "id": "77430", - "box_id": "2", - "ornament_id": "3", - "audience": "robot", - "count": "255", - "used": "0" - }, - { - "id": "77431", - "box_id": "2", - "ornament_id": "4", - "audience": "robot", - "count": "112", - "used": "0" - }, - { - "id": "77432", - "box_id": "2", - "ornament_id": "5", - "audience": "robot", - "count": "49", - "used": "0" - }, - { - "id": "77433", - "box_id": "2", - "ornament_id": "6", - "audience": "robot", - "count": "22", - "used": "0" - }, - { - "id": "77434", - "box_id": "2", - "ornament_id": "7", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "77435", - "box_id": "2", - "ornament_id": "8", - "audience": "robot", - "count": "6", - "used": "0" - }, - { - "id": "77436", - "box_id": "2", - "ornament_id": "9", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "77437", - "box_id": "2", - "ornament_id": "10", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "77438", - "box_id": "2", - "ornament_id": "11", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77439", - "box_id": "2", - "ornament_id": "12", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77440", - "box_id": "2", - "ornament_id": "13", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77441", - "box_id": "2", - "ornament_id": "1", - "audience": "white", - "count": "1351", - "used": "0" - }, - { - "id": "77442", - "box_id": "2", - "ornament_id": "2", - "audience": "white", - "count": "587", - "used": "0" - }, - { - "id": "77443", - "box_id": "2", - "ornament_id": "3", - "audience": "white", - "count": "255", - "used": "0" - }, - { - "id": "77444", - "box_id": "2", - "ornament_id": "4", - "audience": "white", - "count": "112", - "used": "0" - }, - { - "id": "77445", - "box_id": "2", - "ornament_id": "5", - "audience": "white", - "count": "49", - "used": "0" - }, - { - "id": "77446", - "box_id": "2", - "ornament_id": "6", - "audience": "white", - "count": "22", - "used": "0" - }, - { - "id": "77447", - "box_id": "2", - "ornament_id": "7", - "audience": "white", - "count": "10", - "used": "0" - }, - { - "id": "77448", - "box_id": "2", - "ornament_id": "8", - "audience": "white", - "count": "6", - "used": "0" - }, - { - "id": "77449", - "box_id": "2", - "ornament_id": "9", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "77450", - "box_id": "2", - "ornament_id": "10", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "77451", - "box_id": "2", - "ornament_id": "11", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77452", - "box_id": "2", - "ornament_id": "12", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77453", - "box_id": "2", - "ornament_id": "13", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77454", - "box_id": "3", - "ornament_id": "1", - "audience": "common", - "count": "1351", - "used": "0" - }, - { - "id": "77455", - "box_id": "3", - "ornament_id": "2", - "audience": "common", - "count": "587", - "used": "0" - }, - { - "id": "77456", - "box_id": "3", - "ornament_id": "3", - "audience": "common", - "count": "256", - "used": "0" - }, - { - "id": "77457", - "box_id": "3", - "ornament_id": "4", - "audience": "common", - "count": "112", - "used": "0" - }, - { - "id": "77458", - "box_id": "3", - "ornament_id": "5", - "audience": "common", - "count": "49", - "used": "0" - }, - { - "id": "77459", - "box_id": "3", - "ornament_id": "6", - "audience": "common", - "count": "21", - "used": "0" - }, - { - "id": "77460", - "box_id": "3", - "ornament_id": "7", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "77461", - "box_id": "3", - "ornament_id": "8", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "77462", - "box_id": "3", - "ornament_id": "9", - "audience": "common", - "count": "4", - "used": "0" - }, - { - "id": "77463", - "box_id": "3", - "ornament_id": "10", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77464", - "box_id": "3", - "ornament_id": "11", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77465", - "box_id": "3", - "ornament_id": "12", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77466", - "box_id": "3", - "ornament_id": "13", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77467", - "box_id": "3", - "ornament_id": "1", - "audience": "anchor", - "count": "1351", - "used": "0" - }, - { - "id": "77468", - "box_id": "3", - "ornament_id": "2", - "audience": "anchor", - "count": "587", - "used": "0" - }, - { - "id": "77469", - "box_id": "3", - "ornament_id": "3", - "audience": "anchor", - "count": "256", - "used": "0" - }, - { - "id": "77470", - "box_id": "3", - "ornament_id": "4", - "audience": "anchor", - "count": "112", - "used": "0" - }, - { - "id": "77471", - "box_id": "3", - "ornament_id": "5", - "audience": "anchor", - "count": "49", - "used": "0" - }, - { - "id": "77472", - "box_id": "3", - "ornament_id": "6", - "audience": "anchor", - "count": "21", - "used": "0" - }, - { - "id": "77473", - "box_id": "3", - "ornament_id": "7", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "77474", - "box_id": "3", - "ornament_id": "8", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "77475", - "box_id": "3", - "ornament_id": "9", - "audience": "anchor", - "count": "4", - "used": "0" - }, - { - "id": "77476", - "box_id": "3", - "ornament_id": "10", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "77477", - "box_id": "3", - "ornament_id": "11", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77478", - "box_id": "3", - "ornament_id": "12", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77479", - "box_id": "3", - "ornament_id": "13", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77480", - "box_id": "3", - "ornament_id": "1", - "audience": "robot", - "count": "1351", - "used": "0" - }, - { - "id": "77481", - "box_id": "3", - "ornament_id": "2", - "audience": "robot", - "count": "587", - "used": "0" - }, - { - "id": "77482", - "box_id": "3", - "ornament_id": "3", - "audience": "robot", - "count": "256", - "used": "0" - }, - { - "id": "77483", - "box_id": "3", - "ornament_id": "4", - "audience": "robot", - "count": "112", - "used": "0" - }, - { - "id": "77484", - "box_id": "3", - "ornament_id": "5", - "audience": "robot", - "count": "49", - "used": "0" - }, - { - "id": "77485", - "box_id": "3", - "ornament_id": "6", - "audience": "robot", - "count": "21", - "used": "0" - }, - { - "id": "77486", - "box_id": "3", - "ornament_id": "7", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "77487", - "box_id": "3", - "ornament_id": "8", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "77488", - "box_id": "3", - "ornament_id": "9", - "audience": "robot", - "count": "4", - "used": "0" - }, - { - "id": "77489", - "box_id": "3", - "ornament_id": "10", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "77490", - "box_id": "3", - "ornament_id": "11", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77491", - "box_id": "3", - "ornament_id": "12", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77492", - "box_id": "3", - "ornament_id": "13", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77493", - "box_id": "3", - "ornament_id": "1", - "audience": "white", - "count": "1351", - "used": "0" - }, - { - "id": "77494", - "box_id": "3", - "ornament_id": "2", - "audience": "white", - "count": "587", - "used": "0" - }, - { - "id": "77495", - "box_id": "3", - "ornament_id": "3", - "audience": "white", - "count": "256", - "used": "0" - }, - { - "id": "77496", - "box_id": "3", - "ornament_id": "4", - "audience": "white", - "count": "112", - "used": "0" - }, - { - "id": "77497", - "box_id": "3", - "ornament_id": "5", - "audience": "white", - "count": "49", - "used": "0" - }, - { - "id": "77498", - "box_id": "3", - "ornament_id": "6", - "audience": "white", - "count": "21", - "used": "0" - }, - { - "id": "77499", - "box_id": "3", - "ornament_id": "7", - "audience": "white", - "count": "10", - "used": "0" - }, - { - "id": "77500", - "box_id": "3", - "ornament_id": "8", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "77501", - "box_id": "3", - "ornament_id": "9", - "audience": "white", - "count": "4", - "used": "0" - }, - { - "id": "77502", - "box_id": "3", - "ornament_id": "10", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "77503", - "box_id": "3", - "ornament_id": "11", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77504", - "box_id": "3", - "ornament_id": "12", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77505", - "box_id": "3", - "ornament_id": "13", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77506", - "box_id": "4", - "ornament_id": "1", - "audience": "common", - "count": "1351", - "used": "0" - }, - { - "id": "77507", - "box_id": "4", - "ornament_id": "2", - "audience": "common", - "count": "587", - "used": "0" - }, - { - "id": "77508", - "box_id": "4", - "ornament_id": "3", - "audience": "common", - "count": "256", - "used": "0" - }, - { - "id": "77509", - "box_id": "4", - "ornament_id": "4", - "audience": "common", - "count": "111", - "used": "0" - }, - { - "id": "77510", - "box_id": "4", - "ornament_id": "5", - "audience": "common", - "count": "49", - "used": "0" - }, - { - "id": "77511", - "box_id": "4", - "ornament_id": "6", - "audience": "common", - "count": "22", - "used": "0" - }, - { - "id": "77512", - "box_id": "4", - "ornament_id": "7", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "77513", - "box_id": "4", - "ornament_id": "8", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "77514", - "box_id": "4", - "ornament_id": "9", - "audience": "common", - "count": "4", - "used": "0" - }, - { - "id": "77515", - "box_id": "4", - "ornament_id": "10", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77516", - "box_id": "4", - "ornament_id": "11", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77517", - "box_id": "4", - "ornament_id": "12", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77518", - "box_id": "4", - "ornament_id": "13", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77519", - "box_id": "4", - "ornament_id": "1", - "audience": "anchor", - "count": "1351", - "used": "0" - }, - { - "id": "77520", - "box_id": "4", - "ornament_id": "2", - "audience": "anchor", - "count": "587", - "used": "0" - }, - { - "id": "77521", - "box_id": "4", - "ornament_id": "3", - "audience": "anchor", - "count": "256", - "used": "0" - }, - { - "id": "77522", - "box_id": "4", - "ornament_id": "4", - "audience": "anchor", - "count": "111", - "used": "0" - }, - { - "id": "77523", - "box_id": "4", - "ornament_id": "5", - "audience": "anchor", - "count": "49", - "used": "0" - }, - { - "id": "77524", - "box_id": "4", - "ornament_id": "6", - "audience": "anchor", - "count": "22", - "used": "0" - }, - { - "id": "77525", - "box_id": "4", - "ornament_id": "7", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "77526", - "box_id": "4", - "ornament_id": "8", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "77527", - "box_id": "4", - "ornament_id": "9", - "audience": "anchor", - "count": "4", - "used": "0" - }, - { - "id": "77528", - "box_id": "4", - "ornament_id": "10", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "77529", - "box_id": "4", - "ornament_id": "11", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77530", - "box_id": "4", - "ornament_id": "12", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77531", - "box_id": "4", - "ornament_id": "13", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77532", - "box_id": "4", - "ornament_id": "1", - "audience": "robot", - "count": "1351", - "used": "0" - }, - { - "id": "77533", - "box_id": "4", - "ornament_id": "2", - "audience": "robot", - "count": "587", - "used": "0" - }, - { - "id": "77534", - "box_id": "4", - "ornament_id": "3", - "audience": "robot", - "count": "256", - "used": "0" - }, - { - "id": "77535", - "box_id": "4", - "ornament_id": "4", - "audience": "robot", - "count": "111", - "used": "0" - }, - { - "id": "77536", - "box_id": "4", - "ornament_id": "5", - "audience": "robot", - "count": "49", - "used": "0" - }, - { - "id": "77537", - "box_id": "4", - "ornament_id": "6", - "audience": "robot", - "count": "22", - "used": "0" - }, - { - "id": "77538", - "box_id": "4", - "ornament_id": "7", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "77539", - "box_id": "4", - "ornament_id": "8", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "77540", - "box_id": "4", - "ornament_id": "9", - "audience": "robot", - "count": "4", - "used": "0" - }, - { - "id": "77541", - "box_id": "4", - "ornament_id": "10", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "77542", - "box_id": "4", - "ornament_id": "11", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77543", - "box_id": "4", - "ornament_id": "12", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77544", - "box_id": "4", - "ornament_id": "13", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77545", - "box_id": "4", - "ornament_id": "1", - "audience": "white", - "count": "1351", - "used": "0" - }, - { - "id": "77546", - "box_id": "4", - "ornament_id": "2", - "audience": "white", - "count": "587", - "used": "0" - }, - { - "id": "77547", - "box_id": "4", - "ornament_id": "3", - "audience": "white", - "count": "256", - "used": "0" - }, - { - "id": "77548", - "box_id": "4", - "ornament_id": "4", - "audience": "white", - "count": "111", - "used": "0" - }, - { - "id": "77549", - "box_id": "4", - "ornament_id": "5", - "audience": "white", - "count": "49", - "used": "0" - }, - { - "id": "77550", - "box_id": "4", - "ornament_id": "6", - "audience": "white", - "count": "22", - "used": "0" - }, - { - "id": "77551", - "box_id": "4", - "ornament_id": "7", - "audience": "white", - "count": "10", - "used": "0" - }, - { - "id": "77552", - "box_id": "4", - "ornament_id": "8", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "77553", - "box_id": "4", - "ornament_id": "9", - "audience": "white", - "count": "4", - "used": "0" - }, - { - "id": "77554", - "box_id": "4", - "ornament_id": "10", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "77555", - "box_id": "4", - "ornament_id": "11", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77556", - "box_id": "4", - "ornament_id": "12", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77557", - "box_id": "4", - "ornament_id": "13", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77558", - "box_id": "5", - "ornament_id": "1", - "audience": "common", - "count": "1359", - "used": "0" - }, - { - "id": "77559", - "box_id": "5", - "ornament_id": "2", - "audience": "common", - "count": "587", - "used": "0" - }, - { - "id": "77560", - "box_id": "5", - "ornament_id": "3", - "audience": "common", - "count": "253", - "used": "0" - }, - { - "id": "77561", - "box_id": "5", - "ornament_id": "4", - "audience": "common", - "count": "109", - "used": "0" - }, - { - "id": "77562", - "box_id": "5", - "ornament_id": "5", - "audience": "common", - "count": "48", - "used": "0" - }, - { - "id": "77563", - "box_id": "5", - "ornament_id": "6", - "audience": "common", - "count": "21", - "used": "0" - }, - { - "id": "77564", - "box_id": "5", - "ornament_id": "7", - "audience": "common", - "count": "9", - "used": "0" - }, - { - "id": "77565", - "box_id": "5", - "ornament_id": "8", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "77566", - "box_id": "5", - "ornament_id": "9", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "77567", - "box_id": "5", - "ornament_id": "10", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "77568", - "box_id": "5", - "ornament_id": "11", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77569", - "box_id": "5", - "ornament_id": "12", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77570", - "box_id": "5", - "ornament_id": "13", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77571", - "box_id": "5", - "ornament_id": "1", - "audience": "anchor", - "count": "1359", - "used": "0" - }, - { - "id": "77572", - "box_id": "5", - "ornament_id": "2", - "audience": "anchor", - "count": "587", - "used": "0" - }, - { - "id": "77573", - "box_id": "5", - "ornament_id": "3", - "audience": "anchor", - "count": "253", - "used": "0" - }, - { - "id": "77574", - "box_id": "5", - "ornament_id": "4", - "audience": "anchor", - "count": "109", - "used": "0" - }, - { - "id": "77575", - "box_id": "5", - "ornament_id": "5", - "audience": "anchor", - "count": "48", - "used": "0" - }, - { - "id": "77576", - "box_id": "5", - "ornament_id": "6", - "audience": "anchor", - "count": "21", - "used": "0" - }, - { - "id": "77577", - "box_id": "5", - "ornament_id": "7", - "audience": "anchor", - "count": "9", - "used": "0" - }, - { - "id": "77578", - "box_id": "5", - "ornament_id": "8", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "77579", - "box_id": "5", - "ornament_id": "9", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "77580", - "box_id": "5", - "ornament_id": "10", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "77581", - "box_id": "5", - "ornament_id": "11", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77582", - "box_id": "5", - "ornament_id": "12", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77583", - "box_id": "5", - "ornament_id": "13", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77584", - "box_id": "5", - "ornament_id": "1", - "audience": "robot", - "count": "1359", - "used": "0" - }, - { - "id": "77585", - "box_id": "5", - "ornament_id": "2", - "audience": "robot", - "count": "587", - "used": "0" - }, - { - "id": "77586", - "box_id": "5", - "ornament_id": "3", - "audience": "robot", - "count": "253", - "used": "0" - }, - { - "id": "77587", - "box_id": "5", - "ornament_id": "4", - "audience": "robot", - "count": "109", - "used": "0" - }, - { - "id": "77588", - "box_id": "5", - "ornament_id": "5", - "audience": "robot", - "count": "48", - "used": "0" - }, - { - "id": "77589", - "box_id": "5", - "ornament_id": "6", - "audience": "robot", - "count": "21", - "used": "0" - }, - { - "id": "77590", - "box_id": "5", - "ornament_id": "7", - "audience": "robot", - "count": "9", - "used": "0" - }, - { - "id": "77591", - "box_id": "5", - "ornament_id": "8", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "77592", - "box_id": "5", - "ornament_id": "9", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "77593", - "box_id": "5", - "ornament_id": "10", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "77594", - "box_id": "5", - "ornament_id": "11", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77595", - "box_id": "5", - "ornament_id": "12", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77596", - "box_id": "5", - "ornament_id": "13", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77597", - "box_id": "5", - "ornament_id": "1", - "audience": "white", - "count": "1359", - "used": "0" - }, - { - "id": "77598", - "box_id": "5", - "ornament_id": "2", - "audience": "white", - "count": "587", - "used": "0" - }, - { - "id": "77599", - "box_id": "5", - "ornament_id": "3", - "audience": "white", - "count": "253", - "used": "0" - }, - { - "id": "77600", - "box_id": "5", - "ornament_id": "4", - "audience": "white", - "count": "109", - "used": "0" - }, - { - "id": "77601", - "box_id": "5", - "ornament_id": "5", - "audience": "white", - "count": "48", - "used": "0" - }, - { - "id": "77602", - "box_id": "5", - "ornament_id": "6", - "audience": "white", - "count": "21", - "used": "0" - }, - { - "id": "77603", - "box_id": "5", - "ornament_id": "7", - "audience": "white", - "count": "9", - "used": "0" - }, - { - "id": "77604", - "box_id": "5", - "ornament_id": "8", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "77605", - "box_id": "5", - "ornament_id": "9", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "77606", - "box_id": "5", - "ornament_id": "10", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "77607", - "box_id": "5", - "ornament_id": "11", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77608", - "box_id": "5", - "ornament_id": "12", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77609", - "box_id": "5", - "ornament_id": "13", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77610", - "box_id": "6", - "ornament_id": "1", - "audience": "common", - "count": "1360", - "used": "0" - }, - { - "id": "77611", - "box_id": "6", - "ornament_id": "2", - "audience": "common", - "count": "587", - "used": "0" - }, - { - "id": "77612", - "box_id": "6", - "ornament_id": "3", - "audience": "common", - "count": "252", - "used": "0" - }, - { - "id": "77613", - "box_id": "6", - "ornament_id": "4", - "audience": "common", - "count": "109", - "used": "0" - }, - { - "id": "77614", - "box_id": "6", - "ornament_id": "5", - "audience": "common", - "count": "48", - "used": "0" - }, - { - "id": "77615", - "box_id": "6", - "ornament_id": "6", - "audience": "common", - "count": "20", - "used": "0" - }, - { - "id": "77616", - "box_id": "6", - "ornament_id": "7", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "77617", - "box_id": "6", - "ornament_id": "8", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "77618", - "box_id": "6", - "ornament_id": "9", - "audience": "common", - "count": "4", - "used": "0" - }, - { - "id": "77619", - "box_id": "6", - "ornament_id": "10", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77620", - "box_id": "6", - "ornament_id": "11", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77621", - "box_id": "6", - "ornament_id": "12", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77622", - "box_id": "6", - "ornament_id": "13", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77623", - "box_id": "6", - "ornament_id": "1", - "audience": "anchor", - "count": "1360", - "used": "0" - }, - { - "id": "77624", - "box_id": "6", - "ornament_id": "2", - "audience": "anchor", - "count": "587", - "used": "0" - }, - { - "id": "77625", - "box_id": "6", - "ornament_id": "3", - "audience": "anchor", - "count": "252", - "used": "0" - }, - { - "id": "77626", - "box_id": "6", - "ornament_id": "4", - "audience": "anchor", - "count": "109", - "used": "0" - }, - { - "id": "77627", - "box_id": "6", - "ornament_id": "5", - "audience": "anchor", - "count": "48", - "used": "0" - }, - { - "id": "77628", - "box_id": "6", - "ornament_id": "6", - "audience": "anchor", - "count": "20", - "used": "0" - }, - { - "id": "77629", - "box_id": "6", - "ornament_id": "7", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "77630", - "box_id": "6", - "ornament_id": "8", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "77631", - "box_id": "6", - "ornament_id": "9", - "audience": "anchor", - "count": "4", - "used": "0" - }, - { - "id": "77632", - "box_id": "6", - "ornament_id": "10", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "77633", - "box_id": "6", - "ornament_id": "11", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77634", - "box_id": "6", - "ornament_id": "12", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77635", - "box_id": "6", - "ornament_id": "13", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77636", - "box_id": "6", - "ornament_id": "1", - "audience": "robot", - "count": "1360", - "used": "0" - }, - { - "id": "77637", - "box_id": "6", - "ornament_id": "2", - "audience": "robot", - "count": "587", - "used": "0" - }, - { - "id": "77638", - "box_id": "6", - "ornament_id": "3", - "audience": "robot", - "count": "252", - "used": "0" - }, - { - "id": "77639", - "box_id": "6", - "ornament_id": "4", - "audience": "robot", - "count": "109", - "used": "0" - }, - { - "id": "77640", - "box_id": "6", - "ornament_id": "5", - "audience": "robot", - "count": "48", - "used": "0" - }, - { - "id": "77641", - "box_id": "6", - "ornament_id": "6", - "audience": "robot", - "count": "20", - "used": "0" - }, - { - "id": "77642", - "box_id": "6", - "ornament_id": "7", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "77643", - "box_id": "6", - "ornament_id": "8", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "77644", - "box_id": "6", - "ornament_id": "9", - "audience": "robot", - "count": "4", - "used": "0" - }, - { - "id": "77645", - "box_id": "6", - "ornament_id": "10", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "77646", - "box_id": "6", - "ornament_id": "11", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77647", - "box_id": "6", - "ornament_id": "12", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77648", - "box_id": "6", - "ornament_id": "13", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77649", - "box_id": "6", - "ornament_id": "1", - "audience": "white", - "count": "1360", - "used": "0" - }, - { - "id": "77650", - "box_id": "6", - "ornament_id": "2", - "audience": "white", - "count": "587", - "used": "0" - }, - { - "id": "77651", - "box_id": "6", - "ornament_id": "3", - "audience": "white", - "count": "252", - "used": "0" - }, - { - "id": "77652", - "box_id": "6", - "ornament_id": "4", - "audience": "white", - "count": "109", - "used": "0" - }, - { - "id": "77653", - "box_id": "6", - "ornament_id": "5", - "audience": "white", - "count": "48", - "used": "0" - }, - { - "id": "77654", - "box_id": "6", - "ornament_id": "6", - "audience": "white", - "count": "20", - "used": "0" - }, - { - "id": "77655", - "box_id": "6", - "ornament_id": "7", - "audience": "white", - "count": "10", - "used": "0" - }, - { - "id": "77656", - "box_id": "6", - "ornament_id": "8", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "77657", - "box_id": "6", - "ornament_id": "9", - "audience": "white", - "count": "4", - "used": "0" - }, - { - "id": "77658", - "box_id": "6", - "ornament_id": "10", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "77659", - "box_id": "6", - "ornament_id": "11", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77660", - "box_id": "6", - "ornament_id": "12", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77661", - "box_id": "6", - "ornament_id": "13", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77662", - "box_id": "7", - "ornament_id": "1", - "audience": "common", - "count": "1346", - "used": "0" - }, - { - "id": "77663", - "box_id": "7", - "ornament_id": "2", - "audience": "common", - "count": "588", - "used": "0" - }, - { - "id": "77664", - "box_id": "7", - "ornament_id": "3", - "audience": "common", - "count": "258", - "used": "0" - }, - { - "id": "77665", - "box_id": "7", - "ornament_id": "4", - "audience": "common", - "count": "113", - "used": "0" - }, - { - "id": "77666", - "box_id": "7", - "ornament_id": "5", - "audience": "common", - "count": "50", - "used": "0" - }, - { - "id": "77667", - "box_id": "7", - "ornament_id": "6", - "audience": "common", - "count": "22", - "used": "0" - }, - { - "id": "77668", - "box_id": "7", - "ornament_id": "7", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "77669", - "box_id": "7", - "ornament_id": "8", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "77670", - "box_id": "7", - "ornament_id": "9", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77671", - "box_id": "7", - "ornament_id": "10", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77672", - "box_id": "7", - "ornament_id": "11", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77673", - "box_id": "7", - "ornament_id": "12", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77674", - "box_id": "7", - "ornament_id": "13", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77675", - "box_id": "7", - "ornament_id": "1", - "audience": "anchor", - "count": "1346", - "used": "0" - }, - { - "id": "77676", - "box_id": "7", - "ornament_id": "2", - "audience": "anchor", - "count": "588", - "used": "0" - }, - { - "id": "77677", - "box_id": "7", - "ornament_id": "3", - "audience": "anchor", - "count": "258", - "used": "0" - }, - { - "id": "77678", - "box_id": "7", - "ornament_id": "4", - "audience": "anchor", - "count": "113", - "used": "0" - }, - { - "id": "77679", - "box_id": "7", - "ornament_id": "5", - "audience": "anchor", - "count": "50", - "used": "0" - }, - { - "id": "77680", - "box_id": "7", - "ornament_id": "6", - "audience": "anchor", - "count": "22", - "used": "0" - }, - { - "id": "77681", - "box_id": "7", - "ornament_id": "7", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "77682", - "box_id": "7", - "ornament_id": "8", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "77683", - "box_id": "7", - "ornament_id": "9", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "77684", - "box_id": "7", - "ornament_id": "10", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "77685", - "box_id": "7", - "ornament_id": "11", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "77686", - "box_id": "7", - "ornament_id": "12", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77687", - "box_id": "7", - "ornament_id": "13", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77688", - "box_id": "7", - "ornament_id": "1", - "audience": "robot", - "count": "1346", - "used": "0" - }, - { - "id": "77689", - "box_id": "7", - "ornament_id": "2", - "audience": "robot", - "count": "588", - "used": "0" - }, - { - "id": "77690", - "box_id": "7", - "ornament_id": "3", - "audience": "robot", - "count": "258", - "used": "0" - }, - { - "id": "77691", - "box_id": "7", - "ornament_id": "4", - "audience": "robot", - "count": "113", - "used": "0" - }, - { - "id": "77692", - "box_id": "7", - "ornament_id": "5", - "audience": "robot", - "count": "50", - "used": "0" - }, - { - "id": "77693", - "box_id": "7", - "ornament_id": "6", - "audience": "robot", - "count": "22", - "used": "0" - }, - { - "id": "77694", - "box_id": "7", - "ornament_id": "7", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "77695", - "box_id": "7", - "ornament_id": "8", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "77696", - "box_id": "7", - "ornament_id": "9", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "77697", - "box_id": "7", - "ornament_id": "10", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "77698", - "box_id": "7", - "ornament_id": "11", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "77699", - "box_id": "7", - "ornament_id": "12", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77700", - "box_id": "7", - "ornament_id": "13", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77701", - "box_id": "7", - "ornament_id": "1", - "audience": "white", - "count": "1346", - "used": "0" - }, - { - "id": "77702", - "box_id": "7", - "ornament_id": "2", - "audience": "white", - "count": "588", - "used": "0" - }, - { - "id": "77703", - "box_id": "7", - "ornament_id": "3", - "audience": "white", - "count": "258", - "used": "0" - }, - { - "id": "77704", - "box_id": "7", - "ornament_id": "4", - "audience": "white", - "count": "113", - "used": "0" - }, - { - "id": "77705", - "box_id": "7", - "ornament_id": "5", - "audience": "white", - "count": "50", - "used": "0" - }, - { - "id": "77706", - "box_id": "7", - "ornament_id": "6", - "audience": "white", - "count": "22", - "used": "0" - }, - { - "id": "77707", - "box_id": "7", - "ornament_id": "7", - "audience": "white", - "count": "10", - "used": "0" - }, - { - "id": "77708", - "box_id": "7", - "ornament_id": "8", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "77709", - "box_id": "7", - "ornament_id": "9", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "77710", - "box_id": "7", - "ornament_id": "10", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "77711", - "box_id": "7", - "ornament_id": "11", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "77712", - "box_id": "7", - "ornament_id": "12", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77713", - "box_id": "7", - "ornament_id": "13", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77714", - "box_id": "8", - "ornament_id": "1", - "audience": "common", - "count": "1357", - "used": "0" - }, - { - "id": "77715", - "box_id": "8", - "ornament_id": "2", - "audience": "common", - "count": "586", - "used": "0" - }, - { - "id": "77716", - "box_id": "8", - "ornament_id": "3", - "audience": "common", - "count": "254", - "used": "0" - }, - { - "id": "77717", - "box_id": "8", - "ornament_id": "4", - "audience": "common", - "count": "110", - "used": "0" - }, - { - "id": "77718", - "box_id": "8", - "ornament_id": "5", - "audience": "common", - "count": "48", - "used": "0" - }, - { - "id": "77719", - "box_id": "8", - "ornament_id": "6", - "audience": "common", - "count": "21", - "used": "0" - }, - { - "id": "77720", - "box_id": "8", - "ornament_id": "7", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "77721", - "box_id": "8", - "ornament_id": "8", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "77722", - "box_id": "8", - "ornament_id": "9", - "audience": "common", - "count": "4", - "used": "0" - }, - { - "id": "77723", - "box_id": "8", - "ornament_id": "10", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77724", - "box_id": "8", - "ornament_id": "11", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77725", - "box_id": "8", - "ornament_id": "12", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77726", - "box_id": "8", - "ornament_id": "13", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77727", - "box_id": "8", - "ornament_id": "1", - "audience": "anchor", - "count": "1357", - "used": "0" - }, - { - "id": "77728", - "box_id": "8", - "ornament_id": "2", - "audience": "anchor", - "count": "586", - "used": "0" - }, - { - "id": "77729", - "box_id": "8", - "ornament_id": "3", - "audience": "anchor", - "count": "254", - "used": "0" - }, - { - "id": "77730", - "box_id": "8", - "ornament_id": "4", - "audience": "anchor", - "count": "110", - "used": "0" - }, - { - "id": "77731", - "box_id": "8", - "ornament_id": "5", - "audience": "anchor", - "count": "48", - "used": "0" - }, - { - "id": "77732", - "box_id": "8", - "ornament_id": "6", - "audience": "anchor", - "count": "21", - "used": "0" - }, - { - "id": "77733", - "box_id": "8", - "ornament_id": "7", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "77734", - "box_id": "8", - "ornament_id": "8", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "77735", - "box_id": "8", - "ornament_id": "9", - "audience": "anchor", - "count": "4", - "used": "0" - }, - { - "id": "77736", - "box_id": "8", - "ornament_id": "10", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "77737", - "box_id": "8", - "ornament_id": "11", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77738", - "box_id": "8", - "ornament_id": "12", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77739", - "box_id": "8", - "ornament_id": "13", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77740", - "box_id": "8", - "ornament_id": "1", - "audience": "robot", - "count": "1357", - "used": "0" - }, - { - "id": "77741", - "box_id": "8", - "ornament_id": "2", - "audience": "robot", - "count": "586", - "used": "0" - }, - { - "id": "77742", - "box_id": "8", - "ornament_id": "3", - "audience": "robot", - "count": "254", - "used": "0" - }, - { - "id": "77743", - "box_id": "8", - "ornament_id": "4", - "audience": "robot", - "count": "110", - "used": "0" - }, - { - "id": "77744", - "box_id": "8", - "ornament_id": "5", - "audience": "robot", - "count": "48", - "used": "0" - }, - { - "id": "77745", - "box_id": "8", - "ornament_id": "6", - "audience": "robot", - "count": "21", - "used": "0" - }, - { - "id": "77746", - "box_id": "8", - "ornament_id": "7", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "77747", - "box_id": "8", - "ornament_id": "8", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "77748", - "box_id": "8", - "ornament_id": "9", - "audience": "robot", - "count": "4", - "used": "0" - }, - { - "id": "77749", - "box_id": "8", - "ornament_id": "10", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "77750", - "box_id": "8", - "ornament_id": "11", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77751", - "box_id": "8", - "ornament_id": "12", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77752", - "box_id": "8", - "ornament_id": "13", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77753", - "box_id": "8", - "ornament_id": "1", - "audience": "white", - "count": "1357", - "used": "0" - }, - { - "id": "77754", - "box_id": "8", - "ornament_id": "2", - "audience": "white", - "count": "586", - "used": "0" - }, - { - "id": "77755", - "box_id": "8", - "ornament_id": "3", - "audience": "white", - "count": "254", - "used": "0" - }, - { - "id": "77756", - "box_id": "8", - "ornament_id": "4", - "audience": "white", - "count": "110", - "used": "0" - }, - { - "id": "77757", - "box_id": "8", - "ornament_id": "5", - "audience": "white", - "count": "48", - "used": "0" - }, - { - "id": "77758", - "box_id": "8", - "ornament_id": "6", - "audience": "white", - "count": "21", - "used": "0" - }, - { - "id": "77759", - "box_id": "8", - "ornament_id": "7", - "audience": "white", - "count": "10", - "used": "0" - }, - { - "id": "77760", - "box_id": "8", - "ornament_id": "8", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "77761", - "box_id": "8", - "ornament_id": "9", - "audience": "white", - "count": "4", - "used": "0" - }, - { - "id": "77762", - "box_id": "8", - "ornament_id": "10", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "77763", - "box_id": "8", - "ornament_id": "11", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77764", - "box_id": "8", - "ornament_id": "12", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77765", - "box_id": "8", - "ornament_id": "13", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77766", - "box_id": "9", - "ornament_id": "1", - "audience": "common", - "count": "1351", - "used": "0" - }, - { - "id": "77767", - "box_id": "9", - "ornament_id": "2", - "audience": "common", - "count": "586", - "used": "0" - }, - { - "id": "77768", - "box_id": "9", - "ornament_id": "3", - "audience": "common", - "count": "256", - "used": "0" - }, - { - "id": "77769", - "box_id": "9", - "ornament_id": "4", - "audience": "common", - "count": "113", - "used": "0" - }, - { - "id": "77770", - "box_id": "9", - "ornament_id": "5", - "audience": "common", - "count": "49", - "used": "0" - }, - { - "id": "77771", - "box_id": "9", - "ornament_id": "6", - "audience": "common", - "count": "22", - "used": "0" - }, - { - "id": "77772", - "box_id": "9", - "ornament_id": "7", - "audience": "common", - "count": "9", - "used": "0" - }, - { - "id": "77773", - "box_id": "9", - "ornament_id": "8", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "77774", - "box_id": "9", - "ornament_id": "9", - "audience": "common", - "count": "4", - "used": "0" - }, - { - "id": "77775", - "box_id": "9", - "ornament_id": "10", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77776", - "box_id": "9", - "ornament_id": "11", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77777", - "box_id": "9", - "ornament_id": "12", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77778", - "box_id": "9", - "ornament_id": "13", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77779", - "box_id": "9", - "ornament_id": "1", - "audience": "anchor", - "count": "1351", - "used": "0" - }, - { - "id": "77780", - "box_id": "9", - "ornament_id": "2", - "audience": "anchor", - "count": "586", - "used": "0" - }, - { - "id": "77781", - "box_id": "9", - "ornament_id": "3", - "audience": "anchor", - "count": "256", - "used": "0" - }, - { - "id": "77782", - "box_id": "9", - "ornament_id": "4", - "audience": "anchor", - "count": "113", - "used": "0" - }, - { - "id": "77783", - "box_id": "9", - "ornament_id": "5", - "audience": "anchor", - "count": "49", - "used": "0" - }, - { - "id": "77784", - "box_id": "9", - "ornament_id": "6", - "audience": "anchor", - "count": "22", - "used": "0" - }, - { - "id": "77785", - "box_id": "9", - "ornament_id": "7", - "audience": "anchor", - "count": "9", - "used": "0" - }, - { - "id": "77786", - "box_id": "9", - "ornament_id": "8", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "77787", - "box_id": "9", - "ornament_id": "9", - "audience": "anchor", - "count": "4", - "used": "0" - }, - { - "id": "77788", - "box_id": "9", - "ornament_id": "10", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "77789", - "box_id": "9", - "ornament_id": "11", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77790", - "box_id": "9", - "ornament_id": "12", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77791", - "box_id": "9", - "ornament_id": "13", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77792", - "box_id": "9", - "ornament_id": "1", - "audience": "robot", - "count": "1351", - "used": "0" - }, - { - "id": "77793", - "box_id": "9", - "ornament_id": "2", - "audience": "robot", - "count": "586", - "used": "0" - }, - { - "id": "77794", - "box_id": "9", - "ornament_id": "3", - "audience": "robot", - "count": "256", - "used": "0" - }, - { - "id": "77795", - "box_id": "9", - "ornament_id": "4", - "audience": "robot", - "count": "113", - "used": "0" - }, - { - "id": "77796", - "box_id": "9", - "ornament_id": "5", - "audience": "robot", - "count": "49", - "used": "0" - }, - { - "id": "77797", - "box_id": "9", - "ornament_id": "6", - "audience": "robot", - "count": "22", - "used": "0" - }, - { - "id": "77798", - "box_id": "9", - "ornament_id": "7", - "audience": "robot", - "count": "9", - "used": "0" - }, - { - "id": "77799", - "box_id": "9", - "ornament_id": "8", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "77800", - "box_id": "9", - "ornament_id": "9", - "audience": "robot", - "count": "4", - "used": "0" - }, - { - "id": "77801", - "box_id": "9", - "ornament_id": "10", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "77802", - "box_id": "9", - "ornament_id": "11", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77803", - "box_id": "9", - "ornament_id": "12", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77804", - "box_id": "9", - "ornament_id": "13", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77805", - "box_id": "9", - "ornament_id": "1", - "audience": "white", - "count": "1351", - "used": "0" - }, - { - "id": "77806", - "box_id": "9", - "ornament_id": "2", - "audience": "white", - "count": "586", - "used": "0" - }, - { - "id": "77807", - "box_id": "9", - "ornament_id": "3", - "audience": "white", - "count": "256", - "used": "0" - }, - { - "id": "77808", - "box_id": "9", - "ornament_id": "4", - "audience": "white", - "count": "113", - "used": "0" - }, - { - "id": "77809", - "box_id": "9", - "ornament_id": "5", - "audience": "white", - "count": "49", - "used": "0" - }, - { - "id": "77810", - "box_id": "9", - "ornament_id": "6", - "audience": "white", - "count": "22", - "used": "0" - }, - { - "id": "77811", - "box_id": "9", - "ornament_id": "7", - "audience": "white", - "count": "9", - "used": "0" - }, - { - "id": "77812", - "box_id": "9", - "ornament_id": "8", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "77813", - "box_id": "9", - "ornament_id": "9", - "audience": "white", - "count": "4", - "used": "0" - }, - { - "id": "77814", - "box_id": "9", - "ornament_id": "10", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "77815", - "box_id": "9", - "ornament_id": "11", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77816", - "box_id": "9", - "ornament_id": "12", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77817", - "box_id": "9", - "ornament_id": "13", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77818", - "box_id": "10", - "ornament_id": "1", - "audience": "common", - "count": "1360", - "used": "0" - }, - { - "id": "77819", - "box_id": "10", - "ornament_id": "2", - "audience": "common", - "count": "585", - "used": "0" - }, - { - "id": "77820", - "box_id": "10", - "ornament_id": "3", - "audience": "common", - "count": "253", - "used": "0" - }, - { - "id": "77821", - "box_id": "10", - "ornament_id": "4", - "audience": "common", - "count": "109", - "used": "0" - }, - { - "id": "77822", - "box_id": "10", - "ornament_id": "5", - "audience": "common", - "count": "49", - "used": "0" - }, - { - "id": "77823", - "box_id": "10", - "ornament_id": "6", - "audience": "common", - "count": "21", - "used": "0" - }, - { - "id": "77824", - "box_id": "10", - "ornament_id": "7", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "77825", - "box_id": "10", - "ornament_id": "8", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "77826", - "box_id": "10", - "ornament_id": "9", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "77827", - "box_id": "10", - "ornament_id": "10", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77828", - "box_id": "10", - "ornament_id": "11", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77829", - "box_id": "10", - "ornament_id": "12", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77830", - "box_id": "10", - "ornament_id": "13", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77831", - "box_id": "10", - "ornament_id": "1", - "audience": "anchor", - "count": "1360", - "used": "0" - }, - { - "id": "77832", - "box_id": "10", - "ornament_id": "2", - "audience": "anchor", - "count": "585", - "used": "0" - }, - { - "id": "77833", - "box_id": "10", - "ornament_id": "3", - "audience": "anchor", - "count": "253", - "used": "0" - }, - { - "id": "77834", - "box_id": "10", - "ornament_id": "4", - "audience": "anchor", - "count": "109", - "used": "0" - }, - { - "id": "77835", - "box_id": "10", - "ornament_id": "5", - "audience": "anchor", - "count": "49", - "used": "0" - }, - { - "id": "77836", - "box_id": "10", - "ornament_id": "6", - "audience": "anchor", - "count": "21", - "used": "0" - }, - { - "id": "77837", - "box_id": "10", - "ornament_id": "7", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "77838", - "box_id": "10", - "ornament_id": "8", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "77839", - "box_id": "10", - "ornament_id": "9", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "77840", - "box_id": "10", - "ornament_id": "10", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "77841", - "box_id": "10", - "ornament_id": "11", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77842", - "box_id": "10", - "ornament_id": "12", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77843", - "box_id": "10", - "ornament_id": "13", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77844", - "box_id": "10", - "ornament_id": "1", - "audience": "robot", - "count": "1360", - "used": "0" - }, - { - "id": "77845", - "box_id": "10", - "ornament_id": "2", - "audience": "robot", - "count": "585", - "used": "0" - }, - { - "id": "77846", - "box_id": "10", - "ornament_id": "3", - "audience": "robot", - "count": "253", - "used": "0" - }, - { - "id": "77847", - "box_id": "10", - "ornament_id": "4", - "audience": "robot", - "count": "109", - "used": "0" - }, - { - "id": "77848", - "box_id": "10", - "ornament_id": "5", - "audience": "robot", - "count": "49", - "used": "0" - }, - { - "id": "77849", - "box_id": "10", - "ornament_id": "6", - "audience": "robot", - "count": "21", - "used": "0" - }, - { - "id": "77850", - "box_id": "10", - "ornament_id": "7", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "77851", - "box_id": "10", - "ornament_id": "8", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "77852", - "box_id": "10", - "ornament_id": "9", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "77853", - "box_id": "10", - "ornament_id": "10", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "77854", - "box_id": "10", - "ornament_id": "11", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77855", - "box_id": "10", - "ornament_id": "12", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77856", - "box_id": "10", - "ornament_id": "13", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77857", - "box_id": "10", - "ornament_id": "1", - "audience": "white", - "count": "1360", - "used": "0" - }, - { - "id": "77858", - "box_id": "10", - "ornament_id": "2", - "audience": "white", - "count": "585", - "used": "0" - }, - { - "id": "77859", - "box_id": "10", - "ornament_id": "3", - "audience": "white", - "count": "253", - "used": "0" - }, - { - "id": "77860", - "box_id": "10", - "ornament_id": "4", - "audience": "white", - "count": "109", - "used": "0" - }, - { - "id": "77861", - "box_id": "10", - "ornament_id": "5", - "audience": "white", - "count": "49", - "used": "0" - }, - { - "id": "77862", - "box_id": "10", - "ornament_id": "6", - "audience": "white", - "count": "21", - "used": "0" - }, - { - "id": "77863", - "box_id": "10", - "ornament_id": "7", - "audience": "white", - "count": "10", - "used": "0" - }, - { - "id": "77864", - "box_id": "10", - "ornament_id": "8", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "77865", - "box_id": "10", - "ornament_id": "9", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "77866", - "box_id": "10", - "ornament_id": "10", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "77867", - "box_id": "10", - "ornament_id": "11", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77868", - "box_id": "10", - "ornament_id": "12", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77869", - "box_id": "10", - "ornament_id": "13", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77870", - "box_id": "11", - "ornament_id": "1", - "audience": "common", - "count": "1091", - "used": "0" - }, - { - "id": "77871", - "box_id": "11", - "ornament_id": "2", - "audience": "common", - "count": "593", - "used": "0" - }, - { - "id": "77872", - "box_id": "11", - "ornament_id": "3", - "audience": "common", - "count": "324", - "used": "0" - }, - { - "id": "77873", - "box_id": "11", - "ornament_id": "4", - "audience": "common", - "count": "175", - "used": "0" - }, - { - "id": "77874", - "box_id": "11", - "ornament_id": "5", - "audience": "common", - "count": "96", - "used": "0" - }, - { - "id": "77875", - "box_id": "11", - "ornament_id": "6", - "audience": "common", - "count": "53", - "used": "0" - }, - { - "id": "77876", - "box_id": "11", - "ornament_id": "7", - "audience": "common", - "count": "29", - "used": "0" - }, - { - "id": "77877", - "box_id": "11", - "ornament_id": "8", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "77878", - "box_id": "11", - "ornament_id": "9", - "audience": "common", - "count": "8", - "used": "0" - }, - { - "id": "77879", - "box_id": "11", - "ornament_id": "10", - "audience": "common", - "count": "6", - "used": "0" - }, - { - "id": "77880", - "box_id": "11", - "ornament_id": "11", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "77881", - "box_id": "11", - "ornament_id": "12", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "77882", - "box_id": "11", - "ornament_id": "13", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77883", - "box_id": "11", - "ornament_id": "14", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77884", - "box_id": "11", - "ornament_id": "1", - "audience": "anchor", - "count": "1091", - "used": "0" - }, - { - "id": "77885", - "box_id": "11", - "ornament_id": "2", - "audience": "anchor", - "count": "593", - "used": "0" - }, - { - "id": "77886", - "box_id": "11", - "ornament_id": "3", - "audience": "anchor", - "count": "324", - "used": "0" - }, - { - "id": "77887", - "box_id": "11", - "ornament_id": "4", - "audience": "anchor", - "count": "175", - "used": "0" - }, - { - "id": "77888", - "box_id": "11", - "ornament_id": "5", - "audience": "anchor", - "count": "96", - "used": "0" - }, - { - "id": "77889", - "box_id": "11", - "ornament_id": "6", - "audience": "anchor", - "count": "53", - "used": "0" - }, - { - "id": "77890", - "box_id": "11", - "ornament_id": "7", - "audience": "anchor", - "count": "29", - "used": "0" - }, - { - "id": "77891", - "box_id": "11", - "ornament_id": "8", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "77892", - "box_id": "11", - "ornament_id": "9", - "audience": "anchor", - "count": "8", - "used": "0" - }, - { - "id": "77893", - "box_id": "11", - "ornament_id": "10", - "audience": "anchor", - "count": "6", - "used": "0" - }, - { - "id": "77894", - "box_id": "11", - "ornament_id": "11", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "77895", - "box_id": "11", - "ornament_id": "12", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "77896", - "box_id": "11", - "ornament_id": "13", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "77897", - "box_id": "11", - "ornament_id": "14", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77898", - "box_id": "11", - "ornament_id": "1", - "audience": "robot", - "count": "1091", - "used": "0" - }, - { - "id": "77899", - "box_id": "11", - "ornament_id": "2", - "audience": "robot", - "count": "593", - "used": "0" - }, - { - "id": "77900", - "box_id": "11", - "ornament_id": "3", - "audience": "robot", - "count": "324", - "used": "0" - }, - { - "id": "77901", - "box_id": "11", - "ornament_id": "4", - "audience": "robot", - "count": "175", - "used": "0" - }, - { - "id": "77902", - "box_id": "11", - "ornament_id": "5", - "audience": "robot", - "count": "96", - "used": "0" - }, - { - "id": "77903", - "box_id": "11", - "ornament_id": "6", - "audience": "robot", - "count": "53", - "used": "0" - }, - { - "id": "77904", - "box_id": "11", - "ornament_id": "7", - "audience": "robot", - "count": "29", - "used": "0" - }, - { - "id": "77905", - "box_id": "11", - "ornament_id": "8", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "77906", - "box_id": "11", - "ornament_id": "9", - "audience": "robot", - "count": "8", - "used": "0" - }, - { - "id": "77907", - "box_id": "11", - "ornament_id": "10", - "audience": "robot", - "count": "6", - "used": "0" - }, - { - "id": "77908", - "box_id": "11", - "ornament_id": "11", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "77909", - "box_id": "11", - "ornament_id": "12", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "77910", - "box_id": "11", - "ornament_id": "13", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "77911", - "box_id": "11", - "ornament_id": "14", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77912", - "box_id": "11", - "ornament_id": "1", - "audience": "white", - "count": "1091", - "used": "0" - }, - { - "id": "77913", - "box_id": "11", - "ornament_id": "2", - "audience": "white", - "count": "593", - "used": "0" - }, - { - "id": "77914", - "box_id": "11", - "ornament_id": "3", - "audience": "white", - "count": "324", - "used": "0" - }, - { - "id": "77915", - "box_id": "11", - "ornament_id": "4", - "audience": "white", - "count": "175", - "used": "0" - }, - { - "id": "77916", - "box_id": "11", - "ornament_id": "5", - "audience": "white", - "count": "96", - "used": "0" - }, - { - "id": "77917", - "box_id": "11", - "ornament_id": "6", - "audience": "white", - "count": "53", - "used": "0" - }, - { - "id": "77918", - "box_id": "11", - "ornament_id": "7", - "audience": "white", - "count": "29", - "used": "0" - }, - { - "id": "77919", - "box_id": "11", - "ornament_id": "8", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "77920", - "box_id": "11", - "ornament_id": "9", - "audience": "white", - "count": "8", - "used": "0" - }, - { - "id": "77921", - "box_id": "11", - "ornament_id": "10", - "audience": "white", - "count": "6", - "used": "0" - }, - { - "id": "77922", - "box_id": "11", - "ornament_id": "11", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "77923", - "box_id": "11", - "ornament_id": "12", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "77924", - "box_id": "11", - "ornament_id": "13", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "77925", - "box_id": "11", - "ornament_id": "14", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77926", - "box_id": "12", - "ornament_id": "1", - "audience": "common", - "count": "1091", - "used": "0" - }, - { - "id": "77927", - "box_id": "12", - "ornament_id": "2", - "audience": "common", - "count": "594", - "used": "0" - }, - { - "id": "77928", - "box_id": "12", - "ornament_id": "3", - "audience": "common", - "count": "322", - "used": "0" - }, - { - "id": "77929", - "box_id": "12", - "ornament_id": "4", - "audience": "common", - "count": "176", - "used": "0" - }, - { - "id": "77930", - "box_id": "12", - "ornament_id": "5", - "audience": "common", - "count": "96", - "used": "0" - }, - { - "id": "77931", - "box_id": "12", - "ornament_id": "6", - "audience": "common", - "count": "53", - "used": "0" - }, - { - "id": "77932", - "box_id": "12", - "ornament_id": "7", - "audience": "common", - "count": "29", - "used": "0" - }, - { - "id": "77933", - "box_id": "12", - "ornament_id": "8", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "77934", - "box_id": "12", - "ornament_id": "9", - "audience": "common", - "count": "8", - "used": "0" - }, - { - "id": "77935", - "box_id": "12", - "ornament_id": "10", - "audience": "common", - "count": "6", - "used": "0" - }, - { - "id": "77936", - "box_id": "12", - "ornament_id": "11", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "77937", - "box_id": "12", - "ornament_id": "12", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "77938", - "box_id": "12", - "ornament_id": "13", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77939", - "box_id": "12", - "ornament_id": "14", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77940", - "box_id": "12", - "ornament_id": "1", - "audience": "anchor", - "count": "1091", - "used": "0" - }, - { - "id": "77941", - "box_id": "12", - "ornament_id": "2", - "audience": "anchor", - "count": "594", - "used": "0" - }, - { - "id": "77942", - "box_id": "12", - "ornament_id": "3", - "audience": "anchor", - "count": "322", - "used": "0" - }, - { - "id": "77943", - "box_id": "12", - "ornament_id": "4", - "audience": "anchor", - "count": "176", - "used": "0" - }, - { - "id": "77944", - "box_id": "12", - "ornament_id": "5", - "audience": "anchor", - "count": "96", - "used": "0" - }, - { - "id": "77945", - "box_id": "12", - "ornament_id": "6", - "audience": "anchor", - "count": "53", - "used": "0" - }, - { - "id": "77946", - "box_id": "12", - "ornament_id": "7", - "audience": "anchor", - "count": "29", - "used": "0" - }, - { - "id": "77947", - "box_id": "12", - "ornament_id": "8", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "77948", - "box_id": "12", - "ornament_id": "9", - "audience": "anchor", - "count": "8", - "used": "0" - }, - { - "id": "77949", - "box_id": "12", - "ornament_id": "10", - "audience": "anchor", - "count": "6", - "used": "0" - }, - { - "id": "77950", - "box_id": "12", - "ornament_id": "11", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "77951", - "box_id": "12", - "ornament_id": "12", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "77952", - "box_id": "12", - "ornament_id": "13", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "77953", - "box_id": "12", - "ornament_id": "14", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "77954", - "box_id": "12", - "ornament_id": "1", - "audience": "robot", - "count": "1091", - "used": "0" - }, - { - "id": "77955", - "box_id": "12", - "ornament_id": "2", - "audience": "robot", - "count": "594", - "used": "0" - }, - { - "id": "77956", - "box_id": "12", - "ornament_id": "3", - "audience": "robot", - "count": "322", - "used": "0" - }, - { - "id": "77957", - "box_id": "12", - "ornament_id": "4", - "audience": "robot", - "count": "176", - "used": "0" - }, - { - "id": "77958", - "box_id": "12", - "ornament_id": "5", - "audience": "robot", - "count": "96", - "used": "0" - }, - { - "id": "77959", - "box_id": "12", - "ornament_id": "6", - "audience": "robot", - "count": "53", - "used": "0" - }, - { - "id": "77960", - "box_id": "12", - "ornament_id": "7", - "audience": "robot", - "count": "29", - "used": "0" - }, - { - "id": "77961", - "box_id": "12", - "ornament_id": "8", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "77962", - "box_id": "12", - "ornament_id": "9", - "audience": "robot", - "count": "8", - "used": "0" - }, - { - "id": "77963", - "box_id": "12", - "ornament_id": "10", - "audience": "robot", - "count": "6", - "used": "0" - }, - { - "id": "77964", - "box_id": "12", - "ornament_id": "11", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "77965", - "box_id": "12", - "ornament_id": "12", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "77966", - "box_id": "12", - "ornament_id": "13", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "77967", - "box_id": "12", - "ornament_id": "14", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "77968", - "box_id": "12", - "ornament_id": "1", - "audience": "white", - "count": "1091", - "used": "0" - }, - { - "id": "77969", - "box_id": "12", - "ornament_id": "2", - "audience": "white", - "count": "594", - "used": "0" - }, - { - "id": "77970", - "box_id": "12", - "ornament_id": "3", - "audience": "white", - "count": "322", - "used": "0" - }, - { - "id": "77971", - "box_id": "12", - "ornament_id": "4", - "audience": "white", - "count": "176", - "used": "0" - }, - { - "id": "77972", - "box_id": "12", - "ornament_id": "5", - "audience": "white", - "count": "96", - "used": "0" - }, - { - "id": "77973", - "box_id": "12", - "ornament_id": "6", - "audience": "white", - "count": "53", - "used": "0" - }, - { - "id": "77974", - "box_id": "12", - "ornament_id": "7", - "audience": "white", - "count": "29", - "used": "0" - }, - { - "id": "77975", - "box_id": "12", - "ornament_id": "8", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "77976", - "box_id": "12", - "ornament_id": "9", - "audience": "white", - "count": "8", - "used": "0" - }, - { - "id": "77977", - "box_id": "12", - "ornament_id": "10", - "audience": "white", - "count": "6", - "used": "0" - }, - { - "id": "77978", - "box_id": "12", - "ornament_id": "11", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "77979", - "box_id": "12", - "ornament_id": "12", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "77980", - "box_id": "12", - "ornament_id": "13", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "77981", - "box_id": "12", - "ornament_id": "14", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "77982", - "box_id": "13", - "ornament_id": "1", - "audience": "common", - "count": "1090", - "used": "0" - }, - { - "id": "77983", - "box_id": "13", - "ornament_id": "2", - "audience": "common", - "count": "593", - "used": "0" - }, - { - "id": "77984", - "box_id": "13", - "ornament_id": "3", - "audience": "common", - "count": "323", - "used": "0" - }, - { - "id": "77985", - "box_id": "13", - "ornament_id": "4", - "audience": "common", - "count": "176", - "used": "0" - }, - { - "id": "77986", - "box_id": "13", - "ornament_id": "5", - "audience": "common", - "count": "96", - "used": "0" - }, - { - "id": "77987", - "box_id": "13", - "ornament_id": "6", - "audience": "common", - "count": "53", - "used": "0" - }, - { - "id": "77988", - "box_id": "13", - "ornament_id": "7", - "audience": "common", - "count": "29", - "used": "0" - }, - { - "id": "77989", - "box_id": "13", - "ornament_id": "8", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "77990", - "box_id": "13", - "ornament_id": "9", - "audience": "common", - "count": "9", - "used": "0" - }, - { - "id": "77991", - "box_id": "13", - "ornament_id": "10", - "audience": "common", - "count": "6", - "used": "0" - }, - { - "id": "77992", - "box_id": "13", - "ornament_id": "11", - "audience": "common", - "count": "4", - "used": "0" - }, - { - "id": "77993", - "box_id": "13", - "ornament_id": "12", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77994", - "box_id": "13", - "ornament_id": "13", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "77995", - "box_id": "13", - "ornament_id": "14", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "77996", - "box_id": "13", - "ornament_id": "1", - "audience": "anchor", - "count": "1090", - "used": "0" - }, - { - "id": "77997", - "box_id": "13", - "ornament_id": "2", - "audience": "anchor", - "count": "593", - "used": "0" - }, - { - "id": "77998", - "box_id": "13", - "ornament_id": "3", - "audience": "anchor", - "count": "323", - "used": "0" - }, - { - "id": "77999", - "box_id": "13", - "ornament_id": "4", - "audience": "anchor", - "count": "176", - "used": "0" - }, - { - "id": "78000", - "box_id": "13", - "ornament_id": "5", - "audience": "anchor", - "count": "96", - "used": "0" - }, - { - "id": "78001", - "box_id": "13", - "ornament_id": "6", - "audience": "anchor", - "count": "53", - "used": "0" - }, - { - "id": "78002", - "box_id": "13", - "ornament_id": "7", - "audience": "anchor", - "count": "29", - "used": "0" - }, - { - "id": "78003", - "box_id": "13", - "ornament_id": "8", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78004", - "box_id": "13", - "ornament_id": "9", - "audience": "anchor", - "count": "9", - "used": "0" - }, - { - "id": "78005", - "box_id": "13", - "ornament_id": "10", - "audience": "anchor", - "count": "6", - "used": "0" - }, - { - "id": "78006", - "box_id": "13", - "ornament_id": "11", - "audience": "anchor", - "count": "4", - "used": "0" - }, - { - "id": "78007", - "box_id": "13", - "ornament_id": "12", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "78008", - "box_id": "13", - "ornament_id": "13", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "78009", - "box_id": "13", - "ornament_id": "14", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "78010", - "box_id": "13", - "ornament_id": "1", - "audience": "robot", - "count": "1090", - "used": "0" - }, - { - "id": "78011", - "box_id": "13", - "ornament_id": "2", - "audience": "robot", - "count": "593", - "used": "0" - }, - { - "id": "78012", - "box_id": "13", - "ornament_id": "3", - "audience": "robot", - "count": "323", - "used": "0" - }, - { - "id": "78013", - "box_id": "13", - "ornament_id": "4", - "audience": "robot", - "count": "176", - "used": "0" - }, - { - "id": "78014", - "box_id": "13", - "ornament_id": "5", - "audience": "robot", - "count": "96", - "used": "0" - }, - { - "id": "78015", - "box_id": "13", - "ornament_id": "6", - "audience": "robot", - "count": "53", - "used": "0" - }, - { - "id": "78016", - "box_id": "13", - "ornament_id": "7", - "audience": "robot", - "count": "29", - "used": "0" - }, - { - "id": "78017", - "box_id": "13", - "ornament_id": "8", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78018", - "box_id": "13", - "ornament_id": "9", - "audience": "robot", - "count": "9", - "used": "0" - }, - { - "id": "78019", - "box_id": "13", - "ornament_id": "10", - "audience": "robot", - "count": "6", - "used": "0" - }, - { - "id": "78020", - "box_id": "13", - "ornament_id": "11", - "audience": "robot", - "count": "4", - "used": "0" - }, - { - "id": "78021", - "box_id": "13", - "ornament_id": "12", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "78022", - "box_id": "13", - "ornament_id": "13", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "78023", - "box_id": "13", - "ornament_id": "14", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "78024", - "box_id": "13", - "ornament_id": "1", - "audience": "white", - "count": "1090", - "used": "0" - }, - { - "id": "78025", - "box_id": "13", - "ornament_id": "2", - "audience": "white", - "count": "593", - "used": "0" - }, - { - "id": "78026", - "box_id": "13", - "ornament_id": "3", - "audience": "white", - "count": "323", - "used": "0" - }, - { - "id": "78027", - "box_id": "13", - "ornament_id": "4", - "audience": "white", - "count": "176", - "used": "0" - }, - { - "id": "78028", - "box_id": "13", - "ornament_id": "5", - "audience": "white", - "count": "96", - "used": "0" - }, - { - "id": "78029", - "box_id": "13", - "ornament_id": "6", - "audience": "white", - "count": "53", - "used": "0" - }, - { - "id": "78030", - "box_id": "13", - "ornament_id": "7", - "audience": "white", - "count": "29", - "used": "0" - }, - { - "id": "78031", - "box_id": "13", - "ornament_id": "8", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78032", - "box_id": "13", - "ornament_id": "9", - "audience": "white", - "count": "9", - "used": "0" - }, - { - "id": "78033", - "box_id": "13", - "ornament_id": "10", - "audience": "white", - "count": "6", - "used": "0" - }, - { - "id": "78034", - "box_id": "13", - "ornament_id": "11", - "audience": "white", - "count": "4", - "used": "0" - }, - { - "id": "78035", - "box_id": "13", - "ornament_id": "12", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "78036", - "box_id": "13", - "ornament_id": "13", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "78037", - "box_id": "13", - "ornament_id": "14", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "78038", - "box_id": "14", - "ornament_id": "1", - "audience": "common", - "count": "1097", - "used": "0" - }, - { - "id": "78039", - "box_id": "14", - "ornament_id": "2", - "audience": "common", - "count": "593", - "used": "0" - }, - { - "id": "78040", - "box_id": "14", - "ornament_id": "3", - "audience": "common", - "count": "322", - "used": "0" - }, - { - "id": "78041", - "box_id": "14", - "ornament_id": "4", - "audience": "common", - "count": "175", - "used": "0" - }, - { - "id": "78042", - "box_id": "14", - "ornament_id": "5", - "audience": "common", - "count": "94", - "used": "0" - }, - { - "id": "78043", - "box_id": "14", - "ornament_id": "6", - "audience": "common", - "count": "52", - "used": "0" - }, - { - "id": "78044", - "box_id": "14", - "ornament_id": "7", - "audience": "common", - "count": "28", - "used": "0" - }, - { - "id": "78045", - "box_id": "14", - "ornament_id": "8", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "78046", - "box_id": "14", - "ornament_id": "9", - "audience": "common", - "count": "9", - "used": "0" - }, - { - "id": "78047", - "box_id": "14", - "ornament_id": "10", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "78048", - "box_id": "14", - "ornament_id": "11", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78049", - "box_id": "14", - "ornament_id": "12", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78050", - "box_id": "14", - "ornament_id": "13", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "78051", - "box_id": "14", - "ornament_id": "14", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "78052", - "box_id": "14", - "ornament_id": "1", - "audience": "anchor", - "count": "1097", - "used": "0" - }, - { - "id": "78053", - "box_id": "14", - "ornament_id": "2", - "audience": "anchor", - "count": "593", - "used": "0" - }, - { - "id": "78054", - "box_id": "14", - "ornament_id": "3", - "audience": "anchor", - "count": "322", - "used": "0" - }, - { - "id": "78055", - "box_id": "14", - "ornament_id": "4", - "audience": "anchor", - "count": "175", - "used": "0" - }, - { - "id": "78056", - "box_id": "14", - "ornament_id": "5", - "audience": "anchor", - "count": "94", - "used": "0" - }, - { - "id": "78057", - "box_id": "14", - "ornament_id": "6", - "audience": "anchor", - "count": "52", - "used": "0" - }, - { - "id": "78058", - "box_id": "14", - "ornament_id": "7", - "audience": "anchor", - "count": "28", - "used": "0" - }, - { - "id": "78059", - "box_id": "14", - "ornament_id": "8", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78060", - "box_id": "14", - "ornament_id": "9", - "audience": "anchor", - "count": "9", - "used": "0" - }, - { - "id": "78061", - "box_id": "14", - "ornament_id": "10", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "78062", - "box_id": "14", - "ornament_id": "11", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78063", - "box_id": "14", - "ornament_id": "12", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78064", - "box_id": "14", - "ornament_id": "13", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "78065", - "box_id": "14", - "ornament_id": "14", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "78066", - "box_id": "14", - "ornament_id": "1", - "audience": "robot", - "count": "1097", - "used": "0" - }, - { - "id": "78067", - "box_id": "14", - "ornament_id": "2", - "audience": "robot", - "count": "593", - "used": "0" - }, - { - "id": "78068", - "box_id": "14", - "ornament_id": "3", - "audience": "robot", - "count": "322", - "used": "0" - }, - { - "id": "78069", - "box_id": "14", - "ornament_id": "4", - "audience": "robot", - "count": "175", - "used": "0" - }, - { - "id": "78070", - "box_id": "14", - "ornament_id": "5", - "audience": "robot", - "count": "94", - "used": "0" - }, - { - "id": "78071", - "box_id": "14", - "ornament_id": "6", - "audience": "robot", - "count": "52", - "used": "0" - }, - { - "id": "78072", - "box_id": "14", - "ornament_id": "7", - "audience": "robot", - "count": "28", - "used": "0" - }, - { - "id": "78073", - "box_id": "14", - "ornament_id": "8", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78074", - "box_id": "14", - "ornament_id": "9", - "audience": "robot", - "count": "9", - "used": "0" - }, - { - "id": "78075", - "box_id": "14", - "ornament_id": "10", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "78076", - "box_id": "14", - "ornament_id": "11", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78077", - "box_id": "14", - "ornament_id": "12", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78078", - "box_id": "14", - "ornament_id": "13", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "78079", - "box_id": "14", - "ornament_id": "14", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "78080", - "box_id": "14", - "ornament_id": "1", - "audience": "white", - "count": "1097", - "used": "0" - }, - { - "id": "78081", - "box_id": "14", - "ornament_id": "2", - "audience": "white", - "count": "593", - "used": "0" - }, - { - "id": "78082", - "box_id": "14", - "ornament_id": "3", - "audience": "white", - "count": "322", - "used": "0" - }, - { - "id": "78083", - "box_id": "14", - "ornament_id": "4", - "audience": "white", - "count": "175", - "used": "0" - }, - { - "id": "78084", - "box_id": "14", - "ornament_id": "5", - "audience": "white", - "count": "94", - "used": "0" - }, - { - "id": "78085", - "box_id": "14", - "ornament_id": "6", - "audience": "white", - "count": "52", - "used": "0" - }, - { - "id": "78086", - "box_id": "14", - "ornament_id": "7", - "audience": "white", - "count": "28", - "used": "0" - }, - { - "id": "78087", - "box_id": "14", - "ornament_id": "8", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78088", - "box_id": "14", - "ornament_id": "9", - "audience": "white", - "count": "9", - "used": "0" - }, - { - "id": "78089", - "box_id": "14", - "ornament_id": "10", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "78090", - "box_id": "14", - "ornament_id": "11", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78091", - "box_id": "14", - "ornament_id": "12", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78092", - "box_id": "14", - "ornament_id": "13", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "78093", - "box_id": "14", - "ornament_id": "14", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "78094", - "box_id": "15", - "ornament_id": "1", - "audience": "common", - "count": "1091", - "used": "0" - }, - { - "id": "78095", - "box_id": "15", - "ornament_id": "2", - "audience": "common", - "count": "594", - "used": "0" - }, - { - "id": "78096", - "box_id": "15", - "ornament_id": "3", - "audience": "common", - "count": "323", - "used": "0" - }, - { - "id": "78097", - "box_id": "15", - "ornament_id": "4", - "audience": "common", - "count": "175", - "used": "0" - }, - { - "id": "78098", - "box_id": "15", - "ornament_id": "5", - "audience": "common", - "count": "96", - "used": "0" - }, - { - "id": "78099", - "box_id": "15", - "ornament_id": "6", - "audience": "common", - "count": "53", - "used": "0" - }, - { - "id": "78100", - "box_id": "15", - "ornament_id": "7", - "audience": "common", - "count": "29", - "used": "0" - }, - { - "id": "78101", - "box_id": "15", - "ornament_id": "8", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "78102", - "box_id": "15", - "ornament_id": "9", - "audience": "common", - "count": "8", - "used": "0" - }, - { - "id": "78103", - "box_id": "15", - "ornament_id": "10", - "audience": "common", - "count": "6", - "used": "0" - }, - { - "id": "78104", - "box_id": "15", - "ornament_id": "11", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78105", - "box_id": "15", - "ornament_id": "12", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78106", - "box_id": "15", - "ornament_id": "13", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "78107", - "box_id": "15", - "ornament_id": "14", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "78108", - "box_id": "15", - "ornament_id": "1", - "audience": "anchor", - "count": "1091", - "used": "0" - }, - { - "id": "78109", - "box_id": "15", - "ornament_id": "2", - "audience": "anchor", - "count": "594", - "used": "0" - }, - { - "id": "78110", - "box_id": "15", - "ornament_id": "3", - "audience": "anchor", - "count": "323", - "used": "0" - }, - { - "id": "78111", - "box_id": "15", - "ornament_id": "4", - "audience": "anchor", - "count": "175", - "used": "0" - }, - { - "id": "78112", - "box_id": "15", - "ornament_id": "5", - "audience": "anchor", - "count": "96", - "used": "0" - }, - { - "id": "78113", - "box_id": "15", - "ornament_id": "6", - "audience": "anchor", - "count": "53", - "used": "0" - }, - { - "id": "78114", - "box_id": "15", - "ornament_id": "7", - "audience": "anchor", - "count": "29", - "used": "0" - }, - { - "id": "78115", - "box_id": "15", - "ornament_id": "8", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78116", - "box_id": "15", - "ornament_id": "9", - "audience": "anchor", - "count": "8", - "used": "0" - }, - { - "id": "78117", - "box_id": "15", - "ornament_id": "10", - "audience": "anchor", - "count": "6", - "used": "0" - }, - { - "id": "78118", - "box_id": "15", - "ornament_id": "11", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78119", - "box_id": "15", - "ornament_id": "12", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78120", - "box_id": "15", - "ornament_id": "13", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "78121", - "box_id": "15", - "ornament_id": "14", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "78122", - "box_id": "15", - "ornament_id": "1", - "audience": "robot", - "count": "1091", - "used": "0" - }, - { - "id": "78123", - "box_id": "15", - "ornament_id": "2", - "audience": "robot", - "count": "594", - "used": "0" - }, - { - "id": "78124", - "box_id": "15", - "ornament_id": "3", - "audience": "robot", - "count": "323", - "used": "0" - }, - { - "id": "78125", - "box_id": "15", - "ornament_id": "4", - "audience": "robot", - "count": "175", - "used": "0" - }, - { - "id": "78126", - "box_id": "15", - "ornament_id": "5", - "audience": "robot", - "count": "96", - "used": "0" - }, - { - "id": "78127", - "box_id": "15", - "ornament_id": "6", - "audience": "robot", - "count": "53", - "used": "0" - }, - { - "id": "78128", - "box_id": "15", - "ornament_id": "7", - "audience": "robot", - "count": "29", - "used": "0" - }, - { - "id": "78129", - "box_id": "15", - "ornament_id": "8", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78130", - "box_id": "15", - "ornament_id": "9", - "audience": "robot", - "count": "8", - "used": "0" - }, - { - "id": "78131", - "box_id": "15", - "ornament_id": "10", - "audience": "robot", - "count": "6", - "used": "0" - }, - { - "id": "78132", - "box_id": "15", - "ornament_id": "11", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78133", - "box_id": "15", - "ornament_id": "12", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78134", - "box_id": "15", - "ornament_id": "13", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "78135", - "box_id": "15", - "ornament_id": "14", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "78136", - "box_id": "15", - "ornament_id": "1", - "audience": "white", - "count": "1091", - "used": "0" - }, - { - "id": "78137", - "box_id": "15", - "ornament_id": "2", - "audience": "white", - "count": "594", - "used": "0" - }, - { - "id": "78138", - "box_id": "15", - "ornament_id": "3", - "audience": "white", - "count": "323", - "used": "0" - }, - { - "id": "78139", - "box_id": "15", - "ornament_id": "4", - "audience": "white", - "count": "175", - "used": "0" - }, - { - "id": "78140", - "box_id": "15", - "ornament_id": "5", - "audience": "white", - "count": "96", - "used": "0" - }, - { - "id": "78141", - "box_id": "15", - "ornament_id": "6", - "audience": "white", - "count": "53", - "used": "0" - }, - { - "id": "78142", - "box_id": "15", - "ornament_id": "7", - "audience": "white", - "count": "29", - "used": "0" - }, - { - "id": "78143", - "box_id": "15", - "ornament_id": "8", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78144", - "box_id": "15", - "ornament_id": "9", - "audience": "white", - "count": "8", - "used": "0" - }, - { - "id": "78145", - "box_id": "15", - "ornament_id": "10", - "audience": "white", - "count": "6", - "used": "0" - }, - { - "id": "78146", - "box_id": "15", - "ornament_id": "11", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78147", - "box_id": "15", - "ornament_id": "12", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78148", - "box_id": "15", - "ornament_id": "13", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "78149", - "box_id": "15", - "ornament_id": "14", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "78150", - "box_id": "16", - "ornament_id": "1", - "audience": "common", - "count": "1097", - "used": "0" - }, - { - "id": "78151", - "box_id": "16", - "ornament_id": "2", - "audience": "common", - "count": "593", - "used": "0" - }, - { - "id": "78152", - "box_id": "16", - "ornament_id": "3", - "audience": "common", - "count": "321", - "used": "0" - }, - { - "id": "78153", - "box_id": "16", - "ornament_id": "4", - "audience": "common", - "count": "175", - "used": "0" - }, - { - "id": "78154", - "box_id": "16", - "ornament_id": "5", - "audience": "common", - "count": "95", - "used": "0" - }, - { - "id": "78155", - "box_id": "16", - "ornament_id": "6", - "audience": "common", - "count": "52", - "used": "0" - }, - { - "id": "78156", - "box_id": "16", - "ornament_id": "7", - "audience": "common", - "count": "28", - "used": "0" - }, - { - "id": "78157", - "box_id": "16", - "ornament_id": "8", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "78158", - "box_id": "16", - "ornament_id": "9", - "audience": "common", - "count": "9", - "used": "0" - }, - { - "id": "78159", - "box_id": "16", - "ornament_id": "10", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "78160", - "box_id": "16", - "ornament_id": "11", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78161", - "box_id": "16", - "ornament_id": "12", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78162", - "box_id": "16", - "ornament_id": "13", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "78163", - "box_id": "16", - "ornament_id": "14", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "78164", - "box_id": "16", - "ornament_id": "1", - "audience": "anchor", - "count": "1097", - "used": "0" - }, - { - "id": "78165", - "box_id": "16", - "ornament_id": "2", - "audience": "anchor", - "count": "593", - "used": "0" - }, - { - "id": "78166", - "box_id": "16", - "ornament_id": "3", - "audience": "anchor", - "count": "321", - "used": "0" - }, - { - "id": "78167", - "box_id": "16", - "ornament_id": "4", - "audience": "anchor", - "count": "175", - "used": "0" - }, - { - "id": "78168", - "box_id": "16", - "ornament_id": "5", - "audience": "anchor", - "count": "95", - "used": "0" - }, - { - "id": "78169", - "box_id": "16", - "ornament_id": "6", - "audience": "anchor", - "count": "52", - "used": "0" - }, - { - "id": "78170", - "box_id": "16", - "ornament_id": "7", - "audience": "anchor", - "count": "28", - "used": "0" - }, - { - "id": "78171", - "box_id": "16", - "ornament_id": "8", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78172", - "box_id": "16", - "ornament_id": "9", - "audience": "anchor", - "count": "9", - "used": "0" - }, - { - "id": "78173", - "box_id": "16", - "ornament_id": "10", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "78174", - "box_id": "16", - "ornament_id": "11", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78175", - "box_id": "16", - "ornament_id": "12", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78176", - "box_id": "16", - "ornament_id": "13", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "78177", - "box_id": "16", - "ornament_id": "14", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "78178", - "box_id": "16", - "ornament_id": "1", - "audience": "robot", - "count": "1097", - "used": "0" - }, - { - "id": "78179", - "box_id": "16", - "ornament_id": "2", - "audience": "robot", - "count": "593", - "used": "0" - }, - { - "id": "78180", - "box_id": "16", - "ornament_id": "3", - "audience": "robot", - "count": "321", - "used": "0" - }, - { - "id": "78181", - "box_id": "16", - "ornament_id": "4", - "audience": "robot", - "count": "175", - "used": "0" - }, - { - "id": "78182", - "box_id": "16", - "ornament_id": "5", - "audience": "robot", - "count": "95", - "used": "0" - }, - { - "id": "78183", - "box_id": "16", - "ornament_id": "6", - "audience": "robot", - "count": "52", - "used": "0" - }, - { - "id": "78184", - "box_id": "16", - "ornament_id": "7", - "audience": "robot", - "count": "28", - "used": "0" - }, - { - "id": "78185", - "box_id": "16", - "ornament_id": "8", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78186", - "box_id": "16", - "ornament_id": "9", - "audience": "robot", - "count": "9", - "used": "0" - }, - { - "id": "78187", - "box_id": "16", - "ornament_id": "10", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "78188", - "box_id": "16", - "ornament_id": "11", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78189", - "box_id": "16", - "ornament_id": "12", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78190", - "box_id": "16", - "ornament_id": "13", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "78191", - "box_id": "16", - "ornament_id": "14", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "78192", - "box_id": "16", - "ornament_id": "1", - "audience": "white", - "count": "1097", - "used": "0" - }, - { - "id": "78193", - "box_id": "16", - "ornament_id": "2", - "audience": "white", - "count": "593", - "used": "0" - }, - { - "id": "78194", - "box_id": "16", - "ornament_id": "3", - "audience": "white", - "count": "321", - "used": "0" - }, - { - "id": "78195", - "box_id": "16", - "ornament_id": "4", - "audience": "white", - "count": "175", - "used": "0" - }, - { - "id": "78196", - "box_id": "16", - "ornament_id": "5", - "audience": "white", - "count": "95", - "used": "0" - }, - { - "id": "78197", - "box_id": "16", - "ornament_id": "6", - "audience": "white", - "count": "52", - "used": "0" - }, - { - "id": "78198", - "box_id": "16", - "ornament_id": "7", - "audience": "white", - "count": "28", - "used": "0" - }, - { - "id": "78199", - "box_id": "16", - "ornament_id": "8", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78200", - "box_id": "16", - "ornament_id": "9", - "audience": "white", - "count": "9", - "used": "0" - }, - { - "id": "78201", - "box_id": "16", - "ornament_id": "10", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "78202", - "box_id": "16", - "ornament_id": "11", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78203", - "box_id": "16", - "ornament_id": "12", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78204", - "box_id": "16", - "ornament_id": "13", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "78205", - "box_id": "16", - "ornament_id": "14", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "78206", - "box_id": "17", - "ornament_id": "1", - "audience": "common", - "count": "1094", - "used": "0" - }, - { - "id": "78207", - "box_id": "17", - "ornament_id": "2", - "audience": "common", - "count": "594", - "used": "0" - }, - { - "id": "78208", - "box_id": "17", - "ornament_id": "3", - "audience": "common", - "count": "322", - "used": "0" - }, - { - "id": "78209", - "box_id": "17", - "ornament_id": "4", - "audience": "common", - "count": "175", - "used": "0" - }, - { - "id": "78210", - "box_id": "17", - "ornament_id": "5", - "audience": "common", - "count": "95", - "used": "0" - }, - { - "id": "78211", - "box_id": "17", - "ornament_id": "6", - "audience": "common", - "count": "52", - "used": "0" - }, - { - "id": "78212", - "box_id": "17", - "ornament_id": "7", - "audience": "common", - "count": "29", - "used": "0" - }, - { - "id": "78213", - "box_id": "17", - "ornament_id": "8", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "78214", - "box_id": "17", - "ornament_id": "9", - "audience": "common", - "count": "9", - "used": "0" - }, - { - "id": "78215", - "box_id": "17", - "ornament_id": "10", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "78216", - "box_id": "17", - "ornament_id": "11", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78217", - "box_id": "17", - "ornament_id": "12", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78218", - "box_id": "17", - "ornament_id": "13", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "78219", - "box_id": "17", - "ornament_id": "14", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "78220", - "box_id": "17", - "ornament_id": "1", - "audience": "anchor", - "count": "1094", - "used": "0" - }, - { - "id": "78221", - "box_id": "17", - "ornament_id": "2", - "audience": "anchor", - "count": "594", - "used": "0" - }, - { - "id": "78222", - "box_id": "17", - "ornament_id": "3", - "audience": "anchor", - "count": "322", - "used": "0" - }, - { - "id": "78223", - "box_id": "17", - "ornament_id": "4", - "audience": "anchor", - "count": "175", - "used": "0" - }, - { - "id": "78224", - "box_id": "17", - "ornament_id": "5", - "audience": "anchor", - "count": "95", - "used": "0" - }, - { - "id": "78225", - "box_id": "17", - "ornament_id": "6", - "audience": "anchor", - "count": "52", - "used": "0" - }, - { - "id": "78226", - "box_id": "17", - "ornament_id": "7", - "audience": "anchor", - "count": "29", - "used": "0" - }, - { - "id": "78227", - "box_id": "17", - "ornament_id": "8", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78228", - "box_id": "17", - "ornament_id": "9", - "audience": "anchor", - "count": "9", - "used": "0" - }, - { - "id": "78229", - "box_id": "17", - "ornament_id": "10", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "78230", - "box_id": "17", - "ornament_id": "11", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78231", - "box_id": "17", - "ornament_id": "12", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78232", - "box_id": "17", - "ornament_id": "13", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "78233", - "box_id": "17", - "ornament_id": "14", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "78234", - "box_id": "17", - "ornament_id": "1", - "audience": "robot", - "count": "1094", - "used": "0" - }, - { - "id": "78235", - "box_id": "17", - "ornament_id": "2", - "audience": "robot", - "count": "594", - "used": "0" - }, - { - "id": "78236", - "box_id": "17", - "ornament_id": "3", - "audience": "robot", - "count": "322", - "used": "0" - }, - { - "id": "78237", - "box_id": "17", - "ornament_id": "4", - "audience": "robot", - "count": "175", - "used": "0" - }, - { - "id": "78238", - "box_id": "17", - "ornament_id": "5", - "audience": "robot", - "count": "95", - "used": "0" - }, - { - "id": "78239", - "box_id": "17", - "ornament_id": "6", - "audience": "robot", - "count": "52", - "used": "0" - }, - { - "id": "78240", - "box_id": "17", - "ornament_id": "7", - "audience": "robot", - "count": "29", - "used": "0" - }, - { - "id": "78241", - "box_id": "17", - "ornament_id": "8", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78242", - "box_id": "17", - "ornament_id": "9", - "audience": "robot", - "count": "9", - "used": "0" - }, - { - "id": "78243", - "box_id": "17", - "ornament_id": "10", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "78244", - "box_id": "17", - "ornament_id": "11", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78245", - "box_id": "17", - "ornament_id": "12", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78246", - "box_id": "17", - "ornament_id": "13", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "78247", - "box_id": "17", - "ornament_id": "14", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "78248", - "box_id": "17", - "ornament_id": "1", - "audience": "white", - "count": "1094", - "used": "0" - }, - { - "id": "78249", - "box_id": "17", - "ornament_id": "2", - "audience": "white", - "count": "594", - "used": "0" - }, - { - "id": "78250", - "box_id": "17", - "ornament_id": "3", - "audience": "white", - "count": "322", - "used": "0" - }, - { - "id": "78251", - "box_id": "17", - "ornament_id": "4", - "audience": "white", - "count": "175", - "used": "0" - }, - { - "id": "78252", - "box_id": "17", - "ornament_id": "5", - "audience": "white", - "count": "95", - "used": "0" - }, - { - "id": "78253", - "box_id": "17", - "ornament_id": "6", - "audience": "white", - "count": "52", - "used": "0" - }, - { - "id": "78254", - "box_id": "17", - "ornament_id": "7", - "audience": "white", - "count": "29", - "used": "0" - }, - { - "id": "78255", - "box_id": "17", - "ornament_id": "8", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78256", - "box_id": "17", - "ornament_id": "9", - "audience": "white", - "count": "9", - "used": "0" - }, - { - "id": "78257", - "box_id": "17", - "ornament_id": "10", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "78258", - "box_id": "17", - "ornament_id": "11", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78259", - "box_id": "17", - "ornament_id": "12", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78260", - "box_id": "17", - "ornament_id": "13", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "78261", - "box_id": "17", - "ornament_id": "14", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "78262", - "box_id": "18", - "ornament_id": "1", - "audience": "common", - "count": "1096", - "used": "0" - }, - { - "id": "78263", - "box_id": "18", - "ornament_id": "2", - "audience": "common", - "count": "593", - "used": "0" - }, - { - "id": "78264", - "box_id": "18", - "ornament_id": "3", - "audience": "common", - "count": "323", - "used": "0" - }, - { - "id": "78265", - "box_id": "18", - "ornament_id": "4", - "audience": "common", - "count": "175", - "used": "0" - }, - { - "id": "78266", - "box_id": "18", - "ornament_id": "5", - "audience": "common", - "count": "95", - "used": "0" - }, - { - "id": "78267", - "box_id": "18", - "ornament_id": "6", - "audience": "common", - "count": "52", - "used": "0" - }, - { - "id": "78268", - "box_id": "18", - "ornament_id": "7", - "audience": "common", - "count": "27", - "used": "0" - }, - { - "id": "78269", - "box_id": "18", - "ornament_id": "8", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "78270", - "box_id": "18", - "ornament_id": "9", - "audience": "common", - "count": "9", - "used": "0" - }, - { - "id": "78271", - "box_id": "18", - "ornament_id": "10", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "78272", - "box_id": "18", - "ornament_id": "11", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78273", - "box_id": "18", - "ornament_id": "12", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78274", - "box_id": "18", - "ornament_id": "13", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "78275", - "box_id": "18", - "ornament_id": "14", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "78276", - "box_id": "18", - "ornament_id": "1", - "audience": "anchor", - "count": "1096", - "used": "0" - }, - { - "id": "78277", - "box_id": "18", - "ornament_id": "2", - "audience": "anchor", - "count": "593", - "used": "0" - }, - { - "id": "78278", - "box_id": "18", - "ornament_id": "3", - "audience": "anchor", - "count": "323", - "used": "0" - }, - { - "id": "78279", - "box_id": "18", - "ornament_id": "4", - "audience": "anchor", - "count": "175", - "used": "0" - }, - { - "id": "78280", - "box_id": "18", - "ornament_id": "5", - "audience": "anchor", - "count": "95", - "used": "0" - }, - { - "id": "78281", - "box_id": "18", - "ornament_id": "6", - "audience": "anchor", - "count": "52", - "used": "0" - }, - { - "id": "78282", - "box_id": "18", - "ornament_id": "7", - "audience": "anchor", - "count": "27", - "used": "0" - }, - { - "id": "78283", - "box_id": "18", - "ornament_id": "8", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78284", - "box_id": "18", - "ornament_id": "9", - "audience": "anchor", - "count": "9", - "used": "0" - }, - { - "id": "78285", - "box_id": "18", - "ornament_id": "10", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "78286", - "box_id": "18", - "ornament_id": "11", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78287", - "box_id": "18", - "ornament_id": "12", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78288", - "box_id": "18", - "ornament_id": "13", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "78289", - "box_id": "18", - "ornament_id": "14", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "78290", - "box_id": "18", - "ornament_id": "1", - "audience": "robot", - "count": "1096", - "used": "0" - }, - { - "id": "78291", - "box_id": "18", - "ornament_id": "2", - "audience": "robot", - "count": "593", - "used": "0" - }, - { - "id": "78292", - "box_id": "18", - "ornament_id": "3", - "audience": "robot", - "count": "323", - "used": "0" - }, - { - "id": "78293", - "box_id": "18", - "ornament_id": "4", - "audience": "robot", - "count": "175", - "used": "0" - }, - { - "id": "78294", - "box_id": "18", - "ornament_id": "5", - "audience": "robot", - "count": "95", - "used": "0" - }, - { - "id": "78295", - "box_id": "18", - "ornament_id": "6", - "audience": "robot", - "count": "52", - "used": "0" - }, - { - "id": "78296", - "box_id": "18", - "ornament_id": "7", - "audience": "robot", - "count": "27", - "used": "0" - }, - { - "id": "78297", - "box_id": "18", - "ornament_id": "8", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78298", - "box_id": "18", - "ornament_id": "9", - "audience": "robot", - "count": "9", - "used": "0" - }, - { - "id": "78299", - "box_id": "18", - "ornament_id": "10", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "78300", - "box_id": "18", - "ornament_id": "11", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78301", - "box_id": "18", - "ornament_id": "12", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78302", - "box_id": "18", - "ornament_id": "13", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "78303", - "box_id": "18", - "ornament_id": "14", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "78304", - "box_id": "18", - "ornament_id": "1", - "audience": "white", - "count": "1096", - "used": "0" - }, - { - "id": "78305", - "box_id": "18", - "ornament_id": "2", - "audience": "white", - "count": "593", - "used": "0" - }, - { - "id": "78306", - "box_id": "18", - "ornament_id": "3", - "audience": "white", - "count": "323", - "used": "0" - }, - { - "id": "78307", - "box_id": "18", - "ornament_id": "4", - "audience": "white", - "count": "175", - "used": "0" - }, - { - "id": "78308", - "box_id": "18", - "ornament_id": "5", - "audience": "white", - "count": "95", - "used": "0" - }, - { - "id": "78309", - "box_id": "18", - "ornament_id": "6", - "audience": "white", - "count": "52", - "used": "0" - }, - { - "id": "78310", - "box_id": "18", - "ornament_id": "7", - "audience": "white", - "count": "27", - "used": "0" - }, - { - "id": "78311", - "box_id": "18", - "ornament_id": "8", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78312", - "box_id": "18", - "ornament_id": "9", - "audience": "white", - "count": "9", - "used": "0" - }, - { - "id": "78313", - "box_id": "18", - "ornament_id": "10", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "78314", - "box_id": "18", - "ornament_id": "11", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78315", - "box_id": "18", - "ornament_id": "12", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78316", - "box_id": "18", - "ornament_id": "13", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "78317", - "box_id": "18", - "ornament_id": "14", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "78318", - "box_id": "19", - "ornament_id": "1", - "audience": "common", - "count": "1091", - "used": "0" - }, - { - "id": "78319", - "box_id": "19", - "ornament_id": "2", - "audience": "common", - "count": "594", - "used": "0" - }, - { - "id": "78320", - "box_id": "19", - "ornament_id": "3", - "audience": "common", - "count": "323", - "used": "0" - }, - { - "id": "78321", - "box_id": "19", - "ornament_id": "4", - "audience": "common", - "count": "176", - "used": "0" - }, - { - "id": "78322", - "box_id": "19", - "ornament_id": "5", - "audience": "common", - "count": "95", - "used": "0" - }, - { - "id": "78323", - "box_id": "19", - "ornament_id": "6", - "audience": "common", - "count": "53", - "used": "0" - }, - { - "id": "78324", - "box_id": "19", - "ornament_id": "7", - "audience": "common", - "count": "29", - "used": "0" - }, - { - "id": "78325", - "box_id": "19", - "ornament_id": "8", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "78326", - "box_id": "19", - "ornament_id": "9", - "audience": "common", - "count": "9", - "used": "0" - }, - { - "id": "78327", - "box_id": "19", - "ornament_id": "10", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "78328", - "box_id": "19", - "ornament_id": "11", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78329", - "box_id": "19", - "ornament_id": "12", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78330", - "box_id": "19", - "ornament_id": "13", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "78331", - "box_id": "19", - "ornament_id": "14", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "78332", - "box_id": "19", - "ornament_id": "1", - "audience": "anchor", - "count": "1091", - "used": "0" - }, - { - "id": "78333", - "box_id": "19", - "ornament_id": "2", - "audience": "anchor", - "count": "594", - "used": "0" - }, - { - "id": "78334", - "box_id": "19", - "ornament_id": "3", - "audience": "anchor", - "count": "323", - "used": "0" - }, - { - "id": "78335", - "box_id": "19", - "ornament_id": "4", - "audience": "anchor", - "count": "176", - "used": "0" - }, - { - "id": "78336", - "box_id": "19", - "ornament_id": "5", - "audience": "anchor", - "count": "95", - "used": "0" - }, - { - "id": "78337", - "box_id": "19", - "ornament_id": "6", - "audience": "anchor", - "count": "53", - "used": "0" - }, - { - "id": "78338", - "box_id": "19", - "ornament_id": "7", - "audience": "anchor", - "count": "29", - "used": "0" - }, - { - "id": "78339", - "box_id": "19", - "ornament_id": "8", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78340", - "box_id": "19", - "ornament_id": "9", - "audience": "anchor", - "count": "9", - "used": "0" - }, - { - "id": "78341", - "box_id": "19", - "ornament_id": "10", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "78342", - "box_id": "19", - "ornament_id": "11", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78343", - "box_id": "19", - "ornament_id": "12", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78344", - "box_id": "19", - "ornament_id": "13", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "78345", - "box_id": "19", - "ornament_id": "14", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "78346", - "box_id": "19", - "ornament_id": "1", - "audience": "robot", - "count": "1091", - "used": "0" - }, - { - "id": "78347", - "box_id": "19", - "ornament_id": "2", - "audience": "robot", - "count": "594", - "used": "0" - }, - { - "id": "78348", - "box_id": "19", - "ornament_id": "3", - "audience": "robot", - "count": "323", - "used": "0" - }, - { - "id": "78349", - "box_id": "19", - "ornament_id": "4", - "audience": "robot", - "count": "176", - "used": "0" - }, - { - "id": "78350", - "box_id": "19", - "ornament_id": "5", - "audience": "robot", - "count": "95", - "used": "0" - }, - { - "id": "78351", - "box_id": "19", - "ornament_id": "6", - "audience": "robot", - "count": "53", - "used": "0" - }, - { - "id": "78352", - "box_id": "19", - "ornament_id": "7", - "audience": "robot", - "count": "29", - "used": "0" - }, - { - "id": "78353", - "box_id": "19", - "ornament_id": "8", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78354", - "box_id": "19", - "ornament_id": "9", - "audience": "robot", - "count": "9", - "used": "0" - }, - { - "id": "78355", - "box_id": "19", - "ornament_id": "10", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "78356", - "box_id": "19", - "ornament_id": "11", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78357", - "box_id": "19", - "ornament_id": "12", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78358", - "box_id": "19", - "ornament_id": "13", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "78359", - "box_id": "19", - "ornament_id": "14", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "78360", - "box_id": "19", - "ornament_id": "1", - "audience": "white", - "count": "1091", - "used": "0" - }, - { - "id": "78361", - "box_id": "19", - "ornament_id": "2", - "audience": "white", - "count": "594", - "used": "0" - }, - { - "id": "78362", - "box_id": "19", - "ornament_id": "3", - "audience": "white", - "count": "323", - "used": "0" - }, - { - "id": "78363", - "box_id": "19", - "ornament_id": "4", - "audience": "white", - "count": "176", - "used": "0" - }, - { - "id": "78364", - "box_id": "19", - "ornament_id": "5", - "audience": "white", - "count": "95", - "used": "0" - }, - { - "id": "78365", - "box_id": "19", - "ornament_id": "6", - "audience": "white", - "count": "53", - "used": "0" - }, - { - "id": "78366", - "box_id": "19", - "ornament_id": "7", - "audience": "white", - "count": "29", - "used": "0" - }, - { - "id": "78367", - "box_id": "19", - "ornament_id": "8", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78368", - "box_id": "19", - "ornament_id": "9", - "audience": "white", - "count": "9", - "used": "0" - }, - { - "id": "78369", - "box_id": "19", - "ornament_id": "10", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "78370", - "box_id": "19", - "ornament_id": "11", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78371", - "box_id": "19", - "ornament_id": "12", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78372", - "box_id": "19", - "ornament_id": "13", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "78373", - "box_id": "19", - "ornament_id": "14", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "78374", - "box_id": "20", - "ornament_id": "1", - "audience": "common", - "count": "1090", - "used": "0" - }, - { - "id": "78375", - "box_id": "20", - "ornament_id": "2", - "audience": "common", - "count": "594", - "used": "0" - }, - { - "id": "78376", - "box_id": "20", - "ornament_id": "3", - "audience": "common", - "count": "323", - "used": "0" - }, - { - "id": "78377", - "box_id": "20", - "ornament_id": "4", - "audience": "common", - "count": "176", - "used": "0" - }, - { - "id": "78378", - "box_id": "20", - "ornament_id": "5", - "audience": "common", - "count": "96", - "used": "0" - }, - { - "id": "78379", - "box_id": "20", - "ornament_id": "6", - "audience": "common", - "count": "53", - "used": "0" - }, - { - "id": "78380", - "box_id": "20", - "ornament_id": "7", - "audience": "common", - "count": "29", - "used": "0" - }, - { - "id": "78381", - "box_id": "20", - "ornament_id": "8", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "78382", - "box_id": "20", - "ornament_id": "9", - "audience": "common", - "count": "9", - "used": "0" - }, - { - "id": "78383", - "box_id": "20", - "ornament_id": "10", - "audience": "common", - "count": "5", - "used": "0" - }, - { - "id": "78384", - "box_id": "20", - "ornament_id": "11", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78385", - "box_id": "20", - "ornament_id": "12", - "audience": "common", - "count": "3", - "used": "0" - }, - { - "id": "78386", - "box_id": "20", - "ornament_id": "13", - "audience": "common", - "count": "2", - "used": "0" - }, - { - "id": "78387", - "box_id": "20", - "ornament_id": "14", - "audience": "common", - "count": "1", - "used": "0" - }, - { - "id": "78388", - "box_id": "20", - "ornament_id": "1", - "audience": "anchor", - "count": "1090", - "used": "0" - }, - { - "id": "78389", - "box_id": "20", - "ornament_id": "2", - "audience": "anchor", - "count": "594", - "used": "0" - }, - { - "id": "78390", - "box_id": "20", - "ornament_id": "3", - "audience": "anchor", - "count": "323", - "used": "0" - }, - { - "id": "78391", - "box_id": "20", - "ornament_id": "4", - "audience": "anchor", - "count": "176", - "used": "0" - }, - { - "id": "78392", - "box_id": "20", - "ornament_id": "5", - "audience": "anchor", - "count": "96", - "used": "0" - }, - { - "id": "78393", - "box_id": "20", - "ornament_id": "6", - "audience": "anchor", - "count": "53", - "used": "0" - }, - { - "id": "78394", - "box_id": "20", - "ornament_id": "7", - "audience": "anchor", - "count": "29", - "used": "0" - }, - { - "id": "78395", - "box_id": "20", - "ornament_id": "8", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78396", - "box_id": "20", - "ornament_id": "9", - "audience": "anchor", - "count": "9", - "used": "0" - }, - { - "id": "78397", - "box_id": "20", - "ornament_id": "10", - "audience": "anchor", - "count": "5", - "used": "0" - }, - { - "id": "78398", - "box_id": "20", - "ornament_id": "11", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78399", - "box_id": "20", - "ornament_id": "12", - "audience": "anchor", - "count": "3", - "used": "0" - }, - { - "id": "78400", - "box_id": "20", - "ornament_id": "13", - "audience": "anchor", - "count": "2", - "used": "0" - }, - { - "id": "78401", - "box_id": "20", - "ornament_id": "14", - "audience": "anchor", - "count": "1", - "used": "0" - }, - { - "id": "78402", - "box_id": "20", - "ornament_id": "1", - "audience": "robot", - "count": "1090", - "used": "0" - }, - { - "id": "78403", - "box_id": "20", - "ornament_id": "2", - "audience": "robot", - "count": "594", - "used": "0" - }, - { - "id": "78404", - "box_id": "20", - "ornament_id": "3", - "audience": "robot", - "count": "323", - "used": "0" - }, - { - "id": "78405", - "box_id": "20", - "ornament_id": "4", - "audience": "robot", - "count": "176", - "used": "0" - }, - { - "id": "78406", - "box_id": "20", - "ornament_id": "5", - "audience": "robot", - "count": "96", - "used": "0" - }, - { - "id": "78407", - "box_id": "20", - "ornament_id": "6", - "audience": "robot", - "count": "53", - "used": "0" - }, - { - "id": "78408", - "box_id": "20", - "ornament_id": "7", - "audience": "robot", - "count": "29", - "used": "0" - }, - { - "id": "78409", - "box_id": "20", - "ornament_id": "8", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78410", - "box_id": "20", - "ornament_id": "9", - "audience": "robot", - "count": "9", - "used": "0" - }, - { - "id": "78411", - "box_id": "20", - "ornament_id": "10", - "audience": "robot", - "count": "5", - "used": "0" - }, - { - "id": "78412", - "box_id": "20", - "ornament_id": "11", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78413", - "box_id": "20", - "ornament_id": "12", - "audience": "robot", - "count": "3", - "used": "0" - }, - { - "id": "78414", - "box_id": "20", - "ornament_id": "13", - "audience": "robot", - "count": "2", - "used": "0" - }, - { - "id": "78415", - "box_id": "20", - "ornament_id": "14", - "audience": "robot", - "count": "1", - "used": "0" - }, - { - "id": "78416", - "box_id": "20", - "ornament_id": "1", - "audience": "white", - "count": "1090", - "used": "0" - }, - { - "id": "78417", - "box_id": "20", - "ornament_id": "2", - "audience": "white", - "count": "594", - "used": "0" - }, - { - "id": "78418", - "box_id": "20", - "ornament_id": "3", - "audience": "white", - "count": "323", - "used": "0" - }, - { - "id": "78419", - "box_id": "20", - "ornament_id": "4", - "audience": "white", - "count": "176", - "used": "0" - }, - { - "id": "78420", - "box_id": "20", - "ornament_id": "5", - "audience": "white", - "count": "96", - "used": "0" - }, - { - "id": "78421", - "box_id": "20", - "ornament_id": "6", - "audience": "white", - "count": "53", - "used": "0" - }, - { - "id": "78422", - "box_id": "20", - "ornament_id": "7", - "audience": "white", - "count": "29", - "used": "0" - }, - { - "id": "78423", - "box_id": "20", - "ornament_id": "8", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78424", - "box_id": "20", - "ornament_id": "9", - "audience": "white", - "count": "9", - "used": "0" - }, - { - "id": "78425", - "box_id": "20", - "ornament_id": "10", - "audience": "white", - "count": "5", - "used": "0" - }, - { - "id": "78426", - "box_id": "20", - "ornament_id": "11", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78427", - "box_id": "20", - "ornament_id": "12", - "audience": "white", - "count": "3", - "used": "0" - }, - { - "id": "78428", - "box_id": "20", - "ornament_id": "13", - "audience": "white", - "count": "2", - "used": "0" - }, - { - "id": "78429", - "box_id": "20", - "ornament_id": "14", - "audience": "white", - "count": "1", - "used": "0" - }, - { - "id": "78430", - "box_id": "21", - "ornament_id": "7", - "audience": "common", - "count": "868", - "used": "0" - }, - { - "id": "78431", - "box_id": "21", - "ornament_id": "8", - "audience": "common", - "count": "556", - "used": "0" - }, - { - "id": "78432", - "box_id": "21", - "ornament_id": "9", - "audience": "common", - "count": "356", - "used": "0" - }, - { - "id": "78433", - "box_id": "21", - "ornament_id": "10", - "audience": "common", - "count": "228", - "used": "0" - }, - { - "id": "78434", - "box_id": "21", - "ornament_id": "11", - "audience": "common", - "count": "146", - "used": "0" - }, - { - "id": "78435", - "box_id": "21", - "ornament_id": "12", - "audience": "common", - "count": "94", - "used": "0" - }, - { - "id": "78436", - "box_id": "21", - "ornament_id": "13", - "audience": "common", - "count": "61", - "used": "0" - }, - { - "id": "78437", - "box_id": "21", - "ornament_id": "14", - "audience": "common", - "count": "39", - "used": "0" - }, - { - "id": "78438", - "box_id": "21", - "ornament_id": "15", - "audience": "common", - "count": "25", - "used": "0" - }, - { - "id": "78439", - "box_id": "21", - "ornament_id": "16", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "78440", - "box_id": "21", - "ornament_id": "17", - "audience": "common", - "count": "11", - "used": "0" - }, - { - "id": "78441", - "box_id": "21", - "ornament_id": "7", - "audience": "anchor", - "count": "868", - "used": "0" - }, - { - "id": "78442", - "box_id": "21", - "ornament_id": "8", - "audience": "anchor", - "count": "556", - "used": "0" - }, - { - "id": "78443", - "box_id": "21", - "ornament_id": "9", - "audience": "anchor", - "count": "356", - "used": "0" - }, - { - "id": "78444", - "box_id": "21", - "ornament_id": "10", - "audience": "anchor", - "count": "228", - "used": "0" - }, - { - "id": "78445", - "box_id": "21", - "ornament_id": "11", - "audience": "anchor", - "count": "146", - "used": "0" - }, - { - "id": "78446", - "box_id": "21", - "ornament_id": "12", - "audience": "anchor", - "count": "94", - "used": "0" - }, - { - "id": "78447", - "box_id": "21", - "ornament_id": "13", - "audience": "anchor", - "count": "61", - "used": "0" - }, - { - "id": "78448", - "box_id": "21", - "ornament_id": "14", - "audience": "anchor", - "count": "39", - "used": "0" - }, - { - "id": "78449", - "box_id": "21", - "ornament_id": "15", - "audience": "anchor", - "count": "25", - "used": "0" - }, - { - "id": "78450", - "box_id": "21", - "ornament_id": "16", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78451", - "box_id": "21", - "ornament_id": "17", - "audience": "anchor", - "count": "11", - "used": "0" - }, - { - "id": "78452", - "box_id": "21", - "ornament_id": "7", - "audience": "robot", - "count": "868", - "used": "0" - }, - { - "id": "78453", - "box_id": "21", - "ornament_id": "8", - "audience": "robot", - "count": "556", - "used": "0" - }, - { - "id": "78454", - "box_id": "21", - "ornament_id": "9", - "audience": "robot", - "count": "356", - "used": "0" - }, - { - "id": "78455", - "box_id": "21", - "ornament_id": "10", - "audience": "robot", - "count": "228", - "used": "0" - }, - { - "id": "78456", - "box_id": "21", - "ornament_id": "11", - "audience": "robot", - "count": "146", - "used": "0" - }, - { - "id": "78457", - "box_id": "21", - "ornament_id": "12", - "audience": "robot", - "count": "94", - "used": "0" - }, - { - "id": "78458", - "box_id": "21", - "ornament_id": "13", - "audience": "robot", - "count": "61", - "used": "0" - }, - { - "id": "78459", - "box_id": "21", - "ornament_id": "14", - "audience": "robot", - "count": "39", - "used": "0" - }, - { - "id": "78460", - "box_id": "21", - "ornament_id": "15", - "audience": "robot", - "count": "25", - "used": "0" - }, - { - "id": "78461", - "box_id": "21", - "ornament_id": "16", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78462", - "box_id": "21", - "ornament_id": "17", - "audience": "robot", - "count": "11", - "used": "0" - }, - { - "id": "78463", - "box_id": "21", - "ornament_id": "7", - "audience": "white", - "count": "868", - "used": "0" - }, - { - "id": "78464", - "box_id": "21", - "ornament_id": "8", - "audience": "white", - "count": "556", - "used": "0" - }, - { - "id": "78465", - "box_id": "21", - "ornament_id": "9", - "audience": "white", - "count": "356", - "used": "0" - }, - { - "id": "78466", - "box_id": "21", - "ornament_id": "10", - "audience": "white", - "count": "228", - "used": "0" - }, - { - "id": "78467", - "box_id": "21", - "ornament_id": "11", - "audience": "white", - "count": "146", - "used": "0" - }, - { - "id": "78468", - "box_id": "21", - "ornament_id": "12", - "audience": "white", - "count": "94", - "used": "0" - }, - { - "id": "78469", - "box_id": "21", - "ornament_id": "13", - "audience": "white", - "count": "61", - "used": "0" - }, - { - "id": "78470", - "box_id": "21", - "ornament_id": "14", - "audience": "white", - "count": "39", - "used": "0" - }, - { - "id": "78471", - "box_id": "21", - "ornament_id": "15", - "audience": "white", - "count": "25", - "used": "0" - }, - { - "id": "78472", - "box_id": "21", - "ornament_id": "16", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78473", - "box_id": "21", - "ornament_id": "17", - "audience": "white", - "count": "11", - "used": "0" - }, - { - "id": "78474", - "box_id": "22", - "ornament_id": "7", - "audience": "common", - "count": "879", - "used": "0" - }, - { - "id": "78475", - "box_id": "22", - "ornament_id": "8", - "audience": "common", - "count": "557", - "used": "0" - }, - { - "id": "78476", - "box_id": "22", - "ornament_id": "9", - "audience": "common", - "count": "355", - "used": "0" - }, - { - "id": "78477", - "box_id": "22", - "ornament_id": "10", - "audience": "common", - "count": "225", - "used": "0" - }, - { - "id": "78478", - "box_id": "22", - "ornament_id": "11", - "audience": "common", - "count": "144", - "used": "0" - }, - { - "id": "78479", - "box_id": "22", - "ornament_id": "12", - "audience": "common", - "count": "92", - "used": "0" - }, - { - "id": "78480", - "box_id": "22", - "ornament_id": "13", - "audience": "common", - "count": "59", - "used": "0" - }, - { - "id": "78481", - "box_id": "22", - "ornament_id": "14", - "audience": "common", - "count": "38", - "used": "0" - }, - { - "id": "78482", - "box_id": "22", - "ornament_id": "15", - "audience": "common", - "count": "24", - "used": "0" - }, - { - "id": "78483", - "box_id": "22", - "ornament_id": "16", - "audience": "common", - "count": "17", - "used": "0" - }, - { - "id": "78484", - "box_id": "22", - "ornament_id": "17", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "78485", - "box_id": "22", - "ornament_id": "7", - "audience": "anchor", - "count": "879", - "used": "0" - }, - { - "id": "78486", - "box_id": "22", - "ornament_id": "8", - "audience": "anchor", - "count": "557", - "used": "0" - }, - { - "id": "78487", - "box_id": "22", - "ornament_id": "9", - "audience": "anchor", - "count": "355", - "used": "0" - }, - { - "id": "78488", - "box_id": "22", - "ornament_id": "10", - "audience": "anchor", - "count": "225", - "used": "0" - }, - { - "id": "78489", - "box_id": "22", - "ornament_id": "11", - "audience": "anchor", - "count": "144", - "used": "0" - }, - { - "id": "78490", - "box_id": "22", - "ornament_id": "12", - "audience": "anchor", - "count": "92", - "used": "0" - }, - { - "id": "78491", - "box_id": "22", - "ornament_id": "13", - "audience": "anchor", - "count": "59", - "used": "0" - }, - { - "id": "78492", - "box_id": "22", - "ornament_id": "14", - "audience": "anchor", - "count": "38", - "used": "0" - }, - { - "id": "78493", - "box_id": "22", - "ornament_id": "15", - "audience": "anchor", - "count": "24", - "used": "0" - }, - { - "id": "78494", - "box_id": "22", - "ornament_id": "16", - "audience": "anchor", - "count": "17", - "used": "0" - }, - { - "id": "78495", - "box_id": "22", - "ornament_id": "17", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "78496", - "box_id": "22", - "ornament_id": "7", - "audience": "robot", - "count": "879", - "used": "0" - }, - { - "id": "78497", - "box_id": "22", - "ornament_id": "8", - "audience": "robot", - "count": "557", - "used": "0" - }, - { - "id": "78498", - "box_id": "22", - "ornament_id": "9", - "audience": "robot", - "count": "355", - "used": "0" - }, - { - "id": "78499", - "box_id": "22", - "ornament_id": "10", - "audience": "robot", - "count": "225", - "used": "0" - }, - { - "id": "78500", - "box_id": "22", - "ornament_id": "11", - "audience": "robot", - "count": "144", - "used": "0" - }, - { - "id": "78501", - "box_id": "22", - "ornament_id": "12", - "audience": "robot", - "count": "92", - "used": "0" - }, - { - "id": "78502", - "box_id": "22", - "ornament_id": "13", - "audience": "robot", - "count": "59", - "used": "0" - }, - { - "id": "78503", - "box_id": "22", - "ornament_id": "14", - "audience": "robot", - "count": "38", - "used": "0" - }, - { - "id": "78504", - "box_id": "22", - "ornament_id": "15", - "audience": "robot", - "count": "24", - "used": "0" - }, - { - "id": "78505", - "box_id": "22", - "ornament_id": "16", - "audience": "robot", - "count": "17", - "used": "0" - }, - { - "id": "78506", - "box_id": "22", - "ornament_id": "17", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "78507", - "box_id": "22", - "ornament_id": "7", - "audience": "white", - "count": "879", - "used": "0" - }, - { - "id": "78508", - "box_id": "22", - "ornament_id": "8", - "audience": "white", - "count": "557", - "used": "0" - }, - { - "id": "78509", - "box_id": "22", - "ornament_id": "9", - "audience": "white", - "count": "355", - "used": "0" - }, - { - "id": "78510", - "box_id": "22", - "ornament_id": "10", - "audience": "white", - "count": "225", - "used": "0" - }, - { - "id": "78511", - "box_id": "22", - "ornament_id": "11", - "audience": "white", - "count": "144", - "used": "0" - }, - { - "id": "78512", - "box_id": "22", - "ornament_id": "12", - "audience": "white", - "count": "92", - "used": "0" - }, - { - "id": "78513", - "box_id": "22", - "ornament_id": "13", - "audience": "white", - "count": "59", - "used": "0" - }, - { - "id": "78514", - "box_id": "22", - "ornament_id": "14", - "audience": "white", - "count": "38", - "used": "0" - }, - { - "id": "78515", - "box_id": "22", - "ornament_id": "15", - "audience": "white", - "count": "24", - "used": "0" - }, - { - "id": "78516", - "box_id": "22", - "ornament_id": "16", - "audience": "white", - "count": "17", - "used": "0" - }, - { - "id": "78517", - "box_id": "22", - "ornament_id": "17", - "audience": "white", - "count": "10", - "used": "0" - }, - { - "id": "78518", - "box_id": "23", - "ornament_id": "7", - "audience": "common", - "count": "877", - "used": "0" - }, - { - "id": "78519", - "box_id": "23", - "ornament_id": "8", - "audience": "common", - "count": "557", - "used": "0" - }, - { - "id": "78520", - "box_id": "23", - "ornament_id": "9", - "audience": "common", - "count": "356", - "used": "0" - }, - { - "id": "78521", - "box_id": "23", - "ornament_id": "10", - "audience": "common", - "count": "226", - "used": "0" - }, - { - "id": "78522", - "box_id": "23", - "ornament_id": "11", - "audience": "common", - "count": "143", - "used": "0" - }, - { - "id": "78523", - "box_id": "23", - "ornament_id": "12", - "audience": "common", - "count": "92", - "used": "0" - }, - { - "id": "78524", - "box_id": "23", - "ornament_id": "13", - "audience": "common", - "count": "59", - "used": "0" - }, - { - "id": "78525", - "box_id": "23", - "ornament_id": "14", - "audience": "common", - "count": "38", - "used": "0" - }, - { - "id": "78526", - "box_id": "23", - "ornament_id": "15", - "audience": "common", - "count": "25", - "used": "0" - }, - { - "id": "78527", - "box_id": "23", - "ornament_id": "16", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "78528", - "box_id": "23", - "ornament_id": "17", - "audience": "common", - "count": "11", - "used": "0" - }, - { - "id": "78529", - "box_id": "23", - "ornament_id": "7", - "audience": "anchor", - "count": "877", - "used": "0" - }, - { - "id": "78530", - "box_id": "23", - "ornament_id": "8", - "audience": "anchor", - "count": "557", - "used": "0" - }, - { - "id": "78531", - "box_id": "23", - "ornament_id": "9", - "audience": "anchor", - "count": "356", - "used": "0" - }, - { - "id": "78532", - "box_id": "23", - "ornament_id": "10", - "audience": "anchor", - "count": "226", - "used": "0" - }, - { - "id": "78533", - "box_id": "23", - "ornament_id": "11", - "audience": "anchor", - "count": "143", - "used": "0" - }, - { - "id": "78534", - "box_id": "23", - "ornament_id": "12", - "audience": "anchor", - "count": "92", - "used": "0" - }, - { - "id": "78535", - "box_id": "23", - "ornament_id": "13", - "audience": "anchor", - "count": "59", - "used": "0" - }, - { - "id": "78536", - "box_id": "23", - "ornament_id": "14", - "audience": "anchor", - "count": "38", - "used": "0" - }, - { - "id": "78537", - "box_id": "23", - "ornament_id": "15", - "audience": "anchor", - "count": "25", - "used": "0" - }, - { - "id": "78538", - "box_id": "23", - "ornament_id": "16", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78539", - "box_id": "23", - "ornament_id": "17", - "audience": "anchor", - "count": "11", - "used": "0" - }, - { - "id": "78540", - "box_id": "23", - "ornament_id": "7", - "audience": "robot", - "count": "877", - "used": "0" - }, - { - "id": "78541", - "box_id": "23", - "ornament_id": "8", - "audience": "robot", - "count": "557", - "used": "0" - }, - { - "id": "78542", - "box_id": "23", - "ornament_id": "9", - "audience": "robot", - "count": "356", - "used": "0" - }, - { - "id": "78543", - "box_id": "23", - "ornament_id": "10", - "audience": "robot", - "count": "226", - "used": "0" - }, - { - "id": "78544", - "box_id": "23", - "ornament_id": "11", - "audience": "robot", - "count": "143", - "used": "0" - }, - { - "id": "78545", - "box_id": "23", - "ornament_id": "12", - "audience": "robot", - "count": "92", - "used": "0" - }, - { - "id": "78546", - "box_id": "23", - "ornament_id": "13", - "audience": "robot", - "count": "59", - "used": "0" - }, - { - "id": "78547", - "box_id": "23", - "ornament_id": "14", - "audience": "robot", - "count": "38", - "used": "0" - }, - { - "id": "78548", - "box_id": "23", - "ornament_id": "15", - "audience": "robot", - "count": "25", - "used": "0" - }, - { - "id": "78549", - "box_id": "23", - "ornament_id": "16", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78550", - "box_id": "23", - "ornament_id": "17", - "audience": "robot", - "count": "11", - "used": "0" - }, - { - "id": "78551", - "box_id": "23", - "ornament_id": "7", - "audience": "white", - "count": "877", - "used": "0" - }, - { - "id": "78552", - "box_id": "23", - "ornament_id": "8", - "audience": "white", - "count": "557", - "used": "0" - }, - { - "id": "78553", - "box_id": "23", - "ornament_id": "9", - "audience": "white", - "count": "356", - "used": "0" - }, - { - "id": "78554", - "box_id": "23", - "ornament_id": "10", - "audience": "white", - "count": "226", - "used": "0" - }, - { - "id": "78555", - "box_id": "23", - "ornament_id": "11", - "audience": "white", - "count": "143", - "used": "0" - }, - { - "id": "78556", - "box_id": "23", - "ornament_id": "12", - "audience": "white", - "count": "92", - "used": "0" - }, - { - "id": "78557", - "box_id": "23", - "ornament_id": "13", - "audience": "white", - "count": "59", - "used": "0" - }, - { - "id": "78558", - "box_id": "23", - "ornament_id": "14", - "audience": "white", - "count": "38", - "used": "0" - }, - { - "id": "78559", - "box_id": "23", - "ornament_id": "15", - "audience": "white", - "count": "25", - "used": "0" - }, - { - "id": "78560", - "box_id": "23", - "ornament_id": "16", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78561", - "box_id": "23", - "ornament_id": "17", - "audience": "white", - "count": "11", - "used": "0" - }, - { - "id": "78562", - "box_id": "24", - "ornament_id": "7", - "audience": "common", - "count": "868", - "used": "0" - }, - { - "id": "78563", - "box_id": "24", - "ornament_id": "8", - "audience": "common", - "count": "556", - "used": "0" - }, - { - "id": "78564", - "box_id": "24", - "ornament_id": "9", - "audience": "common", - "count": "356", - "used": "0" - }, - { - "id": "78565", - "box_id": "24", - "ornament_id": "10", - "audience": "common", - "count": "228", - "used": "0" - }, - { - "id": "78566", - "box_id": "24", - "ornament_id": "11", - "audience": "common", - "count": "146", - "used": "0" - }, - { - "id": "78567", - "box_id": "24", - "ornament_id": "12", - "audience": "common", - "count": "94", - "used": "0" - }, - { - "id": "78568", - "box_id": "24", - "ornament_id": "13", - "audience": "common", - "count": "60", - "used": "0" - }, - { - "id": "78569", - "box_id": "24", - "ornament_id": "14", - "audience": "common", - "count": "39", - "used": "0" - }, - { - "id": "78570", - "box_id": "24", - "ornament_id": "15", - "audience": "common", - "count": "26", - "used": "0" - }, - { - "id": "78571", - "box_id": "24", - "ornament_id": "16", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "78572", - "box_id": "24", - "ornament_id": "17", - "audience": "common", - "count": "11", - "used": "0" - }, - { - "id": "78573", - "box_id": "24", - "ornament_id": "7", - "audience": "anchor", - "count": "868", - "used": "0" - }, - { - "id": "78574", - "box_id": "24", - "ornament_id": "8", - "audience": "anchor", - "count": "556", - "used": "0" - }, - { - "id": "78575", - "box_id": "24", - "ornament_id": "9", - "audience": "anchor", - "count": "356", - "used": "0" - }, - { - "id": "78576", - "box_id": "24", - "ornament_id": "10", - "audience": "anchor", - "count": "228", - "used": "0" - }, - { - "id": "78577", - "box_id": "24", - "ornament_id": "11", - "audience": "anchor", - "count": "146", - "used": "0" - }, - { - "id": "78578", - "box_id": "24", - "ornament_id": "12", - "audience": "anchor", - "count": "94", - "used": "0" - }, - { - "id": "78579", - "box_id": "24", - "ornament_id": "13", - "audience": "anchor", - "count": "60", - "used": "0" - }, - { - "id": "78580", - "box_id": "24", - "ornament_id": "14", - "audience": "anchor", - "count": "39", - "used": "0" - }, - { - "id": "78581", - "box_id": "24", - "ornament_id": "15", - "audience": "anchor", - "count": "26", - "used": "0" - }, - { - "id": "78582", - "box_id": "24", - "ornament_id": "16", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78583", - "box_id": "24", - "ornament_id": "17", - "audience": "anchor", - "count": "11", - "used": "0" - }, - { - "id": "78584", - "box_id": "24", - "ornament_id": "7", - "audience": "robot", - "count": "868", - "used": "0" - }, - { - "id": "78585", - "box_id": "24", - "ornament_id": "8", - "audience": "robot", - "count": "556", - "used": "0" - }, - { - "id": "78586", - "box_id": "24", - "ornament_id": "9", - "audience": "robot", - "count": "356", - "used": "0" - }, - { - "id": "78587", - "box_id": "24", - "ornament_id": "10", - "audience": "robot", - "count": "228", - "used": "0" - }, - { - "id": "78588", - "box_id": "24", - "ornament_id": "11", - "audience": "robot", - "count": "146", - "used": "0" - }, - { - "id": "78589", - "box_id": "24", - "ornament_id": "12", - "audience": "robot", - "count": "94", - "used": "0" - }, - { - "id": "78590", - "box_id": "24", - "ornament_id": "13", - "audience": "robot", - "count": "60", - "used": "0" - }, - { - "id": "78591", - "box_id": "24", - "ornament_id": "14", - "audience": "robot", - "count": "39", - "used": "0" - }, - { - "id": "78592", - "box_id": "24", - "ornament_id": "15", - "audience": "robot", - "count": "26", - "used": "0" - }, - { - "id": "78593", - "box_id": "24", - "ornament_id": "16", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78594", - "box_id": "24", - "ornament_id": "17", - "audience": "robot", - "count": "11", - "used": "0" - }, - { - "id": "78595", - "box_id": "24", - "ornament_id": "7", - "audience": "white", - "count": "868", - "used": "0" - }, - { - "id": "78596", - "box_id": "24", - "ornament_id": "8", - "audience": "white", - "count": "556", - "used": "0" - }, - { - "id": "78597", - "box_id": "24", - "ornament_id": "9", - "audience": "white", - "count": "356", - "used": "0" - }, - { - "id": "78598", - "box_id": "24", - "ornament_id": "10", - "audience": "white", - "count": "228", - "used": "0" - }, - { - "id": "78599", - "box_id": "24", - "ornament_id": "11", - "audience": "white", - "count": "146", - "used": "0" - }, - { - "id": "78600", - "box_id": "24", - "ornament_id": "12", - "audience": "white", - "count": "94", - "used": "0" - }, - { - "id": "78601", - "box_id": "24", - "ornament_id": "13", - "audience": "white", - "count": "60", - "used": "0" - }, - { - "id": "78602", - "box_id": "24", - "ornament_id": "14", - "audience": "white", - "count": "39", - "used": "0" - }, - { - "id": "78603", - "box_id": "24", - "ornament_id": "15", - "audience": "white", - "count": "26", - "used": "0" - }, - { - "id": "78604", - "box_id": "24", - "ornament_id": "16", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78605", - "box_id": "24", - "ornament_id": "17", - "audience": "white", - "count": "11", - "used": "0" - }, - { - "id": "78606", - "box_id": "25", - "ornament_id": "7", - "audience": "common", - "count": "867", - "used": "0" - }, - { - "id": "78607", - "box_id": "25", - "ornament_id": "8", - "audience": "common", - "count": "556", - "used": "0" - }, - { - "id": "78608", - "box_id": "25", - "ornament_id": "9", - "audience": "common", - "count": "356", - "used": "0" - }, - { - "id": "78609", - "box_id": "25", - "ornament_id": "10", - "audience": "common", - "count": "228", - "used": "0" - }, - { - "id": "78610", - "box_id": "25", - "ornament_id": "11", - "audience": "common", - "count": "147", - "used": "0" - }, - { - "id": "78611", - "box_id": "25", - "ornament_id": "12", - "audience": "common", - "count": "94", - "used": "0" - }, - { - "id": "78612", - "box_id": "25", - "ornament_id": "13", - "audience": "common", - "count": "60", - "used": "0" - }, - { - "id": "78613", - "box_id": "25", - "ornament_id": "14", - "audience": "common", - "count": "40", - "used": "0" - }, - { - "id": "78614", - "box_id": "25", - "ornament_id": "15", - "audience": "common", - "count": "25", - "used": "0" - }, - { - "id": "78615", - "box_id": "25", - "ornament_id": "16", - "audience": "common", - "count": "17", - "used": "0" - }, - { - "id": "78616", - "box_id": "25", - "ornament_id": "17", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "78617", - "box_id": "25", - "ornament_id": "7", - "audience": "anchor", - "count": "867", - "used": "0" - }, - { - "id": "78618", - "box_id": "25", - "ornament_id": "8", - "audience": "anchor", - "count": "556", - "used": "0" - }, - { - "id": "78619", - "box_id": "25", - "ornament_id": "9", - "audience": "anchor", - "count": "356", - "used": "0" - }, - { - "id": "78620", - "box_id": "25", - "ornament_id": "10", - "audience": "anchor", - "count": "228", - "used": "0" - }, - { - "id": "78621", - "box_id": "25", - "ornament_id": "11", - "audience": "anchor", - "count": "147", - "used": "0" - }, - { - "id": "78622", - "box_id": "25", - "ornament_id": "12", - "audience": "anchor", - "count": "94", - "used": "0" - }, - { - "id": "78623", - "box_id": "25", - "ornament_id": "13", - "audience": "anchor", - "count": "60", - "used": "0" - }, - { - "id": "78624", - "box_id": "25", - "ornament_id": "14", - "audience": "anchor", - "count": "40", - "used": "0" - }, - { - "id": "78625", - "box_id": "25", - "ornament_id": "15", - "audience": "anchor", - "count": "25", - "used": "0" - }, - { - "id": "78626", - "box_id": "25", - "ornament_id": "16", - "audience": "anchor", - "count": "17", - "used": "0" - }, - { - "id": "78627", - "box_id": "25", - "ornament_id": "17", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "78628", - "box_id": "25", - "ornament_id": "7", - "audience": "robot", - "count": "867", - "used": "0" - }, - { - "id": "78629", - "box_id": "25", - "ornament_id": "8", - "audience": "robot", - "count": "556", - "used": "0" - }, - { - "id": "78630", - "box_id": "25", - "ornament_id": "9", - "audience": "robot", - "count": "356", - "used": "0" - }, - { - "id": "78631", - "box_id": "25", - "ornament_id": "10", - "audience": "robot", - "count": "228", - "used": "0" - }, - { - "id": "78632", - "box_id": "25", - "ornament_id": "11", - "audience": "robot", - "count": "147", - "used": "0" - }, - { - "id": "78633", - "box_id": "25", - "ornament_id": "12", - "audience": "robot", - "count": "94", - "used": "0" - }, - { - "id": "78634", - "box_id": "25", - "ornament_id": "13", - "audience": "robot", - "count": "60", - "used": "0" - }, - { - "id": "78635", - "box_id": "25", - "ornament_id": "14", - "audience": "robot", - "count": "40", - "used": "0" - }, - { - "id": "78636", - "box_id": "25", - "ornament_id": "15", - "audience": "robot", - "count": "25", - "used": "0" - }, - { - "id": "78637", - "box_id": "25", - "ornament_id": "16", - "audience": "robot", - "count": "17", - "used": "0" - }, - { - "id": "78638", - "box_id": "25", - "ornament_id": "17", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "78639", - "box_id": "25", - "ornament_id": "7", - "audience": "white", - "count": "867", - "used": "0" - }, - { - "id": "78640", - "box_id": "25", - "ornament_id": "8", - "audience": "white", - "count": "556", - "used": "0" - }, - { - "id": "78641", - "box_id": "25", - "ornament_id": "9", - "audience": "white", - "count": "356", - "used": "0" - }, - { - "id": "78642", - "box_id": "25", - "ornament_id": "10", - "audience": "white", - "count": "228", - "used": "0" - }, - { - "id": "78643", - "box_id": "25", - "ornament_id": "11", - "audience": "white", - "count": "147", - "used": "0" - }, - { - "id": "78644", - "box_id": "25", - "ornament_id": "12", - "audience": "white", - "count": "94", - "used": "0" - }, - { - "id": "78645", - "box_id": "25", - "ornament_id": "13", - "audience": "white", - "count": "60", - "used": "0" - }, - { - "id": "78646", - "box_id": "25", - "ornament_id": "14", - "audience": "white", - "count": "40", - "used": "0" - }, - { - "id": "78647", - "box_id": "25", - "ornament_id": "15", - "audience": "white", - "count": "25", - "used": "0" - }, - { - "id": "78648", - "box_id": "25", - "ornament_id": "16", - "audience": "white", - "count": "17", - "used": "0" - }, - { - "id": "78649", - "box_id": "25", - "ornament_id": "17", - "audience": "white", - "count": "10", - "used": "0" - }, - { - "id": "78650", - "box_id": "26", - "ornament_id": "7", - "audience": "common", - "count": "877", - "used": "0" - }, - { - "id": "78651", - "box_id": "26", - "ornament_id": "8", - "audience": "common", - "count": "558", - "used": "0" - }, - { - "id": "78652", - "box_id": "26", - "ornament_id": "9", - "audience": "common", - "count": "354", - "used": "0" - }, - { - "id": "78653", - "box_id": "26", - "ornament_id": "10", - "audience": "common", - "count": "226", - "used": "0" - }, - { - "id": "78654", - "box_id": "26", - "ornament_id": "11", - "audience": "common", - "count": "144", - "used": "0" - }, - { - "id": "78655", - "box_id": "26", - "ornament_id": "12", - "audience": "common", - "count": "92", - "used": "0" - }, - { - "id": "78656", - "box_id": "26", - "ornament_id": "13", - "audience": "common", - "count": "60", - "used": "0" - }, - { - "id": "78657", - "box_id": "26", - "ornament_id": "14", - "audience": "common", - "count": "38", - "used": "0" - }, - { - "id": "78658", - "box_id": "26", - "ornament_id": "15", - "audience": "common", - "count": "24", - "used": "0" - }, - { - "id": "78659", - "box_id": "26", - "ornament_id": "16", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "78660", - "box_id": "26", - "ornament_id": "17", - "audience": "common", - "count": "11", - "used": "0" - }, - { - "id": "78661", - "box_id": "26", - "ornament_id": "7", - "audience": "anchor", - "count": "877", - "used": "0" - }, - { - "id": "78662", - "box_id": "26", - "ornament_id": "8", - "audience": "anchor", - "count": "558", - "used": "0" - }, - { - "id": "78663", - "box_id": "26", - "ornament_id": "9", - "audience": "anchor", - "count": "354", - "used": "0" - }, - { - "id": "78664", - "box_id": "26", - "ornament_id": "10", - "audience": "anchor", - "count": "226", - "used": "0" - }, - { - "id": "78665", - "box_id": "26", - "ornament_id": "11", - "audience": "anchor", - "count": "144", - "used": "0" - }, - { - "id": "78666", - "box_id": "26", - "ornament_id": "12", - "audience": "anchor", - "count": "92", - "used": "0" - }, - { - "id": "78667", - "box_id": "26", - "ornament_id": "13", - "audience": "anchor", - "count": "60", - "used": "0" - }, - { - "id": "78668", - "box_id": "26", - "ornament_id": "14", - "audience": "anchor", - "count": "38", - "used": "0" - }, - { - "id": "78669", - "box_id": "26", - "ornament_id": "15", - "audience": "anchor", - "count": "24", - "used": "0" - }, - { - "id": "78670", - "box_id": "26", - "ornament_id": "16", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78671", - "box_id": "26", - "ornament_id": "17", - "audience": "anchor", - "count": "11", - "used": "0" - }, - { - "id": "78672", - "box_id": "26", - "ornament_id": "7", - "audience": "robot", - "count": "877", - "used": "0" - }, - { - "id": "78673", - "box_id": "26", - "ornament_id": "8", - "audience": "robot", - "count": "558", - "used": "0" - }, - { - "id": "78674", - "box_id": "26", - "ornament_id": "9", - "audience": "robot", - "count": "354", - "used": "0" - }, - { - "id": "78675", - "box_id": "26", - "ornament_id": "10", - "audience": "robot", - "count": "226", - "used": "0" - }, - { - "id": "78676", - "box_id": "26", - "ornament_id": "11", - "audience": "robot", - "count": "144", - "used": "0" - }, - { - "id": "78677", - "box_id": "26", - "ornament_id": "12", - "audience": "robot", - "count": "92", - "used": "0" - }, - { - "id": "78678", - "box_id": "26", - "ornament_id": "13", - "audience": "robot", - "count": "60", - "used": "0" - }, - { - "id": "78679", - "box_id": "26", - "ornament_id": "14", - "audience": "robot", - "count": "38", - "used": "0" - }, - { - "id": "78680", - "box_id": "26", - "ornament_id": "15", - "audience": "robot", - "count": "24", - "used": "0" - }, - { - "id": "78681", - "box_id": "26", - "ornament_id": "16", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78682", - "box_id": "26", - "ornament_id": "17", - "audience": "robot", - "count": "11", - "used": "0" - }, - { - "id": "78683", - "box_id": "26", - "ornament_id": "7", - "audience": "white", - "count": "877", - "used": "0" - }, - { - "id": "78684", - "box_id": "26", - "ornament_id": "8", - "audience": "white", - "count": "558", - "used": "0" - }, - { - "id": "78685", - "box_id": "26", - "ornament_id": "9", - "audience": "white", - "count": "354", - "used": "0" - }, - { - "id": "78686", - "box_id": "26", - "ornament_id": "10", - "audience": "white", - "count": "226", - "used": "0" - }, - { - "id": "78687", - "box_id": "26", - "ornament_id": "11", - "audience": "white", - "count": "144", - "used": "0" - }, - { - "id": "78688", - "box_id": "26", - "ornament_id": "12", - "audience": "white", - "count": "92", - "used": "0" - }, - { - "id": "78689", - "box_id": "26", - "ornament_id": "13", - "audience": "white", - "count": "60", - "used": "0" - }, - { - "id": "78690", - "box_id": "26", - "ornament_id": "14", - "audience": "white", - "count": "38", - "used": "0" - }, - { - "id": "78691", - "box_id": "26", - "ornament_id": "15", - "audience": "white", - "count": "24", - "used": "0" - }, - { - "id": "78692", - "box_id": "26", - "ornament_id": "16", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78693", - "box_id": "26", - "ornament_id": "17", - "audience": "white", - "count": "11", - "used": "0" - }, - { - "id": "78694", - "box_id": "27", - "ornament_id": "7", - "audience": "common", - "count": "869", - "used": "0" - }, - { - "id": "78695", - "box_id": "27", - "ornament_id": "8", - "audience": "common", - "count": "556", - "used": "0" - }, - { - "id": "78696", - "box_id": "27", - "ornament_id": "9", - "audience": "common", - "count": "357", - "used": "0" - }, - { - "id": "78697", - "box_id": "27", - "ornament_id": "10", - "audience": "common", - "count": "228", - "used": "0" - }, - { - "id": "78698", - "box_id": "27", - "ornament_id": "11", - "audience": "common", - "count": "146", - "used": "0" - }, - { - "id": "78699", - "box_id": "27", - "ornament_id": "12", - "audience": "common", - "count": "94", - "used": "0" - }, - { - "id": "78700", - "box_id": "27", - "ornament_id": "13", - "audience": "common", - "count": "59", - "used": "0" - }, - { - "id": "78701", - "box_id": "27", - "ornament_id": "14", - "audience": "common", - "count": "39", - "used": "0" - }, - { - "id": "78702", - "box_id": "27", - "ornament_id": "15", - "audience": "common", - "count": "25", - "used": "0" - }, - { - "id": "78703", - "box_id": "27", - "ornament_id": "16", - "audience": "common", - "count": "17", - "used": "0" - }, - { - "id": "78704", - "box_id": "27", - "ornament_id": "17", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "78705", - "box_id": "27", - "ornament_id": "7", - "audience": "anchor", - "count": "869", - "used": "0" - }, - { - "id": "78706", - "box_id": "27", - "ornament_id": "8", - "audience": "anchor", - "count": "556", - "used": "0" - }, - { - "id": "78707", - "box_id": "27", - "ornament_id": "9", - "audience": "anchor", - "count": "357", - "used": "0" - }, - { - "id": "78708", - "box_id": "27", - "ornament_id": "10", - "audience": "anchor", - "count": "228", - "used": "0" - }, - { - "id": "78709", - "box_id": "27", - "ornament_id": "11", - "audience": "anchor", - "count": "146", - "used": "0" - }, - { - "id": "78710", - "box_id": "27", - "ornament_id": "12", - "audience": "anchor", - "count": "94", - "used": "0" - }, - { - "id": "78711", - "box_id": "27", - "ornament_id": "13", - "audience": "anchor", - "count": "59", - "used": "0" - }, - { - "id": "78712", - "box_id": "27", - "ornament_id": "14", - "audience": "anchor", - "count": "39", - "used": "0" - }, - { - "id": "78713", - "box_id": "27", - "ornament_id": "15", - "audience": "anchor", - "count": "25", - "used": "0" - }, - { - "id": "78714", - "box_id": "27", - "ornament_id": "16", - "audience": "anchor", - "count": "17", - "used": "0" - }, - { - "id": "78715", - "box_id": "27", - "ornament_id": "17", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "78716", - "box_id": "27", - "ornament_id": "7", - "audience": "robot", - "count": "869", - "used": "0" - }, - { - "id": "78717", - "box_id": "27", - "ornament_id": "8", - "audience": "robot", - "count": "556", - "used": "0" - }, - { - "id": "78718", - "box_id": "27", - "ornament_id": "9", - "audience": "robot", - "count": "357", - "used": "0" - }, - { - "id": "78719", - "box_id": "27", - "ornament_id": "10", - "audience": "robot", - "count": "228", - "used": "0" - }, - { - "id": "78720", - "box_id": "27", - "ornament_id": "11", - "audience": "robot", - "count": "146", - "used": "0" - }, - { - "id": "78721", - "box_id": "27", - "ornament_id": "12", - "audience": "robot", - "count": "94", - "used": "0" - }, - { - "id": "78722", - "box_id": "27", - "ornament_id": "13", - "audience": "robot", - "count": "59", - "used": "0" - }, - { - "id": "78723", - "box_id": "27", - "ornament_id": "14", - "audience": "robot", - "count": "39", - "used": "0" - }, - { - "id": "78724", - "box_id": "27", - "ornament_id": "15", - "audience": "robot", - "count": "25", - "used": "0" - }, - { - "id": "78725", - "box_id": "27", - "ornament_id": "16", - "audience": "robot", - "count": "17", - "used": "0" - }, - { - "id": "78726", - "box_id": "27", - "ornament_id": "17", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "78727", - "box_id": "27", - "ornament_id": "7", - "audience": "white", - "count": "869", - "used": "0" - }, - { - "id": "78728", - "box_id": "27", - "ornament_id": "8", - "audience": "white", - "count": "556", - "used": "0" - }, - { - "id": "78729", - "box_id": "27", - "ornament_id": "9", - "audience": "white", - "count": "357", - "used": "0" - }, - { - "id": "78730", - "box_id": "27", - "ornament_id": "10", - "audience": "white", - "count": "228", - "used": "0" - }, - { - "id": "78731", - "box_id": "27", - "ornament_id": "11", - "audience": "white", - "count": "146", - "used": "0" - }, - { - "id": "78732", - "box_id": "27", - "ornament_id": "12", - "audience": "white", - "count": "94", - "used": "0" - }, - { - "id": "78733", - "box_id": "27", - "ornament_id": "13", - "audience": "white", - "count": "59", - "used": "0" - }, - { - "id": "78734", - "box_id": "27", - "ornament_id": "14", - "audience": "white", - "count": "39", - "used": "0" - }, - { - "id": "78735", - "box_id": "27", - "ornament_id": "15", - "audience": "white", - "count": "25", - "used": "0" - }, - { - "id": "78736", - "box_id": "27", - "ornament_id": "16", - "audience": "white", - "count": "17", - "used": "0" - }, - { - "id": "78737", - "box_id": "27", - "ornament_id": "17", - "audience": "white", - "count": "10", - "used": "0" - }, - { - "id": "78738", - "box_id": "28", - "ornament_id": "7", - "audience": "common", - "count": "879", - "used": "0" - }, - { - "id": "78739", - "box_id": "28", - "ornament_id": "8", - "audience": "common", - "count": "557", - "used": "0" - }, - { - "id": "78740", - "box_id": "28", - "ornament_id": "9", - "audience": "common", - "count": "355", - "used": "0" - }, - { - "id": "78741", - "box_id": "28", - "ornament_id": "10", - "audience": "common", - "count": "227", - "used": "0" - }, - { - "id": "78742", - "box_id": "28", - "ornament_id": "11", - "audience": "common", - "count": "144", - "used": "0" - }, - { - "id": "78743", - "box_id": "28", - "ornament_id": "12", - "audience": "common", - "count": "92", - "used": "0" - }, - { - "id": "78744", - "box_id": "28", - "ornament_id": "13", - "audience": "common", - "count": "58", - "used": "0" - }, - { - "id": "78745", - "box_id": "28", - "ornament_id": "14", - "audience": "common", - "count": "38", - "used": "0" - }, - { - "id": "78746", - "box_id": "28", - "ornament_id": "15", - "audience": "common", - "count": "24", - "used": "0" - }, - { - "id": "78747", - "box_id": "28", - "ornament_id": "16", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "78748", - "box_id": "28", - "ornament_id": "17", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "78749", - "box_id": "28", - "ornament_id": "7", - "audience": "anchor", - "count": "879", - "used": "0" - }, - { - "id": "78750", - "box_id": "28", - "ornament_id": "8", - "audience": "anchor", - "count": "557", - "used": "0" - }, - { - "id": "78751", - "box_id": "28", - "ornament_id": "9", - "audience": "anchor", - "count": "355", - "used": "0" - }, - { - "id": "78752", - "box_id": "28", - "ornament_id": "10", - "audience": "anchor", - "count": "227", - "used": "0" - }, - { - "id": "78753", - "box_id": "28", - "ornament_id": "11", - "audience": "anchor", - "count": "144", - "used": "0" - }, - { - "id": "78754", - "box_id": "28", - "ornament_id": "12", - "audience": "anchor", - "count": "92", - "used": "0" - }, - { - "id": "78755", - "box_id": "28", - "ornament_id": "13", - "audience": "anchor", - "count": "58", - "used": "0" - }, - { - "id": "78756", - "box_id": "28", - "ornament_id": "14", - "audience": "anchor", - "count": "38", - "used": "0" - }, - { - "id": "78757", - "box_id": "28", - "ornament_id": "15", - "audience": "anchor", - "count": "24", - "used": "0" - }, - { - "id": "78758", - "box_id": "28", - "ornament_id": "16", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78759", - "box_id": "28", - "ornament_id": "17", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "78760", - "box_id": "28", - "ornament_id": "7", - "audience": "robot", - "count": "879", - "used": "0" - }, - { - "id": "78761", - "box_id": "28", - "ornament_id": "8", - "audience": "robot", - "count": "557", - "used": "0" - }, - { - "id": "78762", - "box_id": "28", - "ornament_id": "9", - "audience": "robot", - "count": "355", - "used": "0" - }, - { - "id": "78763", - "box_id": "28", - "ornament_id": "10", - "audience": "robot", - "count": "227", - "used": "0" - }, - { - "id": "78764", - "box_id": "28", - "ornament_id": "11", - "audience": "robot", - "count": "144", - "used": "0" - }, - { - "id": "78765", - "box_id": "28", - "ornament_id": "12", - "audience": "robot", - "count": "92", - "used": "0" - }, - { - "id": "78766", - "box_id": "28", - "ornament_id": "13", - "audience": "robot", - "count": "58", - "used": "0" - }, - { - "id": "78767", - "box_id": "28", - "ornament_id": "14", - "audience": "robot", - "count": "38", - "used": "0" - }, - { - "id": "78768", - "box_id": "28", - "ornament_id": "15", - "audience": "robot", - "count": "24", - "used": "0" - }, - { - "id": "78769", - "box_id": "28", - "ornament_id": "16", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78770", - "box_id": "28", - "ornament_id": "17", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "78771", - "box_id": "28", - "ornament_id": "7", - "audience": "white", - "count": "879", - "used": "0" - }, - { - "id": "78772", - "box_id": "28", - "ornament_id": "8", - "audience": "white", - "count": "557", - "used": "0" - }, - { - "id": "78773", - "box_id": "28", - "ornament_id": "9", - "audience": "white", - "count": "355", - "used": "0" - }, - { - "id": "78774", - "box_id": "28", - "ornament_id": "10", - "audience": "white", - "count": "227", - "used": "0" - }, - { - "id": "78775", - "box_id": "28", - "ornament_id": "11", - "audience": "white", - "count": "144", - "used": "0" - }, - { - "id": "78776", - "box_id": "28", - "ornament_id": "12", - "audience": "white", - "count": "92", - "used": "0" - }, - { - "id": "78777", - "box_id": "28", - "ornament_id": "13", - "audience": "white", - "count": "58", - "used": "0" - }, - { - "id": "78778", - "box_id": "28", - "ornament_id": "14", - "audience": "white", - "count": "38", - "used": "0" - }, - { - "id": "78779", - "box_id": "28", - "ornament_id": "15", - "audience": "white", - "count": "24", - "used": "0" - }, - { - "id": "78780", - "box_id": "28", - "ornament_id": "16", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78781", - "box_id": "28", - "ornament_id": "17", - "audience": "white", - "count": "10", - "used": "0" - }, - { - "id": "78782", - "box_id": "29", - "ornament_id": "7", - "audience": "common", - "count": "878", - "used": "0" - }, - { - "id": "78783", - "box_id": "29", - "ornament_id": "8", - "audience": "common", - "count": "559", - "used": "0" - }, - { - "id": "78784", - "box_id": "29", - "ornament_id": "9", - "audience": "common", - "count": "355", - "used": "0" - }, - { - "id": "78785", - "box_id": "29", - "ornament_id": "10", - "audience": "common", - "count": "226", - "used": "0" - }, - { - "id": "78786", - "box_id": "29", - "ornament_id": "11", - "audience": "common", - "count": "144", - "used": "0" - }, - { - "id": "78787", - "box_id": "29", - "ornament_id": "12", - "audience": "common", - "count": "91", - "used": "0" - }, - { - "id": "78788", - "box_id": "29", - "ornament_id": "13", - "audience": "common", - "count": "59", - "used": "0" - }, - { - "id": "78789", - "box_id": "29", - "ornament_id": "14", - "audience": "common", - "count": "38", - "used": "0" - }, - { - "id": "78790", - "box_id": "29", - "ornament_id": "15", - "audience": "common", - "count": "24", - "used": "0" - }, - { - "id": "78791", - "box_id": "29", - "ornament_id": "16", - "audience": "common", - "count": "16", - "used": "0" - }, - { - "id": "78792", - "box_id": "29", - "ornament_id": "17", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "78793", - "box_id": "29", - "ornament_id": "7", - "audience": "anchor", - "count": "878", - "used": "0" - }, - { - "id": "78794", - "box_id": "29", - "ornament_id": "8", - "audience": "anchor", - "count": "559", - "used": "0" - }, - { - "id": "78795", - "box_id": "29", - "ornament_id": "9", - "audience": "anchor", - "count": "355", - "used": "0" - }, - { - "id": "78796", - "box_id": "29", - "ornament_id": "10", - "audience": "anchor", - "count": "226", - "used": "0" - }, - { - "id": "78797", - "box_id": "29", - "ornament_id": "11", - "audience": "anchor", - "count": "144", - "used": "0" - }, - { - "id": "78798", - "box_id": "29", - "ornament_id": "12", - "audience": "anchor", - "count": "91", - "used": "0" - }, - { - "id": "78799", - "box_id": "29", - "ornament_id": "13", - "audience": "anchor", - "count": "59", - "used": "0" - }, - { - "id": "78800", - "box_id": "29", - "ornament_id": "14", - "audience": "anchor", - "count": "38", - "used": "0" - }, - { - "id": "78801", - "box_id": "29", - "ornament_id": "15", - "audience": "anchor", - "count": "24", - "used": "0" - }, - { - "id": "78802", - "box_id": "29", - "ornament_id": "16", - "audience": "anchor", - "count": "16", - "used": "0" - }, - { - "id": "78803", - "box_id": "29", - "ornament_id": "17", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "78804", - "box_id": "29", - "ornament_id": "7", - "audience": "robot", - "count": "878", - "used": "0" - }, - { - "id": "78805", - "box_id": "29", - "ornament_id": "8", - "audience": "robot", - "count": "559", - "used": "0" - }, - { - "id": "78806", - "box_id": "29", - "ornament_id": "9", - "audience": "robot", - "count": "355", - "used": "0" - }, - { - "id": "78807", - "box_id": "29", - "ornament_id": "10", - "audience": "robot", - "count": "226", - "used": "0" - }, - { - "id": "78808", - "box_id": "29", - "ornament_id": "11", - "audience": "robot", - "count": "144", - "used": "0" - }, - { - "id": "78809", - "box_id": "29", - "ornament_id": "12", - "audience": "robot", - "count": "91", - "used": "0" - }, - { - "id": "78810", - "box_id": "29", - "ornament_id": "13", - "audience": "robot", - "count": "59", - "used": "0" - }, - { - "id": "78811", - "box_id": "29", - "ornament_id": "14", - "audience": "robot", - "count": "38", - "used": "0" - }, - { - "id": "78812", - "box_id": "29", - "ornament_id": "15", - "audience": "robot", - "count": "24", - "used": "0" - }, - { - "id": "78813", - "box_id": "29", - "ornament_id": "16", - "audience": "robot", - "count": "16", - "used": "0" - }, - { - "id": "78814", - "box_id": "29", - "ornament_id": "17", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "78815", - "box_id": "29", - "ornament_id": "7", - "audience": "white", - "count": "878", - "used": "0" - }, - { - "id": "78816", - "box_id": "29", - "ornament_id": "8", - "audience": "white", - "count": "559", - "used": "0" - }, - { - "id": "78817", - "box_id": "29", - "ornament_id": "9", - "audience": "white", - "count": "355", - "used": "0" - }, - { - "id": "78818", - "box_id": "29", - "ornament_id": "10", - "audience": "white", - "count": "226", - "used": "0" - }, - { - "id": "78819", - "box_id": "29", - "ornament_id": "11", - "audience": "white", - "count": "144", - "used": "0" - }, - { - "id": "78820", - "box_id": "29", - "ornament_id": "12", - "audience": "white", - "count": "91", - "used": "0" - }, - { - "id": "78821", - "box_id": "29", - "ornament_id": "13", - "audience": "white", - "count": "59", - "used": "0" - }, - { - "id": "78822", - "box_id": "29", - "ornament_id": "14", - "audience": "white", - "count": "38", - "used": "0" - }, - { - "id": "78823", - "box_id": "29", - "ornament_id": "15", - "audience": "white", - "count": "24", - "used": "0" - }, - { - "id": "78824", - "box_id": "29", - "ornament_id": "16", - "audience": "white", - "count": "16", - "used": "0" - }, - { - "id": "78825", - "box_id": "29", - "ornament_id": "17", - "audience": "white", - "count": "10", - "used": "0" - }, - { - "id": "78826", - "box_id": "30", - "ornament_id": "7", - "audience": "common", - "count": "869", - "used": "0" - }, - { - "id": "78827", - "box_id": "30", - "ornament_id": "8", - "audience": "common", - "count": "556", - "used": "0" - }, - { - "id": "78828", - "box_id": "30", - "ornament_id": "9", - "audience": "common", - "count": "356", - "used": "0" - }, - { - "id": "78829", - "box_id": "30", - "ornament_id": "10", - "audience": "common", - "count": "229", - "used": "0" - }, - { - "id": "78830", - "box_id": "30", - "ornament_id": "11", - "audience": "common", - "count": "146", - "used": "0" - }, - { - "id": "78831", - "box_id": "30", - "ornament_id": "12", - "audience": "common", - "count": "94", - "used": "0" - }, - { - "id": "78832", - "box_id": "30", - "ornament_id": "13", - "audience": "common", - "count": "60", - "used": "0" - }, - { - "id": "78833", - "box_id": "30", - "ornament_id": "14", - "audience": "common", - "count": "38", - "used": "0" - }, - { - "id": "78834", - "box_id": "30", - "ornament_id": "15", - "audience": "common", - "count": "25", - "used": "0" - }, - { - "id": "78835", - "box_id": "30", - "ornament_id": "16", - "audience": "common", - "count": "17", - "used": "0" - }, - { - "id": "78836", - "box_id": "30", - "ornament_id": "17", - "audience": "common", - "count": "10", - "used": "0" - }, - { - "id": "78837", - "box_id": "30", - "ornament_id": "7", - "audience": "anchor", - "count": "869", - "used": "0" - }, - { - "id": "78838", - "box_id": "30", - "ornament_id": "8", - "audience": "anchor", - "count": "556", - "used": "0" - }, - { - "id": "78839", - "box_id": "30", - "ornament_id": "9", - "audience": "anchor", - "count": "356", - "used": "0" - }, - { - "id": "78840", - "box_id": "30", - "ornament_id": "10", - "audience": "anchor", - "count": "229", - "used": "0" - }, - { - "id": "78841", - "box_id": "30", - "ornament_id": "11", - "audience": "anchor", - "count": "146", - "used": "0" - }, - { - "id": "78842", - "box_id": "30", - "ornament_id": "12", - "audience": "anchor", - "count": "94", - "used": "0" - }, - { - "id": "78843", - "box_id": "30", - "ornament_id": "13", - "audience": "anchor", - "count": "60", - "used": "0" - }, - { - "id": "78844", - "box_id": "30", - "ornament_id": "14", - "audience": "anchor", - "count": "38", - "used": "0" - }, - { - "id": "78845", - "box_id": "30", - "ornament_id": "15", - "audience": "anchor", - "count": "25", - "used": "0" - }, - { - "id": "78846", - "box_id": "30", - "ornament_id": "16", - "audience": "anchor", - "count": "17", - "used": "0" - }, - { - "id": "78847", - "box_id": "30", - "ornament_id": "17", - "audience": "anchor", - "count": "10", - "used": "0" - }, - { - "id": "78848", - "box_id": "30", - "ornament_id": "7", - "audience": "robot", - "count": "869", - "used": "0" - }, - { - "id": "78849", - "box_id": "30", - "ornament_id": "8", - "audience": "robot", - "count": "556", - "used": "0" - }, - { - "id": "78850", - "box_id": "30", - "ornament_id": "9", - "audience": "robot", - "count": "356", - "used": "0" - }, - { - "id": "78851", - "box_id": "30", - "ornament_id": "10", - "audience": "robot", - "count": "229", - "used": "0" - }, - { - "id": "78852", - "box_id": "30", - "ornament_id": "11", - "audience": "robot", - "count": "146", - "used": "0" - }, - { - "id": "78853", - "box_id": "30", - "ornament_id": "12", - "audience": "robot", - "count": "94", - "used": "0" - }, - { - "id": "78854", - "box_id": "30", - "ornament_id": "13", - "audience": "robot", - "count": "60", - "used": "0" - }, - { - "id": "78855", - "box_id": "30", - "ornament_id": "14", - "audience": "robot", - "count": "38", - "used": "0" - }, - { - "id": "78856", - "box_id": "30", - "ornament_id": "15", - "audience": "robot", - "count": "25", - "used": "0" - }, - { - "id": "78857", - "box_id": "30", - "ornament_id": "16", - "audience": "robot", - "count": "17", - "used": "0" - }, - { - "id": "78858", - "box_id": "30", - "ornament_id": "17", - "audience": "robot", - "count": "10", - "used": "0" - }, - { - "id": "78859", - "box_id": "30", - "ornament_id": "7", - "audience": "white", - "count": "869", - "used": "0" - }, - { - "id": "78860", - "box_id": "30", - "ornament_id": "8", - "audience": "white", - "count": "556", - "used": "0" - }, - { - "id": "78861", - "box_id": "30", - "ornament_id": "9", - "audience": "white", - "count": "356", - "used": "0" - }, - { - "id": "78862", - "box_id": "30", - "ornament_id": "10", - "audience": "white", - "count": "229", - "used": "0" - }, - { - "id": "78863", - "box_id": "30", - "ornament_id": "11", - "audience": "white", - "count": "146", - "used": "0" - }, - { - "id": "78864", - "box_id": "30", - "ornament_id": "12", - "audience": "white", - "count": "94", - "used": "0" - }, - { - "id": "78865", - "box_id": "30", - "ornament_id": "13", - "audience": "white", - "count": "60", - "used": "0" - }, - { - "id": "78866", - "box_id": "30", - "ornament_id": "14", - "audience": "white", - "count": "38", - "used": "0" - }, - { - "id": "78867", - "box_id": "30", - "ornament_id": "15", - "audience": "white", - "count": "25", - "used": "0" - }, - { - "id": "78868", - "box_id": "30", - "ornament_id": "16", - "audience": "white", - "count": "17", - "used": "0" - }, - { - "id": "78869", - "box_id": "30", - "ornament_id": "17", - "audience": "white", - "count": "10", - "used": "0" - } - ], - "game_olympus_box_template": [ - { - "id": "2", - "box_id": "1", - "audience": "common", - "name": "ces", - "total_rounds": "900", - "items_json": "[{\"ornamentId\":1,\"count\":592},{\"ornamentId\":2,\"count\":198},{\"ornamentId\":3,\"count\":69},{\"ornamentId\":4,\"count\":22},{\"ornamentId\":5,\"count\":8},{\"ornamentId\":6,\"count\":3},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "0", - "create_time": "2026-08-20 10:11:33" - }, - { - "id": "3", - "box_id": "1", - "audience": "common", - "name": "默认配比", - "total_rounds": "900", - "items_json": "[{\"ornamentId\":1,\"count\":596},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":65},{\"ornamentId\":4,\"count\":22},{\"ornamentId\":5,\"count\":8},{\"ornamentId\":6,\"count\":4},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "4", - "box_id": "1", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "900", - "items_json": "[{\"ornamentId\":1,\"count\":596},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":65},{\"ornamentId\":4,\"count\":22},{\"ornamentId\":5,\"count\":8},{\"ornamentId\":6,\"count\":4},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "5", - "box_id": "1", - "audience": "robot", - "name": "默认配比", - "total_rounds": "900", - "items_json": "[{\"ornamentId\":1,\"count\":596},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":65},{\"ornamentId\":4,\"count\":22},{\"ornamentId\":5,\"count\":8},{\"ornamentId\":6,\"count\":4},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "6", - "box_id": "1", - "audience": "white", - "name": "默认配比", - "total_rounds": "900", - "items_json": "[{\"ornamentId\":1,\"count\":596},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":65},{\"ornamentId\":4,\"count\":22},{\"ornamentId\":5,\"count\":8},{\"ornamentId\":6,\"count\":4},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "7", - "box_id": "2", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":552},{\"ornamentId\":2,\"count\":166},{\"ornamentId\":3,\"count\":51},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "8", - "box_id": "2", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":552},{\"ornamentId\":2,\"count\":166},{\"ornamentId\":3,\"count\":51},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "9", - "box_id": "2", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":552},{\"ornamentId\":2,\"count\":166},{\"ornamentId\":3,\"count\":51},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "10", - "box_id": "2", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":552},{\"ornamentId\":2,\"count\":166},{\"ornamentId\":3,\"count\":51},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "11", - "box_id": "3", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":552},{\"ornamentId\":2,\"count\":166},{\"ornamentId\":3,\"count\":51},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "12", - "box_id": "3", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":552},{\"ornamentId\":2,\"count\":166},{\"ornamentId\":3,\"count\":51},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "13", - "box_id": "3", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":552},{\"ornamentId\":2,\"count\":166},{\"ornamentId\":3,\"count\":51},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "14", - "box_id": "3", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":552},{\"ornamentId\":2,\"count\":166},{\"ornamentId\":3,\"count\":51},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "15", - "box_id": "4", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":556},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":49},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "16", - "box_id": "4", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":556},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":49},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "17", - "box_id": "4", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":556},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":49},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "18", - "box_id": "4", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":556},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":49},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "19", - "box_id": "5", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":557},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":48},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "20", - "box_id": "5", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":557},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":48},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "21", - "box_id": "5", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":557},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":48},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "22", - "box_id": "5", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":557},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":48},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "23", - "box_id": "6", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":556},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":48},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "24", - "box_id": "6", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":556},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":48},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "25", - "box_id": "6", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":556},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":48},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "26", - "box_id": "6", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":556},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":48},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "27", - "box_id": "7", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":550},{\"ornamentId\":2,\"count\":167},{\"ornamentId\":3,\"count\":51},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":6},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "28", - "box_id": "7", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":550},{\"ornamentId\":2,\"count\":167},{\"ornamentId\":3,\"count\":51},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":6},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "29", - "box_id": "7", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":550},{\"ornamentId\":2,\"count\":167},{\"ornamentId\":3,\"count\":51},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":6},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "30", - "box_id": "7", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":550},{\"ornamentId\":2,\"count\":167},{\"ornamentId\":3,\"count\":51},{\"ornamentId\":4,\"count\":16},{\"ornamentId\":5,\"count\":6},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "31", - "box_id": "8", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":557},{\"ornamentId\":2,\"count\":164},{\"ornamentId\":3,\"count\":49},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "32", - "box_id": "8", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":557},{\"ornamentId\":2,\"count\":164},{\"ornamentId\":3,\"count\":49},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "33", - "box_id": "8", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":557},{\"ornamentId\":2,\"count\":164},{\"ornamentId\":3,\"count\":49},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "34", - "box_id": "8", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":557},{\"ornamentId\":2,\"count\":164},{\"ornamentId\":3,\"count\":49},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "35", - "box_id": "9", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":555},{\"ornamentId\":2,\"count\":166},{\"ornamentId\":3,\"count\":49},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "36", - "box_id": "9", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":555},{\"ornamentId\":2,\"count\":166},{\"ornamentId\":3,\"count\":49},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "37", - "box_id": "9", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":555},{\"ornamentId\":2,\"count\":166},{\"ornamentId\":3,\"count\":49},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "38", - "box_id": "9", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":555},{\"ornamentId\":2,\"count\":166},{\"ornamentId\":3,\"count\":49},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "39", - "box_id": "10", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":555},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":50},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "40", - "box_id": "10", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":555},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":50},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "41", - "box_id": "10", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":555},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":50},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "42", - "box_id": "10", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":555},{\"ornamentId\":2,\"count\":165},{\"ornamentId\":3,\"count\":50},{\"ornamentId\":4,\"count\":15},{\"ornamentId\":5,\"count\":5},{\"ornamentId\":6,\"count\":2},{\"ornamentId\":7,\"count\":2},{\"ornamentId\":8,\"count\":1},{\"ornamentId\":9,\"count\":1},{\"ornamentId\":10,\"count\":1},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "43", - "box_id": "11", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":409},{\"ornamentId\":2,\"count\":198},{\"ornamentId\":3,\"count\":95},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":7},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "44", - "box_id": "11", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":409},{\"ornamentId\":2,\"count\":198},{\"ornamentId\":3,\"count\":95},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":7},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "45", - "box_id": "11", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":409},{\"ornamentId\":2,\"count\":198},{\"ornamentId\":3,\"count\":95},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":7},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "46", - "box_id": "11", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":409},{\"ornamentId\":2,\"count\":198},{\"ornamentId\":3,\"count\":95},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":7},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "47", - "box_id": "12", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":410},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":95},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":23},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "48", - "box_id": "12", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":410},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":95},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":23},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "49", - "box_id": "12", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":410},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":95},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":23},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "50", - "box_id": "12", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":410},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":95},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":23},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "51", - "box_id": "13", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":412},{\"ornamentId\":2,\"count\":198},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":21},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":7},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "52", - "box_id": "13", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":412},{\"ornamentId\":2,\"count\":198},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":21},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":7},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "53", - "box_id": "13", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":412},{\"ornamentId\":2,\"count\":198},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":21},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":7},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "54", - "box_id": "13", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":412},{\"ornamentId\":2,\"count\":198},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":21},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":7},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "55", - "box_id": "14", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":415},{\"ornamentId\":2,\"count\":196},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":44},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "56", - "box_id": "14", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":415},{\"ornamentId\":2,\"count\":196},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":44},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "57", - "box_id": "14", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":415},{\"ornamentId\":2,\"count\":196},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":44},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "58", - "box_id": "14", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":415},{\"ornamentId\":2,\"count\":196},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":44},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "59", - "box_id": "15", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":10},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":4},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "60", - "box_id": "15", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":10},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":4},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "61", - "box_id": "15", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":10},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":4},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "62", - "box_id": "15", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":10},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":4},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "63", - "box_id": "16", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":196},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":23},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "64", - "box_id": "16", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":196},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":23},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "65", - "box_id": "16", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":196},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":23},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "66", - "box_id": "16", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":196},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":23},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "67", - "box_id": "17", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":414},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":93},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "68", - "box_id": "17", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":414},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":93},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "69", - "box_id": "17", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":414},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":93},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "70", - "box_id": "17", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":414},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":93},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "71", - "box_id": "18", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "72", - "box_id": "18", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "73", - "box_id": "18", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "74", - "box_id": "18", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":94},{\"ornamentId\":4,\"count\":45},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "75", - "box_id": "19", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":93},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "76", - "box_id": "19", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":93},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "77", - "box_id": "19", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":93},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "78", - "box_id": "19", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":413},{\"ornamentId\":2,\"count\":197},{\"ornamentId\":3,\"count\":93},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":22},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "79", - "box_id": "20", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":411},{\"ornamentId\":2,\"count\":196},{\"ornamentId\":3,\"count\":95},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":23},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "80", - "box_id": "20", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":411},{\"ornamentId\":2,\"count\":196},{\"ornamentId\":3,\"count\":95},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":23},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "81", - "box_id": "20", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":411},{\"ornamentId\":2,\"count\":196},{\"ornamentId\":3,\"count\":95},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":23},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "82", - "box_id": "20", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":1,\"count\":411},{\"ornamentId\":2,\"count\":196},{\"ornamentId\":3,\"count\":95},{\"ornamentId\":4,\"count\":46},{\"ornamentId\":5,\"count\":23},{\"ornamentId\":6,\"count\":11},{\"ornamentId\":7,\"count\":6},{\"ornamentId\":8,\"count\":3},{\"ornamentId\":9,\"count\":3},{\"ornamentId\":10,\"count\":2},{\"ornamentId\":11,\"count\":1},{\"ornamentId\":12,\"count\":1},{\"ornamentId\":13,\"count\":1},{\"ornamentId\":14,\"count\":1}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "83", - "box_id": "21", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":293},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":49},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":7},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "84", - "box_id": "21", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":293},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":49},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":7},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "85", - "box_id": "21", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":293},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":49},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":7},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "86", - "box_id": "21", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":293},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":49},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":7},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "87", - "box_id": "22", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":297},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":19},{\"ornamentId\":14,\"count\":11},{\"ornamentId\":15,\"count\":9},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "88", - "box_id": "22", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":297},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":19},{\"ornamentId\":14,\"count\":11},{\"ornamentId\":15,\"count\":9},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "89", - "box_id": "22", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":297},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":19},{\"ornamentId\":14,\"count\":11},{\"ornamentId\":15,\"count\":9},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "90", - "box_id": "22", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":297},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":19},{\"ornamentId\":14,\"count\":11},{\"ornamentId\":15,\"count\":9},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "91", - "box_id": "23", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":18},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "92", - "box_id": "23", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":18},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "93", - "box_id": "23", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":18},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "94", - "box_id": "23", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":18},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "95", - "box_id": "24", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":293},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":119},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "96", - "box_id": "24", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":293},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":119},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "97", - "box_id": "24", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":293},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":119},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "98", - "box_id": "24", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":293},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":119},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "99", - "box_id": "25", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":291},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":118},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":7},{\"ornamentId\":17,\"count\":3}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "100", - "box_id": "25", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":291},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":118},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":7},{\"ornamentId\":17,\"count\":3}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "101", - "box_id": "25", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":291},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":118},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":7},{\"ornamentId\":17,\"count\":3}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "102", - "box_id": "25", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":291},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":118},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":7},{\"ornamentId\":17,\"count\":3}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "103", - "box_id": "26", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":116},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":18},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "104", - "box_id": "26", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":116},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":18},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "105", - "box_id": "26", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":116},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":18},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "106", - "box_id": "26", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":116},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":18},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":6},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "107", - "box_id": "27", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":293},{\"ornamentId\":8,\"count\":185},{\"ornamentId\":9,\"count\":119},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "108", - "box_id": "27", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":293},{\"ornamentId\":8,\"count\":185},{\"ornamentId\":9,\"count\":119},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "109", - "box_id": "27", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":293},{\"ornamentId\":8,\"count\":185},{\"ornamentId\":9,\"count\":119},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "110", - "box_id": "27", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":293},{\"ornamentId\":8,\"count\":185},{\"ornamentId\":9,\"count\":119},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "111", - "box_id": "28", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":29},{\"ornamentId\":13,\"count\":19},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "112", - "box_id": "28", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":29},{\"ornamentId\":13,\"count\":19},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "113", - "box_id": "28", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":29},{\"ornamentId\":13,\"count\":19},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "114", - "box_id": "28", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":187},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":29},{\"ornamentId\":13,\"count\":19},{\"ornamentId\":14,\"count\":12},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "115", - "box_id": "29", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":11},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "116", - "box_id": "29", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":11},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "117", - "box_id": "29", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":11},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "118", - "box_id": "29", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":298},{\"ornamentId\":8,\"count\":186},{\"ornamentId\":9,\"count\":117},{\"ornamentId\":10,\"count\":74},{\"ornamentId\":11,\"count\":47},{\"ornamentId\":12,\"count\":30},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":11},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":5},{\"ornamentId\":17,\"count\":4}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "119", - "box_id": "30", - "audience": "common", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":294},{\"ornamentId\":8,\"count\":185},{\"ornamentId\":9,\"count\":118},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":29},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":7},{\"ornamentId\":17,\"count\":3}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "120", - "box_id": "30", - "audience": "anchor", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":294},{\"ornamentId\":8,\"count\":185},{\"ornamentId\":9,\"count\":118},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":29},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":7},{\"ornamentId\":17,\"count\":3}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "121", - "box_id": "30", - "audience": "robot", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":294},{\"ornamentId\":8,\"count\":185},{\"ornamentId\":9,\"count\":118},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":29},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":7},{\"ornamentId\":17,\"count\":3}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - }, - { - "id": "122", - "box_id": "30", - "audience": "white", - "name": "默认配比", - "total_rounds": "800", - "items_json": "[{\"ornamentId\":7,\"count\":294},{\"ornamentId\":8,\"count\":185},{\"ornamentId\":9,\"count\":118},{\"ornamentId\":10,\"count\":75},{\"ornamentId\":11,\"count\":48},{\"ornamentId\":12,\"count\":29},{\"ornamentId\":13,\"count\":20},{\"ornamentId\":14,\"count\":13},{\"ornamentId\":15,\"count\":8},{\"ornamentId\":16,\"count\":7},{\"ornamentId\":17,\"count\":3}]", - "is_active": "1", - "create_time": "2026-09-17 20:08:07" - } - ] -} \ No newline at end of file diff --git a/olympus-py/_create_reward_log.py b/olympus-py/_create_reward_log.py deleted file mode 100644 index bbdd9ee..0000000 --- a/olympus-py/_create_reward_log.py +++ /dev/null @@ -1,27 +0,0 @@ -"""建奥林匹斯奖池调整日志表 game_olympus_reward_log(幂等,IF NOT EXISTS)。 -用法: python _create_reward_log.py -""" -import olympus_db as db - -DDL = """ -CREATE TABLE IF NOT EXISTS game_olympus_reward_log ( - id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'PK', - box_id BIGINT DEFAULT NULL COMMENT '箱子ID(批量/重置为汇总,可空)', - box_name VARCHAR(100) DEFAULT NULL COMMENT '箱子名/汇总描述', - audience VARCHAR(16) NOT NULL DEFAULT 'common' COMMENT '身份 common/anchor/robot/white', - type TINYINT NOT NULL COMMENT '1注水 2抽水 3重置', - amount DECIMAL(14,2) NOT NULL DEFAULT '0.00' COMMENT '调整金额(绝对值)', - balance_before DECIMAL(14,2) DEFAULT NULL COMMENT '调整前水位', - balance_after DECIMAL(14,2) DEFAULT NULL COMMENT '调整后水位', - pool_balance DECIMAL(14,2) DEFAULT NULL COMMENT '操作后总池余额', - operator VARCHAR(64) DEFAULT NULL COMMENT '操作人', - remark VARCHAR(200) DEFAULT NULL COMMENT '备注', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (id), - KEY idx_create (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Olympus奖池水位调整日志'; -""" - -with db.get_conn() as conn: - conn.cursor().execute(DDL) -print('game_olympus_reward_log ready.') diff --git a/olympus-py/_make_templates.py b/olympus-py/_make_templates.py deleted file mode 100644 index d0cce07..0000000 --- a/olympus-py/_make_templates.py +++ /dev/null @@ -1,71 +0,0 @@ -"""给每箱 × 每身份建"默认配比"模板并置为生效。 - -为什么需要:箱子的一个 cycle 抽满后(drawn ≥ N),Java 会 - cycleRepository.resetCycle() + distributionService.refillFromConfig(boxId, audience) -而 refillFromConfig 是**按 (boxId, audience) 查生效模板**(is_active=1,取 id 最大的一条): - - 有生效模板 → 按模板 items_json 重写 count(writeDistribution),并按固定池反推回写 total_ornaments - - 没有模板 → 只把 used 归零(配比不变) -所以要让"抽完按模板重填",必须给每个 (box, audience) 都有一条生效模板。 - -用法: - python _make_templates.py --dry # 只看会写什么 - python _make_templates.py # 写入 -""" -import argparse -import json -from collections import defaultdict - -import olympus_db as db -from olympus.config import AUDIENCES - -TEMPLATE_NAME = '默认配比' - - -def build_rows(): - rows = [] - with db.get_conn() as conn: - cur = conn.cursor() - cur.execute('SELECT box_id, box_type, bet, rtp FROM game_olympus_box ORDER BY box_id') - boxes = cur.fetchall() - cur.execute('''SELECT box_id, ornament_id, audience, count - FROM game_olympus_box_ornament ORDER BY box_id, audience, ornament_id''') - cfg = defaultdict(dict) - for box_id, orn, aud, cnt in cur.fetchall(): - cfg[(int(box_id), aud)][int(orn)] = int(cnt) - for box_id, box_type, bet, rtp in boxes: - for aud in AUDIENCES: - items = cfg.get((int(box_id), aud), {}) - if not items: - print('!! box%s %s 无配比,跳过' % (box_id, aud)) - continue - items_json = json.dumps( - [{'ornamentId': k, 'count': v} for k, v in sorted(items.items())], - ensure_ascii=False, separators=(',', ':')) - rows.append((int(box_id), aud, TEMPLATE_NAME, sum(items.values()), items_json)) - return rows - - -def main(): - ap = argparse.ArgumentParser() - ap.add_argument('--dry', action='store_true') - args = ap.parse_args() - rows = build_rows() - print('待写模板 %d 条(%d 箱 × %d 身份)' % (len(rows), len(rows) // len(AUDIENCES), len(AUDIENCES))) - if args.dry: - print('样例:', rows[0][:4], rows[0][4][:80], '...') - return - with db.get_conn() as conn: - cur = conn.cursor() - # 先取消原有生效模板,再 upsert(表上有 uk_box_aud_name 唯一键) - cur.execute('''UPDATE game_olympus_box_template SET is_active=0''') - cur.executemany('''INSERT INTO game_olympus_box_template - (box_id, audience, name, total_rounds, items_json, is_active, create_time) - VALUES (%s,%s,%s,%s,%s,1,NOW()) - ON DUPLICATE KEY UPDATE total_rounds=VALUES(total_rounds), - items_json=VALUES(items_json), is_active=1, create_time=NOW()''', rows) - cur.execute('''SELECT COUNT(*), SUM(is_active) FROM game_olympus_box_template''') - print('模板表: 总行数/生效行数 =', cur.fetchone()) - - -if __name__ == '__main__': - main() diff --git a/olympus-py/_recalc_boxes.py b/olympus-py/_recalc_boxes.py deleted file mode 100644 index 84bcf2c..0000000 --- a/olympus-py/_recalc_boxes.py +++ /dev/null @@ -1,183 +0,0 @@ -"""复刻后端 OlympusDistributionService.calcRounds + reset(),重算某模式各箱的桶配比。 - -为什么需要:`OlympusModeType.getOrnamentRange()` 改了(buy_fs 从 5~17 改成 7~17)之后, -后台要「一键重算」才会按新范围重配 count。这个脚本把同一套算法跑在 py 侧, -用来在没有后台操作的场合先把配比算出来(结果与后端一键重算一致)。 - -算法移植自 Java(calcRounds):基础权重 30×0.6^k → 二分 power 命中目标均值 → -按权重分配固定池 → 单调约束 count[i] ≥ count[i+1] → 只在最大的 3 个桶间微调逼近目标。 - -用法: - python _recalc_boxes.py --mode buy_fs --dry # 只看结果 - python _recalc_boxes.py --mode buy_fs # 写库(count 覆盖、used 归零) -""" -import argparse - -import olympus_db as db -from olympus.config import MODES, MODE_ORNAMENT_IDS, ORNAMENTS, payout_expect - -# 回退用的"桶期望":桶 17 展示 1000x 但封顶发放,按实测值算(见 config.PAYOUT_EXPECT_OVERRIDE) -_ORN_MUL = {o['id']: payout_expect(o['id']) for o in ORNAMENTS} - -# 该桶池子里一条回合都没有时才回退到 _ORN_MUL(名义/封顶修正值)。 -# 只要池子里有回合,就用它的实际均值:箱子发的是**自己这批固定回合**,它的均值就是这个箱子的真实期望。 -MIN_POOL_SAMPLE = 1 - - -def load_pool_expect(box_id, mode_code): - """该箱池子里每个桶的**实际**均值(AVG(actual_mul))——配比按"这个箱子真正会赔出去多少"算, - 而不是按桶区间中点:桶内均值偏差(实测 -18%~+11%)和每箱池子的抽样差异会直接变成 RTP 偏差。""" - with db.get_conn() as conn: - cur = conn.cursor() - cur.execute('''SELECT ornament_id, COUNT(*), AVG(actual_mul) FROM game_olympus_round - WHERE box_id=%s GROUP BY ornament_id''', (box_id,)) - rows = cur.fetchall() - exp = {i: _ORN_MUL[i] for i in MODE_ORNAMENT_IDS[mode_code]} - for oid, n, avg in rows: - if int(oid) in exp and int(n) >= MIN_POOL_SAMPLE and avg and float(avg) > 0: - exp[int(oid)] = float(avg) - return exp - - -def derive_total_rounds(fixed_rounds, cr): - cr = min(max(cr, 0.0), 0.9) - return int(fixed_rounds / (1.0 - cr) + 0.5) - - -def derive_fixed_rounds(total_rounds, cr): - cr = min(max(cr, 0.0), 0.9) - return int(total_rounds * (1.0 - cr) + 0.5) - - -def calc_rounds(mode_code, total_rounds, target_rtp, expect=None): - """返回 {ornament_id: count},合计 = total_rounds(对齐 Java calcRounds) - - expect:桶 → 该桶"实际赔出去多少"的期望;不传则用名义值(含桶 17 封顶修正)。""" - mul = expect or _ORN_MUL - cm = float(MODES[mode_code].cost_multiplier) - target_avg = target_rtp * cm - ids = sorted(MODE_ORNAMENT_IDS[mode_code]) - n = len(ids) - if total_rounds <= n: - return {i: 1 for i in ids} - base_w = [30.0 * (0.6 ** i) for i in range(n)] - remaining = total_rounds - n - guaranteed = sum(mul[i] for i in ids) - rem_target_avg = (target_avg * total_rounds - guaranteed) / remaining - power, plo, phi = 1.0, 0.2, 10.0 - for _ in range(30): - ws = [w ** power for w in base_w] - sw = sum(ws) - avg = sum(w * mul[i] for w, i in zip(ws, ids)) / sw if sw > 0 else 0.0 - if abs(avg - rem_target_avg) < max(rem_target_avg * 0.01, 0.001): - break - if avg < rem_target_avg: - phi = power - else: - plo = power - power = (plo + phi) / 2 - ws = [w ** power for w in base_w] - tw = sum(ws) - result = {i: 1 for i in ids} - rem = remaining - for i, w in zip(ids, ws): - extra = int(w / tw * remaining + 0.5) if tw > 0 else 0 - put = min(extra, rem) - if put > 0: - result[i] += put - rem -= put - if rem > 0: - result[ids[0]] += rem - for k in range(n - 2, -1, -1): # 单调 count[i] >= count[i+1] - if result[ids[k]] < result[ids[k + 1]]: - result[ids[k]] = result[ids[k + 1]] - diff = total_rounds - sum(result.values()) - if diff: - result[ids[0]] += diff - for _ in range(400): # 微调:任取两个桶搬 1 个(保持单调),只要更接近目标 - cur_mul = sum(result[i] * mul[i] for i in ids) - delta = target_avg * total_rounds - cur_mul - if abs(delta) < 0.01: - break - best, best_diff = None, abs(delta) - for lo in ids: - if result[lo] <= 1: - continue - for hi in ids: - if lo == hi: - continue - change = mul[hi] - mul[lo] - if abs(change) < 0.001: - continue - nd = abs(delta - change) - if nd < best_diff: - ok = True - ca, idx_lo = result[lo] - 1, ids.index(lo) - if idx_lo < n - 1 and ca < result[ids[idx_lo + 1]]: - ok = False - ca2, idx_hi = result[hi] + 1, ids.index(hi) - if idx_hi > 0 and ca2 > result[ids[idx_hi - 1]]: - ok = False - if ids.index(lo) < ids.index(hi) and ca < ca2: - ok = False - if ok: - best, best_diff = (lo, hi), nd - if best is None: - break - result[best[0]] -= 1 - result[best[1]] += 1 - return result - - -def main(): - ap = argparse.ArgumentParser() - ap.add_argument('--mode', default='buy_fs') - ap.add_argument('--dry', action='store_true') - args = ap.parse_args() - from olympus.config import AUDIENCES - with db.get_conn() as conn: - cur = conn.cursor() - cur.execute('''SELECT box_id, rtp, rtp_converge_ratio, total_ornaments - FROM game_olympus_box WHERE box_type=%s ORDER BY box_id''', (args.mode,)) - boxes = cur.fetchall() - print('%s 共 %d 箱,桶范围 %s;池内实际均值按**每个箱子自己的池子**算(样本<%d的桶回退名义值)' - % (args.mode, len(boxes), sorted(MODE_ORNAMENT_IDS[args.mode]), MIN_POOL_SAMPLE)) - for box_id, rtp, cr, total in boxes: - rtp = float(rtp or 0.8) - cr = float(cr if cr is not None else 0.15) - fixed = derive_fixed_rounds(int(total or 1000), cr) - pool_expect = load_pool_expect(box_id, args.mode) - counts = calc_rounds(args.mode, fixed, rtp, pool_expect) - s = sum(counts.values()) - cm = float(MODES[args.mode].cost_multiplier) - pay_pool = sum(counts[i] * pool_expect[i] for i in counts) - pay_nominal = sum(counts[i] * _ORN_MUL[i] for i in counts) - print('box%-3d 固定池%d 配比 %s' % ( - box_id, s, {k: v for k, v in sorted(counts.items()) if v > 0})) - print(' 目标RTP(本箱池内实际口径) %.2f%% 名义口径 %.2f%%' - % (100.0 * pay_pool / s / cm, 100.0 * pay_nominal / s / cm)) - if args.dry: - continue - n_cycle = derive_total_rounds(s, cr) - with db.get_conn() as conn: - cur = conn.cursor() - for aud in AUDIENCES: - cur.execute('DELETE FROM game_olympus_box_ornament WHERE box_id=%s AND audience=%s', - (box_id, aud)) - cur.executemany('''INSERT INTO game_olympus_box_ornament - (box_id, ornament_id, audience, count, used) VALUES (%s,%s,%s,%s,0)''', - [(box_id, i, aud, c) for i, c in sorted(counts.items()) if c > 0]) - cur.execute('UPDATE game_olympus_box SET total_ornaments=%s WHERE box_id=%s', - (n_cycle, box_id)) - if not args.dry: - print('已写库(count 覆盖、used 归零、total_ornaments=一轮抽数)。') - print('提醒:本脚本只动库里的 used,**不会清 Redis 的本轮账本**' - '(ruoyi:olympus:cycle:{boxId}:{audience});不清的话后台「实时RTP」会把' - '重置前的旧抽数和新抽数混在一起显示。' - '清账本:docker exec redis_cache redis-cli -a DEL ruoyi:olympus:cycle::,' - '或在后台对这几个箱点一次「一键重算分布」(它会同时清账本)。') - print('记得再跑 python _make_templates.py 刷新模板。') - - -if __name__ == '__main__': - main() diff --git a/olympus-py/_regen.py b/olympus-py/_regen.py deleted file mode 100644 index c6a1d74..0000000 --- a/olympus-py/_regen.py +++ /dev/null @@ -1,329 +0,0 @@ -"""重新生成回合数据前的检查 / 快照 / 清空 / 还原(一次性脚本,用完可删)。 - - python _regen.py check # 查看箱配置、桶配比、回合池构成 - python _regen.py snapshot # 备份 box / box_ornament / box_template 到 JSON - python _regen.py clear # 清空 game_olympus_step + game_olympus_round - python _regen.py restore # 从备份还原 box_ornament 的 count(used 归 0) - python _regen.py verify # 校验:buy_fs 触发盘 4S0M、FS 内仍有炸弹、池子每桶 77 - python _regen.py fsstats [box_id] # 免费旋转观感统计:每局几次消除、几次炸弹并入、累计倍数 -""" -import json -import sys -from datetime import datetime -from pathlib import Path - -import olympus_db as db - -SNAP = Path(__file__).resolve().parent / '_backup_box_config.json' - - -def check(): - with db.get_conn() as conn: - cur = conn.cursor() - cur.execute('''SELECT box_id, box_type, bet, total_ornaments, rtp - FROM game_olympus_box ORDER BY box_id''') - boxes = cur.fetchall() - print('boxes=%d' % len(boxes)) - for b in boxes[:3] + boxes[-2:]: - print(' ', b) - cur.execute('''SELECT audience, COUNT(DISTINCT box_id), SUM(count), SUM(used) - FROM game_olympus_box_ornament GROUP BY audience''') - print('box_ornament:', cur.fetchall()) - cur.execute('''SELECT box_id, audience, COUNT(*) n, SUM(count) total, SUM(used) used - FROM game_olympus_box_ornament WHERE box_id IN (1,21,30) - GROUP BY box_id, audience ORDER BY box_id''') - for r in cur.fetchall(): - print(' cfg', r) - for box_id in (1, 21, 30): - cur.execute('''SELECT ornament_id, COUNT(*) FROM game_olympus_round - WHERE box_id=%s GROUP BY ornament_id ORDER BY ornament_id''', - (box_id,)) - print(' pool box%d:' % box_id, cur.fetchall()) - cur.execute('''SELECT ornament_id, count FROM game_olympus_box_ornament - WHERE box_id=21 AND audience='common' ORDER BY ornament_id''') - print(' cfg box21 common:', cur.fetchall()) - cur.execute('''SELECT ornament_id, count FROM game_olympus_box_ornament - WHERE box_id=30 AND audience='common' ORDER BY ornament_id''') - print(' cfg box30 common:', cur.fetchall()) - - -def snapshot(): - data = {'ts': datetime.now().isoformat(timespec='seconds')} - with db.get_conn() as conn: - cur = conn.cursor(db.pymysql.cursors.DictCursor) - for t in ('game_olympus_box', 'game_olympus_box_ornament', 'game_olympus_box_template'): - cur.execute('SELECT * FROM ' + t) - data[t] = [{k: (str(v) if v is not None else None) for k, v in row.items()} - for row in cur.fetchall()] - SNAP.write_text(json.dumps(data, ensure_ascii=False, indent=1), encoding='utf-8') - print('snapshot ->', SNAP, - {k: len(v) for k, v in data.items() if k != 'ts'}) - - -def clear(): - with db.get_conn() as conn: - cur = conn.cursor() - for t in ('game_olympus_step', 'game_olympus_round'): - cur.execute('TRUNCATE TABLE ' + t) - print('truncated', t) - print('max_round_id =', db.get_max_round_id(), 'max_step_id =', db.get_max_step_id()) - - -def restore(): - data = json.loads(SNAP.read_text(encoding='utf-8')) - rows = data['game_olympus_box_ornament'] - with db.get_conn() as conn: - cur = conn.cursor() - cur.execute('SELECT COUNT(*) FROM game_olympus_box_ornament') - before = cur.fetchone()[0] - cur.execute('DELETE FROM game_olympus_box_ornament') - cur.executemany('''INSERT INTO game_olympus_box_ornament - (box_id, ornament_id, audience, count, used) VALUES (%s,%s,%s,%s,0)''', - [(int(r['box_id']), int(r['ornament_id']), r['audience'], - int(r['count'])) for r in rows]) - print('box_ornament: %d -> %d rows (used=0)' % (before, len(rows))) - # box 行对比:库里现值 vs 备份值 - dc = conn.cursor(db.pymysql.cursors.DictCursor) - dc.execute('''SELECT box_id, box_type, bet, total_ornaments, rtp, price, status, sort - FROM game_olympus_box ORDER BY box_id''') - old = {int(r['box_id']): r for r in data['game_olympus_box']} - for r in dc.fetchall(): - o = old.get(int(r['box_id'])) - if o is None: - print(' box %s: 新增' % r['box_id']) - continue - diff = {k: (o[k], str(r[k])) for k in r - if str(o[k]) != str(r[k])} - if diff: - print(' box %s 变化:' % r['box_id'], diff) - - -def verify(): - """grid_state 里数 '"S"'/'"M"' 出现次数(短码是独立 JSON 字符串,不会误匹配符号名)。""" - cnt = ("(CHAR_LENGTH(s.grid_state)-CHAR_LENGTH(REPLACE(s.grid_state,'\"%s\"','')))/3") - with db.get_conn() as conn: - cur = conn.cursor() - cur.execute('SELECT COUNT(*) FROM game_olympus_round') - rounds = cur.fetchone()[0] - cur.execute('SELECT COUNT(*) FROM game_olympus_step') - steps = cur.fetchone()[0] - cur.execute('SELECT box_id, COUNT(*) FROM game_olympus_round GROUP BY box_id ORDER BY box_id') - per_box = {r[0]: r[1] for r in cur.fetchall()} - print('rounds=%d steps=%d 每箱回合=%s' % (rounds, steps, sorted(set(per_box.values())))) - cur.execute('''SELECT COUNT(*) FROM game_olympus_round WHERE box_id NOT BETWEEN 1 AND 30''') - print('越界 box_id 回合:', cur.fetchone()[0]) - - # buy_fs 触发盘:每步 4 个 S、0 个 M - cur.execute(f'''SELECT COUNT(*), SUM({cnt % 'S'}), SUM({cnt % 'M'}), MAX(s.cascade_seq) - FROM game_olympus_step s JOIN game_olympus_round r USING(round_id) - WHERE r.box_id BETWEEN 21 AND 30 AND s.free_spin_id=0''') - n, sc, bm, maxseq = cur.fetchone() - print('buy_fs 触发盘: 步数=%s S总数=%s(应=%s) M总数=%s(应=0) 最大级联页=%s' - % (n, sc, 4 * (n or 0), bm, maxseq)) - - # 免费旋转内仍然有炸弹(累计器机制没被误伤) - cur.execute(f'''SELECT COUNT(*), SUM({cnt % 'M'}) FROM game_olympus_step s - JOIN game_olympus_round r USING(round_id) - WHERE s.free_spin_id > 0''') - fn, fm = map(float, cur.fetchone()) - fm = fm or 0.0 - cur.execute(f'''SELECT COUNT(*), SUM({cnt % 'M'}) FROM game_olympus_step s - JOIN game_olympus_round r USING(round_id) - WHERE r.box_id <= 10 AND s.free_spin_id = 0''') - nn, nm = map(float, cur.fetchone()) - nm = nm or 0.0 - print('FS 步数=%s 带炸弹格=%s (%.1f%%)' % (fn, fm, 100.0 * (fm or 0) / (30 * fn))) - print('normal 基础盘步数=%s 带炸弹格=%s (%.1f%%)' % (nn, nm, 100.0 * (nm or 0) / (30 * nn))) - - # ante 基础旋同样不许有炸弹;normal/ante 的 100x+ 桶(基础旋无炸弹赔不出来)应全是 FS 回合 - cur.execute(f'''SELECT COUNT(*), SUM({cnt % 'M'}) FROM game_olympus_step s - JOIN game_olympus_round r USING(round_id) - WHERE r.box_id BETWEEN 11 AND 20 AND s.free_spin_id = 0''') - an, am = map(float, cur.fetchone()) - am = am or 0.0 - print('ante 基础盘步数=%s 带炸弹格=%s (%.1f%%)' % (an, am, 100.0 * am / (30 * an))) - cur.execute('''SELECT r.ornament_id, COUNT(*) 回合, SUM(r.free_spin_count>0) FS回合 - FROM game_olympus_round r WHERE r.box_id <= 20 AND r.ornament_id >= 12 - GROUP BY r.ornament_id ORDER BY r.ornament_id''') - print('normal/ante 100x+ 桶(应 回合=FS回合):', cur.fetchall()) - cur.execute('''SELECT r.box_id, COUNT(*) 回合, SUM(r.free_spin_count>0) FS回合 - FROM game_olympus_round r WHERE r.box_id <= 20 - GROUP BY r.box_id ORDER BY r.box_id''') - print('normal/ante 每箱 FS 回合数:', cur.fetchall()) - - # 爆奖档位:只有小/中/大,且只落在免费旋转的旋末帧 - cur.execute('''SELECT bigwin_level, COUNT(*) FROM game_olympus_step - GROUP BY bigwin_level ORDER BY bigwin_level''') - print('爆奖档位分布:', cur.fetchall()) - cur.execute('''SELECT COUNT(*) FROM game_olympus_step - WHERE bigwin_level IS NOT NULL AND (is_final <> 1 OR free_spin_id = 0)''') - print('爆奖档位落在基础旋/非末帧的行数(应=0):', cur.fetchone()[0]) - - # 盘面绝不能有空格(前端会显示成空白格子) - cur.execute('''SELECT COUNT(*) FROM game_olympus_step WHERE grid_state LIKE '%""%' ''') - print('盘面空白格行数(应=0):', cur.fetchone()[0]) - - # 池子构成(每桶应≈77) - cur.execute('''SELECT box_id, MIN(c), MAX(c) FROM - (SELECT box_id, ornament_id, COUNT(*) c FROM game_olympus_round - GROUP BY box_id, ornament_id) t GROUP BY box_id ORDER BY box_id''') - print('每箱桶内回合数 min/max:', sorted({(r[1], r[2]) for r in cur.fetchall()})) - - # 配比是否为后台调好的曲线(非均分) - cur.execute('''SELECT box_id, SUM(count) FROM game_olympus_box_ornament - GROUP BY box_id, audience ORDER BY box_id LIMIT 3''') - print('box_ornament 前3箱×(身份) sum(count):', cur.fetchall()) - - -def fsstats(): - """免费旋转观感统计(按桶):每局"有消除的旋数"、"炸弹并入累计器的次数"、局末累计倍数中位数。 - 玩家真正感受到的是"按后台配比加权"后的那一行 —— 低倍桶占比高时观感就被低倍桶决定。""" - import statistics - from collections import defaultdict, Counter - from olympus.config import ORNAMENTS - _orn = {o['id']: o['name'] for o in ORNAMENTS} - box_id = int(sys.argv[2]) if len(sys.argv) > 2 else 21 - with db.get_conn() as conn: - cur = conn.cursor() - cur.execute('''SELECT r.round_id, r.ornament_id, s.free_spin_id, s.base_score, - s.accumulator_before, s.accumulator_after - FROM game_olympus_step s JOIN game_olympus_round r USING(round_id) - WHERE r.box_id=%s AND s.free_spin_id>0 ORDER BY s.round_id, s.id''', (box_id,)) - rows = cur.fetchall() - cur.execute('''SELECT ornament_id, count FROM game_olympus_box_ornament - WHERE box_id=%s AND audience=%s''', (box_id, 'common')) - cfg = {int(a): int(b) for a, b in cur.fetchall()} - cur.execute('SELECT ornament_id, AVG(actual_mul) FROM game_olympus_round WHERE box_id=%s GROUP BY ornament_id', (box_id,)) - avg = {int(a): float(b) for a, b in cur.fetchall()} - spins = {} - for rid, orn, fs, base, ab, aa in rows: - d = spins.setdefault((rid, orn, fs), {'elim': False, 'ab': 0.0, 'aa': 0.0}) - if float(base or 0) > 0: - d['elim'] = True - d['ab'] = float(ab or 0) - d['aa'] = float(aa or 0) - per = defaultdict(Counter) - rounds_of = defaultdict(set) - acc_of = {} - for (rid, orn, fs), d in spins.items(): - c = per[orn] - c['spins'] += 1 - rounds_of[orn].add(rid) - acc_of[rid] = max(acc_of.get(rid, 0.0), d['aa']) - if d['elim']: - c['elim'] += 1 - if d['aa'] > d['ab']: - c['merges'] += 1 - total = sum(cfg.values()) or 1 - print('box%d 免费旋转观感(桶 抽中率 | 每局消除旋数 | 每局炸弹并入 | 局末累计中位 | 该桶实际均值)' % box_id) - w_elim = w_merges = 0.0 - for orn in sorted(per): - c = per[orn] - n = len(rounds_of[orn]) or 1 - elim_per, merge_per = c['elim'] / n, c['merges'] / n - accs = [acc_of[r] for r in rounds_of[orn]] - w = cfg.get(orn, 0) / total - w_elim += w * elim_per - w_merges += w * merge_per - print(' %2d %6sx %5.1f%% | %6.1f 旋 | %6.2f 次 | %8.0f | %8.2f' % ( - orn, _orn.get(orn, '?'), 100 * w, elim_per, merge_per, - statistics.median(accs) if accs else 0, avg.get(orn, 0))) - print(' ── 玩家加权(按配比):每局 %.1f 旋有消除、%.2f 次炸弹并入' % (w_elim, w_merges)) - - -def fsdry(): - """免费旋转"连续空转"统计:一局里最长连续几旋一个符号都没消(玩家观感=一直白转)。 - - dry = 该旋所有步的 base_score 都是 0(scatter 赔付不算消除:S 不参与消除)。 - 只统计有 FS 的回合;1000x 封顶截断的回合里被丢掉的旋按缺失处理,不参与连击计数。 - """ - from collections import Counter, defaultdict - from olympus.config import ORNAMENTS - _orn = {o['id']: o['name'] for o in ORNAMENTS} - box_id = int(sys.argv[2]) if len(sys.argv) > 2 else None - sql = '''SELECT r.round_id, r.box_id, r.ornament_id, s.free_spin_id, - SUM(s.base_score > 0) elim - FROM game_olympus_step s JOIN game_olympus_round r USING(round_id) - WHERE s.free_spin_id > 0''' - args = () - if box_id: - sql += ' AND r.box_id=%s' - args = (box_id,) - sql += ''' GROUP BY r.round_id, r.box_id, r.ornament_id, s.free_spin_id - ORDER BY r.round_id, s.free_spin_id''' - with db.get_conn() as conn: - cur = conn.cursor() - cur.execute(sql, args) - rows = cur.fetchall() - cur.execute('SELECT box_id, box_type FROM game_olympus_box') - btype = {int(a): str(b) for a, b in cur.fetchall()} - cur.execute("""SELECT box_id, ornament_id, count FROM game_olympus_box_ornament - WHERE audience='common'""") - cfg = defaultdict(dict) - for b, o, c in cur.fetchall(): - cfg[int(b)][int(o)] = int(c) - - per_round, meta = defaultdict(list), {} - for rid, b, o, fs, elim in rows: - per_round[int(rid)].append((int(fs), float(elim or 0) > 0)) - meta[int(rid)] = (int(b), int(o)) - - # rid -> (box, orn, 旋数, 有消除旋数, 最长连空) - stat = {} - for rid, seq in per_round.items(): - run = mx = 0 - for _, e in sorted(seq): - run = 0 if e else run + 1 - mx = max(mx, run) - stat[rid] = (meta[rid][0], meta[rid][1], len(seq), sum(e for _, e in seq), mx) - - def show(tag, rids, weight_by_cfg=False): - """weight_by_cfg:≥4 连空的概率按后台配比 count 加权(= 玩家实际遇到的概率)""" - if not rids: - return [] - dist, wsum, p4 = Counter(), 0.0, 0.0 - spins = elim = 0 - worst = [] - for rid in rids: - b, o, n, e, mx = stat[rid] - w = (cfg.get(b, {}).get(o, 0) or 1) if weight_by_cfg else 1 - wsum += w - spins += n - elim += e - dist[min(mx, 6)] += 1 # 6 及以上并作一格,否则 >6 的局不进任何格、占比会莫名少一截 - if mx >= 4: - p4 += w - worst.append((mx, rid)) - n = len(rids) - worst.sort(reverse=True) - print('%-12s 局=%5d 旋/局=%5.1f 有消除=%4.1f/局(%4.1f%%) | 最长连空 0~6+ : %s | ≥4连空 %5.1f%%' - % (tag, n, spins / n, elim / n, 100.0 * elim / max(1, spins), - ' '.join('%d%s:%.0f%%' % (k, '+' if k == 6 else '', 100.0 * dist[k] / n) - for k in range(7)), - 100.0 * p4 / wsum)) - return worst - - by_mode = defaultdict(list) - for rid, (b, o) in meta.items(): - by_mode[btype.get(b, '?')].append(rid) - print('=== 免费旋转"连续空转"(dry = 该旋 base_score 全 0;≥4连空 一列买 FS 按配比加权)===') - all_worst = [] - for mode in ('normal', 'ante', 'buy_fs'): - rids = by_mode.get(mode) or [] - if not rids: - continue - all_worst += [(mx, rid, mode, meta[rid]) for mx, rid in show(mode, rids)[:5]] - by_orn = defaultdict(list) # 同一模式的 10 个箱合并看桶 - for rid in rids: - by_orn[meta[rid][1]].append(rid) - for o in sorted(by_orn): - show(' %s %s' % (mode, _orn.get(o, o)), by_orn[o], weight_by_cfg=True) - all_worst.sort(reverse=True) - print('--- 最差局(最长连空 / round_id / 模式 / 桶)---') - for mx, rid, mode, (b, o) in all_worst[:12]: - print(' 连空%d旋 round=%d %s box%d 桶%s' % (mx, rid, mode, b, _orn.get(o, o))) - - -if __name__ == '__main__': - cmd = sys.argv[1] if len(sys.argv) > 1 else 'check' - globals()[cmd]() diff --git a/olympus-py/_reset_boxes.py b/olympus-py/_reset_boxes.py deleted file mode 100644 index edd10b1..0000000 --- a/olympus-py/_reset_boxes.py +++ /dev/null @@ -1,21 +0,0 @@ -"""一次性重置:清空箱子相关数据(box_id 改从 1 起、总回合调整时用)。 -保留 game_olympus_ornament(桶定义) 与 game_olympus_prize_*(返奖饰品)。 -用法: python _reset_boxes.py -""" -import olympus_db as db - -# 子表在前(无外键,仅为清晰) -TABLES = [ - 'game_olympus_step', - 'game_olympus_round', - 'game_olympus_box_ornament', - 'game_olympus_box_template', - 'game_olympus_box', -] - -with db.get_conn() as conn: - cur = conn.cursor() - for t in TABLES: - cur.execute(f'TRUNCATE TABLE {t}') - print('truncated', t) -print('reset done. max_round_id =', db.get_max_round_id()) diff --git a/olympus-py/generate_rounds.py b/olympus-py/generate_rounds.py deleted file mode 100644 index bf59432..0000000 --- a/olympus-py/generate_rounds.py +++ /dev/null @@ -1,295 +0,0 @@ -""" -Olympus 回合生成运行入口 — 只负责回合库存的多样性 - -职责说明: - - Python 端: 为模式允许的每个饰品桶预生成足够数量的回合(库存),保证多样性 - - 后台端: 通过配置各桶的数量分布(game_olympus_box_ornament.count)控制箱子 - 抽完的实时 RTP 是否达到目标,本脚本不做 RTP 收敛 - -用法: - python generate_rounds.py # 干跑: 每模式每桶生成20个验证多样性 - python generate_rounds.py --all --per-box 200 --write # 全部模式×档位建箱写库(box_id从1,每箱总回合200) - python generate_rounds.py --all --alloc config --write # 池子按后台桶配比分配(推荐):每桶 = 配比 ×1.5 - python generate_rounds.py --box-type normal --bet 1 --per-box 200 --write # 单箱写库 -""" -import argparse -import time -from collections import Counter - -from olympus.config import MODES, MODE_ORNAMENT_IDS, ORNAMENTS, RTP, AUDIENCES -from olympus_simulator import fill_distribution - -_ORN = {o['id']: o for o in ORNAMENTS} -ALLOC_K = 1.5 # --alloc config 时,每桶池子 = 配比 × 该系数(留余量给"不重复发") -# 每桶池子下限。只对"配比 × ALLOC_K < 下限"的桶起作用,也就是**配比 ≤13 的桶(6.5x 以上)**: -# 这些桶条数少、盘面又显眼(有赢奖/大奖动画),是玩家真正会"看着眼熟"的那几个,所以 20 → 60 -# 把它们的复现间隔拉长 3 倍(实测 10x 桶 5,455 抽 → 16,364 抽、100x 桶 6 万 → 18 万抽), -# 而 0x~4x 这些"量大但没人记住"的桶走 ALLOC_K、完全不受影响 —— 池子总量只 +8%。 -ALLOC_FLOOR = 60 # 每桶池子下限 - -# 并行写库用:round_id 按"该箱池子的实际条数"连续切段(池子条数由配比唯一决定,能事前算准,见 planned_rounds); -# 步数取决于随机级联、事前算不准,只能给每箱预留一段固定 id 空间。 -# 注意要按**最重的箱**留:buy_fs 每箱 ~84,000 步(每局都是 15~30 旋的 FS),normal/ante 只有 ~1.1w 步(大多 1~2 步的基础旋)。 -BOX_STEP_BAND = 200_000 - - -def read_config_counts(box_id): - """读该箱现有桶配比(各身份取最大值)。没有配置时返回 {}。""" - import olympus_db as db - with db.get_conn() as conn: - cur = conn.cursor() - cur.execute('''SELECT ornament_id, MAX(count) FROM game_olympus_box_ornament - WHERE box_id=%s GROUP BY ornament_id''', (box_id,)) - return {int(a): int(b) for a, b in cur.fetchall()} - - -def build_counts_from_config(mode_code, box_id, k=ALLOC_K): - """按后台桶配比分配池子:每桶回合数 = max(下限, 配比 × k)。 - 池子 ≥ 配比 → 一个 cycle 内每个回合最多发一次(配合 Java 侧"不重复发")。""" - cfg = read_config_counts(box_id) - counts = {} - for i in MODE_ORNAMENT_IDS[mode_code]: - c = cfg.get(i, 0) - if c > 0: - counts[i] = max(ALLOC_FLOOR, int(round(c * k))) - return counts - - -def build_counts(mode_code, per_bucket): - """每个可用桶生成相同数量的回合库存,后台再按需配比""" - return {i: per_bucket for i in MODE_ORNAMENT_IDS[mode_code]} - - -def build_counts_total(mode_code, total): - """每箱固定总回合数,跨该模式可用桶均分(余数分给靠前的桶),保证每桶≥1""" - ids = list(MODE_ORNAMENT_IDS[mode_code]) - n = len(ids) - base, rem = divmod(total, n) - return {i: base + (1 if idx < rem else 0) for idx, i in enumerate(ids)} - - -def seq_box_id(mode_code, bet): - """全局顺序 box_id(从 1 开始):按模式顺序×档位顺序编号""" - idx = 0 - for mc, mode in MODES.items(): - for b in mode.base_bets: - idx += 1 - if mc == mode_code and b == bet: - return idx - return idx - - -def report(mode_code, counts, rounds): - total_target = sum(counts.values()) - dist = Counter(rd['ornament_id'] for rd, _ in rounds) - print(f'[{mode_code}] rounds={len(rounds)}/{total_target}') - for i in sorted(counts): - hits = [rd for rd, _ in rounds if rd['ornament_id'] == i] - if hits: - muls = [rd['actual_mul'] for rd in hits] - casc = [rd['total_cascades'] for rd in hits] - fs = sum(1 for rd in hits if rd['free_spin_count'] > 0) - extra = (f'倍率{min(muls):.2f}~{max(muls):.2f} ' - f'步数{min(casc)}~{max(casc)} FS回合{fs}') - else: - extra = '' - print(f' 桶{i:>2} {_ORN[i]["name"]:>6}: {dist.get(i, 0):>5}/{counts[i]:>5} {extra}') - capped = sum(1 for _, ss in rounds if any(s['win_capped'] for s in ss)) - overmax = sum(1 for rd, _ in rounds if rd['actual_mul'] > 1000) - print(f' 封顶截断={capped} 超1000={overmax}') - - -def dry_run(per_bucket=20): - for mode_code in MODES: - counts = build_counts(mode_code, per_bucket) - t0 = time.time() - rounds, _ = fill_distribution({'box_id': 0, 'box_type': mode_code}, counts) - report(mode_code, counts, rounds) - print(f' 耗时 {time.time() - t0:.1f}s\n') - - -def derive_cycle_rounds(box_id, fixed_sum, default_cr=0.15): - """一轮抽多少抽 N = round(固定池 /(1-收敛比例)),对齐 Java deriveTotalRounds。 - box.total_ornaments 的语义是"一轮总抽数",不是池子大小(抽完补池时 Java 也会回写这个值)。""" - import olympus_db as db - cr = default_cr - with db.get_conn() as conn: - cur = conn.cursor() - cur.execute('SELECT rtp_converge_ratio FROM game_olympus_box WHERE box_id=%s', (box_id,)) - row = cur.fetchone() - if row and row[0] is not None: - cr = float(row[0]) - cr = min(max(cr, 0.0), 0.9) - return int(round(fixed_sum / (1.0 - cr))) - - -def write_box(mode_code, bet, box_id, per_box, alloc='flat', round_base=None, step_base=None, planned=None): - import olympus_db as db - mode = MODES[mode_code] - if bet not in mode.base_bets: - raise SystemExit(f'模式 {mode_code} 不支持档位 {bet},可选: {mode.base_bets}') - price = float(mode.price_for(bet)) - counts = build_counts_from_config(mode_code, box_id) if alloc == 'config' else {} - if not counts: - counts = build_counts_total(mode_code, per_box) - total_rounds = sum(counts.values()) - config_counts = read_config_counts(box_id) or dict(counts) - box = {'box_id': box_id, 'box_type': mode_code, 'bet': bet, - 'name': f'{mode.name}-bet{bet}', 'price': price, - 'total_ornaments': derive_cycle_rounds(box_id, sum(config_counts.values())), - 'rtp': RTP} - print(f'生成 box={box_id} {box["name"]} price={price} ' - f'分配方式={alloc} 池子{total_rounds}条 / 一轮{box["total_ornaments"]}抽 ...') - t0 = time.time() - rounds, gen = fill_distribution(box, counts) - report(mode_code, counts, rounds) - if planned is not None and len(rounds) != planned: - raise SystemExit(f'box={box_id}: 实际生成 {len(rounds)} 条 ≠ 预留 id 段 {planned} 条,' - f'id 段会错位 —— 重跑一次(配比刚改过就先 --clear)') - - round_base = db.get_max_round_id() if round_base is None else round_base - step_base = db.get_max_step_id() if step_base is None else step_base - rounds_data, steps_data = [], [] - rid, sid = round_base, step_base - for rd, steps in rounds: - rid += 1 - rd['round_id'] = rid - rounds_data.append(rd) - for s in steps: - sid += 1 - s['id'] = sid - s['round_id'] = rid - steps_data.append(s) - if len(steps_data) > BOX_STEP_BAND: - raise SystemExit(f'box={box_id}: {len(steps_data)} 步超出每箱预留段 {BOX_STEP_BAND},' - f'把 BOX_STEP_BAND 调大再跑') - - db.insert_box(box) - # box_ornament:已有后台配比则原样写回(池子形状 ≠ 配比,别把配比覆盖成池子形状); - # 首次建箱才用实际生成量做种子 - keep = read_config_counts(box_id) or dict(gen) - db.write_box_distributions(box_id, {aud: dict(keep) for aud in AUDIENCES}) - db.write_rounds_and_steps(box_id, rounds_data, steps_data) - print(f'写库完成: {len(rounds_data)} rounds / {len(steps_data)} steps, ' - f'耗时 {time.time() - t0:.1f}s\n') - - -def all_boxes(): - """(box_id, mode_code, bet) 顺序编号,与旧 --all 完全一致(1..30)""" - boxes = [] - box_id = 0 - for mode_code, mode in MODES.items(): - for bet in mode.base_bets: - box_id += 1 - boxes.append((box_id, mode_code, bet)) - return boxes - - -def _box_task(task): - """worker 入口:跑一箱(id 段由任务自带,互不重叠)""" - box_id, mode_code, bet, per_box, alloc, r_base, s_base, planned = task - write_box(mode_code, bet, box_id, per_box, alloc, r_base, s_base, planned) - return box_id - - -def planned_rounds(mode_code, box_id, per_box, alloc): - """这箱这一轮会生成多少条回合:池子条数 = Σ max(ALLOC_FLOOR, 配比×ALLOC_K),由配比唯一决定。 - 用它给每箱切一段**连续**的 round_id(并行写库不会撞主键,id 也不会有空洞)。""" - counts = build_counts_from_config(mode_code, box_id) if alloc == 'config' else {} - if not counts: - counts = build_counts_total(mode_code, per_box) - return sum(counts.values()) - - -def clear_pool_tables(): - """TRUNCATE 回合池两张表:清空全部 30 箱的旧回合,id 从 1 重排。 - 注意 game_olympus_record(战绩)里的 round_id 会因此指向新池子里的同号回合 —— 战绩金额本身不受影响。""" - import olympus_db as db - with db.get_conn() as conn: - cur = conn.cursor() - cur.execute('TRUNCATE TABLE game_olympus_step') - cur.execute('TRUNCATE TABLE game_olympus_round') - print('已 TRUNCATE game_olympus_step / game_olympus_round(旧池子清空,round_id 从 1 重排)') - - -def _existing_rounds(box_id): - import olympus_db as db - with db.get_conn() as conn: - cur = conn.cursor() - cur.execute('SELECT COUNT(*) FROM game_olympus_round WHERE box_id=%s', (box_id,)) - return int(cur.fetchone()[0]) - - -def run_all(alloc, per_box, workers, clear=False, resume=False): - """--all:顺序(workers=1)或按箱并行写库。 - 生成是纯 CPU 活(多线程没用,要进程);各箱 round_id 段连续不重叠,所以并行也不会撞主键。 - --resume:跳过已经写好的箱(条数 == 计划条数),只补剩下的 —— id 段仍按整轮 30 箱排,所以布局和整轮跑一致。 - 进程数:本机只有 4 个物理核(8 逻辑),而且 VS Code/IDEA/Docker 常驻,建议 1~2。""" - import olympus_db as db - if clear: - clear_pool_tables() - cursor, step_base0 = 0, 0 - else: - cursor, step_base0 = db.get_max_round_id(), db.get_max_step_id() - tasks = [] - for bid, mc, bet in all_boxes(): - n = planned_rounds(mc, bid, per_box, alloc) - tasks.append((bid, mc, bet, per_box, alloc, - cursor, step_base0 + bid * BOX_STEP_BAND, n)) - cursor += n - if resume: - left = [t for t in tasks if _existing_rounds(t[0]) != t[7]] - print(f'--resume:已写好 {len(tasks) - len(left)} 箱,待生成 {len(left)} 箱') - tasks = left - if not tasks: - print('没有需要生成的箱') - return - print(f'共 {len(tasks)} 箱,round_id 段 {tasks[0][5] + 1} ~ {cursor}(连续、不重叠)') - if workers <= 1: - t0 = time.time() - for done, t in enumerate(tasks, 1): - _box_task(t) - print(f'--- [{done}/{len(tasks)}] box={t[0]} 完成 累计 {time.time() - t0:.1f}s') - return - import multiprocessing as mp - t0 = time.time() - print(f'并行 {workers} 进程 ...') - with mp.Pool(processes=workers) as pool: - for done, bid in enumerate(pool.imap_unordered(_box_task, tasks), 1): - print(f'--- [{done}/{len(tasks)}] box={bid} 完成 累计 {time.time() - t0:.1f}s') - - -def main(): - ap = argparse.ArgumentParser() - ap.add_argument('--box-type', choices=list(MODES), help='模式') - ap.add_argument('--bet', type=int, help='基础下注档位') - ap.add_argument('--per-bucket', type=int, default=20, help='干跑每桶回合数(仅 dry-run 用)') - ap.add_argument('--per-box', type=int, default=200, help='每箱总回合数(--alloc flat 用,跨桶均分)') - ap.add_argument('--alloc', choices=['flat', 'config'], default='flat', - help='flat=跨桶均分(旧行为);config=按后台桶配比×1.5 分配(池子≥配比)') - ap.add_argument('--write', action='store_true', help='写入数据库') - ap.add_argument('--all', action='store_true', help='全部模式×档位,box_id 从 1 顺序编号') - ap.add_argument('--workers', type=int, default=1, - help='并行进程数(生成是纯 CPU 活,多线程没用;建议 ≤3,DB 连接数限制)') - ap.add_argument('--clear', action='store_true', - help='先 TRUNCATE 回合池两张表(全部箱),round_id 从 1 重排;只配合 --all 用') - ap.add_argument('--resume', action='store_true', - help='只补还没写好的箱(跳过已写完的),id 段仍按整轮 30 箱排') - args = ap.parse_args() - - if args.clear and not args.all: - raise SystemExit('--clear 会清空全部箱的回合池,只配合 --all 用') - if not args.write: - dry_run(min(args.per_bucket, 20)) - return - if args.all: - run_all(args.alloc, args.per_box, args.workers, args.clear, args.resume) - elif args.box_type and args.bet: - write_box(args.box_type, args.bet, seq_box_id(args.box_type, args.bet), - args.per_box, args.alloc) - else: - raise SystemExit('--write 需要 --box-type + --bet,或 --all') - - -if __name__ == '__main__': - main() diff --git a/olympus-py/olympus/__init__.py b/olympus-py/olympus/__init__.py deleted file mode 100644 index 2502f0e..0000000 --- a/olympus-py/olympus/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Olympus 电子游戏规则核心 (0.1.0)。""" - -from .config import ( - SYMBOL_RULES, - SCATTER_CODE, - SCATTER_PAYOUTS, - MODES, - DEFAULT_MODE, - MAX_WIN_MULTIPLIER, - GameMode, -) - -__version__ = "0.1.0" - -__all__ = [ - # config - "SYMBOL_RULES", - "SCATTER_CODE", - "SCATTER_PAYOUTS", - "MODES", - "DEFAULT_MODE", - "MAX_WIN_MULTIPLIER", - "GameMode", - # engine - "OlympusEngine", - "RuleViolation", - # models - "Bomb", - "CascadeResult", - "Elimination", - "SessionState", - "SpinResult", -] - -from .engine import OlympusEngine, RuleViolation -from .models import Bomb, CascadeResult, Elimination, SessionState, SpinResult diff --git a/olympus-py/olympus/config.py b/olympus-py/olympus/config.py deleted file mode 100644 index 2eee350..0000000 --- a/olympus-py/olympus/config.py +++ /dev/null @@ -1,311 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from decimal import Decimal -from typing import Mapping, Tuple - - -@dataclass(frozen=True) -class SymbolRule: - code: str - name: str - payouts: Mapping[int, Decimal] - - def payout_for(self, count: int) -> Decimal: - for threshold in sorted(self.payouts, reverse=True): - if count >= threshold: - return self.payouts[threshold] - return Decimal("0") - - -# 赔付倍数以 1 倍下注为基准,数值对齐官方赔付表(8-9 / 10-11 / 12-30 三档) -SYMBOL_RULES = { - "CROWN": SymbolRule("CROWN", "皇冠", {12: Decimal("50"), 10: Decimal("25"), 8: Decimal("10")}), - "HOURGLASS": SymbolRule("HOURGLASS", "沙漏", {12: Decimal("25"), 10: Decimal("10"), 8: Decimal("2.5")}), - "RING": SymbolRule("RING", "环", {12: Decimal("15"), 10: Decimal("5"), 8: Decimal("2")}), - "CHALICE": SymbolRule("CHALICE", "杯赛", {12: Decimal("12"), 10: Decimal("2"), 8: Decimal("1.5")}), - "RED_GEM": SymbolRule("RED_GEM", "红宝石", {12: Decimal("10"), 10: Decimal("1.5"), 8: Decimal("1")}), - "PURPLE_GEM": SymbolRule("PURPLE_GEM", "紫宝石", {12: Decimal("8"), 10: Decimal("1.2"), 8: Decimal("0.8")}), - "YELLOW_GEM": SymbolRule("YELLOW_GEM", "黄宝石", {12: Decimal("5"), 10: Decimal("1"), 8: Decimal("0.5")}), - "GREEN_GEM": SymbolRule("GREEN_GEM", "绿宝石", {12: Decimal("4"), 10: Decimal("0.9"), 8: Decimal("0.4")}), - "BLUE_GEM": SymbolRule("BLUE_GEM", "蓝宝石", {12: Decimal("2"), 10: Decimal("0.75"), 8: Decimal("0.25")}), -} - -SCATTER_CODE = "SCATTER" -# 6/5/4 个 SCATTER 的赔付倍数(1 倍下注基准,与皇冠系列量表同源) -SCATTER_PAYOUTS = { - 6: Decimal("100"), - 5: Decimal("5"), - 4: Decimal("3"), -} -SCATTER_FREE_SPIN_TRIGGER = 4 # 基础盘 4+ SCATTER 触发免费旋转 -SCATTER_RETRIGGER_TRIGGER = 3 # 免费旋转中 3+ SCATTER 触发重触发 -FREE_SPINS_AWARDED = 15 # 首次触发赠送次数 -FREE_SPIN_RETRIGGER_COUNT = 5 # 每次重触发追加次数 - -MULTIPLIER_VALUES = frozenset({ - 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 25, 50, 100, 250, 500, 1000, -}) - -MIN_WIN_COUNT = 8 -MAX_SYMBOL_COUNT = 30 - -# 单局最高赢奖封顶:1000 倍基础下注(最高 1000x 饰品) -MAX_WIN_MULTIPLIER = Decimal("1000") - - -# ============================================================ -# 游戏模式:互斥规则 -# - 底注(ante)开启时禁用购买免费旋转;购买免费旋转(buy_fs)时禁用底注效果 -# - 只有一个购买免费旋转档(无 super 模式) -# ============================================================ - -@dataclass(frozen=True) -class GameMode: - code: str - name: str - cost_multiplier: int # 实际扣费 = 基础下注 × cost_multiplier - base_bets: Tuple[int, ...] # 可选基础下注档位 - ante_boost: bool # 底注效果(SCATTER 概率提升) - guaranteed_free_spins: bool # 保证直接进入免费旋转 - can_buy_free_spins: bool # 是否允许购买免费旋转 - - def price_for(self, base_bet: int) -> Decimal: - if base_bet not in self.base_bets: - raise ValueError(f"模式 {self.code} 不支持下注档位 {base_bet}") - return Decimal(base_bet) * self.cost_multiplier - - -_NORMAL_BETS = (1, 3, 5, 10, 20, 30, 50, 100, 200, 300) -_BUY_FS_BETS = _NORMAL_BETS # 10 档,实际价格 50 ~ 15000 - -MODES = { - # 普通旋转:实际扣费 = 1 3 5 10 20 30 50 100 200 300 - "normal": GameMode( - code="normal", name="普通旋转", cost_multiplier=1, - base_bets=_NORMAL_BETS, - ante_boost=False, guaranteed_free_spins=False, can_buy_free_spins=True, - ), - # 底注:实际扣费 = 2 6 10 20 40 60 100 200 400 600;禁止购买免费旋转 - "ante": GameMode( - code="ante", name="底注", cost_multiplier=2, - base_bets=_NORMAL_BETS, - ante_boost=True, guaranteed_free_spins=False, can_buy_free_spins=False, - ), - # 购买免费旋转:实际扣费 = 50 150 250 500 1000 1500 2500 5000 10000 15000;无底注效果 - # 售价 50 倍(原 20 倍):局均赔付 = 售价 × RTP = 40 倍底注 → 后台一键重算会把配比压到 - # 25x/40x/60x 桶(FS 预算 22~57),每局才演得出 6~10 次消除 + 4~6 次炸弹累计; - # 20 倍时局均只有 16 倍,配比被迫压在 10x/16x 桶(预算 7~13),动画"钱不够"。 - "buy_fs": GameMode( - code="buy_fs", name="购买免费旋转", cost_multiplier=50, - base_bets=_BUY_FS_BETS, - ante_boost=False, guaranteed_free_spins=True, can_buy_free_spins=False, - ), -} - -DEFAULT_MODE = "normal" - - -# ============================================================ -# 饰品桶定义(DB 写入用):最高 1000x,对齐封顶规则 -# 设计原则:区间连续无缝覆盖 0~1370(封顶前理论值域); -# expect_mul 严格等于 (range_low + range_high) / 2; -# 低倍段密集(高频小奖)、高倍段递宽。 -# 注:顶桶 id=17 区间 630~1370(中点=期望=1000),但实际发放受封顶约束: -# 超过 1000 倍的部分不再给奖,一律按 1000 倍发放(引擎 MAX_WIN_MULTIPLIER 截断)。 -# ============================================================ -ORNAMENTS = [ - {'id': 1, 'name': '0x', 'expect_mul': 0.0, 'range_low': 0.00, 'range_high': 0.00}, - {'id': 2, 'name': '0.5x', 'expect_mul': 0.5, 'range_low': 0.01, 'range_high': 1.00}, - {'id': 3, 'name': '1.5x', 'expect_mul': 1.5, 'range_low': 1.00, 'range_high': 2.00}, - {'id': 4, 'name': '2.5x', 'expect_mul': 2.5, 'range_low': 2.00, 'range_high': 3.00}, - {'id': 5, 'name': '4x', 'expect_mul': 4.0, 'range_low': 3.00, 'range_high': 5.00}, - {'id': 6, 'name': '6.5x', 'expect_mul': 6.5, 'range_low': 5.00, 'range_high': 8.00}, - {'id': 7, 'name': '10x', 'expect_mul': 10.0, 'range_low': 8.00, 'range_high': 12.00}, - {'id': 8, 'name': '16x', 'expect_mul': 16.0, 'range_low': 12.00, 'range_high': 20.00}, - {'id': 9, 'name': '25x', 'expect_mul': 25.0, 'range_low': 20.00, 'range_high': 30.00}, - {'id': 10, 'name': '40x', 'expect_mul': 40.0, 'range_low': 30.00, 'range_high': 50.00}, - {'id': 11, 'name': '60x', 'expect_mul': 60.0, 'range_low': 50.00, 'range_high': 70.00}, - {'id': 12, 'name': '100x', 'expect_mul': 100.0, 'range_low': 70.00, 'range_high': 130.00}, - {'id': 13, 'name': '150x', 'expect_mul': 150.0, 'range_low': 130.00, 'range_high': 170.00}, - {'id': 14, 'name': '200x', 'expect_mul': 200.0, 'range_low': 170.00, 'range_high': 230.00}, - {'id': 15, 'name': '300x', 'expect_mul': 300.0, 'range_low': 230.00, 'range_high': 370.00}, - {'id': 16, 'name': '500x', 'expect_mul': 500.0, 'range_low': 370.00, 'range_high': 630.00}, - {'id': 17, 'name': '1000x', 'expect_mul': 1000.0, 'range_low': 630.00, 'range_high': 1370.00}, -] - - -# ============================================================ -# 回合生成器参数 -# ============================================================ -GRID_ROWS = 5 -GRID_COLS = 6 -GRID_SIZE = GRID_ROWS * GRID_COLS # 30 - -# 短码映射(网格/步骤 JSON 存储用,对齐 sweet 风格) -SYMBOL_SHORT = { - 'CROWN': 'A', 'HOURGLASS': 'B', 'RING': 'C', 'CHALICE': 'D', 'RED_GEM': 'E', - 'PURPLE_GEM': 'F', 'YELLOW_GEM': 'G', 'GREEN_GEM': 'H', 'BLUE_GEM': 'I', -} -SHORT_TO_SYMBOL = {v: k for k, v in SYMBOL_SHORT.items()} -SCATTER_SHORT = 'S' -BOMB_SHORT = 'M' - -# 普通符号随机权重(赔率越高权重越低) -SYMBOL_WEIGHTS = {'A': 5, 'B': 6, 'C': 7, 'D': 8, 'E': 9, 'F': 10, 'G': 10, 'H': 10, 'I': 12} - -# 炸弹(乘数符号)参数 -BOMB_CELL_PROBABILITY = 0.04 # 每格出现炸弹概率 ≈1个/屏 -BOMB_VALUES = sorted(MULTIPLIER_VALUES) -BOMB_WEIGHT_POWER = 0.5 # 值越大权重越低: w = 1/v^power - -# 炸弹品质(按乘数值分 5 档,对应绿/蓝/紫/红/彩虹 5 种球体) -BOMB_QUALITIES = [ - # (quality_code, 名称, 等级, 包含乘数值) - ('GREEN', '绿色炸弹', 1, (2, 3, 4, 5, 6)), - ('BLUE', '蓝色炸弹', 2, (8, 10, 12, 15)), - ('PURPLE', '紫色炸弹', 3, (20, 25, 50)), - ('RED', '红色炸弹', 4, (100, 250)), - ('RAINBOW', '彩虹炸弹', 5, (500, 1000)), -] -# value -> quality_code 快速查表 -BOMB_VALUE_QUALITY = {v: code for code, _, _, vals in BOMB_QUALITIES for v in vals} - - -def bomb_quality(value: int) -> str: - """炸弹乘数值 → 品质编码(GREEN/BLUE/PURPLE/RED/RAINBOW)""" - return BOMB_VALUE_QUALITY[value] - -# 免费旋转触发/散布(底注概率翻倍,互斥规则:buy_fs 保证触发) -BASE_FS_TRIGGER_PROB = {'normal': 0.005, 'ante': 0.010} -BASE_NEARMISS_SCATTER = { - 'normal': {0: 0.88, 1: 0.08, 2: 0.03, 3: 0.01}, - 'ante': {0: 0.74, 1: 0.16, 2: 0.07, 3: 0.03}, -} -FS_TRIGGER_SCATTER = {4: 0.80, 5: 0.15, 6: 0.05} # 触发盘落 S 数 -FREESPIN_SCATTER = { # FS 每旋落 S 数(≥3 → +5) - 'default': {0: 0.70, 1: 0.15, 2: 0.07, 3: 0.05, 4: 0.025, 5: 0.005}, - 'buy_fs': {0: 0.78, 1: 0.13, 2: 0.06, 3: 0.02, 4: 0.008, 5: 0.002}, -} -MAX_FREE_SPINS = 30 # 重触发总次数上限 - -# ============================================================ -# 免费旋转"每旋保底消除"(仅 buy_fs) -# 动机:15 旋原来 86.8% 的旋完全空转(45% 强制空转 + 自然填充 82.9% 凑不出 8 连) -# 做法:本旋没消成时按概率补一组最小档消除(8 连低价值符号 = 0.25 倍底注),消完再按概率续链 -# 预算:补桶时按桶目标反推本局 FS 预算 —— 按剩余旋数自平衡,花完即停 -# 注:normal/ante 的自然触发盘不走这套(仍按原 45% 空转 + 自然),避免打穿 0x~2.5x 低倍桶 -# ============================================================ -FS_SPIN_WIN_PROB = 0.60 # 自然阶段:每旋"至少消一波"的概率 -# 有预算时(buy_fs / 高倍桶补桶)不再按"剩余预算÷剩余旋数"算每旋概率 —— 扣预算扣的是**已被累计 -# 倍数放大**的赢额,累计器一涨几旋就把预算烧穿 → 后半局 p=0 一直空转到结束(实测买 FS 有 88% 的局 -# 出现 ≥4 旋连空、51.7% 的局尾巴一直空转)。改成:一局能买几次消除由预算反推 -# (k ≈ √(预算 / FS_GUARANTEE_COST)),每次消除后**重抽下一次的位置**(间隔随机,见 -# `olympus_simulator._fs_next_target`)—— 整局消除次数不变,但不再"每 4 旋必消"。 -FS_GUARANTEE_COST = 0.45 # 每多一次"保底消除+炸弹累计"的边际赔付(实测 ≈0.45×k) -# 随机间隔的兜底:连续空转越久,越可能被强制补一次保底消除(**软**上限,不是硬闸)—— -# 硬上限(到第 N 旋必消)会留下新的固定天花板(实测硬顶 6 时有 42% 的局"最长连空正好 6"), -# 所以按概率递增:dry=5/6 时 0.5/0.85,dry≥8 必补。配额提前用完时靠它兜住长空尾。 -# 实测(box21 整池):最长连空 2~8 旋散开、单值最高 42%(等距版是 87% 都卡在 4),≥7 旋占 2.5%。 -FS_DRY_FORCE_PROB = {5: 0.50, 6: 0.85} -FS_DRY_FORCE_HARD = 8 # 到这个旋数无条件补(概率表兜不到时) -# 连消概率:越低,同一份预算能覆盖的"有消除的旋"越多(0.45 时预算集中在少数旋上, -# 一局只剩 1~2 旋有消除;0.10 时同样的钱能铺到 4~10 旋) -FS_CASCADE_CONTINUE_PROB = 0.10 -FS_CASCADE_MAX = 5 # 单旋最大消除波数 -FS_GUARANTEE_COMBOS = ( # 保底消除组合 (符号, 个数, 赔付倍数@1倍底注),最小档为主 - ('I', 8, 0.25), ('I', 8, 0.25), ('H', 8, 0.40), ('G', 8, 0.50), -) - -# 免费旋转里的炸弹(buy_fs):**每一旋都可能出现、每屏最多 1 颗**。 -# ① 每一旋都可能出现:前面几旋的消除盘面上也看得见炸弹,倍数从第 1~2 旋就开始涨; -# ② 每屏最多 1 颗:一屏堆 2~4 颗会"一次消除好几颗一起爆",看不出"消一次爆一颗"的节奏; -# ③ 炸弹**有大有小**,但按该局的档位配:低倍档(< FS_BIG_BOMB_TARGET)用 2~8 的小球 -# (累计倍数作用到每一次消除:0.4×2 + 0.5×4 = 2.8 这种滚雪球,大球一合并后面每次消除 -# 都被放大几十倍,低倍档的预算立刻见底),高倍档(≥100x)用全量 2~1000 —— 所以 -# "大炸弹"照样存在,只是它出现的那些局自然落在高倍桶。 -FS_SCREEN_BOMB_PROB = 0.08 -FS_SMALL_BOMB_MAX = 8 -FS_BIG_BOMB_TARGET = 100 -FS_MAX_BOMBS_PER_SPIN = 1 - -# "只能靠免费旋转赔出来"的目标倍数下限:normal/ante 的基础旋不出炸弹,最多只能凑到 -# A12(50) + B10(10) ≈ 60x(自然多组 75x 已属极罕见),所以目标 ≥80x 的桶(100x/150x/200x) -# 只能生成"自然触发 FS"的回合来装 —— 这也是这两个模式里玩家能进 FS 的现实来源。 -FS_ONLY_TARGET_MUL = 80.0 - -# 目标 RTP 与各模式可用饰品桶 -RTP = 0.8 -MODE_ORNAMENT_IDS = { - 'normal': list(range(1, 14)), # 0x ~ 150x - 'ante': list(range(1, 15)), # 0x ~ 200x - 'buy_fs': list(range(7, 18)), # 10x ~ 1000x(保证进 FS;原 5 起=4x 起,低倍桶预算装不下 FS 动画) -} -# 桶"实际发出期望"的**回退值**:桶 17 展示 1000x(区间 630~1370),但单局封顶 1000 → -# 真正发出去的是 min(原始分, 1000): -# · 理论值(假设原始分在 630~1370 均匀分布):E[min(x,1000)] = 907.5 -# · 实测值(buy_fs 10 箱 × 桶17 全部回合 = 200 个):均值 834.6、中位 821.3、27.5% 触顶 1000 -# —— 生成端种的大奖目标集中在 1000 附近、不是均匀分布,所以实测低于理论值。 -# 注意:配比(calcRounds)与后台「目标RTP」现在优先用**该箱池内 AVG(actual_mul)**(见 -# _recalc_boxes.load_pool_expect / Java loadPoolExpect),这个覆盖值只在"池子里一条回合都没有" -# 时兜底 —— 它比名义 1000 诚实(高估约 0.5~1.6 个百分点)。 -PAYOUT_EXPECT_OVERRIDE = {17: 835.0} - -_ORN_EXPECT = {o['id']: float(o['expect_mul']) for o in ORNAMENTS} - - -def payout_expect(orn_id: int) -> float: - """桶的"实际发出期望"回退值:受封顶影响的桶用实测覆盖值,其余用名义 expect_mul。 - - 用于:池子里还没有该桶回合时的兜底、以及模拟/报表里的名义口径对比。 - **抽回合时的 payoutCap 过滤仍用名义值**(Java OlympusRoundPicker 读 DB 的 expect_mul, - 偏保守,保留安全余量)。""" - return PAYOUT_EXPECT_OVERRIDE.get(orn_id, _ORN_EXPECT.get(orn_id, 0.0)) - - -# ============================================================ -# 多身份爆率(audience):每种身份一套独立的饰品桶数量分布,实现差异化 RTP。 -# 回合库存池(round/step)完全共享,仅“抽哪个桶”的概率按身份区分。 -# 与现有宝箱 tt_user 对齐:user_type 01主播 / 02普通 / 03机器人;is_blank_account=1 白号。 -# 身份解析优先级(Java 开局):白号最高优先,压过 user_type: -# is_blank_account==1 → white;user_type=01 → anchor;03 → robot;其余 → common。 -# 白号池(white):独立 count/used,抽取只递减自己的 used、独立补桶, -# 永不消耗 common/anchor/robot 库存;白号 bet/win 不计水位、赢奖不进真实经济。 -# 机器人暂不细分幸运/非幸运档(后续视 2v2 需求再扩展 robot_unlucky)。 -# ============================================================ -AUDIENCE_COMMON = 'common' # 普通用户(对齐 user_type=02 及招商/卡商/超管等其他) -AUDIENCE_ANCHOR = 'anchor' # 主播(user_type=01) -AUDIENCE_ROBOT = 'robot' # 机器人(user_type=03) -AUDIENCE_WHITE = 'white' # 白号(is_blank_account=1,压过 user_type) -AUDIENCES = (AUDIENCE_COMMON, AUDIENCE_ANCHOR, AUDIENCE_ROBOT, AUDIENCE_WHITE) -DEFAULT_AUDIENCE = AUDIENCE_COMMON - - -# ============================================================ -# 爆奖动画(小/中/大 3 档):**只在免费旋转里判档**,按"单旋赢额"独立判档, -# 达标即在该旋末帧盖章,同档一局可多次;normal/ante 的基础旋一律 null(不播大奖动画)。 -# 两种情况两套阈值(单位:单旋赢额 ÷ 基础下注): -# ① 自然免费旋转(normal/ante 摇出 4+ S 进入):5 / 25 / 100 -# 两个模式的 FS 预算与旋数完全一致 → 共用一套;实测 FS 内约 15% 的旋 ≥5、 -# 约 4% 的旋 ≥25、约 1.7% 的旋 ≥100(约 1/4 的 FS 局能见到"大") -# ② 购买免费旋转(buy_fs):20 / 60 / 200 -# 买 FS 的赢额分散在 15+ 旋里,单旋阈值整体下调;实测约 3.5% 的旋 ≥20、 -# 约 0.9% 的旋 ≥60、约 0.1% 的旋 ≥200(约 2% 的买 FS 局能见到"大") -# 编码 small < middle < big:前端按这三个值播小/中/大动画,null 表示不播 -# ============================================================ -BIGWIN_LEVELS = ('small', 'middle', 'big') -BIGWIN_TIERS = { - 'normal': (5, 25, 100), - 'ante': (5, 25, 100), # 与 normal 同一口径:都是"自然触发的 FS",整局规模相同 - 'buy_fs': (20, 60, 200), -} - - -def bigwin_level(mode_code: str, spin_mul: float): - """单旋赢额(基础注倍数)→ 爆奖档位编码 small/middle/big;未达最低档返回 None""" - tiers = BIGWIN_TIERS[mode_code] - level = None - for name, threshold in zip(BIGWIN_LEVELS, tiers): - if spin_mul >= threshold: - level = name - return level diff --git a/olympus-py/olympus/engine.py b/olympus-py/olympus/engine.py deleted file mode 100644 index e9f8ce8..0000000 --- a/olympus-py/olympus/engine.py +++ /dev/null @@ -1,169 +0,0 @@ -from __future__ import annotations - -from collections import Counter -from decimal import Decimal -from typing import Iterable, Mapping, Sequence - -from .config import ( - DEFAULT_MODE, - MAX_SYMBOL_COUNT, - MAX_WIN_MULTIPLIER, - MIN_WIN_COUNT, - MODES, - MULTIPLIER_VALUES, - SYMBOL_RULES, - GameMode, -) -from .models import Bomb, CascadeResult, Elimination, SessionState, SpinResult - - -class RuleViolation(ValueError): - pass - - -class OlympusEngine: - def __init__( - self, - bet: Decimal | int | str = Decimal("1"), - mode: str = DEFAULT_MODE, - ) -> None: - if mode not in MODES: - raise RuleViolation(f"未知模式: {mode},可选: {sorted(MODES)}") - self.mode: GameMode = MODES[mode] - self.bet = Decimal(str(bet)) - if self.bet <= 0: - raise RuleViolation("bet 必须大于 0") - # 档位校验:整数档位必须在模式允许列表内(非整数 bet 仅用于单测/模拟,不校验) - if self.bet == self.bet.to_integral_value() and int(self.bet) not in self.mode.base_bets: - raise RuleViolation( - f"模式 {self.mode.code} 不支持下注档位 {int(self.bet)},可选: {self.mode.base_bets}" - ) - self.cost = self.bet * self.mode.cost_multiplier # 实际扣费 - self.max_win = self.bet * MAX_WIN_MULTIPLIER # 单局封顶 1000x 基础下注 - self.state = SessionState() - self._cascades: list[CascadeResult] | None = None - self._symbol_counts: Counter[str] = Counter() - self._spin_base_win = Decimal("0") - self._bombs: dict[str, Bomb] = {} - self._is_free_spin = False - - def buy_free_spins_allowed(self) -> bool: - """当前模式是否允许购买免费旋转(底注与购买互斥)。""" - return self.mode.can_buy_free_spins - - def ante_boost_active(self) -> bool: - """当前模式是否启用底注效果(购买免费旋转模式下强制关闭)。""" - return self.mode.ante_boost - - def begin_spin(self, is_free_spin: bool = False) -> int: - """is_free_spin=True 表示免费旋转:炸弹进入累计器;基础旋转炸弹仅对本旋生效。""" - if self._cascades is not None: - raise RuleViolation("当前回合尚未结束") - self._cascades = [] - self._symbol_counts.clear() - self._spin_base_win = Decimal("0") - self._bombs.clear() - self._is_free_spin = is_free_spin - return self.state.next_spin_index - - def record_cascade( - self, - symbols: Mapping[str, Sequence[int]] | None = None, - bombs: Iterable[Bomb] = (), - ) -> CascadeResult: - if self._cascades is None: - raise RuleViolation("请先调用 begin_spin") - - eliminations = [] - cascade_base_win = Decimal("0") - for symbol, raw_positions in (symbols or {}).items(): - if symbol not in SYMBOL_RULES: - raise RuleViolation(f"未知符号: {symbol}") - positions = tuple(raw_positions) - count = len(positions) - if count < MIN_WIN_COUNT or count > MAX_SYMBOL_COUNT: - raise RuleViolation(f"{symbol} 消除数量必须在 {MIN_WIN_COUNT}-{MAX_SYMBOL_COUNT} 之间") - if len(set(positions)) != count: - raise RuleViolation(f"{symbol} 的消除位置不能重复") - - payout_multiplier = SYMBOL_RULES[symbol].payout_for(count) - elimination = Elimination( - symbol=symbol, - count=count, - positions=positions, - payout_multiplier=payout_multiplier, - ) - eliminations.append(elimination) - cascade_base_win += payout_multiplier * self.bet - self._symbol_counts[symbol] += count - - current_bombs = [] - for bomb in bombs: - if bomb.value not in MULTIPLIER_VALUES: - raise RuleViolation(f"不支持的炸弹倍数: {bomb.value}") - existing = self._bombs.get(bomb.bomb_id) - if existing is not None and existing != bomb: - raise RuleViolation(f"炸弹 {bomb.bomb_id} 的数据前后不一致") - if existing is None: - self._bombs[bomb.bomb_id] = bomb - current_bombs.append(bomb) - - self._spin_base_win += cascade_base_win - result = CascadeResult( - cascade_index=len(self._cascades) + 1, - eliminations=tuple(eliminations), - bombs=tuple(current_bombs), - base_win=cascade_base_win, - accumulated_symbols=dict(self._symbol_counts), - ) - self._cascades.append(result) - return result - - def end_spin(self) -> SpinResult: - if self._cascades is None: - raise RuleViolation("请先调用 begin_spin") - - multiplier_before = self.state.accumulated_multiplier - has_win = self._spin_base_win > 0 - bombs_added = sum(bomb.value for bomb in self._bombs.values()) if has_win else 0 - if self._is_free_spin: - # 免费旋转:炸弹并入累计器,赢额乘“累计+本旋新炸弹” - multiplier_after = multiplier_before + bombs_added - effective_multiplier = max(1, multiplier_after) if has_win else 0 - else: - # 基础旋转:无累计器,炸弹仅乘本旋赢额,不留存 - multiplier_after = multiplier_before - effective_multiplier = max(1, bombs_added) if has_win else 0 - total_win = self._spin_base_win * effective_multiplier - - # 封顶:整局累计赢奖不超过 1000 倍基础下注(最高 1000x 饰品) - remaining = self.max_win - self.state.total_win - if remaining < 0: - remaining = Decimal("0") - capped = total_win > remaining - if capped: - total_win = remaining - - result = SpinResult( - spin_index=self.state.next_spin_index, - cascades=tuple(self._cascades), - base_win=self._spin_base_win, - multiplier_before=multiplier_before, - bombs_added=bombs_added, - multiplier_after=multiplier_after, - effective_multiplier=effective_multiplier, - total_win=total_win, - accumulated_symbols=dict(self._symbol_counts), - win_capped=capped, - ) - self.state.spins.append(result) - self.state.next_spin_index += 1 - self.state.accumulated_multiplier = multiplier_after - self.state.total_win += total_win - self._cascades = None - return result - - def reset_session(self) -> None: - if self._cascades is not None: - raise RuleViolation("当前回合尚未结束") - self.state.reset() diff --git a/olympus-py/olympus/models.py b/olympus-py/olympus/models.py deleted file mode 100644 index ac55c16..0000000 --- a/olympus-py/olympus/models.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass, field -from decimal import Decimal -from typing import Dict, List, Tuple - - -@dataclass(frozen=True) -class Bomb: - bomb_id: str - value: int - position: int | None = None - - -@dataclass(frozen=True) -class Elimination: - symbol: str - count: int - positions: Tuple[int, ...] - payout_multiplier: Decimal - - -@dataclass(frozen=True) -class CascadeResult: - cascade_index: int - eliminations: Tuple[Elimination, ...] - bombs: Tuple[Bomb, ...] - base_win: Decimal - accumulated_symbols: Dict[str, int] - - -@dataclass(frozen=True) -class SpinResult: - spin_index: int - cascades: Tuple[CascadeResult, ...] - base_win: Decimal - multiplier_before: int - bombs_added: int - multiplier_after: int - effective_multiplier: int - total_win: Decimal - accumulated_symbols: Dict[str, int] - win_capped: bool = False # 本回合赢奖因 1000x 封顶被截断 - - -@dataclass -class SessionState: - next_spin_index: int = 1 - accumulated_multiplier: int = 0 - total_win: Decimal = Decimal("0") - spins: List[SpinResult] = field(default_factory=list) - - def reset(self) -> None: - self.next_spin_index = 1 - self.accumulated_multiplier = 0 - self.total_win = Decimal("0") - self.spins.clear() diff --git a/olympus-py/olympus_db.py b/olympus-py/olympus_db.py deleted file mode 100644 index 7426f29..0000000 --- a/olympus-py/olympus_db.py +++ /dev/null @@ -1,229 +0,0 @@ -""" -Olympus 数据库操作模块 - -用法: - python olympus_db.py # 建表 + 写入饰品桶种子数据 -连接参数可用环境变量覆盖: OLY_DB_HOST / OLY_DB_PORT / OLY_DB_USER / OLY_DB_PASSWORD / OLY_DB_NAME -默认对齐本地开发库 (127.0.0.1:23306 changzhou,见 docker/compose/docker-compose.yml)。 -""" -import json -import os -import time -from contextlib import contextmanager -from pathlib import Path - -import pymysql - -from olympus.config import ORNAMENTS - -DB_CONFIG = { - 'host': os.environ.get('OLY_DB_HOST', '127.0.0.1'), - 'port': int(os.environ.get('OLY_DB_PORT', '23306')), - 'user': os.environ.get('OLY_DB_USER', 'changzhou'), - 'password': os.environ.get('OLY_DB_PASSWORD', 'iTSJSPPZM3LSGAPC'), - 'database': os.environ.get('OLY_DB_NAME', 'changzhou'), - 'charset': 'utf8mb4', - 'local_infile': True, -} - -SCHEMA_FILE = Path(__file__).resolve().parent / 'schema.sql' - - -@contextmanager -def get_conn(): - conn = pymysql.connect(**DB_CONFIG) - try: - yield conn - conn.commit() - except Exception: - conn.rollback() - raise - finally: - conn.close() - - -# ============================================================ -# 建表(执行 schema.sql 中所有语句,注释行自动跳过) -# ============================================================ - -def _load_schema_statements(): - text = SCHEMA_FILE.read_text(encoding='utf-8') - lines = [ln for ln in text.splitlines() if not ln.strip().startswith('--')] - statements = [s.strip() for s in '\n'.join(lines).split(';') if s.strip()] - return statements - - -def create_tables(): - """建表 + 写入饰品桶种子数据(幂等,可重复执行)""" - stmts = _load_schema_statements() - with get_conn() as conn: - cur = conn.cursor() - for sql in stmts: - cur.execute(sql) - print(f'OK: executed {len(stmts)} statements (tables + ornament seed).') - - -def verify_tables(): - """校验所有 olympus 表已存在并输出行数""" - with get_conn() as conn: - cur = conn.cursor() - cur.execute("SHOW TABLES LIKE 'game_olympus_%'") - tables = [row[0] for row in cur.fetchall()] - for t in sorted(tables): - cur.execute(f"SELECT COUNT(*) FROM {t}") - print(f'{t}: {cur.fetchone()[0]} rows') - return tables - - -# ============================================================ -# Ornament 操作 -# ============================================================ - -def insert_ornaments(): - """按 config.ORNAMENTS 覆盖写入饰品桶(与 schema.sql 种子一致)""" - with get_conn() as conn: - cur = conn.cursor() - for o in ORNAMENTS: - cur.execute(""" - INSERT INTO game_olympus_ornament (id, game_type, expect_mul, name, range_low, range_high) - VALUES (%s,'OLYMPUS',%s,%s,%s,%s) - ON DUPLICATE KEY UPDATE expect_mul=VALUES(expect_mul), name=VALUES(name), - range_low=VALUES(range_low), range_high=VALUES(range_high) - """, (o['id'], o['expect_mul'], o['name'], o['range_low'], o['range_high'])) - - -# ============================================================ -# Box 操作 -# ============================================================ - -def insert_box(box): - with get_conn() as conn: - cur = conn.cursor() - cur.execute(""" - INSERT INTO game_olympus_box (box_id, game_type, box_type, bet, name, price, - total_ornaments, rtp, sort, status) - VALUES (%s,'OLYMPUS',%s,%s,%s,%s,%s,%s,%s,%s) - ON DUPLICATE KEY UPDATE box_type=VALUES(box_type), - bet=VALUES(bet), name=VALUES(name), - price=VALUES(price), total_ornaments=VALUES(total_ornaments), - rtp=VALUES(rtp) - """, (box['box_id'], box['box_type'], box['bet'], box['name'], - box['price'], box['total_ornaments'], box['rtp'], - box.get('sort', 0), box.get('status', '1'))) - - -def query_boxes(): - with get_conn() as conn: - cur = conn.cursor(pymysql.cursors.DictCursor) - cur.execute("SELECT * FROM game_olympus_box WHERE status='1' ORDER BY box_id") - return cur.fetchall() - - -# ============================================================ -# Round / Step 批量写入(单连接清空+写入,死锁自动重试) -# ============================================================ - -def get_max_round_id(): - with get_conn() as conn: - cur = conn.cursor() - cur.execute("SELECT COALESCE(MAX(round_id), 0) FROM game_olympus_round") - return cur.fetchone()[0] - - -def get_max_step_id(): - with get_conn() as conn: - cur = conn.cursor() - cur.execute("SELECT COALESCE(MAX(id), 0) FROM game_olympus_step") - return cur.fetchone()[0] - - -def _j(v, default='[]'): - if v is None: - return default - return v if isinstance(v, str) else json.dumps(v, ensure_ascii=False) - - -def write_box_distribution(box_id, counts, audience='common'): - """单连接:按身份清空并写入饰品桶分布(只动该 audience,不影响其他身份)""" - for attempt in range(5): - try: - with get_conn() as conn: - cur = conn.cursor() - cur.execute( - "DELETE FROM game_olympus_box_ornament WHERE box_id=%s AND audience=%s", - (box_id, audience)) - if counts: - sql = ("INSERT INTO game_olympus_box_ornament" - " (box_id, ornament_id, audience, count, used)" - " VALUES (%s,%s,%s,%s,0)") - rows = [(box_id, oid, audience, cnt) - for oid, cnt in sorted(counts.items()) if cnt > 0] - cur.executemany(sql, rows) - return - except Exception as e: - if 'Deadlock' in str(e) and attempt < 4: - time.sleep(0.5 * (attempt + 1)) - continue - raise - - -def write_box_distributions(box_id, counts_by_audience): - """一次写入多套身份分布。counts_by_audience: {audience: {ornament_id: count}}""" - for audience, counts in counts_by_audience.items(): - write_box_distribution(box_id, counts, audience) - - -def write_rounds_and_steps(box_id, rounds_data, steps_data): - """单连接:清空旧回合+步骤,批量写入新数据""" - for attempt in range(5): - try: - with get_conn() as conn: - cur = conn.cursor() - cur.execute("""DELETE FROM game_olympus_step WHERE round_id IN - (SELECT round_id FROM game_olympus_round WHERE box_id=%s)""", (box_id,)) - cur.execute("DELETE FROM game_olympus_round WHERE box_id=%s", (box_id,)) - if rounds_data: - sql_r = """INSERT INTO game_olympus_round - (round_id, box_id, ornament_id, actual_mul, free_spin_count, total_cascades) - VALUES (%s,%s,%s,%s,%s,%s)""" - rows_r = [(rd['round_id'], rd['box_id'], rd['ornament_id'], - rd['actual_mul'], rd['free_spin_count'], rd['total_cascades']) - for rd in rounds_data] - cur.executemany(sql_r, rows_r) - if steps_data: - sql_s = """INSERT INTO game_olympus_step - (id, round_id, free_spin_id, cascade_seq, grid_state, - multiplier_detail, symbol_links, accumulated_symbols, spin_winners, - removed_positions, drop_data, fill_data, - scatter_trigger, retrigger_info, base_score, win_amount, - accumulator_before, accumulator_after, spin_win, win_capped, - bigwin_level, is_final) - VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)""" - rows_s = [(s['id'], s['round_id'], s['free_spin_id'], s['cascade_seq'], - _j(s['grid_state']), - _j(s.get('multiplier_detail')), - _j(s.get('symbol_links')), - _j(s.get('accumulated_symbols'), default='{}'), - _j(s.get('spin_winners')), - _j(s.get('removed_positions')), - _j(s.get('drop_data')), - _j(s.get('fill_data')), - _j(s.get('scatter_trigger'), default=None), - _j(s.get('retrigger_info'), default=None), - s['base_score'], s['win_amount'], - s['accumulator_before'], s['accumulator_after'], - s['spin_win'], s.get('win_capped', 0), - s.get('bigwin_level'), s['is_final']) - for s in steps_data] - cur.executemany(sql_s, rows_s) - return - except Exception as e: - if 'Deadlock' in str(e) and attempt < 4: - time.sleep(0.5 * (attempt + 1)) - continue - raise - - -if __name__ == '__main__': - create_tables() - verify_tables() diff --git a/olympus-py/olympus_simulator.py b/olympus-py/olympus_simulator.py deleted file mode 100644 index 1da3985..0000000 --- a/olympus-py/olympus_simulator.py +++ /dev/null @@ -1,805 +0,0 @@ -""" -Olympus 回合生成器核心 — 正向生成 - -流程: 生成 cascade → 自然累计 → 总分落桶 -核心规则(与 olympus/engine.py 对齐): - 1. 一旋转内多次消除,中间步只记基础分,末帧统一引爆炸弹 - 2. 炸弹倍数跨旋转累计(accumulator 贯穿整个回合:基础旋转 + 免费旋转) - 3. 每步 accumulated_symbols 记录本旋转累计消除符号 - 4. 落桶用理论总分(最高 1370),发放封顶 1000(末步截断 + win_capped) -""" -import json -import random -from collections import Counter - -from olympus.config import ( - SYMBOL_RULES, SHORT_TO_SYMBOL, SCATTER_SHORT, BOMB_SHORT, - SYMBOL_WEIGHTS, BOMB_CELL_PROBABILITY, BOMB_VALUES, BOMB_WEIGHT_POWER, - GRID_ROWS, GRID_COLS, GRID_SIZE, MIN_WIN_COUNT, - SCATTER_PAYOUTS, SCATTER_FREE_SPIN_TRIGGER, SCATTER_RETRIGGER_TRIGGER, - FREE_SPINS_AWARDED, FREE_SPIN_RETRIGGER_COUNT, MAX_FREE_SPINS, - BASE_FS_TRIGGER_PROB, BASE_NEARMISS_SCATTER, FS_TRIGGER_SCATTER, FREESPIN_SCATTER, - FS_SPIN_WIN_PROB, FS_CASCADE_CONTINUE_PROB, FS_CASCADE_MAX, FS_GUARANTEE_COMBOS, - FS_GUARANTEE_COST, FS_DRY_FORCE_PROB, FS_DRY_FORCE_HARD, - FS_SMALL_BOMB_MAX, FS_BIG_BOMB_TARGET, FS_SCREEN_BOMB_PROB, FS_MAX_BOMBS_PER_SPIN, - FS_ONLY_TARGET_MUL, - ORNAMENTS, MAX_WIN_MULTIPLIER, bomb_quality, bigwin_level, -) - -REGULAR_SHORTS = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'] -MAX_WIN = float(MAX_WIN_MULTIPLIER) # 发放封顶 1000 -BUCKET_CEIL = max(o['range_high'] for o in ORNAMENTS) # 落桶理论上限 1370 - -# 保底消除预算(1 倍底注口径):最小档 0.25,连消后期望 1/(1-p) 波 -FS_FLOOR_UNIT = min(c[2] for c in FS_GUARANTEE_COMBOS) -FS_FLOOR_SPEND = FS_FLOOR_UNIT / (1.0 - FS_CASCADE_CONTINUE_PROB) # ≈0.45/旋 -FS_FLOOR_BUDGET_CAP = FREE_SPINS_AWARDED * FS_FLOOR_SPEND # ≈6.8/局 - - -# ==================================================================== -# 工具 -# ==================================================================== - -def _payout(short, cnt): - return float(SYMBOL_RULES[SHORT_TO_SYMBOL[short]].payout_for(cnt)) - - -def _weighted_sym(exclude=()): - syms = [s for s in REGULAR_SHORTS if s not in exclude] - wts = [SYMBOL_WEIGHTS[s] for s in syms] - r = random.uniform(0, sum(wts)) - a = 0.0 - for s, w in zip(syms, wts): - a += w - if r <= a: - return s - return syms[-1] - - -def _weighted_choice(dist): - r = random.random() - cum, last = 0.0, None - for k, p in dist.items(): - last = k - cum += p - if r <= cum: - return k - return last - - -def _bucket(score): - """理论总分落桶(score 可到 1370+,超出归顶桶)""" - if score <= 0: - return 1 - if score > BUCKET_CEIL: - return ORNAMENTS[-1]['id'] - for o in ORNAMENTS: - if o['range_low'] <= score <= o['range_high']: - return o['id'] - for o in ORNAMENTS[1:]: - if score <= o['range_high']: - return o['id'] - return ORNAMENTS[-1]['id'] - - -def _gen_bomb_value(max_val=None): - opts = [v for v in BOMB_VALUES if max_val is None or v <= max_val] - wts = [1.0 / (v ** BOMB_WEIGHT_POWER) for v in opts] - r = random.uniform(0, sum(wts)) - a = 0.0 - for v, w in zip(opts, wts): - a += w - if r <= a: - return v - return opts[-1] - - -def _bomb_combos(target): - """列出 ≤3 颗炸弹、和最接近 target 的所有组合:(误差, 组合),按误差升序""" - out = [] - vals = BOMB_VALUES - for a in vals: - out.append((abs(a - target), [a])) - if a > target * 1.5: - break - for a in vals: - if a > target: - break - for b in vals: - s = a + b - if s > target * 1.5: - break - out.append((abs(s - target), [a, b])) - for a in vals: - if a > target: - break - for b in vals: - if a + b > target: - break - for c in vals: - s = a + b + c - if s > target * 1.5: - break - out.append((abs(s - target), [a, b, c])) - out.sort(key=lambda x: (x[0], len(x[1]))) - return out - - -def _best_bombs(target): - """确定性最优(只用于评估方案误差)""" - combos = _bomb_combos(target) - return combos[0][1] if combos else [2] - - -def _decompose_best(target): - """在"接近最优"的组合里随机挑一个,并强烈偏向单颗 —— 一屏一颗, - 玩家才看得出"消一次爆一颗、倍数一颗一颗涨";改前每次都返回同一套 - (比如某些目标永远是 [3] 或 [3,6]),看起来"消除必定带 3 和 6 倍炸弹"。""" - combos = _bomb_combos(target) - if not combos: - return [2] - best_err = combos[0][0] - pool = [c for e, c in combos if e <= best_err + max(0.5, best_err * 0.15)] - weights = [1.0 / (len(c) ** 3) for c in pool] # 1 颗 : 2 颗 : 3 颗 = 1 : 1/8 : 1/27 - return random.choices(pool, weights=weights)[0] - - -# ==================================================================== -# 网格 -# ==================================================================== - -def _empty_grid(): - return [[''] * GRID_COLS for _ in range(GRID_ROWS)] - - -def _count(grid): - c = Counter() - for row in grid: - for cell in row: - if cell: - c[cell] += 1 - return c - - -def _winners(grid): - w = {} - for sym, cnt in _count(grid).items(): - if sym in (BOMB_SHORT, SCATTER_SHORT, ''): - continue - if cnt >= MIN_WIN_COUNT: - pay = _payout(sym, cnt) - if pay > 0: - w[sym] = (cnt, pay) - return w - - -def _no_extra(grid, allowed): - """打散计划外的 ≥8 同符号组""" - backup = [s for s in REGULAR_SHORTS if s not in allowed] or ['I'] - for _ in range(50): - dirty = False - for sym, cnt in _count(grid).items(): - if sym in (BOMB_SHORT, SCATTER_SHORT, '') or sym in allowed: - continue - if cnt >= MIN_WIN_COUNT: - excess = cnt - (MIN_WIN_COUNT - 1) - for r in range(GRID_ROWS): - for c in range(GRID_COLS): - if grid[r][c] == sym and excess > 0: - grid[r][c] = random.choice(backup) - excess -= 1 - dirty = True - if not dirty: - break - - -def _fill(grid, mul_map, allow_bomb, track=False, exclude_syms=(), - bomb_prob=BOMB_CELL_PROBABILITY, bomb_max=None, max_bombs=None): - """填充空格。allow_bomb 时按 bomb_prob 概率放炸弹(bomb_max 限制炸弹值、max_bombs 限制每屏颗数)。 - track 返回填充数据(仅普通符号)""" - filled = [] if track else None - placed = len(mul_map) - for r in range(GRID_ROWS): - for c in range(GRID_COLS): - if grid[r][c] == '': - pos = r * GRID_COLS + c - if allow_bomb and random.random() < bomb_prob \ - and (max_bombs is None or placed < max_bombs): - grid[r][c] = BOMB_SHORT - mul_map[pos] = _gen_bomb_value(bomb_max) - placed += 1 - else: - sym = _weighted_sym(exclude_syms) - grid[r][c] = sym - if track: - filled.append({"pos": pos + 1, "symbol": sym}) - return filled - - -def _collapse(grid, remove, mul_map): - """消除指定位置(0-based),重力下落。炸弹/散布随位置移动,mul_map 同步重映射""" - rs = set(remove) - drops = [] - ng = _empty_grid() - pos_remap = {} - for c in range(GRID_COLS): - surviving = [] - for r in range(GRID_ROWS - 1, -1, -1): - p = r * GRID_COLS + c - if p not in rs: - surviving.append((p, grid[r][c])) - for i, (old_pos, sym) in enumerate(surviving): - new_r = GRID_ROWS - 1 - i - new_pos = new_r * GRID_COLS + c - ng[new_r][c] = sym - pos_remap[old_pos] = new_pos - if new_pos != old_pos: - drops.append({"from": old_pos + 1, "to": new_pos + 1, "symbol": sym}) - new_mul = {pos_remap[p]: v for p, v in mul_map.items() if p in pos_remap} - return ng, drops, new_mul - - -def _place_scatter(grid, count, protect=()): - cand = [(r, c) for r in range(GRID_ROWS) for c in range(GRID_COLS) - if grid[r][c] not in ({SCATTER_SHORT, BOMB_SHORT} | set(protect))] - for r, c in random.sample(cand, min(count, len(cand))): - grid[r][c] = SCATTER_SHORT - - -def _count_scatter(grid): - return sum(1 for row in grid for cell in row if cell == SCATTER_SHORT) - - -def _force_small_win(grid, prefer=()): - """就地补一组最小档消除(默认 8 连低价值符号),不覆盖已有 S/M 格。 - - prefer = 这一波"刚落下 / 刚补位"的格子(0 基)。保底消除优先种在这些格子上: - 它们本来就有 drop/fill 记录,前端按动画数据落地就能与 grid_state 完全一致; - 种在**没移动过**的格子上则没有任何动画记录,前端只能靠 grid_state 兜底。""" - sym, cnt, _ = random.choice(FS_GUARANTEE_COMBOS) - free = [p for p in range(GRID_SIZE) - if grid[p // GRID_COLS][p % GRID_COLS] not in (SCATTER_SHORT, BOMB_SHORT)] - if len(free) < cnt: - return - fresh = [p for p in prefer if p in set(free)] - if len(fresh) >= cnt: - picks = random.sample(fresh, cnt) - else: - rest = [p for p in free if p not in set(fresh)] - picks = fresh + random.sample(rest, cnt - len(fresh)) - for p in picks: - grid[p // GRID_COLS][p % GRID_COLS] = sym - _no_extra(grid, {sym}) - - -def _scatter_payout(count): - if count >= SCATTER_FREE_SPIN_TRIGGER: - return float(SCATTER_PAYOUTS.get(min(count, 6), 0)) - return 0.0 - - -# ==================================================================== -# 强制中奖(bias 补桶用) -# ==================================================================== - -# (基础分, 组合) 按基础分降序 -_BASE_OPTIONS = [ - (60.0, [('A', 12), ('B', 10)]), - (50.0, [('A', 12)]), - (35.0, [('A', 10), ('B', 10)]), - (25.0, [('A', 10)]), - (12.5, [('A', 8), ('B', 8)]), - (10.0, [('A', 8)]), - (5.0, [('C', 10)]), - (3.5, [('C', 8), ('D', 8)]), - (2.5, [('B', 8)]), - (2.0, [('C', 8)]), - (1.5, [('D', 8)]), - (1.0, [('E', 8)]), - (0.8, [('F', 8)]), - (0.5, [('G', 8)]), - (0.4, [('H', 8)]), - (0.25, [('I', 8)]), -] - - -def _plan_for(target): - """全局搜索 (基础组合, 炸弹列表),使 base × max(1, Σ炸弹) 最接近 target。 - - 在"误差在最优 +5 个百分点以内"的若干方案里随机挑一个 —— 改前永远返回同一套最优解, - 所以同一档位的回合盘面/炸弹老是那几个("某个符号消除必定带着 3 和 6 倍炸弹")。""" - if target <= 0: - return [], [] - plans = [] - for base, combo in _BASE_OPTIONS: - plans.append((abs(base - target) / target, combo, None)) # 方案A:无炸弹 - m = target / base - if m >= 2: # 方案B:配炸弹 - achieved = base * sum(_best_bombs(m)) - plans.append((abs(achieved - target) / target, combo, m)) - plans.sort(key=lambda p: p[0]) - best_err = plans[0][0] - pool = [p for p in plans if p[0] <= best_err + 0.05] - _, combo, m = random.choice(pool) - return combo, ([] if m is None else _decompose_best(m)) - - -def _plan_base_only(target): - """只挑基础组合(不植炸弹):在接近最优的组合里随机。acc 已经提供倍数时用这个。""" - if target <= 0: - return [('I', 8)] - cands = sorted((abs(base - target) / target, combo) for base, combo in _BASE_OPTIONS) - best = cands[0][0] - pool = [c for e, c in cands if e <= best + 0.05] - return random.choice(pool) - - -def _force_win(grid, target, mul_map, acc=0.0, allow_bomb=True): - """把 target 当"本旋最终赢额"来造奖:先按当前累计倍数 acc 折算需要的基础分。 - - 买单旋赢额 = 基础分 × max(1, acc + 本旋炸弹),所以: - · acc 已经 >1 时只挑基础分、**不再植炸弹**(否则 acc 和炸弹的倍数会被算两次, - 实测把 10 倍目标打成 40~50 倍); - · 该模式不允许炸弹(普通旋转/底注)时同理只挑基础分; - · 其余情况(buy_fs 且 acc=0)按原逻辑植炸弹,大奖局(100 倍以上)靠这一步才能到。""" - need_base = target / max(1.0, acc) if acc > 0 else target - if need_base >= 0.25: - if acc > 1 or not allow_bomb: - combo, bombs = _plan_base_only(need_base), [] - else: - combo, bombs = _plan_for(need_base) - else: - combo, bombs = [('I', 8)], [] # acc 本身已超过目标:只给最小档,不再加炸弹 - used = set() - win_syms = set() - for sym, cnt in combo: - win_syms.add(sym) - free = [p for p in range(GRID_SIZE) if p not in used] - for p in random.sample(free, min(cnt, len(free))): - r, c = divmod(p, GRID_COLS) - grid[r][c] = sym - used.add(p) - free = [p for p in range(GRID_SIZE) if p not in used] - for v in bombs: - if not free: - break - p = free.pop(random.randrange(len(free))) - r, c = divmod(p, GRID_COLS) - grid[r][c] = BOMB_SHORT - mul_map[p] = v - used.add(p) - for p in range(GRID_SIZE): - r, c = divmod(p, GRID_COLS) - if grid[r][c] == '': - grid[r][c] = _weighted_sym(exclude=win_syms) - _no_extra(grid, win_syms) - return win_syms - - -# ==================================================================== -# 核心:一次旋转的 cascade 链 -# ==================================================================== - -def _spin(mode_code, bias=None, acc=0, is_free_spin=False, target_scatter=0, - allow_bomb=True, win_prob=0.0, continue_prob=0.0, cascade_max=0, - bomb_prob=None, bomb_max=None, max_bombs=None): - """ - 一次旋转(含多次 cascade)。 - 炸弹跨 cascade 保留不消除;末帧统一结算: total = base_sum * max(1, acc+bomb_sum) - acc 只在本旋转有消除时累加炸弹(与 engine.end_spin 一致)。 - allow_bomb=False 时初始盘也不出炸弹;级联补位一律不出。 - **炸弹只存在于免费旋转内**:普通旋转/底注的基础旋(含进 FS 的那张触发盘)一律不出炸弹; - 三个模式进 FS 后规则完全一致(炸弹 + 累计器 + 每旋保底消除)。 - win_prob/continue_prob:本盘凑不出消除时按概率补最小档消除(首盘/级联各自取一个), - cascade_max 限制单旋最大波数 —— 让 FS 里"每旋都在消"。 - 返回 (steps, spin_total, acc_out, scatter_count) - """ - if not is_free_spin: - allow_bomb = False - steps = [] - seq = 0 - mul_map = {} # {pos0: bomb_val} 跨 cascade 累积 - if bomb_prob is None: - bomb_prob = BOMB_CELL_PROBABILITY - # 本旋是不是"大奖旋"(补桶植入目标额的那一旋):只有它按累计倍数结算 - plant_spin = isinstance(bias, (int, float)) and bias > 0 - - grid = None - prev_win_pos = [] - sym_acc = Counter() # 本旋转累计消除符号 - spin_winners = [] # 落库留档用(接口已不下发,面板由 symbol_links 汇总) - spin_links = [] # 本旋累积连线上报:[{symbol,count,loc,base_score,tumble}],新旋清空 - total_base = 0.0 - recorded = 0.0 # 已记入前置步骤的赢额 - removed, drops, fills = [], [], [] - acc_before_spin = acc - - while True: - seq += 1 - if seq == 1: - grid = _empty_grid() - if isinstance(bias, (int, float)) and bias > 0: - win_syms = _force_win(grid, float(bias), mul_map, acc, allow_bomb) - else: - _fill(grid, mul_map, allow_bomb, bomb_prob=bomb_prob, bomb_max=bomb_max, - max_bombs=max_bombs) - if bias == 'zero': - _no_extra(grid, set()) - if target_scatter > 0: - _place_scatter(grid, target_scatter, protect=set(_winners(grid))) - removed, drops, fills = [], [], [] - else: - removed = [p + 1 for p in prev_win_pos] - grid, drops, mul_map = _collapse(grid, prev_win_pos, mul_map) - fills = _fill(grid, mul_map, allow_bomb=False, track=True) - - # 保底消除:本盘自然凑不出 8 连时,按概率补一组最小档(首盘用 win_prob,级联用 continue_prob) - prob = win_prob if seq == 1 else continue_prob - if prob > 0 and (not cascade_max or seq < cascade_max) \ - and random.random() < prob and not _winners(grid): - _force_small_win(grid, prefer=[f['pos'] - 1 for f in fills]) - - # 保底消除 / 打散会覆盖"刚落下"或"刚补位"的格子 —— 这两份动画数据里的 symbol 按**最终盘面** - # 回填,前端按"消→落→补"重建出来的盘面才与 grid_state 完全一致(否则会画出不对的符号)。 - if drops: - drops = [dict(d, symbol=grid[(d['to'] - 1) // GRID_COLS][(d['to'] - 1) % GRID_COLS]) - for d in drops] - if fills: - fills = [dict(f, symbol=grid[(f['pos'] - 1) // GRID_COLS][(f['pos'] - 1) % GRID_COLS]) - for f in fills] - - w = _winners(grid) - is_final = (len(w) == 0) - - base = 0.0 - win_pos = [] - mul_det = [{"pos": p + 1, "val": v, "quality": bomb_quality(v)} - for p, v in sorted(mul_map.items())] - bomb_sum = sum(mul_map.values()) - - # symbol_links = **本旋累积**的连线上报(每波消除往上追加,tumble 标明是旋内第几波)—— - # 它同时充当"获胜者面板"(前端按 symbol 汇总即可),所以 spin_winners 字段已删。 - for sym, (cnt, pay) in sorted(w.items()): - pos_list = [r * GRID_COLS + c for r in range(GRID_ROWS) - for c in range(GRID_COLS) if grid[r][c] == sym][:cnt] - base += pay - win_pos.extend(pos_list) - sym_acc[sym] += cnt - spin_links.append({ - 'symbol': sym, 'count': cnt, - 'loc': [p + 1 for p in pos_list], - 'base_score': round(pay, 4), - 'tumble': seq, # 本旋第几波消除(= 本步 tumbleNum) - }) - total_base += base - - # 末帧:统一引爆(一旋转内不管几次消除,消完才爆) - # 累计器仅免费旋转有效;基础旋转炸弹只乘本旋赢额、不留存 - scatter_win = 0.0 - scatter_count = 0 - acc_after = acc - if is_final: - scatter_count = _count_scatter(grid) - scatter_win = _scatter_payout(scatter_count) - if total_base > 0: - if is_free_spin: - acc_after = acc + bomb_sum # FS:炸弹并入累计器,跨旋传递 - # 本旋赢额 = 基础分 × max(1, 累计 + 本旋炸弹) —— 累计倍数作用到每一次消除 - # (玩家要的就是这个滚雪球:0.4×2 + 0.5×4 = 2.8) - effective = max(1, acc_after) - else: - acc_after = acc # 基础旋:无累计器 - effective = max(1, bomb_sum) - win_amt = total_base * effective - recorded + scatter_win - else: - win_amt = scatter_win # 无消除→炸弹作废、累计不变 - else: - win_amt = base # 中间步只记基础分 - recorded += win_amt if not is_final else 0.0 - - # scatter 触发/重触发(仅末帧) - scatter_trigger, retrigger_info = None, None - if is_final: - if is_free_spin and scatter_count >= SCATTER_RETRIGGER_TRIGGER: - retrigger_info = {'scatterCount': scatter_count, - 'extraSpins': FREE_SPIN_RETRIGGER_COUNT, - 'scatterWin': round(scatter_win, 2)} - elif not is_free_spin and scatter_count >= SCATTER_FREE_SPIN_TRIGGER: - scatter_trigger = {'scatterCount': scatter_count, - 'freeSpins': FREE_SPINS_AWARDED, - 'scatterWin': round(scatter_win, 2)} - - steps.append({ - 'cascade_seq': seq, 'free_spin_id': 0, - 'grid_state': [row[:] for row in grid], - 'multiplier_detail': json.dumps(mul_det), - 'symbol_links': json.dumps(spin_links, ensure_ascii=False), # 本旋累积(含 tumble) - 'accumulated_symbols': json.dumps(dict(sym_acc)), # 仅落库留档,不下发 - 'spin_winners': json.dumps(spin_winners, ensure_ascii=False), # 仅落库留档,不下发 - 'removed_positions': json.dumps(removed), # 仅落库留档,不下发 - 'drop_data': json.dumps(drops), - 'fill_data': json.dumps(fills if fills else []), - 'scatter_trigger': json.dumps(scatter_trigger) if scatter_trigger else None, - 'retrigger_info': json.dumps(retrigger_info) if retrigger_info else None, - 'base_score': round(base, 2), # 仅落库留档,不下发 - 'win_amount': round(win_amt, 2), - 'accumulator_before': round(acc_before_spin, 2), - 'accumulator_after': round(acc_after if is_final else acc, 2), - 'spin_win': 0, 'win_capped': 0, 'bigwin_level': None, - 'is_final': 1 if is_final else 0, - }) - - if is_final: - total = round(sum(s['win_amount'] for s in steps), 2) - return steps, total, acc_after, scatter_count - prev_win_pos = win_pos - - -# ==================================================================== -# 免费旋转会话(15 次起,≥3S 重触发 +5,上限 30) -# ==================================================================== - - -def _fs_next_target(spin_no, wins, total_spins, quota): - """下一旋"该保底消除"的位置:间隔随机,但均值 = 剩余旋数 / 剩余配额。 - - 为什么要随机:等距铺(每 quota/总局数 旋必消一次)会让玩家几局就看穿"到第 4 旋肯定消"—— - 实测等距版本有 87% 的局"最长连空正好 4 旋"。这里改成每次消除后重抽下一次的位置: - 间隔 = round(均值 + δ),δ ∈ {-2..2} 权重 {1,2,3,2,1}(均值 0,所以整局的消除次数不变), - 玩家看到的空转长度在 1~5 旋之间跳,而不是恒定 4。剩余配额为 0 时返回 None(后面只靠自然消)。 - """ - left_spins = total_spins - spin_no - left_quota = quota - wins - if left_spins <= 0 or left_quota <= 0: - return None - delta = random.choices((-2, -1, 0, 1, 2), weights=(1, 2, 3, 2, 1))[0] - return spin_no + max(1, int(round(left_spins / left_quota + delta))) - - -def _free_spin_session(mode_code, slot_target=None, acc=0, fs_budget=None): - """免费旋转会话(15 次起,≥3S 重触发 +5,上限 30)。 - - **三个模式同一套规则**(炸弹 + 累计器 + 每旋保底消除),区别只在"这一局值多少钱": - · buy_fs:价格换来桶目标,拆成「保底消除预算 fs_budget + 一旋大奖 slot_target」; - · normal/ante 自然触发:没有价格可拆 → 只给一份同档保底预算、不植大奖旋, - 整局赔付落在哪由自然结果决定。 - fs_budget 按"配额 + 随机间隔"花:预算能买 k 次消除(k ≈ √(预算/FS_GUARANTEE_COST)), - 每次消除后重抽下一次的位置(间隔随机、均值 = 剩余旋数/剩余配额),自然消除也算一次进度; - 空转太久再按 FS_DRY_FORCE_PROB 软兜底 —— 旧写法按"剩余预算÷剩余旋数"算每旋概率,累计倍数 - 一涨就把预算烧穿,后半局十几旋连空;等距铺法又会让玩家看穿"到第 4 旋肯定消"(见 _fs_next_target)。 - slot_target 只作用于其中随机一旋(它按累计倍数结算,不占保底配额)。 - """ - total_spins = FREE_SPINS_AWARDED - fs_index = 0 - all_steps = [] - fs_total = 0.0 - sc_dist = FREESPIN_SCATTER.get(mode_code, FREESPIN_SCATTER['default']) - budget = fs_budget - # 炸弹大小按档位配:低倍局用小球(预算买得起多次"消除+爆"),高倍局用全量(2~1000 的大球) - target_mul = float(slot_target) if isinstance(slot_target, (int, float)) else 0.0 - spin_bomb_max = None if target_mul >= FS_BIG_BOMB_TARGET else FS_SMALL_BOMB_MAX - bias_slot = random.randrange(FREE_SPINS_AWARDED) if slot_target else -1 - # 保底配额:预算 ÷ 单次消除边际赔付再开方 = 这一局"买得起"几次消除 - quota = None if budget is None else max(1, int((max(0.0, budget) / FS_GUARANTEE_COST) ** 0.5)) - wins = 0 # 已消除的旋数(自然 + 保底) - dry = 0 # 已经连续空转了几旋(兜底用) - target = None if quota is None else _fs_next_target(0, 0, total_spins, quota) - while fs_index < total_spins: - fs_index += 1 - is_target_slot = (fs_index - 1) == bias_slot - spin_bias = slot_target if is_target_slot else None - forced = False - if spin_bias is None and quota is not None: - # 到点了 / 空转太久被软兜底抽中 → 本旋必补一次保底消除 - forced = target is not None and fs_index >= target - if not forced and dry >= FS_DRY_FORCE_HARD: - forced = True - elif not forced and dry in FS_DRY_FORCE_PROB: - forced = random.random() < FS_DRY_FORCE_PROB[dry] - p = 1.0 if forced else 0.0 - else: - p = FS_SPIN_WIN_PROB - sc = _weighted_choice(sc_dist) - # 已到 30 旋上限:这一旋不再落 3+ 个 S —— 否则会写出"extraSpins=5 但没有旋可加"的 - # retrigger_info(玩家白看一次 +5,前端还得自己夹 30)。上限之前的那一旋照常触发第 3 次 +5。 - if total_spins >= MAX_FREE_SPINS: - sc = min(sc, SCATTER_RETRIGGER_TRIGGER - 1) - spin_steps, spin_score, acc, sc_count = _spin( - mode_code, spin_bias, acc, is_free_spin=True, target_scatter=sc, - win_prob=p, continue_prob=FS_CASCADE_CONTINUE_PROB, - cascade_max=FS_CASCADE_MAX, - bomb_prob=FS_SCREEN_BOMB_PROB, bomb_max=spin_bomb_max, - max_bombs=FS_MAX_BOMBS_PER_SPIN) - if sc_count >= SCATTER_RETRIGGER_TRIGGER and total_spins < MAX_FREE_SPINS: - total_spins += FREE_SPIN_RETRIGGER_COUNT - # 目标大奖旋的花费由目标额单独控制,不占用保底预算 - if budget is not None and not is_target_slot: - budget -= spin_score - for s in spin_steps: - s['free_spin_id'] = fs_index - fs_total += spin_score - all_steps.extend(spin_steps) - # 进度:消了(不管自然还是保底)就重新抽下一次的位置 —— 整局消除次数仍由配额决定 - if spin_bias is None and quota is not None: - if any(float(s['base_score']) > 0 for s in spin_steps): - wins += 1 - dry = 0 - target = _fs_next_target(fs_index, wins, total_spins, quota) - else: - dry += 1 - return all_steps, fs_total - - -# ==================================================================== -# 主入口 -# ==================================================================== - -def generate_round(box, bias=None): - """ - 生成一个完整回合。box: {'box_id','box_type',...} bias: None/'zero'/目标倍率(float) - 返回 (round_data, steps, orn_id) - """ - mode_code = box['box_type'] - buy_fs = mode_code == 'buy_fs' - # 目标高于"基础旋(无炸弹)能赔到的上限"时,只能生成自然触发 FS 的回合来装这个桶: - # normal/ante 的 100x/150x/200x 桶没有别的来源(基础旋不出炸弹,最多 ~60x)。 - must_free_spins = isinstance(bias, (int, float)) and not buy_fs \ - and float(bias) >= FS_ONLY_TARGET_MUL - is_fs = False - if buy_fs: - is_fs = True # 购买模式保证进 FS(互斥:无底注效果) - elif must_free_spins: - is_fs = True - elif mode_code in BASE_FS_TRIGGER_PROB and bias is None: - is_fs = random.random() < BASE_FS_TRIGGER_PROB[mode_code] - - if is_fs: - # 触发盘:三模式都固定/摇出 SCATTER,且**不出炸弹**(炸弹只存在于免费旋转内) - trigger_sc = 4 if buy_fs else _weighted_choice(FS_TRIGGER_SCATTER) - base_steps, base_total, _, _ = _spin( - mode_code, None, 0, is_free_spin=False, target_scatter=trigger_sc, - allow_bomb=False) - # FS 预算:买 FS / 高倍桶(bias=桶目标)拆成「保底消除预算 + 一旋大奖」; - # normal/ante 自然摇出来的 FS(bias=None)没有价格可拆 → 只给同档保底预算、不植大奖旋, - # 整局赔付由自然消除 + 炸弹累计决定(实测约 80x、5 旋有消除,落在模式自己的桶里)。 - fs_budget, slot_target = None, None - if isinstance(bias, (int, float)) and bias > 0: - budget = max(0.0, float(bias) - float(base_total)) - if budget <= FS_FLOOR_BUDGET_CAP: - fs_budget = budget # 低倍局:全靠保底小消 - else: - fs_budget = FS_FLOOR_BUDGET_CAP # 保底小消是平价结算(×1),预算可以给足 - slot_target = budget - fs_budget # 其余给"大奖旋"(它按累计倍数结算) - elif not buy_fs: - fs_budget = FS_FLOOR_BUDGET_CAP - # 炸弹累计器仅免费旋转内生效:FS 会话从 0 起累,触发盘炸弹不带入 - fs_steps, fs_total = _free_spin_session( - mode_code, slot_target=slot_target, acc=0, fs_budget=fs_budget) - steps = base_steps + fs_steps - else: - nearmiss = _weighted_choice(BASE_NEARMISS_SCATTER.get(mode_code, {0: 1.0})) - steps, _, _, _ = _spin(mode_code, bias, 0, - is_free_spin=False, target_scatter=nearmiss) - - # 理论总分(落桶用,可到 1370+) - raw_total = round(sum(s['win_amount'] for s in steps), 2) - orn_id = _bucket(raw_total) - - # 发放截断 + 口径重写:累计到 1000 即终止,动画=到账零偏差。 - # win_amount = **本旋累计到本步的赢额**(该旋末步就是这一旋的总额); - # spin_win = **本局累计到本步的赢额**(最后一步 = 整局实付 = totalWin) - # 前端因此不需要自己相加:本步增量 = 与上一步相减,本旋总额 = 本旋末步的 win_amount。 - raw_increments = [s['win_amount'] for s in steps] - cumul, spin_cumul, capped_steps, hit = 0.0, {}, [], False - for s, inc in zip(steps, raw_increments): - if cumul + inc >= MAX_WIN: # 封顶:本步补足到 1000 并终止 - inc = round(MAX_WIN - cumul, 2) - s['win_capped'] = 1 - s['is_final'] = 1 - hit = True - key = s['free_spin_id'] - spin_cumul[key] = round(spin_cumul.get(key, 0.0) + inc, 2) - cumul = round(cumul + inc, 2) - s['win_amount'] = spin_cumul[key] # 本旋累计(末步即该旋总额) - s['spin_win'] = cumul # 本局累计 - capped_steps.append(s) - if hit: - break - if hit: - steps = capped_steps - paid_total = round(cumul, 2) - - # 爆奖动画(小/中/大):**只有免费旋转里才判档** —— normal/ante 靠自然触发 FS、 - # buy_fs 本身即 FS;基础旋(free_spin_id=0)一律 null,不播大奖动画。 - # 单旋赢额 = 该旋末帧的 win_amount;旋与旋互不影响,同档一局可多次播放。 - for s in steps: - if s['is_final'] and s['free_spin_id'] > 0: - s['bigwin_level'] = bigwin_level(mode_code, float(s['win_amount'])) - - fs_cnt = len({s['free_spin_id'] for s in steps if s['free_spin_id'] > 0}) - rd = { - 'box_id': box['box_id'], 'ornament_id': orn_id, - 'actual_mul': round(paid_total, 4), # 实付倍率 ≤1000 - 'free_spin_count': fs_cnt, - 'total_cascades': len(steps), - } - return rd, steps, orn_id - - -# ==================================================================== -# 分布补偿:两阶段填桶 -# ==================================================================== - -def _bias_for(orn_id): - if orn_id == 1: - return 'zero' - o = next(x for x in ORNAMENTS if x['id'] == orn_id) - return float(o['expect_mul']) - - -def _calibrate_bias(box, orn_id, samples=8): - """给某个桶在线找一个"更容易撞上"的 bias(只改传给生成器的目标值,不动任何规则)。 - - 为什么需要:`_bias_for` 用的是桶的名义 expect_mul,但 buy_fs 的整局赔付被 FS 会话抬高了 —— - 实测 bias=10(桶 7 名义 10x)时实际赔付 min 14 / 中位 58,**永远撞不进 8~12**,命中率仅 3%, - stage2 只能白跑几万次(单箱 5~10 分钟)。这里拿几个候选值各试几局,挑命中率最高的那个。""" - target = _bias_for(orn_id) - best, best_hit = target, -1 - for mult in (1.0, 0.5, 0.3, 0.15, 0.08): - b = target * mult - hit = 0 - for _ in range(samples): - _, _, oid = generate_round(box, b) - hit += (oid == orn_id) - if hit > best_hit: - best, best_hit = b, hit - if hit == samples: # 已经全中,不用再试更小/更大的 - break - return best - - -def fill_distribution(box, target_counts, max_rounds=None): - """阶段1 自由生成积累常见桶;阶段2 按缺额 bias 补桶。返回 (rounds, 实际分布) - - 阶段2 的尝试上限按 total×60:保底消除改成"随机间隔"之后,低倍桶(10x/16x)的命中率大约减半 - (同样的钱摊到整局、摊到累计倍数高的旋上更贵),尝试数不够会填不满池子(实测 ×30 时 - 10x 桶只填到 178/297,×60 满池)。 - 某个桶饿死时(试了 4 倍于它条数的次数还没填满)会重新校准它的 bias —— 只对慢桶生效, - 命中率本来就高的桶走原来的名义值,不额外花钱。""" - gen = Counter() - rounds = [] - total_target = sum(target_counts.values()) - bias_use = {} # orn_id -> 校准后的 bias - tries = Counter() # orn_id -> 已尝试次数 - - def _accept(oid): - if oid in target_counts and gen[oid] < target_counts[oid]: - gen[oid] += 1 - return True - return False - - free_budget = min(total_target * 3, 12000) - for _ in range(free_budget): - if sum(gen.values()) >= total_target: - break - rd, steps, oid = generate_round(box, None) - if _accept(oid): - rounds.append((rd, steps)) - - max_att = (max_rounds or total_target * 60) - for _ in range(max_att): - deficit = [(oid, target_counts[oid] - gen[oid]) - for oid in target_counts if gen[oid] < target_counts[oid]] - if not deficit: - break - # 按缺口加权随机选桶,避免“最难桶”饿死其他桶 - need = random.choices([d[0] for d in deficit], - weights=[d[1] for d in deficit])[0] - tries[need] += 1 - if need not in bias_use and tries[need] > target_counts[need] * 4: - bias_use[need] = _calibrate_bias(box, need) - rd, steps, oid = generate_round(box, bias_use.get(need, _bias_for(need))) - if _accept(oid): - rounds.append((rd, steps)) - - return rounds, dict(gen) diff --git a/olympus-py/pyproject.toml b/olympus-py/pyproject.toml deleted file mode 100644 index 5c8fb9c..0000000 --- a/olympus-py/pyproject.toml +++ /dev/null @@ -1,16 +0,0 @@ -[build-system] -requires = ["setuptools>=61", "wheel"] -build-backend = "setuptools.build_meta" - -[project] -name = "olympus" -version = "0.1.0" -description = "Olympus match-3 game engine with bomb accumulator rules." -readme = "README.md" -requires-python = ">=3.9" -license = {text = "Proprietary"} - -[tool.setuptools.packages.find] -where = ["."] -include = ["olympus*"] -namespaces = false diff --git a/olympus-py/schema.sql b/olympus-py/schema.sql deleted file mode 100644 index 1bae08f..0000000 --- a/olympus-py/schema.sql +++ /dev/null @@ -1,240 +0,0 @@ --- ============================================================ --- Olympus 电子游戏模块 全量建表 DDL --- 表前缀: game_olympus_ 游戏类型: OLYMPUS --- 设计对齐 sweet 体系,差异点: --- 1. 饰品桶最高 1000x(单局封顶 1000 倍基础下注) --- 2. step 表自带 accumulator_before/after(跨回合炸弹累计)与 win_capped --- 3. record 表自带结算/补发字段(settled 等),无需后期 ALTER --- 4. 模式仅三种: normal / ante / buy_fs(互斥,无 super 模式) --- ============================================================ - --- 1. 箱子配置(模式 × 下注档位) -CREATE TABLE IF NOT EXISTS game_olympus_box ( - box_id BIGINT NOT NULL COMMENT 'PK', - game_type VARCHAR(16) NOT NULL DEFAULT 'OLYMPUS', - box_type VARCHAR(32) NOT NULL COMMENT 'normal|ante|buy_fs', - bet DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '基础下注', - name VARCHAR(50) DEFAULT NULL, - price DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '实际扣费 = bet × CM (normal=1/ante=2/buy_fs=20)', - total_ornaments INT NOT NULL DEFAULT '0' COMMENT '总回合数', - rtp DECIMAL(5,4) NOT NULL DEFAULT '0.8000' COMMENT '目标返奖率', - rtp_converge_ratio DECIMAL(4,3) NULL COMMENT '一轮收敛回合占比,空=默认0.15', - sort INT DEFAULT '0', - status VARCHAR(1) DEFAULT '1' COMMENT '1启用 0禁用', - del_flag VARCHAR(1) DEFAULT '0', - remark VARCHAR(200) DEFAULT NULL, - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (box_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Olympus箱子配置'; - --- 2. 饰品桶定义(0x ~ 1000x,共17档) -CREATE TABLE IF NOT EXISTS game_olympus_ornament ( - id BIGINT NOT NULL COMMENT 'PK 1-17', - game_type VARCHAR(16) NOT NULL DEFAULT 'OLYMPUS', - expect_mul DECIMAL(8,2) NOT NULL COMMENT '期望倍率(最高1000)', - name VARCHAR(50) DEFAULT NULL, - range_low DECIMAL(8,2) DEFAULT NULL COMMENT '落桶区间下限(含)', - range_high DECIMAL(8,2) DEFAULT NULL COMMENT '落桶区间上限(含)', - PRIMARY KEY (id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Olympus饰品桶定义'; - --- 3. 箱子-饰品桶分布(库存配额,剩余 = count - used) --- audience 多身份爆率:每种身份(common/anchor/robot)一套独立分布,回合库存池共享 -CREATE TABLE IF NOT EXISTS game_olympus_box_ornament ( - id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'PK', - box_id BIGINT NOT NULL, - ornament_id INT NOT NULL, - audience VARCHAR(16) NOT NULL DEFAULT 'common' COMMENT '身份维度 common普通/anchor主播/robot机器人', - count INT NOT NULL DEFAULT '0' COMMENT '分配回合数', - used INT NOT NULL DEFAULT '0' COMMENT '已消耗回合数,剩余=count-used', - PRIMARY KEY (id), - UNIQUE KEY uk_box_ornament_aud (box_id, ornament_id, audience) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Olympus箱子饰品分布(多身份)'; - --- 4. 预生成回合 -CREATE TABLE IF NOT EXISTS game_olympus_round ( - round_id BIGINT NOT NULL COMMENT 'PK', - box_id BIGINT NOT NULL, - ornament_id INT NOT NULL COMMENT '落入的饰品桶 1-17', - actual_mul DECIMAL(10,4) NOT NULL DEFAULT '0.0000' COMMENT '实际总倍率(≤1000)', - free_spin_count INT NOT NULL DEFAULT '0' COMMENT '免费旋转总次数(15+重触发×5)', - total_cascades INT NOT NULL DEFAULT '0' COMMENT '总步数', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (round_id), - INDEX idx_box_ornament (box_id, ornament_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Olympus预生成回合'; - --- 5. 回合步骤(级联/翻滚明细) -CREATE TABLE IF NOT EXISTS game_olympus_step ( - id BIGINT NOT NULL COMMENT 'PK', - round_id BIGINT NOT NULL, - free_spin_id INT NOT NULL DEFAULT '0' COMMENT '0=基础旋转, >0=第N次免费旋转', - cascade_seq INT NOT NULL DEFAULT '0' COMMENT '本旋转内第几次翻滚', - grid_state JSON DEFAULT NULL COMMENT '6×5网格最终状态', - multiplier_detail JSON DEFAULT NULL COMMENT '炸弹符号 [{"pos":int,"val":int},...]', - symbol_links JSON DEFAULT NULL COMMENT '中奖明细 [{"symbol","count","loc",...}]', - accumulated_symbols JSON DEFAULT NULL COMMENT '本旋转累计消除符号 {"CROWN":9,...}', - spin_winners JSON DEFAULT NULL COMMENT '获胜者面板:本旋逐波追加 [{"symbol","count","win"},...] 新旋清空', - removed_positions JSON DEFAULT NULL COMMENT '消除位置 [pos,...]', - drop_data JSON DEFAULT NULL COMMENT '下落数据 [{"from":int,"to":int,"symbol":"A"},...]', - fill_data JSON DEFAULT NULL COMMENT '填充数据 [{"pos":int,"symbol":"A"},...]', - scatter_trigger JSON DEFAULT NULL COMMENT '基础盘触发免费旋转 {scatterCount,freeSpins(15),scatterWin}', - retrigger_info JSON DEFAULT NULL COMMENT '免费旋转重触发 {scatterCount,extraSpins(5),scatterWin}', - base_score DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '本步基础分(未乘炸弹)', - win_amount DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '本步赢额(回合末帧含炸弹结算)', - accumulator_before DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '本步前累计炸弹倍数', - accumulator_after DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '本步后累计炸弹倍数(跨回合传递)', - spin_win DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '整局累计赢额', - win_capped TINYINT NOT NULL DEFAULT '0' COMMENT '本步赢额被1000x封顶截断 0否1是', - bigwin_level VARCHAR(16) DEFAULT NULL COMMENT '爆奖档位(小/中/大,只在免费旋转里判档,按单旋赢额盖在该旋末帧,同档可多次): small/middle/big', - is_final TINYINT DEFAULT '0' COMMENT '本旋转末帧', - PRIMARY KEY (id), - INDEX idx_round (round_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Olympus回合步骤'; - --- 6. 游戏记录(含结算/补发字段,建表即带全) -CREATE TABLE IF NOT EXISTS game_olympus_record ( - id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'PK', - user_id BIGINT NOT NULL, - box_id BIGINT NOT NULL, - round_id BIGINT NOT NULL, - cost DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '扣费 = bet × CM', - total_win DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '总赢额(结算时填,≤bet×1000)', - settled TINYINT NOT NULL DEFAULT '0' COMMENT '是否已结算 0未1已(幂等守卫)', - mode_type VARCHAR(20) DEFAULT NULL COMMENT '游戏模式 normal/ante/buy_fs', - auto_dismantle TINYINT DEFAULT 0 COMMENT '开局自动分解快照 0关1开', - reward_type VARCHAR(10) DEFAULT NULL COMMENT '发放类型 gold/ornament', - settled_win DECIMAL(10,2) DEFAULT 0.00 COMMENT '本局价值(金币金额=饰品价格)', - prize_id BIGINT DEFAULT NULL COMMENT '饰品ID game_olympus_prize_ornament.id', - prize_name VARCHAR(100) DEFAULT NULL COMMENT '饰品名称', - prize_image_url VARCHAR(500) DEFAULT NULL COMMENT '饰品图片URL', - prize_level_img VARCHAR(500) DEFAULT NULL COMMENT '品质图片URL', - settle_source VARCHAR(20) DEFAULT NULL COMMENT '结算来源 start=开局已结算(动画还在播) / finish=末帧播完(已结束,进战绩列表)', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (id), - INDEX idx_user_time (user_id, create_time), - INDEX idx_box_time (box_id, create_time), - INDEX idx_settled_time (settled, create_time) -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='Olympus游戏记录'; - --- 7. 用户统计 -CREATE TABLE IF NOT EXISTS game_olympus_user ( - id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'PK', - user_id BIGINT NOT NULL, - total_spins BIGINT NOT NULL DEFAULT '0', - total_cost DECIMAL(12,2) NOT NULL DEFAULT '0.00', - total_win DECIMAL(12,2) NOT NULL DEFAULT '0.00', - total_free_spins INT NOT NULL DEFAULT '0', - max_win_mul DECIMAL(10,4) NOT NULL DEFAULT '0.0000' COMMENT '历史最高倍率(≤1000)', - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (id), - UNIQUE KEY uk_user (user_id) -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='Olympus用户统计'; - --- 8. 每日盈亏 -CREATE TABLE IF NOT EXISTS game_olympus_win ( - id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'PK', - date DATE NOT NULL, - total_spins BIGINT NOT NULL DEFAULT '0', - total_cost DECIMAL(14,2) NOT NULL DEFAULT '0.00', - total_win DECIMAL(14,2) NOT NULL DEFAULT '0.00', - net_profit DECIMAL(14,2) NOT NULL DEFAULT '0.00', - player_count INT NOT NULL DEFAULT '0', - PRIMARY KEY (id), - UNIQUE KEY uk_date (date) -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='Olympus每日盈亏'; - --- 9. 返奖饰品定义(展示用饰品,按赢额区间匹配) -CREATE TABLE IF NOT EXISTS game_olympus_prize_ornament ( - id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'PK', - game_type VARCHAR(16) NOT NULL DEFAULT 'OLYMPUS', - name VARCHAR(100) DEFAULT NULL COMMENT '饰品名称', - image_url VARCHAR(500) DEFAULT NULL COMMENT '饰品图片URL', - grade INT DEFAULT NULL COMMENT '品质等级 1-5', - exterior_name VARCHAR(100) DEFAULT NULL COMMENT '品质名称 崭新出厂/略有磨损/久经沙场/破损不堪/战痕累累', - sort INT DEFAULT '0', - status VARCHAR(1) DEFAULT '1', - is_fallback TINYINT DEFAULT '0' COMMENT '兜底饰品 1是(仅展示不覆盖金额) 0否', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (id) -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='Olympus返奖饰品'; - --- 10. 返奖饰品区间(与 prize_ornament 一一对应,区间连续覆盖) -CREATE TABLE IF NOT EXISTS game_olympus_prize_range ( - id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'PK', - prize_id BIGINT NOT NULL COMMENT 'game_olympus_prize_ornament.id', - win_low DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '返奖区间下限(含)', - win_high DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '返奖区间上限(含)', - PRIMARY KEY (id), - UNIQUE KEY uk_prize (prize_id) -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='Olympus返奖区间'; - --- 11. 箱子饰品分布模板(抽干补桶依据,按身份区分) -CREATE TABLE IF NOT EXISTS game_olympus_box_template ( - id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'PK', - box_id BIGINT NOT NULL COMMENT '所属箱子ID', - audience VARCHAR(16) NOT NULL DEFAULT 'common' COMMENT '身份维度 common/anchor/robot', - name VARCHAR(100) NOT NULL COMMENT '模板名称(同一箱子+身份内唯一)', - total_rounds INT NOT NULL DEFAULT 0 COMMENT '总回合数 = items 各 count 之和', - items_json TEXT NOT NULL COMMENT '饰品分布JSON: [{"ornamentId":1,"count":100},...]', - is_active TINYINT NOT NULL DEFAULT 0 COMMENT '是否生效模板(抽干补桶依据) 1是 0否', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (id), - UNIQUE KEY uk_box_aud_name (box_id, audience, name), - KEY idx_box_aud_active (box_id, audience, is_active) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Olympus箱子饰品分布模板(多身份)'; - --- 12. 奖池水位调整日志(注水/抽水/重置审计) -CREATE TABLE IF NOT EXISTS game_olympus_reward_log ( - id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'PK', - box_id BIGINT DEFAULT NULL COMMENT '箱子ID(批量/重置为汇总,可空)', - box_name VARCHAR(100) DEFAULT NULL COMMENT '箱子名/汇总描述', - audience VARCHAR(16) NOT NULL DEFAULT 'common' COMMENT '身份 common/anchor/robot/white', - type TINYINT NOT NULL COMMENT '1注水 2抽水 3重置', - amount DECIMAL(14,2) NOT NULL DEFAULT '0.00' COMMENT '调整金额(绝对值)', - balance_before DECIMAL(14,2) DEFAULT NULL COMMENT '调整前水位', - balance_after DECIMAL(14,2) DEFAULT NULL COMMENT '调整后水位', - pool_balance DECIMAL(14,2) DEFAULT NULL COMMENT '操作后总池余额', - operator VARCHAR(64) DEFAULT NULL COMMENT '操作人', - remark VARCHAR(200) DEFAULT NULL COMMENT '备注', - create_time DATETIME DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (id), - KEY idx_create (create_time) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Olympus奖池水位调整日志'; - --- ============================================================ --- 种子数据:饰品桶 1-17(0x ~ 1000x) --- ============================================================ -INSERT INTO game_olympus_ornament (id, game_type, expect_mul, name, range_low, range_high) VALUES - (1, 'OLYMPUS', 0.00, '0x', 0.00, 0.00), - (2, 'OLYMPUS', 0.50, '0.5x', 0.01, 1.00), - (3, 'OLYMPUS', 1.50, '1.5x', 1.00, 2.00), - (4, 'OLYMPUS', 2.50, '2.5x', 2.00, 3.00), - (5, 'OLYMPUS', 4.00, '4x', 3.00, 5.00), - (6, 'OLYMPUS', 6.50, '6.5x', 5.00, 8.00), - (7, 'OLYMPUS', 10.00, '10x', 8.00, 12.00), - (8, 'OLYMPUS', 16.00, '16x', 12.00, 20.00), - (9, 'OLYMPUS', 25.00, '25x', 20.00, 30.00), - (10, 'OLYMPUS', 40.00, '40x', 30.00, 50.00), - (11, 'OLYMPUS', 60.00, '60x', 50.00, 70.00), - (12, 'OLYMPUS', 100.00, '100x', 70.00, 130.00), - (13, 'OLYMPUS', 150.00, '150x', 130.00, 170.00), - (14, 'OLYMPUS', 200.00, '200x', 170.00, 230.00), - (15, 'OLYMPUS', 300.00, '300x', 230.00, 370.00), - (16, 'OLYMPUS', 500.00, '500x', 370.00, 630.00), - (17, 'OLYMPUS', 1000.00, '1000x', 630.00, 1370.00) -ON DUPLICATE KEY UPDATE expect_mul=VALUES(expect_mul), name=VALUES(name), - range_low=VALUES(range_low), range_high=VALUES(range_high); - --- ============================================================ --- 附:Java 服务上线时执行(先不执行,接入后台时再放开) --- ============================================================ --- 维护开关 --- INSERT INTO sys_config (config_name, config_key, config_value, config_type, create_by, create_time, remark) --- VALUES ('Olympus维护', 'olympusMaintenance', '0', 'Y', 'admin', NOW(), 'Olympus维护开关 0正常 1维护'); - --- 未结算兜底补发定时任务(系统监控>定时任务) --- INSERT INTO sys_job (job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark) --- VALUES ('Olympus未结算补发', 'OLYMPUS', 'olympusSettleTask.scheduledSettle()', '0 */10 * * * ?', '1', '0', '0', 'admin', NOW(), '每10分钟扫描超35分钟未结算记录并补发金币/饰品'); diff --git a/olympus-py/simulate_cycle.py b/olympus-py/simulate_cycle.py deleted file mode 100644 index 71b1424..0000000 --- a/olympus-py/simulate_cycle.py +++ /dev/null @@ -1,176 +0,0 @@ -"""复刻 Java 抽回合链路,模拟每个箱子抽完一个 cycle 的实时 RTP。 - -复刻的 Java 逻辑(OlympusRoundPicker / OlympusGameService / OlympusDistributionService): - 1. 固定池 fixedSum = Σ 配比 count(按身份),N = round(fixedSum/(1-收敛比例)), - fixedRounds = round(N×(1-收敛比例));固定阶段 drawn < fixedRounds - 2. 固定阶段:在"remaining>0 且 expect_mul×bet ≤ payoutCap"的桶里按 remaining 加权抽; - 桶内取 40 条候选,优先取 actual_mul×bet ≤ payoutCap 的(取不到就退回候选第一条);该桶 used+1 - 3. 收敛阶段:desiredNextMul = (N×目标倍率 − 已派彩) / (N − drawn), - 取期望最接近 desired 的 3 个桶(随机其一),桶内取 actual_mul 最接近 min(desired, capMul) 的一条 - (优先 ≤ capMul),不消耗 used - 4. 水位:payoutCap = max(0, 水位 + 本局成本) × rtp;水位 += 成本 − 派彩(白号不参与水位 → 无上限) - 5. drawn ≥ N → cycle 结束:resetCycle + refillFromConfig(有生效模板则按模板重写 count,否则 used 归零) - -用法: - python simulate_cycle.py # 全部 30 箱 × common 身份,跑 3 个 cycle - python simulate_cycle.py --cycles 5 --audience white - python simulate_cycle.py --box 21 -""" -import argparse -import random -from collections import defaultdict - -import olympus_db as db -from olympus.config import ORNAMENTS, MODES, payout_expect - -# 抽回合的 payoutCap 过滤用**名义** expect_mul(对齐 Java OlympusRoundPicker,偏保守); -# 统计里的"名义 RTP"用 payout_expect(对齐后台「目标RTP」列的封顶修正口径)。 -_ORN_MUL = {o['id']: o['expect_mul'] for o in ORNAMENTS} -_ORN_PAY = {o['id']: payout_expect(o['id']) for o in ORNAMENTS} -CANDIDATE_LIMIT = 40 - - -def load_boxes(box_id=None): - with db.get_conn() as conn: - cur = conn.cursor() - sql = 'SELECT box_id, box_type, bet, rtp, rtp_converge_ratio, total_ornaments FROM game_olympus_box' - if box_id: - sql += ' WHERE box_id=%s' % int(box_id) - cur.execute(sql + ' ORDER BY box_id') - boxes = [dict(zip(('box_id', 'box_type', 'bet', 'rtp', 'cr', 'total'), r)) for r in cur.fetchall()] - cur.execute('SELECT box_id, ornament_id, audience, count FROM game_olympus_box_ornament') - cfg = defaultdict(dict) - for b, o, a, c in cur.fetchall(): - cfg[(int(b), a)][int(o)] = int(c) - cur.execute('SELECT box_id, ornament_id, actual_mul FROM game_olympus_round') - rounds = defaultdict(lambda: defaultdict(list)) - for b, o, m in cur.fetchall(): - rounds[int(b)][int(o)].append(float(m)) - # 生效模板(抽完重填的依据) - cur.execute('''SELECT box_id, audience, items_json FROM game_olympus_box_template - WHERE is_active=1 ORDER BY id''') - tpl = {} - import json - for b, a, items in cur.fetchall(): - tpl[(int(b), a)] = {int(i['ornamentId']): int(i['count']) - for i in json.loads(items) if int(i.get('count', 0)) > 0} - for b in boxes: - b['cm'] = float(MODES[b['box_type']].cost_multiplier) - return boxes, cfg, rounds, tpl - - -def _weighted(cands, weights): - total = sum(weights) - r = random.uniform(0, total) - acc = 0.0 - for c, w in zip(cands, weights): - acc += w - if r <= acc: - return c - return cands[-1] - - -def simulate(box, cfg, rounds, tpl, audience, cycles=3, cr_override=None): - cm, rtp = box['cm'], float(box['rtp']) - cr = float(cr_override if cr_override is not None else (box['cr'] or 0.15)) - fixed_sum = sum(cfg.values()) - n_total = round(fixed_sum / (1.0 - cr)) - fixed_rounds = round(n_total * (1.0 - cr)) - target_mul = rtp * cm - capped = (audience != 'white') # 白号 payoutCap=null - stats = [] - counts = dict(cfg) - cumulative = {'drawn': 0, 'mul': 0.0} - balance = 0.0 # 箱子水位:跨 cycle 持久(= Σ投注 − Σ返奖) - for cyc in range(1, cycles + 1): - used = defaultdict(int) - drawn, mul_sum = 0, 0.0 - rtp_fixed = None - while drawn < n_total: - cap_mul = max(0.0, balance + cm) * rtp if capped else float('inf') - remaining = {o: max(0, c - used[o]) for o, c in counts.items()} - if drawn < fixed_rounds and sum(remaining.values()) > 0: - elig = [o for o, r in remaining.items() - if r > 0 and _ORN_MUL[o] <= cap_mul] or \ - [o for o, r in remaining.items() if r > 0] - orn = _weighted(elig, [remaining[o] for o in elig]) - pool = rounds[box['box_id']].get(orn) or [0.0] - cands = random.sample(pool, min(CANDIDATE_LIMIT, len(pool))) - mul = next((m for m in cands if m <= cap_mul), cands[0]) - used[orn] += 1 - else: - desired = max(0.0, (n_total * target_mul - mul_sum) / max(1, n_total - drawn)) - allowed = [o for o, c in counts.items() if _ORN_MUL[o] <= cap_mul] or list(counts) - allowed.sort(key=lambda o: abs(_ORN_MUL[o] - desired)) - orn = random.choice(allowed[:3]) - pool = rounds[box['box_id']].get(orn) or [0.0] - eff = min(desired, cap_mul) - mul = sorted(pool, key=lambda m: (0 if m <= cap_mul else 1, abs(m - eff)))[0] - drawn += 1 - mul_sum += mul - balance += cm - mul - cumulative['drawn'] += 1 - cumulative['mul'] += mul - if drawn == fixed_rounds: - rtp_fixed = mul_sum / drawn / cm - # cycle 结束 → 按模板重填(无模板则 used 归零、count 不变) - tpl_counts = tpl.get((box['box_id'], audience)) - refilled = '模板' if tpl_counts else 'used归零' - if tpl_counts: - counts = {o: c for o, c in tpl_counts.items() if c > 0} - stats.append({ - 'cycle': cyc, - 'rtp_fixed': rtp_fixed, - 'rtp_end': mul_sum / drawn / cm, - 'balance_end': balance, - 'refill': refilled, - }) - return { - 'box_id': box['box_id'], 'box_type': box['box_type'], 'audience': audience, - 'rtp': rtp, 'cr': cr, 'n_total': n_total, 'fixed_rounds': fixed_rounds, - 'fixed_sum': fixed_sum, 'nominal': sum(c * _ORN_PAY.get(o, 0) for o, c in cfg.items()) / fixed_sum / cm, - 'cycles': stats, - 'cum_rtp': cumulative['mul'] / cumulative['drawn'] / cm, - } - - -def main(): - ap = argparse.ArgumentParser() - ap.add_argument('--box', type=int) - ap.add_argument('--audience', default='common') - ap.add_argument('--cycles', type=int, default=3) - ap.add_argument('--cr', type=float, default=None, help='覆盖收敛比例(测敏感度用,不写库)') - ap.add_argument('--seed', type=int, default=2026) - args = ap.parse_args() - random.seed(args.seed) - boxes, cfg, rounds, tpl = load_boxes(args.box) - print('身份=%s cycle 数=%d seed=%d 收敛比例=%s' % ( - args.audience, args.cycles, args.seed, - ('箱配置' if args.cr is None else args.cr))) - print('箱 模式 配比sum N 固定池 名义RTP 各cycle(固定池末→整轮末) 累计RTP') - rows = [] - for b in boxes: - c = cfg.get((b['box_id'], args.audience), {}) - if not c: - continue - r = simulate(b, c, rounds, tpl, args.audience, args.cycles, args.cr) - rows.append(r) - per = ' '.join('%.1f→%.1f' % (s['rtp_fixed'] * 100, s['rtp_end'] * 100) for s in r['cycles']) - flag = '✓' if abs(r['cum_rtp'] - r['rtp']) < 0.03 else '✗' - print('%2d %-7s %5d %4d %5d %6.1f%% %-34s %6.1f%% %s' % ( - r['box_id'], r['box_type'], r['fixed_sum'], r['n_total'], r['fixed_rounds'], - r['nominal'] * 100, per, r['cum_rtp'] * 100, flag)) - bad = [r for r in rows if abs(r['cum_rtp'] - r['rtp']) >= 0.03] - print('\n达标(±3个百分点): %d/%d' % (len(rows) - len(bad), len(rows))) - if bad: - print('未达标:', [(r['box_id'], round(r['cum_rtp'] * 100, 1), round(r['rtp'] * 100, 1)) for r in bad]) - if rows: - r = rows[0] - print('\n样例 box%d 逐 cycle 明细(%s):' % (r['box_id'], r['audience'])) - for s in r['cycles']: - print(' cycle%d: 固定池结束 %.1f%% 整轮结束 %.1f%% 抽完水位=%.0f 倍底注 重填=%s' % ( - s['cycle'], s['rtp_fixed'] * 100, s['rtp_end'] * 100, s['balance_end'], s['refill'])) - - -if __name__ == '__main__': - main() diff --git a/olympus-py/tests/__init__.py b/olympus-py/tests/__init__.py deleted file mode 100644 index bc842e9..0000000 --- a/olympus-py/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Olympus tests.""" diff --git a/olympus-py/tests/test_engine.py b/olympus-py/tests/test_engine.py deleted file mode 100644 index d5357a7..0000000 --- a/olympus-py/tests/test_engine.py +++ /dev/null @@ -1,292 +0,0 @@ -"""Olympus 引擎单元测试(unittest)。不依赖第三方库,可直接 `python tests/test_engine.py` 运行。""" - -from __future__ import annotations - -import sys -import unittest -from decimal import Decimal -from pathlib import Path - -# 支持直接 `python tests/test_engine.py` 运行(无需先安装为包) -sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) - -from olympus import OlympusEngine, RuleViolation -from olympus.config import MODES, SYMBOL_RULES -from olympus.models import Bomb - - -class TestSymbolRule(unittest.TestCase): - """符号赔付校验:8/10/12 三档取值正确。""" - - def test_symbol_payout_tiers(self) -> None: - crown = SYMBOL_RULES["CROWN"] - self.assertEqual(crown.payout_for(7), Decimal("0")) - self.assertEqual(crown.payout_for(8), Decimal("10")) - self.assertEqual(crown.payout_for(9), Decimal("10")) - self.assertEqual(crown.payout_for(10), Decimal("25")) - self.assertEqual(crown.payout_for(11), Decimal("25")) - self.assertEqual(crown.payout_for(12), Decimal("50")) - self.assertEqual(crown.payout_for(30), Decimal("50")) - - -class TestSingleCascade(unittest.TestCase): - """单回合单次消除:无炸弹时有效倍数为 1。""" - - def test_one_cascade_no_bomb(self) -> None: - engine = OlympusEngine(bet="1") - engine.begin_spin() - engine.record_cascade(symbols={"BLUE_GEM": list(range(8))}, bombs=[]) - spin = engine.end_spin() - self.assertEqual(spin.base_win, Decimal("0.25")) - self.assertEqual(spin.effective_multiplier, 1) - self.assertAlmostEqual(float(spin.total_win), 0.25, places=2) - - def test_one_cascade_with_bomb(self) -> None: - engine = OlympusEngine(bet="1") - engine.begin_spin() - engine.record_cascade( - symbols={"CROWN": [0, 1, 2, 7, 8, 9, 14, 15, 16]}, # 9 个 → 10x - bombs=[Bomb("b1", 2)], - ) - spin = engine.end_spin() - self.assertEqual(spin.base_win, Decimal("10")) - self.assertEqual(spin.effective_multiplier, 2) - self.assertEqual(spin.total_win, Decimal("20")) - - -class TestMultiCascadeWithinSpin(unittest.TestCase): - """一回合多次消除:炸弹只在整回合结束后统一引爆,本回合中间不逐次乘算。""" - - def test_bomb_only_applies_after_all_cascades(self) -> None: - engine = OlympusEngine(bet="1") - engine.begin_spin() - c1 = engine.record_cascade( - symbols={"BLUE_GEM": list(range(8))}, bombs=[Bomb("m1", 3)] - ) - c2 = engine.record_cascade( - symbols={"GREEN_GEM": list(range(8, 16))}, bombs=[] - ) - # 级联阶段自身的 base_win 不应被乘数放大 - self.assertEqual(c1.base_win, Decimal("0.25")) - self.assertEqual(c2.base_win, Decimal("0.4")) - spin = engine.end_spin() - # 回合基础总赢额 = 两次消除之和(未乘算) - self.assertEqual(spin.base_win, Decimal("0.65")) - # 炸弹在回合结束后才统一生效 - self.assertEqual(spin.effective_multiplier, 3) - self.assertEqual(spin.total_win, Decimal("1.95")) - - def test_symbols_are_accumulated_across_cascades(self) -> None: - engine = OlympusEngine(bet="1") - engine.begin_spin() - engine.record_cascade(symbols={"BLUE_GEM": list(range(8))}, bombs=[Bomb("m1", 3)]) - c2 = engine.record_cascade(symbols={"BLUE_GEM": list(range(8, 16))}, bombs=[]) - # 同一符号在本回合内跨级联累计记录(8 + 8 = 16) - self.assertEqual(c2.accumulated_symbols.get("BLUE_GEM"), 16) - spin = engine.end_spin() - self.assertEqual(spin.accumulated_symbols.get("BLUE_GEM"), 16) - - -class TestCrossSpinAccumulator(unittest.TestCase): - """炸弹累计器:仅免费旋转生效;基础旋转炸弹只乘本旋、不留存。""" - - def test_base_spin_does_not_accumulate(self) -> None: - """基础旋转:炸弹乘本旋赢额,但不进累计器。""" - engine = OlympusEngine(bet="1") - engine.begin_spin() # 默认基础旋转 - engine.record_cascade(symbols={"RED_GEM": list(range(8))}, bombs=[Bomb("m1", 5)]) - s1 = engine.end_spin() - self.assertEqual(s1.effective_multiplier, 5) # 本旋生效 - self.assertEqual(s1.total_win, Decimal("5")) # 1 * 5 - self.assertEqual(s1.multiplier_after, 0) # 不留存 - # 第二次基础旋转:只吃自己的炸弹 - engine.begin_spin() - engine.record_cascade(symbols={"PURPLE_GEM": list(range(8, 16))}, bombs=[Bomb("m2", 3)]) - s2 = engine.end_spin() - self.assertEqual(s2.multiplier_before, 0) - self.assertEqual(s2.effective_multiplier, 3) - self.assertEqual(s2.total_win, Decimal("2.4")) # 0.8 * 3 - - def test_accumulator_carries_across_free_spins(self) -> None: - """免费旋转:炸弹并入累计器并跨旋传递。""" - engine = OlympusEngine(bet="1") - engine.begin_spin(is_free_spin=True) - engine.record_cascade(symbols={"RED_GEM": list(range(8))}, bombs=[Bomb("m1", 5)]) - s1 = engine.end_spin() - self.assertEqual(s1.multiplier_before, 0) - self.assertEqual(s1.multiplier_after, 5) - self.assertEqual(s1.total_win, Decimal("5")) # 1 * 5 - - engine.begin_spin(is_free_spin=True) - engine.record_cascade(symbols={"PURPLE_GEM": list(range(8, 16))}, bombs=[Bomb("m2", 3)]) - s2 = engine.end_spin() - self.assertEqual(s2.multiplier_before, 5) - self.assertEqual(s2.multiplier_after, 8) - self.assertEqual(s2.total_win, Decimal("6.4")) # PURPLE_GEM 8个=0.8x, 0.8 * 8 = 6.4 - - def test_fs_spin_without_win_keeps_accumulator_unchanged(self) -> None: - engine = OlympusEngine(bet="1") - engine.begin_spin(is_free_spin=True) - engine.record_cascade(symbols={"RED_GEM": list(range(8))}, bombs=[Bomb("m1", 5)]) - engine.end_spin() - - # 第二次 FS 旋转没有任何消除(即使传入炸弹也无效,不计入累计) - engine.begin_spin(is_free_spin=True) - s2 = engine.end_spin() - self.assertEqual(s2.base_win, Decimal("0")) - self.assertEqual(s2.effective_multiplier, 0) - self.assertEqual(s2.multiplier_after, 5) # 累计值保持不变 - - def test_bomb_discarded_when_cascade_has_no_win(self) -> None: - """无消除的旋转即使出现炸弹,也不应计入累计(消除是炸弹生效的前提)。""" - engine = OlympusEngine(bet="1") - engine.begin_spin(is_free_spin=True) - engine.record_cascade(symbols={}, bombs=[Bomb("m1", 10)]) - spin = engine.end_spin() - self.assertEqual(spin.base_win, Decimal("0")) - self.assertEqual(spin.multiplier_after, 0) - - -class TestStateGuards(unittest.TestCase): - """状态机误用与非法输入的防御性校验。""" - - def test_non_positive_bet_raises(self) -> None: - with self.assertRaises(RuleViolation): - OlympusEngine(bet="0") - - def test_record_cascade_without_begin_raises(self) -> None: - engine = OlympusEngine(bet="1") - with self.assertRaises(RuleViolation): - engine.record_cascade(symbols={"CROWN": list(range(8))}) - - def test_begin_spin_twice_raises(self) -> None: - engine = OlympusEngine(bet="1") - engine.begin_spin() - with self.assertRaises(RuleViolation): - engine.begin_spin() - - def test_invalid_symbol_raises(self) -> None: - engine = OlympusEngine(bet="1") - engine.begin_spin() - with self.assertRaises(RuleViolation): - engine.record_cascade(symbols={"UNKNOWN": list(range(8))}) - - def test_below_min_win_count_raises(self) -> None: - engine = OlympusEngine(bet="1") - engine.begin_spin() - with self.assertRaises(RuleViolation): - engine.record_cascade(symbols={"CROWN": list(range(7))}) - - def test_duplicate_positions_raises(self) -> None: - engine = OlympusEngine(bet="1") - engine.begin_spin() - with self.assertRaises(RuleViolation): - engine.record_cascade(symbols={"CROWN": [0, 0, 1, 2, 3, 4, 5, 6]}) - - def test_invalid_bomb_value_raises(self) -> None: - engine = OlympusEngine(bet="1") - engine.begin_spin() - with self.assertRaises(RuleViolation): - engine.record_cascade(symbols={"CROWN": list(range(8))}, bombs=[Bomb("m1", 7)]) - - def test_inconsistent_bomb_redefinition_raises(self) -> None: - engine = OlympusEngine(bet="1") - engine.begin_spin() - engine.record_cascade(symbols={"CROWN": list(range(8))}, bombs=[Bomb("m1", 5)]) - with self.assertRaises(RuleViolation): - engine.record_cascade(symbols={}, bombs=[Bomb("m1", 10)]) - - def test_reset_session_while_spin_active_raises(self) -> None: - engine = OlympusEngine(bet="1") - engine.begin_spin() - with self.assertRaises(RuleViolation): - engine.reset_session() - - -class TestGameModes(unittest.TestCase): - """模式互斥与下注档位。""" - - def test_mode_bet_tiers_and_prices(self) -> None: - # 普通:1 3 5 10 20 30 50 100 200 300(扣费=下注) - normal = MODES["normal"] - self.assertEqual(normal.base_bets, (1, 3, 5, 10, 20, 30, 50, 100, 200, 300)) - self.assertEqual(normal.price_for(20), Decimal("20")) - # 底注:扣费 = 2 6 10 20 40 60 100 200 400 600 - ante = MODES["ante"] - self.assertEqual( - [int(ante.price_for(b)) for b in ante.base_bets], - [2, 6, 10, 20, 40, 60, 100, 200, 400, 600], - ) - # 购买免费旋转:扣费 = 50 150 250 500 1000 1500 2500 5000 10000 15000(10 档,50 倍) - buy = MODES["buy_fs"] - self.assertEqual( - [int(buy.price_for(b)) for b in buy.base_bets], - [50, 150, 250, 500, 1000, 1500, 2500, 5000, 10000, 15000], - ) - - def test_mode_mutual_exclusion(self) -> None: - # 底注模式禁止购买免费旋转 - ante_engine = OlympusEngine(bet="1", mode="ante") - self.assertFalse(ante_engine.buy_free_spins_allowed()) - self.assertTrue(ante_engine.ante_boost_active()) - # 购买免费旋转模式禁用底注效果 - buy_engine = OlympusEngine(bet="1", mode="buy_fs") - self.assertFalse(buy_engine.ante_boost_active()) - self.assertFalse(buy_engine.buy_free_spins_allowed()) - self.assertTrue(buy_engine.mode.guaranteed_free_spins) - # 普通模式允许购买 - normal_engine = OlympusEngine(bet="1", mode="normal") - self.assertTrue(normal_engine.buy_free_spins_allowed()) - self.assertFalse(normal_engine.ante_boost_active()) - - def test_engine_cost_uses_mode_multiplier(self) -> None: - self.assertEqual(OlympusEngine(bet="20", mode="normal").cost, Decimal("20")) - self.assertEqual(OlympusEngine(bet="20", mode="ante").cost, Decimal("40")) - self.assertEqual(OlympusEngine(bet="20", mode="buy_fs").cost, Decimal("1000")) - - def test_invalid_mode_raises(self) -> None: - with self.assertRaises(RuleViolation): - OlympusEngine(bet="1", mode="buy_super_fs") - - def test_bet_not_in_mode_tiers_raises(self) -> None: - # normal 没有 7 档 - with self.assertRaises(RuleViolation): - OlympusEngine(bet="7", mode="normal") - # buy_fs 没有 40 档 - with self.assertRaises(RuleViolation): - OlympusEngine(bet="40", mode="buy_fs") - - -class TestMaxWinCap(unittest.TestCase): - """单局赢奖封顶 1000 倍基础下注。""" - - def test_win_capped_at_1000x(self) -> None: - engine = OlympusEngine(bet="1") - engine.begin_spin() - # CROWN 12个=50x,配 1000 倍炸弹 → 未封顶前 50000x - engine.record_cascade( - symbols={"CROWN": list(range(12))}, bombs=[Bomb("m1", 1000)] - ) - spin = engine.end_spin() - self.assertTrue(spin.win_capped) - self.assertEqual(spin.total_win, Decimal("1000")) - self.assertEqual(engine.state.total_win, Decimal("1000")) - - def test_cap_applies_to_session_cumulative(self) -> None: - engine = OlympusEngine(bet="1") - # 第 1 次 FS 旋转拿 900x(基础 10x × 炸弹 90),累计器 → 90 - engine.begin_spin(is_free_spin=True) - engine.record_cascade(symbols={"CROWN": list(range(8))}, bombs=[Bomb("m1", 50), Bomb("m2", 25), Bomb("m3", 15)]) - s1 = engine.end_spin() - self.assertEqual(s1.total_win, Decimal("900")) - self.assertFalse(s1.win_capped) - # 第 2 次 FS 旋转再赢(无新炸弹,吃累计 90)→ 只能再拿 100x,整局顶格 1000x - engine.begin_spin(is_free_spin=True) - engine.record_cascade(symbols={"CROWN": list(range(8))}, bombs=[]) - s2 = engine.end_spin() - self.assertTrue(s2.win_capped) - self.assertEqual(engine.state.total_win, Decimal("1000")) - - -if __name__ == "__main__": - unittest.main() diff --git a/olympus-py/tests/test_simulator.py b/olympus-py/tests/test_simulator.py deleted file mode 100644 index 3eeb1e8..0000000 --- a/olympus-py/tests/test_simulator.py +++ /dev/null @@ -1,263 +0,0 @@ -"""回合生成器规则校验(unittest)。不依赖第三方库,可直接 `python tests/test_simulator.py` 运行。""" - -from __future__ import annotations - -import json -import random -import sys -import unittest -from collections import Counter -from pathlib import Path - -# 支持直接 `python tests/test_simulator.py` 运行(无需先安装为包) -sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) - -from olympus.config import BIGWIN_LEVELS, BIGWIN_TIERS, BOMB_SHORT, SCATTER_SHORT -import olympus_simulator as sim -from olympus_simulator import generate_round - - -def _cells(step): - return [c for row in step['grid_state'] for c in row] - - -class TestBuyFsTriggerSpin(unittest.TestCase): - """buy_fs 触发盘:首盘固定 4 个 SCATTER,且整盘不出炸弹(炸弹只在免费旋转内生效)。""" - - def test_trigger_pages(self) -> None: - for _ in range(100): - _, steps, _ = generate_round({'box_id': 0, 'box_type': 'buy_fs'}) - trigger = [s for s in steps if s['free_spin_id'] == 0] - self.assertTrue(trigger) - for s in trigger: - cells = _cells(s) - self.assertEqual(cells.count(SCATTER_SHORT), 4) - self.assertEqual(cells.count(BOMB_SHORT), 0) - - -class TestBuyFsFreeSpinHits(unittest.TestCase): - """buy_fs 免费旋转"每旋保底消除":自然阶段命中率应远高于旧实现的 ~13%(旧版 45% 强制空转)。""" - - def test_natural_phase_hit_rate(self) -> None: - random.seed(12345) - box = {'box_id': 0, 'box_type': 'buy_fs'} - total = hit = 0 - for _ in range(60): - _, steps, _ = generate_round(box, None) - spins = Counter(s['free_spin_id'] for s in steps if s['free_spin_id'] > 0) - total += len(spins) - hit += sum(1 for v in spins.values() if v > 1) - self.assertGreater(hit / total, 0.45) - - -class TestBombOnlyInFreeSpins(unittest.TestCase): - """炸弹只存在于免费旋转内: - · normal/ante 的基础旋(含进 FS 的触发盘、含补桶植入)一律不出炸弹; - · 三个模式一旦进 FS 规则一致(炸弹 + 累计器),normal/ante 靠它演 100x/150x/200x 桶。""" - - def test_normal_ante_base_spin_never_emit_bomb(self) -> None: - random.seed(20260822) - for mode in ('normal', 'ante'): - box = {'box_id': 0, 'box_type': mode} - bombs = 0 - for bias in (None, 'zero', 0.5, 10.0, 150.0): - for _ in range(400): - _, steps, _ = generate_round(box, bias) - bombs += sum(_cells(s).count(BOMB_SHORT) - for s in steps if s['free_spin_id'] == 0) - self.assertEqual(bombs, 0, '%s 基础旋不该出炸弹' % mode) - - def test_normal_ante_free_spins_have_bomb(self) -> None: - random.seed(11) - for mode in ('normal', 'ante'): - box = {'box_id': 0, 'box_type': mode} - grids = bombs = 0 - for _ in range(400): - # 目标 150x ≥ FS_ONLY_TARGET_MUL → 该模式的 150x 桶只能靠自然触发 FS 的回合来装 - _, steps, _ = generate_round(box, 150.0) - for s in steps: - if s['free_spin_id'] > 0: - grids += 1 - bombs += _cells(s).count(BOMB_SHORT) - self.assertGreater(grids, 0, '%s 高倍桶应该是 FS 回合' % mode) - self.assertGreater(bombs / (30.0 * grids), 0.02, - '%s 的 FS 盘面应该有炸弹' % mode) - - def test_high_target_round_enters_free_spins(self) -> None: - """目标高到基础旋赔不出来(≥80x)时,生成器必须改用自然触发 FS 的回合。""" - for mode in ('normal', 'ante'): - _, steps, _ = generate_round({'box_id': 0, 'box_type': mode}, 150.0) - self.assertTrue(any(s['free_spin_id'] > 0 for s in steps)) - trigger = [s for s in steps if s['free_spin_id'] == 0][-1] - self.assertGreaterEqual(_cells(trigger).count(SCATTER_SHORT), 4) - self.assertEqual(_cells(trigger).count(BOMB_SHORT), 0) - - def test_buy_fs_still_has_bomb_in_free_spins(self) -> None: - random.seed(7) - box = {'box_id': 0, 'box_type': 'buy_fs'} - bombs = 0 - for _ in range(200): - _, steps, _ = generate_round(box, None) - bombs += sum(_cells(s).count(BOMB_SHORT) for s in steps if s['free_spin_id'] > 0) - self.assertGreater(bombs, 0) - - -class TestBigWinLevels(unittest.TestCase): - """爆奖动画只有小/中/大 3 档,而且只在免费旋转里判档: - · 基础旋(含触发盘,`free_spin_id=0`)恒为 null —— 这两个模式的普通旋转不播大奖动画; - · FS 旋的档位 = 该旋末帧 winAmount 与新阈值(两种情况两套)比对的结果。""" - - def test_tiers_are_three_and_two_cases(self) -> None: - self.assertEqual(BIGWIN_LEVELS, ('small', 'middle', 'big')) - self.assertEqual(BIGWIN_TIERS['normal'], BIGWIN_TIERS['ante']) # 自然 FS 共用一套 - self.assertNotEqual(BIGWIN_TIERS['normal'], BIGWIN_TIERS['buy_fs']) - for tiers in BIGWIN_TIERS.values(): - self.assertEqual(len(tiers), len(BIGWIN_LEVELS)) - - def test_only_free_spin_final_frames_get_level(self) -> None: - random.seed(4242) - cases = (('normal', None), ('ante', None), ('buy_fs', None), - ('normal', 150.0), ('ante', 200.0)) - for mode, bias in cases: - box = {'box_id': 0, 'box_type': mode} - for _ in range(120): - _, steps, _ = generate_round(box, bias) - for s in steps: - if s['free_spin_id'] == 0 or not s['is_final']: - self.assertIsNone(s['bigwin_level'], - '%s 基础旋/非末帧不该有爆奖档' % mode) - continue - want = None - for name, threshold in zip(BIGWIN_LEVELS, BIGWIN_TIERS[mode]): - if float(s['win_amount']) >= threshold: - want = name - self.assertEqual(s['bigwin_level'], want) - - -class TestFreeSpinFloorSpread(unittest.TestCase): - """保底消除"随机间隔":既不能出现长空尾,也不能变成固定节奏。 - - 口径:dry = 该旋所有 cascade 步 `base_score` 全 0(scatter 赔付不算消除,S 不参与消除)。 - 两版历史(都别退回去): - · 最初按"剩余预算÷剩余旋数"算概率、扣预算扣的是被累计倍数放大后的赢额 → 买 FS 87.9% 的局 - ≥4 旋连空、51.7% 的局尾巴一直空转、单局最长 29 旋; - · 修成"等距铺"后虽然没长空尾,但 87% 的局"最长连空正好 4 旋",玩家几局就看穿"到第 4 旋肯定消"。""" - - @staticmethod - def _max_dry(steps) -> int: - per = {} - for s in steps: - if s['free_spin_id'] > 0: - per[s['free_spin_id']] = per.get(s['free_spin_id'], 0.0) + float(s['base_score']) - run = mx = 0 - for k in sorted(per): - run = 0 if per[k] > 0 else run + 1 - mx = max(mx, run) - return mx - - def test_dry_run_stays_short(self) -> None: - random.seed(20260916) - for bias in (25.0, None): - runs = [self._max_dry(generate_round({'box_id': 0, 'box_type': 'buy_fs'}, bias)[1]) - for _ in range(80)] - avg, worst = sum(runs) / len(runs), max(runs) - self.assertLess(avg, 7, 'bias=%s 平均最长连空 %.1f 太长(旧实现 9.6)' % (bias, avg)) - self.assertLessEqual(worst, 9, 'bias=%s 出现 %d 旋死尾巴(旧实现最长 29)' % (bias, worst)) - - def test_gap_is_not_a_fixed_cadence(self) -> None: - """最长连空的取值必须散开:等距版 87% 的局都卡在同一个值。""" - random.seed(4242) - runs = [self._max_dry(generate_round({'box_id': 0, 'box_type': 'buy_fs'}, 25.0)[1]) - for _ in range(120)] - top, cnt = Counter(runs).most_common(1)[0] - self.assertGreaterEqual(len(set(runs)), 3, '最长连空只有 %s,节奏太固定' % sorted(set(runs))) - self.assertLess(cnt / len(runs), 0.7, '最长连空过于集中:%.0f%% 都是 %d 旋' % (100 * cnt / len(runs), top)) - - -class TestTumbleGeometry(unittest.TestCase): - """每一步都要能按"消掉上一步的连线 → 重力下落 → 补位"复刻出 `grid_state`: - 前端据此播动画,任何一格对不上/漏掉都会表现成画错符号或空白格子。""" - - def _check_round(self, steps) -> None: - rows, cols = 5, 6 - for i in range(1, len(steps)): - prev, cur = steps[i - 1], steps[i] - drops = json.loads(cur['drop_data'] or '[]') - fills = json.loads(cur['fill_data'] or '[]') - if prev['free_spin_id'] != cur['free_spin_id']: - # 新的一旋:整盘重发,不该带下落/补位 - self.assertEqual(drops, []) - self.assertEqual(fills, []) - continue - links = json.loads(prev['symbol_links'] or '[]') - removed = {p - 1 for l in links if l.get('tumble') == prev['cascade_seq'] - for p in l['loc']} - old = prev['grid_state'] - board = [['' if (r * cols + c) in removed else old[r][c] for c in range(cols)] - for r in range(rows)] - nb = [[''] * cols for _ in range(rows)] - exp_drops = [] - for c in range(cols): - surv = [(r, board[r][c]) for r in range(rows - 1, -1, -1) if board[r][c] != ''] - for k, (r, sym) in enumerate(surv): - nr = rows - 1 - k - nb[nr][c] = sym - if nr != r: - exp_drops.append({'from': r * cols + c + 1, 'to': nr * cols + c + 1, - 'symbol': sym}) - key = lambda d: (d['from'], d['to']) - self.assertEqual(sorted(exp_drops, key=key), sorted(drops, key=key)) - # 下落目标的 symbol 必须是"这一波结束后"的符号(= grid_state 同位置) - for d in drops: - p = d['to'] - 1 - self.assertEqual(d['symbol'], cur['grid_state'][p // cols][p % cols]) - # 补位必须正好盖住所有空位,且符号与最终盘面一致 - empty = sorted(r * cols + c for c in range(cols) for r in range(rows) - if nb[r][c] == '') - self.assertEqual(sorted(f['pos'] - 1 for f in fills), empty) - for f in fills: - p = f['pos'] - 1 - self.assertEqual(f['symbol'], cur['grid_state'][p // cols][p % cols]) - nb[p // cols][p % cols] = f['symbol'] - self.assertEqual(nb, cur['grid_state'], '复刻盘面 ≠ grid_state') - - def test_all_modes(self) -> None: - random.seed(31337) - for mode, biases in (('normal', (None, 'zero', 20.0, 150.0)), - ('ante', (None, 100.0, 200.0)), - ('buy_fs', (None, 25.0, 500.0))): - for bias in biases: - for _ in range(60): - _, steps, _ = generate_round({'box_id': 0, 'box_type': mode}, bias) - self._check_round(steps) - - -class TestFreeSpinCap(unittest.TestCase): - """30 旋上限(15 + 5×3):到上限之后不得再落 3+ 个 S。 - - 旧实现只在"加旋"那行有闸门,写 retrigger_info 的那行没有 —— 到 30 旋后照样摇出 3+ 个 S、 - 照样写 extraSpins=5,玩家白看 "+5" 提示(全库 650 个这种帧)。这里用"每旋必落 5 个 S"的 - 极端分布把上限逼出来:应恰好 30 旋、恰好 3 次重触发,上限之后的旋 S 数 ≤2。 - """ - - def test_no_retrigger_beyond_cap(self) -> None: - random.seed(20260919) - original = sim.FREESPIN_SCATTER - sim.FREESPIN_SCATTER = {'default': {5: 1.0}, 'buy_fs': {5: 1.0}} - try: - for mode in ('normal', 'ante', 'buy_fs'): - steps, _ = sim._free_spin_session(mode) - spins = {s['free_spin_id'] for s in steps} - retrigger = [s for s in steps if s.get('retrigger_info')] - self.assertEqual(len(spins), 30, f'{mode}: 旋数应为 30,实际 {len(spins)}') - self.assertEqual(len(retrigger), 3, f'{mode}: 重触发应为 3 次,实际 {len(retrigger)}') - for s in steps: - if s['is_final'] and s['free_spin_id'] > 3: - self.assertLessEqual(_cells(s).count(SCATTER_SHORT), 2, - f'{mode}: 上限之后(第 {s["free_spin_id"]} 旋)又落了 3+ 个 S') - finally: - sim.FREESPIN_SCATTER = original - - -if __name__ == '__main__': - unittest.main() diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index d6e5d42..366207e 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -33,8 +33,8 @@ spring: druid: # 主库数据源 master: - url: jdbc:mysql://127.0.0.1:23306/changzhou?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 - username: changzhou + url: jdbc:mysql://127.0.0.1:23306/henan?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: henan password: iTSJSPPZM3LSGAPC # 从库数据源 slave: diff --git a/ruoyi-admin/src/main/resources/application-pro.yml b/ruoyi-admin/src/main/resources/application-pro.yml index a9f1bd9..74605fb 100644 --- a/ruoyi-admin/src/main/resources/application-pro.yml +++ b/ruoyi-admin/src/main/resources/application-pro.yml @@ -33,8 +33,8 @@ spring: druid: # 主库数据源 master: - url: jdbc:mysql://127.0.0.1:23306/changzhou?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 - username: changzhou + url: jdbc:mysql://127.0.0.1:23306/henan?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: henan password: iTSJSPPZM3LSGAPC # 从库数据源 slave: