Skip to content

Commit

Permalink
Adds option to protect both triples and tractors from pairs (#453)
Browse files Browse the repository at this point in the history
* fmt

* option to protect both triples and tractors from pairs

* frontend
  • Loading branch information
rbtying committed Jan 20, 2024
1 parent 6c66001 commit 15e5877
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 22 deletions.
4 changes: 1 addition & 3 deletions backend/src/shengji_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,7 @@ async fn register_user<S: Storage<VersionedGame, E>, E: std::fmt::Debug + Send>(
let (assigned_player_id, register_msgs) = g.register(name_)?;
info!(logger_, "Joining room"; "player_id" => assigned_player_id.0);
let mut clients_to_disconnect = vec![];
let clients = associated_websockets
.entry(assigned_player_id)
.or_default();
let clients = associated_websockets.entry(assigned_player_id).or_default();
// If the same user joined before, remove the previous entries
// from the state-store.
if !g.allows_multiple_sessions_per_user() {
Expand Down
2 changes: 2 additions & 0 deletions core/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ impl MessageVariant {
format!("{} protected longer tuples from being drawn out by shorter ones (pair does not draw triple)", n?),
TrickDrawPolicySet { policy: TrickDrawPolicy::OnlyDrawTractorOnTractor } =>
format!("{} protected tractors from being drawn out by non-tractors", n?),
TrickDrawPolicySet { policy: TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor } =>
format!("{} protected longer tuples from being drawn out by shorter ones, and tractors from being drawn out by non-tractors", n?),
ThrowEvaluationPolicySet { policy: ThrowEvaluationPolicy::All } =>
format!("{} set throws to be evaluated based on all of the cards", n?),
ThrowEvaluationPolicySet { policy: ThrowEvaluationPolicy::Highest } =>
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/Credits.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const contentStyle: React.CSSProperties = {
transform: "translate(-50%, -50%)",
};

const changeLogVersion: number = 22;
const changeLogVersion: number = 23;

const ChangeLog = (): JSX.Element => {
const [modalOpen, setModalOpen] = React.useState<boolean>(false);
Expand Down Expand Up @@ -66,6 +66,13 @@ const ChangeLog = (): JSX.Element => {
game
</li>
</ul>
<p>1/20/2024:</p>
<ul>
<li>
Added the ability to protect both longer tuples and tractors at the
same time.
</li>
</ul>
<p>2/24/2023:</p>
<ul>
<li>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/Initialize.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,10 @@ const Initialize = (props: IProps): JSX.Element => {
<option value="OnlyDrawTractorOnTractor">
Only tractors can draw tractors
</option>
<option value="LongerTuplesProtectedAndOnlyDrawTractorOnTractor">
Longer tuples are protected from shorter, and only tractors can
draw tractors
</option>
<option value="NoFormatBasedDraw">
No format-based requirements (pairs do not draw pairs)
</option>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/gen-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,10 @@ export type BonusLevelPolicy =
export type KittyPenalty = "Times" | "Power";
export type KittyBidPolicy = "FirstCard" | "FirstCardOfLevelOrHighest";
export type TrickDrawPolicy =
| "NoProtections"
| ("NoProtections" | "NoFormatBasedDraw")
| "LongerTuplesProtected"
| "OnlyDrawTractorOnTractor"
| "NoFormatBasedDraw";
| "LongerTuplesProtectedAndOnlyDrawTractorOnTractor";
export type ThrowPenalty = "None" | "TenPointsPerAttempt";
export type ThrowEvaluationPolicy = "All" | "Highest" | "TrickUnitLength";
export type PlayTakebackPolicy = "AllowPlayTakeback" | "NoPlayTakeback";
Expand Down
47 changes: 36 additions & 11 deletions frontend/src/gen-types.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -640,11 +640,22 @@
"enum": ["JokerOrHigherSuit", "JokerOrGreaterLength", "GreaterLength"]
},
"BidReinforcementPolicy": {
"type": "string",
"enum": [
"ReinforceWhileWinning",
"OverturnOrReinforceWhileWinning",
"ReinforceWhileEquivalent"
"oneOf": [
{
"description": "A bid can be reinforced when it is the winning bid.",
"type": "string",
"enum": ["ReinforceWhileWinning"]
},
{
"description": "A bid can be reinforced when it is the winning bid, or overturned with a greater bid.",
"type": "string",
"enum": ["OverturnOrReinforceWhileWinning"]
},
{
"description": "A bid can be reinforced if it is equivalent to the winning bid after reinforcement.",
"type": "string",
"enum": ["ReinforceWhileEquivalent"]
}
]
},
"BidTakebackPolicy": {
Expand Down Expand Up @@ -3131,12 +3142,26 @@
}
},
"TrickDrawPolicy": {
"type": "string",
"enum": [
"NoProtections",
"LongerTuplesProtected",
"OnlyDrawTractorOnTractor",
"NoFormatBasedDraw"
"oneOf": [
{
"type": "string",
"enum": ["NoProtections", "NoFormatBasedDraw"]
},
{
"description": "Don't require longer tuples to be drawn if the original format was a shorter tuple.",
"type": "string",
"enum": ["LongerTuplesProtected"]
},
{
"description": "Only allow tractors to be drawn if the original format was also a tractor.",
"type": "string",
"enum": ["OnlyDrawTractorOnTractor"]
},
{
"description": "Both `LongerTuplesProtected` and `OnlyDrawTractorOnTractor`",
"type": "string",
"enum": ["LongerTuplesProtectedAndOnlyDrawTractorOnTractor"]
}
]
},
"TrickFormat": {
Expand Down
76 changes: 71 additions & 5 deletions mechanics/src/trick.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,13 @@ pub enum TrickError {
pub enum TrickDrawPolicy {
#[default]
NoProtections,
/// Don't require longer tuples to be drawn if the original format was a
/// shorter tuple.
LongerTuplesProtected,
/// Only allow tractors to be drawn if the original format was also a tractor.
OnlyDrawTractorOnTractor,
/// Both `LongerTuplesProtected` and `OnlyDrawTractorOnTractor`
LongerTuplesProtectedAndOnlyDrawTractorOnTractor,
NoFormatBasedDraw,
}

Expand Down Expand Up @@ -205,7 +209,9 @@ impl TrickFormat {
std::iter::once_with(move || {
subsequent_decomposition_ordering(
adj_tuples,
trick_draw_policy != TrickDrawPolicy::OnlyDrawTractorOnTractor,
trick_draw_policy != TrickDrawPolicy::OnlyDrawTractorOnTractor
&& trick_draw_policy
!= TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor,
)
.into_iter()
.map(|requirements| {
Expand Down Expand Up @@ -716,7 +722,8 @@ impl Trick {
points: all_card_points,
largest_trick_unit_size: tf.units.iter().map(|u| u.size()).max().unwrap_or(0),
failed_throw_size: self
.played_cards.first()
.played_cards
.first()
.ok_or(TrickError::OutOfOrder)?
.bad_throw_cards
.len(),
Expand Down Expand Up @@ -898,7 +905,8 @@ impl UnitLike {
TrickDrawPolicy::NoFormatBasedDraw
| TrickDrawPolicy::NoProtections
| TrickDrawPolicy::OnlyDrawTractorOnTractor => true,
TrickDrawPolicy::LongerTuplesProtected => !matching
TrickDrawPolicy::LongerTuplesProtected
| TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor => !matching
.iter()
.any(|(card, count)| counts_.get(card).copied().unwrap_or_default() > *count),
};
Expand Down Expand Up @@ -1759,6 +1767,21 @@ mod tests {
&[S_3, S_3, S_5, S_5],
TrickDrawPolicy::LongerTuplesProtected
));
assert!(!tf.is_legal_play(
&hand,
&[S_2, S_2, S_2, S_2],
TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor
));
assert!(tf.is_legal_play(
&hand,
&[S_2, S_2, S_3, S_3],
TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor
));
assert!(tf.is_legal_play(
&hand,
&[S_3, S_3, S_5, S_5],
TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor
));
assert!(tf.is_legal_play(
&hand,
&[S_2, S_2, S_2, S_2],
Expand Down Expand Up @@ -1804,6 +1827,16 @@ mod tests {
&[S_2, S_2, S_5, S_5],
TrickDrawPolicy::LongerTuplesProtected
));
assert!(tf.is_legal_play(
&hand,
&[S_2, S_2, S_2, S_2],
TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor
));
assert!(tf.is_legal_play(
&hand,
&[S_2, S_2, S_5, S_5],
TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor
));
// This play is tenuously legal, since the 2222 is protected by the 355 is not, and the
// trick-format is 2233. Normally we would expect that the 2233 is required, but the player
// has decided to break the 22 but *not* play the 55.
Expand Down Expand Up @@ -1842,6 +1875,16 @@ mod tests {
&[S_2, S_2, S_5],
TrickDrawPolicy::LongerTuplesProtected
));
assert!(tf.is_legal_play(
&hand,
&[S_2, S_2, S_2],
TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor
));
assert!(tf.is_legal_play(
&hand,
&[S_2, S_2, S_5],
TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor
));
}

#[test]
Expand Down Expand Up @@ -1870,6 +1913,11 @@ mod tests {
&[S_5, S_5, S_6],
TrickDrawPolicy::LongerTuplesProtected
));
assert!(tf.is_legal_play(
&hand,
&[S_5, S_5, S_6],
TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor
));
assert!(!tf.is_legal_play(
&hand,
&[S_6, S_7, S_8],
Expand Down Expand Up @@ -1899,6 +1947,16 @@ mod tests {
&[S_5, S_6, S_7, S_8],
TrickDrawPolicy::LongerTuplesProtected
));
assert!(!tf.is_legal_play(
&hand,
&[S_5, S_6, S_7, S_8],
TrickDrawPolicy::OnlyDrawTractorOnTractor
));
assert!(tf.is_legal_play(
&hand,
&[S_5, S_6, S_7, S_8],
TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor
));
}

#[test]
Expand Down Expand Up @@ -1932,6 +1990,11 @@ mod tests {
&[S_3, S_5, S_10, S_J, S_Q],
TrickDrawPolicy::NoFormatBasedDraw
));
assert!(tf.is_legal_play(
&hand,
&[S_3, S_5, S_10, S_J, S_Q],
TrickDrawPolicy::LongerTuplesProtected
));
assert!(tf.is_legal_play(
&hand,
&[S_3, S_6, S_8, S_8, S_8],
Expand All @@ -1944,7 +2007,7 @@ mod tests {
));
assert!(tf.is_legal_play(
&hand,
&[S_3, S_5, S_10, S_J, S_Q],
&[S_3, S_6, S_8, S_8, S_8],
TrickDrawPolicy::LongerTuplesProtected
));
}
Expand Down Expand Up @@ -2000,6 +2063,8 @@ mod tests {
TrickDrawPolicy::NoProtections,
TrickDrawPolicy::LongerTuplesProtected,
TrickDrawPolicy::NoFormatBasedDraw,
TrickDrawPolicy::OnlyDrawTractorOnTractor,
TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor,
] {
let mut hands = Hands::new(vec![P1, P2, P3, P4]);

Expand Down Expand Up @@ -2034,7 +2099,8 @@ mod tests {
}
TrickDrawPolicy::LongerTuplesProtected
| TrickDrawPolicy::NoProtections
| TrickDrawPolicy::OnlyDrawTractorOnTractor => {
| TrickDrawPolicy::OnlyDrawTractorOnTractor
| TrickDrawPolicy::LongerTuplesProtectedAndOnlyDrawTractorOnTractor => {
// This play should not succeed, because P2 also has S_K, S_K which is a pair.
if let Err(TrickError::IllegalPlay) = trick.play_cards(pc!(
P2,
Expand Down

0 comments on commit 15e5877

Please sign in to comment.