hamvocke / doppelkopf Goto Github PK
View Code? Open in Web Editor NEWA "Doppelkopf" card game - in your browser ♣️♠️♥️♦️
Home Page: https://doppelkopf.party
License: MIT License
A "Doppelkopf" card game - in your browser ♣️♠️♥️♦️
Home Page: https://doppelkopf.party
License: MIT License
Not an issue, but an update/announcement.
For the past few months I've tried building a multiplayer mode into the existing game. I started with a spike back in December 2020 - which confirmed my original (and naive) idea that it'd be possible to retrofit a multiplayer mode into this game somehow. As someone who's completely new to building multiplayer game servers, this spike validated a few initial assumptions around the tech stack (a JS front end, socket.io and a Flask backend) and the flow it'd take to establish a multiplayer game (establishing connections, joining game rooms, dispatching messages, handling disconnects/reconnects).
The following months I've tried bringing multiplayer into this code base. And while I made some progress over the last few months, today I'm officially announcing that I will not continue working on a multiplayer mode for this code base.
There's a few main reasons why I'm stopping multiplayer development
That's not to say there's nothing to learn from this experience. I did build a bunch of things that were somewhat working.
There's a "lobby" kind of concept (called waitingRoom
in our code). A player can start a game on the server. The server will create a game room that players can join. The server keeps track of all games, all players and the games that players have joined. The player starting a game can share a link to the game with other players. When opening the link, those players will establish a socket connection and join the right game until the room is full (4 players joined). In the meantime, the WaitingRoom
component will show who's waiting for the game to start.
socketClient.ts
, multiplayerHandler.ts
, WaitingRoom.vue
, waitingRoom.ts
and others) from the code base. I'm even thinking about nuking the entire backend/
project since it ceases to have any reason to exist. I'll drop all "multiplayer" related tasks from the project backlog.Right now the scorecard isn't scrollable. After playing multiple rounds the points appear not readable outside of the screen.
I really want this game to be playable by everyone who wants to play it, regardless of disabilities or other limitations.
One of the things we can do to improve here is to take care about proper accessibility. I'm far away from being an expert and need to read up on a lot of topics to get a better overview of what needs improvement, but here's a rough list of things to check and improve from the top of my head:
Playing the Game for the first time on mobile I believe I saw a few UI Flaws.
Image you play the game on a relatively small device, we have the issue that flexbox wraps into the next line, creating 3 cards in the first row and one in the second. Technically we still know that the order doesn't change, seeing the last card wrapping into the second line is really inconvenient, especially when cards just keep 'plopping' in. I strongly believe having static card slots where players drop their cards will simulate a real table and will improve intuition and therefore usability.
Another issue with this screenshot becomes clear when asking yourself the question. Who started the round? You need to calculate back. I suggest highlighting the player who actually started the trick will be another quick and dirty benefit to the game.
Take a look at the attached screenshot (which was modified using paint) to get a feeling what i mean.
Indeed, another great and easy improvement would be to highlight the winner of the trick with another colour.
Colourwise I like yellow since it's different to the blue and red colour scheme and blends into the current scheme very well (obviously not this yellow).
Technically i would suggest using gridview and the order property for the card-layout.
Think about the game some things come to my mind where this could be going and how it can be improved.
First something about the visuals:
Functionality
Currently the infrastructure is in a throwaway state. Servers hardly store any state and if they do it's not a big deal if that data gets lost. Servers can be rebuild, configuration restored automatically.
Once the app sees some real-world usage, keeping data around becomes more important. We need to come up with a way to backup data periodically and restore that data when we re-build our infrastructure.
Things to think about when backing up:
If it is the turn of the player, it would be comfortable to directly play the card, instead of pressing on next trick.
Currently there is a bug with Charlie extra point calculation. Sometimes Charlie doesn't win the trick, nor is caugth but still points are added.
Tests are all positive and logic behind this seems very reasonable.
I couldn't completely reproduce, but I believe there is some sort of issue with rounds and the when the trick.setLastTrickInRound()
function is called.
This issue is open to keep track of this, since I'm completely aware of this for some time now.
As discussed in #34: The current trick shows up as a linear view of the cards that have been played. On small viewports, cards can wrap into a new line if the space available isn't wide enough. This makes it hard to track which player has played a card and looks rather unnatural to people who ever played the game in real life.
Let's improve the current trick design by placing cards closer to the player who played the card. A combination of CSS's grid
and the playerPosition
property could be useful here.
See #7:
Longer Names are truncated. There should be a nice way to show names fully, no matter the screen size. Font scaling or a little different design could be possible
Currently deployments and infrastructure are quite tied to a lot of back-and-forth in approaches. This makes maintaining infrastructure quite messy and introduces a high barrier to entry for people who want to get started with setting up the entire project on their hosts.
I want to simplify infrastructure and deployments to:
docker-compose
With the upgrade to vue-cli 3 the storybook dependency broke. Adding storybook will create certain config artifacts (e.g. .babelrc) that break compatibility with vue-cli. Waiting for this issue to be resolved until I try again.
This should be some kind of gamelog.
To be honest I don't know if this is a good idea for production (since remembering played cards is kind of a mandatory strategy), but it's definately for dev.
I was serving the game on my local machine, when I experienced an error and wanted to check for the latest cards i played, but couldn't find a way.
Maybe it's just me and there are ways to look into such things, but adding some sort of gamelog would be really convenient.
Official Doppelkopf rules say that you can request the cards to be dealt again if your hand is too shitty to play:
https://de.wikipedia.org/wiki/Doppelkopf#Einmischen
We should come up with a way to either do this automatically or let the user request a new hand if their hand matches these criteria:
See #7:
It annoys me a little, that the cards of the player are not centered, because the trick stack is included for the centering. Centering the cards and therefore push everything now as it is just a little to the right, when looking at the human player.
Doppelkopf should work even when offline.
Using Service Workers should allow to cache content and redirect network requests to hit local caches. All communication to servers should be optional (telemetry, feature toggles) and use fallbacks for offline mode (either queue requests, rely on local storage or use default values).
An upgrade to vue-cli > 3.0 could simplify this by using the PWA plugin it provides. A first attempt at upgrading has failed due to incompatibilities with jest/vue-cli/storybook. In a couple of weeks I'll retry the upgrade and see if the PWA plugin helps resolve this issue.
See #7
Displaying a finished trick doesn't seem necessary anymore since we're able to show the previous trick #68.
Implementing a timer based next trick action with a short but reasonable wait time would eliminate the necessity of pressing the next trick button and therefore making the game faster.
For extremely fast paced play it would additionally make sense to make the finished trick clickable, triggering the next trick action.
On a UX perspective this adds the possibility to remove the next trick button completely, making space for the trick or other UI components.
After announcements have landed, we have another way for players to reveal which party they belong to (until now they could only do so by playing a queen of clubs).
Whenever a player (human or computer) makes a move that's clearly showing that they belong to the Re or Kontra party (announcing or playing a queen of clubs) they reveal information that's relevant for other players and will change the way they collaborate with that player. We should track this kind of bilateral information between players as something I'd love to call "affinities". We can use affinities for a couple of things:
An affinity value for another player could be a scale ranging from -1 (opponent) over 0 (unknown) to 1 (collaborator).
Keep in mind that affinities are always asynchronous information. One player might now that another player is their collaborator while the other player might be completely clueless.
An example:
1
-1
-1
0
for all)To implement this, we need to react to certain affinity events in the game. Playing the queen of spades is one of them, announcing is another one. Later on, we can even expand this to include things that look likely but are not 100% clear signs of affinity - a player serving with a fox (or another high value card) early in he game. These events could shift the affinity score slightly (to be tweaked) in one direction.
The game
instance could provide an affinityEvent
method that will be triggered whenever an affinity event happens - a trick
gets added a queen of clubs
or a player makes an announcement
. This method could go on and call every player
in the current round to handle the given affinity event. There they could evaluate which player triggered the event, look at their own state ("am I Re? an I Kontra?") and update their affinities towards the player who triggered the event.
Currently finding a fox relies on the findFox()
function. This will return the first Fox and check it's state.
This behaviour is a source to at least two problems.
findFox()
will calculate the extra on this card and saving a fox is worth only the pointsI will fix this issue, prepare for a PR soon 👍
When i build the docker, i get an error in ubuntu 20.04:
in the backend directory i type:
docker-compose build
after that i get the following error:
Building webserver
Step 1/7 : FROM caddy:2-alpine
---> 2c73dc9258a8
Step 2/7 : COPY dist/ /var/www
ERROR: Service 'webserver' failed to build: COPY failed: stat /var/lib/docker/tmp/docker-builder832339339/dist: no such file or directory
Currently the AI plays random valid cards. This is the most trivial, non-predictable implementation but of course doesn't make sense for anyone seeking real fun and a challenging game.
We could implement different computer player personas that the human player can choose from. Each with a different approach of playing and therefore with a different difficulty.
Some ideas to approach implementation:
See #7:
the notification bar is spamable to so other content is seeable. Limiting it to 1 seems reasonable because right now i cant think for a moment to display to notifications at the same time or within the 4 seconds its shown.
When playing a wedding the score isn't calculated correctly.
Obviously a wedding hasn't been implemented yet, nonetheless it will run the game as a "silent wedding"/"stille Hochzeit" ("silent solo"/"stiller Solo"). Summing all Points together should always equal zero, which isn't the case.
See images for details:
At the moment, different shades of the game's colors are defined within the vue component files. This will ultimately become a maintenance mess and lead to colors drifting apart.
Let's define colors and their different shades centrally, e.g. in src/assets/vars.css
.
As mentioned in issue #43, it would be a decent addition to have an additional button within the UI to see the last trick.
I can imagine one of these icons in the top right corner, next to the settings icon: ActivityIcon, InfoIcon or RotateCcwIcon from the icon pack we're currently using.
Pressing the icon could display a modal, using parts of the vue trick component, displaying the trick with the appropriate grid, maybe even showing the names.
Currently we're going a "functionality before design" approach, so don't spend too much time on optics - we can polish and make everything shiny later 😃👍🏻
See #7
Adding speech bubbles instead of the notifications would probably be an improvement for user experience.
Speech bubbles are commonly known and due to the very specific form easilyrecognizable. In it's nature of having white background colour a buuble would have high contrast to the game board and therefore easily to spot.
I can think of drawbacks though:
Speech bubbles are hard to stack. They suggest appearing at similar or the same position. They have different content and therefore different sizes. Current notifications do stack very well. Regarding issue #9 this might not be relevant though.
Discussed in #34: At the moment, we show "The trick goes to: " under the current trick. This is an obvious but not really smooth way of showing who's going to win the trick. The implementation was simple and stuck around for a while now but I think we can improve this design-wise.
Instead of showing the text message underneath the current trick, we can instead highlight the name of the winning player to signal that they're going to win the trick. The icon set we're using has a badge
icon we could use and display next to the player name, for example.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.