début de chapitre 7 et générateur de yakus
This commit is contained in:
parent
14d512f691
commit
4c65bb0a65
6 changed files with 309 additions and 0 deletions
1
build/dp7.js
Normal file
1
build/dp7.js
Normal file
File diff suppressed because one or more lines are too long
1
build/txt7.js
Normal file
1
build/txt7.js
Normal 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
182
src/display/dp7.ts
Normal 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
18
src/text/txt7.ts
Normal 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
23
src/text/txt7.txt
Normal 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
84
src/yakus/generator.ts
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in a new issue