diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..de86cab --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "riichi"] + path = riichi + url = https://github.com/Didictateur/Riichi diff --git a/EasterEggs/EasterEgg0.txt b/EasterEggs/EasterEgg0.txt new file mode 100644 index 0000000..b543ad2 --- /dev/null +++ b/EasterEggs/EasterEgg0.txt @@ -0,0 +1,5 @@ +- EE1: "Quoique l'on me dise..." +- EE2: code source +- EE3: PacENS +- EE4: Croco Loop +- EE5: Crocodile Loop diff --git a/EasterEggs/images/0.png b/EasterEggs/images/0.png new file mode 100644 index 0000000..2bdeb19 Binary files /dev/null and b/EasterEggs/images/0.png differ diff --git a/EasterEggs/images/1.png b/EasterEggs/images/1.png new file mode 100644 index 0000000..639ad8a Binary files /dev/null and b/EasterEggs/images/1.png differ diff --git a/EasterEggs/images/ENSL.png b/EasterEggs/images/ENSL.png new file mode 100644 index 0000000..30bbcff Binary files /dev/null and b/EasterEggs/images/ENSL.png differ diff --git a/EasterEggs/images/ENSR.png b/EasterEggs/images/ENSR.png new file mode 100644 index 0000000..6c82372 Binary files /dev/null and b/EasterEggs/images/ENSR.png differ diff --git a/EasterEggs/images/ENSS.jpeg b/EasterEggs/images/ENSS.jpeg new file mode 100644 index 0000000..96d25c7 Binary files /dev/null and b/EasterEggs/images/ENSS.jpeg differ diff --git a/EasterEggs/images/ENSU.png b/EasterEggs/images/ENSU.png new file mode 100644 index 0000000..a1d4beb Binary files /dev/null and b/EasterEggs/images/ENSU.png differ diff --git a/EasterEggs/images/crocodile.jpg b/EasterEggs/images/crocodile.jpg new file mode 100644 index 0000000..7e50adb Binary files /dev/null and b/EasterEggs/images/crocodile.jpg differ diff --git a/EasterEggs/images/pp.jpg b/EasterEggs/images/pp.jpg new file mode 100644 index 0000000..0dcbbfb Binary files /dev/null and b/EasterEggs/images/pp.jpg differ diff --git a/EasterEggs/pacman.css b/EasterEggs/pacman.css new file mode 100644 index 0000000..00da559 --- /dev/null +++ b/EasterEggs/pacman.css @@ -0,0 +1,40 @@ +html, +body { + margin: 0px; + padding: 0px; + width: 100%; + overflow: hidden; + background-color: black; +} + +.container { + width: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.social-links { + padding: 5px; + width: 560px; +} + +#pacman-banner { + font-family: Arial; + font-weight: bold; + font-size: 21pt; + margin-right: 100px; + color: yellow; + float: left; +} + +.social-buttons { + float: right; + position: relative; + top: 5px; +} + +.game-container { + transform: scale(0.7) translateY(-20%); +} + diff --git a/EasterEggs/pacman.html b/EasterEggs/pacman.html new file mode 100644 index 0000000..9dd0e7d --- /dev/null +++ b/EasterEggs/pacman.html @@ -0,0 +1,40 @@ + + + + + + + Easter Egg 3 + + + + +
+ +
+ +
+ +
+ + + + + diff --git a/EasterEggs/pacman.js b/EasterEggs/pacman.js new file mode 100644 index 0000000..11843a4 --- /dev/null +++ b/EasterEggs/pacman.js @@ -0,0 +1,859 @@ +let width = 800; +let height = 625; +let gridSize = 32; +let offset = parseInt(gridSize / 2); +let config = { + type: Phaser.CANVAS, + width: width, + height: height, + canvas: document.getElementById("mycanvas"), + physics: { + default: "arcade", + arcade: { + debug: false, + gravity: { + x: 0, + y: 0 + } + } + }, + scene: { + preload: preload, + create: create, + update: update + } +}; + +let game = new Phaser.Game(config); +let cursors; +let player; +let ghosts = []; +let pills; +let pillsCount = 0; +let pillsAte = 0; +let map; +let layer1; +let layer2; +let graphics; +let scoreText; +let livesImage = []; +let tiles = "pacman-tiles"; +let spritesheet = "pacman-spritesheet"; +let customPacman = "customPacman"; +let ensr = "ENSR"; +let ensl = "ENSL"; +let ensu = "ENSU"; +let enss = "ENSS"; +let spritesheetPath = "https://raw.githubusercontent.com/kudchikarsk/phaser-pacman/master/assets/images/pacmansprites.png"; +let tilesPath = "https://raw.githubusercontent.com/kudchikarsk/phaser-pacman/master/assets/images/background.png"; +let mapPath = + "https://raw.githubusercontent.com/kudchikarsk/phaser-pacman/master/assets/levels/codepen-level.json"; +let Animation = { + Player: { + Eat: "player-eat", + Stay: "player-stay", + Die: "player-die" + }, + Ghost: { + Blue: { + Move: "ghost-blue-move" + }, + + Orange: { + Move: "ghost-orange-move" + }, + + White: { + Move: "ghost-white-move" + }, + + Pink: { + Move: "ghost-pink-move" + }, + + Red: { + Move: "ghost-red-move" + } + } +}; + +function preload() { + this.load.spritesheet(spritesheet, spritesheetPath, { + frameWidth: gridSize, + frameHeight: gridSize + }); + this.load.tilemapTiledJSON("map", mapPath); + this.load.image(tiles, tilesPath); + this.load.image("pill", "https://raw.githubusercontent.com/kudchikarsk/phaser-pacman/master/assets/images/pac%20man%20pill/spr_pill_0.png"); + this.load.image("lifecounter", "https://raw.githubusercontent.com/kudchikarsk/phaser-pacman/master/assets/images/pac%20man%20life%20counter/spr_lifecounter_0.png"); + this.load.spritesheet(customPacman, "images/pp.jpg", { + frameWidth: gridSize, + frameHeight: gridSize + }); + this.load.image("0", "./images/0.png"); + this.load.image("1", "./images/1.png"); + + this.load.spritesheet(ensr, "images/ENSR.png", { + frameWidth: gridSize, + frameHeight: gridSize + }); + this.load.spritesheet(ensl, "images/ENSL.png", { + frameWidth: gridSize, + frameHeight: gridSize + }); + this.load.spritesheet(ensu, "images/ENSU.png", { + frameWidth: gridSize, + frameHeight: gridSize + }); + this.load.spritesheet(enss, "images/ENSS.jpeg", { + frameWidth: gridSize, + frameHeight: gridSize + }); +} + +function create() { + this.anims.create({ + key: Animation.Player.Stay, + frames: this.anims.generateFrameNumbers(customPacman, {frame: 0}), + frameRate: 20 + }); + + this.anims.create({ + key: Animation.Ghost.Blue.Move, + frames: this.anims.generateFrameNumbers(ensl, { start: 0, end: 0 }), + frameRate: 10, + repeat: -1 + }); + + this.anims.create({ + key: Animation.Ghost.Orange.Move, + frames: this.anims.generateFrameNumbers(enss, { start: 0, end: 0 }), + frameRate: 10, + repeat: -1 + }); + + //this.anims.create({ + // key: Animation.Ghost.White.Move, + // frames: this.anims.generateFrameNumbers(ensl, { start: 0, end: 0 }), + // frameRate: 10, + // repeat: -1 + //}); + + this.anims.create({ + key: Animation.Ghost.Pink.Move, + frames: this.anims.generateFrameNumbers(ensu, { + start: 0, + end: 0 + }), + frameRate: 10, + repeat: -1 + }); + + this.anims.create({ + key: Animation.Ghost.Red.Move, + frames: this.anims.generateFrameNumbers(ensr, { + start: 0, + end: 0 + }), + frameRate: 10, + repeat: -1 + }); + + map = this.make.tilemap({ + key: "map", + tileWidth: gridSize, + tileHeight: gridSize + }); + const tileset = map.addTilesetImage(tiles); + + layer1 = map.createStaticLayer("Layer 1", tileset, 0, 0); + layer1.setCollisionByProperty({ collides: true }); + + layer2 = map.createStaticLayer("Layer 2", tileset, 0, 0); + layer2.setCollisionByProperty({ collides: true }); + + let spawnPoint = map.findObject("Objects", obj => obj.name === "Player"); + let position = new Phaser.Geom.Point( + spawnPoint.x + offset, + spawnPoint.y - offset + ); + player = new Player(this, position, Animation.Player, function() { + if (player.life <= 0) { + newGame(); + } else { + respawn(); + } + }); + + let scene = this; + + pills = this.physics.add.group(); + map.filterObjects("Objects", function(value, index, array) { + if (value.name == "Pill") { + let pill; + let randomValue = Phaser.Math.Between(0,1); + if (randomValue > 0.5) { + pill = scene.physics.add.sprite( + value.x + offset, + value.y - offset, + "0" + ); + pill.setScale(0.07); + } else { + pill = scene.physics.add.sprite( + value.x + offset, + value.y - offset, + "1" + ); + pill.setScale(0.1); + } + pills.add(pill); + pillsCount++; + } + }); + + let ghostsGroup = this.physics.add.group(); + let i = 0; + let skins = [ + Animation.Ghost.Blue, + Animation.Ghost.Red, + Animation.Ghost.Orange, + Animation.Ghost.Pink + ]; + map.filterObjects("Objects", function(value, index, array) { + if (value.name == "Ghost") { + let position = new Phaser.Geom.Point(value.x + offset, value.y - offset); + let ghost = new Ghost(scene, position, skins[i]); + ghosts.push(ghost); + ghostsGroup.add(ghost.sprite); + i++; + } + }); + + this.physics.add.collider(player.sprite, layer1); + this.physics.add.collider(player.sprite, layer2); + this.physics.add.collider(ghostsGroup, layer1); + this.physics.add.overlap( + player.sprite, + pills, + function(sprite, pill) { + pill.disableBody(true, true); + pillsAte++; + player.score += 10; + if (pillsCount == pillsAte) { + reset(); + } + }, + null, + this + ); + + this.physics.add.overlap( + player.sprite, + ghostsGroup, + function(sprite, ghostSprite) { + if (player.active) { + player.die(); + for (let ghost of ghosts) { + ghost.freeze(); + } + } + }, + null, + this + ); + + cursors = this.input.keyboard.createCursorKeys(); + + graphics = this.add.graphics(); + + scoreText = this.add + .text(25, 595, "Score: " + player.score) + .setFontFamily("Arial") + .setFontSize(18) + .setColor("#ffffff"); + this.add + .text(630, 595, "Lives:") + .setFontFamily("Arial") + .setFontSize(18) + .setColor("#ffffff"); + for (let i = 0; i < player.life; i++) { + livesImage.push(this.add.image(700 + i * 25, 605, "lifecounter")); + } +} + +function respawn() { + player.respawn(); + for (let ghost of ghosts) { + ghost.respawn(); + } +} + +function reset() { + respawn(); + for (let child of pills.getChildren()) { + child.enableBody(false, child.x, child.y, true, true); + } + pillsAte = 0; +} + +function newGame() { + reset(); + player.life = 3; + player.score = 0; + for (let i = 0; i < player.life; i++) { + let image = livesImage[i]; + if (image) { + image.alpha = 1; + } + } +} + +function update() { + player.setDirections(getDirection(map, layer1, player.sprite)); + + if (!player.playing) { + for (let ghost of ghosts) { + ghost.freeze(); + } + } + + for (let ghost of ghosts) { + ghost.setDirections(getDirection(map, layer1, ghost.sprite)); + } + + player.setTurningPoint(getTurningPoint(map, player.sprite)); + + for (let ghost of ghosts) { + ghost.setTurningPoint(getTurningPoint(map, ghost.sprite)); + } + + if (cursors.left.isDown) { + player.setTurn(Phaser.LEFT); + } else if (cursors.right.isDown) { + player.setTurn(Phaser.RIGHT); + } else if (cursors.up.isDown) { + player.setTurn(Phaser.UP); + } else if (cursors.down.isDown) { + player.setTurn(Phaser.DOWN); + } else { + player.setTurn(Phaser.NONE); + } + + player.update(); + + for (let ghost of ghosts) { + ghost.update(); + } + + scoreText.setText("Score: " + player.score); + + for (let i = player.life; i < 3; i++) { + let image = livesImage[i]; + if (image) { + image.alpha = 0; + } + } + + if (player.active) { + if (player.sprite.x < 0 - offset) { + player.sprite.setPosition(width + offset, player.sprite.y); + } else if (player.sprite.x > width + offset) { + player.sprite.setPosition(0 - offset, player.sprite.y); + } + } + + //drawDebug(); +} + +function drawDebug() { + graphics.clear(); + player.drawDebug(graphics); + for (let ghost of ghosts) { + ghost.drawDebug(graphics); + } +} + +function getDirection(map, layer, sprite) { + let directions = []; + let sx = Phaser.Math.FloorTo(sprite.x); + let sy = Phaser.Math.FloorTo(sprite.y); + let currentTile = map.getTileAtWorldXY(sx, sy, true); + if (currentTile) { + var x = currentTile.x; + var y = currentTile.y; + + directions[Phaser.LEFT] = map.getTileAt(x - 1, y, true, layer); + directions[Phaser.RIGHT] = map.getTileAt(x + 1, y, true, layer); + directions[Phaser.UP] = map.getTileAt(x, y - 1, true, layer); + directions[Phaser.DOWN] = map.getTileAt(x, y + 1, true, layer); + } + + return directions; +} + +function getTurningPoint(map, sprite) { + let turningPoint = new Phaser.Geom.Point(); + let sx = Phaser.Math.FloorTo(sprite.x); + let sy = Phaser.Math.FloorTo(sprite.y); + let currentTile = map.getTileAtWorldXY(sx, sy, true); + if (currentTile) { + turningPoint.x = currentTile.pixelX + offset; + turningPoint.y = currentTile.pixelY + offset; + } + + return turningPoint; +} + +class Ghost { + constructor(scene, position, anim) { + this.sprite = scene.physics.add + .sprite(position.x, position.y, "ghost") + .setScale(0.85) + .setOrigin(0.5); + this.spawnPoint = position; + this.anim = anim; + this.speed = 100; + this.moveTo = new Phaser.Geom.Point(); + this.safetile = [-1, 19]; + this.directions = []; + this.opposites = [ + null, + null, + null, + null, + null, + Phaser.DOWN, + Phaser.UP, + Phaser.RIGHT, + Phaser.LEFT + ]; + this.turning = Phaser.NONE; + this.current = Phaser.NONE; + this.turningPoint = new Phaser.Geom.Point(); + this.threshold = 5; + this.rnd = new Phaser.Math.RandomDataGenerator(); + this.sprite.anims.play(anim.Move, true); + this.turnCount = 0; + this.turnAtTime = [4, 8, 16, 32, 64]; + this.turnAt = this.rnd.pick(this.turnAtTime); + } + + freeze() { + this.moveTo = new Phaser.Geom.Point(); + this.current = Phaser.NONE; + } + + move() { + this.move(this.rnd.pick([Phaser.UP, Phaser.DOWN])); + } + + respawn() { + this.sprite.setPosition(this.spawnPoint.x, this.spawnPoint.y); + this.move(this.rnd.pick([Phaser.UP, Phaser.DOWN])); + this.sprite.flipX = false; + } + + moveLeft() { + this.moveTo.x = -1; + this.moveTo.y = 0; + this.sprite.flipX = false; + this.sprite.angle = 0; + } + + moveRight() { + this.moveTo.x = 1; + this.moveTo.y = 0; + this.sprite.flipX = false; + this.sprite.angle = 0; + } + + moveUp() { + this.moveTo.x = 0; + this.moveTo.y = -1; + this.sprite.angle = 0; + } + + moveDown() { + this.moveTo.x = 0; + this.moveTo.y = 1; + this.sprite.angle = 0; + } + + update() { + this.sprite.setVelocity( + this.moveTo.x * this.speed, + this.moveTo.y * this.speed + ); + this.turn(); + if ( + this.directions[this.current] && + !this.isSafe(this.directions[this.current].index) + ) { + this.sprite.anims.play("faceRight", true); + this.takeRandomTurn(); + } + } + + setDirections(directions) { + this.directions = directions; + } + + setTurningPoint(turningPoint) { + this.turningPoint = turningPoint; + } + + setTurn(turnTo) { + if ( + !this.directions[turnTo] || + this.turning === turnTo || + this.current === turnTo || + !this.isSafe(this.directions[turnTo].index) + ) { + return false; + } + + //console.log("turning:"+this.turning+" current:"+this.current+" turnTo:"+turnTo); + + if (this.opposites[turnTo] && this.opposites[turnTo] === this.current) { + this.move(turnTo); + this.turning = Phaser.NONE; + this.turningPoint = new Phaser.Geom.Point(); + } else { + this.turning = turnTo; + } + } + + takeRandomTurn() { + let turns = []; + for (let i = 0; i < this.directions.length; i++) { + let direction = this.directions[i]; + if (direction) { + if (this.isSafe(direction.index)) { + turns.push(i); + } + } + } + + if (turns.length >= 2) { + let index = turns.indexOf(this.opposites[this.current]); + if (index > -1) { + turns.splice(index, 1); + } + } + + let turn = this.rnd.pick(turns); + this.setTurn(turn); + + this.turnCount = 0; + this.turnAt = this.rnd.pick(this.turnAtTime); + } + + turn() { + if (this.turnCount === this.turnAt) { + this.takeRandomTurn(); + } + this.turnCount++; + + if (this.turning === Phaser.NONE) { + return false; + } + + // This needs a threshold, because at high speeds you can't turn because the coordinates skip past + if ( + !Phaser.Math.Within(this.sprite.x, this.turningPoint.x, this.threshold) || + !Phaser.Math.Within(this.sprite.y, this.turningPoint.y, this.threshold) + ) { + return false; + } + + this.sprite.setPosition(this.turningPoint.x, this.turningPoint.y); + this.move(this.turning); + this.turning = Phaser.NONE; + this.turningPoint = new Phaser.Geom.Point(); + return true; + } + + move(direction) { + this.current = direction; + + switch (direction) { + case Phaser.LEFT: + this.moveLeft(); + break; + + case Phaser.RIGHT: + this.moveRight(); + break; + + case Phaser.UP: + this.moveUp(); + break; + + case Phaser.DOWN: + this.moveDown(); + break; + } + } + + isSafe(index) { + for (let i of this.safetile) { + if (i === index) return true; + } + + return false; + } + + drawDebug(graphics) { + let thickness = 4; + let alpha = 1; + let color = 0x00ff00; + for (var t = 0; t < 9; t++) { + if (this.directions[t] === null || this.directions[t] === undefined) { + continue; + } + + if (!this.isSafe(this.directions[t].index)) { + color = 0xff0000; + } else { + color = 0x00ff00; + } + + graphics.lineStyle(thickness, color, alpha); + graphics.strokeRect( + this.directions[t].pixelX, + this.directions[t].pixelY, + 32, + 32 + ); + } + + color = 0x00ff00; + graphics.lineStyle(thickness, color, alpha); + graphics.strokeRect(this.turningPoint.x, this.turningPoint.y, 1, 1); + } +} + +class Player { + constructor(scene, position, anim, dieCallback) { + this.sprite = scene.physics.add + .sprite(position.x, position.y, "pacman") + .setScale(0.9) + .setOrigin(0.5); + this.spawnPoint = position; + this.anim = anim; + this.dieCallback = dieCallback; + this.speed = 95; + this.moveTo = new Phaser.Geom.Point(); + this.sprite.angle = 0; + this.safetile = [-1, 18]; + this.directions = []; + this.opposites = [ + null, + null, + null, + null, + null, + Phaser.DOWN, + Phaser.UP, + Phaser.RIGHT, + Phaser.LEFT + ]; + this.turning = Phaser.NONE; + this.current = Phaser.NONE; + this.turningPoint = new Phaser.Geom.Point(); + this.threshold = 5; + this.life = 3; + this.score = 0; + this.active = true; + this.sprite.anims.play(this.anim.Stay, true); + let ref = this; + this.sprite.on( + "animationcomplete", + function(animation, frame) { + ref.animComplete(animation, frame); + }, + scene + ); + this.playing = false; + } + + die() { + this.active = false; + this.playing = false; + this.life--; + this.moveTo = new Phaser.Geom.Point(); + setTimeout(() => { + this.dieCallback(); + }, 500) + } + + animComplete(animation, frame) { + if (animation.key == this.anim.Die) { + this.dieCallback(); + } + } + + respawn() { + this.active = true; + this.playing = false; + this.sprite.setPosition(this.spawnPoint.x, this.spawnPoint.y); + this.moveTo = new Phaser.Geom.Point(); + this.sprite.anims.play(this.anim.Stay, true); + this.sprite.angle = 0; + this.turning = Phaser.NONE; + this.current = Phaser.NONE; + } + + moveLeft() { + this.moveTo.x = -1; + this.moveTo.y = 0; + this.sprite.anims.play(this.anim.Eat, true); + this.sprite.angle = 0; + } + + moveRight() { + this.moveTo.x = 1; + this.moveTo.y = 0; + this.sprite.anims.play(this.anim.Eat, true); + this.sprite.angle = 0; + } + + moveUp() { + this.moveTo.x = 0; + this.moveTo.y = -1; + this.sprite.anims.play(this.anim.Eat, true); + this.sprite.angle = 0; + } + + moveDown() { + this.moveTo.x = 0; + this.moveTo.y = 1; + this.sprite.anims.play(this.anim.Eat, true); + this.sprite.angle = 0; + } + + update() { + this.sprite.setVelocity( + this.moveTo.x * this.speed, + this.moveTo.y * this.speed + ); + this.turn(); + if ( + this.directions[this.current] && + !this.isSafe(this.directions[this.current].index) + ) { + this.sprite.anims.play("faceRight", true); + } + } + + setDirections(directions) { + this.directions = directions; + } + + setTurningPoint(turningPoint) { + this.turningPoint = turningPoint; + } + + setTurn(turnTo) { + if ( + !this.active || + !this.directions[turnTo] || + this.turning === turnTo || + this.current === turnTo || + !this.isSafe(this.directions[turnTo].index) + ) { + return false; + } + + if (this.opposites[turnTo] && this.opposites[turnTo] === this.current) { + this.move(turnTo); + this.turning = Phaser.NONE; + this.turningPoint = new Phaser.Geom.Point(); + } else { + this.turning = turnTo; + } + } + + turn() { + if (this.turning === Phaser.NONE) { + return false; + } + + // This needs a threshold, because at high speeds you can't turn because the coordinates skip past + if ( + !Phaser.Math.Within(this.sprite.x, this.turningPoint.x, this.threshold) || + !Phaser.Math.Within(this.sprite.y, this.turningPoint.y, this.threshold) + ) { + return false; + } + + this.sprite.setPosition(this.turningPoint.x, this.turningPoint.y); + this.move(this.turning); + this.turning = Phaser.NONE; + this.turningPoint = new Phaser.Geom.Point(); + return true; + } + + move(direction) { + this.playing = true; + this.current = direction; + + switch (direction) { + case Phaser.LEFT: + this.moveLeft(); + break; + + case Phaser.RIGHT: + this.moveRight(); + break; + + case Phaser.UP: + this.moveUp(); + break; + + case Phaser.DOWN: + this.moveDown(); + break; + } + } + + isSafe(index) { + for (let i of this.safetile) { + if (i === index) return true; + } + + return false; + } + + drawDebug(graphics) { + let thickness = 4; + let alpha = 1; + let color = 0x00ff00; + + for (var t = 0; t < 9; t++) { + if (this.directions[t] === null || this.directions[t] === undefined) { + continue; + } + + if (this.directions[t].index !== -1) { + color = 0xff0000; + } else { + color = 0x00ff00; + } + + graphics.lineStyle(thickness, color, alpha); + graphics.strokeRect( + this.directions[t].pixelX, + this.directions[t].pixelY, + 32, + 32 + ); + } + + color = 0x00ff00; + graphics.lineStyle(thickness, color, alpha); + graphics.strokeRect(this.turningPoint.x, this.turningPoint.y, 1, 1); + } +} + diff --git a/Photo_Profil.jpg b/Photo_Profil.jpg new file mode 100644 index 0000000..4923ccf Binary files /dev/null and b/Photo_Profil.jpg differ diff --git a/README.md b/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/agreg/Licence.pdf b/agreg/Licence.pdf new file mode 100644 index 0000000..747d365 Binary files /dev/null and b/agreg/Licence.pdf differ diff --git a/agreg/MPI.pdf b/agreg/MPI.pdf new file mode 100644 index 0000000..9593a3d Binary files /dev/null and b/agreg/MPI.pdf differ diff --git a/agreg/Terminal.pdf b/agreg/Terminal.pdf new file mode 100644 index 0000000..ac25b02 Binary files /dev/null and b/agreg/Terminal.pdf differ diff --git a/cours/6piles/index.html b/cours/6piles/index.html new file mode 100644 index 0000000..5f511b1 --- /dev/null +++ b/cours/6piles/index.html @@ -0,0 +1,43 @@ + + + + + File avec 6 piles + + + +
+ + + diff --git a/cours/6piles/pile.js b/cours/6piles/pile.js new file mode 100644 index 0000000..2c97d56 --- /dev/null +++ b/cours/6piles/pile.js @@ -0,0 +1,519 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var CANVAS_ID = "myCanvas"; +var BG_RECT = { x: 0, y: 0, w: 1050, h: 1050 }; +var FPS = 30; +var FRAME_INTERVAL = 1000 / FPS; +var WAIT_TIME = 300; +var PROCESSING = false; +var GLOBAL_PROCESSING = false; +var LEARNING = false; +var canvas = document.getElementById(CANVAS_ID); +var ctx = canvas.getContext("2d"); +canvas.width = BG_RECT.w; +canvas.height = BG_RECT.h; +var staticCanvas = document.createElement('canvas'); +var staticCtx = staticCanvas.getContext("2d"); +staticCanvas.width = BG_RECT.w; +staticCanvas.height = BG_RECT.h; +var animationFrameId; +var lastFrameTime = 0; +var callbacks = []; +var Pile = /** @class */ (function () { + function Pile() { + this.length = 0; + this.mem = []; + } + Pile.prototype.push = function (e) { + this.length++; + this.mem.push(e); + }; + Pile.prototype.pop = function () { + this.length = this.length > 0 ? this.length - 1 : 0; + return this.mem.pop(); + }; + Pile.prototype.empty = function () { + this.mem = []; + this.length = 0; + }; + Pile.prototype.draw = function (x, y) { + var w = 150; + var h = 200; + var e = 10; + var eh = 10; + // container + staticCtx.fillStyle = "#000000"; + staticCtx.fillRect(x - w / 2, y - h, w, h); + staticCtx.fillStyle = "#ffffff"; + staticCtx.fillRect(x - w / 2 + e, y - h, w - 2 * e, h - e); + // element + var ye = y - e - eh / 2; + for (var i = 0; i < this.length; i++) { + var ew = this.mem[i].value / 10 * (w - 2 * e); + staticCtx.fillStyle = this.mem[i].color; + staticCtx.fillRect(x - ew / 2, ye - i * eh - eh / 2, ew, eh); + } + }; + return Pile; +}()); +var File_t = /** @class */ (function () { + function File_t() { + this.head = new Pile(); + this.b_head = new Pile(); + this.tail = new Pile(); + this.c_tail = new Pile(); + this.b_1 = new Pile(); + this.b_2 = new Pile(); + this.stepLevel = 0; + this.count = 0; + } + File_t.prototype.emptyAll = function () { + this.b_head.empty(); + this.b_1.empty(); + this.b_2.empty(); + this.c_tail.empty(); + while (this.tail.length > 0) { + this.b_1.push(this.tail.pop()); + } + while (this.b_1.length > 0) { + var e = this.b_1.pop(); + this.tail.push(e); + this.c_tail.push(e); + } + }; + File_t.prototype.push = function (e) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + console.log("Current step:", this.stepLevel, "\n"); + if (!(this.head.length === 0 && this.tail.length === 0)) return [3 /*break*/, 1]; + this.tail.push(e); + this.c_tail.push(e); + return [3 /*break*/, 5]; + case 1: + if (!(this.stepLevel > 0)) return [3 /*break*/, 3]; + this.head.push(e); + GLOBAL_PROCESSING = true; + return [4 /*yield*/, this.step()]; + case 2: + _a.sent(); + return [3 /*break*/, 5]; + case 3: + this.head.push(e); + if (!(this.head.length === this.tail.length)) return [3 /*break*/, 5]; + console.log("Begin Transfert\n"); + this.stepLevel = 1; + GLOBAL_PROCESSING = true; + return [4 /*yield*/, this.step()]; + case 4: + _a.sent(); + _a.label = 5; + case 5: return [2 /*return*/]; + } + }); + }); + }; + File_t.prototype.pop = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!(this.stepLevel > 2)) return [3 /*break*/, 2]; + this.count++; + this.tail.pop(); + GLOBAL_PROCESSING = true; + return [4 /*yield*/, this.step()]; + case 1: + _a.sent(); + return [3 /*break*/, 4]; + case 2: + this.c_tail.pop(); + this.tail.pop(); + if (!(this.stepLevel > 0)) return [3 /*break*/, 4]; + GLOBAL_PROCESSING = true; + return [4 /*yield*/, this.step()]; + case 3: + _a.sent(); + _a.label = 4; + case 4: + if (!(this.head.length === this.tail.length)) return [3 /*break*/, 6]; + console.log("Begin Transfert\n"); + this.stepLevel = 1; + return [4 /*yield*/, this.step()]; + case 5: + _a.sent(); + _a.label = 6; + case 6: return [2 /*return*/]; + } + }); + }); + }; + File_t.prototype.step = function () { + return __awaiter(this, arguments, void 0, function (n) { + var e, e, e; + var _a, _b, _c, _d, _e, _f; + if (n === void 0) { n = 5; } + return __generator(this, function (_g) { + switch (_g.label) { + case 0: + if (n <= 0) { // finit le nombre d'étape pour le moment + GLOBAL_PROCESSING = false; + return [2 /*return*/]; + } + if (!!LEARNING) return [3 /*break*/, 2]; + return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, WAIT_TIME); })]; + case 1: + _g.sent(); + _g.label = 2; + case 2: + this.draw(); + console.log("Transfer step:", this.stepLevel, "\n"); + if (!(this.stepLevel === 1)) return [3 /*break*/, 5]; + _a = [this.b_head, this.head], this.head = _a[0], this.b_head = _a[1]; + this.stepLevel++; + if (!!LEARNING) return [3 /*break*/, 4]; + return [4 /*yield*/, this.step(n)]; + case 3: + _g.sent(); + _g.label = 4; + case 4: return [3 /*break*/, 34]; + case 5: + if (!(this.stepLevel === 2)) return [3 /*break*/, 13]; + e = this.b_head.pop(); + if (!(e === undefined)) return [3 /*break*/, 8]; + this.stepLevel++; + if (!!LEARNING) return [3 /*break*/, 7]; + return [4 /*yield*/, this.step(n)]; + case 6: + _g.sent(); + _g.label = 7; + case 7: return [3 /*break*/, 12]; + case 8: + this.b_1.push(e); + this.b_2.push(e); + if (!!LEARNING) return [3 /*break*/, 10]; + return [4 /*yield*/, this.step(n - 2)]; + case 9: + _g.sent(); + return [3 /*break*/, 12]; + case 10: return [4 /*yield*/, this.step(n)]; + case 11: + _g.sent(); + _g.label = 12; + case 12: return [3 /*break*/, 34]; + case 13: + if (!(this.stepLevel === 3)) return [3 /*break*/, 16]; + _b = [this.b_2, this.b_head], this.b_head = _b[0], this.b_2 = _b[1]; + this.stepLevel++; + if (!!LEARNING) return [3 /*break*/, 15]; + return [4 /*yield*/, this.step(n)]; + case 14: + _g.sent(); + _g.label = 15; + case 15: return [3 /*break*/, 34]; + case 16: + if (!(this.stepLevel === 4)) return [3 /*break*/, 24]; + e = this.c_tail.pop(); + if (!(e === undefined)) return [3 /*break*/, 19]; + this.stepLevel++; + if (!!LEARNING) return [3 /*break*/, 18]; + return [4 /*yield*/, this.step(n)]; + case 17: + _g.sent(); + _g.label = 18; + case 18: return [3 /*break*/, 23]; + case 19: + this.b_2.push(e); + if (!!LEARNING) return [3 /*break*/, 21]; + return [4 /*yield*/, this.step(n - 1)]; + case 20: + _g.sent(); + return [3 /*break*/, 23]; + case 21: return [4 /*yield*/, this.step(n)]; + case 22: + _g.sent(); + _g.label = 23; + case 23: return [3 /*break*/, 34]; + case 24: + if (!(this.stepLevel === 5)) return [3 /*break*/, 33]; + if (this.b_2.length === this.count) { // ne pas compter les éléments déjà retirés + _c = [this.c_tail, this.b_1], this.b_1 = _c[0], this.c_tail = _c[1]; + _d = [this.tail, this.b_head], this.b_head = _d[0], this.tail = _d[1]; + this.stepLevel = 0; + this.emptyAll(); + GLOBAL_PROCESSING = false; + this.count = 0; + return [2 /*return*/]; + } + e = this.b_2.pop(); + if (!(e === undefined)) return [3 /*break*/, 25]; + this.stepLevel++; + if (!LEARNING) { + this.step(n); + } + return [3 /*break*/, 32]; + case 25: + this.b_head.push(e); + this.b_1.push(e); + if (!(this.b_2.length === 0)) return [3 /*break*/, 28]; + this.stepLevel++; + if (!!LEARNING) return [3 /*break*/, 27]; + return [4 /*yield*/, this.step(n)]; + case 26: + _g.sent(); + _g.label = 27; + case 27: return [3 /*break*/, 32]; + case 28: + if (!!LEARNING) return [3 /*break*/, 30]; + return [4 /*yield*/, this.step(n - 2)]; + case 29: + _g.sent(); + return [3 /*break*/, 32]; + case 30: return [4 /*yield*/, this.step(n)]; + case 31: + _g.sent(); + _g.label = 32; + case 32: return [3 /*break*/, 34]; + case 33: + if (this.stepLevel === 6) { // dernier échange + _e = [this.c_tail, this.b_1], this.b_1 = _e[0], this.c_tail = _e[1]; + _f = [this.tail, this.b_head], this.b_head = _f[0], this.tail = _f[1]; + this.stepLevel = 0; + this.emptyAll(); + GLOBAL_PROCESSING = false; + this.count = 0; + return [2 /*return*/]; + } + _g.label = 34; + case 34: return [2 /*return*/]; + } + }); + }); + }; + File_t.prototype.draw = function () { + var ofs = 200; + var c = 525; + staticCtx.clearRect(0, 0, BG_RECT.w, BG_RECT.h); + // processing + if (PROCESSING) { + staticCtx.fillStyle = "#000000"; + staticCtx.font = "40px garamond"; + staticCtx.fillText("Échanges en cours", 390, 50); + } + // piles + this.head.draw(c - ofs, 300); + this.tail.draw(c + ofs, 300); + this.b_head.draw(c - ofs, 600); + this.c_tail.draw(c + ofs, 600); + this.b_1.draw(c - ofs, 900); + this.b_2.draw(c + ofs, 900); + // flèches entrée/sortie + var R = 80; + var ox = c - ofs - 150 / 2 - 5; + var oy = 100; + staticCtx.strokeStyle = "#000000"; + staticCtx.beginPath(); + staticCtx.moveTo(ox - R, oy); + staticCtx.quadraticCurveTo(ox, oy - R, ox + R, oy); + staticCtx.lineTo(ox + R, oy - 10); + staticCtx.moveTo(ox + R, oy); + staticCtx.lineTo(ox + R - 10, oy); + staticCtx.stroke(); + ox = c + ofs + 150 / 2 + 5; + staticCtx.beginPath(); + staticCtx.moveTo(ox - R, oy); + staticCtx.quadraticCurveTo(ox, oy - R, ox + R, oy); + staticCtx.lineTo(ox + R, oy - 10); + staticCtx.moveTo(ox + R, oy); + staticCtx.lineTo(ox + R - 10, oy); + staticCtx.stroke(); + // explication touches + staticCtx.fillStyle = "#000000"; + staticCtx.font = "40px garamond"; + if (!LEARNING) { + if (!PROCESSING) { + staticCtx.fillText("'+' : empiler", c - ofs - 300, 150); + staticCtx.fillText("'-' : dépiler", c - ofs - 300, 242); + staticCtx.fillText("'a' : mode apprentissage (off)", c - ofs - 300, 335); + } + } + else { + if (!GLOBAL_PROCESSING) { + staticCtx.fillText("'+' : empiler", c - ofs - 300, 150); + staticCtx.fillText("'-' : dépiler", c - ofs - 300, 242); + staticCtx.fillText("'a' : mode apprentissage (on)", c - ofs - 300, 335); + } + else { + staticCtx.fillText("'Entrée' :", c - ofs - 300, 150); + staticCtx.fillText(" suivant", c - ofs - 300, 200); + staticCtx.fillText("'a' : mode apprentissage (on)", c - ofs - 300, 335); + } + } + // séparation + staticCtx.strokeStyle = "#ff0000"; + staticCtx.beginPath(); + staticCtx.moveTo(c - ofs - 200, 350); + staticCtx.lineTo(c + ofs + 200, 350); + staticCtx.stroke(); + // flèches + console.log("step:", this.stepLevel, "\n"); + staticCtx.strokeStyle = "#000000"; + if (this.stepLevel === 2 || this.stepLevel === 3) { + this.drawArrow(c - ofs, 610, c - ofs, 700); + this.drawArrow(c - ofs, 610, c + ofs, 700); + } + else if (this.stepLevel == 5) { + this.drawArrow(c + ofs, 610, c + ofs, 700); + } + else if (this.stepLevel === 6) { + this.drawArrow(c + 100, 800, c - 100, 800); + this.drawArrow(c + 100, 800, c - ofs, 610); + } + ctx.clearRect(0, 0, BG_RECT.w, BG_RECT.h); + ctx.drawImage(staticCanvas, 0, 0); + }; + File_t.prototype.drawArrow = function (x0, y0, x1, y1) { + staticCtx.beginPath(); + staticCtx.moveTo(x0, y0); + staticCtx.lineTo(x1, y1); + var vx = x1 - x0; + var vy = y1 - y0; + var n = Math.sqrt(Math.pow(vx, 2) + Math.pow(vy, 2)); + if (n !== 0) { + var s2 = Math.sqrt(2) / 2; + vx /= -n; + vy /= -n; + var vx1 = s2 * vx + s2 * vy; + var vy1 = -s2 * vx + s2 * vy; + staticCtx.lineTo(x1 + vx1 * 10, y1 + vy1 * 10); + staticCtx.moveTo(x1, y1); + var vx2 = s2 * vx - s2 * vy; + var vy2 = s2 * vx + s2 * vy; + staticCtx.lineTo(x1 + vx2 * 10, y1 + vy2 * 10); + } + staticCtx.stroke(); + }; + return File_t; +}()); +//animation de la file +var FILE = new File_t(); +function drawFrame() { + FILE.draw(); +} +function animationLoop(currentTime) { + animationFrameId = requestAnimationFrame(animationLoop); + var delaTime = currentTime - lastFrameTime; + if (delaTime < FRAME_INTERVAL) + return; + lastFrameTime = currentTime - (delaTime % FRAME_INTERVAL); + drawFrame(); +} +function initEventListener() { + var _this = this; + var handlers = { + handleKeyDown: function (e) { return __awaiter(_this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!(e.key === '+')) return [3 /*break*/, 3]; + if (!(!PROCESSING && !GLOBAL_PROCESSING)) return [3 /*break*/, 2]; + PROCESSING = true; + return [4 /*yield*/, FILE.push({ + value: Math.floor(Math.random() * 10) + 1, + color: getRandomColor() + })]; + case 1: + _a.sent(); + PROCESSING = false; + _a.label = 2; + case 2: return [3 /*break*/, 10]; + case 3: + if (!(e.key === "-")) return [3 /*break*/, 6]; + if (!(!PROCESSING && !GLOBAL_PROCESSING)) return [3 /*break*/, 5]; + PROCESSING = true; + return [4 /*yield*/, FILE.pop()]; + case 4: + _a.sent(); + PROCESSING = false; + _a.label = 5; + case 5: return [3 /*break*/, 10]; + case 6: + if (!(e.key === "Enter")) return [3 /*break*/, 9]; + if (!(LEARNING && !PROCESSING)) return [3 /*break*/, 8]; + PROCESSING = true; + return [4 /*yield*/, FILE.step()]; + case 7: + _a.sent(); + PROCESSING = false; + _a.label = 8; + case 8: return [3 /*break*/, 10]; + case 9: + if (e.key === "a") { + LEARNING = !LEARNING; + FILE.emptyAll(); + FILE.stepLevel = 0; + FILE.count = 0; + PROCESSING = false; + GLOBAL_PROCESSING = false; + } + _a.label = 10; + case 10: return [2 /*return*/]; + } + }); + }); } + }; + document.addEventListener('keydown', handlers.handleKeyDown); + callbacks.push(function () { + document.removeEventListener('keydown', handlers.handleKeyDown); + }); +} +function getRandomColor() { + return '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0'); +} +function initDisplay() { + initEventListener(); + requestAnimationFrame(animationLoop); + window.cleanup = cleanup; +} +function cleanup() { + cancelAnimationFrame(animationFrameId); + callbacks.forEach(function (fn) { return fn(); }); +} +if (typeof window !== 'undefined') { + initDisplay(); +} diff --git a/cours/CYK.html b/cours/CYK.html new file mode 100644 index 0000000..1e657ae --- /dev/null +++ b/cours/CYK.html @@ -0,0 +1,176 @@ + + +Rappels de notions +=============== +Une grammaire $G$ est la donnée d'un quadruplet $G=(V, T, P, S)$, où $V$ est un ensemble de variable, $T$ un ensemble de terminaux, $P$ une règle de production et $S$ une variable de début. Par la suite, nous considérerons les grammaires sous forme normale de Chomsky qui sont les grammaires dont $P\subseteq V\times (V^2\times T)$. + +Idée de l'algorithme +============== + +Problème formel +----------- + +Le problème du mot consiste en le fait de déterminer si un mot donné est engendré par une grammaire. Une manière d'y parvenir est de considérer les sous-mots du mot en question et de déterminer s'il existe des suites de règle de production qui permettent d'engendrer ce sous-mot. Ainsi, il est possible de transformer le problème du mot en problème de programation dynamique: + +Soit un mot $a=a_1a_2...a_{n-1}a_{n}$ et une grammaire. Nous commençons par définir $\forall i< j, E_{i, j}=\{X|a_{i}...a_{j}\in L_G(X)\}$. Autrement dit, toutes les règles qui peuvent engendrer le sous-mot $a_{i}...a_{j}$. Ces ensembles peuvent ensuite etre construits par récurrence sur $j-i$: +- Cas de base: + + $\forall i, X\in E_{i, i} \Leftrightarrow (X, a_i)\in P$ + +- Cas récursif: + + $\forall i< j, X\in E_{i, j} \Leftrightarrow \exists k\in [|i, j|], \exists (X, YZ)\in P, Y\in E_{i, k}, Z\in E_{k+1, j}$ + +Visuellement +--------- + +Concrètement, il s'agit de considérer tous les sous-découpages de cette forme: + +*************************************************************** +* i k j +* .-------.----------------.----------------.--------. +* Mot a: | | Engendré par Y | Engendré par Z | | +* '-------'-------+--------'--------+-------'--------' +* | | +* | | +* i v v j +* .-------.---------------------------------.--------. +* Mot a: | | Engendré par X via X--> YZ | | +* '-------'---------------------------------'--------' +* +*************************************************************** + +Nous pouvons ainsi construire $E_{1,n}$, et conclure que le mot est engendré par notre grammaire si et seulement si $S\in E_{1,n}$. + +Exemple +============= +Considérons la grammaire suivante: +- $S\to AB$ +- $A\to BC|a$ +- $B\to CD|AD|b$ +- $C\to a$ +- $D\to b$ + +Question: le mot $abba$ est-il engendré par cette grammaire ? + +Pour se faire, nous cherchons les sous-mots engendrés par des règles. Pour se faire, nous créons le tableau suivant: + +$a$ $(1)$ | $b$ $(2)$ | $a$ $(3)$ | $b$ $(4)$ +:----------------:|:----------------:|:----------------:|:----------------: +$E_{1,1}=\{A,C\}$ |$E_{2,2}=\{B,D\}$ |$E_{3,3}=\{A,C\}$ |$E_{4,4}=\{B,D\}$ + +Puisque la lettre $a$ peut etre obtenue via les règles $A\to a$ et $C\to a$, et que $b$ peut etre obtenu via les règles $B\to b$ et $D\to b$. + +La deuxième étape consiste à calculer les $E_{i, i+1}$ par récurrence. Pour cette étape, les seules possibilités sont la concaténation entre $E_{i, i}$ et $E_{i+1, i+1}$. +Ainsi, $E_{1, 2}$ est calculé à partir de $E_{1, 1}$ et $E_{2, 2}$ en considérant tous les $N$ tels qu'il existe les règles $N\to AB$ ou $N\to AD$ ou $N\to CB$ ou $N\to CD$. Or, il n'y a ici que les règles $S\to AB$, $B\to CD$ et $B\to AD$. Ainsi, $E_{1, 2} = \{S,B\}$. Ainsi, nous obtenons une nouvelle ligne au tableau: + + +$a$ $(1)$ | $b$ $(2)$ | $a$ $(3)$ | $b$ $(4)$ +:----------------:|:----------------:|:----------------:|:----------------: +$E_{1,1}=\{A,C\}$ |$E_{2,2}=\{B,D\}$ |$E_{3,3}=\{A,C\}$ |$E_{4,4}=\{B,D\}$ +$E_{1,2}=\{S,B\}$ |$E_{2,3}=\{A\}$ |$E_{3,4}=\{S,B\}$| + +Pour l'étape suivante, il y a maintenant plus de cas à considérer. En effet, nous pouvons écrire les sous-mots de plusieurs manières possible : $aba=a+ba=ab+a$. Il faut ainsi consédérer pour calculer $E_{1, 3}$ les couples $(E_{1, 2}, E_{3, 3})$ et $(E_{1, 1}, E_{2, 3})$. Ainsi, dans cet exemple, $E_{1, 3}=\{V\}$, puisque le cas des $\emptyset$ ne peuvent pas aboutir à une règle de production d'une grammaire sous la forme normale de Chomsky. + +Ainsi, le tableau est transformé en : + + +$a$ $(1)$ | $b$ $(2)$ | $a$ $(3)$ | $b$ $(4)$ +:----------------:|:----------------:|:----------------:|:----------------: +$E_{1,1}=\{A,C\}$ |$E_{2,2}=\{B,D\}$ |$E_{3,3}=\{A,C\}$ |$E_{4,4}=\{B,D\}$ +$E_{1,2}=\{S,B\}$ |$E_{2,3}=\{A\}$ |$E_{3,4}=\{S,B\}$| +$E_{1,3}=\{V\}$ |$E_{2,4}=\{U\}$ || + +Et enfin, nous pouvons calculer $E_{1, 4}$: + +$a$ $(1)$ | $b$ $(2)$ | $a$ $(3)$ | $b$ $(4)$ +:----------------:|:----------------:|:----------------:|:----------------: +$E_{1,1}=\{T,V\}$ |$E_{2,2}=\{W,U\}$ |$E_{3,3}=\{W,U\}$ |$E_{4,4}=\{TV\}$ +$E_{1,2}=\{S,V\}$ |$E_{2,3}=\emptyset$|$E_{3,4}=\{U,T\}$| +$E_{1,3}=\{V\}$ |$E_{2,4}=\{U\}$ || +$E_{1,4}=\{V, S\}$||| + +Puisque $S\in E_{1, 4}$, cela signifie qu'il existe des dérivations aboutissants au mot $abba$, et donc que ce mot est bel et bien engendré par cette grammaire. + +Algorithme Cocke-Younger-Kasami +================== + +L'algorithme +----------- + +L'algorithme suit le procédé présenté précédemment: le cas initial en premier, puis une boucle sur $d=j-i$ croissant, dans lesquels nous construisons les $E_{i, j}$: + +~~~~~~~~~~none +initialiser E[i,j] = {} +pour i de 1 à n faire: // cas de base +| pour (X, a[i]) dans P faire: +| | rajouter X dans E[i, j] + +pour d de 1 à n-1 faire: // récurrence sur d = j-i (1) +| pour i de 1 à n-d faire: // pour i < j (2) +| | pour k de i à i+d-1 faire: // parcourir les découpage du sous-mot possible (3) +| | | pour (X, YZ) dans P faire: +| | | | si Y dans E[i, k] et Z dans E[k+1, j] faire: +| | | | | rajouter X dans E[i, j] + +retourne vrai si S est dans E[1, n], faux sinon +~~~~~~~~~~ + +Finitude +-------------- +La finitude est assez directe, puisque l'algorithme effectue des boucle sur une variable croissant strictement à chaque boucle, et majoré par $n$ ou $|G|$. Ainsi, nous sommes certain que cet algorithme termine. + +Correction +---------------- +Pour montrer la correction, il suffit de démontrer l'invariant de fin de boucle pour la boucle $(1)$: + + $$I(d):\forall i, \forall j, j-i\leq d, E_{i, j} \textit{ est correct}$$ + +- Initialisation: + + L'initialisation se fait sur $I(0)$, avant que la boucle commence. Dans ce cas la, il sagit des mots de longueur $1$. Or par construction dans le cas de base, tous les $E_{i, i}$ sont correct. Ainsi, nous avons bien l'initialisation. + +- Hérédité: + + Supposons l'invariant vrai à la fin de la boucle $d-1$. Nous pouvons constater que pour tout $i$ la boucle $(3)$ construit $E_{i, j}=\{X|\exists k, \exists (X, YZ), Y\in E_{i,k}, Z\in E_{k+1,j}\}$. Or, par hypothèse de récurences, les $E_{i, k}$ et les $E_{k+1, j}$ sont corrects. De ce fait, $E_{i, j}$ est lui aussi correct. + +Ainsi, à la fin de la dernière boucle, il est découle que $E_{1, n}$ est correct, et donc que l'algorithme est correct. + +Complexité +================= + +Temporelle +----------- +Le cas de base s'effectue via une boucle en $O(n)$, dans lauelle une autre boucle s'effectue en $O(|G)$, soit au total en $O(n|G|)$. + +La cas d'hérédité effectue trois boucles en $O(n)$, et une boucle en $O(|G|)$. La réponse évidente est donc en $O(n^3|G|)$. Cependant, vérifier si une variable $Y$ est dans un $E_{i, j}$ est lui aussi couteux. Si la structure est une liste, alors vérifier l'appartenance se fait en $O(|G)$. Il est possible d'utiliser des structures plus efficaces, et en particulier le bitset qui permet d'inserer et de tester l'appartenance en temps constant. + +Au final, l'algorithme proposé s'effectue en $O(n^3|G|)$. + +Une autre vision possible est de considérer la taille de la grammaire négligeable face à la taille du mot. Dans ce cas seulement, nous pouvons englober $|G|$ dans une constante. + +Il est également possible d'adapter l'algorithme à une multiplication de matrice permettant de réduire la complexité cubique au profit d'une constante cachée conséquente. + +Spatiale +----------- +Naivement, il est nécessaire de garder en mémoire la valeur de chaque $E_{i, j}$, chacun de taille au plus en $O(|G|)$. Ainsi, la complexité est en $O(n^2|G|)$. + +Peut-on faire mieux ? À priori, il n'est pas possible de faire mieux dans la mesure où le calcule de $E_{i, j}$ dépend de la valeur de $E{i, i}$ et de $E_{j, j}$, qui sont donc réutilisés aussi bien au début de l'algorithme qu'en arrivant en fin d'algorithme. Il ne semble donc pas possible de pouvoir diminuer la complexité spatiale. + +Exercices +============ + +Application +------------ +Le mot $abab$ est-il engendré par la grammaire proposé en exemple ? + +Sous-mots +------------ +Déduire de l'algorithme CYK l'ensemble des sous-mots de $bbaba$ engendré par la grammaire donné en exemple. + +Généralisation +------------ +En pratique, l'algorithme est-il généralisable à toute grammaire non contextuelle ? Sinon, sous-quelle condition peut-on le faire ? + + + diff --git a/cours/GlushkovDeterministe.html b/cours/GlushkovDeterministe.html new file mode 100644 index 0000000..fa2a22a --- /dev/null +++ b/cours/GlushkovDeterministe.html @@ -0,0 +1,421 @@ + + +Idée de l'algorithme +===================================================== + +Dans le but de comprendre l'algorithme, il faut commencer par comprendre l'idée qui se cache derrière. Pour se faire, nous commençons donc par un exemple informel sur le langage $(a|b)^*abb$. Pour commencer, il est nécessaire de considérer un caractère de fin: $ (a|b)^*abb\# $. Ensuite, nous distinguons chaque lettre de l'expression à l'aide d'un indicage: $ (a_1 | b_2)^* a_3 b_4 b_5 \#_6 $. + +Cela nous permet maintenant de définir l'ensemble des premières lettres, des dernières lettre et les transitions: + +- *premières lettres* : $\{1,2,3\}$ +- *dernière lettre* : $\{6\}$ +- *transitions* : $1 \mapsto \{1,2,3\}, \ 2 \mapsto \{1,2,3\}, \ 3 \mapsto \{4\}, \ 4 \mapsto \{5\}, \ 5 \mapsto \{6\}$ + +L'ensemble des transitions est définit en fonction des positions possibles depuis une autres. Par exemple, les mots suivants sont accépté: $a_1a_1a_3b_4b_5\#_6$, $a_1b_2a_3b_4b_5\#_6$ et $a_1a_3b_4b_5\#_6$. De fait, après la lettre $a_1$, il est possible d'avoir les lettres $a_1$, $b_2$ et $a_3$. Dit autrement, depuis la position $1$, il est possible d'atteindre les états $1$, $2$ et $3$. + +Maintenant, il est possible de construire l'automate fini déterministe (AFN) en suivant ces étapes: + +Premièrement, nous plaçons l'état initial correspondant aux premières lettres. Il s'agit de l'état initial: + +************************* +* +* .-------. +* -->| 1, 2, 3 | +* '-------' +* +************************* + +Ensuite, pour chaque lettre possible, nous regardons quels sont les suivant correspondant pour cet état donné. Ici par exemple, s'il on considère la lettre $a$, il peut s'agit soit de $a_1$, soit de $a_3$. En effet, les positions considérées sont $1$, $2$ et $3$, mais la position $2$ correspond à $b_2$. Le nouvel état sera l'union des positions suivantes possibles. Par exemple, les positions suivants $1$ sont $1$, $2$ et $3$, et les positions suivants $3$ sont réduites à $4$. Ainsi, nous pouvons rajouter l'état $\{1, 2, 3, 4\}$ et la transition qui vient avec. Après cette étape, l'automate devient: + +******************************************* +* b +* .-. +* | | +* | v +* .-+-----. a .----------. +* -->| 1, 2, 3 +------>| 1, 2, 3, 4 | +* '-------'x '----------' +* +******************************************* + +Maintenant que toutes les transitions sortant de l'état considéré ont été tracés, nous pouvons donc marquer cet état comme traité. Puisque nous avons au moins un état non marqué (ici, un seul état non marqué), nous pouvons réitérer: + +******************************************* +* b a +* .-. .-. +* | | | | +* | v | v +* .-+-----. a .--+-------. +* -->| 1, 2, 3 +------>| 1, 2, 3, 4 | +* '-------'x '----+-----'x +* | +* |b +* v +* .----------. +* | 1, 2, 3, 5 | +* '----------' +* +* +******************************************* + +Puis de même: + +******************************************* +* b a +* .-. .-. +* | | | | +* | v | v +* .-+-----. a .--+-------. +* -->| 1, 2, 3 +------>| 1, 2, 3, 4 | +* '-------'x '----+--+--'x +* ^ | +* |a |b +* | v +* .----------. b .--+-------. +* | 1, 2, 3, 6 |<----+ 1, 2, 3, 5 | +* '----------' '----------'x +* +* +******************************************* + +Et enfin: + +******************************************* +* b a +* .-. .-. +* | | | | +* | v | v +* .-+-----. a .--+-------. +* -->| 1, 2, 3 +---+-->| 1, 2, 3, 4 | +* '-------'x '----+--+--'x +* ^ ^ | +* a | |a |b +* .--------' | | +* | | v +* .----+---+-. b .---+------. +* | 1, 2, 3, 6 |<----+ 1, 2, 3, 5 | +* '----+-----' '----------'x +* +* +* +******************************************* + +Les états finaux sont les états contenant la position du caractère final, donc ici contenant $6$, nous laissant finalement avec l'AFD final: + +******************************************* +* b a +* .-. .-. +* | | | | +* | v | v +* .-+-----. a .--+-------. +* -->| 1, 2, 3 +---+-->| 1, 2, 3, 4 | +* '-------' '----+--+--' +* ^ ^ ^ | +* |b a | |a |b +* | .--------' | | +* | | | v +* .----+---+-. b .---+------. +* | 1, 2, 3, 6 |<----+ 1, 2, 3, 5 | +* '----+-----' '----------' +* | +* | +* v +******************************************* + +Algorithme de Glushkov déterministe +======================== + +L'algorithme de Glushkov travaille sur l'arbre syntaxique de l'expression régulière. En voici un pour l'exemple précédent: + +********************************************************************* +* .-. +* | . | +* +-+ +* / \ +* / \ +* / \ +* .-+ +-. +* | ⁎ | | . | +* '+' +-+ +* | / \ +* .+. .-+ \ +* | ǀ | | a | \ +* +-+ '-' +-. +* / \ | . | +* .-+ +-. +-+ +* | a | | b | / \ +* '-' '-' .-+ \ +* | b | \ +* '-' +-. +* | . | +* +-+ +* / \ +* .-+ +-. +* | b | | # | +* '-' '-' +********************************************************************* + +La première étape a été d'individualiser les tokens. Cela peut se faire arbitrairement et n'a pas une importance en soi pour l'algorithme, car ce dernier les distingues déjà de fait de manière implicite. Cependant, cela permet de mieux le comprendre et de le visualiser grâce aux exemples. + +Voici ce que cela pourrait donner pour notre arbre syntaxique: + +********************************************************************* +* .-. +* | . | +* +-+1 +* / \ +* / \ +* / \ +* .-+ +-. +* | ⁎ | | . | +* '+'2 +-+3 +* | / \ +* .+. .-+ \ +* | ǀ | | a | \ +* +-+4 '-'5 +-. +* / \ | . | +* .-+ +-. +-+6 +* | a | | b | / \ +* '-'7 '-'8 .-+ \ +* | b | \ +* '-'9 +-. +* | . | +* +-+10 +* / \ +* .-+ +-. +* | b | | # | +* '-'11 '-'12 +********************************************************************* + +Cela nous sera grandement utile pour notre exemple. Mais pour l'instant, nous pouvons définir quatre fonctions récursivement. Ces dernières travaillent sur les sous-arbres de l'arbre syntaxique, ce qui nous permettra de ressortir algorithmiquement des informations sur les relations entre les tokens. + +Annulabilité : +---------- + +Nous commençons avec la fonction $annulable()$ qui renvoie vrai si et seulement si le l'expression régulière représentée par le sous-arbre mis en paramètre contient le mot vide. Cette fonction se définit de manière récursive: + +- $annulable(feuille) = \bot$ +- $annulable(e_1 \cdot e_2) = annulable(e_1) \wedge annulable(e_2)$ +- $annulable(e_1 | e_2) = annulable(e_1) \vee annulable(e_2)$ +- $annulable(e^*) = \top$ + +Premières positions : +--------- + +Cela nous permet ensuite de définir la fonction $premier()$ qui renvoie l'ensemble des tokens par lesquels l'expression régulière représentée par le sous-arbre considérée peut commencer. Elle se définit également par récurrence comme suit: +- $premier(feuille \ n) = \{n\}$ +- $premier(e_1 \cdot e_2) = \bigg\{ \begin{array}{c} premier(e_1) \ \text{si} \ \neg annulable(e_1) \\ premier(e_1) \cup premier(e_2) \ \text{sinon} \end{array}$ +- $premier(e_1 | e_2) = premier(e_1) \cup premier(e_2)$ +- $premier(e^*) = premier(e)$ + +La disjonction de cas vient du fait que si dans la concaténation, $e_1$ n'est pas annulable, alors il possède nécessairement au moins un caractère. De fait, les premières lettres de $e_1\cdot e_2$ sont celles de $e_1$. À l'inverse, si $e_1$ est annulable, alors il est possible de commencer un mot de $e_1\cdot e_2$ de la forme $\epsilon\cdot e_2$. Les premiers caractères de $e_1\cdot e_2$ peuvent alors être également ceux de $e_2$, et l'union est alors nécessaire. + +Dernières positions : +------------ + +Ce cas reste sesiblement identique au cas précédent, à la différence que cette fonction calcule les derniers caractères possible de l'expression représenté par le sous-arbre considéré. + +- $dernier(feuille \ n) = \{n\}$ +- $dernier(e_1 \cdot e_2) = \bigg\{ \begin{array}{c} dernier(e_2) \ \text{si} \ \neg annulable(e_2) \\ dernier(e_1) \cup dernier(e_2) \ \text{sinon} \end{array}$ +- $dernier(e_1 | e_2) = dernier(e_1) \cup dernier(e_2)$ +- $dernier(e^*) = dernier(e)$ + +Cependant, le cas de la concaténation est inversée. En effet, puisqu'il s'agit de déterminer les caractères finaux, la disjonction de cas se fait maintenant sur le fils droite, et non plus le fils gauche. + +Positions suivantes : +------------- + +Enfin, vient la dernière fonction qui permet de déterminer les caractères qui en suive un autre. Nous pouvons le définir comme suit: + +- $suivant(feuille \ n)=\bigg\{\begin{array}{c} \bigcup{premier(e_2)}\ \textit{s'il existe } e_1\cdot e_2\textit{ tels que }n\in dernier(e_1) \\ union \\ \bigcup{premier(e)}\ \textit{s'il existe } e^*\textit{ tel que }n\in dernier(e) \end{array}$ +- $suivant(e_1\cdot e_2)=\emptyset$ +- $suivant(e_1|e_2)=\emptyset$ +- $suivant(e^*)=\emptyset$ + +Une autre façon de déterlminer les caractères suivants un autre, est de construire l'ensemble de manière procédurale. Lorsque que l'on considère un noeud, nous rajoutons des éléments dans certains ensenble selon la nature du noeud en question. Il y a alors deux options possibles: + +- noeud $e_1 \cdot e_2$ $\to$ $\forall a \in dernier(e_1), premier(e_2) \subset suivant(a)$ +- noeud $e^*$ $\to$ $\forall a \in dernier(e), premier(e) \subset suivant(a)$ + + +Algorithme +---------------- +Enfin, nous nous intéressons maintenant à l'algorithme qui utilise les informations précédentes dans le but de construire l'ensemble des états $Etats$ et les transitions $Trans$ de l'automate: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~none +Etats = {premier(e0)} non marqué, avec e0 la racine de l'arbre syntaxique +tant qu'il existe S dans Etats non marqué faire: + marquer S + pour tout symbole a dans Sigma faire: + S' = Union(suivant(p)) ou p est dans S et p correspond à a + si S' n'est pas Etats : + rajouter S' dans Etats non marqué + rajouter Trans(S,e) = S' +retourner Etats, Trans +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Exemple +============ + +Pour l'arbre syntaxique précédent, nous obtenons le tableau suivant: + +Noeud/Feuille | annulable | premier | dernier | suivant +:------------:|:---------:|:-------:|:-------:|:-------: + 1 | Faux | 5, 7,8 | 12 | + 2 | Vrai | 7,8 | 7, 8 | + 3 | Faux | 5 | 12 | + 4 | Faux | 7, 8 | 7, 8 | + 5 | Faux | 5 | 5 | 9 + 6 | Faux | 9 | 12 | + 7 | Faux | 7 | 7 | 5,7,8 + 8 | Faux | 8 | 8 | 5,7,8 + 9 | Faux | 9 | 9 | 11 + 10 | Faux | 11 | 12 | + 11 | Faux | 11 | 11 | 12 + 12 | Faux | 12 | 12 | + +Ici, le neud racine est le noeud $1$. De fait, nous pouvons appliquer l'algorithme décrit précédemment en commençant par l'état initial: + +************************* +* +* .-------. +* -->| 5, 7, 8 | +* '-------' +* +************************* + +Puis: + +******************************************* +* b +* .-. +* | | +* | v +* .-+-----. a .----------. +* -->| 5, 7, 8 +------>| 5, 7, 8, 9 | +* '-------'x '----------' +* +******************************************* + +Ensuite: + +******************************************* +* b a +* .-. .-. +* | | | | +* | v | v +* .-+-----. a .--+-------. +* -->| 5, 7, 8 +------>| 5, 7, 8, 9 | +* '-------'x '----+-----'x +* | +* |b +* v +* .-----------. +* | 5, 7, 8, 11 | +* '-----------' +* +* +******************************************* + +Suivie de: + +******************************************* +* b a +* .-. .-. +* | | | | +* | v | v +* .-+-----. a .--+-------. +* -->| 5, 7, 8 +------>| 5, 7, 8, 9 | +* '-------'x '----+--+--'x +* ^ | +* |a |b +* | v +* .-----------. b .--+--------. +* | 5, 7, 8, 12 |<----+ 5, 7, 8, 11 | +* '-----------' '-----------'x +* +* +******************************************* + +Et enfin: + +******************************************* +* b a +* .-. .-. +* | | | | +* | v | v +* .-+-----. a .--+-------. +* -->| 5, 7, 8 +------>| 5, 7, 8, 9 | +* '-------'x '----+--+--'x +* ^ ^ ^ | +* |b a | |a |b +* | .--------' | | +* | | | v +* .-----+---+-. b .--+--------. +* | 5, 7, 8, 12 |<----+ 5, 7, 8, 11 | +* '-----------'x '-----------'x +* +* +* +******************************************* + +Et finalement, on rajoute les états finaux: + +******************************************* +* b a +* .-. .-. +* | | | | +* | v | v +* .-+-----. a .--+-------. +* -->| 5, 7, 8 +------>| 5, 7, 8, 9 | +* '-------' '----+--+--' +* ^ ^ ^ | +* |b a | |a |b +* | .--------' | | +* | | | v +* .-----+---+-. b .--+--------. +* | 5, 7, 8, 12 |<----+ 5, 7, 8, 11 | +* '-----+-----' '-----------' +* | +* | +* v +******************************************* + +Nous obtenons bien l'automate que nous avions en première section ! + +Exercices +=================== + +$a^*b$ +------- +Appliquer l'algorithme sur le langage $a^*b$. Que peut-on en dire ? + +
+ Voir la réponse ici + +L'automate créé possède un état puits représenté par l'ensemble vide, et est de surcroit minimal. Sa complétude s'explique simplement par la création de transition pour chaque symbole pour chaque état. +
+ +$(a|b)^*a(a|b)$ +------- +Faites de meme sur le langage $(a|b)^*a(a|b)$. Il est possible de trouver la correction ici. + +Correction et finitude +------- +Démontrer que l'algorithme proposé finit et est correct. + +Minimalité +------- +Démontrer que l'automate obtenu est l'automate minimal, ou trouver un contre exemple. + +
+ Voir la réponse ici + +Le langage $c^*|c$ fournit un contre exemple où l'algorithme ne retourne pas l'automate minimal. +
+ + + + + + diff --git a/cours/MasterTheorem.html b/cours/MasterTheorem.html new file mode 100644 index 0000000..9bd942e --- /dev/null +++ b/cours/MasterTheorem.html @@ -0,0 +1,277 @@ + + +Introduction +================ + +Il existe beaucoup de méthode dans le but de calculer la compléxiter temporelle d'un algorithme. Prenons par exemple l'algorithme de tri fusion ci-dessous décrit en pseudo-code: + +~~~~~~~~~~~node +tri_fusion(L): + si |L| <= 1 retourner L + sinon + diviser L en deux sous listes L1 et L2 + + tri_fusion(L1) + tri_fusion(L2) + + L = fusion de L1 et L2 +~~~~~~~~~~~ + +Notons $n$ la taille de la liste en entrée. Nous cherchons à determiner $T(n)$ la compléxité de cet algorithme en temps en fonction de $n$, la taille de l'entrée. + +Tout d'abord, il y a deux appelles à $tri\_fusion$, dont la compléxité en temps est $T(\frac n2)$, du au fait que la liste a étée divisée en deux. Ensuite, la fusion peut s'effectuer en temps linéaire puisque les deux listes fusionnée sont déjà triée. Il suffit alors d'ajouter à la fin de la nouvelle liste l'élément le plus petit des deux listes (qui sont donc nécessairement en tete de liste, car triée). + +Au final, nous obtenons la relation suivante: + $$T(n)=2T(\frac n2)+O(n)$$ + +Nous nous rendons alors compte qu'il est nécessaire d'avoir des outils supplémentaires afin de pouvoir conclure. En particulier, le Master Theorem est un outils très puissant pour les relation de cette forme là. + +Theorème (Master Theorem) +============================ + +Énoncé général: +------------- + +!!! + Soit $T$ une fonction croissante à partir d'un certain rang. S'il existe $a$ et $c > 0$ et $b > 1$ tels que: + - $T(n) = aT(\frac nb)+cn^k$ + Alors: + - Si $a < b^k$, $T(n)=\Theta(n^k)$ + - Si $a > b^k$, $T(n)=\Theta(n^{\log_b(a)})$ + - Si $a=b^k$, $T(n)=\Theta(n^k\log_b(n))$ + +!!! tip + Il s'agit ici de l'énoncé purement mathématique du Master Theorem, puisqu'il ny a pas nécessairement de conditions initiales, ni de condition de divisibilité par de $n$ par $b$. Cependant, en informatique, ces conditions sont nécessaire, et npous retrouverons plutôt l'énoncé suivant (justifiant la démonstration de la première condition): + +Énoncé informatique: +----------------------- + +!!! + Soit $T:N\to N$ une fonction croissante à partir d'un certain rang. S'il existe $a$, $c$, $d$ et $n_0 > 0$ et $b > 1$ tels que: + - $T(n_0)=d$ + - $T(n) = aT(\frac nb)+cn^k$ pour $n$ de la forme $n_0b^p$ + + Alors: + - Si $a < b^k$, $T(n)=\Theta(n^k)$ + - Si $a > b^k$, $T(n)=\Theta(n^{\log_b(a)})$ + - Si $a=b^k$, $T(n)=\Theta(n^k\log_b(n))$ + + +Intuition +================= + +Ce théorème décrit comment la subdivision d'un problème affecte le temps de calcule. En particulier, lorsqu'un problème donné est subdivisé en $a$ problèmes de taille $\frac1b$, et ainsi de suite, nous construisons en réalité un arbre équilibré dont la géométrie varie en fonction de $a$ et de $b$. + +$\textit{Cas } a< b^k:$ +------------ + +Voici un exemple d'illustration pour $a=2$ et $b=4$: + +***************************************** +*.--------.---------.---------.--------.* +*| | | | |* +*'--------'---------'---------'--------'* +* | | * +* | | * +* v v * +* .--.--.--.--. .--.--.--.--. * +* | | | | | | | | | | * +* '--'--'--'--' '--'--'--'--' * +* | | | | * +* | | | | * +* v v v v * +* .--. .--. .--. .--. * +* | | | | | | | | * +* '--' '--' '--' '--' * +***************************************** + +Ici, nous constatons que la taille de chaque couche est drastiquement réduite lors des subdivisions. De ce fait, on "sent" bien que l'impact de la subdivision est moindre, puisque la quantité de sous-problème à traiter est relativement faible. La composante principale est donc ici le traitement du problème à la racine, laissant ainsi une compléxité en $\Theta(n^k)$. + +$\textit{Cas } a> b^k:$ +------------------- + +Voici un exemple d'illustration pour $a=4$ et $b=2$: + +******************************************* +* .-------.-------. * +* | | | * +* '-------'-------' * +* | | | | * +* | | | | * +* v v v v * +*.---.---. .---.---. .---.---. .---.---.* +*| | | | | | | | | | | |* +*'---'---' '---'---' '---'---' '---'---'* +* | | | | | | | | | | | | | | | | * +* | | | | | | | | | | | | | | | | * +* v v v v v v v v v v v v v v v v * +* * +* ... ... ... ... * +******************************************* + +Ici on voit bien que la largeur de l'arbre croit exponentiellement. Ce n'est donc pas le traitement du problème à la racinbe qui sera couteux, mais bien le nombre d'appel à des sous-problèmes. Ainsi, multiplier par deux l'entrée multiplie par $4$ la taille de l'arbre, et donc des calculs. Cela peut être retrouvé mathématiquement, puisque $2^{\log_2(4)}=2^2=4$. + +$\textit{Cas } a= b^k:$ +------------------ + +Voici un exemple d'illustration pour $a=2$ et $b=2$: + +******************************************* +*.-------------------.-------------------.* +*| | |* +*'-------------------'-------------------'* +* | | * +* | | * +* v v * +*.---------.---------.---------.---------.* +*| | | | |* +*'---------'---------'---------'---------'* +* | | | | * +* | | | | * +* v v v v * +*.----.----.----.----.----.----.----.----.* +*| | | | | | | | |* +*'----'----'----'----'----'----'----'----'* +******************************************* + +Ici, nous obtenons un arbre binaire équilibré. De ce fait, la compléxité du calcul est similaire que le traitement d'un arbre équilibré, donc la compléxité est en $\Theta(n^k)$ pour chaque couche, multiplié par le nombre couche qui est en $\Theta(\ln(n))$. Ainsi, on obtient bien une compléxité en $\Theta(n^k\ln(n))$ + +Preuve +================= +Pour commencer, nous pouvons constater que $T(\frac nb)$ appelle derrière $T(\frac n{b^2})$ lui-même apellant lui-même $T(\frac n{b³})$ etc... Ainsi, la suite géométrique $(b^pn_0)_{p\ge 0}$ apparait naturellement dans l'analyse de cette fonction. Nous commencerons donc par démontrer le théorème sur ce cas particulier, ce qui nous permettra de généraliser pour n quelconque. + +$\textit{Situation }n=b^pn_0:$ +---------------------------------- + +Nous noterons par la suite $n_p=b^pn_0$. Pour procéder par récurrence, il nous faut tout d'abord une formule explicite en fonction de $p$. Pour se faire, nous pouvons remarquer que: + +$$T(n)=aT(\frac nb)+cn^k=a^2T(\frac{n}{b^2})+ac(\frac{n}{b})^k+cn^k\\=a^2T(\frac{n}{b^2})+ac(\frac{n}{b})^k+a^0c(\frac n{b^0})^k\\$$ + +Ainsi, nous sentons bien qu'en réitérant, nous devrions optenir une formule de la forme $T(n)\sim a^pT(\frac{n}{b^p})+\sum_{i=0}^{p-1}{a^ic(\frac{n}{b^i})^k}\sim da^p+\sum_{i=0}^{p-1}{ca^i(\frac{n_p}{b^i})^k}$ + +Nous allons donc montrer par récurrence sur $p$ que $$T(n_p)=da^p+\sum_{i=0}^{p-1}{ca^i(\frac{n_p}{b^i})^k}$$ + +- Initialisation: + + Nous avons d'une part: $T(n_0)=d$ par définition + + Et d'autre part: $da^0+\sum_{i=0}^{0-1}{ca^i(\frac{n_0}{b^i})^k}=d$ + + Le résultat est donc vrai pour $p=0$. + +- Récurence: + + Supposons le résultat vrai au rang $p$. Alors: + $$T(n_{p+1}) = aT(\frac {n_{p+1}}b)+{n_{p+1}}^k\textit{ par définition de T}\\ + = aT(n_p)+{n_{p+1}}^k\textit{ car }b*n_p=n_{p+1}\\ + = a*da^p+a*\sum_{i=0}^{p-1}{ca^i(\frac{n_p}{b^i})^k}+cn_{p+1}^k\textit{ par hypothèse de récurence}\\ + = da^{p+1}+\sum_{i=0}^{p-1}{ca^{i+1}(\frac{n_{p+1}}{b^{i+1}})^k}+cn_{p+1}^k\\ + = da^{p+1}+\sum_{j=1}^{p}{ca^j(\frac{n_{p+1}}{b^j})^k}+cn_{p+1}^k\textit{ avec le changement d'indice }j=i+1\\ + = da^{p+1}+\sum_{j=0}^{p}{ca^j(\frac{n_{p+1}}{b^j})^k}\textit{ en remarquant que } cn_{p+1}^k = c(\frac{n_{p+1}}{b^0})^k$$ + +Nous avons donc bien montré la formule par récurrence sur $p$. Nous pouvons dès lors travailler sur cette dernière. Pour commencer, nous pouvons isoler $n_p$ pour obtenir: + + $$T(n_p)=da^p+c{n_p}^k\sum_{i=0}^{p-1}{(\frac{a}{b^k})^i}=\delta(p)+c{n_p}^k\gamma(p)$$ + +avec + + - $\delta(p)=da^p=da^{\log_b(\frac {n_p}{n_0})}=d{a^{\log_b(n_p)}}/{a^{\log_b(n_0)}}=d{b^{\log_b(a)\log_b(n_p)}}/{a^{\log_b(n_0)}}=d{{n_p}^{\log_b(a)}}/{a^{\log_b(n_0)}}=\Theta({n_p}^{\log_b(a)})$ + + - $\gamma(p)=\sum_{i=0}^{p-1}{(\frac{a}{b^k})^k}$ + +Nous pouvons ainsi reconnaitre une somme géométrique de raison $\frac a{b^k}$ dont la valeur varie: + + - Si $a < b^k$, alors la série géométrique converge et: + + $$\gamma(p)=\sum_{i=0}^{p-1}{(\frac{a}{b^k})^k}=\frac{1-(\frac{a}{b^k})^p}{1-\frac a{b^k}}\le\frac1{1-\frac a{b^k}}=\Theta(1)$$ + + Nous avons alors: + + $$\underline{T(n_p)}=\Theta({n_p}^{\log_b(a)})+\Theta({n_p}^k)=\underline{\Theta({n_p}^{k})}\textit{ car }\log_b(a) < k$$ + - Si $a > b^k$, alors la série diverge et: + + $$\gamma(p)=\sum_{i=0}^{p-1}{(\frac{a}{b^k})^k}=\frac{(\frac{a}{b^k})^p-1}{\frac a{b^k}-1}\sim\frac{(\frac{a}{b^k})^p}{\frac a{b^k}-1}=\frac{1}{\frac a{b^k}-1}(\frac{a}{b^k})^p$$ + + Or: + + $$c{n_p}^k(\frac{a}{b^k})^p=c(\frac{n_p}{b^p})^ka^p=c{n_0^k}a^p=\Theta({n_p}^{\log_b(a)})$$ + + Ainsi: + + $$\underline{T(n_p)=\Theta({n_p}^{\log_b(a)})}$$ + + + - Si $a = b^k$, alors: + + $$\gamma(p)=p=\Theta(\log_b({n_p}))$$ + + Ainsi: + + $$\underline{T(n)}=\Theta({n_p}^{\log_b(a)})+\Theta(n_p^k\log_b({n_p}))=\underline{\Theta(n_p^k\log_b({n_p}))}$$ + + +$\textit{Situation }n\in N:$ +--------------------------------- + +Le théorème étant démontré pour les termes d'une suite géométrique, il ne suffit plus qu'à montrer que le résultat reste vrai lorsque n est quelconque. Pour se faire, il suffit en réalité d'encadre l'entier n antre deux termes consécutifs de la suite géométrique $(n_0b^p)_{p\ge 0}$. + +Soit donc $n\ge n_0$. Il existe $p\in N$ tel que $n_0b^p\le n< n_0b^{p+1}$. Soit un tel $p$. Puisque $T$ est croissante à partir d'un certain rang, alors pour $n$ assez grand: + + $$T(n_0b^p)\le T(n)< T(n_0b^{p+1})$$ + +Or, si $T(n_p)=\Theta(f(n_p))$, alors il existe $k_1, k_2>0$ tels qu'à partir d'un certain rang: + $$k_1f(n_p) \le T(n_p)\le T(n)< T(n_{p+1})\le k_2f(n_p)$$ +Ce qui nous permet d'en déduire directement $T(n)=\Theta(f(n_p))=\Theta(f(n))$ + +Cela conclue la démonstration du théorème. + + +Intuition +================== + +Par l'apparition de la suite $(n_0b^p)_{p\ge0}$ et la comparaison de $n$ avec les termes de cette suite, nous nous rendons compte que la démonstration se base en partie sur l'ordre de grandeur de $n$ dans la base $b$. Ainsi, lors de l'étude des deux termes $aT(\frac nb)$ et $cn^k$, il s'agit de savoir quel terme "prédomine" lorsque l'on considère la base $b$. Or, en calculant $T(bn)$ (simplement la puissance suivante en base $b$), nous nous rendons compte que les deux termes deviennent $aT(n)$ et $cb^kn^k$. De ce fait, il est maintenant visible que les valeurs déterminante pour savoir quel terme "prédomine" sont $a$ et $b^k$, expliquant ainsi les disjonctions de cas: + +- si $a< b^k$, alors le terme "prédominant" est $cn^k$, nous donnant directement $\Theta(n^k)$ +- si $a>b^k$, alors le sencond prédomine, nous laissant ainsi $aT(\frac nb)$. Ici, nous pouvons facilement montrer que $T(n_p)=a^pn_0$ et $a^p=\Theta(n^{\log_b(a)})$ +- si $a=b^k$, ce cas est plus délicat, car aucun des terme ne "prédomine" l'autre. Nous somme alors dans une situation semblable au traitement d'un arbre b-naire équilibré. Ainsi, la compléxité est en $n\log(n)$. En considérant ainsi la puissance $n^k$ et la base $b$, nous obtenons $\Theta(n^k\log_b(n))$ + + +Exemple +===================== + +Considérons l'algorithme vu en in troduction. Nous savons que sa compléxité en temps vérifie l'égalité suivante: + $$T(n)=2T(\frac n2)+O(n)$$ +Nous avons alors: + - a = 2 + - b = 2 + - k = 1 +Et donc $2=2^1$, nous donnant comme complexité $T(n)=O(n\log_2(n))$. Il s'agit de la complexité optimale pour un algorithme de trie. + + +Exercice +===================== + +Tri-fusion +--------- +Nous considérons une modification de l'algorithme de tri fusion: plutôt que de diviser la liste en deux, nous la disivons dorénavant en trois. Quel est la nouvelle complexité de l'algorithme ? + +m-fusion +---------- +Même question pour une subdivision de la liste en m sous-liste. Quels sont les avantages et inconvénients d'un tel algorithme ? + +Strassen +----------- +Nous considérons maintenant la multipliation de matrice. + +- Rappeller la compléxité d'un produit de matrice classique. +- Démontrer la compléxité d'un produit de matrice suivant l'algorithme de Strassen + + + + + + + + + + diff --git a/cours/ex1.pdf b/cours/ex1.pdf new file mode 100644 index 0000000..ddc1099 Binary files /dev/null and b/cours/ex1.pdf differ diff --git a/cours/index.html b/cours/index.html new file mode 100644 index 0000000..cd1e24c --- /dev/null +++ b/cours/index.html @@ -0,0 +1,16 @@ + + +**Répertoire des développements** + +Développements +===== + +Master Theorem

+Glushkov déterministe

+Coke-Younger-Kasami

+File avec 6 piles

+ + + + + diff --git a/croco.html b/croco.html new file mode 100644 index 0000000..0592e1a --- /dev/null +++ b/croco.html @@ -0,0 +1,69 @@ + + + + EasterEgg 4 + + + + + +
+ +
+ +
+

+ <=🐊 + Crocoloop + 🐊=> +

+
+ + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..4e91310 --- /dev/null +++ b/index.html @@ -0,0 +1,141 @@ + + + + + + + + + + + + + Decosse Adrien + + + +
+
+ +

Contact

+ +
+ +
+ +

Présentation

+ +

Cursus scolaire

+ + + + + +

Expériences

+ + + + + +

Intérêts culturels

+ + + +
+
+ + + + + + diff --git a/index_eng.html b/index_eng.html new file mode 100644 index 0000000..479d329 --- /dev/null +++ b/index_eng.html @@ -0,0 +1,130 @@ + + + + + + + + + + Decosse Adrien + + + +
+
+ +

Contact

+ +
+ +
+ +

Presentation

+ +

School Curriculum

+ + + + + +

Experiences

+ + + + + +

Cultural interests

+ + + +
+
+ + + + + + diff --git a/riichi b/riichi new file mode 160000 index 0000000..8900360 --- /dev/null +++ b/riichi @@ -0,0 +1 @@ +Subproject commit 890036094aa5cdb3efbd554c3249b2d19d04f6e9 diff --git a/save/Yakus.pdf b/save/Yakus.pdf new file mode 100644 index 0000000..3812cb1 Binary files /dev/null and b/save/Yakus.pdf differ diff --git a/save/chii.png b/save/chii.png new file mode 100644 index 0000000..92e864e Binary files /dev/null and b/save/chii.png differ diff --git a/save/data/chii.gif b/save/data/chii.gif new file mode 100644 index 0000000..ca6eb62 Binary files /dev/null and b/save/data/chii.gif differ diff --git a/save/data/example.gif b/save/data/example.gif new file mode 100644 index 0000000..4188d9b Binary files /dev/null and b/save/data/example.gif differ diff --git a/save/data/example.png b/save/data/example.png new file mode 100644 index 0000000..3d1bef5 Binary files /dev/null and b/save/data/example.png differ diff --git a/save/data/kan.gif b/save/data/kan.gif new file mode 100644 index 0000000..7371bb9 Binary files /dev/null and b/save/data/kan.gif differ diff --git a/save/data/main.png b/save/data/main.png new file mode 100644 index 0000000..48ad6a3 Binary files /dev/null and b/save/data/main.png differ diff --git a/save/data/pon.gif b/save/data/pon.gif new file mode 100644 index 0000000..d20c4c4 Binary files /dev/null and b/save/data/pon.gif differ diff --git a/save/data/tenpai.png b/save/data/tenpai.png new file mode 100644 index 0000000..3b7ee06 Binary files /dev/null and b/save/data/tenpai.png differ diff --git a/save/data/tenpai2.png b/save/data/tenpai2.png new file mode 100644 index 0000000..ca55e88 Binary files /dev/null and b/save/data/tenpai2.png differ diff --git a/save/data/tenpai3.png b/save/data/tenpai3.png new file mode 100644 index 0000000..efc1276 Binary files /dev/null and b/save/data/tenpai3.png differ diff --git a/save/dora.png b/save/dora.png new file mode 100644 index 0000000..c0bc36d Binary files /dev/null and b/save/dora.png differ diff --git a/save/famille.png b/save/famille.png new file mode 100644 index 0000000..6e519e2 Binary files /dev/null and b/save/famille.png differ diff --git a/save/honneur.png b/save/honneur.png new file mode 100644 index 0000000..62e1daf Binary files /dev/null and b/save/honneur.png differ diff --git a/save/index.html b/save/index.html new file mode 100644 index 0000000..7340bb4 --- /dev/null +++ b/save/index.html @@ -0,0 +1,606 @@ + + + +# Sources + +Toutes les images utilisées ici ont été réalisées par [Axelle Geyre](https://axellegeyre.com/), et sont disponible directement sur [https://zes.sx/riichi](https://zes.sx/riichi) mettant à disposition des fiches récapitulatives: +- [d'initiation](https://zes.sx/riichi/doc/initiation-v1.2-fr.pdf) +- [du calcul du score](https://zes.sx/riichi/doc/score-v1.1-fr.pdf) +- [des différents Yakus](https://zes.sx/riichi/doc/yakus-v1.5-fr.pdf) + +# Présentation + +Le riichi mahjong est une variante japonaise du mahjong. +Il s'agit d'un jeu qui se joue principalement à 4, mais il en existe des variantes pour 3 ou 5 joueurs. +Cependant, cela sera seulement discuté en ouverture. +Le principe du jeu peut être rapproché au Poker, Rummikube ou même le Tarot dans sa manière de jouer. + +# Mode de jeu + +Chacun des 4 joueurs à un rôle particulier en fonction de sa place autour de la table: un vent lui est attribué. + +Placement des vents (Optionnel) +-------- +Les 4 joueurs, formant un carré, se voient attribués une direction parmi *Nord*, *Sud*, *Est* et *Ouest*. +Le *Nord* et le *Sud* se font face tout comme l'*Est* et l'*Ouest*. + +!!! warning + Attention cependant: il faut imaginer le jeu comme étant une carte céleste. + De ce fait, le joueur au *Sud* à l'*Est* à sa gauche, et l'*Ouest* à sa droite. + +**************** +* +* Nord +* | +* Est--+--Ouest +* | +* Sud +* +**************** +$$\textit{Distribution des vents en fonction du placement des joueurs, vu de dessus}$$ + +Fonctionnement d'une manche +---------- +À chaque manche, chacun se voit distribuer 13 tuiles: cela constitue sa main (il est le seul à la connaître). +Chaque joueur joue dans l'ordre anti-horaire, en commençant par l'*est* (le joueur dont le vent qui lui est attribué est l'*est*). +Pendant le tour d'un joueur, celui-ci commence par récupérer une nouvelle tuile (soit dans la pioche, soit autrement), puis en défausse une devant lui. +De ce fait, un joueur a toujours une main représentée par 13 tuiles. + +![Exemple de partie sur mahjongo.com](data/example.gif) + +La fin d'une manche se termine lorsqu'un joueur gagne ou qu'il y a égalité. +Les perdants donnent alors un certain nombre de leur point aux gagnants. + +Fonctionement d'une partie +-------- +Une partie peut se déroule canoniquement en 8 manches: 4 manches d'*Est* et 4 manches de *Sud*. +Cependant, nous considérerons pour la suite uniquement la variante à 4 manches, dans la mesure où cela n'est pas nécessaire à la compréhension du jeu. +En revanche, cela sera expliqué en ouverture. +À la fin de chacune d'elles, les vents tournent: +- l'*Est* devient le *Nord* +- le *Nord* devient l'*Ouest* +- l'*Ouest* devient le *Sud* +- le *Sud* devient l'*Est* +Dit autrement, les vents tournent dans le sens antihoraire. +Lorsque chaque joueur à fini sa manche en tant qu'*Est*, la partie s'arrête. + +!!! Warning + Il existe cependant une exeption: si à la fin d'une manche, le joueur à l'*Est* gagne, il y a une répétition sans que les vents ne tournent. + Ainsi, il est tout à fait possible d'envisager une partie en 4 manches qui se termine en réalité après une dizaine de manche. + +Le role de l'est +------- +Le joueur à l'*Est* a un rôle particulier. +En plus d'être toujours le premier à commencer et de recommencer une manche en cas de victoire, il a en plus deux autres particularités: +- Son vent (*Est*) est le vent dominant (cf yakus) +- Il gagne plus de points, mais en perd également plus + +!!! Tip + Durant une partie physique, l'*Est* décide également où commence la pioche avec des dés, et est le premier à piocher sa main initiale. + +Tuiles +========= +L'ensemble des tuiles peut être divisé en deux: +- Les familles, elles-mêmes divisées en trois familles: + - Les *Caractères* (ou *Man*) + - Les *Bambous* (ou *Sou*) + - Les *Ronds* (ou *Pin*) +![](famille.png) + +- Les honneurs, eux-mêmes divisés en deux groupes: + - Les *Vents* + - Les *Dragons* +![](honneur.png) + +!!! Note + Chaque tuile est présente en exactement 4 exemplaires. Ainsi, il y a un total de 136 tuiles dans le jeu et initialement 70 tuiles dans la pioche. + +Main valide +========= +Maintenant que l'on sait **comment** jouer, il est nécessaire de savoir **quoi** jouer. +En particulier, dans le but de gagner, il faut que la main respecte deux conditions en même temps: +- Elle doit être valide +- Elle doit être gagnante +Dans cette partie, nous discuterons des mains valides. + +Forme classique +-------- +Dans la majorité des cas, une main valide prend toujours la même forme, à deux exceptions près (cf yakus). +Une telle main possède: +- une paire (exactement la même tuile) (appelée *Toitsu*) + ![Exemple de paire](paire.png) +- quatre groupes de trois tuiles +Un groupe de trois tuiles peut être formé de deux manières différentes: + +- En formant une suite de chiffres consécutives (appelée *Chii*) + + ![Exemple de suite](chii.png) + +- En formant un groupe de trois tuiles identiques (exactement la même tuile) (appelée *Pon*) + + ![Exemple de brelan](pon.png) + +!!! + Les *Vents* et les *Dragons* ne peuvent pas former de suite. + +![Exemple de main](data/tenpai3.png) +Danse l'exemple ci-dessus, nous pouvons constater qu'il y a un brelan de *5 de Bambou*, +et une suite de *Pin* (les 7, 8, 9) à droite. +Cela fait donc 2 groupe de trois parmi les 4 qu'il faut former pour avoir une main valide. + +Dans ce même exemple ci-dessus, quelles sont les tuiles qui permettent d'avoir une main valide ? + +
+ Cliquer pour voir la réponse +Actuellement, les groupes formés sont: +- Une suite de *Caractères* (7, 8, 9 à gauche) +- Un brelan de *5 de Bambou* +- Une suite de *Pin* (7, 8, 9 à droite) + +Et la paire: +- Une paire de *1 de Caractère* (sur la gauche) + +Il ne manque ainsi qu'un seul groupe de trois pour avoir une main valide. +Avoir un *1 de Bambou* ou un *4 de Bambou* permet de compléter la suite au milieu (1, 2, 3 ou 2, 3, 4), et donc le quatrième bloc, +et donc d'avoir une main valide. + +
+ +Tenpai +------- +Si vous avez bien fait attention, vous remarquerez qu'une main valide comporte en tout 14 tuiles, alors qu'une main en comporte habituellement 13. +Cela signifie qu'une main est valide uniquement lors de la récupération d'une tuile (en piochant par exemple, une main comporte 14 tuiles avant défausse). +Si un joueur est à une seule tuile d'avoir une main valide, on dit alors qu'il est en *Tenpai*: une tuile le sépare de la victoire. + +![Exemple de main en Tenpai](data/tenpai.png) + +Dans l'exemple ci-dessus, seule le *6 de Bambou* permet de rendre la main valide. + +![Exemple de main en Tenpai](data/tenpai2.png) + +Dans cet exemple très similaire, pourquoi la main est-elle toujours en *Tenpai* ? + +
+ Cliquer pour voir la réponse +Sur toute la partie gauche (les *Caractères*), nous pouvons discerner: +- Une suite (1, 2, 3) +- Une suite (4, 5, 6) +- Une paire (7, 7) + +Donc parmi les bambous, il faut trouver 2 blocs de trois. +Le tri peut être piégeux, car nous pouvons former: +- une suite (7, 8, 9) +- un début de suite (7, 8) + +Ainsi, le *6 de Bambou* et le *9 de Bambou* permettent tout deux de compléter cette suite et d'avoir une main valide. +Même si cela semble moins évident, la main est bien en *Tenpai*. + +!!! Tip + Ici, nous nous rendons compte même que le nombre de tuiles permettant de rendre la main valide à augmenter. + Cette situation est donc plus avantageuse que la précédente. +
+ +Égalités +------- +Lorsqu'il n'y a plus de pioche (ce qui peut arriver), la manche s'arrête. +Chaque joueur, en commençant par l'*Est* annonce s'il est en *Tenpai* - en dévoilant son jeu - ou non - en couchant son jeu. +Les joueurs en situation de *Tenpai* sont gagnants, les autres perdants. + +!!! Note + Dans cette situation, tout ce qui importe est de savoir si la main est sur le point d'être valide ou non. + Il n'y a aucune notion de main gagnante, et il peut donc être stratégique en fin de partie de chercher à se mettre en *Tenpai*, sachant qu'il n'est pas possible de gagner autrement que part l'égalité. + +Main gagnante +========= +Comme précisé précédemment, avoir une main valide ne suffit pas: il faut également une main gagnante qui respecte au moins une condition de victoire. +Dit autrement, en plus de la forme, la main doit se démarquer. + +Les *Yakus* sont ces conditions de victoires. Il s'agit de particularités qui rendent une main unique et spéciale. +Par exemple, avoir un brelan de *Dragon* est un *Yaku*. +N'avoir que des *Bambous* dans son jeu en est un autre. + +Les *Yakus* les plus communs sont présentés et expliqués plus en détails un peu plus loin. + +Chii/Pon +========== +Lorsqu'un joueur doit jouer, il doit commencer par récupérer une tuile, comme déjà expliqué. +Cependant, il n'est pas obligé de récupérer une tuile dans la pioche. + +Chii +-------- +Si, lors de son tour, la dernière tuile défaussée lui permet de compléter une suite (*Chii*), il peut alors la récupérer à la place de la pioche. +Cela se fait alors en trois étapes: +- Le joueur annonce "Chii" +- Il dévoile sa suite et la met de côté, face découverte +- Il défausse une tuile + +!!! Tip + En anglais, le terme est "Chow". + +![Exemple de *Chii* sur Mahjongo.com](data/chii.gif) + +Dans l'exemple ci-dessus, le joueur qui précède a défaussé un *8 de Caractère*, +qui est la tuile permettant de compléter une suite. +Il est alors possible dans ce cas d'appeler un "Chii". + +Pon +------ +Si, lors d'un défausse quelconque, une tuile lui permet de compléter un brelan (*Pon*), il peut alors faire de même: +- Le joueur annonce "Pon" +- Il dévoile son brelan et la met de côté, face découverte +- Il défausse une tuile + +!!! Warning + Le joueur annonçant un *Pon* peut le faire derrière n'importe quelle défausse. + Cela signifie que le jeu reprend par sa défausse, et le tour de certains joueur peuvent être sauté. + +![Exemple de *Pon* sur mahjongo.com](data/pon.gif) + +Dans l'exemple ci-dessus, le joueur à droite à défaussé un *Dragon rouge*. +Puisque cela permet de compléter un groupe de trois (cici un brelan), alors un *Pon* est possible. +Dans ce cas, la tuile est récupérer, et le tour du joueur en face et du joueur à gauche sont sautés. + +!!! Tip + Dans une partie physique, le joueur annonçant "Chii" ou "Pon" dévoile ses deux tuiles, défausse, puis seulement récupère la tuile. + Cela permet de fluidifier la partie. + +Ron +------ +Lorsqu'un joueur est en *Tenpai* est a ou attend au moins un *Yaku*, il est alors en attente de la bonne tuile pour gagner. +Dans ce cas, à l'instant où elle est défaussée, le joueur en situation de *Tenpai* peut s'écrier "Ron !". Dès lors, il dévoile son jeu et gagne la manche. Le joueur ayant défaussé la tuile gagnante est lui en revanche le grand perdant. +Il faut donc faire très attention de ne pas défausser la tuile gagnante de quelqu'un d'autre, au risque d'en payer le prix. +Les autres joueurs en revanche sont neutre dans cette situation et ne gagnent ni perdent de points. + +!!! Note + Si une tuile permet à plusieurs personnes de gagner, alors le joueur ayant défaussé leur tuile gagnante se prend une double ou triple pénalité, car il doit distribuer ses points à tous ceux qui ont fait un "Ron" individuellement. + +!!! Note + S'il y a des conflits - un "Chii" en même temps qu'un "Pon", par exemple - , la priorité est au "Ron", puis au "Pon" puis au "Chii". + Remarque: il ne peut y avoir plusieurs "Ron" ou plusieurs "Chii" en même temps. + +Tsumo +----- +Si un joueur récupère une tuile gagnante en piochant, il peut alors annoncer "Tsumo". +Dès lors, il gagne la manche et tous les autres joueurs sont perdants. + +Furiten +--------- +Une des subtilités du jeu est de restreindre les conditions de victoire dans un cas précis: +lorqu'un joueur est en *Tenpai*, et qu'il a défaussé à un moment de la partie une tuile qui le ferait gagner, il est alors en situation de *Furiten*. +Cela signifie qu'il est dans l'interdiction de "Ron". +Cependant, il a toujours la possibilité de faire "Tsumo", même si cela limite fortement ses chances de victoire. + +Main ouverte/fermée +-------- +Une notion très importante du Riichi Mahjong est l'ouverture de la main. +Une main est dite ouverte lorsqu'un joueur a effectué un appel (un "Chii", un "Pon"). +Autrement dit, lorsqu'un joueur à dévoilé une partie de son jeu, sa main est ouverte. +Dans le cas contraire, sa main est fermée. + +L'interrêt d'ouvrir sa main est de pouvoir récupérer des tuiles qui nous arrangent, nous offrant donc plus de facilité pour obtenir une main valide et gagnante. +Cependant, une main fermée étant plus dure à réalisée (car uniquement formée de tuiles piochées) implique plus de points en cas de victoire. + +Ainsi, les joueurs ont tendance à tarder au maximum l'ouverture de leur main. + +!!! Warning + Certaines conditions de victoires (*Yakus*) ne sont réalisables qu'avec une main fermée. + Tous les *Yakus* sont réalisables en main fermée. + +!!! Warning + Lorsque des tuiles sont dévoilées par un "Chii" ou un "Pon" par exemple, elles sont mises de côté, et il n'est alors plus possible de les toucher. + Il est donc nécessaire de mûrement réfléchir à ses appels pour s'éviter de se supprimer des conditions de victoire. + +Kan +===== +Une situation qui a été jusque là tue est le cas où un joueur a ou peut avoir 4 tuiles identiques. +Dans ces cas là, s'il souhaite le traiter comme un carré, a trois possibilités: + +Kan ouvert +-------- +Si le joueur possède un brelan d'une tuile dans son jeu, et que la quatrième est défaussée, il peut annoncer "Kan". +Il récupère alors la tuile et les mets de côté, face découverte. + +Kan fermé +-------- +S'il possède déjà dans son jeu 4 tuiles identiques, il peut, s'il le souhaite, l'annoncer en disant "Kan". +Ses quatres tuiles sont alors mises de côtés avec: +- deux des tuiles face découverte +- deux des tuiles face cachée +Ce procéder permet de rappeler que ce "Kan" est un *Kan fermé*. +Autrement dit, si la main du joueur n'a pas encore été ouverte, on considérera qu'elle est toujours fermée. +Ce *Kan*, venant de sa propre main, ne restreint donc pas les *Yakus* qu'il peut réaliser. + +Pon amélioré +------- +Si un joueur pioche une tuile qu'il a déjà "Pon" dans un précédent tour, il peut alors l'anoncer. +Il prononce donc "Kan", dévoile sa tuile qu'il met face découverte avec les autres. + +!!! Note + Dans tous les cas, un *Kan* est considéré comme un brelan. + Ainsi, annoncer un "Kan" revient au même pour le joueur que d'annoncer un "Pon" + Cela signifie que lorsqu'un joueur annonce un "Kan", il doit piocher de nouveau. + Auquel cas, il lui serait impossible d'avoir une main valide. + +!!!! + L'intérrêt du *Kan* sera discuté un peu plus loin lorsque nous aborderons la notion de *Dora*. + +!!! Tip + En anglais, le terme associé est "Kong" + +![Exemple de *Kan* ouvert sur mahjongo.com](data/kan.gif) +Dans l'exemple ci-dessus, le joueur à gauche a défaussé un *9 de Bambou*. +Puisque cela permet de former un *Kan*, l'appel est possible. +Le reste est alors similaire à un *Pon*. + +Quelques Yakus +=========== +Il est maintenant nécessaire de parler du sujet qui fâche: les *Yakus*. +L'une des compléxités du jeu provient de son nombre de *Yakus*: 39. +Ce nombre peut changer selon les règles: il peut varier s'il l'on considère les règles européenes ou japonaises. +Selon les platformes, les règles peuvent également changer, afin d'assurer par exemple qu'un partie ne puisse pas durer éternellement. + +Cependant, il n'est pas nécessaire de prendre peur. +Dans le cas général, une douzaine de *Yakus* est suffisant pour profiter pleinement du jeu. +Les *Yakus* restant sont soit des *Yakus* extrêmement rares, soit des formes de redondance. + +Yakus communs +------- +Parmis les plus communs, nous pouvons retrouver ceux ci-dessous: + +### *Riichi* +Le *Riichi* est ce qui fait la spécificité de ce jeu (d'où son nom). +Ce *Yaku* peut en revanche être effectué **uniquement en main fermée**. +S'il y a eu un appel, il n'est plus possible de le faire (d'où la prudence lors d'appels). + +Lorsqu'un joueur à une main fermée, et qu'il est en situation de *Tenpai* lors de son tour, il peut annoncer "Richi". +Dès lors, il défausse une tuile, et se met en attente: +sa main ne peut alors plus être changée. +Autrement dit, soit il finit par piocher la tuile qu'il a décidé d'attendre (*Tsumo*), +soit un joueur finit par la défausser (*Ron*). + +!!! Warning + Cela implique que le joueur ne choisit pas sa défausse, puisque ça main ne peut plus changer. + Cela lui rapporte des points en cas de victoire, mais l'expose au risque de défausser la tuile gagnante d'un autre joueur. + +### *Ippatsu* +L'*Ippatsu* est une condition qui se rajoute à la précédente. +Lorsqu'un joueur annonce "Riichi", la prudence est alors de mise pendant un tour: +si d'ici à ce qu'il rejoue, il "Ron" ou "Tsumo" sans interruption - sans "Chii" ni "Pon" -, donc dans la foulée, +cela lui rajoute un *Yaku* supplémentaire. + +### *Vent dominant* +Si un joueur obtient un brelan de vent dominant - en général l'*Est* - , cela lui fait un *Yaku*, que sa main soit ouverte ou non. +Il s'agit d'un *Yaku* facile à faire et assure donc une condition de victoire, mais vaut en contrepartie que peu de point. + +### *Vent du joueur* +Si un joueur obtient un brelan de son propre vent, cela lui fait un *Yaku*, que sa main soit ouverte ou non. +Un brelan de *Nord* assure au joueur *Nord* une condition de victoire, mais n'assure rien du tout au joueur *Sud* par exemple. + +!!! + Si le joueur à l'*Est* fait un brelan de vent d'*Est*, cela lui assure alors deux *Yakus*: + - brelan de vent dominant + - brelan de son propre vent + +### *Tout ordinaire* +Si la main du joueur ne contient aucun terminal (ni de 1, ni de 9) et aucun honneur (ni de vent, ni de dragon), +cela lui ocrtoît un *Yaku*, que sa main soit ouverte ou non. + +### *Tout triple* +Si tous les groupes de trois d'une main sont des brelans, un *Yaku* est alors réalisé, que la main soit ouverte ou non. + +### *Main semi-pure* +Une main est dite *semi-pure* si elle est composée d'honneurs (vent ou dragon) et d'une seule famille. +Dès lors que la main contient deux tuiles de famille différente, elle n'est pas semi-pure. + +### *Main pure* +Une main est par extension *pure* si elle n'est composée que d'une seule famille (donc sans honneur). + +### *Suite pure* +Une suite pure est une suite entière (toutes les tuiles de 1 à 9) de la même famille. + +### *Triple brelan* +Si le joueur possède un brelan du même chiffre dans les trois familles, il réalise alors un *Yaku*, que son jeu soit ouvert ou non. + +### *7 paires* +Les *7 paires* est la première des deux exceptions sur les conditions de validité d'une main. +Comme sont nom l'indique, il s'agit tout simplement d'avoir une main composée uniquement de paire. + +!!! Warning + Il n'est pas possible d'avoir de *Kan* pour ce *Yaku*. + Autrement dit, toutes les paires doivent être distinctes. + +!!! + Puisque l'ouverture d'une main se fait par des appels, + et que les appels impliquent d'avoir un groupe de trois, + alors ce *Yaku* implique d'avoir une main fermée. + +## Autres yakus +Il existe de nombreux autres *Yaku*, cependant, beaucoup sont soit redondants, soit quasiment impossibles à réaliser. +En voici quelques exemples: + +### *Petit vent* +Ce *Yaku* est réalisé lorsque le joueur possède trois brelans de vent et une pair du quatrième. + +### *Grand vent* +La seule différence avec le *Yaku* précédent est que le joueur doit posséder un brelan de tous les vents. + +!!! + La même règle s'applique pour *Petit dragon* et *Grand dragon*. + Cela consitue donc quatre *Yakus* fortement semblables. + +### *Bénédiction du ciel* +Ce *yaku* est réalisé lorsque le joueur *Est* gagne la manche sur sa toute première pioche. + +!!! + Il va sans dire que ce dernier est exceptionnellement rare. + +### *13 orphelins* +Il s'agit de la deuxième exception sur la condition de validité de la main. +Ce *Yaku* est réalisé lorsqu'une main contient un exemplaire de chaque terminaux (1 et 9), de chaque vent et de chaque dragon, sans compter la réalisation d'une paire. + +Une liste exaustive des *Yakus* peut être trouvée [ici](Yakus.pdf). + +Dora +======== +Parlons maintenant d'un sujet croustillant: les *Doras*. +Un dora est une tuile qui, lorsqu'un joueur la possède dans sa main lors de sa victoire, lui ocroît des points supplémentaires. + +Dora +------ +En début de partie , une tuile est découverte (et elle ne bougera pas de son emplacement). +Cette tuile est l'*indicateur de dora*. +Cela signifie que la tuile suivante dans un cycle est un *Dora*. + +![Cycle des tuiles](dora.png) + +Par exemple, si l'indicateur est un **9 de Pin**, la *dora* est alors le **1 de Pin**. +Si l'indicateur est le **Dragon rouge**, alors la *Dora* est alors le **Dragon blanc**. + +!!! + Initialement, il y a 4 *Doras* dans le jeu. + +C'est alors que les *Kans* prennent tout leur intérêt ! +Lorsqu'un joueur annonce un *Kan*, un nouvel indicateur est dévoilé. +Il ne peut cependant y avoir qu'au plus 4 *Indicateurs de Dora* au total. + +!!! + Si deux *Indicateurs* sont identiques, alors la *Dora* correspondante compte pour double. + +Ura dora +----- +Lorsqu'un joueur gagne en ayant fait "Riichi", alors pour chaque *Indicateur de Dora*, un *Indicateur de Ura Dora* est dévoilé. +Ces derniers ont exactement les mêmes rôles que les *Doras*, mais ne sont dévoilés qu'une fois la victoire annoncée, et ne bénéficient qu'aux joueurs ayant gagné avec un "Riichi". + +Dora rouge +----- +Les *Doras rouges* ou les *Cinq rouges* (appelés *Red Five*) sont des tuiles qui se démarquent par leur couleur rouge. +Il y en a exactement trois: +- Un cinq de *Pin* (*Rond*) +- Un cinq de *Man* (*Caractère*) +- Un cinq de *Sou* (*Bambou*) + +!!! + Cela ne change en rien le nombre de cinq dans le jeu: + il y a par exemple 3 cinq de *Pin* normaux plus un cinq de *Pin* rouge. + +Comme son nom l'indique, ils agissent exactement de la même manière que les *Doras*. + +!!! Tip + Dans les règles européennes, les *Doras rouges* ne sont pas présents. + Cependant, il est tout de même courant de jouer avec pour des parties plus intenses. + +Introduction aux points +========== +Le système de point est sans aucun doute la partie la plus complexe du jeu pour les nouveaux joueurs. +Cependant, il est nécessaire de comprendre comment fonctionne ce système afin d'évaluer une main. + +En particulier, si un joueur à une main avec énormément de point, il peut vouloir prendre le risque de rester en *Tenpai*, même si cela signifie défausser des tuiles potentiellement gagnantes pour autre joueur. +À l'inverse, si l'ouverture d'un joueur semble montrer une très grosse main, les joueurs avec une très petite main peuvent décider d'abandonner l'idée de victoire pour éviter de perdre la moitié de leurs points. + +Il est donc temps d'introduire deux notions: les *Fus* et les *Hans*. + +## *Fu* +Les *Fu* sont des points accumulés lorsqu'une main contient des configurations intéressantes comme des brelans, des brelans fermés, des dragons etc... +Ces derniers sont arrondis à la dizaine supérieure. +Ainsi, avoir 31 ou 40 *Fus* ne change en rien le score final. + +## *Han* +Les *Hans* en revanche sont notamment accumulés par les *Yakus*. +Ainsi, un triplet de dragon correspond à *1 Han*, alors q'une main pure fermée correspond à *6 Hans*, car beaucoup plus dure à réaliser. +Chaque *Dora* fournie également *1 Han*. + +Le calcul des points se fait via la formule: + +$$Fu*2^{Han+2}$$ + +En réalité, d'autres paramètres entrent en jeu: est-ce que le joueur qui gagne ou perd est *Est* ou non par exemple. +Cependant, cela permet de bien comprendre l'importance des *Hans*. +Alors que gagner des *Fus* supplémentaires peut être fastidieux pour ne pas ou quasiment pas changer le score, avoir un *Han* supplémentaire permet de le doubler. + +Distribution des points +----- +Au début, chaque joueur commence avec 30 000 ou 25 000 points selon les règles. +Tout au long de la partie, la somme des points de tous les joueurs reste constant. +Autrement dit, le calcul des points permet de déterminer combien doivent les perdants aux gagnants, sachant que: +- l'*Est* perd et gagne plus de points +- le *Tsumo* fait plus gagner de points que le *Ron* +- chaque répétition (lorsque l'*Est* gagne, les mises augmentent + +!!! + En cas d'égalité (lorsque la pioche est vide), un total de 3000 points est échangé, indépendament des mains des gagnants. + Ainsi, il y a quatre situations: + - Il n'y a qu'un seul gagnant -> tout le monde lui donne 1000 points + - Il y a deux gagnants -> chaque gagnants récupère 1500 points et chaque perdant donne 1500 points + - Il y a trois gagnants -> chaque gagnant récupère 1000 points du perdant + - Tout le monde est gagnant ou perdant -> il ne se passe rien. + +Introduction aux stratégies +============ +Le Riichi Mahjong possède des stratégies parfois très poussées: il faut savoir quelles tuiles défausser, savoir quand prendre des risques ou non, savoir lire la défausse des autres joueurs etc... +Heureusement, le [Riichi Book 1](https://dainachiba.github.io/RiichiBooks/) est une très bonne introduction à partir du chapitre 3 pour progresser rapidement au début et comprendre le jeu plus en profondeur. + +Variantes et extensions +========== +8 manches +------ +Originalement, le Riichi Mahjong est en 8 manches: 4 manches *Est* suivie de 4 manches *Sud*. +Ce que cela signifie est que lorsque chaque joueur à fini ses manches en tant qu'*Est*, quatre nouvelles manches sont relancées presque exactement comme en début de partie, à deux différence près: +- Les points ne sont bien évidemment pas réinitialisés +- Le vent dominant est dorénavant le *Sud*. +Tout le reste se déroule exactement comme une partie à 4 manches. + +!!! Warning + Seul le vent dominant change. Cela implique que: + - un brelan de vent d'*Est* n'est un *Yaku* que pour le joueur à l'*Est* + - un brelan de vent de *Sud* est un *Yaku* pour tous les joueurs + - le système de points reste inchangé: l'asymétrie porte toujours sur le joueur *Est* + +!!! Tip + Une partie en 4 manche d'*Est* s'appelle *Tonpusen* signifiant "manche d'Est uniquement". + + Une partie en 8 manche s'appelle *Hanchan* signifiant... "demi partie". + Cela vient du fait que la variante chinoise se déroule en 16 manches ! + (Heureusement, il n'y a pas de répétition dans les règles compétitives) + +5 joueurs +------ +Une variante possible pour jouer à 5 est de procéder à une tournante. À chaque manche (modulo répétition), un des joueurs ne joue pas. +Pendant ce temps, les quatre autres jouent leur manche comme si de rien n'étant. +Afin d'alterner, le joueur finissant sa manche d'*Est* cède sa place à celui qui ne jouait pas. + +Ainsi fait, modulo les répétitions, chaque joueur reste inactif pendant une manche, a été au moins une fois *Est*, et ne bougera plus une fois qu'il se sera rassie. + +!!! + Notons donc que le joueur qui s'assoie jouera donc en temps que *Nord*. + Ainsi, même la rotation des vents est respectée du point de vue des joueurs. + +3 joueurs +------ +Jouer à trois joueurs relève cependant d'un défi supplémentaire. + +Pour commencer, le nombre de tuiles est de fait diminué: **toutes les tuiles de 2 à 8 de *Pin* sont retirées**. +Seuls les terminaux sont gardés, car cela permet d'éviter de tuer des *Yakus* tels les *13 orphelins*. + +Ensuite, **il n'y a pas de joueur *Nord***. + +**Il n'y a pas non plus de *Chii* possible**. + +Enfin, **la tuile *Nord* joue le rôle de *Dora***. +Elle peut être défaussée, gardée ou bien mise de côté. +Dans ce dernier cas, le joueur annonce "Kita", met le vent du *Nord* de côté et repioche. + +Tout le reste est identique à une partie à quatre joueurs. + +# Liens pour s'entrainer +Pour s'entrainer, rien de mieux que la pratique. +Le mieux est évidemment de jouer en présentiel. +Heureusement, de nombreux sites propopsent du mahjong en ligne, même si dans la plupart des cas, ils sont soit payants, soit non traduits. +Nous pouvons tout de même en citer quelques-uns: +- [Tenhou](https://tenhou.net/) est très populaire, mais non traduit. Les chapitres 1 et 2 du [Riichi Book 1](https://dainachiba.github.io/RiichiBooks/) expliquent tout de même comment procéder pour se débrouiller sur ce site. +- [Mahjongo](https://mahjongo.com/riichi) est plus simple, mais gratuit et traduit en anglais, parfait pour prendre en main le jeu, voir jouer contre de vrais joueurs. +- [Scoringtrainer](https://scoringtrainer.konbamwa.net/) permet de s'entrainer à calculer les points. La formule complète y est également détaillée et expliquée. +- [Mahjong Efficiency](https://euophrys.itch.io/mahjong-efficiency-trainer) permet de s'entrainer à se mettre en *Tenpai* le plus rapidement possible, utile pour certaine situation et mieux reconnaitre les attentes. + + + + diff --git a/save/paire.png b/save/paire.png new file mode 100644 index 0000000..66c8703 Binary files /dev/null and b/save/paire.png differ diff --git a/save/pon.png b/save/pon.png new file mode 100644 index 0000000..d10747c Binary files /dev/null and b/save/pon.png differ diff --git a/style.css b/style.css new file mode 100644 index 0000000..61c64fb --- /dev/null +++ b/style.css @@ -0,0 +1,44 @@ +* { + box-sizing: border-box; +} + +#pp { + border-radius: 50%; + width: 320px; + height: 400px; +} + +.row { + display: flex; + flex-wrap: wrap; +} + +.side { + flex: 20%; + padding: 20px; +} + +.main { + flex: 70%; + padding: 20px; + background: whitesmoke; +} + +.flag { + width: 80px; + float: right; +} + +li{ + margin: 10px 0; +} + +body { + font-family: 'EB Garamond', serif; + font-size: 18px; + background: royalblue; +} + +#easteregg { + color: black; +} diff --git a/syllabus.html b/syllabus.html new file mode 100644 index 0000000..bf96be7 --- /dev/null +++ b/syllabus.html @@ -0,0 +1,107 @@ + + + + + + Syllabus + + + +
+ + + +