Wilson Sarruf

Portfólio

🕹️ Tetriz

O clássico jogo de empilhar blocos para Android, desenvolvido em Kotlin e Jetpack Compose. Inclui três modos (individual, multijogador local e VS IA), tabuleiro 10×20, top 100 de pontuações e layout adaptativo para telemóvel e tablet.

Ecrã do menu principal do Tetriz com o logótipo e os modos de jogo.
Menu principal
Tabuleiro do Tetriz durante uma partida a cores.
Tabuleiro em jogo
Modo VS IA com o oponente a jogar automaticamente.
Modo VS IA

Destaques

  • Motor de jogo próprio: rotação, bloqueio, níveis, pontuação e fim de jogo.
  • IA heurística no modo VS AI — pondera altura das colunas, linhas completas, buracos e irregularidade.
  • Persistência local (DataStore), acessibilidade TalkBack e badge PEGI.

KotlinJetpack ComposeMVVMCoroutinesDataStore

Código (exemplo)

private fun evaluateBoard(board: GameBoard): Double {
    val heights = getColumnHeights(board)
    val aggregateHeight = heights.sum()
    val completeLines = countCompleteLines(board)
    val holes = countHoles(board, heights)
    val bumpiness = calculateBumpiness(heights)

    return WEIGHT_AGGREGATE_HEIGHT * aggregateHeight +
            WEIGHT_COMPLETE_LINES * completeLines +
            WEIGHT_HOLES * holes +
            WEIGHT_BUMPINESS * bumpiness
}

Fonte: app/src/main/java/com/example/tetriz/domain/TetrisAI.kt

Prompt de código

«Cria um motor de Tetris em Kotlin/Compose com um oponente de IA heurística que pondere a altura das colunas, as linhas completas, os buracos e a irregularidade do tabuleiro.»

Aplicação Android — disponível para instalação direta no dispositivo através do APK.

🦆 Game Duck

Jogo de tiro ao pato "Feira", 100% em HTML, CSS e JavaScript puros (sem dependências). Mira por rato ou toque, munição com recarga automática, três níveis de dificuldade e top 10 de recordes guardado localmente.

Captura de ecrã do jogo de tiro ao pato com o pato amarelo ao centro da mira.
Captura de ecrã do jogo.

Destaques

  • Áudio sintetizado em tempo real com WebAudio (sem ficheiros de som).
  • Dificuldade ajustável (spawn, velocidade e tamanho do alvo).
  • Conformidade WCAG AA e rótulo IA (Regulamento UE — AI Act Art. 50.º, PEGI 7).

HTMLCSSJavaScriptWebAudioCanvas/SVG

Código (exemplo)

const AudioEngine = (function(){
  const ctx = new (window.AudioContext || window.webkitAudioContext)();
  function play(freq, time=0.06, type='sine', gain=0.12){
    const o = ctx.createOscillator();
    const g = ctx.createGain();
    o.type = type; o.frequency.value = freq;
    g.gain.value = gain;
    o.connect(g); g.connect(ctx.destination);
    o.start();
    o.stop(ctx.currentTime + time);
  }
  function hit(){ play(880,0.08,'square',0.12); }
  function shot(){ play(600,0.06,'sawtooth',0.12); }
  function reload(){ play(220,0.12,'sine',0.08); play(330,0.12,'sine',0.08); }
  function end(){ play(120,0.18,'sine',0.18); }
  return { hit, shot, reload, end };
})();

Fonte: js/game.js

Prompt de código

«Gera um jogo de tiro ao pato sem dependências externas, com mira por rato e toque, sons WebAudio, recordes em localStorage e classificação etária PEGI 7.»

Aplicação estática — hospedagem prevista no Oracle Cloud Free Tier (always free); implementação futura.

📊 Dashboard Financeiro

Dashboard financeiro global com seis vistas (Mundo, União Europeia, Portugal, Comparador, Calendário e Fontes). Cruza dados oficiais do Eurostat via SDMX 2.1 com gráficos interativos em Chart.js e mapa mundial em SVG.

Ilustração de gráfico de barras ascendente e indicador HICP.
Previsão gráfica do dashboard (gráficos Chart.js + mapa SVG).

Destaques

  • Coletor real de dados do Eurostat (HICP) com cache e tratamento de erros.
  • Comparador entre geografias com exportação CSV e filtros.
  • Backend FastAPI (uvicorn) com modo de dados de demonstração.

PythonFastAPIPydanticChart.jsSDMXSVG

Código (exemplo)

params = {
    "geo": geo,
    "unit": "RCH_A",
    "lastTimePeriod": ultimos_periodos,
    "format": "JSON",
}
url = f"{EUROSTAT_BASE}/{DATASET_HICP}"

async with httpx.AsyncClient(timeout=20) as client:
    resp = await client.get(url, params=params)
    resp.raise_for_status()
    payload = resp.json()

Fonte: backend/app/collectors/eurostat.py

Prompt de código

«Constrói um dashboard financeiro com backend FastAPI e recolha de dados reais do Eurostat (SDMX), gráficos interativos em Chart.js e mapa mundial em SVG.»

Requer backend FastAPI — hospedagem diferenciada prevista no Oracle Cloud Free Tier (always free); implementação futura.

🔥 Dashboard Incêndios

Dashboard de risco de incêndio florestal (projeto Firewise) que cruza APIs científicas da União Europeia — EFFIS, GWIS e Copernicus — com meteorologia do Open-Meteo. Gera projeções por regressão linear e um motor de medidas de mitigação priorizadas.

Ilustração de chama sobre colinas com indicador FWI.
Previsão gráfica do dashboard (tema Firewise).

Destaques

  • Índice FWI, meteorologia e risco em tempo real com atualização programada.
  • Solution Engine: medidas de silvicultura, hidráulica, agrícola e logística priorizadas por risco.
  • REST API e interface Thymeleaf + Tailwind; base de dados PostgreSQL/H2.

Java 17Spring BootThymeleafTailwindChart.jsEFFIS/GWIS

Código (exemplo)

public List<Solution> gerarSolucoes(FireRisk risco) {
    List<Solution> solucoes = new ArrayList<>();
    if (risco == null) {
        return solucoes;
    }
    FireRiskLevel nivel = risco.getNivelRisco();
    double temperatura = valorOuZero(risco.getTemperatura());
    double humidade = valorOuZero(risco.getHumidade());
    double vento = valorOuZero(risco.getVelocidadeVento());
    double precipitacao = valorOuZero(risco.getPrecipitacao());
    double fwi = valorOuZero(risco.getFwi());

    boolean seco = humidade <= 30.0;
    boolean ventoso = vento >= 30.0;
    boolean deficitHidrico = precipitacao < 15.0;
    ...
}

Fonte: src/main/java/pt/firewise/dashboard/service/SolutionEngineService.java

Prompt de código

«Cria um dashboard de incêndios em Spring Boot que cruza APIs científicas da UE (EFFIS/GWIS/Copernicus) e gera medidas de mitigação priorizadas por nível de risco.»

Requer backend Spring Boot — hospedagem diferenciada prevista no Oracle Cloud Free Tier (always free); implementação futura.

♟️ Game Xadrez

Xadrez completo no navegador, em JavaScript puro, com motor próprio: movimentos legais, roque, en passant, promoção, xeques e xeque-mate. Inclui IA minimax com poda alfa-beta, limites de tempo e níveis de dificuldade.

Captura de ecrã do jogo de xadrez com o tabuleiro ao centro.
Captura de ecrã do jogo.

Destaques

  • Motor completo com notação de movimentos e deteção de empate.
  • IA minimax + poda alfa-beta com ponderação de material e limitação de tempo.
  • Jogo para duas pessoas no mesmo dispositivo ou contra o computador.

HTMLCSSJavaScriptMinimaxAlfa-beta

Código (exemplo)

function search(d, alpha, beta) {
  if (timeLimitMs && Date.now() - start > timeLimitMs) throw 'timeout';
  if (d === 0) return evaluate(pos);
  const moves = ChessEngine.generateLegalMoves(pos);
  if (moves.length === 0) return ChessEngine.isCheck(pos, pos.side) ? -999999 : 0;
  let value = -Infinity;
  for (const m of moves) {
    const rec = ChessEngine.makeMove(pos, m);
    const v = -search(d - 1, -beta, -alpha);
    ChessEngine.undoMove(pos);
    if (v > value) { value = v; if (d === rootDepth) best = m; }
    alpha = Math.max(alpha, v);
    if (alpha >= beta) break;
  }
  return value;
}

Fonte: script/ai.js

Prompt de código

«Implementa um motor de xadrez completo (roque, en passant, promoção) e uma IA minimax com poda alfa-beta em JavaScript puro.»

Aplicação estática — hospedagem prevista no Oracle Cloud Free Tier (always free); implementação futura.

💬 SuperChat SC

Chat ponto-a-ponto no navegador com WebRTC: texto, chamadas de voz/vídeo e partilha de ficheiros sem armazenamento intermédio. O servidor de sinalização (Node.js + Socket.IO) apenas reencaminha o handshake — zero retenção de dados (RGPD).

Ilustração de dois balões de conversa ligados ponto a ponto.
Previsão gráfica da ligação P2P entre dois utilizadores.

Destaques

  • Ficheiros fatiados em blocos de 16 KB com verificação SHA-256 e retoma automática.
  • Credenciais TURN REST com expiração (coturn) para casos sem P2P direto.
  • Interface pt-PT/en-EU, chamadas áudio/vídeo e design privacy by design.

Node.jsExpressSocket.IOWebRTCRGPD

Código (exemplo)

io.on('connection', (socket) => {
  socket.on('create-room', (roomId, ack) => {
    if (!rooms.has(roomId)) rooms.set(roomId, new Set());
    if (rooms.get(roomId).size >= 2) {
      return ack && ack({ ok: false, error: 'full' });
    }
    socket.join(roomId);
    rooms.get(roomId).add(socket.id);
    if (ack) ack({ ok: true });
  });

  // Reencaminhamento efémero de sinalização WebRTC (offer/answer/ICE).
  socket.on('signal', (roomId, data) => {
    socket.to(roomId).emit('signal', { from: socket.id, data });
  });
});

Fonte: server.js

Prompt de código

«Implementa um chat ponto-a-ponto com WebRTC e um servidor de sinalização Socket.IO com zero retenção e conformidade RGPD.»

Requer servidor Node.js (Socket.IO) — hospedagem diferenciada prevista no Oracle Cloud Free Tier (always free); implementação futura.

📱 SuperChat Android

Versão nativa do SuperChat para Android, em Kotlin e Jetpack Compose com WebRTC (org.webrtc). Liga-se ao mesmo servidor de sinalização SuperChat SC e traz chat, chamadas e partilha de ficheiros em modo P2P no dispositivo.

Ilustração de um telemóvel com ecrã de conversa e balão de chat ao lado.
Previsão gráfica da aplicação Android.

Destaques

  • Partilha de ficheiros por blocos de 16 KB com controlo do buffer e verificação SHA-256.
  • Conexão WebRTC nativa (infobip M74) com fluxo correto setRemoteDescription/answer.
  • Diálogos de consentimento RGPD e interface em português/inglês.

KotlinJetpack ComposeWebRTCSocket.IOMVVM

Código (exemplo)

/**
 * Partilha de ficheiros pesados (chunking P2P).
 * Ficheiros fatiados em blocos de 16 KB no envio; na receção,
 * o hash SHA-256 é verificado antes de guardar em Downloads.
 */
class FileTransferManager(...) {
    companion object {
        const val CHUNK = 16 * 1024L
        const val BUFFER_HIGH = 512 * 1024L
        const val BUFFER_LOW = 256 * 1024L
    }
}

Fonte: app/src/main/java/com/example/superchat/p2p/FileTransfer.kt

Prompt de código

«Porta o SuperChat SC para Android nativo com Kotlin, Jetpack Compose e WebRTC, mantendo a partilha de ficheiros por blocos de 16 KB com verificação de integridade.»

Aplicação Android — disponível para instalação direta no dispositivo através do APK.

🌐 Site Pessoal

Portfólio profissional em HTML5, CSS3, JavaScript e PHP, com foco em acessibilidade (WCAG AA). Cinco páginas, separadores ARIA navegáveis por teclado, layout responsivo e formulário de contacto protegido contra bots e spam.

Ilustração de um monitor com páginas de um site.
Previsão gráfica do site (este portfólio).

Destaques

  • Formulário seguro em PHP: honeypot, rate limiting por IP/sessão, CSRF e validação server-side.
  • Menu de navegação móvel, saltar para o conteúdo e contraste AA.
  • Marcação semântica, sitemap, declarações de privacidade (RGPD/ePrivacy) e de acessibilidade (WCAG 2.1 AA/EAA), aviso de IA (AI Act) e otimização básica.

HTML5CSS3JavaScriptPHPWCAG AA

Código (exemplo)

const ORIGEM_PERMITIDA  = 'https://wsarruf.com';
const MAX_ENVIOS        = 5;
const JANELA_SEGUNDOS   = 600;

function resposta(bool $ok, string $mensagem, int $codigo = 200): void
{
    http_response_code($codigo);
    echo json_encode(['ok' => $ok, 'mensagem' => $mensagem], JSON_UNESCAPED_UNICODE);
    exit;
}

Fonte: enviar.php

Prompt de código

«Constrói um portfólio estático acessível (WCAG AA) com separadores ARIA e um formulário de contacto seguro em PHP (honeypot, rate limiting e CSRF).»

Já em produção em wsarruf.com (alojamento partilhado com PHP).