From de45b6c3b077082ed930b542729197f24d829e66 Mon Sep 17 00:00:00 2001 From: truetype Date: Thu, 24 Sep 2026 14:55:53 +0200 Subject: [PATCH] Aggiungi documentazione: requisiti, implementazione, lezioni apprese --- docs/IMPLEMENTAZIONE.md | 62 +++++++ docs/LEZIONI.md | 62 +++++++ docs/REQUISITI.md | 46 ++++++ index.html | 354 ++++++++++++++++------------------------ pong.py | 166 +++++++++++++++++++ server3.js | 20 +++ 6 files changed, 500 insertions(+), 210 deletions(-) create mode 100644 docs/IMPLEMENTAZIONE.md create mode 100644 docs/LEZIONI.md create mode 100644 docs/REQUISITI.md create mode 100644 pong.py create mode 100644 server3.js diff --git a/docs/IMPLEMENTAZIONE.md b/docs/IMPLEMENTAZIONE.md new file mode 100644 index 0000000..19ffce3 --- /dev/null +++ b/docs/IMPLEMENTAZIONE.md @@ -0,0 +1,62 @@ +# Report Implementazione — Stato Attuale + +**Data:** 2026-09-24 +**Stato:** NON FUNZIONANTE + +## Cosa è stato fatto + +### Struttura file +``` +giochino/ +├── index.html # Gioco principale (Canvas JS) — NON FUNZIONA +├── server.js # Server HTTP Node.js +├── server2.js # Server HTTP Node.js (backup) +├── server3.js # Server HTTP Node.js (backup) +├── pong.py # Versione Python pygame — NON TESTATA +├── kitbitz-mcp-call.sh # Test MCP Kitbitz +├── validate.py # Script validazione JS +├── Schermata.png # Screenshot dell'utente +├── README.md # Documentazione base +└── docs/ # Questa cartella +``` + +### Server HTTP +- Creati 3 diversi server Node.js (`server.js`, `server2.js`, `server3.js`) +- Il server resta attivo su porta 8095 (via nohup) +- Servizio file HTML funziona (HTTP 200) + +### Gioco JavaScript — Problemi riscontrati + +#### Tentativo 1 +- **Problema:** Menu disegnato su Canvas non apparebbava (sfondo nero con solo testo HTML visibile) +- **Causa probabile:** Funzione `drawMenu()` non chiamata correttamente o errori JS silenziosi + +#### Tentativo 2 +- **Problema:** Menu con elementi HTML apparevano ma i tasti non rispondevano +- **Causa probabile:** Event listener non registrava correttamente i tasti frecce +- **Errore specifico:** La tastiera dell'utente genera codici non standard (`convert`, `nonconvert`, `NumPadEnter`) invece di `ArrowUp`/`ArrowDown` + +#### Tentativo 3 +- **Problema:** Pagina si blocca completamente dopo selezione "VS CPU" +- **Causa probabile:** Loop di gioco con errore runtime non gestito + +#### Tentativo 4 +- **Problema:** Nessun menu, gioco parte direttamente ma disco non si muove con F +- **Causa probabile:** Logica di stato mal implementata, conflitti nel loop principale + +### Analisi dei fallimenti + +1. **Nessun test incrementale:** Ho riscritto il codice da zero più volte senza verificare ogni passaggio +2. **Debug insufficiente:** Assenza di try-catch e logging effettivo +3. **Complessità prematura:** Ho tentato di implementare troppe funzionalità contemporaneamente +4. **Mancanza di validazione:** Il validator JS (`validate.py`) rilevava solo errori sintattici, non runtime +5. **Assunza errata sulle API:** Non ho verificato il comportamento reale degli eventi Canvas/tastiera nel browser + +### Cosa manca +- [ ] Menu funzionante con navigazione tastiera +- [ ] Selezione difficoltà +- [ ] Gioco vero e proprio con fisica disco +- [ ] IA CPU +- [ ] Sistema punteggio con set +- [ ] Impostazioni tasti funzionanti +- [ ] Integrazione con asset Kitbitz (mai iniziata) diff --git a/docs/LEZIONI.md b/docs/LEZIONI.md new file mode 100644 index 0000000..98d3f78 --- /dev/null +++ b/docs/LEZIONI.md @@ -0,0 +1,62 @@ +# Lezioni Apprese e Prossimi Passi + +## Errori commessi + +1. **Sovrainingegneria:** Ho creato troppe varianti di server e codice senza consolidare +2. **Mancanza di test incrementali:** Ogni riscrittura completa impediva di isolare i bug +3. **Assunzioni non verificate:** Ho assunto che frecce standard funzionassero, che Canvas rispondesse, che il loop JS non lanciasse eccezioni +4. **Debug "alla cieca":** Senza console del browser accessibile, non potevo vedere gli errori reali +5. **Nessuna architettura chiara:** Mescolare logica di rendering, input e stato in un unico file monolitico ha creato accoppiamento + +## Cosa farebbe funzionare + +### Approccio consigliato per il prossimo tentativo + +1. **Iniziare con un prototipo HTML puro** + - Menu come elementi HTML (non Canvas) + - Nessun JavaScript per il menu + - Solo click su pulsanti + +2. **Separare rendering e logica** + - Canvas SOLO per il gioco + - HTML per menu e UI + +3. **Testare ogni meccanica isolatamente** + - Prima: solo movimento giocatore + - Poi: solo disco che si muove + - Poi: interazione disco-giocatore + - Infine: IA e punteggio + +4. **Aggiungere try-catch con visibilizzazione errori** + ```javascript + try { /* codice */ } catch(e) { + X.fillStyle='red'; X.fillText('ERR:'+e.message,10,20); + } + ``` + +5. **Gestire tastiera in modo difensivo** + ```javascript + var up = ['ArrowUp','KeyW','KeyI']; + var dn = ['ArrowDown','KeyS','KeyK']; + // Testare effettivamente quali codici arrivano + ``` + +## Suggerimento alternativo + +Considerare l'uso di **pygame** (Python) invece di JavaScript/Canvas: +- Più semplice da debuggare +- Game loop più robusto +- Input gestito via eventi pygame +- Sarebbe necessario un server che serva un canvas via WebSocket o un'applicazione desktop + +## Riferimenti utili + +- [Windjammers Wiki](https://windjammers.fandom.com/wiki/Windjammers) +- [Fandom Wiki (regole dettagliate)](https://snk.fandom.com/wiki/Windjammers) +- [Gitea MCP](https://gitea.com/gitea/gitea-mcp) — server MCP per integrazione Gitea +- [opencode-review-gitea](https://github.com/ccsert/opencode-review-gitea) — skill per review PR su Gitea +- [Kitbitz MCP](https://kitbitz.art/docs/mcp) — endpoint MCP per asset grafici CC0 + +## Stato del repository Gitea + +La repository `truetype/giteo` è stata creata su http://192.168.0.183:8085 ma contiene il codice non funzionante. diff --git a/docs/REQUISITI.md b/docs/REQUISITI.md new file mode 100644 index 0000000..528d4e1 --- /dev/null +++ b/docs/REQUISITI.md @@ -0,0 +1,46 @@ +# Specifica dei Requisiti — Windjammers Arcade + +**Data:** 2026-09-24 +**Autore:** utente +**Stato:** incompleto — implementazione parziale con problemi + +## Obiettivo + +Creare un gioco web ispirato a **Windjammers** (noto in Giappone come **Flying Power Disc**), sviluppato da Data East e pubblicato da SNK nel 1994 per Neo Geo. + +## Requisiti concordati + +### Modalità di gioco +- **VS CPU** con tre livelli di difficoltà: Facile, Normale, Difficile +- **2 Giocatori** locale +- **Impostazioni** per riconfigurare i tasti di entrambi i giocatori + +### Meccaniche di gioco (ispirate a Windjammers) +- Campo diviso in due metà da una rete +- Gol suddiviso in zone da 3 e 5 punti +- Tre tipi di tiro: normale, lob (alto), supersonic (veloc) +- Slide (scivolata) per recuperare il disco +- 3 set per vincere la partita + +### Controlli +| Azione | P1 (default) | P2 (default) | +|--------|-------------|-------------| +| Movimento | WASD | Frecce | +| Tiro | F | K | +| Lob | G | L | +| Slide | H | ; | + +### Interfaccia +- Menu principale con selezione modalità +- Schermata difficoltà (solo VS CPU) +- Impostazioni tasti con riconfigurazione live +- HUD con punteggi e timer + +### Grafica +- Sprite procedurali (Canvas API) +- Stile hand-drawn (ispirato a Kitbitz, CC0) +- Sfondo campo, reti, porte colorate + +--- + +**Nota:** I requisiti sono stati discussi verbalmente e non sono stati formalizzati in un documento scritto prima dell'implementazione. diff --git a/index.html b/index.html index 526e0fe..170a516 100644 --- a/index.html +++ b/index.html @@ -1,240 +1,174 @@ - + -Windjammers Arcade +Windjammers
-
P1: 0
SET 1
CPU: 0
-
-
P1: W/A/S/D + F G H
+
+
P1: 0
+
SET 1
+
CPU: 0
+
diff --git a/pong.py b/pong.py new file mode 100644 index 0000000..c64406d --- /dev/null +++ b/pong.py @@ -0,0 +1,166 @@ +import pygame +import sys +import random + +# Inizializzazione +pygame.init() +W, H = 900, 500 +screen = pygame.display.set_mode((W, H)) +pygame.display.set_caption("Windjammers Arcade") +clock = pygame.time.Clock() +font = pygame.font.Font(None, 36) +big_font = pygame.font.Font(None, 72) + +# Colori +SKY = (135, 206, 235) +GRASS = (125, 206, 160) +WHITE = (255, 255, 255) +RED = (231, 76, 60) +BLUE = (52, 152, 219) +ORANGE = (243, 156, 18) +BLACK = (0, 0, 0) + +# Giocatori +p1 = pygame.Rect(100, 200, 30, 80) +p2 = pygame.Rect(770, 200, 30, 80) +p1_score = 0 +p2_score = 0 + +# Disco +disc = pygame.Rect(440, 240, 20, 20) +disc_vx = 0 +disc_vy = 0 +disc_owner = None # 'p1', 'p2', or None +disc_in_play = False + +# Rete +net = pygame.Rect(445, 80, 10, 340) + +def reset_disc(): + global disc_owner, disc_in_play, disc_vx, disc_vy + disc.center = (450, 250) + disc_vx = 0 + disc_vy = 0 + disc_owner = 'p1' if random.random() < 0.5 else 'p2' + disc_in_play = False + +reset_disc() + +def throw_disc(player): + global disc_owner, disc_in_play, disc_vx, disc_vy + disc_owner = None + disc_in_play = True + target_x = 850 if player == 'p1' else 50 + target_y = 100 + random.randint(0, 300) + dx = target_x - disc.centerx + dy = target_y - disc.centery + dist = max(1, (dx**2 + dy**2)**0.5) + disc_vx = dx / dist * 8 + disc_vy = dy / dist * 8 + +# Game loop +running = True +mode = 'menu' + +while running: + # Eventi + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_SPACE and mode == 'menu': + mode = 'playing' + reset_disc() + if event.key == pygame.K_ESCAPE and mode == 'playing': + mode = 'menu' + + if mode == 'menu': + # Menu + screen.fill((26, 26, 46)) + title = big_font.render("WINDJAMMERS", True, RED) + subtitle = font.render("FLYING DISC ARCADE", True, ORANGE) + hint = font.render("Premi SPAZIO per iniziare", True, WHITE) + screen.blit(title, (W//2 - title.get_width()//2, 120)) + screen.blit(subtitle, (W//2 - subtitle.get_width()//2, 200)) + screen.blit(hint, (W//2 - hint.get_width()//2, 320)) + + elif mode == 'playing': + # Input P1 + keys = pygame.key.get_pressed() + p1_speed = 5 + if keys[pygame.K_w] and p1.top > 80: p1.y -= p1_speed + if keys[pygame.K_s] and p1.bottom < 420: p1.y += p1_speed + if keys[pygame.K_a] and p1.left > 50: p1.x -= p1_speed + if keys[pygame.K_d] and p1.right < 430: p1.x += p1_speed + if keys[pygame.K_f] and disc_owner == 'p1': + throw_disc('p1') + + # AI P2 + if disc_owner == 'p2' and random.random() < 0.02: + throw_disc('p2') + elif disc_in_play and disc_owner is None: + # Move toward disc + if disc.centerx < p2.centerx: p2.x -= 3 + if disc.centerx > p2.centerx: p2.x += 3 + if disc.centery < p2.centery: p2.y -= 3 + if disc.centery > p2.centery: p2.y += 3 + # Keep P2 in bounds + p2.clamp_ip(pygame.Rect(470, 80, 380, 340)) + p1.clamp_ip(pygame.Rect(50, 80, 380, 340)) + + # Disco fisso al possessore + if disc_owner == 'p1': + disc.centerx = p1.right + 10 + disc.centery = p1.centery + elif disc_owner == 'p2': + disc.centerx = p2.left - 10 + disc.centery = p2.centery + elif disc_in_play: + disc.x += disc_vx + disc.y += disc_vy + # Rimbalzi + if disc.top < 80 or disc.bottom > 420: disc_vy *= -0.8 + if disc.left < 50 or disc.right > 850: disc_vx *= -0.8 + if net.colliderect(disc): disc_vx *= -0.7 + # Cattura + if p1.colliderect(disc) and disc_owner is None: + disc_owner = 'p1' + disc_in_play = False + elif p2.colliderect(disc) and disc_owner is None: + disc_owner = 'p2' + disc_in_play = False + # Gol + if disc.left < 50: + p2_score += 3 + reset_disc() + elif disc.right > 850: + p1_score += 3 + reset_disc() + + # Disegno + screen.fill(SKY) + # Campo + pygame.draw.rect(screen, GRASS, (50, 80, 800, 340)) + pygame.draw.rect(screen, WHITE, (50, 80, 800, 340), 3) + # Rete + pygame.draw.rect(screen, WHITE, net) + # Porte + pygame.draw.rect(screen, RED, (20, 180, 30, 140)) + pygame.draw.rect(screen, BLUE, (850, 180, 30, 140)) + # Giocatori + pygame.draw.rect(screen, RED, p1) + pygame.draw.rect(screen, BLUE, p2) + pygame.draw.ellipse(screen, ORANGE, disc) + # Punteggi + s1 = font.render(f"P1: {p1_score}", True, WHITE) + s2 = font.render(f"CPU: {p2_score}", True, WHITE) + screen.blit(s1, (200, 20)) + screen.blit(s2, (650, 20)) + # Istruzioni + hint = font.render("WASD=muovi F=tiro ESC=menu", True, (150,150,150)) + screen.blit(hint, (W//2 - hint.get_width()//2, 460)) + + pygame.display.flip() + clock.tick(60) + +pygame.quit() diff --git a/server3.js b/server3.js new file mode 100644 index 0000000..d5d4a9e --- /dev/null +++ b/server3.js @@ -0,0 +1,20 @@ +const http = require('http'); +const fs = require('fs'); +const path = require('path'); + +const PORT = 8095; +const ROOT = '/home/codex/giochino'; + +const MIME = { '.html': 'text/html', '.js': 'application/javascript', '.css': 'text/css', '.png': 'image/png' }; + +const server = http.createServer((req, res) => { + let filePath = path.join(ROOT, req.url === '/' ? 'index.html' : req.url); + const ext = path.extname(filePath); + fs.readFile(filePath, (err, data) => { + if (err) { res.writeHead(404); res.end('404'); return; } + res.writeHead(200, { 'Content-Type': MIME[ext] || 'text/plain' }); + res.end(data); + }); +}); + +server.listen(PORT, '0.0.0.0', () => console.log(`Server: http://0.0.0.0:${PORT}/`));