Warehouse Management System
WIP
(2026)
C#
ASP.NET
EF Core
Fluent Validation
Fluent Assertions
Scrutor
Serilog
PostgreSQL
Docker
Azure
TypeScript
Vue.JS
Pinia
TanStack Query
VeeValidate
Zod
PrimeVue
Tailwind CSS
Chart.js
ApexCharts

A full-stack warehouse management system covering the complete inbound → storage → outbound lifecycle: product and location master data, lot/expiry tracking, multi-dimensional location capacity, planned putaway and picking, an immutable stock-movement ledger, and an analytics dashboard.

The backend is an ASP.NET Core (.NET 10) Web API built with a clean, layered architecture. The frontend is a Vue 3 single-page app. Data is stored in PostgreSQL.


Live demo:   https://ambitious-wave-066504303.7.azurestaticapps.net/
Login: demo@wms.local
Password: 1wFWvd8zrS7!

Master data
  • Products — SKU, description, category, required temperature zone, unit weight/volume (used to compute location load).
  • Product categories — hierarchical tree (parent/child), browsable and reorderable.
  • Locations — code + structured address, type (Storage / Quarantine / Returns), temperature zone (Ambient / Chilled / Frozen), multi-dimensional capacity (Units / Weight / Volume — each independently capped, most restrictive wins), mixed-SKU / mixed-lot rules, active & blocked states, and per-location preferred products.
  • Lots — lot number and expiry date for batch/expiry tracking.
Inventory
  • On-hand / reserved / available quantities tracked per location + product + lot.
  • Manual inventory adjustments (with audit trail) and availability lookups.
Inbound — Stock-In (receiving & putaway)
  • Lifecycle: Draft → Putaway → Completed (cancellable from Draft or Putaway).
  • The system plans putaway placements automatically via a pluggable putaway planner. Strategies: PreferredLocation, ConsolidateSameLot, ConsolidateSameSku, Proximity, NearestEmpty, NearestAvailable.
  • Manual override or re-plan of placements while in Draft.
  • Capacity is reserved when putaway starts (row-locked) so concurrent receipts don't oversubscribe a location.
  • Item-by-item putaway confirmation; cancellation releases reservations and reverses already-placed stock.
Outbound — Stock-Out (picking)
  • Lifecycle: Draft → Picking → Completed (cancellable).
  • The system plans pick allocations across locations/lots via a pluggable picking planner. Strategies: FEFO (first-expired), FIFO, LIFO, LeastQuantity, plus Manual override.
  • Edit pick locations / re-plan in Draft; item-by-item pick confirmation; cancellation returns reserved stock.
Stock transfers & movement ledger
  • Move stock between locations.
  • Every quantity change (stock-in, stock-out, transfer, adjustment, and their cancellations) writes an immutable StockMovement record, produced as a side effect via domain events — a complete, append-only audit trail.
Dashboard & admin
  • Dashboard with overview, inbound, outbound, inventory and capacity tabs (charts via Chart.js / ApexCharts).
  • Authentication & roles: JWT login with three roles — Admin, Manager, Worker. All endpoints require authentication by default; write operations require Admin/Manager.
  • Admin panel: user management (create / list / delete users, change passwords) and database tools (seed demo data / truncate).
Job Offer Aggregator
(2026)
Source code
Python
FastAPI
SQLAlchemy
Pydantic
Playwright
BeautifulSoup
SQLite
TypeScript
Vue.JS
PrimeVue
Docker
OVHCloud

A job-offer aggregator for the Belgian market: a set of scrapers collects postings from five portals, stores them in a shared, deduplicated database and serves them through a single panel with filters. Runs start automatically on a schedule managed from the app, or manually with one button.

Built as a commissioned project for a company — deployed on the client's server (OVHCloud) as a set of Docker containers.

The backend is a FastAPI service (SQLAlchemy + Pydantic, SQLite, Alembic migrations). The frontend is a Vue 3 single-page app with TypeScript and PrimeVue.


Offer sources
  • Jooble — official API.
  • VDAB (the Flemish public employment service) — fetched with Playwright.
  • poloniusz.pl, axintor.be, impact.be — HTML listing parsing (requests + BeautifulSoup).
  • Each source has its own scraper behind a shared interface; scrapers never touch the database — they return validated offers, while persistence and deduplication are handled by the runner.
  • Deduplication on a source:external_id key with a UNIQUE index — repeated runs only append genuinely new postings.
  • A failing scraper doesn't stop the others; the full traceback is recorded in the run history.
Panel
  • Offers — server-side paginated table with sorting and filters (source, position, location, company, title fragment, publication date); new postings are flagged.
  • History — scraper runs grouped into batches, with status, found/new counts and error details.
  • Positions — dictionary of searched job titles (PL/NL/FR name + per-portal listing URLs), editable from the panel.
  • Schedule — automatic run times added and toggled without touching server configuration.
Running & deployment
  • The scheduler service doesn't scrape on its own — at the scheduled time it calls the API, so the automation and the panel button share a single lock and runs never overlap.
  • Password login with JWT tokens; the API isn't publicly exposed — nginx serves the panel and proxies requests to the backend.
  • The whole stack starts with a single docker compose up; the database and the positions dictionary live in a volume, so rebuilding images leaves them untouched. The schema is created by migrations executed on API startup.
Portfolio Template
(2026)
TypeScript
Angular
RxJS
SCSS
Bootstrap
GLightbox
JSON
Markdown

Portfolio website template built with the Angular framework. The application has no backend — all content comes from files in the public/assets directory, so creating your own portfolio comes down to replacing them, without touching the source code. The whole thing is built as a static site (SSG): every page, in every language, is rendered to finished HTML at build time and can be served from a CDN, with no application server.

Structured data (profile, experience, projects, technologies, site settings) is described in JSON files, while longer workplace and project descriptions are written in Markdown, in a separate file for each language.


Portfolio sections
  • About
    • profile — avatar, GitHub link, header text and a short description
    • technology stack — programming languages, frameworks and libraries, tools and environment
    • education — university, field of study, years of study
    • certificates — certificate name, issuer, issue date
    • spoken languages
  • Experience — a list of workplaces: company logo with a link, years of collaboration (including a "present" variant), technology stack and a description of responsibilities.
  • Projects — a list of projects: year, technology stack, description, media gallery and a repository link; closed-source projects are marked with a padlock, and those still in progress with a WIP badge.
  • Contact — email address.
Features
  • Prerendering (SSG)outputMode: static produces static HTML for every route and every language. Search engines and link previews get finished content instead of an empty <app-root>, and in the browser the page hydrates into a full SPA. The JSON data and Markdown descriptions fetched at build time are embedded into the resulting HTML, so the browser never fetches them a second time.
  • Multiple languages — the language is part of the URL (/pl, /en), and each language version has its own prerendered HTML. Interface texts come from translation files (@ngx-translate), and the list of available languages from the site configuration. On the root address the language is taken from the visitor's earlier choice, falling back to their browser preferences. The switcher in the navigation bar changes language while staying on the same page, without a reload.
  • SEO — every page carries a canonical link and a full set of hreflang links to its counterparts in the other languages (including x-default), and the lang attribute on <html> matches the content being shown. The CV download page is excluded from indexing.
  • Light and dark mode — theming based on CSS variables, toggled by a class on the body element, with smooth color transitions. The choice is remembered and applied before the page is first painted, so there is no flash of the other theme on load.
  • Media gallery — support for images and video. The file type is detected from the extension, thumbnails scroll horizontally, and the preview opens in a lightbox. GLightbox is loaded only once the first gallery appears, as a single shared instance for the whole page.
  • Thumbnail generator — the tools/generate-thumbnails.py script (Pillow, OpenCV) creates optimized JPEG thumbnails for images and from the first frame of videos, following the thumb_<name>.jpg naming convention.
  • Collapsible descriptions — long project descriptions are shortened by default, with a fade gradient and a "Show more" button. The button appears only when the content exceeds a given height, which is recalculated on window resize and after a language change.
  • Technology badges — each technology can be assigned a color in technologies.json; labels without an entry fall back to a default color.
  • Scroll animations — sections and cards appear in a cascade, triggered by an IntersectionObserver; each element animates once.
  • Responsive layout — based on the Bootstrap 5 grid, with a collapsible menu on mobile devices.
M2Chat
WIP
(2025)
Source code
C#
ASP.NET
EF Core
Fluent Validation
.NET MAUI
Blazor Hybrid
Bootstrap
MudBlazor
PostgreSQL

Desktop application for advanced monitoring of in-game chat in Metin2. Chat is read purely from the screen — the game window is captured every 100 ms and the text is recognized by a custom pixel-based OCR algorithm. The application does not interfere with the game process: no client files are modified, no memory is read, no code is injected.

The client is a Blazor Hybrid app (.NET MAUI, .NET 10) with a MudBlazor UI; screen capture is implemented for Windows (P/Invoke into user32/gdi32: GetDC + BitBlt over the client area of the selected window). The backend is an ASP.NET Core Web API with a PostgreSQL database.


Program Features
  • Game window selection (any window picked by the user) and continuous chat reading
  • Separation of player messages and system messages (detected by text color)
  • Grouping duplicates into a list of unique messages with first-seen / last-seen timestamps
  • Extraction of item links ([name]) as separate objects within a message
  • Boss tracker: remaining count per channel plus a countdown to the next respawn
  • Boss kill feed (time, channel, player, boss)
  • Boss kill rankings per player, with a configurable time window
  • Chat activity statistics (unique messages per minute)
  • Per-server configurations that can be shared with other users
Character Recognition

Instead of an off-the-shelf OCR engine, the app uses bitwise pattern matching, which fits the game's fixed raster font:

  • Charset build-up — the font bitmap (charset.bmp plus the character list in charset.txt) is scanned column by column; every glyph is encoded as a UInt128 mask and stored in a two-way dictionary (mask <-> character).
  • Line reading — for each of the 20 chat lines (bottom to top) consecutive pixel columns are scanned. A pixel in one of the colors defined in the configuration sets a bit in a UInt128 buffer (|= with a bit shift); an empty column ends the glyph and triggers a dictionary lookup.
  • Glued glyphs — when the full mask has no match, the buffer is progressively truncated with a bit mask starting from the longest variant, the matched fragment is removed with a bit shift, and the remainder carries over to the next iteration.
  • Spaces — detected from runs of consecutive empty columns.
  • Unknown glyphs — dumped to a log together with the bitmap fragment, which makes extending the charset straightforward.
  • Character color carries semantics: it distinguishes system messages from player messages and identifies the item type in a link (equipment / other).
Game Server Configurations

Private servers differ in font, chat colors, message format, channel count, server start hour, and boss list (name, count, respawn time).

  • Message patterns are stored with {Channel}, {Player}, and {Boss} placeholders and compiled into regular expressions with named groups, from which kill details are extracted.
  • Respawn times are derived from the server start hour and each boss's interval; the tracker counter resets automatically 3 minutes before a respawn (guarded against double resets).
Web API Service

ASP.NET Core Minimal API (.NET 10), EF Core + PostgreSQL, validation via FluentValidation.

  • User authentication using ASP.NET Core Identity (cookie-based authentication).
  • Configuration versioning: every save creates a new version (number, JSON payload, hash) linked to its parent entry.
  • A user can publish a configuration; others can import it (imports are counted).
  • An import records the version number — once the author publishes an update, importers are informed that a newer version is available and can sync it at any time.
  • The author can change visibility or delete the configuration (soft-delete); users who imported it earlier retain access.
Planned Features
  • Message filtering
  • Notifications for chat events
  • Muting players
  • Reading item bonuses from chat
  • Extended statistics panel
  • Chat streaming
Calyx Engine
(2024)
C++
OpenGL
GLM
ImGui
EnTT
Box2D
GameNetworkingSockets
Premake5

A 2D game engine written in C++ with a graphical editor: OpenGL renderer, Entity Component System (EnTT), physics simulation (Box2D), native scripting, and a custom networking module built on GameNetworkingSockets.

The core architecture is based on the open-source version of the Hazel engine, but it includes numerous modifications and custom-built solutions — the largest being an entirely custom networking module. It is a hobby project in an early stage of development, not intended for commercial game production; development is currently suspended.

As part of the master's thesis, the engine was extended with a networking module, which was then analyzed in terms of the impact of network latency on gameplay simulation quality. In particular, the study examined how different parameter values of the client-side prediction algorithm affect the frequency and scale of desynchronization.

The project is configured with Premake5 (Windows, Visual Studio) and builds in three configurations: Debug, Release (with the editor), and Distribution — an editor-free build that serves as the game runtime.

The engine was previously developed under the name Proton2D — the entire git history lives in the old repository https://github.com/Damian-Zuk/proton2d.


Editor (ImGui)
  • Viewport, scene hierarchy, object inspector, content browser (textures, scenes, prefabs), settings panel, and statistics panel.
  • Simulation control: start / pause / stop, with a snapshot of the scene state that is restored once the simulation ends.
  • Multiple game instances in a single process — each with its own viewport, scene manager, and network manager, which makes it possible to run a server and several clients side by side and test multiplayer gameplay without leaving the editor (a client instance can start automatically once the server goes up).
2D Renderer (OpenGL / Glad)
  • Batch renderer — up to 10,000 quads per batch by default, with the texture slot count read from the driver (up to 32); separate pipelines for quads, circles, lines, and text.
  • Sprites and texture atlases (TextureAtlas, Sprite), 9-slice scaling, and atlas-based animations.
  • Text rendering from MSDF atlases (msdf-atlas-gen), orthographic camera with zoom, framebuffers, and a draw call counter.
Scene, ECS, and Assets
  • Scene wraps the EnTT registry; Entity is a pair of an identifier and a scene pointer, with a UUID assigned to every object.
  • The parent-child hierarchy is implemented through a relationship component (pointers to the first child and to siblings), with local and world positions kept separate.
  • Serialization and deserialization of scenes and prefabs to JSON; a prefab is an object template shared across scenes and used when spawning objects over the network.
  • Physics runs on a fixed timestep (accumulator), independent of the frame rate.
Native C++ Scripting
  • Class hierarchy AppScript > GameScript > EntityScript (application, game mode, and single object logic), a script factory that instantiates classes by name, and a registration macro.
  • Script fields registered through a macro (REGISTER_FIELD) show up in the inspector and are serialized together with the scene; supported types include scalars, vectors, strings, and any trivially copyable data.
  • A field marked as replicated (REPLICATED_FIELD) is synchronized over the network automatically, optionally with a callback invoked when a new value arrives.
  • No hot-reloading; a C# scripting engine was planned for the future.
Physics (Box2D)

Rigid bodies, box and circle colliders with material parameters and collision filters, sensors, contact listening with a callback into the script, and revolute joints (attaching an object to its parent).

Networking Module

A client-server architecture with a fully authoritative server, running as a listen server, a dedicated server, or a client. All communication goes through a custom binary stream (the header is written back once the payload size is known), and every value read is validated against the remaining buffer size — an inconsistent packet disconnects the client instead of reading out of bounds.

  • Protocol — the handshake carries both the engine and the game protocol version; a mismatch in either closes the connection with a specific error code. Message types cover object spawn and despawn, replication, sequence number acknowledgements, and custom messages sent from scripts (no RPC).
  • Delta replication — the server keeps the last sent state separately for each client and sends only what changed: transform components are marked with bit flags (PositionX, PositionY, ScaleX, ScaleY, Rotation), while changes to script fields are detected with a CRC32 checksum computed per client. An object with nothing to report is rolled back out of the stream.
  • Cull distance — objects beyond a given radius from the player are not replicated, and their physics bodies are put to sleep on the client.
  • Late joining — the server keeps a record of every spawn and despawn, so a new client receives the full scene state along with a forced full replication. An object is sent as a prefab identifier, or as a complete JSON description when it is not a prefab.
  • Replication tickrate (64 Hz by default) independent of the frame rate, optionally synchronized with the physics tick.
Transform Synchronization

The synchronization method is set per object in the inspector, together with the reconciliation and teleport thresholds and the reconciliation duration:

  • Interpolation — movement is replayed between the two most recent authoritative states.
  • Extrapolation — the position is predicted from a velocity estimated as a blend of the local Box2D body velocity and the velocity implied by the last two server states, projected forward by the measured lag.
  • Client-side prediction (method description) — instead of replaying an input queue, the client buffers per-tick transform deltas along with a sequence number and sends that number to the server. The server echoes the last processed number back in the replication message, and the client discards the acknowledged deltas and applies the remaining ones on top of the authoritative state to obtain the predicted position.
  • Reconciliation — the error correction is spread over time (interpolation toward the predicted state, compensating for deltas accumulating during the correction) rather than snapping; only crossing the teleport threshold moves the object instantly.
  • Dynamic extrapolation — a custom solution: an object synchronized by interpolation switches to extrapolation the moment it makes physical contact with a predicted (locally controlled) object, and returns to interpolation once its divergence from the server state falls below a threshold. This limits the visible drift of objects colliding with the player character.
Diagnostic and Research Tools
  • Network latency simulation configurable from an editor panel (artificial lag on both incoming and outgoing packets).
  • Network statistics logging to file — ping, throughput in both directions, and the average number of replicated objects per message, sampled at fixed intervals and described by a header carrying the scene and tickrate; this data was used for the measurements in the master's thesis.
  • In-editor statistics: object counts (including scripted and networked ones), frame time, and per-client ping and throughput.
  • Assertion macros, an instrumentation profiler (writing a trace file), and a logger (spdlog).
CVE Report Generator
(2024)
Python
HTML
JavaScript
CSS
NVDLib
JSON
Nmap XML

A Python CLI tool that generates a vulnerability (CVE) report for IoT devices. It takes a manually defined device list in JSON format (model, firmware version, MAC address) and/or network scans produced by Nmap (XML). For every identified piece of software — firmware, services on open ports, and the operating system — the script queries the National Institute of Standards and Technology National Vulnerability Database (NIST NVD) and attaches the discovered vulnerabilities to the output report.

Built for the "Security of IoT Systems" course at Rzeszów University of Technology.


Processing Pipeline
  1. Load the device list (devices.json) — manually entered data forms the base of the report and takes precedence over scan data.
  2. Parse Nmap scans (XML, nmap -A -oX) — hosts are matched to listed devices by MAC address; unknown hosts are added as new entries. Extracted data includes: IPv4 address, vendor (from the MAC), only ports in the open state along with their services, and the OS detection result.
  3. Resolve CPE identifiers — for items without a CPE, a keyword search is performed (model + version, product + version, OS name + family + version). Identifiers in the older CPE 2.2 format are normalized to CPE 2.3.
  4. Fetch vulnerabilities — queries by cpeName; when no CPE could be resolved for a device, a keyword search is used instead and the CPE is backfilled from the first matching vulnerability.
  5. Save the report to a JSON file structured like the input, extended with a vulnerabilities section.
Report Contents

The report is an array of devices; each entry contains:

  • Device identification — model, description, MAC address, IPv4 address, vendor, firmware version, and the assigned CPE. Any custom fields from the input file are carried over into the report and rendered by the report viewer.
  • Open ports and services — port number, service name, product, version, and CPE.
  • Operating system — name, whether the match came from Nmap along with its accuracy percentage, and a list of OS classes (vendor, family, version, accuracy, CPE).
  • Vulnerabilities in three categories: firmware, service (keyed by product version), and os (keyed by family version). Each category holds the total number of CVEs found and the list itself, where a single entry carries the CVE identifier, publication date, description, the CVSS metric used, the numeric score, and the severity level (LOW / MEDIUM / HIGH / CRITICAL).
Configuration (config.ini)
  • Paths: output file, device list, and the directory or single file holding Nmap scans.
  • NIST NVD API key — shortens the delay between requests from 6 s to 0.6 s.
  • A list of operating systems excluded from database lookups (e.g. linux, android), where the number of hits is too large for the result to be useful; the report records a note about the skipped check.
Report Viewer

A static report-viewer.html page (no backend, no dependencies) — the JSON file is loaded via drag and drop. Devices are presented as collapsible panels, severity levels are color-coded, and CVE and CPE identifiers link directly to the corresponding NVD pages. The same view also handles the input device list, since it shares the exact same structure.

Limitations
  • CVE matching relies on an exact CPE — version ranges of vulnerable software (versionEndIncluding) are not evaluated, so a vulnerability described by a range may go undetected.
  • The accuracy of keyword search depends on how the vendor names its products in the NVD database.
Ansible Project
(2024)
Ansible
Ansible Semaphore
Ubuntu
AWS
PHP
MySQL
Apache

A student project using Ansible to automate the configuration management of virtual machines, installation of system packages, and deployment of a simple LAMP-based application.

The whole setup runs in the AWS cloud — the control node and the managed web servers were launched on separate instances. Playbooks are executed through Ansible Semaphore, a graphical interface for Ansible that provides browser-based task execution, schedules, a key store for access credentials, and a history of runs with their logs.

The core of the project was documenting the configuration of the entire solution — from preparing the instances and SSH access, through installing and configuring Semaphore, to describing how each role works. The repository contains Ansible files only: playbooks, roles, and the inventory.


Ansible Roles
  • create_admin_user — Managing system administrator accounts.
    • Creates users based on the admin list (with specified UID and description).
    • Configures SSH access by adding public keys to authorized_keys.
    • Grants sudo privileges by modifying the /etc/sudoers file.
  • update_system — Role responsible for operating system updates.
    • Refreshes the APT package cache.
    • Upgrades all installed packages to the latest available versions.
  • setup_apache_and_php — Installation and configuration of the web environment.
    • Installs the Apache HTTP server.
    • Installs PHP along with required modules.
    • Enables the PHP module in Apache.
    • Modifies PHP configuration: increases memory_limit to 256 MB.
    • Restarts the Apache service to apply changes.
  • setup_mysql_db — Installation and configuration of the MySQL database server.
    • Installs required dependencies (pip, PyMySQL).
    • Installs and starts the MySQL server.
    • Creates a MySQL user with full privileges.
    • Creates the application database.
    • Creates the posts table if it does not exist.
    • Inserts sample data into the posts table based on an SQL template.
  • deploy_website — Deployment of the web application and its configuration.
    • Pulls the latest version of the application from a Git repository.
    • Synchronizes the code to the /var/www directory.
    • Updates files only when changes are detected.
    • Creates the application configuration directory.
    • Loads database variables.
    • Generates the config.php configuration file from a Jinja2 template.
    • Injects database credentials and site parameters.
Poll Wizard
(2023)
Python
FastAPI
SQLAlchemy
Pydantic
PostgreSQL
Redis
TypeScript
React.JS
Bootstrap
Docker

A web application for creating simple single-question polls and voting on them, inspired by the StrawPoll.com platform.

The backend is a REST API built on FastAPI (SQLAlchemy ORM, PostgreSQL, validation through Pydantic), with Redis acting as the session state store and the rate limit counter. The frontend is a single-page application in React + TypeScript (Vite, Bootstrap). Everything runs through Docker Compose, and the API documentation is generated automatically (Swagger).


Application Features
  • User accounts — registration with reCAPTCHA, login, and authentication using JWT tokens.
  • Poll creation — a question plus 2 to 16 unique answers (validated on the API side).
  • Voting — one vote per poll for a logged-in user, with instant results (vote counts and percentage shares on bars).
  • User profile — a list of polls created by a given person; the author can delete their own poll.
  • Rate limiting — request limits counted in Redis, applied separately to whole endpoint groups and to sensitive operations (e.g. 3 sign-ups per minute, 5 new polls per minute).
Custom JWT Extension Backed by Redis

Rather than simply issuing tokens, I wrote a separate session management module (fastapi_jwt_redis) that plugs into the FastAPI dependency system:

  • Token pair — a short-lived access token and a refresh token, each with its own jti identifier, expiration time, and not-before time.
  • Refresh token rotation — every session refresh issues a new token pair, and the old refresh token is marked as used in Redis.
  • Reuse detection — an attempt to use a refresh token a second time (the classic symptom of a stolen token) is treated as a breach: the request is rejected and the entire session is invalidated.
  • Cascading revocation — Redis stores the links between tokens created across successive rotations, so a logout or a detected abuse recursively blacklists the whole chain of tokens descending from that session, not just the token presented in the request.
  • Verification at the entry point — a custom security class checks the header scheme, the signature, the expiration time, the token type match (a refresh token will not be accepted where an access token is required), and whether the identifier is on the blacklist.
  • Redis entries carry a time-to-live equal to the refresh token lifetime, so the store cleans itself up.
Security and Validation
  • Passwords hashed with Argon2, with complexity requirements (length, upper and lower case, a digit, a special character) enforced at registration.
  • Input validation through Pydantic schemas: length limits on the question and the answers, a uniqueness requirement for answers, username format checks, and verification that the email and username are not already taken.
  • Resource-level authorization — only the author can delete a poll, and a repeat vote on the same poll is rejected on the API side.
  • Sensitive configuration (JWT secret, token lifetimes, database credentials) comes exclusively from environment variables; development endpoints are registered only in debug mode.
Frontend

A React application with client-side routing and periodic background session refresh (the access token is exchanged before it expires). A single-poll view under its own address makes polls shareable, and the votes already cast by the logged-in user are fetched in one batched request covering every poll on screen.

Key features the application is missing
  • pagination,
  • GUIDs for poll links instead of IDs,
  • private polls (accessible via link only),
  • voting without an account,
  • a vote limit per IP address.
Auto Parts
(2021)
Python
Django
PostgreSQL
Bootstrap
JavaScript
Leaflet
Heroku
PWA
Agile Scrum

An e-commerce application for selling car parts. It was developed in a team of five in collaboration with a management faculty group using the Scrum methodology. The application was created as part of a university project course titled “Software and Database Engineering.”

Built with Django (Python) on a PostgreSQL database, with server-side templates and a Bootstrap-based interface. The application is localized for the Polish market, works as a PWA, and was deployed to Heroku (gunicorn, whitenoise).

My scope of work covered the product catalog and the presentation layer: filtering and sorting of the product list, splitting the assortment into sections and categories along with the navigation bar, pagination, discounted price handling with the sale view and the special offers section, as well as extracting the home view queries into a separate service layer. On the interface side I built, among others, the responsive navigation bar, the floating shopping cart, and the templates for the product page, the product list, login and registration, and the order summary.


Product Catalog
  • Hierarchical categories — a category can have a parent category; browsing a section shows products from all of its subcategories, and the navigation bar switches to the subcategories of the selected section.
  • Filtering and sorting (django-filter) by name, price, and discount, with pagination at 12 items per page. Price sorting uses the effective price — an SQL expression picks the discounted price when one exists and falls back to the base price otherwise.
  • Search across product names and descriptions.
  • Discounts — a dedicated sale view plus a special offers section on the home page; each product shows the price before and after the reduction along with the amount saved.
  • Product parameters stored in a JSON field, which makes it possible to describe different part categories with different attribute sets without changing the database schema.
Product Comparison

Built on an open-source Django module (the comparison list is kept in the session, with items added and removed over AJAX), extended with a parameter table sourced from the JSON field: the attributes of all compared products are merged into a single set and laid out row by row, with missing values marked by a dash. Comparison happens within a single category.

Store Locations and Geolocation
  • Sales points with an address, phone number, photo, and geographic coordinates, along with a search across city, address, postal code, and phone number.
  • Nearest store lookup — the browser obtains the user's position (Geolocation API) and the backend finds the closest point with an SQL query based on the haversine formula, with no need for GeoDjango. The result is returned as JSON and placed on an OpenStreetMap map (Leaflet) next to the user's own marker.
  • Stock levels are tracked separately for each location (product quantity at a given point).
Accounts and Orders
  • Registration with email activation — the account stays inactive until the user clicks a link carrying a one-time token; username and password validation is handled by custom validators (minimum length, a digit, an uppercase letter) with Polish messages.
  • User types — individual customer and repair workshop; the account type changes which payment methods are available.
  • Shopping cart — an order in the "not ordered" state acts as the cart, supporting adding items, decreasing quantities, and removing lines, with totals recalculated against active discounts.
  • Checkout — a delivery address form with country selection and a choice of payment method (BLIK, card, Apple Pay, Google Pay, PayPal).
  • PDF invoice — when paying by invoice (available to workshops), a document is generated with the order lines and the VAT rate, stored alongside the order and available from the purchase history.
  • A user profile with editable contact details and a list of placed orders.
Other Solutions
  • Product images, location photos, and invoice files are stored in the database (separate tables for content, filename, and MIME type) rather than on disk — convenient when deploying to a platform with an ephemeral filesystem.
  • The Django admin panel for managing the catalog, locations, and orders, with Polish model names.
  • A management command that generates test data (categories, several hundred products, locations) for development purposes.
Push The Box
(2020)
C++
SFML
CMake

A puzzle game inspired by the classic Sokoban — the goal is to push every crate onto its designated target tile. Written in C++17 with the SFML library and no ready-made engine: the game loop, state system, entities, animations, and UI controls were all implemented from scratch. The in-game interface is in Polish.

The project is built with CMake and targets the Win32 (x86) platform, matching the bundled SFML version.


Application Framework
  • Game loop with frame time measurement (delta time) and an FPS cap; window configuration and the asset list are loaded at startup.
  • State stack — the main menu, options, level selection, gameplay, and editor are separate states; only the state on top of the stack is updated and rendered, and closing it releases every entity it owns.
  • Entity layers — layer membership (up to 32 layers) is stored as a bit mask, which makes it possible to show and hide whole groups of elements within a single state (e.g. in the editor menu: main view / new level / load).
  • Entities with a parent-child hierarchy, position pinning to another entity, spritesheet animation, and smooth movement toward a target point.
  • Resolution-independent layout — positions and sizes are expressed in coordinates normalized against a 1920×1080 model and scaled to the actual window size.
  • Asset manager that loads textures and fonts based on an external asset list file (file name, identifier, smoothing).
  • Custom UI controls: button, checkbox, dropdown list, text box (character limit, digits-only mode, blinking cursor), and text label.
Gameplay
  • Movement in four directions with character animation; pushing a crate only succeeds when the tile behind it is free — collisions are checked against a grid of tile identifiers rather than against sprites.
  • Move undo — a register of the last 10 moves (direction, animation, and the pushed crate if any) working as a FIFO buffer; an undo replays the move for both the player and the crate and updates the collision grid.
  • Move and time counters, level restart, and control via keyboard or on-screen buttons.
  • The camera follows the player with a margin from the edge of the view and locks an axis whenever the board fits entirely on screen.
  • A level ends the moment every target tile is occupied; the result is written to the save file only when it beats the previous one (fewer moves or a shorter time).
  • 16 built-in levels plus a separate menu for custom levels.
Binary Level Format

Header (18 bytes): board dimensions, player start position, and crate count. It is followed by an array of target tile positions (8 bytes each) and the tile grid stored as one byte per field (no tile / floor / wall / crate).

The floor and wall texture variant is drawn from a seed derived from the level dimensions, so the board looks varied yet renders identically on every load.

Level Editor
  • Create a new level (name, dimensions from 5 to 99 per axis) or load an existing one for further editing.
  • Tools for placing walls, floor, target tiles, and the player start point, plus an eraser and a crate count control; a board larger than the view is scrolled with the arrow keys.
  • Validation on save — checks that the start point is set, that crates and target tiles are present, and that their counts match; the message lists every detected problem at once.
  • Saved levels land in the custom levels directory and are immediately available from the game menu.
Progress and Settings Persistence
  • Progress is stored in a binary file with one record per level (completion, best time, lowest move count), preceded by a SHA-256 checksum computed from the data and a fixed string. A checksum mismatch means the file was hand-edited or corrupted and results in the progress being reset.
  • Display settings (five predefined resolutions, fullscreen mode) are written to a configuration file; applying changes recreates the window and recalculates the interface layout without restarting the game.
Console Chat
(2019)
C++
Winsock
Diffie-Hellman Key Exchange
SHA256

A console-based client-server chat application for Windows, written in C++ on Winsock sockets (TCP) with no external libraries. The server and the client are two separate command-line applications; all the logic — accounts, ranks, permissions, bans — lives on the server side, while the client is only responsible for displaying output and forwarding whatever the user types.

An early hobby project, born out of a desire to learn network programming and multithreading.


Architecture and Protocol
  • A thread per client — the main thread accepts connections, while each authentication and session handler gets its own thread; access to the server files is guarded by a mutex.
  • A custom packet protocol — every packet is preceded by its type (message, command, input request), and the packet structure is selected by a template based on the type of the object being sent.
  • Server-driven input requests — the server can ask the client to prompt for text, passing the field label and whether the characters should be masked. Password login, account creation, and operation confirmations are all built on this mechanism, so the client needs no knowledge of any of those flows.
  • Public or local mode chosen at startup, along with the port, the slot count, and an optional server password; on the first run the server creates its file directory and a message of the day (MOTD).
Encryption and Authentication
  • The client and the server agree on a shared key through a Diffie-Hellman key exchange (modular exponentiation over a fixed base and modulus), and the agreed secret combined with the username is run through SHA-256, from which the actual session key is taken.
  • Every packet in both directions is encrypted with a byte stream generated by srand seeded with successive characters of the key and mixed into the data with XOR.
  • Account passwords are never stored in the clear — the server file holds their SHA-256 hashes (the algorithm implementation ships with the project).
  • The cipher used is a custom construction of low cryptographic strength — its purpose was to learn the mechanics of key exchange, not to genuinely protect the transmission.
Accounts, Ranks, and Moderation
  • Ranks: GUEST (unregistered), USER, VIP (colors in messages), MOD, and ADMIN, compared numerically — a command requires a rank no lower than its threshold.
  • Registration means setting a password for your own nickname; on subsequent connections the server asks for it in a masked field.
  • User commands: the list of available commands, who is online, private messages with a quick reply to the last correspondent, and disconnecting.
  • Moderator commands: details about online users, kicking someone off the server, IP bans, and reviewing the ban list.
  • Admin commands: promoting and demoting users, removing a password from an account, reviewing accounts and ranks, plus shutting down or restarting the server with the option to cancel the operation.
  • Timed and permanent bans — the duration is given in shorthand (e.g. 7d, 12h, 30m) and the expiry is stored as a timestamp; once it passes the entry stops applying, and the remaining time is presented in a readable form.
  • Accounts, ranks, and bans are kept in the server's text files, loaded at startup and updated as changes happen.
MTConsole — A Custom Multithreaded Console Library

A plain console is unsuitable for a chat: text arriving from the network breaks up the line the user is currently typing. A separate component was built for this project to solve that problem:

  • Input handled on a separate thread, character by character, with the input line drawn independently of the message stream — before a new line is printed, the input field is cleared and redrawn below it, and printing is guarded by a mutex.
  • Cursor movement within the text being typed (left/right arrows), inserting and deleting characters mid-line, with correct handling of wrapping at the console window edge.
  • History of entered commands, available through the up/down arrows.
  • Input masking with asterisks when typing passwords, plus the ability to hide and disable the input field entirely.
  • Text coloring through {number} markers embedded directly in the content — the same string can be printed with colors or stripped of the markers, which is used for the messages of VIP-ranked users, among other things.
Flappy Bird Clone
(2018)
Python
Pygame

A clone of Flappy Bird written in Python using the Pygame library. Pygame only handles the window, drawing, and input events — the game layer (objects, scenes, resources, update loop) was built from scratch.


Game Framework
  • Main loop with frame time measurement and an FPS cap; all movement is expressed in units per second and multiplied by the frame time, so the game behaves the same regardless of machine performance.
  • Object hierarchy — a base game object (with an update method and an active flag) extended by a drawable object (position, size, texture or fill color). The bird, pipes, background, texts, and buttons all derive from it.
  • Resource manager — central registries of updated and drawn objects plus a dictionary of resources accessible by name; the draw list is sorted by z_index, which establishes the layer order.
  • Scene manager — a scene is a named group of objects toggled in a single call: tap (start screen), playing, died, and died_new_best. Changing the game state comes down to enabling one scene and disabling the others, rather than hiding individual elements by hand.
  • Graphics layer with global scale factors that every coordinate and size passes through, and an AABB collision routine.
  • Font manager caching fonts by name and size, while text labels regenerate their texture only when the content has actually changed.
  • Debug overlay showing the frame rate, frame time, process memory usage, and the number of active pipes in the corner of the screen.
Pipe Generation and Recycling
  • The bird never moves horizontally — the game tracks the distance covered, and pipe positions are derived from that value together with the pipe index. This makes the world endless without storing any of its history.
  • The number of pipe objects is fixed and sized to the screen width; instead of creating and destroying pipes, the game slides a window of indices and reuses the same objects, assigning them new positions and heights.
  • Deterministic gap generation — the gap position for a given pipe number is derived from a seed built out of the run's start time and the pipe index. The same pipe always gets the same gap, so its parameters can be reproduced at any moment without remembering earlier draws.
  • A point is awarded once the bird passes the halfway point of a pipe's width, and collisions are checked separately against the top and bottom sections.
Flight Handling

Falling speed builds up to a terminal value, and the acceleration depends on the phase of flight — an ascent is damped more strongly than a free fall, which produces the characteristic jump arc. After a collision the bird switches to a separate falling mode with higher acceleration and a higher terminal speed, stopped only at ground level, while the end screen appears after a short delay.

High Score Persistence

The record is kept in a binary file preceded by a SHA-256 hash computed from the score and a fixed string embedded in the code. On load the hash is recomputed and compared against the stored one — a mismatch (that is, a hand-edited file) or corruption causes the record to be discarded and the count to start over from zero.

2D Platformer
(2018)
Source code
C++
SDL2

A 2D platformer game written in C++ on the SDL2 library, which only handles the window, texture drawing, and keyboard input — the game loop, objects, physics, camera, and map loading were all written from scratch.


Engine Framework
  • Frame loop measuring how long each frame takes; all velocities are expressed in units per second and multiplied by that time, so movement does not depend on machine performance.
  • Object hierarchy — a base game object (identifier, active flag, update method) extended by a dynamic object with position, velocity, size, and mass. The player and the map blocks both derive from it, which lets the physics operate on a single shared type.
  • Object registry maintained centrally by the engine, with a pool of recovered identifiers — the number of a deleted object returns to the pool and is handed out to the next one.
  • Type-based object lookup implemented as a template returning every object castable to a given class — used by both the physics and the rendering.
  • Virtual resolution — the world and the interface are described in a fixed 1920×1080 reference frame and converted through scale factors to the actual window size at draw time.
  • A custom two-dimensional vector template with overloaded arithmetic operators, plus an input manager that polls the keyboard state once per frame.
Map and Rendering
  • A level is loaded from a text file holding the tile size, the board dimensions in tiles, and a list of identifiers; a non-zero identifier creates a collision block. The test map is 300×200 tiles of 32 pixels each, giving a world of 9600×6400 pixels.
  • Tiles are drawn from a texture atlas — the identifier from the map file selects the atlas region.
  • Off-screen culling — before drawing, each object is checked against the area visible to the camera, and the rest of the board is skipped.
Physics and Collisions

Collisions do not work by pushing an object back once an overlap is detected. For each axis separately, every object lying in the path of movement is scanned and the nearest blocking edge in the direction of travel is determined, with the new position clamped to it. As a result an object never jumps through an obstacle even at high speed, and the map bounds act as a natural limit.

Gravity builds up in proportion to the object's mass until it reaches a set terminal velocity, while contact with the ground or ceiling zeroes the vertical velocity and updates whether the object is airborne — that flag gates the jump, which is only possible while standing on ground.

Character Controls

Horizontal movement accelerates from a starting speed up to a maximum, with lower acceleration in the air than on the ground and a slightly higher top speed while airborne — producing a noticeable difference between running and steering mid-jump. Changing direction in flight slightly adjusts the vertical velocity, and a separate key returns the player to the spawn point.

Camera and Parallax Background
  • The camera follows the player with a dead zone — it only moves once the character leaves a designated area of the frame — and is clamped to the map bounds so it never reveals space beyond the board.
  • During a fast fall the framing point smoothly shifts upward, exposing more space below the character, and eases just as smoothly back to its default once the fall slows.
  • Background layers are given a distance from the camera, and their scroll factor is derived from that distance and the field of view according to a perspective relation, rather than being hand-tuned per layer. Optionally, the layer's size is scaled from the distance as well.
  • Layers repeat horizontally without end, with rounding errors corrected at the seam between adjacent copies, and the same texture used repeatedly at different distances builds the sense of depth in the jungle.
Roulette Simulator
(2017)
HTML
CSS
JavaScript
Bootstrap
jQuery
SHA256

A roulette simulator inspired by the CSGODouble platform, running entirely in the browser — no backend, built on plain JavaScript with jQuery and Bootstrap. An early project from 2017, created while learning the basics of JavaScript and web development.

The wheel has 15 fields, and a bet is placed on one of three colors:

  • red (1–7) — 2X payout,
  • black (8–14) — 2X payout,
  • green (0) — 14X payout.

Result Generation (Provably Fair)

The algorithm reproduces the "provably fair" scheme used by platforms of this kind — the outcome is fixed before the spin begins and can be verified afterwards. The winning number is derived from three values:

  • seed — a secret string disclosed the following day so that the previous day's results can be verified (generated as the hash of a random 48–64 character string),
  • public — a public value, which here is the current date in year-month-day form,
  • round — the round number within that day.

The values are joined into a single string and passed through the SHA-256 hash function. The first eight characters of the result are read as a hexadecimal number, and its remainder modulo 15 determines the winning field. Further characters of the same hash also produce the offset within the field, so the pointer stops at a different spot of the same field every time — in an equally verifiable way.

Wheel Animation

The fields sit on a looped strip in alternating order (red and black on both sides of the zero), and a spin comes down to shifting the background by a computed distance. The movement decelerates exponentially — each step covers a fraction of the remaining distance, so the wheel slows down the more gently the closer it gets to its target. The starting distance spans several full revolutions plus the path to the drawn field, and the rate of deceleration is adjustable with a speed control.

Gameplay
  • A countdown to the start of the round with a progress bar; betting is locked while the wheel spins, and once the result is announced the round number increments automatically.
  • Bet limits: a minimum stake of 10 coins and at most 3 bets per round.
  • A stake field with shortcut buttons (+10, +100, +1000, +10000, half, double, all-in), while the balance and the pools on each color change through an animated counter, green on a win and red on a loss.
  • Free coins available to collect once the balance drops below a set threshold.
  • A history of the last ten results, sound effects for the spin and the stop, and event notifications.
Settings Panel

A separate panel makes it possible to inspect the generator first-hand: manually set or regenerate the seed, public, and round values, disable the automatic round increment, change the countdown length and the animation speed, lift the free coins limit, and start a spin immediately without waiting for the countdown to finish. By entering the same three values, any earlier spin can be reproduced and confirmed to have not been altered along the way.

Damian Żuk — Software Engineer © 2026