Use Python and Qt for a Skat game helper

Jan Walter September 20, 2023 [LANG] #python #qt #skat

This blog starts a series about using Python to implement some helper programs to learn the German Skat game (and keep track of things). Reading the rules on Wikipedia is a good introduction and gives you an overview, but to learn how to play the game I can recommend the Skat Island. So here is how a simple helper app might look like to keep track of which cards where played:

A simple helper app to keep track of which cards where played

I have written such an app in Python using the Qt library for the graphical user interface (GUI). To get started with Qt for Python read the official documentation.

But back to the screenshot above. The idea is to keep track of the cards which were played already, while playing a game (online or for real) with two other players. The variant above was written to first choose one of the supported varieties (see Wikipedia article), suit, grand, or null, of the game. In addition to those there is an unofficial variety, called Sächsische Spitze, which is similar to grand, just with reversed order. So on the left you see the jacks. If you click on one of the top two jacks you will select the grand variant, the other two (lower) jacks select the Sächsische Spitze. For a suit game select one of the aces (to the right of the jacks). Because there are four suits, Clubs (♣), Spades (♠), Hearts (♥), and Diamonds (♦), it matters which of the aces you clicked. For the last variant, null, just click one of the sevens (7), which one doesn't matter.

After selecting the variant of the Skat game, all cards get visible (ordered for a suit game), and you can click individual cards to flip them:

After selecting the variety you can flip individual cards

In the screenshot above I clicked the Queen of Hearts (♥), so it becomes invisible again. After having clicked all cards (and made them invisible), the situation is pretty much the same as when you started the helper program.

Using a counter

The first version of my Python program had a slightly different purpose, which I will explain later, but I'm working on a new version, which introduces counters to keep track of how many points each individual player has collected while playing out tricks. The screenshot above shows the situation after the first trick was played (for a Clubs suit game) and the full print out (to the shell or console where you started the Python script) looks like this:

$ ./skat_new.py
Clubs
2['J♥', 'J♠', 'A♦'] [0, 15, 0]
3['Q♦', '10♦', '7♦'] [0, 15, 13]
1['Q♥', 'A♥', '8♥'] [14, 15, 13]
1['A♠', 'K♠', '9♠'] [29, 15, 13]
1['J♦', '7♣', '7♠'] [31, 15, 13]
1['Q♣', '8♣', '7♥'] [34, 15, 13]
1['K♦', '8♦', '8♠'] [38, 15, 13]
2['9♦', '9♣', '10♠'] [38, 25, 13]
2['J♣', '10♥', 'K♣'] [38, 41, 13]
2['A♣', 'K♥', '10♣'] [38, 66, 13]
tricks: [  5,   4,   1]
scores: [ 38,  66,  13]
skat:   [  3]

So, while clicking away cards, which were played out, the program knows a little bit about the rules, knows which variant (in this case Clubs for the suit game), was chosen, and adds the values of each trick to the winner's counter, so it also knows how to detect, who won the trick. Because the source code is still under development, I'm not showing the entire Python code (or making it public yet), but here is how the current class structure and the methods involved are structured:

Some of the Python source code

Qt

Here some of the Qt classes used:

Writing to a File

You might have noticed that the first screenshot (without the counters) showed an option at the lower left, called Select two Skat cards first. The reason for that was that this version was writing a simple file to disk, which looks like this:

$ head /var/tmp/skat001.txt 
c
sl
20
12
24
27
25
31
19
16

The c stands for the Clubs variant, the sl for Skat last, whereas there is a sf for Skat first as well. The other numbers simply encode the played cards without any knowledge of the Skat rules. Where are those numbers coming from? In the past I started writing some Rust code to play Skat on the console (or in the shell). It had some options to record a game, like this:

$ ./target/release/skat -h
Usage: ./target/release/skat [options]

Options:
    -h, --help          print this help menu
    -i FILE             display a recorded Skat game
    -r, --record        record a Skat game
    -v, --version       print version number
$ ./target/release/skat -r
highest bid:
24
highest bid = 24
announced game [gbncshd]:
c
Clubs announced ...
Hand? Press '1' for yes:

Hand = no
Are you the declarer? Press '1' for yes:
1
Declarer = yes
Cards:
 4: J♣   0: A♣   1:10♣   2: K♣   3: Q♣   5: 9♣   6: 8♣   7: 7♣  
12: J♠   8: A♠   9:10♠  10: K♠  11: Q♠  13: 9♠  14: 8♠  15: 7♠  
20: J♥  16: A♥  17:10♥  18: K♥  19: Q♥  21: 9♥  22: 8♥  23: 7♥  
28: J♦  24: A♦  25:10♦  26: K♦  27: Q♦  29: 9♦  30: 8♦  31: 7♦  

card to play?
11
...

If you are the declarer you need to specify first which two cards to drop for the Skat, then you play the cards for all tricks. At the end of the match you got a card distribution printed out as well as the tricks being played:

...
Player A:
0: J♥  1: J♦  2:10♣  3: K♣  4: Q♣  5: A♥  6: A♠  7: K♦  8: 9♦  9: 7♦  
Player B:
0: J♣  1: J♠  2: A♣  3: 9♣  4: 8♣  5: 7♣  6: 8♥  7: K♠  8: Q♦  9: 8♦  
Player C:
0:10♥  1: K♥  2: Q♥  3: 7♥  4:10♠  5: 9♠  6: 8♠  7: 7♠  8: A♦  9:10♦  
A: J♥   J♠   A♦  
B: Q♦  10♦   7♦  
C: Q♥   A♥   8♥  
A: A♠   K♠   9♠  
A: J♦   7♣   7♠  
A: Q♣   8♣   7♥  
A: K♦   8♦   8♠  
A: 9♦   9♣  10♠  
B: J♣  10♥   K♣  
B: A♣   K♥  10♣  
New game? [press 'q' to quit]
q

All the details are not really relevant. The point is: Instead of tediously feeding the Rust program with numbers (which represent the Skat cards) you clicked all cards in an order, specifying if the first two cards (or the last ones) are representing the Skat, recorded the card order in a file (written by the Python code), and later used this option to display the card distribution and the tricks being played:

$ ./target/release/skat -i /var/tmp/skat001.txt 
FILE = /var/tmp/skat001.txt
Clubs announced ...
Skat last
Player A:
0: J♥  1: J♦  2:10♣  3: K♣  4: Q♣  5: A♥  6: A♠  7: K♦  8: 9♦  9: 7♦  
Player B:
0: J♣  1: J♠  2: A♣  3: 9♣  4: 8♣  5: 7♣  6: 8♥  7: K♠  8: Q♦  9: 8♦  
Player C:
0:10♥  1: K♥  2: Q♥  3: 7♥  4:10♠  5: 9♠  6: 8♠  7: 7♠  8: A♦  9:10♦  
successfully wrote to /var/tmp/skat001.dst
A: J♥   J♠   A♦  
B: Q♦  10♦   7♦  
C: Q♥   A♥   8♥  
A: A♠   K♠   9♠  
A: J♦   7♣   7♠  
A: Q♣   8♣   7♥  
A: K♦   8♦   8♠  
A: 9♦   9♣  10♠  
B: J♣  10♥   K♣  
B: A♣   K♥  10♣  
Skat:
 Q♠   9♥  

This time you get the two extra cards for the Skat printed as well. So, let's use Python again to show the card distribution (without the two Skat cards) and make it playable:

Playing the recorded game again via Python

This time the counter makes more sense, because you see one row per player:

Re-playing the recorded game and counting

The goal for the next version of the Python code is to combine both methods (recording, and re-playing) in a single application.

Back to top