début de chapitre 7 et générateur de yakus

This commit is contained in:
Didictateur 2025-04-15 22:32:14 +02:00
parent 14d512f691
commit 4c65bb0a65
6 changed files with 309 additions and 0 deletions

1
build/dp7.js Normal file

File diff suppressed because one or more lines are too long

1
build/txt7.js Normal file
View file

@ -0,0 +1 @@
(()=>{"use strict";const t=document.getElementById("myTextCanvas"),n=t.getContext("2d");t.width=800,t.height=1050,n.fillStyle="#007733",n.fillRect(0,0,800,1050),function(t,n){return e=this,o=void 0,f=function*(){console.log(t,"\n");const e=yield fetch(t).then((t=>t.text())),o="#ffffff";n.fillStyle=o,n.font="30px Garamond";let l=!1,f="",i="",c="",a=1,r=0;for(var d of e)"*"===d?(i=""===i?"bold ":"",n.font=c+i+30+"px Garamond"):"~"===d?(c=""===c?"italic ":"",n.font=c+i+30+"px Garamond"):"#"===d?(f="#",l=!0):"{"===d?(l=!1,n.fillStyle=f):"}"===d?(f="",n.fillStyle=o):l?f+=d:"\n"===d?(a++,r=0):(n.fillText(d,10+r,20+35*a),r+=n.measureText(d).width)},new((l=void 0)||(l=Promise))((function(t,n){function i(t){try{a(f.next(t))}catch(t){n(t)}}function c(t){try{a(f.throw(t))}catch(t){n(t)}}function a(n){var e;n.done?t(n.value):(e=n.value,e instanceof l?e:new l((function(t){t(e)}))).then(i,c)}a((f=f.apply(e,o||[])).next())}));var e,o,l,f}("src/text/txt7.txt",n).catch((t=>console.error(t)))})();

182
src/display/dp7.ts Normal file
View file

@ -0,0 +1,182 @@
import { Hand } from "../hand";
import { function_generator } from "../yakus/generator"
// Configuration globale
const CANVAS_ID = "myCanvas";
const FPS = 30;
const FRAME_INTERVAL = 1000 / FPS;
/**
* Classe RiichiDisplay responsable de la gestion de l'affichage du jeu Riichi Mahjong
*/
class RiichiDisplay {
// Canvas principal et contexte
private canvas: HTMLCanvasElement;
private ctx: CanvasRenderingContext2D;
// Ressources du jeu
private hands: Array<Hand> = [];
// Animation et événements
private animationFrameId: number | null = null;
private lastFrameTime: number = 0;
private cleanupCallbacks: Array<() => void> = [];
// Constantes pour le rendu
private readonly BASE_X: number = 50;
private readonly BASE_Y: number = 90;
private readonly DY = 180;
private readonly SIZE: number = 0.75;
constructor(canvasId: string = CANVAS_ID) {
// Initialisation du canvas
const canvas = document.getElementById(canvasId) as HTMLCanvasElement;
if (!canvas) {
throw new Error(`Canvas avec ID '${canvasId}' introuvable`);
}
this.canvas = canvas;
// Récupération du contexte 2D
const ctx = canvas.getContext("2d");
if (!ctx) {
throw new Error("Impossible d'obtenir le contexte 2D du canvas");
}
this.ctx = ctx;
}
/**
* Dessine une frame complète
*/
private drawFrame(): void {
// Copie du canvas statique vers le canvas principal
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// Dessin des éléments statiques et dynamiques
this.drawHandsAndLabels();
}
/**
* Dessine les mains et leurs étiquettes
*/
private drawHandsAndLabels(): void {
for (let i = 0; i < this.hands.length; i++) {
this.hands[i].drawHand(
this.ctx,
this.BASE_X,
this.BASE_Y + i * this.DY,
5,
this.SIZE
);
}
}
/**
* Démarre la boucle d'animation
*/
private startAnimationLoop(): void {
const animationLoop = (currentTime: number) => {
this.animationFrameId = requestAnimationFrame(animationLoop);
const deltaTime = currentTime - this.lastFrameTime;
if (deltaTime < FRAME_INTERVAL) return;
this.lastFrameTime = currentTime - (deltaTime % FRAME_INTERVAL);
this.drawFrame();
};
this.animationFrameId = requestAnimationFrame(animationLoop);
}
/**
* Précharge une main
*/
private async preloadHand(hand: Hand): Promise<void> {
await hand.preload();
}
/**
* Initialise l'affichage et démarre l'application
*/
public async initialize(): Promise<void> {
try {
// Création des mains prédéfinies
this.hands.push(
function_generator.ordinaires(),
function_generator.sept_pairs()
);
// Préchargement parallèle des ressources
await Promise.all([
...this.hands.map(hand => this.preloadHand(hand))
]);
// Premier rendu
this.drawFrame();
// Démarrage de la boucle d'animation
this.startAnimationLoop();
} catch (error) {
console.error("Erreur d'initialisation:", error);
}
}
/**
* Nettoie les ressources
*/
public cleanup(): void {
// Arrêter la boucle d'animation
if (this.animationFrameId !== null) {
cancelAnimationFrame(this.animationFrameId);
this.animationFrameId = null;
}
// Exécuter tous les callbacks de nettoyage
this.cleanupCallbacks.forEach(callback => callback());
this.cleanupCallbacks = [];
// Nettoyer les ressources des decks et des mains
this.hands.forEach(hand => hand.cleanup());
// Vider les collections
this.hands = [];
}
}
// Instance globale pour le nettoyage
let displayInstance: RiichiDisplay | null = null;
/**
* Fonction de nettoyage exportée
*/
export function cleanup(): void {
if (displayInstance) {
displayInstance.cleanup();
displayInstance = null;
}
}
/**
* Fonction d'initialisation exportée
*/
export async function initDisplay(): Promise<void> {
try {
displayInstance = new RiichiDisplay(CANVAS_ID);
await displayInstance.initialize();
window.cleanup = cleanup;
} catch (error) {
console.error("Erreur lors de l'initialisation:", error);
}
}
// Déclaration globale pour TypeScript
declare global {
interface Window {
cleanup: () => void;
}
}
// Initialisation automatique si le script est chargé directement
if (typeof window !== 'undefined') {
initDisplay().catch(console.error);
}

18
src/text/txt7.ts Normal file
View file

@ -0,0 +1,18 @@
import { drawText } from "./parse"
const CANVAS_ID = "myTextCanvas";
const BG_RECT = { x: 0, y: 0, w: 800, h: 1050, color: "#007733" };
const canvas = document.getElementById(CANVAS_ID) as HTMLCanvasElement;
const ctx = canvas.getContext("2d") as NonNullable<CanvasRenderingContext2D>;
canvas.width = BG_RECT.w;
canvas.height = BG_RECT.h;
const path = "src/text/";
ctx.fillStyle = BG_RECT.color;
ctx.fillRect(BG_RECT.x, BG_RECT.y, BG_RECT.w, BG_RECT.h);
drawText(path + "txt7.txt", ctx).catch(error => console.error(error));
export {};

23
src/text/txt7.txt Normal file
View file

@ -0,0 +1,23 @@
~Chapitre 7: Les yakus~
Il est maintenant temps de révéler la vérité. Depuis le début,
bous êtes dans le mensonge. En effet, une main *Valide* (avec
tout ses groupes) n'est pas suffisant pour gagner: la main doit
également respecter une #ff00ff{condition de victoire}.
Ces conditions sont appelées des #ff00ff{Yakus}, et il y en a
un total de *39*. Mais pas de panique ! En général, une dizaine
d'entre eux est largment suffisant pour la pllupart des parties.
Sur la gauche, sont présentés les Yakus les plus courant:
- #ff00ff{Tout ordinaire}: Aucun honneur, aucun terminal
- #ff00ff{Brelan de valeur}:
- Brelan de dragon
- Brelan d'Est
- Brelan du vent du joueur
- #ff00ff{Main pure}: une seule famille
- #ff00ff{Main semi-pure}: une seule famille en plus d'honneurs
- #ff00ff{Double suite}: deux suites identiques
#ff0000{Ne fonctionne qu'en main fermée !}
- #ff00ff{Sept paire}: sept pairs distinctes
#ff0000{L'une des deux exceptions sur la forme des groupes}

84
src/yakus/generator.ts Normal file
View file

@ -0,0 +1,84 @@
import { Hand } from "../hand"
import { Deck } from "../deck"
import { Tile } from "../tile";
export const function_generator = {
ordinaires(): Hand {
let h = new Hand();
let deck = new Deck();
deck.shuffle();
// choix d'une paire
let t1 = deck.pop();
let t2 = deck.find(t1.getFamily(), t1.getValue()) as NonNullable<Tile>;
h.push(t1);
h.push(t2);
let count = 2;
while (count !== 14) {
if (Math.random() < 0.5) { // pon
let t1 = deck.pop();
let f = t1.getFamily();
let v = t1.getValue();
if (
f < 4 && v !== 1 && v !== 9 &&
deck.count(f, v) > 1
) {
let t2 = deck.find(f, v) as NonNullable<Tile>;
let t3 = deck.find(f, v) as NonNullable<Tile>;
h.push(t1);
h.push(t2);
h.push(t3);
count += 3;
} else {
deck.push(t1);
deck.shuffle();
}
} else { // chii
let t1 = deck.pop();
let f = t1.getFamily();
let v = t1.getValue();
if (
f < 4 && v !== 1 && v < 6 &&
deck.count(f, v + 1) > 0 &&
deck.count(f, v + 2) > 0
) {
let t2 = deck.find(f, v + 1) as NonNullable<Tile>;
let t3 = deck.find(f, v + 2) as NonNullable<Tile>;
h.push(t1);
h.push(t2);
h.push(t3);
count += 3;
} else {
deck.push(t1);
deck.shuffle();
}
}
}
h.sort();
return h;
},
sept_pairs(): Hand {
let h = new Hand();
let deck = new Deck();
deck.shuffle();
let count = 0;
while (count !== 7) {
let tile = deck.pop();
if (h.getTiles().every(
t => t.compare(tile) !== 0
)) {
count++;
h.push(tile);
h.push(new Tile(tile.getFamily(), tile.getValue(), false));
}
}
h.sort();
return h;
}
}