This is a score tracking system developed for a group ping pong environment. It is written in Python and runs on a Raspberry Pi.
The system uses the ELO ranking algorithm to dynamically adjust player scores based on game outcomes, rewarding wins against higher-ranked opponents. Players' scores are tracked over time, with both monthly and lifetime win/loss records. The system uses a graphical user interface built with PyQt5, offering a user-friendly way to manage players, view leaderboards, and track ongoing games.
It also includes management and security functionalities like password-protected player profiles, the ability to add and delete user profiles, and the ability to manually edit a score.
Score Formula and Tracking
The formula to track the score is a bit complex, as it factors in the total amount won or lost, and the "expected outcome" of the other players. If you were expected to win and get beat by an underdog, you will lose a lot more than if you lose to someone who is ranked closely to you. Additionally, if you lose by a lot, you lose more points than if you lost by 1 or 2 game points.
Score change is calculated by:
K = 70 + point_difference*6
# Calculate the K factor based on the point difference
if player_is_unranked and opponent_is_unranked:
K = K*1.15 # Both players are unranked, 15% increase in volatility
elif player_is_unranked:
K = K*1.15 # 15% increase in volatility for unranked player
elif opponent_is_unranked:
K = 20 # The ranked player only gains/loses a maximum of 20 points
else:
pass # Both players are ranked, normal scoring applies
result = 1 if won else 0
expected_score = 1 / (1 + 10 ** ((opponent.score - self.score) / 400))
score_change = K * (result - expected_score)
self.score += score_change
self.games_played += 1
To change the volatility of the score calculations, increase k.
To view a player's lifetime score, and see their score over time, double click on a player name. You will see a display like this:
Note that lifetime score is distinct from "monthly" score. For more info on that, see admin controls.
The Scoreboard
There is a scoreboard display that takes up the screen while a game is in progress. This can be controlled by key inputs or with a mouse. When a game ends from this state, the game history, player scores/rankings, and data display are all updated automatically.
Player Accounts
I set up a pretty basic player account system where you can create a profile with a password, and you need to enter that password each time you want to play. This is to prevent a bad actor from coming in and creating a lot of artificial wins/losses for certain players, but it is a pretty weak security feature. Passwords are stored as plaintext in players.txt, and only serves as a basic deterrent from malicious behavior.
The Software
Running the program
To use the program, just run ping_pong.py
, and load in the scores. I would recommend loading the program with the test files (included in the GitHub) as it can have some weird behaviors/crash when trying to load in game history and there are only blank files. Once you create a player profile, feel free to delete the dummy accounts. This might get fixed in the future, but currently I see no demand for this to get changed.
Here's an interactive view of the Ping-Pong-Pi project directory structure:
- game_data
- game_history.txt
- players.txt
- modules
- extraDialogs
- adminControlsDialog.py
- addPlayerDialog.py
- gameResult.py
- player.py
- scoreboardDialog.py
- util.py
- extraDialogs
- README.md
- ping_pong.py
- settings.py
Here is the current file structure for the project. Note that all of the game history files are stored in the same directory as the primary program engine. I attempted to break up the functions of the program into modules, but a majority of the logic is still within ping_pong.py
.
This program was built for a TV display, where many people could watch and play. It is currently set up in a room where it is always on, ping pong is the only thing that happens in that room. I say this to explain that this program is not optimized for smaller screen sizes, and it is intended to stay on between games. Some of the features will be lost if the program is turned off and on between games.
Controls and Usages
Currently, all of the game controls are mapped to number keys (this is because I control the program with a numpad, see "the hardware" for more on that). These controls can be found/modified in keyPressEvent()
in scoreboardDialog.py
. All of the controls have on-screen buttons as well, but here is the current key mapping.
Admin Controls
Certain functions are behind an admin passcode, like the ability to manually set a score or delete players. To set the admin password, it needs to be hard coded into ping_pong.py
, in open_admin_controls_dialog()
The admin controls popup is pictured here as well. The "reset all scores" button does not delete lifetime scores, it just sets the current game to a blank slate. I use this to implement a "monthly score" feature, but you could reset the scoreboard as little or as frequently as needed.
The Hardware
This runs on a Raspberry Pi 3, which is just what I had available when I started this mini-project. I originally intended to wire buttons to the Pi, so you could hit buttons to change the score mid-game. I ended up using a Bluetooth numpad, as this was much easier to set up with the Python scripts. I painted numbers (like +1 and -1) to correspond to player actions, but there was not much tech customization that went into this. I am using a mouse & keyboard Bluetooth bundle for the main menu screen, which allows users to use a mouse to select their profile and check out game scores and history.