stackPython · TypeScript · Rust

🛠 Systems programming, applied ML, and things that run fast. Based in Rabat, Morocco.

Recent posts

more coming soon.

tagrust tagai taggame tag42cursus

Building a Gomoku AI in Rust: forbidden moves, alpha-beta pruning, and pain

This project is part of the 42 cursus at 1337/UM6P. The subject requires the AI to respond in under 500ms per move on a 19×19 board.

# Abstract

Gomoku is deceptively simple on the surface — place five stones in a row and you win. But with forbidden moves, forced captures, and a 19×19 board state space, building a competitive AI is a non-trivial systems problem. This post walks through the core implementation decisions: the board representation, the Minimax search with alpha-beta pruning, and the gnarly double-three forbidden move detection.

# Board representation

The board is stored as a flat [u8; 361] array. Two bits per cell encode three states: empty, black, white. Using bitboards (u64 pairs for each player) speeds up threat detection significantly — a five-in-a-row check becomes a bitwise scan rather than a loop over neighbors.

// Check horizontal five-in-a-row via bitmask fn has_five(bits: u64, mask: u64) -> bool { let b = bits & mask; b & (b >> 1) & (b >> 2) & (b >> 3) & (b >> 4) != 0 }

# The double-three problem

A "free three" is an unbroken sequence of three stones where both ends are open. Placing a stone that simultaneously creates two free threes is forbidden. The algorithm must enumerate all candidate moves, then for each candidate, scan four directions and count open-three formations — without counting the same sequence twice through different pivot points.

The hardest edge cases: overlapping sequences that share a stone, sequences blocked by the board edge vs. an opponent stone, and sequences that would become five-in-a-row (which overrides the double-three rule). After three rewrites, the get_forbidden_moves function finally passes all test vectors.

# Alpha-beta pruning

Minimax without pruning is impractical at depth ≥ 4 on a 19×19 board. Alpha-beta cuts the effective branching factor from ~50 legal moves to ~8-12 in practice. The move ordering heuristic sorts candidate moves by a fast static evaluation (proximity to existing stones + threat score) before recursing, which dramatically improves the pruning ratio.

With these optimizations, the AI reaches depth 5 consistently under 400ms on a modern CPU — comfortably within the 500ms budget.

tageeg tagml tagpython tagbci

Total Perspective Vortex: classifying motor imagery EEG with CSP + LDA

Dataset: PhysioNet EEGMMI — 109 subjects, 64 channels, 160 Hz. Target: ≥ 60% mean accuracy across subjects using a classical signal-processing pipeline.

# Abstract

Brain-computer interfaces that decode motor imagery from EEG are an active research area, but the classical CSP + LDA pipeline still delivers competitive results with interpretable intermediate steps. This post describes the full implementation: preprocessing with MNE-Python, covariance estimation, the generalized eigenvalue problem, spatial filter projection, and linear discriminant analysis.

# Common Spatial Patterns

CSP finds spatial filters W such that the projected signal maximizes variance for one class while minimizing it for another. Formally, this reduces to solving Σ₁w = λΣ₂w — the generalized eigenvalue problem. In practice: whiten the pooled covariance via Cholesky, transform both class covariances, solve the standard eigenvalue problem, and back-project the filters.

The resulting filters are interpretable as neural source components. The top and bottom k eigenvalues (by convention k=3) capture the most discriminative spatial patterns — motor cortex lateralization is directly visible in the weight maps.

# Results

Mean accuracy across 109 subjects: 67.3% for left vs. right hand imagery using 4 CSP components + LDA. Subject-level variance is high (range: 51%–84%), reflecting genuine neurophysiological differences — not model error. The interactive brain visualizer bonus renders the top CSP filter weights onto a 2D scalp topography in real time.

taggo tagstreaming tagffmpeg tagnextjs

CINETHOS: self-hosted movie streaming with Go, HLS, and FFmpeg

# Overview

CINETHOS is a self-hosted streaming platform that ingests video files, transcodes them to adaptive HLS segments using FFmpeg, and serves them through a Go API consumed by a Next.js frontend. Authentication and storage are handled by Supabase. The whole stack runs in Docker.

# Transcoding pipeline

On upload, a Go worker queues the file for FFmpeg transcoding. The pipeline generates three quality tiers (1080p, 720p, 480p) as HLS .m3u8 playlists with 6-second segments. The master playlist is stored in Supabase Storage; the Go API serves signed URLs with short TTLs to prevent direct hotlinking.

# Real video frames as cover images

Rather than requiring manual poster uploads, the pipeline extracts a frame at the 10% mark of each video using ffmpeg -ss with the -vframes 1 flag. The frame is resized to 600×900 and stored alongside the segments. This gave the GitHub repo screenshots actual visual content instead of placeholder images.

tagc++ tagnetworking tagrfc tag42cursus

ft_irc: writing an RFC-compliant IRC server in C++98

# Overview

ft_irc is a from-scratch IRC server written in C++98, strictly RFC 2812 compliant. It handles multiple simultaneous clients using non-blocking I/O via poll(), with no threads and no external libraries.

# I/O model

The server maintains a single poll() loop over all file descriptors: the listening socket plus one entry per connected client. Each readable FD is consumed into a per-client receive buffer; complete messages (terminated by \r\n) are parsed and dispatched to command handlers. Writable FDs drain a per-client send queue, ensuring backpressure without blocking the event loop.

# Protocol compliance

Implementing RFC 2812 faithfully means handling the full numeric reply space, MODE flags for channels and users, KICK/INVITE/TOPIC operator commands, and the subtleties of nick collision during registration. The server was tested against irssi and WeeChat; both connect and operate normally without modification.