Tressette ai 21 all'oratorio: in quattro a coppie, Andrea e il socio contro il giocatore di carte e l'altro. Obbligo di seme, punti in terzi e ultima presa, accuse (napoletana, tre o quattro Assi, 2 o 3), segni onesti di chi esce (busso, striscio, volo) coi loro suoni, si va ai 21 smazzata dopo smazzata. Avversari «Alla bona», «Seria» (gioca a regola ma non conta) e «A fassi male» (conta, legge semi finiti, accuse e busso del socio). Tavolo a quattro, pulsanti dei segni e della mano, tastiera B/S/V; test, taratura e prova nel browser
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A1dxvtsZx8NE9fwjXUNDBr
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { canPlay, legal, accuseName, NAMES } from './tressette.js';
|
||||
import { drawCard, drawTable } from './briscola-render.js';
|
||||
import { text, panel } from './bocce-render.js';
|
||||
|
||||
// La mano di Andrea a ventaglio, carte a grandezza naturale (42×60): coordinate condivise da disegno e tocco.
|
||||
export const CARD = { w: 42, h: 60, y: 288, lift: 8 };
|
||||
export function handX(n, i) {
|
||||
const gap = n > 1 ? Math.min(44, (440 - CARD.w) / (n - 1)) : 0;
|
||||
return Math.round(320 - (gap * (n - 1) + CARD.w) / 2 + i * gap);
|
||||
}
|
||||
// Le carte si coprono: vince quella sopra, cioè la più a destra.
|
||||
export function cardAt(c, [x, y]) {
|
||||
if (!canPlay(c)) return -1;
|
||||
const n = c.hands[0].length;
|
||||
for (let i = n - 1; i >= 0; i--) {
|
||||
const top = CARD.y - (c.selected === i ? CARD.lift : 0), left = handX(n, i);
|
||||
if (x >= left && x < left + CARD.w && y >= top && y < top + CARD.h + CARD.lift) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
// Dove cade la carta di ognuno al centro, e da dove arriva.
|
||||
const SPOT = [[299, 190], [354, 146], [299, 100], [244, 146]], FROM = [[0, 70], [80, 0], [0, -60], [-80, 0]];
|
||||
const LABEL = [[320, 280, 'center'], [612, 92, 'right'], [320, 39, 'center'], [28, 92, 'left']];
|
||||
const gold = '#ffe09b', paper = '#fff0cd', ours = '#9fe3ff', theirs = '#ffb0a3';
|
||||
const SAY = { busso: 'BUSSO!', striscio: 'STRISCIO!', volo: 'VOLO!' };
|
||||
|
||||
function others(ctx, c, names) {
|
||||
// Il socio di fronte, gli avversari ai lati: carte coperte, quante ne hanno.
|
||||
const n2 = c.hands[2].length, w2 = n2 ? (n2 - 1) * 12 + 26 : 0;
|
||||
for (let i = 0; i < n2; i++) drawCard(ctx, null, 320 - w2 / 2 + i * 12, 46, 26, 37, true);
|
||||
for (const [seat, x] of [[3, 30], [1, 584]]) {
|
||||
for (let i = 0; i < c.hands[seat].length; i++) drawCard(ctx, null, x, 102 + i * 12, 26, 37, true);
|
||||
}
|
||||
for (const seat of [1, 2, 3]) {
|
||||
// Chi deve giocare ha il nome in giallo.
|
||||
const [x, y, align] = LABEL[seat], turn = c.step === 'play' && c.current === seat;
|
||||
text(ctx, names[seat], x, y, 6, turn ? gold : seat % 2 ? theirs : ours, align);
|
||||
}
|
||||
}
|
||||
function scoreboard(ctx, c) {
|
||||
panel(ctx, 26, 34, 118, 38);
|
||||
text(ctx, `Noi ${c.score[0]}`, 34, 46, 7, ours);
|
||||
text(ctx, `Loro ${c.score[1]}`, 136, 46, 7, theirs, 'right');
|
||||
text(ctx, `Smazzata ${c.round} · ai 21`, 85, 62, 5, paper, 'center');
|
||||
}
|
||||
function accuses(ctx, c, names) {
|
||||
// Le accuse si vedono finché non è finita la prima presa.
|
||||
if (c.tricks.length || !c.accuse.length) return;
|
||||
panel(ctx, 456, 34, 158, 12 + c.accuse.length * 11);
|
||||
c.accuse.forEach((a, i) => text(ctx, `${names[a.who]}: ${accuseName(a)} +${a.points}`, 463, 41 + i * 11, 5, a.who % 2 ? theirs : ours));
|
||||
}
|
||||
function trick(ctx, c, names) {
|
||||
c.table.forEach((p, i) => {
|
||||
const slide = i === c.table.length - 1 && c.step === 'play' ? Math.max(0, 1 - c.t / 0.18) : 0;
|
||||
const [x, y] = SPOT[p.who], [dx, dy] = FROM[p.who];
|
||||
drawCard(ctx, p.card, x + dx * slide, y + dy * slide, CARD.w, CARD.h);
|
||||
if (p.signal) {
|
||||
const [lx, ly] = p.who === 0 ? [x + 21, y + 70] : p.who === 2 ? [x + 21, y - 8] : p.who === 1 ? [x + 66, y + 30] : [x - 24, y + 30];
|
||||
panel(ctx, lx - 30, ly - 7, 60, 14); text(ctx, SAY[p.signal], lx, ly, 6, gold, 'center');
|
||||
}
|
||||
});
|
||||
if (c.step === 'trick') text(ctx, `Prende ${names[c.last.winner]}`, 320, 262, 6, gold, 'center');
|
||||
}
|
||||
function hand(ctx, c) {
|
||||
const cards = c.hands[0], n = cards.length, mine = canPlay(c), ok = mine ? legal(cards, c.table) : [];
|
||||
cards.forEach((card, i) => {
|
||||
const x = handX(n, i), up = mine && c.selected === i ? CARD.lift : 0;
|
||||
if (up) { ctx.fillStyle = '#ffdb76'; ctx.fillRect(x - 2, CARD.y - up - 2, CARD.w + 4, CARD.h + 4); }
|
||||
drawCard(ctx, card, x, CARD.y - up, CARD.w, CARD.h);
|
||||
// Le carte che non rispondono al seme si spengono.
|
||||
if (mine && !ok.includes(i)) { ctx.fillStyle = 'rgba(20,28,24,0.55)'; ctx.fillRect(x, CARD.y, CARD.w, CARD.h); }
|
||||
});
|
||||
if (mine && !c.table.length && c.signal) text(ctx, `Segno: ${SAY[c.signal]}`, 320, 278, 6, gold, 'center');
|
||||
}
|
||||
function summary(ctx, c, title) {
|
||||
const s = c.summary, row = (y, who, t, color) =>
|
||||
text(ctx, `${who}: ${s.thirds[t]} terzi = ${s.points[t]} punti${s.accused[t] ? `, accuse +${s.accused[t]}` : ''}${s.lastTeam === t ? ' (con l’ultima)' : ''}`, 172, y, 6, color);
|
||||
panel(ctx, 160, 96, 320, 132);
|
||||
text(ctx, title, 320, 112, 8, gold, 'center');
|
||||
row(140, 'Noi', 0, ours); row(158, 'Loro', 1, theirs);
|
||||
text(ctx, `Siamo ${c.score[0]} a ${c.score[1]}`, 320, 186, 8, paper, 'center');
|
||||
text(ctx, c.step === 'result' ? 'Esc o Invio per tornare all’oratorio' : 'Invio o un tocco per la prossima smazzata', 320, 210, 5, paper, 'center');
|
||||
}
|
||||
export function drawTressette(ctx, c, def) {
|
||||
const names = def?.seats ?? NAMES;
|
||||
ctx.save(); ctx.imageSmoothingEnabled = false;
|
||||
drawTable(ctx, 'TRESSETTE AI 21');
|
||||
scoreboard(ctx, c); others(ctx, c, names); accuses(ctx, c, names); trick(ctx, c, names); hand(ctx, c);
|
||||
if (c.step === 'smazzata') summary(ctx, c, `Fine della smazzata ${c.round}`);
|
||||
if (c.step === 'result') summary(ctx, c, c.result.winner === 'player' ? 'Avete vinto!' : 'Hanno vinto loro');
|
||||
ctx.restore();
|
||||
}
|
||||
Reference in New Issue
Block a user