diff --git a/frontend/css/hand.css b/frontend/css/hand.css
index b52b828..bf42a52 100644
--- a/frontend/css/hand.css
+++ b/frontend/css/hand.css
@@ -46,8 +46,8 @@
}
.hand-tile-item.draw-tile {
- /* Le draw s'affiche simplement à droite grâce à flex */
flex: 0 0 auto;
+ margin-left: 20px;
}
/* Orientation variations */
diff --git a/frontend/js/game.js b/frontend/js/game.js
new file mode 100644
index 0000000..7163256
--- /dev/null
+++ b/frontend/js/game.js
@@ -0,0 +1,135 @@
+// frontend/js/game.js
+// Adaptation frontend de la logique de game.py en JavaScript
+
+async function think(t) {
+ return new Promise(resolve => setTimeout(resolve, 1000 * t));
+}
+
+class Wall {
+ constructor() {
+ this.tiles = Tile.generateAll();
+ this.shuffle();
+ }
+ shuffle() {
+ for (let i = this.tiles.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [this.tiles[i], this.tiles[j]] = [this.tiles[j], this.tiles[i]];
+ }
+ }
+ draw() {
+ return this.tiles.shift();
+ }
+ drawHand() {
+ const handTiles = this.tiles.splice(0, 13);
+ return new Hand(handTiles);
+ }
+}
+
+class Hand {
+ constructor(tiles) {
+ this.tiles = tiles;
+ this.draw = null;
+ }
+ drawTile(tile) {
+ this.draw = tile;
+ }
+ discardTile(tileId) {
+ let idx = this.tiles.findIndex(t => t.id === tileId);
+ if (idx !== -1) {
+ return this.tiles.splice(idx, 1)[0];
+ }
+ if (this.draw && this.draw.id === tileId) {
+ const discarded = this.draw;
+ this.draw = null;
+ return discarded;
+ }
+ return null;
+ }
+ flatten() {
+ if (this.draw) {
+ this.tiles.push(this.draw);
+ this.draw = null;
+ }
+ }
+}
+
+class Game {
+ constructor(onUpdate) {
+ this.wall = new Wall();
+ this.handPlayer = this.wall.drawHand();
+ this.discardPlayer = [];
+ this.handsBots = [this.wall.drawHand(), this.wall.drawHand(), this.wall.drawHand()];
+ this.discardsBots = [[], [], []];
+ this.turn = 0;
+ this.hasDraw = true;
+ this.end = false;
+ this.onUpdate = onUpdate || (() => {});
+ // Est pioche une tuile
+ const tile = this.wall.draw();
+ if (tile) this.handPlayer.drawTile(tile);
+ this.onUpdate(this);
+ }
+
+ drawTile(hand) {
+ const tile = this.wall.draw();
+ if (tile) hand.drawTile(tile);
+ }
+
+ putTile(hand, tileId) {
+ const discarded = hand.discardTile(tileId);
+ if (discarded) {
+ this.wall.tiles.push(discarded);
+ this.wall.shuffle();
+ }
+ }
+
+ async nextTurn() {
+ this.turn = (this.turn + 1) % 4;
+ this.hasDraw = false;
+ this.onUpdate(this);
+ if (this.turn !== 0) {
+ await this.botTurn();
+ } else {
+ this.drawTile(this.handPlayer);
+ this.hasDraw = true;
+ this.onUpdate(this);
+ }
+ }
+
+ async botTurn() {
+ const bot = this.handsBots[this.turn - 1];
+ this.drawTile(bot);
+ this.onUpdate(this); // Affiche la pioche
+ await think(2);
+ // Défausse une tuile aléatoire
+ const idx = Math.floor(Math.random() * bot.tiles.length);
+ const tileToDiscard = bot.tiles[idx];
+ const discarded = bot.discardTile(tileToDiscard.id);
+ bot.flatten()
+ this.onUpdate(this); // Affiche la défausse
+ await think(0.5);
+ if (discarded) this.discardsBots[this.turn - 1].push(discarded);
+ await this.nextTurn();
+ }
+
+ async discardTile(tileId) {
+ if (this.turn === 0 && this.hasDraw) {
+ const wasDraw = (this.handPlayer.draw && this.handPlayer.draw.id === tileId);
+ const discarded = this.handPlayer.discardTile(tileId);
+ // Si la tuile défaussée n'était pas draw, on intègre draw à la main avant le rendu
+ if (!wasDraw) {
+ this.handPlayer.flatten();
+ }
+ this.onUpdate(this); // Affiche la défausse
+ if (discarded) this.discardPlayer.push(discarded);
+ await this.nextTurn();
+ return discarded;
+ }
+ return null;
+ }
+}
+
+// Expose dans le scope global
+window.Game = Game;
+window.Hand = Hand;
+window.Wall = Wall;
diff --git a/frontend/pages/riichi/chap5.html b/frontend/pages/riichi/chap5.html
index 4e48d90..606db22 100644
--- a/frontend/pages/riichi/chap5.html
+++ b/frontend/pages/riichi/chap5.html
@@ -140,164 +140,179 @@
+
+