diff --git a/.DS_Store b/.DS_Store index 4078b56..d1fe5b3 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/2048.html b/2048.html new file mode 100644 index 0000000..bb67f4e --- /dev/null +++ b/2048.html @@ -0,0 +1,25 @@ + + + + + + + 2048 + + + + +
+
+

2048

+
+ Score: 0 +
+ +
+
+
+ + + + \ No newline at end of file diff --git a/CrossyRoad.mp4 b/CrossyRoad.mp4 new file mode 100644 index 0000000..3a6b18b Binary files /dev/null and b/CrossyRoad.mp4 differ diff --git a/contact.html b/contact.html index 6d54d80..5db3cd4 100644 --- a/contact.html +++ b/contact.html @@ -23,13 +23,12 @@
-

Contactez-moi

+ Contactez-moi



-
diff --git a/images/.DS_Store b/images/.DS_Store new file mode 100644 index 0000000..26a9f2a Binary files /dev/null and b/images/.DS_Store differ diff --git a/images/dino-sprite.png b/images/dino-sprite.png new file mode 100644 index 0000000..9a1bc3c Binary files /dev/null and b/images/dino-sprite.png differ diff --git a/mail.png b/mail.png new file mode 100644 index 0000000..a05a6dd Binary files /dev/null and b/mail.png differ diff --git a/projets.html b/projets.html index 6dfa51d..898a172 100644 --- a/projets.html +++ b/projets.html @@ -23,7 +23,7 @@
-

Mes Projets

+

Mes projets

@@ -46,6 +46,40 @@

Document :

+ +
+
+

Développement d'un jeu de société - Qawale

+

Contexte : Projet personnel de développement d'application mobile cross-platform.

+

Objectifs du projet : Recréer le jeu de société Qawale en version numérique en utilisant .NET + MAUI et C#, + permettant de jouer sur différentes plateformes (iOS, Android, Windows).

+

Travail réalisé : J'ai développé une application complète incluant : +

    +
  • Une interface utilisateur intuitive et responsive
  • +
  • Un mode deux joueurs local
  • +
  • Un système de validation des coups selon les règles officielles
  • +
  • Une intelligence artificielle pour jouer contre l'ordinateur
  • +
  • Un système de sauvegarde des parties
  • +
+

+

Technologies utilisées : +

    +
  • .NET MAUI pour le développement cross-platform
  • +
  • C# pour la logique du jeu
  • +
  • XAML pour l'interface utilisateur
  • +
+

+

Résultats du projet : Ce projet m'a permis d'approfondir mes compétences en développement + avec .NET MAUI, en programmation orientée objet avec C#, et en conception d'interfaces utilisateur. + J'ai également acquis de l'expérience dans la gestion de la logique de jeu complexe et dans + l'implémentation d'IA basiques.

+

Code source : Voir le dépôt Git

+
+
+

Création d’un Portfolio Web

@@ -103,8 +137,10 @@ sur internet afin de comprendre et réaliser les tâches demandées. Je me suis également grandement familiarisé avec l'utilisation du terminal et ses lignes de commandes. Enfin, cela m'a permis d'en apprendre plus sur le fonctionnement d'un espace disque et d'un système d'exploitation.

-

Document : -

+

Résultats du projet : J'ai réussi à installer une distribution Debian, à la configurer avec + les paquets demandés, à partitionner les disques et à mettre en place les Backports ainsi que + FlatPak. J'ai également appris à utiliser le terminal de manière efficace, ce qui m'a permis de + gagner en autonomie dans la gestion de systèmes Linux.

@@ -123,7 +159,7 @@

Résultats du projet : Ce projet m'a permis de me familiariser avec le langage C et m'a appris à gérer des tableaux de manière efficace (Chargement, Recherche, Tri, Affichage). Cela m'a également permis d'utiliser différents types de fichiers afin d'effectuer des sauvegardes.

-

Document :

+
@@ -138,11 +174,11 @@ particulier au respect de l'utilisation des différentes structures (Pile, File, Liste). Ce projet a renforcé ma compréhension des structures de données et m'a introduit à la notion de complexité et à l'utilisation de fichiers binaires pour effectuer des sauvegardes.

-

Document : +

Sujet du projet :

- +

Mes projets personnels

@@ -154,13 +190,54 @@ algorithme en C# capable d'interagir avec le jeu afin qu'il soit imbattable. Une fois le programme fonctionnel, je souhaite entraîner une IA capable de battre le jeu et comparer les deux solutions pour analyser celle qui obtient le meilleur score.

-

Résultats du projet : Ce projet m'a permis de m'autoformer sur Unity et C#, de travailler sur - un projet complexe, et de m'introduire aux notions d'IA et de robotique.

-

Document :

+

Résultats du projet : Ce projet me permettra de m'autoformer sur Unity et C#, de travailler + sur + un projet complexe, et sera une bonne introduction aux notions d'IA et de robotique.

+ +
+
+ +
+
+

Vidéo d'inspiration : Voir sur YouTube

+ + +
+
+

Développement du jeu 2048

+

Contexte : Projet personnel réalisé pour approfondir mes compétences en développement web.

+

Objectifs du projet : Recréer le célèbre jeu 2048 en utilisant HTML, CSS et JavaScript, tout + en + développant une interface utilisateur moderne et responsive.

+

Travail réalisé : J'ai développé une version complète du jeu 2048 en intégrant plusieurs + fonctionnalités : +

+

+

Résultats du projet : Ce projet m'a permis de renforcer mes compétences en JavaScript, + notamment + dans la manipulation de tableaux 2D, la gestion des événements clavier, et la création d'animations + CSS. + J'ai également approfondi en gestion de + la + logique de jeu.

+

Jouer : Jouer à 2048

+
+
+ + + + diff --git a/script_2048.js b/script_2048.js new file mode 100644 index 0000000..340afa1 --- /dev/null +++ b/script_2048.js @@ -0,0 +1,223 @@ +class Game2048 { + constructor() { + this.grid = Array(4).fill().map(() => Array(4).fill(0)); + this.score = 0; + this.gridElement = document.querySelector('.grid'); + this.scoreElement = document.getElementById('score'); + this.setupGrid(); + this.addNumber(); + this.addNumber(); + this.setupEventListeners(); + this.showInstructions(); // Ajout de l'appel à la nouvelle méthode + } + + setupGrid() { + for (let i = 0; i < 16; i++) { + const cell = document.createElement('div'); + cell.classList.add('cell'); + this.gridElement.appendChild(cell); + } + this.updateGrid(); + } + + updateGrid() { + const cells = this.gridElement.getElementsByClassName('cell'); + for (let i = 0; i < 4; i++) { + for (let j = 0; j < 4; j++) { + const value = this.grid[i][j]; + const cell = cells[i * 4 + j]; + const previousValue = parseInt(cell.getAttribute('data-value')) || 0; + + cell.textContent = value || ''; + cell.setAttribute('data-value', value); + + // Ajouter les animations + if (value && value !== previousValue) { + // Animation de fusion si la valeur a doublé + if (previousValue && value === previousValue * 2) { + cell.classList.add('merge'); + } + // Animation de glissement selon la direction + else if (value) { + const lastMove = this.lastMoveDirection; + if (lastMove) { + cell.classList.add(`slide-${lastMove}`); + } + } + } + + // Retirer les classes d'animation après leur exécution + cell.addEventListener('animationend', () => { + cell.classList.remove('merge', 'slide-left', 'slide-right', 'slide-up', 'slide-down'); + }); + } + } + this.scoreElement.textContent = this.score; + } + + addNumber() { + const emptyCells = []; + for (let i = 0; i < 4; i++) { + for (let j = 0; j < 4; j++) { + if (this.grid[i][j] === 0) { + emptyCells.push({ x: i, y: j }); + } + } + } + if (emptyCells.length) { + const { x, y } = emptyCells[Math.floor(Math.random() * emptyCells.length)]; + this.grid[x][y] = Math.random() < 0.9 ? 2 : 4; + } + } + + move(direction) { + let moved = false; + const gridCopy = JSON.parse(JSON.stringify(this.grid)); + + // Convertir la direction en format plus simple + this.lastMoveDirection = direction.toLowerCase().replace('arrow', ''); + + switch (direction) { + case 'ArrowLeft': + moved = this.moveLeft(); + break; + case 'ArrowRight': + moved = this.moveRight(); + break; + case 'ArrowUp': + moved = this.moveUp(); + break; + case 'ArrowDown': + moved = this.moveDown(); + break; + } + + if (moved) { + this.addNumber(); + this.updateGrid(); + if (this.isGameOver()) { + alert('Game Over! Score: ' + this.score); + } + } + } + + moveRow(row) { + let arr = row.filter(x => x !== 0); + for (let i = 0; i < arr.length - 1; i++) { + if (arr[i] === arr[i + 1]) { + arr[i] *= 2; + this.score += arr[i]; + arr.splice(i + 1, 1); + } + } + while (arr.length < 4) arr.push(0); + return arr; + } + + moveLeft() { + let moved = false; + for (let i = 0; i < 4; i++) { + const oldRow = [...this.grid[i]]; + const newRow = this.moveRow([...oldRow]); + this.grid[i] = newRow; + if (JSON.stringify(oldRow) !== JSON.stringify(newRow)) moved = true; + } + return moved; + } + + moveRight() { + let moved = false; + for (let i = 0; i < 4; i++) { + const oldRow = [...this.grid[i]]; + const newRow = this.moveRow([...oldRow].reverse()).reverse(); + this.grid[i] = newRow; + if (JSON.stringify(oldRow) !== JSON.stringify(newRow)) moved = true; + } + return moved; + } + + moveUp() { + let moved = false; + for (let j = 0; j < 4; j++) { + const oldColumn = this.grid.map(row => row[j]); + const newColumn = this.moveRow([...oldColumn]); + for (let i = 0; i < 4; i++) { + if (this.grid[i][j] !== newColumn[i]) { + moved = true; + this.grid[i][j] = newColumn[i]; + } + } + } + return moved; + } + + moveDown() { + let moved = false; + for (let j = 0; j < 4; j++) { + const oldColumn = this.grid.map(row => row[j]); + const newColumn = this.moveRow([...oldColumn].reverse()).reverse(); + for (let i = 0; i < 4; i++) { + if (this.grid[i][j] !== newColumn[i]) { + moved = true; + this.grid[i][j] = newColumn[i]; + } + } + } + return moved; + } + + isGameOver() { + // Check for empty cells + for (let i = 0; i < 4; i++) { + for (let j = 0; j < 4; j++) { + if (this.grid[i][j] === 0) return false; + } + } + + // Check for possible merges + for (let i = 0; i < 4; i++) { + for (let j = 0; j < 4; j++) { + const current = this.grid[i][j]; + if ((j < 3 && current === this.grid[i][j + 1]) || + (i < 3 && current === this.grid[i + 1][j])) { + return false; + } + } + } + return true; + } + + setupEventListeners() { + document.addEventListener('keydown', (e) => { + if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) { + e.preventDefault(); + this.move(e.key); + } + }); + + document.getElementById('new-game').addEventListener('click', () => { + this.grid = Array(4).fill().map(() => Array(4).fill(0)); + this.score = 0; + this.addNumber(); + this.addNumber(); + this.updateGrid(); + }); + } + + showInstructions() { + const message = document.createElement('div'); + message.className = 'instruction-message'; + message.textContent = '↑ ↓ ← → Utilisez les flèches du clavier pour jouer'; + document.body.appendChild(message); + + // Supprime le message après 60 secondes + setTimeout(() => { + message.remove(); + }, 60000); + } +} + +// Start the game when the page loads +document.addEventListener('DOMContentLoaded', () => { + new Game2048(); +}); \ No newline at end of file diff --git a/style.css b/style.css index 424bfd3..059ce18 100644 --- a/style.css +++ b/style.css @@ -201,7 +201,20 @@ textarea { text-decoration: none; } +.game-btn { + display: inline-block; + padding: 10px 20px; + background-color: #0d082e; + color: white; + text-decoration: none; + border-radius: 5px; + transition: background-color 0.3s ease; +} +.game-btn:hover { + background-color: #45a049; + transform: scale(1.05); +} .contact-container { @@ -355,4 +368,70 @@ b { padding: 0 1rem; margin: 6rem auto 2rem; } +} + +/* Styles pour la vidéo intégrée */ +.video-container { + position: relative; + width: 100%; + margin: 20px 0; + overflow: hidden; + border-radius: 10px; + cursor: pointer; +} + +.video-wrapper { + position: relative; + padding-bottom: 56.25%; + /* Ratio 16:9 */ + height: 0; + transition: all 0.3s ease; +} + +.video-wrapper.expanded { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 90vw; + height: 90vh; + padding-bottom: 0; + z-index: 1000; +} + +.video-wrapper iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: 10px; +} + +.video-wrapper video { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 10px; +} + +.video-wrapper.expanded video { + object-fit: contain; +} + +.video-btn { + display: inline-block; + padding: 8px 16px; + background-color: #ff0000; + color: white; + text-decoration: none; + border-radius: 5px; + transition: background-color 0.3s ease; +} + +.video-btn:hover { + background-color: #cc0000; } \ No newline at end of file diff --git a/style_2048.css b/style_2048.css new file mode 100644 index 0000000..082151d --- /dev/null +++ b/style_2048.css @@ -0,0 +1,238 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + background-color: #faf8ef; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + font-family: Arial, sans-serif; +} + +.container { + width: 460px; + text-align: center; +} + +.header { + margin-bottom: 20px; +} + +h1 { + font-size: 48px; + color: #776e65; + margin-bottom: 10px; +} + +.score-container { + display: inline-block; + background: #bbada0; + padding: 10px 20px; + border-radius: 3px; + color: white; + font-weight: bold; + margin: 10px; +} + +#new-game { + background: #8f7a66; + color: white; + border: none; + padding: 10px 20px; + border-radius: 3px; + cursor: pointer; +} + +.grid { + background: #bbada0; + padding: 15px; + border-radius: 6px; + display: grid; + grid-template-columns: repeat(4, 100px); + /* Définition explicite de la largeur */ + grid-gap: 15px; + position: relative; + box-shadow: 0 0 0 2px #bbada0; + /* Ajout d'une bordure sous forme d'ombre */ + margin: 0 auto; + /* Centrage horizontal */ + width: fit-content; + /* Ajustement automatique de la largeur */ +} + +.cell { + width: 100px; + height: 100px; + background: rgba(238, 228, 218, 0.35); + border-radius: 3px; + display: flex; + justify-content: center; + align-items: center; + font-size: 36px; + font-weight: bold; + color: #776e65; + transition: all 0.15s ease; + position: relative; +} + +/* Animations de déplacement */ +.cell.merge { + animation: merge 0.2s ease-in-out; +} + +.cell.slide-left { + animation: slideLeft 0.2s ease-in-out; +} + +.cell.slide-right { + animation: slideRight 0.2s ease-in-out; +} + +.cell.slide-up { + animation: slideUp 0.2s ease-in-out; +} + +.cell.slide-down { + animation: slideDown 0.2s ease-in-out; +} + +@keyframes merge { + 0% { + transform: scale(1); + } + + 50% { + transform: scale(1.2); + } + + 100% { + transform: scale(1); + } +} + +@keyframes slideLeft { + 0% { + transform: translateX(100px); + } + + 100% { + transform: translateX(0); + } +} + +@keyframes slideRight { + 0% { + transform: translateX(-100px); + } + + 100% { + transform: translateX(0); + } +} + +@keyframes slideUp { + 0% { + transform: translateY(100px); + } + + 100% { + transform: translateY(0); + } +} + +@keyframes slideDown { + 0% { + transform: translateY(-100px); + } + + 100% { + transform: translateY(0); + } +} + +.cell[data-value="2"] { + background: #eee4da; +} + +.cell[data-value="4"] { + background: #ede0c8; +} + +.cell[data-value="8"] { + background: #f2b179; + color: #f9f6f2; +} + +.cell[data-value="16"] { + background: #f59563; + color: #f9f6f2; +} + +.cell[data-value="32"] { + background: #f67c5f; + color: #f9f6f2; +} + +.cell[data-value="64"] { + background: #f65e3b; + color: #f9f6f2; +} + +.cell[data-value="128"] { + background: #edcf72; + color: #f9f6f2; + font-size: 32px; +} + +.cell[data-value="256"] { + background: #edcc61; + color: #f9f6f2; + font-size: 32px; +} + +.cell[data-value="512"] { + background: #edc850; + color: #f9f6f2; + font-size: 32px; +} + +.cell[data-value="1024"] { + background: #edc53f; + color: #f9f6f2; + font-size: 28px; +} + +.cell[data-value="2048"] { + background: #edc22e; + color: #f9f6f2; + font-size: 28px; +} + +.instruction-message { + position: fixed; + top: 20px; + left: 50%; + transform: translateX(-50%); + background-color: rgba(119, 110, 101, 0.9); + color: white; + padding: 15px 25px; + border-radius: 5px; + font-size: 16px; + animation: fadeOut 1s ease-in 59s forwards; + z-index: 1000; +} + +@keyframes fadeOut { + from { + opacity: 1; + } + + to { + opacity: 0; + visibility: hidden; + } +} \ No newline at end of file diff --git a/sujet.pdf b/sujet.pdf new file mode 100644 index 0000000..e95b84e Binary files /dev/null and b/sujet.pdf differ