Coder Social home page Coder Social logo

rbtying / shengji Goto Github PK

View Code? Open in Web Editor NEW
84.0 8.0 42.0 3.96 MB

An online version of shengji (a.k.a. tractor) and zhaopengyou (a.k.a. Finding Friends)

Home Page: https://robertying.com/shengji/

License: MIT License

Rust 26.84% HTML 2.64% JavaScript 4.76% CSS 0.31% TypeScript 65.20% Dockerfile 0.25% Shell 0.01%
shengji tractor finding-friends zhaopengyou card-games

shengji's Introduction

What is this?

ๅ‡็บง is a popular Chinese trick-taking playing card game, also known as tractor, finding friends, fighting for 100 points, 80 points, etc. Rules are available here. Due to the COVID-19 shelter-in-place, I've been unable to play the this game in person... so I figured an online version would be worthwhile.

Usage:

cd frontend && yarn build && cd .. && cd backend && cargo run

The server is a self-contained static binary and does not terminate TLS. It listens on 127.0.0.1:3030, and should only be exposed to an external network behind a proxy that supports both HTTP and WebSocket protocols (only tested with nginx).

Development

cd frontend && yarn watch
cd backend && cargo run --features dynamic

Syncing types from Rust to Typescript

There are shared types in the Rust backend and the Typescript frontend; the frontend/json-schema-bin/src/main.rs file dumps the Rust types to JSON Schema, and the yarn types command will generate the Typescript types.

yarn types && yarn prettier --write && yarn lint --fix

Generating JSON

A mapping of card data is generated from the server. It's checked in at src/generated/cards.json. To update it, start up the server and run

yarn download-cards-json

Prettier

To format frontend code:

# Dry-run/check
yarn prettier --check

# Fix files, will overwrite files
yarn prettier --write

Lint

To run tslint:

cd frontend && yarn lint

And clippy:

cargo clippy

Tests

To run tests:

Frontend:

cd frontend && yarn test

Backend:

cargo test

Technical details

The entire state of each game is stored in the memory of the server process. Restarting the game kicks all players, and games are automatically closed when all players have disconnected. The bulk of the game logic is implemented in the server, but players are expected to keep each other in check -- the server does not validate moves in their entirety.

For simplicity, the game is written in Rust and Javascript, linking in Axum as the WebSocket/HTTP server implementation and using React from a CDN.

Known issues

  • No mobile support
  • Incomplete validity checking for forced-plays
  • No player limit per game
  • No overall player limit

Play online!

https://robertying.com/shengji/

shengji's People

Contributors

alvinsiu avatar aspin avatar cai-jack avatar danwang avatar dependabot[bot] avatar dkgramming avatar fangherk avatar fly-high-bj avatar hgbyhgby avatar jimmyfang94 avatar katexyu avatar kjzhang avatar mwilbz avatar precup avatar rbtying avatar tjohnwu avatar tliu30 avatar zenador avatar zenazn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

shengji's Issues

Throttle beeps to 1/second rather than 2/second

Our group found some joy in spamming beeps, but it should probably be throttled a tad (doesn't currently seem to be any limit). I'm not too familiar with React, but seems like there's a few decently easy ways to unmount the beeper for a second or two.

Related bug: sending too many beeps too quickly seems to cause the beep sender in question to need to refresh the page in order to play their next hand.

improved detail in game log messages in chat

Screen Shot 2020-04-07 at 10 21 15 PM

suggestion: if the backend can return structured data message like

{
  type: 'play',
  player: PlayerID,
  cards: Array[Card],
}

we could render in the chat more detailed information, like

  • rendering cards as displayed number + suit instead of the card text
  • making names clickable, or rendering them in a different color depending on team

Option to allow players to "steal the kitty"

Once Landlord has taken the kitty and set it down, other players in turn order may over-declare aka over-flip to a new trump suit, following standard over-flipping rules, to both change the trump suit and access the kitty. Exception: if you were the one whose declaration is the currently active, you may not over-flip your own declaration.

If over-flipping doesn't occur, the game immediately starts as normal.

If over-flipping occurs, after the kitty is set back down again, all of the players besides the over-flipper, including the landlord, may yet again over-flip, following the same rules, in turn order starting from after the over-flipper. And so on.

So this is possible:

  1. Landlord declares clubs by revealing a 2 of clubs
  2. Landlord reinforces by adding a second 2 of clubs
  3. Landlord takes kitty, swaps cards, and puts it down
  4. The player who goes after the landlord (player A) over-flips by revealing a pair of 2 of hearts; then takes kitty, swaps cards, and puts it down
  5. After confirming the other 2 players in turn order will not over-flip, it becomes Landlord's turn to over-flip if able. Landlord over-flips by revealing a pair of Little Jokers. Takes the kitty, swaps cards, then puts it down
  6. Player A over-flips by revealing a pair of Big Jokers. Takes kitty, swaps cards, puts it down.
  7. Nothing can over-flip Big Jokers so the game begins with Landlord leading.

Issues to resolve:

  1. I don't know how we'd take care of the edge case of no one declaring, but regardless if no one declares you can only flip with small and big joker, and not the other cards anymore
  2. If it's 3 decks people feel differently on how people should be able to flip with jokers...

Blank screen after card pickup for 9 player Find Your Friends

For game #2d3e38799f8d7e2d, the game refreshes and fails to load for the leader once cards are picked up. This was a 9 player game.

Console shows:

Error: Invalid card string: undefined
    at main.js:60
    at kr (main.js:60)
    at Qa (main.js:39)
    at vl (main.js:39)
    at su (main.js:39)
    at lu (main.js:39)
    at Zl (main.js:39)
    at main.js:39
    at t.unstable_runWithPriority (main.js:47)
    at Wo (main.js:39)
el @ main.js:39

Show grouping on throws

Since there is ambiguity in trick-format interpretation for throws, we should show the trick groupings after we've identified the trick format, so as to not surprise people about their legality.

The fastest way to do this is probably to use trick.units in the frontend for the leading trick, and then use played_cards for the remainder. This is kind of ugly from the data model point of view, so we could also make played_cards either a Vec<Card> or a Vec<TrickUnit>, depending on whether the play matches the format.

Implement throw penalties / take-back penalties

On an optional basis, we should allow games to run with explicit throw penalties.

This will require the following:

  1. additional data in the points data structure to track negative value
  2. handling in the various locations which read from the points struct to compute the total points
  3. some way which allows players to establish the penalty policy

Since there is currently no general state machine for throw mechanics (it's all done via the take-back mechanism), the fastest-path-to-happiness here is probably for there to be a way for players to specify point penalties, independent of the actual throw.

Longer tractor in Trump did not obligate Trump pairs

Trump was K / Spades

On Trick N, Player 1 played 778899 of Spades, and Player 2 played 6 single Trump, but no pairs.

On Trick N+2, after winning Trick N+1, Player 2 led with KK of Spades. Other players noticed that Player 2 played no Trump pairs during Trick N.

95322734_231570734844263_2756101803996086272_n

[Suggestion] Hide name of player who can beat a throw

Chat currently gives away name of player who has a higher card than a given throw.

Current Behavior

Chat shows:
<Player 1> tried to throw ๐Ÿƒ—๐Ÿƒ—๐Ÿƒ, but <Player 2> can beat it.

Suggested Behavior

Chat should show:
<Player 1> tried to throw ๐Ÿƒ—๐Ÿƒ—๐Ÿƒ, but someone else can beat it.

[Bug] Cannot set number of friends

Thanks again for creating this game ๐Ÿ’ฏ

Settings

  • Game Mode: Finding Friends
  • Number of Players: 6

Steps to Repro:

  • Attempt to select 1 friend for Number of Friends
  • Chat will show GAME: <Name> set the game mode to Finding Friends

Expected Behavior:

  • Game changes setting to only allow 1 friend

Actual Behavior:

  • Game shows incorrect message: GAME: <Name> set the game mode to Finding Friends

The longer tuples is not protected in this case

May be this is a corer case.
https://robertying.com/shengji/stats:
{"num_games_created":4,"num_active_games":1,"num_players_online_now":4,"sha":"ae9e896131daf4fc3b9a25ff7a682dffb0600f25"}

"(...)" - cards left in hand (probably does not matter)

  • Playing "Finding Friends"
  • This is not Trump suit.
  • Four Players Playing with 4 deck of cards
  • LongerTuplesProtected enabled
  • This trick plays 5 cards:

P1: 9 9 10 10 K (3 4 4 5 7 7)

P2: 3 3 5 5 7 (8 J Q)

P3: 3 5 10 J Q (6 8 8 8) <- This considered illegal

P4: 6 6 6 x x (no card in same suit left in hand) <- Only have 3 cards in the same suit

`cargo run` yields error: frontend/dist/ dir doesn't exist

All I did was install Rust, git clone the shengji project, then run cargo run on my MacBook (Mojave 10.14.6). Looks like frontend/dist doesn't exist.

error: couldn't read backend/src/../../frontend/dist/main.js: No such file or directory (os error 2)
--> backend/src/main.rs:581:19
|
581 | static JS: &str = include_str!("../../frontend/dist/main.js");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: couldn't read backend/src/../../frontend/dist/main.js.map: No such file or directory (os error 2)
--> backend/src/main.rs:583:23
|
583 | static JS_MAP: &str = include_str!("../../frontend/dist/main.js.map");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 2 previous errors

error: could not compile shengji.

Detect when the websocket connection has apparently died

It seems like the websocket connection will occasionally die for unknown reasons, without updating the client state to show that the connection has died.

We can probably detect this, because every websocket message is shortly followed by a state update from the server -- setting a few hundred millisecond timeout before alerting the user is probably sufficient

Allow "protected" trick-units

Have a game option where a player is not forced to play a "higher" play, even if a sub-component of that play matches the trick-format.

e.g.

  • 66 doesn't draw out JJJ, but tractors do (so 6677 would draw out JJJ)

Open questions: how to generalize to N decks; do pairs draw out tractors, etc.

Allow trump declaration from the kitty

If nobody bids a trump, allow cards from the kitty to be revealed and bid instead.

Two variants:

  1. suit of the first card in the kitty becomes the trump
  2. suit of the first card of the trump rank in the kitty becomes the trump. If none are found, suit of the highest card in the kitty becomes the trump

Revealing cards from the kitty prevents players from making bids themselves.

Game incorrectly treats any other tractor of greater or equal length as preventing a tractor-throw

For example, say there are two players in a game โ€“ let's call them Lincoln and Alex. If Lincoln wants to play a 5-card combo such as AKKQQ in a suit, and Alex only has four cards, it forces Lincoln to play the KKQQ, even though AKKQQ is a legit play. Then, when Lincoln plays the A, Alex can trump it and take control, much earlier than he should have been able to.

Note in our scenario, there was another player who has a pair AA in that suit as well, so that might have contributed to the bug.

"sleep" mode for players

Allow players to mark themselves as "suspended", so they act as observers until they come back

Fix "Moving Target" issue when making bids

When autodraw is on (the default), making bids involves a minigame of trying to hunt down the card to bid before clicking "make bid". This is not desired behavior. Here are two ideas I had for how to fix this:

  1. Move the trump cards to the left. This doesn't completely fix the moving target issue but it does cut it down by a factor of ~10. Probably the easiest to engineer.

  2. Have a row of five buttons in fixed positions (representing the four suits and NT. Maybe six if you want to separate NT into small Jokers and big Jokers) that are only activated when you can make that bid. In terms of bid quantity, probably the simplest solution is just that the buttons represent the lowest valid bid quantity; if you want to bid two cards on a previously un-bid round in a row you can simply double click.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.