Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/build-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Build image
on:
push:
branches:
- master
paths:
- 'build-image/Dockerfile'
schedule:
# Rebuild weekly on Mondays to keep Rust toolchain current
- cron: '0 6 * * 1'
workflow_dispatch:

jobs:
build-image:
name: "Build and push build image"
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: ./build-image
platforms: linux/amd64,linux/arm64
push: true
tags: ghcr.io/rbtying/shengji-build-image:latest
cache-from: type=registry,ref=ghcr.io/rbtying/shengji-build-image:buildcache
cache-to: type=registry,ref=ghcr.io/rbtying/shengji-build-image:buildcache,mode=max
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
ARG PLATFORM=$BUILDPLATFORM

FROM --platform=$PLATFORM ghcr.io/rbtying/yarn-wasm-rust-build-image:master as wasmbase
FROM --platform=$PLATFORM ghcr.io/rbtying/shengji-build-image:latest as wasmbase

Check warning on line 3 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

Check warning on line 3 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

# Create a workspace recipe.json to pre-fetch and pre-compile dependencies
FROM wasmbase as planner

Check warning on line 6 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

Check warning on line 6 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/
WORKDIR /app
COPY . .
RUN cargo chef prepare --recipe-path recipe.json

# Pre-compile frontend wasm dependencies
FROM wasmbase as frontend-cacher

Check warning on line 12 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

Check warning on line 12 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/
WORKDIR /app
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json --target=wasm32-unknown-unknown -p shengji-wasm

# Download Yarn dependencies
FROM wasmbase as frontend-deps-fetch

Check warning on line 18 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

Check warning on line 18 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/
COPY frontend/package.json ./
COPY frontend/yarn.lock ./
RUN yarn install

# Actually build the frontend
FROM frontend-deps-fetch as frontend-builder

Check warning on line 24 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

Check warning on line 24 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/
WORKDIR /app
COPY --from=frontend-cacher /app/target /app/target
# Run the actual build
Expand All @@ -41,7 +41,7 @@

# Create a workspace recipe.json to pre-fetch and pre-compile dependencies, but
# without shengji-wasm because this is the backend
FROM wasmbase as planner-no-wasm

Check warning on line 44 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

Check warning on line 44 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/
WORKDIR /app
COPY . .
RUN rm -r frontend
Expand All @@ -50,7 +50,7 @@

# Compile backend for amd64 and arm64, because TARGETPLATFORM can't be used in
# the build stages
FROM --platform=$PLATFORM messense/rust-musl-cross:x86_64-musl as amd64

Check warning on line 53 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

Check warning on line 53 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/
ARG PLATFORM
RUN case "$PLATFORM" in \
"linux/arm64") echo "aarch64-unknown-linux-gnu" > /host-target ;; \
Expand Down Expand Up @@ -86,7 +86,7 @@
*) exit 1 ;; \
esac

FROM --platform=$PLATFORM messense/rust-musl-cross:aarch64-musl as arm64

Check warning on line 89 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

Check warning on line 89 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/
ARG PLATFORM
RUN case "$PLATFORM" in \
"linux/arm64") echo "aarch64-unknown-linux-gnu" > /host-target ;; \
Expand Down Expand Up @@ -123,7 +123,7 @@
esac

# Merge them
FROM alpine as merged

Check warning on line 126 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

Check warning on line 126 in Dockerfile

View workflow job for this annotation

GitHub Actions / Build a docker image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/
ARG TARGETPLATFORM
COPY --from=amd64 /app/target/x86_64-unknown-linux-musl/release/shengji /shengji.x86_64
COPY --from=arm64 /app/target/aarch64-unknown-linux-musl/release/shengji /shengji.aarch64
Expand Down
55 changes: 55 additions & 0 deletions build-image/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
ARG PLATFORM=$BUILDPLATFORM

FROM --platform=$PLATFORM messense/rust-musl-cross:x86_64-musl as amd64
RUN cargo install wasm-opt --target=x86_64-unknown-linux-musl
RUN cp /root/.cargo/bin/wasm-opt /
RUN cargo install wasm-pack --target=x86_64-unknown-linux-musl
RUN cp /root/.cargo/bin/wasm-pack /
RUN cargo install cargo-chef --target=x86_64-unknown-linux-musl
RUN cp /root/.cargo/bin/cargo-chef /

FROM --platform=$PLATFORM messense/rust-musl-cross:aarch64-musl as arm64
RUN cargo install wasm-opt --target=aarch64-unknown-linux-musl
RUN cp /root/.cargo/bin/wasm-opt /
RUN cargo install wasm-pack --target=aarch64-unknown-linux-musl
RUN cp /root/.cargo/bin/wasm-pack /
RUN cargo install cargo-chef --target=aarch64-unknown-linux-musl
RUN cp /root/.cargo/bin/cargo-chef /

FROM node:lts-slim as base
RUN apt-get update && apt-get -y install curl build-essential libssl-dev pkg-config
# Install Rust
RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain stable -y
ENV PATH="/root/.cargo/bin:${PATH}"
ARG TARGETPLATFORM
RUN echo $TARGETPLATFORM
RUN case "$TARGETPLATFORM" in \
"linux/arm64") echo "aarch64-unknown-linux-musl" > /rust_target.txt ;; \
"linux/amd64") echo "x86_64-unknown-linux-musl" > /rust_target.txt ;; \
*) exit 1 ;; \
esac
RUN rustup target add wasm32-unknown-unknown $(cat /rust_target.txt)
COPY --from=amd64 /wasm-opt /wasm-opt.x86_64
COPY --from=arm64 /wasm-opt /wasm-opt.aarch64
RUN case "$TARGETPLATFORM" in \
"linux/arm64") ln /wasm-opt.aarch64 /wasm-opt ;; \
"linux/amd64") ln /wasm-opt.x86_64 /wasm-opt ;; \
*) exit 1 ;; \
esac
COPY --from=amd64 /wasm-pack /wasm-pack.x86_64
COPY --from=arm64 /wasm-pack /wasm-pack.aarch64
RUN case "$TARGETPLATFORM" in \
"linux/arm64") ln /wasm-pack.aarch64 /wasm-pack ;; \
"linux/amd64") ln /wasm-pack.x86_64 /wasm-pack ;; \
*) exit 1 ;; \
esac
ENV WASM_PACK_PATH="/wasm-pack"
RUN yarn global add rimraf webpack webpack-cli
COPY --from=amd64 /cargo-chef /cargo-chef.x86_64
COPY --from=arm64 /cargo-chef /cargo-chef.aarch64
RUN case "$TARGETPLATFORM" in \
"linux/arm64") ln /cargo-chef.aarch64 /cargo-chef ;; \
"linux/amd64") ln /cargo-chef.x86_64 /cargo-chef ;; \
*) exit 1 ;; \
esac
ENV PATH=$PATH:/
2 changes: 1 addition & 1 deletion core/src/game_state/initialize_phase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl InitializePhase {
num_friends: (self.propagated.players.len() / 2) - 1,
friends: vec![],
},
GameModeSettings::Tractor if self.propagated.players.len() % 2 == 0 => {
GameModeSettings::Tractor if self.propagated.players.len().is_multiple_of(2) => {
GameMode::Tractor
}
GameModeSettings::Tractor => {
Expand Down
9 changes: 5 additions & 4 deletions core/src/game_state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ mod tests {

use shengji_mechanics::hands::Hands;
use shengji_mechanics::trick::{
PlayCards, ThrowEvaluationPolicy, TractorRequirements, Trick, TrickDrawPolicy,
BombPolicy, PlayCards, ThrowEvaluationPolicy, TractorRequirements, Trick, TrickDrawPolicy,
};

const R2: Rank = Rank::Number(Number::Two);
Expand Down Expand Up @@ -239,6 +239,7 @@ mod tests {
format_hint: None,
hide_throw_halting_player: false,
tractor_requirements: TractorRequirements::default(),
bomb_policy: BombPolicy::NoBombs,
}
};
}
Expand Down Expand Up @@ -663,7 +664,7 @@ mod tests {
hands.add(P2, vec![S_J]).unwrap();
hands.add(P3, vec![S_2]).unwrap();
hands.add(P4, vec![S_3]).unwrap();
let mut trick = Trick::new(JACK_TRUMP, vec![P1, P2, P3, P4]);
let mut trick = Trick::new(JACK_TRUMP, vec![P1, P2, P3, P4], BombPolicy::NoBombs);
trick.play_cards(pc!(P1, &mut hands, &[S_2])).unwrap();
trick.play_cards(pc!(P2, &mut hands, &[S_J])).unwrap();
trick.play_cards(pc!(P3, &mut hands, &[S_2])).unwrap();
Expand Down Expand Up @@ -699,7 +700,7 @@ mod tests {
hands.add(P2, vec![S_J]).unwrap();
hands.add(P3, vec![S_2]).unwrap();
hands.add(P4, vec![S_3]).unwrap();
let mut trick = Trick::new(JACK_TRUMP, vec![P1, P2, P3, P4]);
let mut trick = Trick::new(JACK_TRUMP, vec![P1, P2, P3, P4], BombPolicy::NoBombs);
trick.play_cards(pc!(P1, &mut hands, &[S_2])).unwrap();
trick.play_cards(pc!(P2, &mut hands, &[S_J])).unwrap();
trick.play_cards(pc!(P3, &mut hands, &[S_2])).unwrap();
Expand Down Expand Up @@ -739,7 +740,7 @@ mod tests {
hands.add(P2, vec![S_J]).unwrap();
hands.add(P3, vec![S_2]).unwrap();
hands.add(P4, vec![S_3]).unwrap();
let mut trick = Trick::new(JACK_TRUMP, vec![P1, P2, P3, P4]);
let mut trick = Trick::new(JACK_TRUMP, vec![P1, P2, P3, P4], BombPolicy::NoBombs);
trick.play_cards(pc!(P1, &mut hands, &[S_2])).unwrap();
trick.play_cards(pc!(P2, &mut hands, &[S_J])).unwrap();
trick.play_cards(pc!(P3, &mut hands, &[S_2])).unwrap();
Expand Down
3 changes: 3 additions & 0 deletions core/src/game_state/play_phase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ impl PlayPhase {
let idx = (landlord_idx + offset) % propagated.players.len();
propagated.players[idx].id
}),
propagated.bomb_policy,
),
points: propagated
.players
Expand Down Expand Up @@ -182,6 +183,7 @@ impl PlayPhase {
format_hint,
hide_throw_halting_player: self.propagated.hide_throw_halting_player,
tractor_requirements: self.propagated.tractor_requirements,
bomb_policy: self.propagated.bomb_policy,
})?;
if self.propagated.hide_played_cards {
for msg in &mut msgs {
Expand Down Expand Up @@ -346,6 +348,7 @@ impl PlayPhase {
let idx = (winner_idx + offset) % self.propagated.players.len();
self.propagated.players[idx].id
}),
self.propagated.bomb_policy,
);
self.last_trick = Some(std::mem::replace(&mut self.trick, new_trick));

Expand Down
7 changes: 6 additions & 1 deletion core/src/interactive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use shengji_mechanics::bidding::{
use shengji_mechanics::deck::Deck;
use shengji_mechanics::scoring::GameScoringParameters;
use shengji_mechanics::trick::{
ThrowEvaluationPolicy, TractorRequirements, TrickDrawPolicy, TrickUnit,
BombPolicy, ThrowEvaluationPolicy, TractorRequirements, TrickDrawPolicy, TrickUnit,
};
use shengji_mechanics::types::{Card, PlayerID, Rank};

Expand Down Expand Up @@ -290,6 +290,10 @@ impl InteractiveGame {
info!(logger, "Setting tractor requirements"; "tractor_requirements" => requirements);
state.set_tractor_requirements(requirements)?
}
(Action::SetBombPolicy(policy), GameState::Initialize(ref mut state)) => {
info!(logger, "Setting bomb policy"; "bomb_policy" => policy);
state.set_bomb_policy(policy)?
}
(Action::DrawCard, GameState::Draw(ref mut state)) => {
debug!(logger, "Drawing card");
state.draw_card(id)?;
Expand Down Expand Up @@ -460,6 +464,7 @@ pub enum Action {
SetHideThrowHaltingPlayer(bool),
SetJackVariation(BackToTwoSetting),
SetTractorRequirements(TractorRequirements),
SetBombPolicy(BombPolicy),
SetGameVisibility(GameVisibility),
StartGame,
DrawCard,
Expand Down
10 changes: 9 additions & 1 deletion core/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use shengji_mechanics::bidding::{
};
use shengji_mechanics::deck::Deck;
use shengji_mechanics::scoring::GameScoringParameters;
use shengji_mechanics::trick::{ThrowEvaluationPolicy, TractorRequirements, TrickDrawPolicy};
use shengji_mechanics::trick::{
BombPolicy, ThrowEvaluationPolicy, TractorRequirements, TrickDrawPolicy,
};
use shengji_mechanics::types::{Card, PlayerID, Rank};

use crate::game_state::play_phase::PlayerGameFinishedResult;
Expand Down Expand Up @@ -192,6 +194,9 @@ pub enum MessageVariant {
TractorRequirementsChanged {
tractor_requirements: TractorRequirements,
},
BombPolicySet {
bomb_policy: BombPolicy,
},
}

impl MessageVariant {
Expand Down Expand Up @@ -385,6 +390,9 @@ impl MessageVariant {
format!("{} required tractors to be at least {} cards wide by {} tuples long", n?, tractor_requirements.min_count, tractor_requirements.min_length),
GameVisibilitySet { visibility: GameVisibility::Public} => format!("{} listed the game publicly", n?),
GameVisibilitySet { visibility: GameVisibility::Unlisted} => format!("{} unlisted the game", n?),
BombPolicySet { bomb_policy: BombPolicy::AllowBombs } => format!("{} enabled bomb cards (any suit)", n?),
BombPolicySet { bomb_policy: BombPolicy::AllowBombsSuitFollowing } => format!("{} enabled bomb cards (must follow suit)", n?),
BombPolicySet { bomb_policy: BombPolicy::NoBombs } => format!("{} disabled bomb cards", n?),
})
}
}
18 changes: 17 additions & 1 deletion core/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use shengji_mechanics::bidding::{
use shengji_mechanics::deck::Deck;
use shengji_mechanics::player::Player;
use shengji_mechanics::scoring::GameScoringParameters;
use shengji_mechanics::trick::{ThrowEvaluationPolicy, TractorRequirements, TrickDrawPolicy};
use shengji_mechanics::trick::{
BombPolicy, ThrowEvaluationPolicy, TractorRequirements, TrickDrawPolicy,
};
use shengji_mechanics::types::{Card, Number, PlayerID, Rank};

use crate::message::MessageVariant;
Expand Down Expand Up @@ -313,6 +315,8 @@ pub struct PropagatedState {
#[serde(default)]
pub(crate) tractor_requirements: TractorRequirements,
#[serde(default)]
pub(crate) bomb_policy: BombPolicy,
#[serde(default)]
pub(crate) max_rank: MaxRank,
#[serde(default)]
pub(crate) game_visibility: GameVisibility,
Expand Down Expand Up @@ -912,4 +916,16 @@ impl PropagatedState {
Ok(vec![])
}
}

pub fn set_bomb_policy(
&mut self,
bomb_policy: BombPolicy,
) -> Result<Vec<MessageVariant>, Error> {
if self.bomb_policy != bomb_policy {
self.bomb_policy = bomb_policy;
Ok(vec![MessageVariant::BombPolicySet { bomb_policy }])
} else {
Ok(vec![])
}
}
}
22 changes: 22 additions & 0 deletions frontend/src/Initialize.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ interface IUncommonSettings {
setJackVariation: (v: React.ChangeEvent<HTMLSelectElement>) => void;
setHideThrowHaltingPlayer: (v: React.ChangeEvent<HTMLSelectElement>) => void;
setTractorRequirements: (v: TractorRequirements) => void;
setBombPolicy: (v: React.ChangeEvent<HTMLSelectElement>) => void;
}

const UncommonSettings = (props: IUncommonSettings): JSX.Element => {
Expand Down Expand Up @@ -577,6 +578,25 @@ const UncommonSettings = (props: IUncommonSettings): JSX.Element => {
numDecks={props.numDecksEffective}
onChange={(req) => props.setTractorRequirements(req)}
/>
{props.numDecksEffective >= 4 && (
<div>
<label>
Bomb cards (4+ identical cards beat any play of the same size):{" "}
<select
value={props.state.propagated.bomb_policy ?? "NoBombs"}
onChange={props.setBombPolicy}
>
<option value="NoBombs">Disabled</option>
<option value="AllowBombs">
Enabled (any suit, no suit-following required)
</option>
<option value="AllowBombsSuitFollowing">
Enabled (must follow suit)
</option>
</select>
</label>
</div>
)}
<div>
<label>
Should reveal kitty at end of game:{" "}
Expand Down Expand Up @@ -787,6 +807,7 @@ const Initialize = (props: IProps): JSX.Element => {
});
}
};
const setBombPolicy = onSelectString("SetBombPolicy");

const setKittyPenalty = onSelectStringDefault("SetKittyPenalty", null);
const setAdvancementPolicy = onSelectStringDefault(
Expand Down Expand Up @@ -1323,6 +1344,7 @@ const Initialize = (props: IProps): JSX.Element => {
setTractorRequirements={(requirements) =>
send({ Action: { SetTractorRequirements: requirements } })
}
setBombPolicy={setBombPolicy}
/>
<DifficultySettings
state={props.state}
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/gen-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ export type Action =
| {
SetTractorRequirements: TractorRequirements;
}
| {
SetBombPolicy: BombPolicy;
}
| {
SetGameVisibility: GameVisibility;
}
Expand Down Expand Up @@ -213,6 +216,7 @@ export type KittyTheftPolicy = "AllowKittyTheft" | "NoKittyTheft";
export type GameShadowingPolicy = "AllowMultipleSessions" | "SingleSessionOnly";
export type GameStartPolicy = "AllowAnyPlayer" | "AllowLandlordOnly";
export type BackToTwoSetting = "Disabled" | "SingleJack";
export type BombPolicy = "NoBombs" | "AllowBombs" | "AllowBombsSuitFollowing";
export type GameVisibility = "Public" | "Unlisted";
export type Card = string;
export type TrickUnit =
Expand Down Expand Up @@ -628,6 +632,11 @@ export type MessageVariant =
tractor_requirements: TractorRequirements;
type: "TractorRequirementsChanged";
[k: string]: unknown;
}
| {
bomb_policy: BombPolicy;
type: "BombPolicySet";
[k: string]: unknown;
};

export interface _Combined {
Expand Down Expand Up @@ -752,6 +761,7 @@ export interface Hands {
[k: string]: unknown;
}
export interface Trick {
bomb_policy?: BombPolicy & string;
current_winner?: number | null;
/**
* A parallel array to `played_cards` which contains the units corresponding to played cards that match the `trick_format`, or `None` if they don't match.
Expand Down Expand Up @@ -895,6 +905,7 @@ export interface PropagatedState {
bid_policy?: BidPolicy & string;
bid_reinforcement_policy?: BidReinforcementPolicy & string;
bid_takeback_policy?: BidTakebackPolicy & string;
bomb_policy?: BombPolicy & string;
chat_link?: string | null;
first_landlord_selection_policy?: FirstLandlordSelectionPolicy & string;
friend_selection_policy?: FriendSelectionPolicy & string;
Expand Down
Loading
Loading