Compare commits

...

24 Commits

Author SHA1 Message Date
Raphael LACOTE 245f301602 Merge remote-tracking branch 'origin/mathis' into raph_combat
1 day ago
Raphael LACOTE e3fe5fb5f4 Merge remote-tracking branch 'origin/cleo' into raph_combat
1 day ago
Mathis MOULIN ebeeac349b Ajout table stock d'emoji disponible dans l'application + commande pour remplir la table
2 days ago
Cleo EIRAS f727fc819d WIP tests unitaires
2 days ago
Raphael LACOTE 318734b2f2 Combats
2 days ago
Raphael LACOTE 4713eab595 Modif de home.js pour ajouter le combat
2 days ago
Mathis MOULIN c4fbcb8709 Merge remote-tracking branch 'origin/cleo' into mathis
3 days ago
Mathis MOULIN 732861cd6f Merge remote-tracking branch 'origin' into mathis
3 days ago
Mathis MOULIN c01f3525ff Ajout 2 ligne émoji + pagination
3 days ago
Raphael LACOTE 9c931cc616 Merge de la branche de Cleo
3 days ago
Raphael LACOTE f5fde68b14 Merge remote-tracking branch 'origin/cleo' into raph_combat
3 days ago
Mathis MOULIN c040ee4a6f Amélioration colléction emoji + débug séléction pour combat repro
4 days ago
Raphael LACOTE 13186d6dc0 Modif du combat (c est une route maintenant)
4 days ago
Cleo EIRAS 3406dc14f4 Commande pour peupler la db
4 days ago
Raphael LACOTE 891f37de5f Modif du combat
4 days ago
Cleo EIRAS edc8730198 Fonction pour ajouter des emojis facilement
4 days ago
Cleo EIRAS e29f03a55f forcer version php
4 days ago
Raphael LACOTE 41916fc9b0 Ajout du combat
4 days ago
Cleo EIRAS 0eda4940bc Fusion de deux emojis
1 week ago
Cleo EIRAS 7b488dc14c Update BDD rarity
1 week ago
Cleo EIRAS a12876480e Merge remote-tracking branch 'origin/mathis' into cleo
1 week ago
Mathis MOULIN ecf9673e5d Fix bug icon info emoji
1 week ago
Mathis MOULIN 60b3a1a445 Maquette page d'acceuil listant les emojis v1
1 week ago
Mathis MOULIN 4baddf303b Debut page Acceuil (liste des emojis)
1 week ago

@ -17,7 +17,6 @@
"phpstan/phpdoc-parser": "^1.14",
"symfony/asset": "6.1.*",
"symfony/console": "6.1.*",
"symfony/doctrine-messenger": "6.1.*",
"symfony/dotenv": "6.1.*",
"symfony/expression-language": "6.1.*",
"symfony/flex": "^2",
@ -32,7 +31,7 @@
"symfony/process": "6.1.*",
"symfony/property-access": "6.1.*",
"symfony/property-info": "6.1.*",
"symfony/runtime": "6.1.*",
"symfony/runtime": "^6.3.2",
"symfony/security-bundle": "6.1.*",
"symfony/serializer": "6.1.*",
"symfony/string": "6.1.*",

223
composer.lock generated

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d0596df364527b4a2e7e00a9d7213e87",
"content-hash": "a904471bc7e1136f2c1810044504fcc9",
"packages": [
{
"name": "doctrine/cache",
@ -2558,78 +2558,6 @@
],
"time": "2023-01-10T18:53:01+00:00"
},
{
"name": "symfony/doctrine-messenger",
"version": "v6.1.11",
"source": {
"type": "git",
"url": "https://github.com/symfony/doctrine-messenger.git",
"reference": "e4b775d2dadcc801d2404c701555c2638690811e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/e4b775d2dadcc801d2404c701555c2638690811e",
"reference": "e4b775d2dadcc801d2404c701555c2638690811e",
"shasum": ""
},
"require": {
"doctrine/dbal": "^2.13|^3.0",
"php": ">=8.1",
"symfony/messenger": "^5.4|^6.0",
"symfony/service-contracts": "^1.1|^2|^3"
},
"conflict": {
"doctrine/persistence": "<1.3"
},
"require-dev": {
"doctrine/persistence": "^1.3|^2|^3",
"symfony/property-access": "^5.4|^6.0",
"symfony/serializer": "^5.4|^6.0"
},
"type": "symfony-messenger-bridge",
"autoload": {
"psr-4": {
"Symfony\\Component\\Messenger\\Bridge\\Doctrine\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Doctrine Messenger Bridge",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/doctrine-messenger/tree/v6.1.11"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2023-01-01T08:36:55+00:00"
},
{
"name": "symfony/dotenv",
"version": "v6.1.11",
@ -3125,16 +3053,16 @@
},
{
"name": "symfony/flex",
"version": "v2.7.0",
"version": "v2.7.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/flex.git",
"reference": "5d743b3b78fabe9f3146586d77b0a1f9292851fc"
"reference": "4ae50d368415a06820739e54d38a4a29d6df9155"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/flex/zipball/5d743b3b78fabe9f3146586d77b0a1f9292851fc",
"reference": "5d743b3b78fabe9f3146586d77b0a1f9292851fc",
"url": "https://api.github.com/repos/symfony/flex/zipball/4ae50d368415a06820739e54d38a4a29d6df9155",
"reference": "4ae50d368415a06820739e54d38a4a29d6df9155",
"shasum": ""
},
"require": {
@ -3173,7 +3101,7 @@
"description": "Composer plugin for Symfony",
"support": {
"issues": "https://github.com/symfony/flex/issues",
"source": "https://github.com/symfony/flex/tree/v2.7.0"
"source": "https://github.com/symfony/flex/tree/v2.7.1"
},
"funding": [
{
@ -3189,7 +3117,7 @@
"type": "tidelift"
}
],
"time": "2025-05-23T11:41:40+00:00"
"time": "2025-05-28T14:22:54+00:00"
},
{
"name": "symfony/form",
@ -3945,92 +3873,6 @@
],
"time": "2023-01-10T18:53:01+00:00"
},
{
"name": "symfony/messenger",
"version": "v6.1.11",
"source": {
"type": "git",
"url": "https://github.com/symfony/messenger.git",
"reference": "e3b7323b1d59fc77f5870735809bf39ce1596aee"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/messenger/zipball/e3b7323b1d59fc77f5870735809bf39ce1596aee",
"reference": "e3b7323b1d59fc77f5870735809bf39ce1596aee",
"shasum": ""
},
"require": {
"php": ">=8.1",
"psr/log": "^1|^2|^3"
},
"conflict": {
"symfony/event-dispatcher": "<5.4",
"symfony/event-dispatcher-contracts": "<2",
"symfony/framework-bundle": "<5.4",
"symfony/http-kernel": "<5.4",
"symfony/serializer": "<5.4"
},
"require-dev": {
"psr/cache": "^1.0|^2.0|^3.0",
"symfony/console": "^5.4|^6.0",
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/event-dispatcher": "^5.4|^6.0",
"symfony/http-kernel": "^5.4|^6.0",
"symfony/process": "^5.4|^6.0",
"symfony/property-access": "^5.4|^6.0",
"symfony/routing": "^5.4|^6.0",
"symfony/serializer": "^5.4|^6.0",
"symfony/service-contracts": "^1.1|^2|^3",
"symfony/stopwatch": "^5.4|^6.0",
"symfony/validator": "^5.4|^6.0"
},
"suggest": {
"enqueue/messenger-adapter": "For using the php-enqueue library as a transport."
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Messenger\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Samuel Roze",
"email": "samuel.roze@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Helps applications send and receive messages to/from other applications or via message queues",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/messenger/tree/v6.1.11"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2023-01-20T17:44:30+00:00"
},
{
"name": "symfony/mime",
"version": "v6.1.11",
@ -5293,16 +5135,16 @@
},
{
"name": "symfony/runtime",
"version": "v6.1.11",
"version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/runtime.git",
"reference": "717cb91d66893a27a4607f227e28eb74ff007fde"
"reference": "832c3ce3b810509815050434ccb7ead68d06395b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/runtime/zipball/717cb91d66893a27a4607f227e28eb74ff007fde",
"reference": "717cb91d66893a27a4607f227e28eb74ff007fde",
"url": "https://api.github.com/repos/symfony/runtime/zipball/832c3ce3b810509815050434ccb7ead68d06395b",
"reference": "832c3ce3b810509815050434ccb7ead68d06395b",
"shasum": ""
},
"require": {
@ -5314,10 +5156,10 @@
},
"require-dev": {
"composer/composer": "^1.0.2|^2.0",
"symfony/console": "^5.4|^6.0",
"symfony/dotenv": "^5.4|^6.0",
"symfony/http-foundation": "^5.4|^6.0",
"symfony/http-kernel": "^5.4|^6.0"
"symfony/console": "^5.4.9|^6.0.9|^7.0",
"symfony/dotenv": "^5.4|^6.0|^7.0",
"symfony/http-foundation": "^5.4|^6.0|^7.0",
"symfony/http-kernel": "^5.4|^6.0|^7.0"
},
"type": "composer-plugin",
"extra": {
@ -5348,8 +5190,11 @@
],
"description": "Enables decoupling PHP applications from global state",
"homepage": "https://symfony.com",
"keywords": [
"runtime"
],
"support": {
"source": "https://github.com/symfony/runtime/tree/v6.1.11"
"source": "https://github.com/symfony/runtime/tree/v6.4.22"
},
"funding": [
{
@ -5365,7 +5210,7 @@
"type": "tidelift"
}
],
"time": "2023-01-20T17:44:30+00:00"
"time": "2025-05-07T21:15:03+00:00"
},
{
"name": "symfony/security-bundle",
@ -6623,16 +6468,16 @@
},
{
"name": "symfony/var-exporter",
"version": "v6.4.21",
"version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
"reference": "717e7544aa99752c54ecba5c0e17459c48317472"
"reference": "f28cf841f5654955c9f88ceaf4b9dc29571988a9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/717e7544aa99752c54ecba5c0e17459c48317472",
"reference": "717e7544aa99752c54ecba5c0e17459c48317472",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/f28cf841f5654955c9f88ceaf4b9dc29571988a9",
"reference": "f28cf841f5654955c9f88ceaf4b9dc29571988a9",
"shasum": ""
},
"require": {
@ -6680,7 +6525,7 @@
"serialize"
],
"support": {
"source": "https://github.com/symfony/var-exporter/tree/v6.4.21"
"source": "https://github.com/symfony/var-exporter/tree/v6.4.22"
},
"funding": [
{
@ -6696,7 +6541,7 @@
"type": "tidelift"
}
],
"time": "2025-04-27T21:06:26+00:00"
"time": "2025-05-14T13:00:13+00:00"
},
{
"name": "symfony/web-link",
@ -9145,16 +8990,16 @@
},
{
"name": "symfony/phpunit-bridge",
"version": "v7.2.6",
"version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/phpunit-bridge.git",
"reference": "6106ae85a0e3ed509d339b7f924788c9cc4e7cfb"
"reference": "2eabda563921f21cbce1d1e3247b3c36568905e6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/6106ae85a0e3ed509d339b7f924788c9cc4e7cfb",
"reference": "6106ae85a0e3ed509d339b7f924788c9cc4e7cfb",
"url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/2eabda563921f21cbce1d1e3247b3c36568905e6",
"reference": "2eabda563921f21cbce1d1e3247b3c36568905e6",
"shasum": ""
},
"require": {
@ -9207,7 +9052,7 @@
"description": "Provides utilities for PHPUnit, especially user deprecation notices management",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/phpunit-bridge/tree/v7.2.6"
"source": "https://github.com/symfony/phpunit-bridge/tree/v7.3.0"
},
"funding": [
{
@ -9223,7 +9068,7 @@
"type": "tidelift"
}
],
"time": "2025-04-09T08:35:42+00:00"
"time": "2025-05-23T07:26:30+00:00"
},
{
"name": "symfony/web-profiler-bundle",
@ -9356,7 +9201,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"stability-flags": [],
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
@ -9364,6 +9209,6 @@
"ext-ctype": "*",
"ext-iconv": "*"
},
"platform-dev": {},
"platform-dev": [],
"plugin-api-version": "2.6.0"
}

@ -1,29 +0,0 @@
framework:
messenger:
failure_transport: failed
transports:
# https://symfony.com/doc/current/messenger.html#transport-configuration
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
use_notify: true
check_delayed_interval: 60000
retry_strategy:
max_retries: 3
multiplier: 2
failed: 'doctrine://default?queue_name=failed'
# sync: 'sync://'
default_bus: messenger.bus.default
buses:
messenger.bus.default: []
routing:
Symfony\Component\Mailer\Messenger\SendEmailMessage: async
Symfony\Component\Notifier\Message\ChatMessage: async
Symfony\Component\Notifier\Message\SmsMessage: async
# Route your messages to the transports
# 'App\Message\YourMessage': async

@ -6,6 +6,10 @@
parameters:
services:
App\Repository\RarityRepository:
public: true
App\Repository\EmojiRepository:
public: true
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.

@ -0,0 +1,48 @@
création entity :
php bin/console make:entity Emoji
Réinstaller orm + doctrine_bundle :
composer require doctrine/orm doctrine/doctrine-bundle
Recompiler l'autoload (utile si ça persiste)
- composer dump-autoload
Vérifie ensuite que lentité est bien reconnue :
- php bin/console doctrine:mapping:info
'''Found 1 mapped entity:
[OK] App\Entity\Emoji'''
php bin/console make:migration
php bin/console doctrine:migrations:migrate
-------
Récréation BDD quand pb migrations :
php bin/console doctrine:database:drop --force
php bin/console doctrine:database:create
php bin/console doctrine:migrations:migrate
-------
Solution : Forcer la mise à jour de phpstan/phpdoc-parser
- composer require phpstan/phpdoc-parser:^1.26
Cree la BDD :
''' Modification .env (pour retirer URL_BDD)
Ajout .env.local (pour ajouter URL_BDD)'''
- symfony console doctrine:database:create
ok j'ai creer ma BDD avec symfony console doctrine:database:create comment j'applique mon entity dans la BDD que j'avais créer précedement avec symfony console

@ -1,50 +0,0 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250528130332 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
CREATE TABLE emoji (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, nom VARCHAR(255) NOT NULL, code VARCHAR(255) NOT NULL, force DOUBLE PRECISION NOT NULL, robustesse DOUBLE PRECISION NOT NULL, intelligence DOUBLE PRECISION NOT NULL, vitesse DOUBLE PRECISION NOT NULL, nb_combat_gagne INTEGER NOT NULL, rarete INTEGER NOT NULL)
SQL);
$this->addSql(<<<'SQL'
CREATE TABLE messenger_messages (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, body CLOB NOT NULL, headers CLOB NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL, available_at DATETIME NOT NULL, delivered_at DATETIME DEFAULT NULL)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_75EA56E0FB7336F0 ON messenger_messages (queue_name)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_75EA56E0E3BD61CE ON messenger_messages (available_at)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_75EA56E016BA31DB ON messenger_messages (delivered_at)
SQL);
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
DROP TABLE emoji
SQL);
$this->addSql(<<<'SQL'
DROP TABLE messenger_messages
SQL);
}
}

@ -0,0 +1,314 @@
body {
background-color: #314e57;
font-family: 'Georgia', serif;
text-align: center;
}
h1 {
font-size: 3rem;
margin-bottom: 30px;
text-shadow: 2px 2px 4px #000;
color: #f8b435;
}
.section-divider {
border: none;
border-top: 3px solid #f8b435;
margin: 50px auto 20px;
width: 80%;
}
.section-break-icon {
text-align: center;
font-size: 2rem;
margin: -20px 0;
color: #f8b435;
}
.section-title {
margin: 40px 10px;
font-size: 1.8rem;
color: #f8f5e0;
}
.emoji-container:not(.base) {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 30px;
width: fit-content;
margin: 0 auto;
}
.emoji-container.base {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 30px;
border: 3px dashed #f8b435;
padding: 20px;
margin: 0 auto 30px auto;
border-radius: 15px;
width: fit-content;
}
.emoji-card {
background: #f2e6c9;
width: 180px;
height: 220px;
border: 4px solid #000;
border-radius: 12px;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
padding: 20px;
text-align: center;
font-family: 'Georgia', serif;
position: relative;
transition: transform 0.2s ease, box-shadow 0.2s ease;
cursor: pointer;
}
.emoji-card:hover {
transform: translateY(-8px) scale(1.03);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
z-index: 2;
}
/* Animation brillance pour rareté légendaire */
@keyframes shine {
0% { background-position: 0px; }
100% { background-position: 168px; }
}
.emoji-card.gold {
position: relative;
z-index: 1;
}
.emoji-card.gold::before {
content: '';
position: absolute;
top: 1%;
left: 1%;
width: 97%;
height: 99%;
background: linear-gradient(
120deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.8) 50%,
rgba(255, 255, 255, 0) 100%
);
animation: shine 3.5s infinite;
pointer-events: none;
z-index: -1;
}
.emoji-card > * {
position: relative;
z-index: 1;
}
/*---------------------------*/
.emoji-card.gray { border-color: gray; }
@keyframes auraGlow {
0% { box-shadow: 0 0 10px 0 rgba(0,0,0,0.3); }
50% { box-shadow: 0 0 25px 8px currentColor; }
100% { box-shadow: 0 0 10px 0 rgba(0,0,0,0.3); }
}
/* Applique une animation d'aura par rareté */
.emoji-card.green {
color: darkgreen;
animation: auraGlow 3s infinite ease-in-out;
}
.emoji-card.purple {
color: purple;
animation: auraGlow 3s infinite ease-in-out;
}
.emoji-card.red {
color: darkred;
animation: auraGlow 3s infinite ease-in-out;
}
.emoji-card.gold {
color: goldenrod;
animation: auraGlow 3s infinite ease-in-out;
}
/* Couleurs des noms selon rareté */
.emoji-name.green { color: darkgreen; }
.emoji-name.purple { color: purple; }
.emoji-name.red { color: darkred; }
.emoji-name.gold { color: goldenrod; }
.emoji-name.gray { color: gray; }
.emoji-card .emoji {
font-size: 50px;
margin-bottom: 10px;
}
.emoji-name {
font-weight: bold;
font-size: 1.2rem;
margin-top: 5px;
color: purple;
}
.emoji-level {
font-size: 1rem;
font-weight: bold;
color: #3c2f2f;
margin-bottom: 10px;
}
.action-buttons {
display: flex;
justify-content: center;
gap: 60px;
margin-top: 20px;
}
.btn {
background: #f2e6c9;
border: 3px solid #000;
padding: 10px 25px;
font-size: 1.2rem;
font-weight: bold;
border-radius: 8px;
cursor: pointer;
transition: transform 0.2s ease;
box-shadow: 3px 3px 0 #000;
}
.btn:hover {
transform: scale(1.05);
background-color: #e5d6b8;
}
.detail-icon {
position: absolute;
top: 8px;
right: 10px;
cursor: pointer;
font-size: 18px;
color: #555;
}
.popup {
position: absolute;
top: 110%;
left: 50%;
transform: translateX(-50%);
background: #fff8e1;
color: #000;
padding: 10px;
border: 2px solid #000;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
display: none;
z-index: 10;
width: 180px;
font-size: 14px;
}
/* Filtre et Tri*/
.filter-bar {
margin-bottom: 30px;
color: #f8f5e0;
font-size: 1rem;
}
.filter-bar select {
margin: 0 10px;
padding: 5px 10px;
border-radius: 6px;
border: 2px solid #000;
background: #f2e6c9;
font-family: 'Georgia', serif;
}
/* Champs de Recherche */
.filter-bar input[type="text"] {
padding: 5px 10px;
border-radius: 6px;
border: 2px solid #000;
background: #f2e6c9;
font-family: 'Georgia', serif;
margin-right: 10px;
}
/* Style séléction créature pour combat / accouplement */
/* Etat séléctionné */
.emoji-card.selected {
outline: 4px solid #f8b435;
outline-offset: -4px;
box-shadow: 0 0 30px 10px rgba(248, 180, 53, 0.6);
}
#selection-status {
font-size: 1.1rem;
margin-top: 4rem;
font-weight: bold;
color: #f9e8c0;
}
.selection-visual {
display: flex;
justify-content: center;
align-items: center;
gap: 15px;
margin-bottom: 30px;
}
.creature-tag {
padding: 8px 15px;
background: #f2e6c9;
border: 2px solid #000;
border-radius: 8px;
font-weight: bold;
font-size: 1.1rem;
box-shadow: 2px 2px 0 #000;
}
.vs-text {
font-size: 1.5rem;
font-weight: bold;
}
/* Pagination */
.pagination-bar {
margin: 4rem 4rem;
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
color: #f8f5e0;
font-family: 'Georgia', serif;
}
.pagination-bar button {
background: none;
border: none;
font-size: 1.1rem;
color: #f8f5e0;
cursor: pointer;
padding: 5px 10px;
border-radius: 50%;
transition: background 0.2s ease;
}
.pagination-bar button.active {
background: #333;
color: white;
font-weight: bold;
}
.pagination-bar button:hover:not(.active) {
color: #f8b435;
}

@ -0,0 +1,235 @@
document.addEventListener('DOMContentLoaded', () => {
// Copie tout ici
let selectedCards = [];
function toggleSelection(card) {
const id = card.dataset.id;
if (card.classList.contains('selected')) {
card.classList.remove('selected');
selectedCards = selectedCards.filter(c => c.dataset.id !== id);
} else {
if (selectedCards.length < 2) {
card.classList.add('selected');
selectedCards.push(card);
} else {
alert("Tu ne peux sélectionner que 2 créatures à la fois.");
}
}
updateSelectionDisplay();
}
function updateSelectionDisplay() {
const status = document.getElementById("selection-status");
const visual = document.getElementById("selection-visual");
if (selectedCards.length === 0) {
status.textContent = "Sélectionnez 2 créatures...";
visual.innerHTML = "";
} else if (selectedCards.length === 1) {
status.textContent = `1ère sélection : ${selectedCards[0].dataset.name}`;
visual.innerHTML = "";
} else {
status.textContent = "2 créatures sélectionnées !";
visual.innerHTML = `
<div class="creature-tag">${selectedCards[0].dataset.name}</div>
<span class="vs-text"> et </span>
<div class="creature-tag">${selectedCards[1].dataset.name}</div>
`;
}
}
function handleAction(type) {
if (selectedCards.length !== 2) {
alert("Tu dois sélectionner 2 créatures.");
return;
}
const name1 = selectedCards[0].dataset.name;
const name2 = selectedCards[1].dataset.name;
if (type === 'combat') {
const id1 = selectedCards[0].dataset.id;
const id2 = selectedCards[1].dataset.id;
const t1 = 4;
const t2 = 6;
console.log(`Combat : ${name1} contre ${name2}`);
//window.location.href = `/emoji/fight/${t2}/${t1}`;
//window.location.href = `/emoji/fight/${id1}/${id2}`;
fetch(`/emoji/fight/${t2}/${t1}`, { method: 'GET',
headers: {
'Accept': 'application/json'
}}).then(response => {
if (!response.ok) {
throw new Error(`Erreur HTTP ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Données reçues :', data);
})
.catch(error => {
console.error('Erreur lors de la requête :', error);
});
} else if (type === 'reproduction') {
console.log(`Accouplement : ${name1} et ${name2}`);
}
// Réinitialiser après l'action (seulement si pas redirigé)
selectedCards.forEach(card => card.classList.remove('selected'));
selectedCards = [];
updateSelectionDisplay();
}
// Ouvre / Ferme la popup d'information
function togglePopup(id) {
const popup = document.getElementById('popup-' + id);
if (popup) {
popup.style.display = (popup.style.display === 'block') ? 'none' : 'block';
}
}
// Fermer les autres popups en cliquant ailleurs
document.addEventListener('click', function(e) {
document.querySelectorAll('.popup').forEach(p => {
if (!p.contains(e.target) && !p.previousElementSibling.contains(e.target)) {
p.style.display = 'none';
}
});
});
// Fonction pour appliquer la recherche, les filtres et le tri
function applyFilters() {
const selectedColor = document.getElementById('rarete-filter').value;
const sortBy = document.getElementById('sort-select').value;
const searchTerm = document.getElementById('search-input').value.toLowerCase();
const cards = Array.from(document.querySelectorAll('#collection-container .emoji-card'));
cards.forEach(card => {
const color = card.dataset.color;
const name = card.querySelector('.emoji-name')?.textContent.toLowerCase() ?? "";
const matchColor = !selectedColor || color === selectedColor;
const matchName = !searchTerm || name.includes(searchTerm);
if (matchColor && matchName) {
card.style.display = 'block';
} else {
card.style.display = 'none';
}
});
if (sortBy !== 'none') {
const container = document.getElementById('collection-container'); // ✅ corrigé
const visibleCards = cards.filter(c => c.style.display !== 'none');
visibleCards.sort((a, b) => {
const aVal = parseFloat(a.dataset[sortBy]);
const bVal = parseFloat(b.dataset[sortBy]);
return bVal - aVal;
});
visibleCards.forEach(card => container.appendChild(card));
paginateCards();
}
}
// Appel Fonctionnalité de popup d'information
document.querySelectorAll('.detail-icon').forEach(icon => {
icon.addEventListener('click', (e) => {
const card = icon.closest('.emoji-card');
const id = card?.dataset.id;
if (id) {
togglePopup(id);
}
e.stopPropagation(); // évite que ça sélectionne la carte
});
});
// Appliquer l'écouteur à toutes les cartes (base + collection)
document.querySelectorAll('.emoji-container').forEach(container => {
container.addEventListener('click', (e) => {
const card = e.target.closest('.emoji-card');
if (!card || e.target.closest('.detail-icon')) return; // Ignore si clic sur icône
toggleSelection(card);
});
});
// Appel Fonctionnalité de combat et reproduction
document.querySelectorAll('.btn').forEach(button => {
button.addEventListener('click', () => {
const type = button.textContent.includes('Combattre') ? 'combat' : 'reproduction';
handleAction(type);
});
});
// Appel Fonctionnalité de recherche et filtres
document.getElementById('search-input').addEventListener('input', applyFilters);
document.getElementById('rarete-filter').addEventListener('change', applyFilters);
document.getElementById('sort-select').addEventListener('change', applyFilters);
/***** Partie Pagination ******/
const ITEMS_PER_PAGE = 10;
let currentPage = 1;
function paginateCards() {
const allCards = Array.from(document.querySelectorAll('#collection-container .emoji-card'));
const container = document.getElementById('collection-container');
const totalPages = Math.ceil(allCards.length / ITEMS_PER_PAGE);
// Masquer toutes les cartes
allCards.forEach(card => card.style.display = 'none');
// Afficher uniquement les cartes de la page courante
const start = (currentPage - 1) * ITEMS_PER_PAGE;
const end = start + ITEMS_PER_PAGE;
allCards.slice(start, end).forEach(card => card.style.display = 'block');
renderPagination(totalPages);
}
function renderPagination(totalPages) {
const pagination = document.getElementById('pagination');
pagination.innerHTML = '';
const addBtn = (text, page = null, isActive = false, disabled = false) => {
const btn = document.createElement('button');
btn.textContent = text;
if (isActive) btn.classList.add('active');
if (disabled) btn.disabled = true;
if (page !== null) {
btn.addEventListener('click', () => {
currentPage = page;
paginateCards();
});
}
pagination.appendChild(btn);
};
addBtn('Précédent', currentPage - 1, false, currentPage === 1);
for (let i = 1; i <= totalPages; i++) {
if (
i === 1 ||
i === totalPages ||
(i >= currentPage - 1 && i <= currentPage + 1)
) {
addBtn(i, i, i === currentPage);
} else if (
i === 2 && currentPage > 3 ||
i === totalPages - 1 && currentPage < totalPages - 2
) {
addBtn('...');
}
}
addBtn('Suivant', currentPage + 1, false, currentPage === totalPages);
}
paginateCards();
});

@ -0,0 +1,62 @@
<?php
namespace App\Command;
use Doctrine\DBAL\Connection;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class PopulateDBCommand extends Command
{
protected static $defaultName = 'app:populateDB';
private Connection $connection;
public function __construct(Connection $connection)
{
parent::__construct();
$this->connection = $connection;
}
protected function configure()
{
$this
->setDescription('Populate the database.');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
try {
// On supprime la table si elle existe déjà
$this->connection->executeStatement('DROP TABLE IF EXISTS rarity');
// On crée la table
$this->connection->executeStatement('
CREATE TABLE rarity (
id INT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
drop_rate FLOAT NOT NULL
)
');
// On peuple la table
$this->connection->executeStatement("INSERT INTO rarity (id, name, drop_rate) VALUES
(1, 'Common', 0.6),
(2, 'Rare', 0.2),
(3, 'Epic', 0.1),
(4, 'Mythical', 0.085),
(5, 'Legendary', 0.015)
");
$output->writeln('Base de données peuplée.');
} catch (\Exception $e) {
$output->writeln('<error>Erreur : ' . $e->getMessage() . '</error>');
return Command::FAILURE;
}
return Command::SUCCESS;
}
}

@ -0,0 +1,76 @@
<?php
namespace App\Command;
use Doctrine\DBAL\Connection;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class PopulateDBEmojiAvailableCommand extends Command
{
protected static $defaultName = 'app:popDBEmojiAvai';
private Connection $connection;
public function __construct(Connection $connection)
{
parent::__construct();
$this->connection = $connection;
}
protected function configure()
{
$this
->setDescription('Populate the database.');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
try {
// On supprime la table si elle existe déjà
$this->connection->executeStatement('DROP TABLE IF EXISTS stock_emoji');
// On crée la table
$this->connection->executeStatement('
CREATE TABLE stock_emoji (
id INT PRIMARY KEY,
code VARCHAR(255) NOT NULL
)
');
// On peuple la table
$this->connection->executeStatement("INSERT INTO stock_emoji (id, code) VALUES
(1,'🤖'),
(2,'😺'),
(3,'🧠'),
(4,'👻'),
(5,'🧟'),
(6,'🐶'),
(7,'👽'),
(8,'🧛'),
(9,'🎃'),
(10,'🐸'),
(11,'⚡'),
(12,'💀'),
(13,'🔥'),
(14,'🧙'),
(15,'🌪️'),
(16,'😎'),
(17,'😁'),
(18,'🌟'),
(19,'😈')
");
$output->writeln('Base de données peuplée.');
} catch (\Exception $e) {
$output->writeln('<error>Erreur : ' . $e->getMessage() . '</error>');
return Command::FAILURE;
}
return Command::SUCCESS;
}
}

@ -2,21 +2,239 @@
namespace App\Controller;
use App\Entity\Emoji;
use App\Entity\Rarity;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Repository\RarityRepository;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use App\Repository\EmojiRepository;
#[Route('/emoji', name: 'emoji')]
class EmojiController extends AbstractController
{
private RarityRepository $rarityRepository;
private HttpClientInterface $httpClient;
public function __construct(RarityRepository $rarityRepository, HttpClientInterface $httpClient)
{
$this->rarityRepository = $rarityRepository;
$this->httpClient = $httpClient;
}
#[Route('/emoji', name: 'app_emoji')]
public function index(): Response
{
$this->testToMove();
return $this->render('emoji/index.html.twig', [
'controller_name' => 'EmojiController',
]);
}
public function reproduceEmoji(Emoji $emoji1, Emoji $emoji2): Emoji {
return new Emoji();
#[Route('/count', name: 'count')]
public function count(EmojiRepository $emojiRepository): Response
{
$count = count($emojiRepository->findAll());
return new Response(['count' => $count]);
}
#[Route('/addRarity', name: 'addR')]
public function addRarityDebug(EntityManagerInterface $entityManager) {
$rarity = new Rarity();
$rarity->setName('Bip');
$rarity->setDropRate(42);
$entityManager->persist($rarity);
$entityManager->flush();
return new Response();
}
#[Route('/add/{code}', name: 'add')]
public function addEmojiDebug(string $code, EntityManagerInterface $entityManager) {
$emoji = new Emoji();
$emoji->setCode($code);
$emoji->setName('Default Name');
$emoji->setStrength(1.0);
$emoji->setToughness(1.0);
$emoji->setIntelligence(1.0);
$emoji->setSpeed(1.0);
$emoji->setFightsWon(0);
// On récupère une instance de Rarity existante (par exemple, la première)
$rarity = $this->getRarity();
if (!$rarity) {
throw new \RuntimeException('Aucun objet Rarity trouvé en base.');
}
$emoji->setRarity($rarity);
// Optionnel : définir parent1 et parent2 si tu veux tester avec des relations
// $emoji->setParent1(null);
// $emoji->setParent2(null);
$entityManager->persist($emoji);
$entityManager->flush();
return new Response();
}
private function getRarity(): Rarity {
$rarity = $this->rarityRepository->findAll();
$rand = mt_rand() / mt_getrandmax();
$sum = 0.0;
foreach($rarity as $r) {
$sum += $r->getDropRate();
if($sum > $rand) {
return $r;
}
}
return $rarity[0];
}
// Renvoi l'url de l'image issue de la fusion de deux emojis. Fonctionne avec un code et un emoji directement
private function getFusionUrl(string $emoji1, string $emoji2): string
{
$baseUrl = 'https://emojik.vercel.app/s/';
$size = 256;
// On encode les string pour l'url
$encodedEmoji1 = urlencode($emoji1);
$encodedEmoji2 = urlencode($emoji2);
return sprintf('%s%s_%s?size=%d', $baseUrl, $encodedEmoji1, $encodedEmoji2, $size);
}
#[Route('/fusion/{emoji1_id}/{emoji2_id}', name: 'fusion')]
public function reproduceEmoji(int $emoji1_id, int $emoji2_id, EntityManagerInterface $entityManager, EmojiRepository $emojiRepository): JsonResponse {
$emoji1 = $emojiRepository->find($emoji1_id);
$emoji2 = $emojiRepository->find($emoji2_id);
if (!$emoji1 || !$emoji2) {
return new JsonResponse(['error' => 'One or both emojis not found'], 404);
}
$child = new Emoji();
$child->setName("Testenfant");
// Chance de fusion
$fusionRand = mt_rand() / mt_getrandmax();
if($fusionRand > 0.8) {
// Si les emoji parents sont des fusions on remonte l'arbre généalogique jusqu'à trouver un smiley normal
// On ne peut pas fusionner un smiley déjà fusionné
$temp1 = $emoji1;
while(str_contains($temp1->getCode(), "vercel")) {
$temp1 = $temp1->getParent1();
}
$temp2 = $emoji2;
while(str_contains($temp2->getCode(), "vercel")) {
$temp2 = $temp2->getParent2();
}
$child->setCode($this->getFusionUrl($temp1->getCode(), $temp2->getCode()));
} else {
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setCode($emoji1->getCode());
} else {
$child->setCode($emoji2->getCode());
}
}
// Lors d'une fusion un emoji récupère chaque stat d'un de ces deux parents de manière aléatoire
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setStrength($emoji1->getStrength());
} else {
$child->setStrength($emoji2->getStrength());
}
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setToughness($emoji1->getToughness());
} else {
$child->setToughness($emoji2->getToughness());
}
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setIntelligence($emoji1->getIntelligence());
} else {
$child->setIntelligence($emoji2->getIntelligence());
}
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setSpeed($emoji1->getSpeed());
} else {
$child->setSpeed($emoji2->getSpeed());
}
$child->setFightsWon(0);
$child->setRarity($this->getRarity());
$child->setParent1($emoji1);
$child->setParent2($emoji2);
$entityManager->persist($child);
$entityManager->flush();
return new JsonResponse([
'message' => 'Child created',
'childId' => $child->getId()
]);
}
#[Route('/fight/{idEmoji1}/{idEmoji2}', name: 'fight_emoji')]
public function fightEmoji(int $idEmoji1, int $idEmoji2, EntityManagerInterface $entityManager, EmojiRepository $emojiRepository): JsonResponse {
$emoji1 = $emojiRepository->find($idEmoji1);
$emoji2 = $emojiRepository->find($idEmoji2);
if (!$emoji1 || !$emoji2) {
return new JsonResponse(['error' => 'Emoji not found'], 404);
}
$aleatoire = random_int(0,3);
$valEmoji1 = [$emoji1->getStrength(),$emoji1->getToughness(),$emoji1->getIntelligence(),$emoji1->getSpeed()];
$valEmoji2 = [$emoji2->getStrength(),$emoji2->getToughness(),$emoji2->getIntelligence(),$emoji2->getSpeed()];
$difference = $valEmoji1[$aleatoire] - $valEmoji2[$aleatoire];
if($difference > 0){
$emoji1->wonFight();
$entityManager->persist($emoji1);
$entityManager->remove($emoji2);
} else {
$emoji2->wonFight();
$entityManager->persist($emoji2);
$entityManager->remove($emoji1);
}
$entityManager->flush();
return new JsonResponse([
'message' => 'End of the fight',
]);
}
public function testToMove(){
$e = new Emoji();
$e->setName("ROBERT");
$e->setStrength(5);
$e->setIntelligence(2);
$e->setToughness(3);
$e->setSpeed(4);
$e2 = new Emoji();
$e2->setName("BIBOP");
$e2->setStrength(42);
$e2->setIntelligence(1);
$e2->setToughness(1);
$e2->setSpeed(1);
$vic = $this->fightEmoji($e,$e2);
echo $vic->getName();
}
}

@ -0,0 +1,211 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
#[Route('/', name: 'home')]
public function index(): Response
{
$emojisDeBase = [
[
'id' => 1,
'nom' => 'Bob',
'code' => '😊',
'force' => 12.5,
'robustesse' => 9.3,
'intelligence' => 7.8,
'vitesse' => 10.0,
'nbCombatGagne' => 3,
'rarete' => 3, // épique
],
[
'id' => 2,
'nom' => 'John',
'code' => '😭',
'force' => 5.1,
'robustesse' => 4.2,
'intelligence' => 3.3,
'vitesse' => 6.0,
'nbCombatGagne' => 1,
'rarete' => 1, // commun
],
[
'id' => 3,
'nom' => 'Rodolph',
'code' => '😁',
'force' => 20.0,
'robustesse' => 15.0,
'intelligence' => 18.0,
'vitesse' => 17.0,
'nbCombatGagne' => 10,
'rarete' => 5, // légendaire
],
[
'id' => 4,
'nom' => 'Sophie',
'code' => '😎',
'force' => 8.0,
'robustesse' => 7.5,
'intelligence' => 9.0,
'vitesse' => 8.5,
'nbCombatGagne' => 2,
'rarete' => 4, // mythique
]
];
$emojisCrees = [
[
'id' => 6,
'nom' => 'Benoit',
'code' => '🤖',
'force' => 15.0,
'robustesse' => 12.0,
'intelligence' => 14.0,
'vitesse' => 13.0,
'nbCombatGagne' => 5,
'rarete' => 3, // épique
],
[
'id' => 7,
'nom' => 'Eric',
'code' => '🌟',
'force' => 18.0,
'robustesse' => 16.0,
'intelligence' => 17.0,
'vitesse' => 19.0,
'nbCombatGagne' => 8,
'rarete' => 5, // légendaire
],
[
'id' => 5,
'nom' => 'Alice',
'code' => '🥳',
'force' => 6.0,
'robustesse' => 5.0,
'intelligence' => 4.5,
'vitesse' => 6.5,
'nbCombatGagne' => 0,
'rarete' => 2, // rare
],
[
'id' => 8,
'nom' => 'Bobette',
'code' => '🤩',
'force' => 10.0,
'robustesse' => 9.0,
'intelligence' => 11.0,
'vitesse' => 12.0,
'nbCombatGagne' => 4,
'rarete' => 4, // mythique
],
[
'id' => 9,
'nom' => 'Charlie',
'code' => '😇',
'force' => 7.0,
'robustesse' => 6.0,
'intelligence' => 8.0,
'vitesse' => 7.5,
'nbCombatGagne' => 2,
'rarete' => 1, // commun
],
[
'id' => 10,
'nom' => 'Diana',
'code' => '😈',
'force' => 14.0,
'robustesse' => 13.0,
'intelligence' => 15.0,
'vitesse' => 16.0,
'nbCombatGagne' => 6,
'rarete' => 4, // mythique
],
[
'id' => 11,
'nom' => 'Ethan',
'code' => '🤯',
'force' => 9.0,
'robustesse' => 8.0,
'intelligence' => 10.0,
'vitesse' => 11.0,
'nbCombatGagne' => 3,
'rarete' => 3, // épique
],
[
'id' => 12,
'nom' => 'Fiona',
'code' => '🥺',
'force' => 4.0,
'robustesse' => 3.5,
'intelligence' => 5.0,
'vitesse' => 4.5,
'nbCombatGagne' => 1,
'rarete' => 1, // commun
],
[
'id' => 13,
'nom' => 'George',
'code' => '😜',
'force' => 11.0,
'robustesse' => 10.0,
'intelligence' => 12.0,
'vitesse' => 13.5,
'nbCombatGagne' => 2,
'rarete' => 3, // épique
],
[
'id' => 14,
'nom' => 'Hannah',
'code' => '😏',
'force' => 3.0,
'robustesse' => 2.5,
'intelligence' => 4.0,
'vitesse' => 3.5,
'nbCombatGagne' => 0,
'rarete' => 2, // rare
],
[
'id' => 15,
'nom' => 'Ian',
'code' => '😬',
'force' => 17.0,
'robustesse' => 14.0,
'intelligence' => 16.0,
'vitesse' => 18.0,
'nbCombatGagne' => 7,
'rarete' => 5, // légendaire
],
];
foreach ($emojisDeBase as &$emoji) {
$emoji['color'] = match ($emoji['rarete']) {
2 => 'green',
3 => 'purple',
4 => 'red',
5 => 'gold',
default => 'gray',
};
}
foreach ($emojisCrees as &$emoji) {
$emoji['color'] = match ($emoji['rarete']) {
2 => 'green',
3 => 'purple',
4 => 'red',
5 => 'gold',
default => 'gray',
};
}
return $this->render('home/index.html.twig', [
'emojisDeBase' => $emojisDeBase,
'emojisCrees' => $emojisCrees,
]);
}
}

@ -14,42 +14,73 @@ class Emoji
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $nom = null;
private ?string $name = null;
#[ORM\Column(length: 255)]
private ?string $code = null;
#[ORM\Column]
private ?float $force = null;
private ?float $strength = null;
#[ORM\Column]
private ?float $robustesse = null;
private ?float $toughness = null;
#[ORM\Column]
private ?float $intelligence = null;
#[ORM\Column]
private ?float $vitesse = null;
private ?float $speed = null;
#[ORM\Column]
private ?int $nbCombatGagne = null;
private ?int $fightsWon = null;
#[ORM\Column]
private ?int $rarete = null;
#[ORM\ManyToOne(targetEntity: Rarity::class)]
#[ORM\JoinColumn(nullable: false)]
private ?Rarity $rarity = null;
#[ORM\ManyToOne(targetEntity: self::class)]
#[ORM\JoinColumn(nullable: true)]
private ?Emoji $parent1 = null;
#[ORM\ManyToOne(targetEntity: self::class)]
#[ORM\JoinColumn(nullable: true)]
private ?Emoji $parent2 = null;
public function getParent1(): ?self
{
return $this->parent1;
}
public function setParent1(?self $parent1): self
{
$this->parent1 = $parent1;
return $this;
}
public function getParent2(): ?self
{
return $this->parent2;
}
public function setParent2(?self $parent2): self
{
$this->parent2 = $parent2;
return $this;
}
public function getId(): ?int
{
return $this->id;
}
public function getNom(): ?string
public function getName(): ?string
{
return $this->nom;
return $this->name;
}
public function setNom(string $nom): self
public function setName(string $name): self
{
$this->nom = $nom;
$this->name = $name;
return $this;
}
@ -64,25 +95,25 @@ class Emoji
return $this;
}
public function getForce(): ?float
public function getStrength(): ?float
{
return $this->force;
return $this->strength;
}
public function setForce(float $force): self
public function setStrength(float $strength): self
{
$this->force = $force;
$this->strength = $strength;
return $this;
}
public function getRobustesse(): ?float
public function getToughness(): ?float
{
return $this->robustesse;
return $this->toughness;
}
public function setRobustesse(float $robustesse): self
public function setToughness(float $toughness): self
{
$this->robustesse = $robustesse;
$this->toughness = $toughness;
return $this;
}
@ -97,36 +128,45 @@ class Emoji
return $this;
}
public function getVitesse(): ?float
public function getSpeed(): ?float
{
return $this->vitesse;
return $this->speed;
}
public function setVitesse(float $vitesse): self
public function setSpeed(float $speed): self
{
$this->vitesse = $vitesse;
$this->speed = $speed;
return $this;
}
public function getNbCombatGagne(): ?int
public function getFightsWon(): ?int
{
return $this->nbCombatGagne;
return $this->fightsWon;
}
public function setNbCombatGagne(int $nbCombatGagne): self
public function setFightsWon(int $fightsWon): self
{
$this->nbCombatGagne = $nbCombatGagne;
$this->fightsWon = $fightsWon;
return $this;
}
public function getRarete(): ?int
public function getRarity(): ?Rarity
{
return $this->rarete;
return $this->rarity;
}
public function setRarete(int $rarete): self
public function setRarity(Rarity $rarity): self
{
$this->rarete = $rarete;
$this->rarity = $rarity;
return $this;
}
public function wonFight() : self{
$this->fightsWon = $this->fightsWon + 1;
$this->strength += 1;
$this->toughness += 1;
$this->intelligence += 1;
$this->speed += 1;
return $this;
}
}

@ -0,0 +1,48 @@
<?php
namespace App\Entity;
use App\Repository\RarityRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: RarityRepository::class)]
class Rarity
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 50, unique: true)]
private string $name;
#[ORM\Column(type: 'float')]
private float $dropRate;
public function getId(): ?int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getDropRate(): float
{
return $this->dropRate;
}
public function setDropRate(float $dropRate): self
{
$this->dropRate = $dropRate;
return $this;
}
}

@ -0,0 +1,35 @@
<?php
namespace App\Entity;
use App\Repository\StockEmojiRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: StockEmojiRepository::class)]
class StockEmoji
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $code = null;
public function getId(): ?int
{
return $this->id;
}
public function getCode(): ?string
{
return $this->code;
}
public function setCode(string $code): self
{
$this->code = $code;
return $this;
}
}

@ -0,0 +1,48 @@
<?php
namespace App\Repository;
use App\Entity\Rarity;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Rarity>
*
* @method Rarity|null find($id, $lockMode = null, $lockVersion = null)
* @method Rarity|null findOneBy(array $criteria, array $orderBy = null)
* @method Rarity[] findAll()
* @method Rarity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class RarityRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Rarity::class);
}
// /**
// * @return Rarity[] Returns an array of Rarity objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('r')
// ->andWhere('r.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('r.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?Rarity
// {
// return $this->createQueryBuilder('r')
// ->andWhere('r.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

@ -0,0 +1,48 @@
<?php
namespace App\Repository;
use App\Entity\StockEmoji;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<StockEmoji>
*
* @method StockEmoji|null find($id, $lockMode = null, $lockVersion = null)
* @method StockEmoji|null findOneBy(array $criteria, array $orderBy = null)
* @method StockEmoji[] findAll()
* @method StockEmoji[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class StockEmojiRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, StockEmoji::class);
}
// /**
// * @return StockEmoji[] Returns an array of StockEmoji objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('s')
// ->andWhere('s.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('s.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?StockEmoji
// {
// return $this->createQueryBuilder('s')
// ->andWhere('s.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

@ -1,4 +1,13 @@
{
"doctrine/deprecations": {
"version": "1.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "87424683adc81d7dc305eefec1fced883084aab9"
}
},
"doctrine/doctrine-bundle": {
"version": "2.13",
"recipe": {
@ -117,18 +126,6 @@
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
}
},
"symfony/messenger": {
"version": "6.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.0",
"ref": "ba1ac4e919baba5644d31b57a3284d6ba12d52ee"
},
"files": [
"./config/packages/messenger.yaml"
]
},
"symfony/monolog-bundle": {
"version": "3.10",
"recipe": {

@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
{# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
{% block stylesheets %}

@ -0,0 +1,107 @@
{% extends 'base.html.twig' %}
{% block title %}Accueil - Ma collection de créatures{% endblock %}
{% block stylesheets %}
<link rel="stylesheet" href="{{ asset('css/home.css') }}">
{% endblock %}
{% block body %}
<h1>🧬 Ma collection de créatures 🐾</h1>
<h2 class="section-title">🌱 Vos créatures de base</h2>
<div class="emoji-container base">
{% for emoji in emojisDeBase %}
<div class="emoji-card {{ emoji.color }}"
data-id="{{ emoji.id }}"
data-name="{{ emoji.nom }}"
data-color="{{ emoji.color }}"
data-level="{{ emoji.nbCombatGagne }}"
data-force="{{ emoji.force }}"
data-vitesse="{{ emoji.vitesse }}">
<div class="emoji-level">Level {{ emoji.nbCombatGagne }}</div>
<div class="emoji">{{ emoji.code }}</div>
<div class="emoji-name {{ emoji.color }}">{{ emoji.nom }}</div>
<div class="detail-icon" data-id="{{ emoji.id }}"></div>
<div class="popup" id="popup-{{ emoji.id }}">
<strong>Stats :</strong><br>
Force: {{ emoji.force }}<br>
Robustesse: {{ emoji.robustesse }}<br>
Intelligence: {{ emoji.intelligence }}<br>
Vitesse: {{ emoji.vitesse }}
</div>
</div>
{% endfor %}
</div>
<div class="section-break-icon">🔻</div>
<h2 class="section-title">📦 Votre collection personnelle</h2>
<div class="filter-bar">
<label for="search-input">🔍 Rechercher un nom :</label>
<input type="text" id="search-input" placeholder="ex: Bob..." />
<label for="rarete-filter">Filtrer par rareté :</label>
<select id="rarete-filter">
<option value="">Toutes</option>
<option value="green">Communes</option>
<option value="purple">Épiques</option>
<option value="red">Mythiques</option>
<option value="gold">Légendaires</option>
</select>
<label for="sort-select">Trier par :</label>
<select id="sort-select">
<option value="none">--</option>
<option value="level">Level</option>
<option value="force">Force</option>
<option value="vitesse">Vitesse</option>
</select>
</div>
<div class="emoji-container" id="collection-container">
{% for emoji in emojisCrees %}
<div class="emoji-card {{ emoji.color }}"
data-id="{{ emoji.id }}"
data-name="{{ emoji.nom }}"
data-color="{{ emoji.color }}"
data-level="{{ emoji.nbCombatGagne }}"
data-force="{{ emoji.force }}"
data-vitesse="{{ emoji.vitesse }}">
<div class="emoji-level">Level {{ emoji.nbCombatGagne }}</div>
<div class="emoji">{{ emoji.code }}</div>
<div class="emoji-name {{ emoji.color }}">{{ emoji.nom }}</div>
<div class="detail-icon" data-id="{{ emoji.id }}"></div>
<div class="popup" id="popup-{{ emoji.id }}">
<strong>Stats :</strong><br>
Force: {{ emoji.force }}<br>
Robustesse: {{ emoji.robustesse }}<br>
Intelligence: {{ emoji.intelligence }}<br>
Vitesse: {{ emoji.vitesse }}
</div>
</div>
{% endfor %}
</div>
<div id="pagination" class="pagination-bar"></div>
<div id="selection-status">Sélectionnez 2 créatures de votre choix pour commencer ...</div>
<div id="selection-visual" class="selection-visual"></div>
<div class="action-buttons">
<button class="btn">⚔️ Combattre</button>
<button class="btn">💞 Reproduire</button>
</div>
{% endblock %}
{% block javascripts %}
<script src="{{ asset('js/home.js') }}"></script>
{% endblock %}

@ -0,0 +1,89 @@
<?php
namespace App\Tests\Controller;
use App\Entity\Emoji;
use App\Entity\Rarity;
use App\Repository\EmojiRepository;
use App\Repository\RarityRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;
class EmojiControllerTest extends WebTestCase
{
private $client;
private EntityManagerInterface $em;
protected function setUp(): void
{
$this->client = static::createClient();
$this->em = static::getContainer()->get(EntityManagerInterface::class);
// Démarre une transaction pour pouvoir annuler les modifications des tests
$this->em->getConnection()->beginTransaction();
}
protected function tearDown(): void
{
// Rollback la transaction pour annuler les changements des tests
$this->em->getConnection()->rollBack();
parent::tearDown();
}
public function testReproduceEmoji(): void
{
$emoji1 = (new Emoji())
->setCode('😀')
->setName('Parent1')
->setStrength(1.0)
->setToughness(1.0)
->setIntelligence(1.0)
->setSpeed(1.0)
->setFightsWon(5);
$emoji2 = (new Emoji())
->setCode('😎')
->setName('Parent2')
->setStrength(2.0)
->setToughness(2.0)
->setIntelligence(2.0)
->setSpeed(2.0)
->setFightsWon(3);
$rarity = (new Rarity())
->setName('Rare')
->setDropRate(1.0);
$this->em->persist($rarity);
$this->em->persist($emoji1);
$this->em->persist($emoji2);
$this->em->flush();
$id1 = $emoji1->getId();
$id2 = $emoji2->getId();
$this->client->request('GET', "/emoji/fusion/$id1/$id2");
$response = $this->client->getResponse();
$this->assertEquals(Response::HTTP_OK, $response->getStatusCode());
$data = json_decode($response->getContent(), true);
$this->assertArrayHasKey('childId', $data);
$this->assertEquals('Child created', $data['message']);
}
public function testFusionEmojiNotFound(): void
{
$emojiRepo = $this->createMock(EmojiRepository::class);
$emojiRepo->method('find')->willReturn(null);
$this->client->request('GET', '/emoji/fusion/999/998');
$response = $this->client->getResponse();
$this->assertEquals(Response::HTTP_NOT_FOUND, $response->getStatusCode());
$data = json_decode($response->getContent(), true);
$this->assertEquals('One or both emojis not found', $data['error']);
}
}
Loading…
Cancel
Save