Code Monkey home page Code Monkey logo

level10's Introduction

Level10

A real-time multiplayer card game written with Phoenix LiveView

Animated gif of Level 10 in action

Development

After cloning the repo:

  • Install dependencies with mix deps.get
  • Install Node.js dependencies with (cd assets && yarn)
  • Start Phoenix endpoint with mix phx.server

Now you can visit localhost:4000 from your browser.

Simulate clustering

Level 10 takes advantage of Erlang clustering for scale and uptime purposes. While in development mode, the application uses Libcluster's Gossip strategy if a node name is provided when starting the application.

Thus, clustering can be simulated by starting up the application as follows:

# In one terminal window
PORT=4000 iex --cookie level10 --name 4000 -S mix phx.server

# In a different terminal window
PORT=4001 iex --cookie level10 --name 4001 -S mix phx.server

State Handoff

Whenever a node is terminated gracefully with a SIGTERM (as would occur with a normal rolling deploy), any game processes hosted on that node will be handed off to one of the other nodes in the cluster via the Level10.StateHandoff module. In order to simulate this with a cluster running on your local machine, you can use the following command inside of iex for whichever node you'd like to terminate:

:init.stop()

Production

You can build a docker image that can run anywhere docker images can with docker build .

The official version of Level 10 runs in Digital Ocean Kubernetes. You can do the same by tweaking a few files in the deployment manifest and running kubectl apply -f k8s

Contributing

Information about contributing can be found in CONTRIBUTING.md

level10's People

Contributors

brettbeatty avatar dependabot[bot] avatar dnsbty 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

Watchers

 avatar  avatar  avatar  avatar  avatar

level10's Issues

Close out finished games

When a game is finished, it should be automatically closed out so that finished games won't use up memory on the server.

Turn indicator sound

We should play a sound to indicate when it's your turn and get your attention in case you're playing in the background

Allow users to play cards to the table

Allow users to send selected cards to a group on the table.

This should allow them to click on a group with the proper cards selected. It should validate that the selected cards meet the group criteria, and if they do, move them out of their hand and into the group on the table.

sort cards on the table

Cards on the table should be sorted before being displayed so that runs will be in order and numeric cards will be most easily visible.

Join lobby by URL

We should make it so that games can be joined by a URL so that you can text out the URL rather than having to send the URL and join code separately

Start a round

We should have a function that starts a new round. This would take in a game struct and move it to the next round. It should do the following:

  • increment the current_round
  • get the level information for each user and replace the table info with it
  • shuffle a new deck of cards and set it to the draw_pile

Move game state to a GenServer

Having game state in an Agent was great for getting up and running quickly, but we can probably tune things a little better if we move into a GenServer.

Detect whether a round has ended

Whenever a player discards a card or plays new cards to the table, check to see if they have any cards left in their hand. If not, mark the round as ended.

Validate groups with wilds

Currently groups are validated, but wilds aren't taken into account. Add wild usage into the group validation logic.

Set up clustering

Elixir's Registry only works on the local process. Swap it out with Erlang's :global to set things up for clustering and make scaling possible.

:global docs: http://erlang.org/doc/man/global.html
Relevant blog post: https://scottyscripts.com/2019/11/25/erlang-in-elixir-erlangs-global-module.html
Relevant Elixir Forum thread: https://elixirforum.com/t/genserver-and-global-registration/13344

Perhaps longer term, syn should be considered instead based on these benchmarks but it seems like until we're seeing a ton of traffic, :global ought to work just fine

Make skip cards work

When a skip card is discarded, the player whose turn would normally be next is instead skipped over so the turn goes to the next player.

Create card struct

We'll want to create a struct to represent a card.

I think these should have three keys:

  • color: :red, :yellow, :green, :blue
  • number: :one through :twelve, :wild, :skip (not sure on the key for this one, open to suggestions)
  • value: one of 5, 10, 15, 25 to be used for scoring

Add online indicator

We should add Phoenix Presence to be able to detect if a player is currently online. This way the other players will know if they need to tell them it's their turn or not.

Add readiness indicators

On the scoring page, display indicators next to players that have marked that they're ready for the next round so that it's more obvious who you're waiting on

Store all the levels

Depending on how we handle the levels in the Game struct, we'll want to have the basic templates set up for them somewhere else. Perhaps a Levels module with a get function that takes in a level number and returns something like

[set: 3, run: 4]

or

[color: 7]

Add hand length indicators

We should add an indicator to show the size of each player's hand so that it will be more obvious if someone is getting close to completing the current round. This will also take care of the problem with seeing whether or not a player has drawn yet.

Add cards to completed groups

If a player selects one or more cards in their hand and then taps on a group that has already been completed, the selected cards should be attempted to be added to the specified group.

This will get a little more complicated with wilds if we want to make it so that wilds are in a static position and can't be replaced.

Create end game screen

Currently we show a scoring screen after every round with the scores and level. We should also create a screen for the end game that will display the final scores.

Store player ID in a cookie

Rather than store the player ID in the URL, we should store it in a cookie so that you can come back to the game if your battery dies or anything like that and the URL gets corrupted

Change creator if the original creator leaves

Right now if the creator leaves the game while in the lobby, the game becomes unplayable because no one else can start it. We should change it so that the next player in the list will become the creator at that point.

If there is no one else left in the list of players, we should then go ahead and delete the game.

Store game state in agents

We want to store game state in agents, and then use registries to find the proper agent based on a game code.

Create game struct

We'll want a struct for managing the state of the game. This will depend on #1 and #3. I'm seeing this looking something like the following:

  • current_round
  • players: list of players
  • draw pile: list of cards in the draw pile
  • discard pile: list of cards in the discard pile
  • hands: map with a list of the cards in each player's hand keyed by player name
  • scoring: map of score objects (containing level and score) keyed by player name
  • table: map of tabled cards keyed by player name to represent what's been laid out

Table Example

%{
  "Dennis" => [
    {:set, [%Card{}, %Card{}, %Card{}]},
    {:run, [%Card{}, %Card{}, %Card{}]}
  ]
}

Cap players in a game

We should probably put some sort of cap on the number of players that can join a game. I feel like 5 players is ideal, but we should be able to support up to 6 per game.

Properly handle join codes that don't exist

Whenever a player attempts to join a game that doesn't exist, the live view process appears to be crashing. We should handle that crash and display an error message.

I believe that we did this previously, but perhaps Horde's registry handles missing names differently or something like that.

Use proper cursors

During the game, we should make sure that cursors always show the possibilities. The pointer cursor should be used for anything that is clickable at the moment, and the default cursor should be used otherwise.

Validate groups

Create a function that will validate if a list of cards meets the requirements to be a group

Don't allow for wilds to be replaced

Whenever a run is played, we should track the position where each wild should belong so that it will be easier to see the range of the run. This would also allow for us to turn off wild replacement so that only the front and end of the run can be added to

Presence is inaccurate

Currently Presence is inaccurate if a user opens a second session with the same player ID and then leaves either session.

Rather than store only if a user is present or not, we should maintain a count of the user's presences, and only remove them if that count reaches 0.

Close out a game

We want to be able to close out a game. This should remove the game process and its state from the server.

Display scoring page

We should display a scoring page with all of the players in a game, their current level, and current score displayed. The screen should also have a button to go to the next round as soon as all the players are ready.

We should also link the "Check the Scores" button from the "Round Complete" modal to take the players to this page.

Create player struct

We'll want a struct for representing the users in the game. For the first version I see it having the following fields:

  • name
  • key (a key for identification whenever reconnecting to the game)

Further down the road, we might want more fields like:

  • device type (iOS, Android, or web)
  • device ID (for push notifications)

Add selected cards to groups

If a user selects a valid combination of cards and then taps on a group, those cards should be added to the specified group

Show turn indicator

Show an indicator in the game view of whose turn it is. Potentially it could also be shown if they've drawn yet or not

Shuffle the deck

Whenever a round begins, we'll want to create and shuffle a deck. We'll probably also want to make sure we can use the shuffle functionality again in case the draw pile ever runs out so that we can shuffle the discard pile and move it to the draw pile.

So this should create a new deck:

  • 2 x each number + :wild in each color
  • 4 x blue skip cards

Then it should shuffle that deck (or a different one that is passed in) so that the cards will be in a random order.

Add telemetry events

We should probably add a bunch of telemetry events for better observability:

  • Every time a game is created
  • Every time a game is joined
  • Whenever a new round is started
  • Whenever a game is left

Complete a round

Create a function that will take in a game struct and do the following:

  • calculate each player's score for that round and add it to their score
  • determine if each player completed the level and increment their current_level accordingly
  • check if anyone completed the 10th level, and end the game if so
  • clear the draw pile, discard pile, all table info, and player hands

Update whether player has drawn

Once it’s a new player’s turn it should reset whether the player has already drawn or not so that they can draw again without refreshing

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.