pull/92/head
Pierre Ferreira 1 year ago
commit 663ba3be9f

@ -0,0 +1,34 @@
kind: pipeline
type: docker
name: default
trigger:
event:
- push
steps:
- name: build
image: node:20
commands:
- cd cryptide_project
- npm install --legacy-peer-deps
- CI=false npm run build
- name: code-analysis
image: node:20
environment:
SONAR_TOKEN:
from_secret: SECRET_SONAR_LOGIN_CRYPTIDE
commands:
- export SONAR_SCANNER_VERSION=4.7.0.2747
- export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux
- curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip
- unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/
- export PATH=$SONAR_SCANNER_HOME/bin:$PATH
- export SONAR_SCANNER_OPTS="-server"
- sonar-scanner -D sonar.projectKey=Cryptid -D sonar.sources=. -D sonar.host.url=https://codefirst.iut.uca.fr/sonar
secrets: [SECRET_SONAR_LOGIN_CRYPTIDE]
settings:
sonar_host: https://codefirst.iut.uca.fr/sonar/
sonar_token:
from_secret: SECRET_SONAR_LOGIN_CRYPTIDE

8
.gitignore vendored

@ -42,3 +42,11 @@ bower_components
psd
thumb
sketch
### modules ###
yarn.lock
package-lock.json
# db
socialgraph.db

@ -1,2 +1,110 @@
# Cryptid
(:construction: ***Le readme n'est pas a sa version final et est susceptible de changer*** :construction:)
# Présentation :
Bienvenue dans notre jeu de déduction captivant, où l'intrigue et la malice se rejoignent dans une aventure palpitante ! Plongez-vous dans un monde de mystère et d'intrigue, où chaque interaction compte, et chaque indice vous rapproche de la vérité.Imaginez un graphique complexe où chaque sommet représente une personne, chaque axe une relation, et chaque détail compte. Vous êtes plongé dans un défi stimulant pour découvrir qui parmi ces individus est le mystérieux tueur. Chaque joueur détient un indice crucial, et seul le partage stratégique de ces indices vous mènera à la résolution du mystère. Explorez notre page de règles pour comprendre les subtilités du jeu, découvrez les indices qui peuvent vous guider, et élaborez des stratégies intelligentes pour identifier le coupable. Manipuler vos amis, afin d'être le premier à découvrir qui est le meurtrier ! Êtes-vous prêt à relever le défi et à démasquer le tueur caché dans le graphe ? Que l'enquête commence !
## Comment utiliser notre projet :
*Prérequis :* Avoir **git** et **npm** (et donc **Node.js**) sur son poste.
### Etape 1 : Récupérer le projet
Dans un premier terminal, exécutez les commandes suivantes :
```bash
git clone https://codefirst.iut.uca.fr/git/Crypteam/Cryptid.git
cd cryptide_project
npm i --force
```
### Etape 2 : Configurer le réseau
1. Pour jouer en distant, vous devez vous trouver sur le même réseau. (Il faut quand même éviter les réseaux tels qu' eduroam)
2. Récupérez votre adresse IP :
Sur **Windows**
> - Ouvrez l'invite de commandes (`cmd`) et tapez la commande `ipconfig`. Repérez la section de votre connexion sans fil et notez l'adresse IPv4.
Sur **MacOS** / **Linux**
> - Ouvrez le terminal et tapez la commande `ifconfig` ou `ip addr`. Recherchez la section de votre connexion sans fil et notez l'adresse IP.
### Etape 3 : Configurer les serveurs
1. **Ouvrez le fichier `./src/AdressSetup.ts` :**
- Localisez le fichier dans le répertoire de votre application.
2. **Modifiez l'adresse des serveurs :**
- Remplacez les adresses IP existantes par celle que vous avez notée à l'étape 2.
Exemple :
```typescript
// ./AdressSetup.ts
const ADRESSE_WEBSERVER = "http://{VOTRE_IP}:3002"
const ADRESSE_DBSERVER = "http://{VOTRE_IP}:3003"
const ADRESSE_WEBSITE = ""
export {ADRESSE_DBSERVER, ADRESSE_WEBSERVER, ADRESSE_WEBSITE}
```
3. **Ouvrez le fichier `./server/server.js` :**
- Localisez le fichier dans le répertoire de votre application.
4. **Modifiez les adresses qui peuvent accèder aux serveurs :**
- Ajoutez votre adresse notée à l'étape 2 dans le cors.
Exemple :
```typescript
const app = express();
const server = http.createServer(app);
const io = socketIO(server, {
cors: {
origin: ["http://{VOTRE_IP}:3000", "http://localhost:3000"], // Remplacez par l'URL de votre application React
methods: ["GET", "POST"],
credentials: true
}
});
```
5. **Apportez la même modification au fichier `./src/server/server.js` :**
- Une fois le fichier ouvert, appuyez-vous sur l'exemple précédent pour apporter les modifications necéssaire.
### Etape 4 : Démarrer les serveurs
1. **Ouvrez un second terminal :**
- Exécutez le script `./startServer.sh`.
### Etape 5 : Démarrer l'application
1. Revenez dans le premier terminal et exécutez la commande suivante :
```bash
npm start
```
2. L'application va s'ouvrir dans votre navigateur et vous pouvez jouez à notre jeu.
### Etape bonus : Jouez avec ces amis
1. Sur la première page de l'application, cliquez sur **Jouez** > **Créer une partie**.
2. Une fois dans le lobby, **copiez le lien** de la partie et l'envoyez à vos amis.
3. Lancez la partie et **amusez-vous** !
# ::construction_worker:: Développeurs
- Thomas Chazot : thomas.chazot@etu.uca.fr
- Pierre Ferreira : pierre.ferreira@etu.uca.fr
- Baptiste Marcel : baptise.marcel@etu.uca.fr
<div align="center">
<a href = "https://codefirst.iut.uca.fr/git/thomas.chazot2">
<img src="https://codefirst.iut.uca.fr/git/avatars/4ce5054250e693b04367d853b3c469b1?size=870" width="50" >
</a>
<a href = "https://codefirst.iut.uca.fr/git/pierre.ferreira">
<img src="https://codefirst.iut.uca.fr/git/avatars/edbacace5f621ae77077f206ebdcee27?size=870" width="50" >
</a>
<a href = "https://codefirst.iut.uca.fr/git/baptiste.marcel">
<img src="https://codefirst.iut.uca.fr/git/avatars/6b1f2a8b8f636d8f4d315b060075578f?size=870" width="50" >
</a>
© IUT - Auvergne
</div>

File diff suppressed because it is too large Load Diff

@ -10,11 +10,19 @@
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/lodash": "^4.14.200",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"bootstrap": "^5.3.2",
"cookie-parser": "^1.4.6",
"cookie-session": "^2.0.0",
"cors": "^2.8.5",
"express": "^4.18.2",
"express-session": "^1.17.3",
"file-saver": "^2.0.5",
"jszip": "^3.10.1",
"jzip": "^1.0.0",
"lodash": "^4.17.21",
"mysql": "^2.18.1",
"react": "^18.2.0",
"react-bootstrap": "^2.9.1",
"react-country-flag": "^3.1.0",
@ -27,6 +35,7 @@
"react-switch": "^7.0.0",
"socket.io": "^4.7.2",
"socket.io-client": "^4.7.2",
"sqlite3": "^5.1.6",
"typescript": "^5.2.2",
"vis-network": "^9.1.9",
"web-vitals": "^2.1.4"
@ -34,7 +43,7 @@
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"test": "jest --passWithNoTests",
"eject": "react-scripts eject"
},
"eslintConfig": {
@ -58,6 +67,14 @@
"devDependencies": {
"@types/file-saver": "^2.0.7",
"@types/react-router-hash-link": "^2.4.9",
"@types/uuid": "^9.0.7"
"@types/uuid": "^9.0.7",
"babel-jest": "^29.7.0"
},
"babel": {
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
]
}
}

@ -7,7 +7,7 @@ const app = express();
const server = http.createServer(app);
const io = socketIO(server, {
cors: {
origin: ["http://172.20.10.4:3000", "http://localhost:3000"], // Remplacez par l'URL de votre application React
origin: ["http://localhost:3000", "http://localhost:3000"], // Remplacez par l'URL de votre application React
methods: ["GET", "POST"],
credentials: true
}
@ -15,7 +15,6 @@ const io = socketIO(server, {
const map = new Map()
// ... le reste de votre configuration du serveur
server.listen(3002, () => {
console.log('Serveur Socket.IO écoutant sur le port 3002');
@ -29,34 +28,43 @@ io.on('connection', (socket) => {
});
socket.on("lobby joined", (room, player) =>{
if (player.type=="Human"){
console.log(player)
if (player.type=="User"){
socket.join(room)
}
if (map.get(room) == undefined){
map.set(room, [{type: player.type, id: socket.id, name: player.name}])
map.set(room, [{type: player.type, id: socket.id, pseudo: player.pseudo, profilePicture: player.profilePicture}])
}
else{
const tab = map.get(room)
for(let i = 0; i<tab.length; i++){
if (tab[i].id === socket.id && player.type==="Human"){
if (tab[i].id === socket.id && player.type==="User"){
tab.splice(i, 1)
}
}
if (player.type!=="Human"){
map.get(room).push({type: player.type, id: player.id, name: player.name})
if (player.type!=="User"){
map.get(room).push({type: player.type, id: player.id, pseudo: player.pseudo, profilePicture: player.profilePicture})
}
else{
map.get(room).push({type: player.type, id: socket.id, name: player.name})
map.get(room).push({type: player.type, id: socket.id, pseudo: player.pseudo, profilePicture: player.profilePicture})
}
}
io.to(room).emit("new player", map.get(room))
const playerArray = Array.from(map.entries()).map(([key, value]) => ({ key, value }))
const playerJson = JSON.stringify(playerArray);
io.to(socket.id).emit("request lobbies", playerJson)
})
socket.on("request lobbies", () => {
const playerArray = Array.from(map.entries()).map(([key, value]) => ({ key, value }))
const playerJson = JSON.stringify(playerArray);
io.to(socket.id).emit("request lobbies", playerJson)
})
socket.on("bot deleted", (bot, room) =>{
// map.set(room, map.get(room).filter(player => player.id !== bot.id));
const tab = map.get(room)
for(let i = 0; i<tab.length; i++){
if (tab[i].id === bot.id){
@ -64,6 +72,9 @@ io.on('connection', (socket) => {
}
}
io.to(room).emit("new player", map.get(room))
const playerArray = Array.from(map.entries()).map(([key, value]) => ({ key, value }))
const playerJson = JSON.stringify(playerArray);
io.to(socket.id).emit("request lobbies", playerJson)
})
@ -94,6 +105,9 @@ io.on('connection', (socket) => {
if (tab[i].id === socket.id){
tab.splice(i, 1)
io.to(k).emit("player left", tab, i)
if (tab.filter((p) => p.type=="User").length == 0){
map.delete(k)
}
}
}
}
@ -115,6 +129,10 @@ io.on('connection', (socket) => {
io.to(id).emit("put imossible grey")
})
socket.on("can't put square", (askingPlayer, room) => {
io.to(room).emit("can't put square" , askingPlayer)
})
socket.on("opacity activated", (id) => {
io.to(id).emit("opacity activated")
})

@ -0,0 +1,15 @@
#!/bin/bash
# Vérifier si l'adresse IP est fournie en tant que paramètre
if [ -z "$1" ]; then
echo "Usage: $0 <adresse_ip>"
exit 1
fi
# Stocker l'adresse IP fournie en tant que variable
adresse_ip="$1"
# Utiliser l'adresse IP dans la commande find avec Perl
find . -type f -exec perl -pi -e 's|http://[0-9.]+:([0-9]+)|http://localhost:$1|g' {} +
find . -type f -exec perl -pi -e "s|http://localhost:([0-9]+)|http://$adresse_ip:\$1|g" {} +

@ -0,0 +1,30 @@
#!/bin/sh
if lsof -Pi :3000 -sTCP:LISTEN -t >/dev/null; then
# Tuer le processus associé au port
pid=$(lsof -Pi :3000 -sTCP:LISTEN -t)
kill -9 $pid
fi
if lsof -Pi :3002 -sTCP:LISTEN -t >/dev/null; then
# Tuer le processus associé au port
pid=$(lsof -Pi :3002 -sTCP:LISTEN -t)
kill -9 $pid
fi
if lsof -Pi :3003 -sTCP:LISTEN -t >/dev/null; then
# Tuer le processus associé au port
pid=$(lsof -Pi :3003 -sTCP:LISTEN -t)
kill -9 $pid
fi
npm start &
node server/server.js &
node src/server/server.js

@ -0,0 +1,11 @@
const ADRESSE_WEBSERVER = "http://localhost:3002"
const ADRESSE_DBSERVER = "http://localhost:3003"
const tmp = ADRESSE_DBSERVER
const tmp2 = ADRESSE_WEBSERVER
const ADRESSE_WEBSITE = ""
export {ADRESSE_DBSERVER, ADRESSE_WEBSERVER, ADRESSE_WEBSITE}

@ -3,24 +3,29 @@ import React from 'react';
import { useState } from 'react';
import { IntlProvider } from 'react-intl';
import { GameProvider } from './Contexts/GameContext';
import { AuthProvider } from './Contexts/AuthContext';
/* Page */
import Home from './Pages/Home';
import Login from './Pages/LoginForm';
import SignUp from './Pages/SignUpForm';
import Play from './Pages/Play';
import Profile from './Pages/Profile';
import Lobby from './Pages/Lobby';
import InGame from './Pages/InGame';
import EndGame from './Pages/EndGame';
import InfoPage from './Pages/InfoPage';
import SoloGame from './Pages/SoloGame'; //! useless
import DeducGrid from './Pages/DeducGrid';
import Lobbies from './Pages/Lobbies';
/* Component */
import AppNavbar from './Components/NavBar';
/* service */
import SessionService from './services/SessionService';
/* nav */
import { BrowserRouter, Route, Routes, useLocation } from "react-router-dom";
@ -49,7 +54,7 @@ function App() {
const [locale, setLocale] = useState('fr');
//@ts-ignore
const changeLocale = (newLocale) => {
const changeLocale = async (newLocale) => {
setLocale(newLocale);
};
@ -65,8 +70,9 @@ function App() {
// <img src={logo} className="App-logo" alt="logo" />
// </header>
// </div>
<GameProvider>
<AuthProvider>
<GameProvider>
{/*@ts-ignore*/}
<IntlProvider locale={locale} messages={messages[locale]}>
<ThemeProvider>
@ -82,12 +88,15 @@ function App() {
<Route path="/game" element={<InGame locale={locale} changeLocale={changeLocale}/>}/>
<Route path="/info" element={<InfoPage locale={locale} changeLocale={changeLocale}/>} />
<Route path="/deduc" element={<DeducGrid/>} />
<Route path="/profile" element={<Profile/>} />
<Route path="/join" element={<Lobbies/>}/>
{/* <Route path="/solo" element={<SoloGame locale={locale} changeLocale={changeLocale} />}/> */}
</Routes>
</BrowserRouter>
</ThemeProvider>
</IntlProvider>
</GameProvider>
</AuthProvider>
);
}

@ -61,7 +61,7 @@ const ChoiceBar = () => {
{players.map((player, index) => (
player.id !== socket.id &&
<button key={index} className="choice-bar-button" onClick={() => askPlayer(player.id)} style={{ backgroundColor: theme.colors.primary }}>
{player.name}
{player.pseudo}
</button>
))}
</div>

@ -4,14 +4,16 @@ import GraphCreator from "../model/Graph/GraphCreator";
import "./GraphContainer.css";
import IndiceTesterFactory from "../model/Factory/IndiceTesterFactory";
import Person from "../model/Person";
import { Navigate, useLocation, useNavigate } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { useGame } from "../Contexts/GameContext";
import { socket } from "../SocketConfig"
import { colorToEmoji, positionToColor, positionToEmoji } from "../ColorHelper";
import { ColorToHexa } from "../model/EnumExtender";
import Bot from "../model/Bot";
import NodePerson from "../model/Graph/NodePerson";
import { useAuth } from "../Contexts/AuthContext";
import Indice from "../model/Indices/Indice";
import Pair from "../model/Pair";
interface MyGraphComponentProps {
onNodeClick: (shouldShowChoiceBar: boolean) => void;
@ -22,14 +24,20 @@ interface MyGraphComponentProps {
changecptTour: (newcptTour : number) => void
addToHistory: (message : string) => void
solo : boolean
isDaily : boolean
isEasy: boolean
setNetwork: (network: Network) => void
showLast: boolean
setNetworkEnigme: (networkEnigme: Map<number, Pair<Indice, boolean>[]>) => void
askedWrong: boolean
setAskedWrong: (askedWrong: boolean) => void
setPlayerIndex: (playerIndex: number) => void
}
let lastAskingPlayer = 0
let lastNodeId = -1
let first = true
let askedWrong = false
let askedWrongLocal = false
let mapIndexPersons: Map<number, Person[]> = new Map<number, Person[]>()
let touchedPlayer = -1
let botIndex = -1
@ -38,27 +46,56 @@ let lastSocketId= ""
let firstLap = true
let cptHistory = 0
let lastNodes: NodePerson[] = []
let cptEndgame = 0
let firstEnigme = true
let firstIndex = true
let endgame= false
let firstHistory = true
let cptSquare = 0
let cptOnAskedWrong = 0
const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleShowTurnBar, handleTurnBarTextChange, playerTouched, setPlayerTouched, changecptTour, solo, addToHistory, showLast, setNetwork}) => {
const MyGraphComponent: React.FC<MyGraphComponentProps> = ({onNodeClick, handleShowTurnBar, handleTurnBarTextChange, playerTouched, setPlayerTouched, changecptTour, solo, isDaily, isEasy, addToHistory, showLast, setNetwork, setNetworkEnigme, setPlayerIndex, askedWrong, setAskedWrong}) => {
let cptTour: number = 0
const { indices, indice, person, personNetwork, setNodeIdData, players, askedPersons, setActualPlayerIndexData, room, actualPlayerIndex, turnPlayerIndex, setTurnPlayerIndexData, setWinnerData } = useGame();
//* Gestion du temps :
let initMtn = 0
const {isLoggedIn, user, manager} = useAuth();
const { indices, indice, person, personNetwork, setNodeIdData, players, askedPersons, setActualPlayerIndexData, room, actualPlayerIndex, turnPlayerIndex, setTurnPlayerIndexData, setWinnerData, dailyEnigme, setNbCoupData, settempsData, setNetworkDataData, setSeedData} = useGame();
const params = new URLSearchParams(window.location.search);
const navigate = useNavigate();
const [lastIndex, setLastIndex] = useState(-1)
const [elapsedTime, setElapsedTime] = useState(0);
useEffect(() => {
// Démarrez le timer au montage du composant
const intervalId = setInterval(() => {
setElapsedTime((prevElapsedTime) => prevElapsedTime + 0.5);
settempsData(elapsedTime)
// Vérifiez si la durée est écoulée, puis arrêtez le timer
if (endgame) {
clearInterval(intervalId);
}
}, 500);
// Nettoyez l'intervalle lorsque le composant est démonté
return () => clearInterval(intervalId);
}, [elapsedTime, endgame]);
useEffect(() =>{
touchedPlayer=playerTouched
if (touchedPlayer == -1){
if (!askedWrong){
if (!askedWrongLocal){
socket.emit("put correct background", socket.id)
}
}
else if (touchedPlayer < players.length && touchedPlayer>=0){
if(!askedWrong){
if(!askedWrongLocal){
socket.emit("put correct background", socket.id)
socket.emit("put grey background", socket.id, touchedPlayer)
}
@ -72,6 +109,7 @@ let cptTour: number = 0
useEffect(() => {
const tab: NodePerson[] = []
for(const n of lastNodes.reverse()){
//@ts-ignore
if (!tab.find((node) => node.id == n.id)){
tab.push(n)
if (tab.length > players.length * 2) break
@ -88,6 +126,11 @@ let cptTour: number = 0
}, [showLast])
let playerIndex: number = turnPlayerIndex
if (firstIndex){
firstIndex=false
setPlayerIndex(playerIndex)
}
let index = 0
for (let i=0; i<players.length; i++){
if(players[i].id == socket.id){
@ -103,8 +146,10 @@ let cptTour: number = 0
botIndex = lastIndex
if (personNetwork!=null){
const [choosedPlayerIndex, personIndex] = bot.playRound(personNetwork, players)
//@ts-ignore
const person = personNetwork.getPersons().find((p) => p.getId() == personIndex)
if (choosedPlayerIndex == players.length && person != undefined){
//@ts-ignore
console.log(lastIndex + " All in sur => " + personNetwork.getPersons().find((p) => p.getId() == personIndex)?.getName())
let nextPlayerIndex = lastIndex + 1
if (nextPlayerIndex == players.length){
@ -130,6 +175,11 @@ let cptTour: number = 0
if(!works){
socket.emit("node checked", personIndex, works, playerIndex, room, nextPlayerIndex)
const ind = bot.placeSquare(personNetwork, players)
if (ind == -1 ){
addToHistory(lastIndex.toString() + "177")
socket.emit("can't put square", lastIndex, room)
return
}
console.log(lastIndex + " pose carré sur " + personNetwork.getPersons()[ind].getName())
playerIndex = lastIndex + 1
if(playerIndex == players.length){
@ -160,6 +210,11 @@ let cptTour: number = 0
console.log(lastIndex + " interroge " + choosedPlayerIndex + " a propos de " + person.getName() + " et dit non")
socket.emit("node checked", personIndex, false, choosedPlayerIndex, room, lastIndex)
const ind = bot.placeSquare(personNetwork, players)
if (ind == -1 ){
addToHistory(lastIndex.toString() + "212")
socket.emit("can't put square", playerIndex, room)
return
}
console.log(lastIndex + " pose carré sur " + personNetwork.getPersons()[ind].getName())
playerIndex = lastIndex + 1
if(playerIndex == players.length){
@ -175,6 +230,11 @@ let cptTour: number = 0
const tester = IndiceTesterFactory.Create(indices[choosedPlayerIndex])
if (!tester.Works(person)){
const ind = bot.placeSquare(personNetwork, players)
if (ind == -1 ){
addToHistory(lastIndex.toString() + "232")
socket.emit("can't put square", playerIndex, room)
return
}
console.log(lastIndex + " pose carré sur " + personNetwork.getPersons()[ind].getName())
playerIndex = lastIndex + 1
if(playerIndex == players.length){
@ -194,7 +254,7 @@ let cptTour: number = 0
if (first){
first = false
endgame= false
if (!solo){
for(let i = 0; i<indices.length; i++){
mapIndexPersons.set(i, [])
@ -215,12 +275,32 @@ let cptTour: number = 0
}
}
//* fonction qui reinitialise le graphe
const resGraph = () => { //? comment accéder au nework ??
const savedGraphStateString = localStorage.getItem('graphState');
if (savedGraphStateString !== null) {
const savedGraphState = JSON.parse(savedGraphStateString);
//network.setData(savedGraphState);
} else {
// La clé 'graphState' n'existe pas dans le localStorage, prenez une action en conséquence.
console.log("ayoooooo");
}
};
useEffect(() => {
if (personNetwork == null){
return
}
const graph = GraphCreator.CreateGraph(personNetwork)
let n = graph.nodesPerson;
let e = graph.edges;
const graphState = { n, e };
// Sauvegarder l'état dans localStorage
localStorage.setItem('graphState', JSON.stringify(graphState));
const container = document.getElementById('graph-container');
if (!container) {
console.error("Container not found");
@ -239,6 +319,7 @@ let cptTour: number = 0
direction: 'LR', // LR (Left to Right) ou autre selon votre préférence
sortMethod: 'hubsize'
},
distanceMin: 500, // Set the minimum distance between nodes
//randomSeed: 2
},
physics: {
@ -247,17 +328,55 @@ let cptTour: number = 0
gravitationalConstant: -1000,
springConstant: 0.001,
springLength: 100
},
solver: "repulsion",
repulsion: {
nodeDistance: 100 // Put more distance between the nodes.
}
}
};
const networkData = { nodes: nodes, edges: graph.edges };
const network = new Network(container, networkData, initialOptions);
network.stabilize();
setNetwork(network)
setSeedData(network.getSeed())
if (isDaily){
setNetworkEnigme(dailyEnigme)
if (!isEasy){
dailyEnigme.forEach((pairs, index) => {
pairs.forEach((pair) => {
//@ts-ignore
const i = indices.findIndex((indice) => pair.first.getId() === indice.getId())
//@ts-ignore
const node = networkData.nodes.get().find((n) => index == n.id)
if (node != undefined){
networkData.nodes.update({id: node.id, label: node.label + positionToEmoji(i, pair.second)})
}
})
});
}
else{
if (firstHistory){
firstHistory=false
indices.forEach((indice, index) => {
addToHistory("Indice " + positionToEmoji(index, true) + " : " + indice.ToString("fr"))
})
}
}
}
socket.on("reset graph", () => {
console.log("reset graph")
initialOptions.physics.enabled = true
network.setOptions(initialOptions)
})
if (!solo){
socket.on("asked all", (id) =>{
//@ts-ignore
const pers = personNetwork.getPersons().find((p) => p.getId() == id)
if (pers!=undefined){
askedPersons.push(pers)
@ -265,7 +384,9 @@ let cptTour: number = 0
})
socket.on("opacity activated", () => {
//@ts-ignore
nodes.forEach(node => {
//@ts-ignore
if (!lastNodes.find((n) => n.id == node.id)){
networkData.nodes.update({id: node.id, opacity: 0.2})
}
@ -273,23 +394,22 @@ let cptTour: number = 0
})
socket.on("opacity deactivated", () => {
//@ts-ignore
nodes.forEach(node => {
networkData.nodes.update({id: node.id, opacity: 1})
});
})
socket.on("reset graph", () => {
initialOptions.physics.enabled = true
network.setOptions(initialOptions)
})
socket.on("node checked",(id, works, askedIndex, newPlayerIndex, socketId) => {
console.log(newPlayerIndex)
//@ts-ignore
const node = nodes.get().find((n) => id == n.id)
if (node!=undefined){
onNodeClick(false)
playerIndex = newPlayerIndex
setPlayerIndex(playerIndex)
//@ts-ignore
if (mapIndexPersons.get(askedIndex)?.find((p) => p.getId() == id) == undefined){
//@ts-ignore
const p = personNetwork.getPersons().find((p)=> p.getId() == id)
const tab = mapIndexPersons.get(askedIndex)
if (p!=undefined && tab != undefined){
@ -309,7 +429,7 @@ let cptTour: number = 0
cptHistory++
if (cptHistory % 2 == 0){
lastNodes.push(node)
addToHistory(players[askedIndex].name + " à mis un " + positionToEmoji(askedIndex, works) + " à " + personNetwork.getPersons()[id].getName())
addToHistory(players[askedIndex].pseudo + " à mis un " + positionToEmoji(askedIndex, works) + " à " + personNetwork.getPersons()[id].getName())
}
}
@ -332,11 +452,61 @@ let cptTour: number = 0
})
socket.on("asked wrong", () =>{
askedWrong = true
cptSquare++
if (cptSquare % 2 == 0){
if (indice==null){
return
}
const tester = IndiceTesterFactory.Create(indice)
const tabPossible: Person[] = []
for(const person of personNetwork.getPersons()){
//@ts-ignore
const node = nodes.get().find((n) => n.id == person.getId())
if (node != undefined) {
let isSquare = false
players.forEach((p, index) => {
if (node.label.includes(positionToEmoji(index, false))){
isSquare=true
}
})
if (!tester.Works(person) && !isSquare){
tabPossible.push(person)
}
}
}
if (tabPossible.length>0){
askedWrongLocal= true
setAskedWrong(true)
askedWrongBot=true
handleShowTurnBar(true)
handleTurnBarTextChange("Mauvais choix, posez un carré !")
socket.emit("put grey background", socket.id, actualPlayerIndex)
}
else{
socket.emit("can't put square", actualPlayerIndex, room)
}
}
})
socket.on("can't put square", (askingPlayer) => {
cptOnAskedWrong ++
if (cptOnAskedWrong % 2 == 0){
addToHistory(players[askingPlayer].pseudo + " ne peut plus poser de carré")
playerIndex = askingPlayer + 1
if(playerIndex == players.length){
playerIndex = 0
}
setPlayerIndex(playerIndex)
setLastIndex(playerIndex)
if (playerIndex === actualPlayerIndex){
handleTurnBarTextChange("À vous de jouer")
handleShowTurnBar(true)
}
else{
handleShowTurnBar(false)
socket.emit("put correct background", socket.id)
}
}
})
@ -344,6 +514,7 @@ let cptTour: number = 0
if (askingPlayer.id !== lastAskingPlayer || nodeId !== lastNodeId ){
lastAskingPlayer = askingPlayer.id
lastNodeId = nodeId
//@ts-ignore
const pers = personNetwork.getPersons().find((p) => p.getId() == nodeId)
if (pers!=undefined){
if (askedPersons.includes(pers)){
@ -352,10 +523,10 @@ let cptTour: number = 0
}
else{
askedPersons.push(pers)
//@ts-ignore
const node = nodes.get().find((n) => nodeId == n.id)
if (node != undefined && indice != null){
var tester = IndiceTesterFactory.Create(indice)
let maybe = actualPlayerIndex
if (tester.Works(pers)){
playerIndex = playerIndex + 1
if(playerIndex == players.length){
@ -364,10 +535,6 @@ let cptTour: number = 0
socket.emit("node checked", nodeId, true, actualPlayerIndex, room, playerIndex)
}
else{
maybe = actualPlayerIndex - 1
if(maybe == 0){
maybe = players.length - 1
}
let index = players.findIndex((p) => p.id == askingPlayer.id)
if (players[index] instanceof Bot){
index = playerIndex + 1
@ -379,7 +546,6 @@ let cptTour: number = 0
socket.emit("node checked", nodeId, false, actualPlayerIndex, room, index)
socket.emit("asked wrong", askingPlayer, room)
}
}
}
}
@ -391,9 +557,11 @@ let cptTour: number = 0
else {
if (firstLap){
firstLap=false
if (!isDaily){
addToHistory("<----- [Tour " + 1 +"/"+networkData.nodes.length + "] ----->");
}
}
}
socket.on("put correct background", () =>{
@ -414,13 +582,25 @@ let cptTour: number = 0
}
}
else if(indice != null){
//Pour poser un carré
const tester = IndiceTesterFactory.Create(indice)
for(const person of personNetwork.getPersons().filter((p) => tab.includes(p) || tester.Works(p))){
for(const person of personNetwork.getPersons()){
//@ts-ignore
const node = nodes.get().find((n) => n.id == person.getId())
if (node == undefined) continue
let isSquare = false
players.forEach((p, index) => {
if (node.label.includes(positionToEmoji(index, false))){
isSquare=true
}
})
if (tab.includes(person) || tester.Works(person) || isSquare){
networkData.nodes.update({id: person.getId(), color: "#808080"})
}
}
}
}
}
})
socket.on("put imossible grey", ()=>{
@ -428,6 +608,7 @@ let cptTour: number = 0
const tabNodes: any = []
const tester = IndiceTesterFactory.Create(indice)
for (const pers of personNetwork.getPersons()){
//@ts-ignore
const node = nodes.get().find((n) => pers.getId() == n.id)
if (node != undefined){
for(let i=0; i<players.length; i++){
@ -446,15 +627,51 @@ let cptTour: number = 0
})
socket.on("end game", (winnerIndex) =>{
if (cptEndgame % 2 == 0){
cptEndgame++;
const currentPlayer = players[actualPlayerIndex];
const winner = players[winnerIndex];
setNetworkDataData(networkData)
setNodeIdData(-1)
setActualPlayerIndexData(-1)
setLastIndex(-1)
setPlayerTouched(-1)
setWinnerData(players[winnerIndex])
setWinnerData(winner)
setElapsedTime(0)
first = true
cptHistory = 0
askedWrong=false
askedWrongLocal=false
setAskedWrong(false)
askedWrongBot=false
endgame = true
firstHistory=true
try{
if(isLoggedIn){
if(!solo){
if(user && user.onlineStats){
// console.log("nbGames: " + user.onlineStats.nbGames + " nbWins: " + user.onlineStats.nbWins);
if(winner.id === currentPlayer.id){
// Ajouter une victoire
user.onlineStats.nbWins = null ? user.onlineStats.nbWins = 1 : user.onlineStats.nbWins += 1;
}
// Update les stats
user.onlineStats.nbGames = null ? user.onlineStats.nbGames = 1 : user.onlineStats.nbGames += 1;
user.onlineStats.ratio = user.onlineStats.nbWins / user.onlineStats.nbGames;
manager.userService.updateOnlineStats(user.pseudo, user.onlineStats.nbGames, user.onlineStats.nbWins, user.onlineStats.ratio);
}
else{
console.error("User not found");
}
}
}
}
catch(e){
console.log(e);
}
finally{
socket.off("end game")
socket.off("asked all")
socket.off("opacity activated")
@ -469,6 +686,8 @@ let cptTour: number = 0
socket.off("put imossible grey")
navigate("/endgame")
}
}
})
@ -483,7 +702,7 @@ let cptTour: number = 0
}
if (a==indices.length){
//networkData.nodes.update({id: p.getId(), label: p.getName() + "\n🔵"})
console.log(p)
//console.log(p)
}
});
@ -505,11 +724,19 @@ let cptTour: number = 0
setNodeIdData(params.nodes[0])
// addToHistory("Le joueur a cliqué") //! TEST DEBUG
if (!solo){
if (askedWrong){
if (askedWrongLocal){
const person = personNetwork?.getPersons().find((p) => p.getId() == params.nodes[0])
if (person !== undefined && indice !== null){
//@ts-ignore
const node = nodes.get().find((n) => n.id == params.nodes[0])
if (person !== undefined && indice !== null && node!=undefined){
const tester = IndiceTesterFactory.Create(indice)
if (!tester.Works(person) && !askedPersons.includes(person)){
let isSquare = false
players.forEach((p, index) => {
if (node.label.includes(positionToEmoji(index, false))){
isSquare=true
}
})
if (!tester.Works(person) && !askedPersons.includes(person) && !isSquare){
playerIndex = actualPlayerIndex + 1
if(playerIndex == players.length){
playerIndex = 0
@ -518,7 +745,8 @@ let cptTour: number = 0
socket.emit("put correct background", socket.id)
touchedPlayer=-1
askedPersons.push(person)
askedWrong = false
askedWrongLocal=false
setAskedWrong(false)
}
}
}
@ -527,6 +755,7 @@ let cptTour: number = 0
if (players[touchedPlayer] instanceof Bot){
const ind = indices[touchedPlayer]
const test = IndiceTesterFactory.Create(ind)
//@ts-ignore
const person = personNetwork?.getPersons().find((p) => p.getId() == params.nodes[0])
if (person != undefined){
if (test.Works(person)){
@ -545,8 +774,9 @@ let cptTour: number = 0
}
}
else{
if (touchedPlayer > 0){
if (touchedPlayer >= 0){
console.log(touchedPlayer)
//@ts-ignore
socket.emit("ask player", params.nodes[0], players[touchedPlayer].id, players.find((p) => p.id === socket.id, actualPlayerIndex))
socket.emit("put correct background", socket.id)
touchedPlayer=-1
@ -556,6 +786,7 @@ let cptTour: number = 0
}
else if(playerIndex == actualPlayerIndex && touchedPlayer==players.length){
botIndex = -1
//@ts-ignore
const person = personNetwork?.getPersons().find((p) => p.getId() == params.nodes[0])
if (person != undefined){
const indiceTester = IndiceTesterFactory.Create(indices[actualPlayerIndex])
@ -606,6 +837,7 @@ let cptTour: number = 0
}
}
else{
//@ts-ignore
const person = personNetwork?.getPersons().find((p) => p.getId() == params.nodes[0]) //person sélectionnée
if (person != undefined){
let index =0
@ -613,6 +845,7 @@ let cptTour: number = 0
for (const i of indices){
const tester = IndiceTesterFactory.Create(i)
const test = tester.Works(person)
//@ts-ignore
const node = nodes.get().find((n) => params.nodes[0] == n.id)
if (node!=undefined){
if (!node.label.includes(positionToEmoji(index, test))){
@ -622,7 +855,32 @@ let cptTour: number = 0
works = false
}
if (index == indices.length - 1 && works){
navigate("/endgame")
if (user!=null){
setWinnerData(user)
setNetworkDataData(networkData)
}
cptTour ++;
setNbCoupData(cptTour)
setElapsedTime(0)
endgame = true
try{
if(user && user.soloStats){
user.soloStats.nbGames = null ? user.soloStats.nbGames = 1 : user.soloStats.nbGames += 1;
if(cptTour < user.soloStats.bestScore || user.soloStats.bestScore == 0 || user.soloStats.bestScore == null){
user.soloStats.bestScore = cptTour;
}
user.soloStats.avgNbTry = (user.soloStats.avgNbTry * (user.soloStats.nbGames - 1) + cptTour) / user.soloStats.nbGames;
manager.userService.updateSoloStats(user.pseudo, user.soloStats.nbGames, user.soloStats.bestScore, user.soloStats.avgNbTry);
}
}
catch(error){
console.log(error);
}
navigate("/endgame?solo=true&daily=" + isDaily)
}
}
@ -631,7 +889,6 @@ let cptTour: number = 0
}
addToHistory(person.getName() + " n'est pas le tueur !"); //TODO préciser le nombre d'indice qu'il a de juste
//TODO METTRE LA WIN CONDITION ICI AVEC LE MERGE
cptTour ++; // On Incrémente le nombre de tour du joueur
const tour = cptTour+1;
addToHistory("<----- [Tour " + tour +"/"+networkData.nodes.length + "] ----->");

@ -0,0 +1,26 @@
import React from 'react';
import { NavDropdown } from 'react-bootstrap';
import LanguageNavItem from './LangNavItem';
import { HiLanguage } from 'react-icons/hi2';
// @ts-ignore
const LangDropdown = ({ changeLocale }) => {
return (
<NavDropdown title={<HiLanguage/>} id="language-dropdown" align='end' drop='down-centered'>
<LanguageNavItem
countryCode="FR"
languageKey="languageSelector.french"
onClick={() => changeLocale('fr')}
/>
<LanguageNavItem
countryCode="GB"
languageKey="languageSelector.english"
onClick={() => changeLocale('en')}
/>
{/* Ajoutez d'autres langues selon vos besoins */}
</NavDropdown>
);
};
export default LangDropdown;

@ -0,0 +1,21 @@
import React from 'react';
import { NavDropdown } from 'react-bootstrap';
import { FormattedMessage } from 'react-intl';
import ReactCountryFlag from 'react-country-flag';
/*@ts-ignore*/
const LanguageNavItem = ({ countryCode, languageKey, onClick }) => {
return (
<NavDropdown.Item onClick={onClick}>
<ReactCountryFlag
className="custom-flag"
countryCode={countryCode}
svg
style={{ margin: 'auto 10px 3px auto' }}
/>
<FormattedMessage id={languageKey} />
</NavDropdown.Item>
);
};
export default LanguageNavItem;

@ -0,0 +1,92 @@
import React, { useEffect, useState } from 'react';
import { useTheme } from '../Style/ThemeContext';
import Player from '../model/Player';
import { Link } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import Modal from 'react-bootstrap/Modal';
import Offcanvas from 'react-bootstrap/Offcanvas';
import Button from 'react-bootstrap/Button';
import { socket } from '../SocketConfig';
interface LobbyContainerProps {
roomNum : string
HeadPlayer : Player
nbPlayer : number
//? mettre un "nbplayermax" si le nombre de joueur max peut etre fixé ?
}
const LobbyContainer: React.FC<LobbyContainerProps> = ({roomNum, HeadPlayer, nbPlayer}) => {
const theme=useTheme();
const navigate = useNavigate();
const dest = '/lobby?room=' + roomNum;
//* Modal
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const handleContainerClick = () => {
if (show){
handleClose()
}
else{
if (nbPlayer < 6) {
socket.off("request lobbies")
navigate(dest);
} else {
handleShow()
//alert('La salle est pleine. Impossible de rejoindre.');
}
}
};
let stylefull;
let colorfull;
if (nbPlayer >= 6) {
stylefull = "darkred"
colorfull = "darkred"
}
else {
stylefull = "whitesmoke"
colorfull = "black"
}
return(
<div className='lobbyMainContainer' onClick={handleContainerClick} style={{borderColor:stylefull}}>
<header style={{height:'20%', display:'flex', justifyContent:'end'}}>
<h6><i>Room : {roomNum}</i></h6>
</header>
<hr/>
<h3><b>{HeadPlayer.pseudo}</b></h3>
<div style={{display:'flex', justifyContent:'end', alignItems:'end'}}>
<h2 style={{color:colorfull}}>{nbPlayer}/6</h2>
</div>
{/*
<Button onClick={handleContainerClick} variant='danger'>
click
</Button> */}
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Salle pleine</Modal.Title>
</Modal.Header>
<Modal.Body>La salle est pleine, il est impossible d'y aller pour le moment !</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Ok
</Button>
</Modal.Footer>
</Modal>
</div>
);
}
export default LobbyContainer;

@ -1,4 +1,3 @@
.custom-navbar {
/* background-color: #85C9C2; */
color: #fff;
@ -15,17 +14,14 @@
border-radius: 15px;
}
.navbar-title-dd a {
color: #fff;
border-radius: 15;
border-radius: 15px;
padding: 10px 20px; /* Ajustez le rembourrage selon vos préférences */
text-decoration: none; /* Pour supprimer le soulignement des liens */
display: inline-block; /* Pour que les styles de fond s'appliquent aux liens */
margin-right: 10px; /* Pour ajouter de l'espace entre les boutons */
}
.centerdiv {
display: flex;
justify-content: center;

@ -1,4 +1,4 @@
import React from 'react';
import React, {useEffect, useState} from 'react';
/* Naviagtion */
import { Navbar, Container, Nav, NavDropdown } from 'react-bootstrap';
@ -7,30 +7,43 @@ import { Navbar, Container, Nav, NavDropdown } from 'react-bootstrap';
import { FormattedMessage } from 'react-intl';
/* Icon */
import { BiLogInCircle } from 'react-icons/bi';
import { BiDoorOpen, BiLogInCircle } from 'react-icons/bi';
import { BsFillPersonPlusFill } from 'react-icons/bs';
import { HiLanguage } from 'react-icons/hi2';
/* Images */
import logo from '../res/img/logo2_preview_rev_1.png';
/* Components */
import ReactCountryFlag from "react-country-flag"
import LanguageNavItem from './LangNavItem';
import LangDropDown from './LangDropDown';
/* Style */
import './NavBar.css';
/* Style */
import { useTheme } from '../Style/ThemeContext';
import { useAuth } from '../Contexts/AuthContext';
import { useNavigate } from 'react-router-dom';
// @ts-ignore
function AppNavbar({changeLocale}) {
const theme = useTheme();
const {user, isLoggedIn, logout} = useAuth();
const navigate = useNavigate();
function navigateToProfile(){
navigate("/profile")
}
function navigateToHome(){
navigate("/")
}
return (
<Navbar expand="lg" className="custom-navbar" style={{ backgroundColor: theme.colors.primary }}>
<Container>
<Navbar.Brand href="/">
<Navbar.Brand onClick={navigateToHome}>
<img src={logo} alt="logo" className="logo" />
</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
@ -45,40 +58,40 @@ function AppNavbar({changeLocale}) {
</Nav>
<div className='leftdiv'>
<Nav className="ml-auto navbar-title-dd">
<Nav.Link href="login" className='navbar-title-dd' style={{ backgroundColor: theme.colors.secondary }}>
{isLoggedIn ? (
<NavDropdown
title={<span style={{ color: theme.colors.text }}>Menu <BiDoorOpen /></span>}
id="basic-nav-dropdown"
align="end"
drop='down-centered'
>
<NavDropdown.Item onClick={navigateToProfile}>Profil</NavDropdown.Item>
<LanguageNavItem
countryCode="FR"
languageKey="languageSelector.french"
onClick={() => changeLocale('fr')}
/>
<LanguageNavItem
countryCode="GB"
languageKey="languageSelector.english"
onClick={() => changeLocale('en')}
/>
<NavDropdown.Divider />
<NavDropdown.Item onClick={logout}>Déconnexion</NavDropdown.Item>
</NavDropdown>
) : (
<>
<Nav.Link href="/login" className='navbar-title-dd' style={{ backgroundColor: theme.colors.secondary }}>
<BiLogInCircle />
<FormattedMessage id="log_in" />
</Nav.Link>
<Nav.Link href="signup" className='navbar-title-dd' style={{ backgroundColor: theme.colors.secondary }}>
<Nav.Link href="/signup" className='navbar-title-dd' style={{ backgroundColor: theme.colors.secondary }}>
<BsFillPersonPlusFill />
<FormattedMessage id="sign_up" />
</Nav.Link>
</Nav>
<Nav className="me-auto">
<NavDropdown
title={<span><HiLanguage /></span>}
className="navbar-title" id="basic-nav-dropdown">
<NavDropdown.Item onClick={() => changeLocale('fr')}>
<ReactCountryFlag countryCode="FR"
svg
style={{
width: '30px',
height: '20px',
margin: 'auto 10px 3px auto',
}}/>
<FormattedMessage id="languageSelector.french"/>
</NavDropdown.Item>
<NavDropdown.Item onClick={() => changeLocale('en')}>
<ReactCountryFlag countryCode="GB"
svg
style={{
width: '30px',
height: '20px',
margin: 'auto 10px 3px auto',
}}/>
<FormattedMessage id="languageSelector.english"/>
</NavDropdown.Item>
</NavDropdown>
<LangDropDown changeLocale={changeLocale}/>
</>
)}
</Nav>
</div>
</Navbar.Collapse>

@ -17,27 +17,51 @@ interface PlayerStatusProps {
setPlayerTouched: (newPlayerTouch: number) => void;
playerTouched: number
showCircle: boolean
playerIndex: number
askedWrong: boolean
}
let touchedPlayer = -1
//@ts-ignore
const PersonStatus: React.FC<PlayerStatusProps> = ({img = Person, state= Person, name = "Dummy", index, playerTouched, setPlayerTouched, showCircle}) => {
const PersonStatus: React.FC<PlayerStatusProps> = ({img = Person, state= Person, name = "Dummy", index, playerTouched, setPlayerTouched, showCircle, playerIndex, askedWrong}) => {
const theme=useTheme();
const {players} = useGame()
const {players, actualPlayerIndex} = useGame()
if (players[index] instanceof Bot){
img = BotImg
}
const [buffer, setBuffer] = useState("")
const [touchedPlayer, setTouchedPlayer] = useState(-2)
useEffect(() =>{
setTouchedPlayer(playerTouched)
}, [playerTouched])
let IsActualPlayer = index != actualPlayerIndex
useEffect(() => {
if (playerIndex===index){
setBuffer('solid 1px green')
}
else{
setBuffer('')
}
}, [playerIndex])
function onTouch(){
if (IsActualPlayer && !askedWrong){
setPlayerTouched(index)
}
}
return (
<div className='centerDivV' onClick={() => setPlayerTouched(index)}>
<div style={{border:buffer}}>
<div className='centerDivV' onClick={() => onTouch()}>
<img src={img} alt="player" height="60" width="60"/>
<h5>{name}</h5>
{(touchedPlayer == index && showCircle) ?(
{IsActualPlayer && (
(touchedPlayer == index && showCircle) ?(
<div className='statusDiv' style={{ backgroundColor: "gold" }}>
<img src={state} alt="state" height="30" width="30"/>
</div>
@ -46,9 +70,11 @@ const PersonStatus: React.FC<PlayerStatusProps> = ({img = Person, state= Person,
<div className='statusDiv' style={{ backgroundColor: theme.colors.primary }}>
<img src={state} alt="state" height="30" width="30"/>
</div>
)
)}
</div>
</div>
);
}

@ -47,7 +47,7 @@ const PlayerItemList:React.FC<MyPlayerItemListProps> =({ player, room }) => {
<div className='item-horizontal-div'>
<div>
<img src={pdp} alt='player-image' height='100' width='100' />
<h4>{player.name}</h4>
<h4>{player.pseudo}</h4>
</div>
{isBot && (
<Button className='suprButton' onClick={delBot} variant="danger">

@ -1,6 +1,6 @@
import React from 'react';
import { colorToEmoji, positionToColor } from '../ColorHelper';
import { colorToEmoji, positionToColor, positionToEmoji } from '../ColorHelper';
import Player from '../model/Player';
import { useTheme } from '../Style/ThemeContext';
import PersonStatus from './PersonStatus';
@ -14,18 +14,38 @@ interface PlayerListProps {
players: Player[];
playerTouched: number
setPlayerTouched: (newPlayerTouch: number) => void;
playerIndex: number
askedWrong: boolean
}
const PlayerList: React.FC<PlayerListProps> = ({ players, playerTouched, setPlayerTouched}) => {
const PlayerList: React.FC<PlayerListProps> = ({ players, playerTouched, setPlayerTouched, playerIndex, askedWrong}) => {
const theme = useTheme();
function askEveryone(){
if (!askedWrong){
setPlayerTouched(players.length)
}
}
return (
<div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '16px' }}>
{
//@ts-ignore
players.map((player, index) => (
player.id!=socket.id && <PersonStatus img={Person} state={Person} key={index} name={player.name + " " + colorToEmoji(positionToColor(index), true)} playerTouched={playerTouched} setPlayerTouched={setPlayerTouched} index={index} showCircle={true}/>
//player.id!=socket.id &&
<PersonStatus img={player.profilePicture}
state={Person}
key={index}
name={player.pseudo
+ " " +
positionToEmoji(index, true)}
playerTouched={playerTouched}
setPlayerTouched={setPlayerTouched}
index={index}
showCircle={true}
playerIndex={playerIndex}
askedWrong={askedWrong}/>
))
}
</div>
@ -45,7 +65,7 @@ const PlayerList: React.FC<PlayerListProps> = ({ players, playerTouched, setPlay
border: "solid 1px",
textAlign: "center",
padding: "10px"}}
onClick={() => setPlayerTouched(players.length)}>Ask everyone</button>
onClick={() => askEveryone()}>Ask everyone</button>
):
(
<button style={{
@ -55,7 +75,7 @@ const PlayerList: React.FC<PlayerListProps> = ({ players, playerTouched, setPlay
border: "solid 1px",
textAlign: "center",
padding: "10px"}}
onClick={() => setPlayerTouched(players.length)}>Ask everyone</button>
onClick={() => askEveryone()}>Ask everyone</button>
)
}
</div>

@ -0,0 +1,52 @@
import React, { useState } from 'react';
import '../Pages/Profile.css'
import dl from '../res/icon/download.png'
import defaultImg from '../res/img/Person.png'
import { useAuth } from '../Contexts/AuthContext';
//@ts-ignore
const ProfilePDP = () => {
const [selectedFile, setSelectedFile] = useState(null);
const {user} = useAuth()
// @ts-ignores
const handleFileChange = (event) => {
let file = event.target.files[0];
setSelectedFile(file);
if (file) {
const pdpUrl = URL.createObjectURL(file);
if (user!=null){
user.profilePicture = pdpUrl
}
}
};
return (
<div className='mainPDPContainer'>
{selectedFile ? (
<div >
{/* @ts-ignore */}
{/* <p>Selected File: {selectedFile.name}</p> */}
<img src={URL.createObjectURL(selectedFile)} alt="Preview" className='imgContainer' width='100px' height='100px' />
</div>
) : (
<div >
<img src={user?.profilePicture} alt="Preview" className='imgContainer' width='100px' height='100px' />
</div>
)}
<div className="parent">
<div className="file-upload">
<img src={dl} alt="upload" width='35px' height='35px'/>
{/* <h6>Cliquer ici pour ajouter une image</h6> */}
{/* <p>Taille recommandée : 100px</p> */}
<input type="file" accept="image/*" onChange={handleFileChange}/>
</div>
</div>
{/* <input type="file" accept="image/*" onChange={handleFileChange} /> */}
</div>
);
};
export default ProfilePDP;

@ -20,10 +20,14 @@ import Col from 'react-bootstrap/Col';
/* Component */
import ButtonImgNav from './ButtonImgNav';
import User from '../model/User';
/* Types */
//@ts-ignore
function ScoreBoard({Player = null}) {
const ScoreBoard: React.FC<{ Player: User }> = ({ Player }) => {
const theme=useTheme();
return (
// <div className='LeaderBoardiv'>
<div className='LeaderBoardiv'>
@ -31,9 +35,36 @@ function ScoreBoard({Player = null}) {
defaultActiveKey="daily"
id="ScoreBoard"
className="mb-3">
<Tab eventKey="perso" title="Vos Stats" disabled>
Tab content for Contact
<Tab eventKey="perso" title="Vos Stats" disabled = { !Player.pseudo.startsWith("Guest_") ? false : true}>
<Container fluid>
<Row>Stats en solo :</Row>
<Row>
<Col sm={10}>Partie Jouées :</Col>
<Col className='leftRow'>{Player !== null ? Player.soloStats.nbGames : "0"}</Col>
</Row>
<Row>
<Col sm={10}>Best-Score :</Col>
<Col className='leftRow'>{Player !== null ? Player.soloStats.bestScore : "0"}</Col>
</Row>
<Row>
<Col sm={10}>Moyenne d'essai :</Col>
<Col className='leftRow'>{Player !== null ? Player.soloStats.avgNbTry : "0"}</Col>
</Row>
<hr/>
<Row>Stats en ligne :</Row>
<Row>
<Col sm={10}>Partie jouée :</Col>
<Col className='leftRow'>{Player !== null ? Player.onlineStats.nbGames : "0"}</Col>
</Row>
<Row>
<Col sm={10}>Nombre de victoire :</Col>
<Col className='leftRow'>{Player !== null ? Player.onlineStats.nbWins : "0"}</Col>
</Row>
<Row>
<Col sm={10}>Ratio P/V :</Col>
<Col className='leftRow'>{Player !== null ? Player.onlineStats.ratio : "0"}</Col>
</Row>
</Container>
</Tab>
<Tab eventKey="daily" title="Daily"
style={{display:"flex", flexDirection:'column', alignItems:'center'}}>

@ -0,0 +1,62 @@
// AuthContext.js
import React, { createContext, useContext, useState, ReactNode } from 'react';
import DbUserService from '../model/DataManagers/DbUserService';
import Manager from '../model/DataManagers/Manager';
import Player from '../model/Player';
import User from '../model/User';
import AuthService from '../services/AuthService';
interface AuthContextProps {
isLoggedIn: boolean;
login: () => void;
logout: () => void;
user: User | null
setUserData: (newPlayer: User) => void
manager: Manager
}
const AuthContext = createContext<AuthContextProps | undefined>(undefined);
const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
const [user, setUser] = useState<User| null>(null)
const [manager] = useState<Manager>(new Manager(new DbUserService()))
const login = async () => {
setIsLoggedIn(true);
const [u, bool] = await manager.userService.fetchUserInformation()
setUser(u)
};
const setUserData = (newPlayer: User) => {
setUser(newPlayer)
}
const logout = async() => {
try {
await AuthService.logout();
setIsLoggedIn(false);
const [u, bool] = await manager.userService.fetchUserInformation()
setUser(u)
}
catch (error) {
console.log(error);
};
};
return (
<AuthContext.Provider value={{ isLoggedIn, login, logout, user, setUserData, manager }}>
{children}
</AuthContext.Provider>
);
};
const useAuth = (): AuthContextProps => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
};
export { AuthProvider, useAuth };

@ -1,5 +1,6 @@
import React, { createContext, useContext, useState, ReactNode } from 'react';
import Indice from '../model/Indices/Indice';
import Pair from '../model/Pair';
import Person from '../model/Person';
import PersonNetwork from '../model/PersonsNetwork';
import Player from '../model/Player';
@ -17,6 +18,11 @@ interface GameContextProps {
room: string;
onlyFalse: boolean
winner: Player | null
dailyEnigme: Map<number, Pair<Indice, boolean>[]>
nbCoup : number
temps : number
networkData: any
seed: number | string;
setIndicesData: (newIndices: Indice[]) => void;
setIndiceData: (newIndice: Indice) => void;
setPersonData: (newPerson: Person) => void;
@ -30,6 +36,11 @@ interface GameContextProps {
setOnlyFalseData: (newOnlyFalse: boolean) => void
setWinnerData: (winner: Player) => void
reset: () => void
setDailyEnigmeData: (map: Map<number, Pair<Indice, boolean>[]>) => void
setNbCoupData: (newNbCoup : number) => void
settempsData: (newtemps : number) => void
setNetworkDataData: (networkData: any) => void
setSeedData: (seed: number | string) => void
}
const GameContext = createContext<GameContextProps | undefined>(undefined);
@ -51,7 +62,19 @@ export const GameProvider: React.FC<GameProviderProps> = ({ children }) => {
const [room, setRoom] = useState<string>("")
const [onlyFalse, setOnlyFalse] = useState<boolean>(false)
const [winner, setWinner] = useState<Player | null>(null)
const [dailyEnigme, setDailyEnigme] = useState<Map<number, Pair<Indice, boolean>[]>>(new Map())
const [nbCoup, setNbCoup] = useState<number>(0);
const [temps, settemps] = useState<number>(0);
const [networkData, setNetworkData] = useState<any>(null);
const [seed, setSeed] = useState<number | string>(0);
const setNetworkDataData = (networkData: any) => {
setNetworkData(networkData);
}
const setSeedData = (seed: number | string) => {
setSeed(seed);
}
const setIndicesData = (newIndices: Indice[]) => {
setIndices(newIndices);
@ -102,6 +125,19 @@ export const GameProvider: React.FC<GameProviderProps> = ({ children }) => {
setWinner(winner)
}
const setDailyEnigmeData = (map: Map<number, Pair<Indice, boolean>[]>) => {
setDailyEnigme(map)
}
const setNbCoupData = (newNbCoup : number) => {
setNbCoup(newNbCoup);
}
const settempsData = (newtemps : number) => {
settemps(newtemps);
}
const reset = () => {
setIndices([])
setActualPlayerIndex(-1)
@ -114,10 +150,12 @@ export const GameProvider: React.FC<GameProviderProps> = ({ children }) => {
setTurnPlayerIndex(-1)
setNodeId(-1)
setIndice(null)
setNbCoup(0)
settemps(0)
}
return (
<GameContext.Provider value={{ indices, setIndicesData, indice, setIndiceData, person, setPersonData, personNetwork, setPersonNetworkData, players, setPlayersData, nodeId, setNodeIdData, askedPersons, setAskedPersonsData, actualPlayerIndex, setActualPlayerIndexData, turnPlayerIndex, setTurnPlayerIndexData, room, setRoomData, onlyFalse, setOnlyFalseData, winner, setWinnerData, reset }}>
<GameContext.Provider value={{ indices, setIndicesData, indice, setIndiceData, person, setPersonData, personNetwork, setPersonNetworkData, players, setPlayersData, nodeId, setNodeIdData, askedPersons, setAskedPersonsData, actualPlayerIndex, setActualPlayerIndexData, turnPlayerIndex, setTurnPlayerIndexData, room, setRoomData, onlyFalse, setOnlyFalseData, winner, setWinnerData, reset, dailyEnigme, setDailyEnigmeData, nbCoup, setNbCoupData, temps, settempsData, setNetworkDataData, networkData, seed, setSeedData}}>
{children}
</GameContext.Provider>
);

@ -0,0 +1,14 @@
async function loadImageAsync(url: string): Promise<string> {
try {
const response = await fetch(url);
const blob = await response.blob();
// Faire quelque chose avec le blob, par exemple, créer une URL blob
const blobUrl = URL.createObjectURL(blob);
return blobUrl
} catch (error) {
throw new Error("Erreur lors du chargement de l'image :");
}
}
export {loadImageAsync}

@ -1,5 +1,5 @@
import EasyBot from "./model/EasyBot";
import Human from "./model/Human";
import Human from "./model/User";
import AgeIndice from "./model/Indices/AgeIndice";
import ColorEdgesIndice from "./model/Indices/ColorEdgesIndice";
import ColorIndice from "./model/Indices/ColorIndice";
@ -10,6 +10,7 @@ import SportIndice from "./model/Indices/SportIndice";
import Person from "./model/Person";
import PersonNetwork from "./model/PersonsNetwork";
import Player from "./model/Player";
import User from "./model/User";
class JSONParser{
@ -84,10 +85,10 @@ class JSONParser{
static JSONToPlayer(json: any): Player{
switch (json.type){
case "Human":
return new Human(json.id, json.name)
case "User":
return new User(json.id, json.pseudo, json.profilePicture, json.soloStats, json.onlineStats)
case "EasyBot":
return new EasyBot(json.id, json.name)
return new EasyBot(json.id, json.pseudo, json.profilePicture)
default:
throw new Error("PARSER unable to parse player: " + json.type);
}

@ -21,7 +21,7 @@
padding: 20px;
}
.bottom{
.bottomEnd{
display: flex;
justify-content: space-around;
}
@ -80,3 +80,45 @@
padding: 10px;
box-shadow: 5px 5px 5px rgb(246, 246, 246);
}
.SoloContainer{
display: flex;
flex-direction: column;
align-items: center;
border: solid 1px whitesmoke;
border-radius: 15px;
background-color: white;
max-width: 50%;
}
.indicesolo{
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
/* margin: 10px 0; */
/* max-height: 200px; */
}
.solostat{
display: flex;
justify-content: space-between;
width: 70%;
}
.solostat p {
border: solid 1px whitesmoke;
border-radius: 15px;
background-color: white;
padding: 10px;
}
#vis-graph {
height: 500px;
margin: 50px;
border: 2px solid #ccc;
overflow: hidden;
}

@ -1,4 +1,4 @@
import React from 'react';
import React, {useEffect} from 'react';
/* Style */
@ -15,16 +15,80 @@ import Replay from '../res/icon/replay.png';
import PersonStatus from '../Components/PersonStatus';
import ButtonImgNav from '../Components/ButtonImgNav';
import BigButtonNav from '../Components/BigButtonNav';
//
import { Network } from "vis-network/standalone/esm/vis-network";
import { map } from 'lodash';
/* nav */
/* Nav */
import { Link } from 'react-router-dom';
/* lang */
/* Lang */
import { FormattedMessage } from 'react-intl';
/* Model */
import Player from '../model/Player';
/* Context */
import { useGame } from '../Contexts/GameContext';
/* Boostrap */
import { Button } from 'react-bootstrap';
import Bot from '../model/Bot';
function EndGame() {
const {networkData, seed} = useGame();
const params = new URLSearchParams(window.location.search);
const initialOptions = {
layout: {
improvedLayout: true,
randomSeed: seed,
hierarchical: {
enabled: false,
direction: 'LR',
sortMethod: 'hubsize'
},
//randomSeed: 2
},
physics: {
enabled: true,
barnesHut: {
gravitationalConstant: -1000,
springConstant: 0.001,
springLength: 100
}
}
};
useEffect(() => {
const container = document.getElementById("vis-graph");
if (!container) {
console.error("Container not found");
return;
}
const network = new Network(container, networkData, initialOptions);
console.log(networkData)
}, []);
//* Gestion solo
let IsSolo: boolean = false
const solotmp = params.get('solo');
if (solotmp == "true"){
IsSolo=true
}
//* Gestion daily
let IsDaily: boolean = false
const dailytmp = params.get('daily');
if (dailytmp == "true"){
IsDaily=true
}
const {reset} = useGame()
@ -32,51 +96,107 @@ function EndGame() {
reset()
}
const {winner, person, players, indices} =useGame()
console.log(winner)
const {winner, person, players, indices, nbCoup, temps} =useGame()
let indice = indices[0]
let losingPlayers : Player[];
let losingPlayers;
if(!IsSolo){
const index = players.findIndex((p) => p.id == winner?.id)
if (index != -1) {
indice = indices[index]
}
if (winner != null) {
losingPlayers = players.filter(player => player.id !== winner.id);
} else {
losingPlayers = players;
}
}
else{
losingPlayers = [];
}
const theme = useTheme();
let indicenull = false;
if (indices.length == 0){
indicenull = true;
}
return (
<div>
{!IsSolo ? (
<div>
<div className="head">
<header className='leaderboard-header' style={{ borderColor: theme.colors.primary }}>
<h1>{winner?.name} a gagné !</h1>
<h1>{winner?.pseudo} a gagné !</h1>
<h3>Le tueur était <u>{person?.getName()}</u></h3>
</header>
</div>
<div className='winner'>
<img src={Person} width='250' height='250'/>
<h3 className='indiceDisplay'>{indices[players.findIndex((p) => p.id == winner?.id)].ToString("fr")}</h3>
{!indicenull && (<h3 className='indiceDisplay'>{indices[players.findIndex((p) => p.id == winner?.id)].ToString("fr")}</h3>)}
</div>
<div className='bottom'>
<div className='centerDivH' onClick={resetAll}>
<div className='bottomEnd'>
{/* <div className='centerDivH' onClick={resetAll}>
<BigButtonNav dest="/play" img={Leave}/>
</div>
</div> */}
<div className="losingPlayersContainer">
{losingPlayers.map((player, index) => (
<div className="playerContainer" key={index}>
{player.id !== winner?.id && (
<div>
<PersonStatus img={Person} state={Person} key={index} name={player.name} playerTouched={1} setPlayerTouched={() => {}} index={index} showCircle={false}/>
<h6 className='indiceDisplay'>{indices[players.findIndex((p) => p.id == player?.id)].ToString("fr")}</h6>
<PersonStatus img={Person} state={Person} key={index} name={player.pseudo} playerTouched={1} setPlayerTouched={() => {}} index={index} playerIndex={-2} showCircle={false} askedWrong={false}/>
{!indicenull && (<h6 className='indiceDisplay'>{indices[players.findIndex((p) => p.id == player?.id)].ToString("fr")}</h6>)}
</div>
)}
</div>
))}
</div>
<div className='centerDivH'>
<BigButtonNav dest="/lobby" img={Replay}/>
</div>
</div>
): (
<div>
<div className="head">
<header className='leaderboard-header' style={{ borderColor: theme.colors.primary }}>
<h1>Vous avez gagné !</h1>
<h3>Le tueur était <u>{person?.getName()}</u></h3>
</header>
</div>
<div className='winner'>
<img src={Person} width='250' height='250'/>
<h1>{winner?.pseudo}</h1>
</div>
<div className='bottomEnd'>
<div className="SoloContainer">
<div className='solostat'>
{!IsDaily && <p>Nombre de coups : {nbCoup}</p> }
<p>Temps : {temps}s</p>
</div>
<div className='indicesolo'>
{indices.map((indice, index) => (
// <div className="playerContainer" key={index}>
<div>
<h6 className='indiceDisplay'> <u>Indice {index+1}</u> : {indice.ToString("fr")}</h6>
</div>
//</div>
))
}
</div>
</div>
</div>
</div>
)}
<div id="vis-graph"/>
<div className='centerDivH' onClick={resetAll} style={{margin: "20px"}}>
<Button href='/'>Retour à l'accueil</Button>
</div>
</div>
);
}

@ -1,14 +1,43 @@
import React from 'react';
import React, {useEffect} from 'react';
import { useAuth } from '../Contexts/AuthContext';
import SessionService from '../services/SessionService';
import './Home.css';
import '../App.css';
import { useTheme } from '../Style/ThemeContext';
import { Link } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
import ButtonImgNav from '../Components/ButtonImgNav';
import defaultImg from "../res/img/Person.png"
import {loadImageAsync} from "../ImageHelper"
import { socket } from '../SocketConfig';
import JSONParser from '../JSONParser';
// @ts-ignore
function Home() {
const theme=useTheme();
const {isLoggedIn, login, user, setUserData, manager } = useAuth();
useEffect(() => {
if (user == null){
manager.userService.fetchUserInformation().then(([user, loggedIn]) =>{
if (user!=null){
if (loggedIn){
login()
setUserData(user)
}
else{
loadImageAsync(defaultImg).then((blob) => {
user.profilePicture=blob
setUserData(user)
})
}
}
})
}
}, [isLoggedIn]);
return (
<div className="home-container">
@ -16,36 +45,59 @@ function Home() {
<div className="left-section">
<div>
<h2><FormattedMessage id="home.histoire.title"/></h2>
{/* <h2><FormattedMessage id="home.histoire.title"/></h2> */}
<h2>Introduction</h2>
<p>
<FormattedMessage id="home.histoire" />
{/* <FormattedMessage id="home.histoire" /> */}
Bienvenue dans notre jeu de déduction captivant, où l'intrigue et la malice se rejoignent dans une aventure palpitante ! Plongez-vous dans un monde de mystère et d'intrigue, où chaque interaction compte, et chaque indice vous rapproche de la vérité.Imaginez un graphique complexe où chaque sommet représente une personne, chaque axe une relation, et chaque détail compte. Vous êtes plongé dans un défi stimulant pour découvrir qui parmi ces individus est le mystérieux tueur. Chaque joueur détient un indice crucial, et seul le partage stratégique de ces indices vous mènera à la résolution du mystère. Explorez notre page de règles pour comprendre les subtilités du jeu, découvrez les indices qui peuvent vous guider, et élaborez des stratégies intelligentes pour identifier le coupable. Manipuler vos amis, afin d'être le premier à découvrir qui est le meurtrier ! Êtes-vous prêt à relever le défi et à démasquer le tueur caché dans le graphe ? Que l'enquête commence !
</p>
</div>
<div>
<h2><FormattedMessage id="home.jeu.title"/></h2>
<p>
<FormattedMessage id="home.jeu" />
{/* <FormattedMessage id="home.jeu" /> */}
Dans l'univers captivant de notre jeu de déduction, la tromperie et la ruse sont les maîtres mots de la réussite. Explorez le mystère qui se dissimule derrière chaque interaction de notre graphique complexe, dévoilant les liens entre les individus.
Votre mission ultime ? Découvrir qui parmi les individus est le coupable, mais n'attendez pas une collaboration ouverte. Utilisez plutôt la manipulation subtile pour embrouiller les pistes, détourner l'attention de vos adversaires. Posez des questions stratégiques, répondez avec malice et plantez des indices trompeurs pour vous rapprocher du dénouement.
Chaque occasion offre la possibilité de semer le doute parmi vos adversaires. Lorsqu'un joueur vous interroge, répondez en plaçant adroitement un jeton carré pour suggérer que "selon votre indice, cette personne ne peut être le coupable", ou un jeton rond pour indiquer qu'elle reste dans la liste des suspects. Soyez vigilant, chaque geste peut être interprété, et la vérité se dissimule souvent derrière une façade d'indices trompeurs.
Si un joueur place un jeton carré, le questionneur doit également jouer son jeu en plaçant un jeton carré de sa couleur sur un nœud du graphique. La contre-manipulation devient ainsi une arme redoutable pour détourner l'accusation et semer la confusion. Pour en savoir plus, plongez-vous dans les détails de ce passionnant récit sur une autre page.
</p>
<br/>
<p>Pour des informations plus détaillées, consulter les <Link to="/info">règles</Link>.</p>
<br/>
</div>
<div>
<h2><FormattedMessage id="home.plus.title"/></h2>
{/* <h2><FormattedMessage id="home.plus.title"/></h2>
<ul>
<li><FormattedMessage id="home.plus.1"/></li>
<li><FormattedMessage id="home.plus.2"/></li>
<li><FormattedMessage id="home.plus.3"/></li>
</ul>
</ul> */}
<h2>Présentation Video :</h2>
(ici il y aura la vidéo)
</div>
</div>
<div className="vertical-divider"></div>
<div className="right-section">
<h3><FormattedMessage id="game.time"/></h3>
{/* <h3><FormattedMessage id="game.time"/></h3>
<h3><FormattedMessage id="game.players"/></h3>
<h3><FormattedMessage id="game.age"/></h3>
<p>-------------------------------</p>
<h3> <u><FormattedMessage id="game.createdBy"/></u><br/> Hal Duncan & Ruth Veevers</h3>
<h3> <u><FormattedMessage id="game.illustratedBy"/></u><br/> Kwanchai Moriya</h3>
<h3><FormattedMessage id="game.age"/></h3> */}
<h3>
Temps : 20 minutes<br/>
Joueurs : 1 à 6<br/>
Âge : 8ans et +<br/>
</h3>
<hr/>
<h3> <u><FormattedMessage id="game.createdBy"/></u><br/>
Chazot Thomas<br/>
Ferreira Pierre<br/>
Marcel Baptiste<br/>
</h3>
{/* <h3> <u><FormattedMessage id="game.illustratedBy"/></u><br/> Kwanchai Moriya</h3> */}
{/* <button>Jouer au jeu</button> */}
<br/>
<Link to="/play" className='button'

@ -25,6 +25,7 @@ import Download from "../res/icon/download.png"
import Reset from "../res/icon/reset.png";
import Oeye from "../res/icon/eye.png";
import Ceye from "../res/icon/hidden.png";
import JSZip from 'jszip';
/* nav */
import { Link } from 'react-router-dom';
@ -45,7 +46,9 @@ import { NavLink } from 'react-router-dom';
import { last } from 'lodash';
import { socket } from '../SocketConfig';
import { Network } from 'vis-network';
import generateLatexCode from '../Script/LatexScript';
import {generateLatexCode, generateLatexCodeEnigme} from '../Script/LatexScript';
import Pair from '../model/Pair';
import Indice from '../model/Indices/Indice';
//@ts-ignore
const InGame = ({locale, changeLocale}) => {
@ -62,9 +65,23 @@ const InGame = ({locale, changeLocale}) => {
IsSolo=false
}
//* Gestion daily
let isDaily: boolean = true
const isDailytmp = params.get('daily');
if (isDailytmp == "false"){
isDaily=false
}
let isEasy: boolean = true
const isEasytmp = params.get('easy');
if (isEasytmp == "false"){
isEasy=false
}
//* Historique
const [history, setHistory] = useState<string[]>([]);
const [showLast, setShowLast] = useState(false)
const [askedWrong, setAskedWrong] = useState(false)
// Fonction pour ajouter un élément à l'historique
const addToHistory = (message: string) => {
@ -76,6 +93,10 @@ const InGame = ({locale, changeLocale}) => {
setShowLast(!showLast);
}
const setAskedWrongData = (askedWrong: boolean) => {
setAskedWrong(askedWrong)
}
useEffect(() => {
const historyContainer = document.getElementById('history-container');
if (historyContainer) {
@ -90,13 +111,20 @@ const InGame = ({locale, changeLocale}) => {
const [showTurnBar, setShowTurnBar] = useState(false);
const [turnBarText, setTurnBarText] = useState("");
const [playerTouched, setPlayerTouched] = useState(-2)
const [playerIndex, setPlayerIndex] = useState(-2)
const [network, setNetwork] = useState<Network | null>(null)
const [networkEnigme, setNetworkEnigme] = useState<Map<number, Pair<Indice, boolean>[]> | null>(null)
const setNetworkData = (network: Network) => {
setNetwork(network)
}
const setNetworkEnigmeData = (networkEnigme: Map<number, Pair<Indice, boolean>[]>) => {
setNetworkEnigme(networkEnigme)
}
const handleNodeClick = (shouldShowChoiceBar: boolean) => {
setShowChoiceBar(shouldShowChoiceBar);
};
@ -114,13 +142,47 @@ const InGame = ({locale, changeLocale}) => {
setTurnBarText(newTurnBarText)
}
const generateTEX = () => {
const setPlayerIndexData = (playerIndex: number) => {
setPlayerIndex(playerIndex)
}
const generateTEX = async () => {
if (network != null && personNetwork != null && person != null){
const zip = new JSZip();
if (isDaily && networkEnigme != null){
const tex = generateLatexCodeEnigme(personNetwork, person, indices, network, networkEnigme)
const blob = new Blob([tex], { type: 'application/x-latex;charset=utf-8' });
zip.file('socialGraph.tex', tex);
}
else{
const tex = generateLatexCode(personNetwork, person, indices, network)
const blob = new Blob([tex], { type: 'application/x-latex;charset=utf-8' });
zip.file('socialGraph.tex', tex);
}
const imageNames = ['ballon-de-basket.png', 'ballon-de-foot.png', "baseball.png", "bowling.png", "tennis.png"]; // Liste des noms de fichiers d'images
const imagesFolder = 'Script';
for (const imageName of imageNames) {
const imageUrl = process.env.PUBLIC_URL + `/${imagesFolder}/${imageName}`;
const response = await fetch(imageUrl);
if (response.ok) {
const imageBlob = await response.blob();
zip.file(`${imageName}`, imageBlob);
} else {
console.error(`Erreur de chargement de l'image ${imageName}`);
}
}
const content = await zip.generateAsync({ type: 'blob' });
// Enregistre l'archive en tant que fichier
saveAs(content, 'social_graph.zip');
// Utiliser FileSaver pour télécharger le fichier
saveAs(blob, 'socialGraph.tex');
}
}
@ -212,14 +274,20 @@ const InGame = ({locale, changeLocale}) => {
changecptTour={changecptTour}
addToHistory={addToHistory}
solo={IsSolo}
isDaily={isDaily}
isEasy={isEasy}
setPlayerTouched={handleSetPlayerTouched}
playerTouched={playerTouched}
setNetwork={setNetworkData}
showLast={showLast}/>
setNetworkEnigme={setNetworkEnigmeData}
showLast={showLast}
setPlayerIndex={setPlayerIndexData}
askedWrong={askedWrong}
setAskedWrong={setAskedWrongData}/>
</div>
{IsSolo &&
{IsSolo && !isDaily &&
<div className='nbLaps' style={{
backgroundColor: theme.colors.primary,
borderColor: theme.colors.secondary
@ -228,12 +296,13 @@ const InGame = ({locale, changeLocale}) => {
</div>
}
{(!isDaily || (isDaily && isEasy)) &&
<div className='historique' id="history-container">
{history.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
}
<div className='paramDiv'>
<button className='button'
@ -289,6 +358,7 @@ const InGame = ({locale, changeLocale}) => {
<img src={Check} alt="check" height="40"/>
</button> */}
{!IsSolo &&
<Link to='/deduc' target='_blank'>
<button className='button'
style={{
@ -297,23 +367,25 @@ const InGame = ({locale, changeLocale}) => {
}}>
<img src={Check} alt="check" height="40"/>
</button>
</Link>
</Link>}
<button className='button' onClick={handleChange}
{!IsSolo && <button className='button' onClick={handleChange}
style={{
backgroundColor: theme.colors.tertiary,
borderColor: theme.colors.secondary
}}>
<img src={MGlass} alt="indice" height="40"/>
</button>
</button>}
<button className='button' onClick={setShowLastData}
{!IsSolo && <button className='button' onClick={setShowLastData}
style={{
backgroundColor: theme.colors.tertiary,
borderColor: theme.colors.secondary
}}>
<img src={ eye } alt="indice" height="40"/>
</button>
</button>}
{IsSolo &&
<button className='button' onClick={generateTEX}
style={{
@ -322,6 +394,7 @@ const InGame = ({locale, changeLocale}) => {
}}>
<img src={Download} alt="indice" height="40"/>
</button>
}
</div>
{/*
@ -339,7 +412,7 @@ const InGame = ({locale, changeLocale}) => {
{ !IsSolo &&
<div className='playerlistDiv'>
<PlayerList players={players} setPlayerTouched={handleSetPlayerTouched} playerTouched={playerTouched} />
<PlayerList players={players} setPlayerTouched={handleSetPlayerTouched} playerTouched={playerTouched} playerIndex={playerIndex} askedWrong={askedWrong}/>
</div>
}
@ -389,9 +462,6 @@ const InGame = ({locale, changeLocale}) => {
</Offcanvas.Body>
</Offcanvas>
<div id="bottom-container">
{showChoiceBar && <ChoiceBar />}
</div>
{/*
<div id="endgamebutton" > {/* tmp
<ButtonImgNav dest="/endgame" img={Leave} text='endgame'/>

@ -0,0 +1,42 @@
.lobbyMainContainer{
display: flex;
flex-direction: column;
background-color: white;
border: solid 1px whitesmoke;
border-radius: 15px;
box-shadow: 5px 5px 5px rgb(246, 246, 246);
width: 300px;
/* max-width: 300px; */
/* max-width: 20%; */
max-height: 200px;
padding: 20px;
margin: 20px;
cursor: pointer;
}
.lobbyList{
display: flex;
/* justify-content: space-around; */
/* columns: 4; */
flex-wrap: wrap;
justify-content: center;
}
.searchLobby{
border: solid 1px whitesmoke;
border-radius: 15px;
padding: 15px;
width: 50%;
}
h1{
margin: 50px !important;
font-family: "Raleway", sans-serif;
}

@ -0,0 +1,80 @@
import React, { useEffect, useState } from 'react';
/* Style */
import './Lobbies.css';
import "../Style/Global.css"
import { useTheme } from '../Style/ThemeContext';
import LobbyContainer from '../Components/LobbyContainer';
import Player from '../model/Player';
import User from '../model/User';
import { socket } from '../SocketConfig';
import JSONParser from '../JSONParser';
import Person from '../model/Person';
class LobbyDataProps {
roomNum : string
headPlayer: Player
nbPlayer: number
constructor(roomNum: string, player: Player, nbPlayer: number){
this.roomNum = roomNum
this.headPlayer = player
this.nbPlayer = nbPlayer
}
}
function Lobbies() {
const theme=useTheme();
const [lobbyData, setLobbyData] = useState<LobbyDataProps[]>([])
const [searchTerm, setSearchTerm] = useState('');
const filteredLobbies = lobbyData.filter((lobby) =>
lobby.roomNum.toLowerCase().includes(searchTerm.toLowerCase()) ||
lobby.headPlayer.pseudo.toLowerCase().includes(searchTerm.toLowerCase())
);
useEffect(() => {
socket.emit("request lobbies")
socket.on("request lobbies", (map) => {
const jsonMap = JSON.parse(map)
const tmpTab: LobbyDataProps[]=[]
for(const item of jsonMap){
tmpTab.push(new LobbyDataProps(item.key, JSONParser.JSONToPlayer(item.value[0]), item.value.length))
}
setLobbyData(tmpTab )
})
})
return(
<div style={{display:'flex', flexDirection:'column', alignItems:'center'}}>
<h1>Bienvenue dans le lobby des lobbies</h1>
<input
type="text"
className='searchLobby'
placeholder="Rechercher un lobby..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<div className="lobbyList">
{filteredLobbies.map((lobby, index) => (
<LobbyContainer
key={index}
roomNum={lobby.roomNum}
HeadPlayer={lobby.headPlayer}
nbPlayer={lobby.nbPlayer}
/>
))}
</div>
</div>
);
}
export default Lobbies;

@ -60,3 +60,38 @@
display: flex;
justify-content: center;
}
.lobbyR{
display: flex;
flex-direction: column;
align-items: center;
}
.lobbyR *{
margin: 20px 10px;
}
.valuebutton{
border-radius: 90px;
width: 50px;
height: 50px;
background-color: #fff;
}
.nbNodeDiv{
display: flex;
flex-direction: column;
justify-content: center;
border: solid 2px whitesmoke;
border-radius: 15px;
background-color: white;
width: 70%;
}
.nbNodeDiv div {
display: flex;
justify-content: center;
}

@ -1,5 +1,8 @@
import React, { useEffect, useState } from 'react';
/* Style */
import './Lobby.css';
import "../Style/Global.css"
import { useTheme } from '../Style/ThemeContext';
/* res */
@ -8,23 +11,40 @@ import PersonImg from '../res/img/Person.png';
import param from '../res/icon/param.png';
import cible from '../res/icon/cible.png';
import defaultImg from "../res/img/Person.png"
/* Component */
import ButtonImgNav from '../Components/ButtonImgNav';
import { io } from 'socket.io-client';
import { Link } from 'react-router-dom';
/* Context */
import { useGame } from '../Contexts/GameContext';
import { useAuth } from '../Contexts/AuthContext';
import { useNavigate } from 'react-router-dom';
/* Model */
import PersonNetwork from '../model/PersonsNetwork';
import Person from '../model/Person';
import GameCreator from '../model/GameCreator';
import { useGame } from '../Contexts/GameContext';
import JSONParser from '../JSONParser';
import Indice from '../model/Indices/Indice';
import { useNavigate } from 'react-router-dom';
import { socket } from "../SocketConfig";
import { random } from 'lodash';
import JSONParser from '../JSONParser';
import Player from '../model/Player';
import Human from '../model/Human';
import EasyBot from '../model/EasyBot';
import Bot from '../model/Bot';
import User from '../model/User';
import {loadImageAsync} from "../ImageHelper"
/* serv */
import { socket } from "../SocketConfig";
import { random } from 'lodash';
import SessionService from '../services/SessionService';
import { useRef } from 'react';
import Button from 'react-bootstrap/Button';
import Overlay from 'react-bootstrap/Overlay';
let gameStarted = false
@ -33,26 +53,51 @@ function Lobby() {
const theme=useTheme();
const navigate = useNavigate();
const { indices, setIndicesData, indice, setIndiceData, person, setPersonData, personNetwork, setPersonNetworkData, players, setPlayersData, setActualPlayerIndexData, setTurnPlayerIndexData, setRoomData } = useGame();
const {user, setUserData, manager, login} = useAuth()
let first = true
const params = new URLSearchParams(window.location.search);
const room = params.get('room');
function addBot(){
socket.emit("lobby joined", room, new EasyBot("botId" + Math.floor(Math.random() * 1000), "Bot" + Math.floor(Math.random() * 100)).toJson())
socket.emit("lobby joined", room, new EasyBot("botId" + Math.floor(Math.random() * 1000), "Bot" + Math.floor(Math.random() * 100), "").toJson())
}
// function delBot(selectedBot: Bot){
//* nb Node
const [enteredNumber, setEnteredNumber] = useState(20);
// }
//@ts-ignore
const handleNumberChange = (event) => {
const newNumber = Math.max(20, Math.min(60, parseInt(event.target.value, 10)));
setEnteredNumber(newNumber);
};
useEffect(() => {
if (first){
first=false
socket.emit("lobby joined", room, new Human("test", "Test" + Math.floor(Math.random() * 100)).toJson())
if (user == null){
manager.userService.fetchUserInformation().then(([u, loggedIn]) => {
if (u!=null){
if (loggedIn){
login()
setUserData(u)
}
else{
loadImageAsync(defaultImg).then((blob) => {
u.profilePicture=blob
setUserData(u)
})
}
socket.emit("lobby joined", room, u.toJson())
}
})
}
else{
socket.emit("lobby joined", room, user.toJson())
}
return () => {
socket.off('game created');
@ -87,7 +132,7 @@ function Lobby() {
gameStarted = true
socket.off("player left")
socket.off("new player")
navigate('/game?solo=false');
navigate('/game?solo=false&daily=false');
});
socket.on("new player", (tab) =>{
@ -95,6 +140,7 @@ function Lobby() {
for (const p of tab){
tmpTab.push(JSONParser.JSONToPlayer(p))
}
console.log(tmpTab)
setPlayersData(tmpTab)
})
@ -110,14 +156,46 @@ function Lobby() {
function StartGame(){
const [networkPerson, choosenPerson, choosenIndices] = GameCreator.CreateGame(players.length, 30)
const [networkPerson, choosenPerson, choosenIndices] = GameCreator.CreateGame(players.length, enteredNumber)
setPersonData(choosenPerson)
setPersonNetworkData(networkPerson)
setIndicesData(choosenIndices)
let start = 0
let users = players.filter((p) => p instanceof User)
let u = users[Math.floor(Math.random() * users.length)]
let start = players.findIndex((p) => p.id == u.id)
if (start == -1){
start = 0
}
socket.emit('network created', JSON.stringify(networkPerson, null, 2), JSON.stringify(choosenPerson), JSON.stringify(choosenIndices), room, start);
}
const copyGameLink = () => {
setShow(!show)
const gameLink = "http://localhost:3000/lobby?room="+ room;
navigator.clipboard.writeText(gameLink)
.then(() => {
console.log('Lien copié avec succès !');
})
.catch((err) => {
console.error('Erreur lors de la copie du lien :', err);
});
};
const textAreaRef = useRef<HTMLTextAreaElement>(null);
const linkToCopy = "http://localhost:3000/lobby?room="+ room
const handleCopyClick = () => {
setShow(!show)
if(textAreaRef.current != null){
textAreaRef.current.select();
document.execCommand('copy');
}
};
const [show, setShow] = useState(false);
const target = useRef(null);
return (
<div className='lobby-container'>
<div className='left-part'>
@ -150,7 +228,7 @@ function Lobby() {
<div className="lobby-vertical-divider" style={{backgroundColor: theme.colors.secondary}}></div>
<div className='right-part'>
<div className='title-param-div'>
{/* <div className='title-param-div'>
<img src={param} alt="param"/>
<h2>Paramètre de la partie</h2>
</div>
@ -159,19 +237,105 @@ function Lobby() {
<li><h4>paramètre super important pour la partie</h4></li>
<li><h4>paramètre super important pour la partie</h4></li>
<li><h4>paramètre super important pour la partie</h4></li>
<li><h4>Niveau des bots : Facile </h4></li> {/* mettre un dropdown ou un swiper */}
<li><h4>Thèmes : basique </h4></li> {/* mettre un dropdown*/}
<li><h4>Niveau des bots : Facile </h4></li>
<li><h4>Thèmes : basique </h4></li> */}
{
//? mettre un timer pour chaques personne ?
//? indice avancé ? ==> négation, voisin du 2e degré etc.
}
</ul>
<center >
{/* </ul> */}
{/* <center >
<button className='buttonNabImg' onClick={StartGame}>
<img src={cible} alt="Button Image" height="50" width="50" />
<p>{"la chasse !"}</p>
</button>
</center>
</center> */}
<div className='lobbyR'
style={{flexDirection:'column',
alignItems:'space-around'}}>
<h3>Bienvenue dans votre lobby !</h3>
<p>Attendez que tous vos amis rejoignent avant de lancer la partie.</p>
{/* Bouton pour copier le lien */}
<Button variant="primary" ref={target} onClick={copyGameLink}>
Inviter des amis
</Button>
<Overlay target={target.current} show={show} placement="top">
{({
placement: _placement,
arrowProps: _arrowProps,
show: _show,
popper: _popper,
hasDoneInitialMeasure: _hasDoneInitialMeasure,
...props
}) => (
<div
{...props}
style={{
position: 'absolute',
backgroundColor: theme.colors.secondary,
padding: '2px 10px',
color: 'white',
borderRadius: 3,
...props.style,
}}
>
Lien copié
</div>
)}
</Overlay>
<div className='nbNodeDiv'>
<label htmlFor="numberInput">Sélectionner le nombre de noeud (entre 20 et 60) :</label>
<div>
<button className='valuebutton' onClick={() => { if (enteredNumber>20) setEnteredNumber(enteredNumber-1)}}
style={{borderColor:theme.colors.secondary}}> - </button>
<input
// type="number"
id="numberInput"
disabled
value={ "Nombre de noeuds : " + enteredNumber}
onChange={handleNumberChange}
min={20}
max={60}/>
<button className='valuebutton' onClick={() => { if (enteredNumber<60) setEnteredNumber(enteredNumber+1)}}
style={{borderColor:theme.colors.secondary}}> + </button>
</div>
</div>
<button className='button' onClick={StartGame}
style={{
backgroundColor: theme.colors.tertiary,
borderColor: theme.colors.secondary,
width: 'auto',
height: 'auto'
}}>
Démarrer la partie !
</button>
</div>
{/* <div className='centerDivH'>
<div>
<label htmlFor="numberInput">Séléctionner le nombre de noeud (entre 20 et 60) :</label>
<input
type="number"
id="numberInput"
value={enteredNumber}
onChange={handleNumberChange}
min={20}
max={60}/>
<p>La valeur saisie : {enteredNumber}</p>
</div>
<div className='centerDivH'>
<button className='button' onClick={StartGame}
style={{
backgroundColor: theme.colors.tertiary,
borderColor: theme.colors.secondary
}}>
<img src={cible} alt="cible" height="40"/>
</button>
</div> */}
</div>
</div>
);

@ -1,18 +1,65 @@
import React, { Component } from 'react';
import React, { useState, useEffect } from 'react';
import { AiOutlineSend } from 'react-icons/ai';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../Contexts/AuthContext';
import AuthService from '../services/AuthService';
import '../Style/Global.css';
export default class Login extends Component {
render() {
const SignIn = () => {
const navigate = useNavigate();
const { login } = useAuth();
const [error, setError] = useState<string | null>(null);
const [showConfirmation, setShowConfirmation] = useState(false);
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
try {
const data = {
pseudo: (event.target as any).pseudo.value,
password: (event.target as any).password.value,
remember: (event.target as any).remember.checked
};
const validation = await AuthService.validateSignIn(data);
if (!validation.valid) {
setError(validation.error);
} else {
setError(null);
const result = await AuthService.signIn(data);
// console.log(result);
setShowConfirmation(true);
setTimeout(async () => {
await login();
navigate('/play'); // 3 secondes avant de rediriger vers la page de connexion
}, 3000);
}
} catch (error: any) {
setError(error.message);
}
};
useEffect(() => {
return () => {
setShowConfirmation(false);
};
}, []);
return (
<div className="form-container">
<form>
<form onSubmit={handleSubmit}>
<br/>
<h3>Log In</h3>
<div className="mb-3">
<label>Pseudo</label>
<input
type="pseudo"
name='pseudo'
className="form-control"
placeholder="Entrez votre pseudo ici"
/>
@ -21,6 +68,7 @@ export default class Login extends Component {
<label>Password</label>
<input
type="password"
name='password'
className="form-control"
placeholder="Entrez votre mot de passe ici"
/>
@ -29,6 +77,7 @@ export default class Login extends Component {
<div className="custom-control custom-checkbox">
<input
type="checkbox"
name='remember'
className="custom-control-input"
id="customCheck1"
/>
@ -46,7 +95,20 @@ export default class Login extends Component {
<a href="#">Mot de passe</a> oublié ?
</p>
</form>
{error && (
<div className="alert alert-danger" role="alert">
{error}
</div>
)
}
}
)}
{showConfirmation && (
<div className="alert alert-success" role="alert">
Connexion réussie ! Vous serez redirigé vers votre profil dans 3 secondes.
</div>
)}
</div>
);
};
export default SignIn;

@ -1,35 +1,113 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
/* Context */
import { useAuth } from '../Contexts/AuthContext';
/* Style */
import './Play.css';
import { useTheme } from '../Style/ThemeContext';
/* Nav */
import { Link } from 'react-router-dom';
/* Component */
import ButtonImgNav from "../Components/ButtonImgNav"
import ButtonImgNav from "../Components/ButtonImgNav";
/* Img */
import Person from '../res/img/Person.png';
/* Icon */
import trophy from '../res/icon/trophy.png';
import param from '../res/icon/param.png';
import share from '../res/icon/share.png';
import { socket } from '../SocketConfig';
import { useNavigate } from 'react-router-dom';
import GameCreator from '../model/GameCreator';
import { useGame } from '../Contexts/GameContext';
import ScoreBoard from '../Components/ScoreBoard';
import defaultImg from "../res/img/Person.png"
/* Types */
import User from '../model/User';
import EnigmeDuJourCreator from '../model/EnigmeDuJourCreator';
import Stub from '../model/Stub';
import SessionService from '../services/SessionService';
import { loadImageAsync } from '../ImageHelper';
import { Overlay, ToggleButton, ToggleButtonGroup } from 'react-bootstrap';
import Button from 'react-bootstrap/Button';
import ButtonGroup from 'react-bootstrap/ButtonGroup';
function Play() {
let first = true
const theme=useTheme()
const {isLoggedIn, login, user, setUserData, manager } = useAuth();
const {setDailyEnigmeData} = useGame()
const target = useRef(null);
useEffect(() => {
const fetchUserInformation = async () => {
try {
const sessionData = await SessionService.getSession();
// Vérifie si il y a une session
if (sessionData.user) {
// Il y a une session on récupère les infos du joueur
const updatedPlayer: User = new User(socket.id, sessionData.user.pseudo, sessionData.user.profilePicture, {
nbGames: sessionData.user.soloStats.nbGames,
bestScore: sessionData.user.soloStats.bestScore,
avgNbTry: sessionData.user.soloStats.avgNbTry,
},
{
nbGames: sessionData.user.onlineStats.nbGames,
nbWins: sessionData.user.onlineStats.nbWins,
ratio: sessionData.user.onlineStats.ratio,
})
login();
setUserData(updatedPlayer);
} else {
// Pas de session on génère un guest random
const guestPlayer: User = new User(socket.id, 'Guest_' + Math.floor(Math.random() * 1000000), '',
{
nbGames: 0,
bestScore: 0,
avgNbTry: 0,
},
{
nbGames: 0,
nbWins: 0,
ratio: 0,
})
setUserData(guestPlayer);
}
} catch (error) {
console.error(error);
}
};
fetchUserInformation();
}, [isLoggedIn]);
const { setIndicesData, setPersonData, setPersonNetworkData } = useGame();
useEffect(() => {
if (user == null){
manager.userService.fetchUserInformation().then(([user, loggedIn]) =>{
if (user!=null){
if (loggedIn){
login()
setUserData(user)
}
else{
loadImageAsync(defaultImg).then((blob) => {
user.profilePicture=blob
setUserData(user)
})
}
}
})
}
}, [isLoggedIn]);
const [room, setRoom] = useState(null);
const navigate = useNavigate();
@ -43,10 +121,18 @@ function Play() {
setPersonNetworkData(networkPerson)
setIndicesData(choosenIndices)
setIndicesData(choosenIndices)
navigate('/game?solo=true');
navigate('/game?solo=true&daily=false');
}
function launchEngimeJour(){
//* overlay
if (!showOverlay)setShowOverlay(true)
else setShowOverlay(true)
}
useEffect(() => {
const handleLobbyCreated = (newRoom: any) => {
@ -72,6 +158,47 @@ function Play() {
const [showOverlay, setShowOverlay] = useState(false);
const [selectedDifficulty, setSelectedDifficulty] = useState(null);
//@ts-ignore
const handleDifficultyChange = (value) => {
setSelectedDifficulty(value);
};
const handleStartEasyGame = () => {
//* Mode facile
//todo différencier les deux
const [networkPerson, choosenPerson, choosenIndices] = GameCreator.CreateGame(3, 30)
setPersonData(choosenPerson)
setPersonNetworkData(networkPerson)
setIndicesData(choosenIndices)
setIndicesData(choosenIndices)
navigate('/game?solo=true&daily=true&easy=true');
setShowOverlay(false);
};
const handleStartHardGame = () => {
//* Mode difficile
//todo différencier les deux
const [networkPerson, choosenPerson, choosenIndices] = GameCreator.CreateGame(3, 30)
setPersonData(choosenPerson)
setPersonNetworkData(networkPerson)
setIndicesData(choosenIndices)
setIndicesData(choosenIndices)
if (first){
first = false
const map = EnigmeDuJourCreator.createEnigme(networkPerson, choosenIndices, choosenPerson, Stub.GenerateIndice())
setDailyEnigmeData(map)
}
navigate('/game?solo=true&daily=true&easy=false');
setShowOverlay(false);
};
return (
<div className="MainContainer">
@ -79,14 +206,14 @@ function Play() {
{/* <button className='ButtonNav'>
Param
</button> */}
<ButtonImgNav dest='/signup' img={Person} text="Gestion du compte"/>
{/* <ButtonImgNav dest='/signup' img={defaultImg} text="Gestion du compte"/> */}
</div>
<div className="MidContainer">
<div>
<h2>
Guest 177013
{user && user.pseudo}
</h2>
<img src={Person}
<img src={user?.profilePicture}
height='300'
width='300'
alt="Person"
@ -94,13 +221,36 @@ function Play() {
</div>
<div className='buttonGroupVertical'>
<button onClick={launchMastermind} className="ButtonNav" style={{backgroundColor: theme.colors.primary, borderColor: theme.colors.secondary}}> Jouer seul </button>
<button ref={target} onClick={launchEngimeJour} className="ButtonNav" style={{backgroundColor: theme.colors.primary, borderColor: theme.colors.secondary}}> Résoudre une énigme</button>
<Overlay show={showOverlay} target={target.current} placement="bottom" rootClose={true} rootCloseEvent='click'>
{({ placement, arrowProps, show: _show, popper, ...props }) => (
<div
{...props}
style={{
backgroundColor: theme.colors.secondary,
padding: '2px 10px',
borderRadius: 3,
...props.style,
}}>
<ButtonGroup aria-label="difficulty">
<Button onClick={handleStartEasyGame}>Facile</Button>
<Button onClick={handleStartHardGame}>Difficile</Button>
</ButtonGroup>
</div>
)}
</Overlay>
<button onClick={createLobby} className="ButtonNav" style={{backgroundColor: theme.colors.primary, borderColor: theme.colors.secondary}}> Créer une partie </button>
<button className="ButtonNav" style={{backgroundColor: theme.colors.primary, borderColor: theme.colors.secondary}}> Rejoindre </button>
<button onClick= {() => navigate("/join")} className="ButtonNav" style={{backgroundColor: theme.colors.primary, borderColor: theme.colors.secondary}}> Rejoindre </button>
</div>
</div>
<div className='rightContainer'>
<ScoreBoard/>
{user && (<ScoreBoard Player={user}/>)}
</div>
</div>
);

@ -0,0 +1,123 @@
.mainContainer{
display: flex;
/* flex-direction: column; */
justify-content: center;
align-items: center;
margin: 50px;
}
.mainPDPContainer{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 2px solid whitesmoke;
border-radius: 15px;
background-color: white;
margin: 10px;
min-height: 250px;
}
/*Lpart*/
.imgContainer{
border: 5px solid black;
border-radius: 50px;
margin: 15px;
}
/*Rpart*/
.Rpart{
min-width: 40%;
min-height: 250px;
margin: 20px;
padding: 20px;
background-color: white;
border: solid 1px whitesmoke;
border-radius: 15px;
}
.username-display{
display: flex;
}
.editbutton{
border-color: white;
background-color: white;
border: none;
margin-left: 15px;
height: 25px;
width: 25px;
}
.inputpseudo{
display: 'flex';
justify-content: 'flex-start';
align-items: 'center';
flex-direction: 'row';
width: 20vw;
padding: 5;
border:none;
border-bottom: solid 2px gray;
border-radius: 5;
font-size: 40px;
}
.bottom{
display: flex;
flex-direction: column;
justify-content: end;
align-items: end;
height: 100px;
}
/*File upload*/
.parent {
/* width: 250px; */
/* margin: auto; */
margin: 2rem;
background: #ffffff;
border-radius: 25px;
/* box-shadow: 7px 20px 20px rgb(210, 227, 244); */
}
.file-upload {
text-align: center;
border: 3px dashed rgb(210, 227, 244);
/* padding: 1.5rem; */
position: relative;
cursor: pointer;
width: 100px;
height: 50px;
}
.file-upload p {
font-size: 0.5rem;
/* margin-top: 10px; */
color: #bbcada;
}
.file-upload input {
display: block;
height: 100%;
width: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
opacity: 0;
cursor: pointer;
}

@ -0,0 +1,196 @@
import React, { useEffect, useState } from 'react';
import ProfilePDP from '../Components/ProfilePDP';
import SessionService from '../services/SessionService';
import AuthService from '../services/AuthService';
import { PlayerProps } from '../types/Player';
import { delay, update } from 'lodash';
import { socket } from '../SocketConfig';
/* Style */
import './Profile.css'
import Edit from "../res/icon/edit-pen.png"
import Coche from '../res/icon/coche.png'
import Cancel from '../res/icon/cancel.png'
/* Nav */
import { useNavigate } from 'react-router-dom';
/* Model */
import User from '../model/User';
/* Context */
import { useAuth } from '../Contexts/AuthContext';
/* Boostrap */
import Button from 'react-bootstrap/Button';
import Alert from 'react-bootstrap/Alert';
import Modal from 'react-bootstrap/Modal';
import Form from 'react-bootstrap/Form';
//@ts-ignore
const Profile = () => {
const navigate = useNavigate();
//let player;
const {user, logout} = useAuth()
// let pseudoNotNull;
// if(user?.pseudo != null){
// pseudoNotNull = user.pseudo;
// }
const [editingUsername, setEditingUsername] = useState(false);
const [newUsername, setNewUsername] = useState(user?.pseudo);
//@ts-ignore
const onUsernameChange = (newUsername) => {
if(user?.pseudo != null){
SessionService.UpdatePseudo(user.pseudo, newUsername)
user.pseudo = newUsername;
}
}
const handleUsernameChange = () => {
// Maj du pseudo
onUsernameChange(newUsername);
// Désactiver le mode d'édition
setEditingUsername(false);
};
//* Gestion Modal de suppression :
const [showDeleteModal, setShowDeleteModal] = useState(false);
const handleShowDeleteModal = () => {
setShowDeleteModal(true);
};
const handleCloseDeleteModal = () => {
setShowDeleteModal(false);
};
//* Confirmation avec la phrase :
const [confirmationPhrase, setConfirmationPhrase] = useState('');
const [showWrong, setShowWrong] = useState(false);
//@ts-ignore
const handleConfirmationPhraseChange = (e) => {
setConfirmationPhrase(e.target.value);
};
const handleDeleteAccount = () => {
// Verification de la phrase
if (confirmationPhrase.toLowerCase() === 'supprimer mon compte') {
console.log('Compte supprimé !');
if(user!= null){
const pseudo = user.pseudo;
AuthService.delAccount(pseudo);
AuthService.logout();
logout();
}
else{
console.error("l'utilisateur ne peut pas être null")
}
handleCloseDeleteModal();
navigate("/play")
} else {
console.error('Phrase de confirmation incorrecte.');
setShowWrong(true);
setTimeout(async () => {
setShowWrong(false);
}, 3000);
}
};
return (
<>
<center><h1>Mon Compte</h1></center>
<div className='mainContainer'>
<div>
<ProfilePDP/>
</div>
<div className='Rpart'>
{editingUsername ? (
<div className='username-edit'>
<input
type='text'
className='inputpseudo'
value={newUsername}
onChange={(e) => setNewUsername(e.target.value)}
/>
<button className='editbutton' onClick={handleUsernameChange}>
<img src={Coche} alt='edit' width='25' height='25'/>
</button>
<button className='editbutton' onClick={() => setEditingUsername(false)}>
<img src={Cancel} alt='edit' width='25' height='25'/>
</button>
</div>
) : (
<div className='username-display'>
<h1>{user?.pseudo}</h1>
<button className='editbutton' onClick={() => setEditingUsername(true)}>
<img src={Edit} alt='edit' width='25' height='25'/>
</button>
</div>
)
}
<hr/>
{!editingUsername ? (
<Button variant="secondary">Modifier le mot de passe</Button>
) : (
<Alert key='info' variant='info' style={{width:'100%'}}>
Vous êtes en mode "édition".
</Alert>
)}
<div className='bottom'>
<>
<Button variant="danger" onClick={handleShowDeleteModal}>Supprimer</Button>
<Modal show={showDeleteModal} onHide={handleCloseDeleteModal}>
<Modal.Header closeButton>
<Modal.Title>Confirmation de suppression</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>
Pour confirmer la suppression de votre compte, veuillez
entrer la phrase : "supprimer mon compte".
</p>
<Form.Control
type='text'
placeholder='Entrez la phrase de confirmation'
value={confirmationPhrase}
onChange={handleConfirmationPhraseChange}
/>
{
showWrong &&
<Alert key='infomodel' variant='danger' style={{width:'100%'}}>
La phrase de confirmation est incorrecte.
</Alert>
}
</Modal.Body>
<Modal.Footer>
<Button variant='secondary' onClick={handleCloseDeleteModal}>
Annuler
</Button>
<Button variant='danger' onClick={handleDeleteAccount}>Supprimer mon compte</Button>
</Modal.Footer>
</Modal>
</>
</div>
</div>
</div>
</>
);
};
export default Profile;

@ -1,36 +1,86 @@
import React, { Component } from 'react';
import React, { useState, useEffect } from 'react';
import { AiOutlineSend } from 'react-icons/ai';
import { useNavigate } from 'react-router-dom';
import AuthService from '../services/AuthService';
import '../Style/Global.css';
export default class SignUp extends Component {
render() {
const SignUp = () => {
const navigate = useNavigate();
const [error, setError] = useState<string | null>(null);
const [showConfirmation, setShowConfirmation] = useState(false);
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
try {
const data = {
pseudo: (event.target as any).pseudo.value,
password: (event.target as any).password.value,
Cpassword: (event.target as any).Cpassword.value,
};
const validation = await AuthService.validateSignUp(data);
if (!validation.valid) {
setError(validation.error);
} else {
setError(null);
const result = await AuthService.signUp(data);
console.log(result);
setShowConfirmation(true);
setTimeout(() => {
navigate('/login'); // 3 secondes avant de rediriger vers la page de connexion
}, 3000);
}
} catch (error: any) {
setError(error.message);
}
};
useEffect(() => {
return () => {
setShowConfirmation(false);
};
}, []);
return (
<div className="form-container">
<form>
<form onSubmit={handleSubmit}>
<br/>
<h3>Sign Up</h3>
<div className="mb-3">
<label>Pseudo</label>
<label htmlFor="pseudo">Pseudo</label>
<input
type="pseudo"
type="text"
id="pseudo"
name="pseudo"
className="form-control"
placeholder="Entrez votre pseudo ici"
/>
</div>
<div className="mb-3">
<label>Password</label>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
name="password"
className="form-control"
placeholder="Entrez votre mot de passe ici"
required
/>
</div>
<div className="mb-3">
<label>Confirm Password</label>
<label htmlFor="Cpassword">Confirm Password</label>
<input
type="Cpassword"
type="password"
id="Cpassword"
name="Cpassword"
className="form-control"
placeholder="Confirmez votre mot de passe ici"
required
/>
</div>
<div className="d-grid">
@ -42,7 +92,20 @@ export default class SignUp extends Component {
Vous avez déjà un <a href="/login">compte</a> ?
</p>
</form>
{error && (
<div className="alert alert-danger" role="alert">
{error}
</div>
)
}
}
)}
{showConfirmation && (
<div className="alert alert-success" role="alert">
Inscription réussie ! Vous serez redirigé vers la page de connexion dans 3 secondes.
</div>
)}
</div>
);
};
export default SignUp;

@ -10,19 +10,102 @@ import Indice from "../model/Indices/Indice";
import { ColorToString, SportToString } from "../model/EnumExtender";
import GraphCreator from "../model/Graph/GraphCreator";
import { DataSet, Network } from "vis-network";
import Pair from "../model/Pair";
function generateLatexCode(personsNet : PersonNetwork, choosenPerson : Person, choosenIndices : Indice[], network: Network): string {
let latexCode = "";
//*Setup
latexCode += "\\documentclass[11pt]{article}\n"
latexCode += "\\usepackage[landscape]{geometry}\n"
latexCode += "\\usepackage{fullpage}\n"
latexCode += "\\usepackage{times}\n"
latexCode += "\\usepackage{tikz}\n"
latexCode += "\\usepackage{paralist}\n"
latexCode += "\\usepackage{geometry}\n"
latexCode += "\\usetikzlibrary {shapes.multipart}\n"
latexCode += "\\geometry{margin=0.5cm}\n"
latexCode += "\\newcommand{\\Basketball}{\\includegraphics[width=.5cm]{ballon-de-basket.png}}\n"
latexCode += "\\newcommand{\\Football}{\\includegraphics[width=.4cm]{ballon-de-foot.png}}\n"
latexCode += "\\newcommand{\\Bowling}{\\includegraphics[width=.5cm]{bowling.png}}\n"
latexCode += "\\newcommand{\\Baseball}{\\includegraphics[width=.5cm]{baseball.png}}\n"
latexCode += "\\newcommand{\\Tennis}{\\includegraphics[width=.5cm]{tennis.png}}\n"
//** Header
latexCode+= "\\begin{document}\n"
latexCode+= "\\thispagestyle{empty}\n"
latexCode+= "Voici le graphe de SocialGraphe\n"
latexCode+= "\\begin{center}\n"
latexCode+= "\\resizebox{.65\\textwidth}{!}{\n"
latexCode+= "\\begin{tikzpicture}[scale=.18]\n"
personsNet.getPersons().forEach((person, index) => {
var nodesData = network.getPositions();
// Obtenir les coordonnées du nœud
const nodeId = person.getId().toString();
const position = nodesData[nodeId];
if (position) {
const x = (position.x / 5).toFixed(2); // Arrondir à 2 décimales
const y = (position.y / 5).toFixed(2);
latexCode += ` \\node[draw, circle split, align=center] (${person.getId()}) at (${x},${y}) { ${person.getName()} ${person.getAge()} \\nodepart{lower}`;
latexCode += `${ColorToString(person.getColor(), "fr")} \\\\`
person.getSports().forEach((sport) => { latexCode += ` \\${SportToString(sport, 'fr')}{}` });
latexCode += "};\n";
} else {
console.error(`Les coordonnées du nœud ${nodeId} ne sont pas disponibles.`);
}
});
personsNet.getPersons().forEach((person) => {
person.getFriends().forEach((friend) => {
latexCode += ` \\draw (${person.getId()}) -- (${friend.getId()});\n`;
});
console.log(person.getFriends().length);
});
latexCode += "\\end{tikzpicture}\n";
latexCode += "}\n"
latexCode += "\\end{center}\n";
//* Zone d'énoncé :
latexCode += "\n\n\\paragraph{Première énigme}\n"
latexCode += "Trouver qui est le coupable avec les indices suivants.\n"
latexCode += "\\begin{compactitem}\n"
choosenIndices.forEach((indice, index) => {
latexCode += `\\item Indice ${index + 1} : ${indice.ToString('fr')}.\n`
})
latexCode += "\\end{compactitem}\n"
//* Solution
latexCode += "% Solution : " + choosenPerson.getName() + "\n";
latexCode += "\\end{document}\n"
return latexCode
}
function generateLatexCodeEnigme(personsNet : PersonNetwork, choosenPerson : Person, choosenIndices : Indice[], network: Network, map : Map<number, Pair<Indice, boolean>[]>): string {
let latexCode = "";
//*Setup
latexCode += "\\documentclass[11pt]{article}\n"
latexCode += "\\usepackage[landscape]{geometry}\n"
latexCode += "\\usepackage{fullpage}\n"
latexCode += "\\usepackage{times}\n"
latexCode += "\\usepackage{tikz}\n"
latexCode += "\\usepackage{paralist}\n"
latexCode += "\\usepackage{geometry}\n"
latexCode += "\\usetikzlibrary {shapes.multipart}\n"
latexCode += "\\geometry{margin=0.5cm}\n"
latexCode += "\\newcommand{\\Basketball}{\\includegraphics[width=.5cm]{ballon-de-basket.png}}\n"
@ -37,7 +120,8 @@ function generateLatexCode(personsNet : PersonNetwork, choosenPerson : Person, c
latexCode+= "\\thispagestyle{empty}\n"
latexCode+= "Voici le graphe de SocialGraphe\n"
latexCode+= "\\begin{center}\n"
latexCode+= "\\begin{tikzpicture}[scale=.17]\n"
latexCode+= "\\resizebox{.65\\textwidth}{!}{\n"
latexCode+= "\\begin{tikzpicture}[scale=.18]\n"
personsNet.getPersons().forEach((person, index) => {
@ -50,7 +134,7 @@ function generateLatexCode(personsNet : PersonNetwork, choosenPerson : Person, c
const x = (position.x / 9).toFixed(2); // Arrondir à 2 décimales
const y = (position.y / 9).toFixed(2);
latexCode += ` \\node[draw, circle split, align=center] (${person.getId()}) at (${x},${y}) { ${person.getName()} \\\\ ${person.getAge()} \\nodepart{lower}`;
latexCode += ` \\node[draw, circle split, align=center] (${person.getId()}) at (${x},${y}) { ${person.getName()} ${person.getAge()} \\nodepart{lower}`;
latexCode += `${ColorToString(person.getColor(), "fr")} \\\\`
person.getSports().forEach((sport) => { latexCode += ` \\${SportToString(sport, 'fr')}{}` });
latexCode += "};\n";
@ -67,6 +151,7 @@ function generateLatexCode(personsNet : PersonNetwork, choosenPerson : Person, c
});
latexCode += "\\end{tikzpicture}\n";
latexCode += "}\n"
latexCode += "\\end{center}\n";
//* Zone d'énoncé :
@ -77,10 +162,34 @@ function generateLatexCode(personsNet : PersonNetwork, choosenPerson : Person, c
latexCode += "Trouver qui est le coupable avec les indices suivants.\n"
latexCode += "\\begin{compactitem}\n"
const personIndice = new Map<number, string[]>()
choosenIndices.forEach((i, index) => {
personIndice.set(index, [])
})
choosenIndices.forEach((indice, index) => {
map.forEach((pairs, index) => {
pairs.forEach((pair) => {
const person = personsNet.getPersons().find((n) => index == n.getId())
const indice = choosenIndices.findIndex((i) => pair.first.getId() == i.getId())
if (person != undefined && indice != -1){
let string = "L'indice numéro " + (indice + 1) + " répond "
if (pair.second){
string += "vrai "
}
else{
string += "faux "
}
string += "pour " + person.getName()
personIndice.get(indice)?.push(string)
}
})
});
latexCode += `\\item Indice ${index + 1} : ${indice.ToString('fr')}.\n`
personIndice.forEach((indices, index) => {
latexCode += `\\item Indice ${index + 1}:\n`
indices.forEach((string) => {
latexCode += `\\item ${string}.\n`
})
})
latexCode += "\\end{compactitem}\n"
@ -92,4 +201,4 @@ function generateLatexCode(personsNet : PersonNetwork, choosenPerson : Person, c
return latexCode
}
export default generateLatexCode
export {generateLatexCode, generateLatexCodeEnigme}

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 254 KiB

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

@ -1,36 +0,0 @@
import {
Network,
Options,
Data,
Edge,
Node
} from "vis-network/standalone/esm/vis-network";
import React, { useState, useLayoutEffect, useRef } from "react";
export interface UseVisNetworkOptions {
options: Options;
nodes: Node[];
edges: Edge[];
}
export default (props: UseVisNetworkOptions) => {
const { edges, nodes, options } = props;
const [network, addNetwork] = useState<Network | null>(null);
const ref = useRef<HTMLDivElement>(null);
const data: Data = { nodes, edges };
useLayoutEffect(() => {
if (ref.current) {
const instance = new Network(ref.current, data, options);
addNetwork(instance);
}
return () => network?.destroy();
}, []);
return {
network,
ref
};
};

@ -1,6 +1,6 @@
import { io } from "socket.io-client";
import { ADRESSE_WEBSERVER } from "./AdressSetup";
const socket = io("http://localhost:3002");
const socket = io(ADRESSE_WEBSERVER);
export {socket}

@ -21,6 +21,7 @@
"log_in":" Log in ",
"sign_up":" Sign up ",
"log_out":" Log out ",
"join" : "Join",
"create_room" : "Create room",

@ -21,6 +21,7 @@
"log_in":" Se connecter ",
"sign_up":" S'inscrire ",
"log_out":" Se déconnecter ",
"join" : "Rejoindre",
@ -54,7 +55,7 @@
"color_start": "Le suspect a les cheveux",
"color_end": "",
"nb_friends_indice_start": "Le suspect",
"nb_friends_indice_start": "Le suspect a",
"nb_friends_indice_end": "amis",
"nb_sports_indice_start": "Le suspect pratique",

@ -13,8 +13,8 @@ abstract class Bot extends Player{
public actualNetwork: Map<Person, Pair<number, boolean>[]>
constructor( id: string, name: string){
super(id, name);
constructor( id: string, name: string, profilePicture: string){
super(id, name, profilePicture);
this.actualNetwork = new Map<Person, Pair<number, boolean>[]>()
this.index = -1

@ -0,0 +1,74 @@
import SessionService from "../../services/SessionService";
import { socket } from "../../SocketConfig";
import User from "../User";
import IUserService from "./IUserService";
class DbUserService implements IUserService{
async fetchUserInformation(): Promise<[User | null, boolean]> {
try {
const sessionData = await SessionService.getSession();
// Vérifie si il y a une session
if (sessionData.user) {
// Il y a une session on récupère les infos du joueur
const updatedPlayer: User = new User(socket.id, sessionData.user.pseudo, sessionData.user.profilePicture, {
nbGames: sessionData.user.soloStats.nbGames,
bestScore: sessionData.user.soloStats.bestScore,
avgNbTry: sessionData.user.soloStats.avgNbTry,
},
{
nbGames: sessionData.user.onlineStats.nbGames,
nbWins: sessionData.user.onlineStats.nbWins,
ratio: sessionData.user.onlineStats.ratio,
})
return [updatedPlayer, true]
} else {
// Pas de session on génère un guest random
const guestPlayer: User = new User(socket.id, 'Guest_' + Math.floor(Math.random() * 1000000), '',
{
nbGames: 0,
bestScore: 0,
avgNbTry: 0,
},
{
nbGames: 0,
nbWins: 0,
ratio: 0,
})
return [guestPlayer, false]
}
} catch (error) {
console.error(error);
return [null, false]
}
}
async updateSoloStats(pseudo: string, nbGames: number, bestScore: number, avgNbTry: number): Promise<void> {
try {
const result = await SessionService.updateSoloStats(pseudo, nbGames, bestScore, avgNbTry);
if (result) {
console.log("Stats solo updated");
} else {
console.log("Stats solo not updated");
}
} catch (error) {
console.error(error);
}
}
async updateOnlineStats(pseudo: string, nbGames: number, bestScore: number, ratio: number): Promise<void> {
try {
const result = await SessionService.updateOnlineStats(pseudo, nbGames, bestScore, ratio);
if (result) {
console.log("Stats online updated");
} else {
console.log("Stats online not updated");
}
} catch (error) {
console.error(error);
}
}
}
export default DbUserService

@ -0,0 +1,10 @@
import User from "../User";
interface IUserService{
fetchUserInformation(): Promise<[User | null, boolean]>
updateSoloStats(pseudo: string, nbGames: number, bestScore: number, avgNbTry: number): Promise<void>
updateOnlineStats(pseudo: string, nbGames: number, bestScore: number, ratio: number): Promise<void>
}
export default IUserService

@ -0,0 +1,12 @@
import IUserService from "./IUserService";
class Manager{
public userService: IUserService
constructor(userService: IUserService){
this.userService = userService
}
}
export default Manager

@ -8,18 +8,23 @@ import Pair from "./Pair";
import Person from "./Person";
import PersonNetwork from "./PersonsNetwork";
import Player from "./Player";
import DefaultImg from "../res/img/bot.png"
class EasyBot extends Bot{
constructor(id: string, name: string){
super(id, name)
constructor(id: string, pseudo: string, profilePicture: string){
if (profilePicture === undefined || profilePicture === ""){
profilePicture=DefaultImg
}
super(id, pseudo, profilePicture)
}
toJson() {
return {
type: "EasyBot",
id: this.id,
name: this.name,
pseudo: this.pseudo,
profilePicture: this.profilePicture
};
}
@ -93,6 +98,9 @@ class EasyBot extends Bot{
tabFilterPerson.push(p)
}
})
if (tabFilterPerson.length == 0){
return -1
}
return tabFilterPerson[Math.floor(Math.random() * tabFilterPerson.length)].getId()
}

@ -0,0 +1,72 @@
import IndiceTesterFactory from "./Factory/IndiceTesterFactory";
import Indice from "./Indices/Indice";
import Pair from "./Pair";
import Person from "./Person";
import PersonNetwork from "./PersonsNetwork";
class EnigmeDuJourCreator{
static createEnigme(personNetwork: PersonNetwork, choosenIndices: Indice[], choosenPerson: Person, allIndices: Indice[]): Map<number, Pair<Indice, boolean>[]>{
const map = new Map<number, Pair<Indice, boolean>[]>()
personNetwork.getPersons().forEach((p) =>{
map.set(p.getId(), [])
})
choosenIndices.forEach((choosenIndice) => {
const choosenIndiceTester = IndiceTesterFactory.Create(choosenIndice)
const modifiedPersons: Pair<Person, boolean>[] = []
let possibleIndices: Indice[] = [...allIndices]
let i = 0
while (possibleIndices.length != 1){
let tmpPossibleIndices: Indice[] = [...possibleIndices]
let choosenPair : Pair<Person, boolean> = new Pair(personNetwork.getPersons()[0], true)
for(const person of personNetwork.getPersons().filter((p) => p.getId() !== choosenPerson.getId())){
const veryTmpIndice = [...possibleIndices]
if (!choosenIndiceTester.Works(person)){
possibleIndices.forEach((possibleIndice, index) =>{
const tester = IndiceTesterFactory.Create(possibleIndice)
if (tester.Works(person)){
const t = veryTmpIndice.findIndex((tmpIndice) => tmpIndice.getId() == possibleIndice.getId())
if (t != -1){
veryTmpIndice.splice(t, 1)
}
}
})
if (veryTmpIndice.length<tmpPossibleIndices.length){
tmpPossibleIndices = veryTmpIndice
choosenPair = new Pair(person, false)
}
}
else{
possibleIndices.forEach((possibleIndice, index) =>{
const tester = IndiceTesterFactory.Create(possibleIndice)
if (!tester.Works(person)){
const t = veryTmpIndice.findIndex((tmpIndice) => tmpIndice.getId() == possibleIndice.getId())
if (t != -1){
veryTmpIndice.splice(t, 1)
}
}
})
if (veryTmpIndice.length<tmpPossibleIndices.length){
tmpPossibleIndices = veryTmpIndice
choosenPair = new Pair(person, true)
}
}
}
possibleIndices = [...tmpPossibleIndices]
modifiedPersons.push(choosenPair)
console.log(possibleIndices)
}
console.log("choosenIndice => " + choosenIndice.ToString("fr"))
console.log("possibleIndices => " + possibleIndices[0].ToString("fr"))
modifiedPersons.forEach((pair) =>{
map.get(pair.first.getId())?.push(new Pair(choosenIndice, pair.second))
})
})
return map
}
}
export default EnigmeDuJourCreator

@ -1,18 +0,0 @@
import Player from "./Player";
class Human extends Player{
constructor(id: string, name: string){
super(id, name)
}
toJson() {
return {
type: "Human",
id: this.id,
name: this.name,
};
}
}
export default Human

@ -17,7 +17,7 @@ class NetworkGenerator{
const tabPerson: Person[] = []
const tabNames = json.names
const tabNames = [...json.names]
/*
let id = 0

@ -1,10 +1,14 @@
abstract class Player{
public id: string
public name: string;
public pseudo: string;
public profilePicture: string
constructor(id: string, name: string){
constructor(id: string, pseudo: string, profilePicture: string){
this.id=id
this.name=name
this.pseudo=pseudo
this.profilePicture=profilePicture
}
abstract toJson(): any

@ -22,20 +22,14 @@ class Stub{
let test = 7
for (let i: Color=0; i<5; i++){
for (let j: Color=0; j<5; j++){
if (j==i){
continue
}
for (let j: Color=i + 1; j<5; j++){
indices.push(new ColorIndice(test, [i, j]))
test++
}
}
for (let i: Sport=0; i<5; i++){
for (let j: Sport=0; j<5; j++){
if (j==i){
continue
}
for (let j: Sport=i + 1; j<5; j++){
indices.push(new SportIndice(test, [i, j]))
test++
}

@ -0,0 +1,27 @@
import Player from "./Player";
import defaultImg from "../res/img/Person.png"
class User extends Player{
public soloStats: any
public onlineStats: any
constructor(id: string, pseudo: string, profilePicture: string, soloStats: any, onlineStats: any){
super(id, pseudo, profilePicture || defaultImg)
this.soloStats=soloStats
this.onlineStats=onlineStats
}
toJson() {
return {
type: "User",
id: this.id,
profilePicture: this.profilePicture,
pseudo: this.pseudo,
soloStats: this.soloStats,
onlineStats: this.onlineStats
};
}
}
export default User

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

@ -22,7 +22,6 @@
"Riley",
"Layla",
"Stella",
"Aurora",
"Natalie",
"Zoe",
"Lucy",
@ -48,7 +47,6 @@
"Bella",
"Sadie",
"Hailey",
"Aurora",
"Liam",
"Noah",
"Oliver",
@ -69,7 +67,6 @@
"Jack",
"Jayden",
"Owen",
"Noah",
"Ethan",
"Mason",
"Logan",
@ -93,4 +90,3 @@
"Julian"
]
}

@ -0,0 +1,137 @@
const UserService = require('../services/UserService');
const DatabaseService = require('../services/DatabaseService');
const bcrypt = require('bcrypt');
const sqlite3 = require('sqlite3');
class AuthController {
static async signUp(req, res) {
const databaseService = new DatabaseService();
const pseudo = req.body.pseudo;
const date = new Date();
const hour = date.getHours();
const minutes = date.getMinutes();
try {
await databaseService.connect();
// Vérifier que le pseudo n'existe pas déjà
const verif = await databaseService.getUserByPseudo(pseudo);
if (verif) {
res.status(400).json({ error: 'Le pseudo est déjà utilisé.' });
return;
}
// Créer un nouvel utilisateur
const currentUser = await UserService.createUser(req.body);
const insertedUser = await databaseService.insertUser(currentUser);
const user = await databaseService.getUserByPseudo(pseudo);
// Initialiser les stats de l'utilisateur
await databaseService.initSoloStats(user.idUser);
await databaseService.initOnlineStats(user.idUser);
const soloStats = await databaseService.getSoloStatsByUserId(user.idUser);
const onlineStats = await databaseService.getOnlineStatsByUserId(user.idUser);
await databaseService.updateUserIDStats(user.idUser, soloStats.idSoloStats, onlineStats.idOnlineStats);
console.log("[" + hour + ":" + minutes + "] " + user.pseudo + " have been registered.");
res.status(201).json({ message: 'Inscription réussie', user: insertedUser});
}
catch (error) {
// Gérer les erreurs
console.error(error);
res.status(500).json({ error: 'Erreur lors de l\'inscription.' });
}
finally {
await databaseService.disconnect();
}
}
static async signIn(req, res) {
const databaseService = new DatabaseService();
const date = new Date();
const hour = date.getHours();
const minutes = date.getMinutes();
try{
await databaseService.connect();
// Vérifier que le pseudo existe
const pseudo = req.body.pseudo;
const user = await databaseService.getUserByPseudo(pseudo);
if (!user) {
res.status(400).json({ error: 'Le pseudo n\'existe pas.' });
return;
}
// Vérifier que le mot de passe est correct
const password = req.body.password;
const validPassword = await bcrypt.compare(password, user.password);
if (!validPassword) {
res.status(400).json({ error: 'Le mot de passe est incorrect.' });
return;
}
// Stocker l'utilisateur dans la session)
req.session.user = user;
// Envoyer une réponse réussie
console.log("[" + hour + ":" + minutes + "] " + user.pseudo + " have been connected.");
res.status(200).json({ message: 'Connexion réussie', user: user });
}
catch(error){
// Gérer les erreurs
console.error(error);
res.status(500).json({ error: 'Erreur lors de la connexion.' });
}
finally{
await databaseService.disconnect();
}
}
static async logout(req, res) {
const pseudo = req.session.user.pseudo;
const date = new Date();
const hour = date.getHours();
const minutes = date.getMinutes();
// Détruire la session pour déconnecter l'utilisateur
req.session.destroy((err) => {
if (err) {
console.error(err);
res.status(500).json({ error: 'Erreur lors de la déconnexion.' });
} else {
console.log("[" + hour + ":" + minutes + "] " + pseudo + " have been disconnected.");
res.status(200).json({ message: 'Déconnexion réussie' });
}
});
}
static async delAccount(req, res){
const db = new DatabaseService();
try{
await db.connect();
const user = await db.getUserByPseudo(req.body.pseudo);
if(!user){
res.status(400).json({ error: 'Le pseudo n\'existe pas.' });
return;
}
await db.deleteSoloStat(user.idUser);
await db.deleteOnlineStat(user.idUser);
await db.deleteUser(user.idUser);
}
catch(error){
console.error(error);
res.status(500).json({ error: 'Erreur lors de la supression du compte.' });
}
finally{
db.disconnect();
}
}
}
module.exports = AuthController;

@ -0,0 +1,146 @@
const DatabaseService = require('../services/DatabaseService');
class SessionController {
static async getUserInformation(req, res) {
const db = new DatabaseService();
const date = new Date();
const hour = date.getHours();
const minutes = date.getMinutes();
try{
await db.connect();
if (!req.session.user) {
res.status(200).json({ error: "true", message: 'User not found' });
return;
}
req.session.user.soloStats = await db.getSoloStatsByUserId(req.session.user.idUser);
req.session.user.onlineStats = await db.getOnlineStatsByUserId(req.session.user.idUser);
console.log("[" + hour + ":" + minutes + "] " + req.session.user.pseudo + " have a session.");
res.status(200).json({ user: req.session.user });
}
catch(error){
console.error(error);
res.status(500).json({ error: 'Erreur lors de la récupération de l\'utilisateur.' });
}
finally{
await db.disconnect();
}
}
static async updateSoloStats(req, res) {
const db = new DatabaseService();
const date = new Date();
const hour = date.getHours();
const minutes = date.getMinutes();
try{
await db.connect();
const user = await db.getUserByPseudo(req.body.pseudo);
if (!user) {
res.status(200).json({ error: "true", message: 'User not found' });
return;
}
const soloStats = await db.getSoloStatsByUserId(user.idUser);
if (!soloStats) {
res.status(200).json({ error: "true", message: 'Solo stats not found' });
return;
}
await db.updateSoloStats(user.idUser, req.body.nbGames, req.body.bestScore, req.body.avgNbTry);
const newSoloStats = await db.getSoloStatsByUserId(user.idUser);
req.session.user.soloStats = newSoloStats;
console.log("[" + hour + ":" + minutes + "] " + req.session.user.pseudo + "'s solot_stats are updated.");
res.status(200).json({ user: req.session.user });
}
catch(error){
console.error(error);
res.status(500).json({ error: 'Erreur lors de la mise à jour des statistiques solo.' });
}
finally{
await db.disconnect();
}
}
static async updateOnlineStats(req, res) {
const db = new DatabaseService();
const date = new Date();
const hour = date.getHours();
const minutes = date.getMinutes();
try{
await db.connect();
const user = await db.getUserByPseudo(req.body.pseudo);
if (!user) {
res.status(200).json({ error: "true", message: 'User not found' });
return;
}
const onlineStats = await db.getOnlineStatsByUserId(user.idUser);
if (!onlineStats) {
res.status(200).json({ error: "true", message: 'Online stats not found' });
return;
}
await db.updateOnlineStats(user.idUser, req.body.nbGames, req.body.nbWins, req.body.ratio);
const newOnlineStats = await db.getOnlineStatsByUserId(user.idUser);
req.session.user.onlineStats = newOnlineStats;
console.log("[" + hour + ":" + minutes + "] " + req.session.user.pseudo + "'s online_stats are updated.");
res.status(200).json({ user: req.session.user });
}
catch(error){
console.error(error);
res.status(500).json({ error: 'Erreur lors de la mise à jour des statistiques en ligne.' });
}
finally{
await db.disconnect();
}
}
static async UpdatePseudo(req, res){
const db = new DatabaseService();
try{
await db.connect();
const user = await db.getUserByPseudo(req.body.pseudo);
console.log("utilisateur" + user.idUser + " pseudo" + user.pseudo)
if (!user) {
res.status(200).json({ error: "true", message: 'User not found' });
return;
}
await db.updatePseudo(user.idUser, req.body.newPseudo); //* update
const updatedUser = await db.getUserByPseudo(req.body.newPseudo);
console.log("updaetdutilisateur" + updatedUser.idUser + " pseudo" + updatedUser.pseudo)
req.session.user.pseudo = updatedUser.pseudo;
console.log("req.session.user.pseudo" + req.session.user.pseudo)
res.status(200).json({ user: req.session.user }); //verif rep
}
catch(error){
console.error(error);
res.status(500).json({ error: 'Erreur lors de la modification du pseudo de l\'utilisateur.' });
}
finally{
await db.disconnect();
}
}
}
module.exports = SessionController;

@ -0,0 +1,18 @@
const express = require('express');
const router = express.Router();
const AuthController = require('../controllers/AuthController');
const SessionController = require('../controllers/SessionController');
// Routes pour l'authentification
router.post('/auth/signup', AuthController.signUp);
router.post('/auth/signin', AuthController.signIn);
router.delete('/auth/logout', AuthController.logout)
router.delete('/auth/delAccount', AuthController.delAccount)
// Routes pour les sessions
router.get('/session', SessionController.getUserInformation);
router.put('/session/updatePseudo', SessionController.UpdatePseudo);
router.put('/session/updateSoloStats', SessionController.updateSoloStats);
router.put('/session/updateOnlineStats', SessionController.updateOnlineStats);
module.exports = router;

@ -0,0 +1,59 @@
const express = require('express');
const session = require('express-session');
const cors = require('cors');
const bodyParser = require('body-parser');
const crypto = require('crypto');
const authRoutes = require('./routes/authRoutes');
const DatabaseService = require('./services/DatabaseService');
const app = express();
const port = 3003;
// Middleware
app.use(cors(
{
origin: ["http://localhost:3000", "http://localhost:3000"],
credentials: true
}
)); // Autoriser les requêtes cross-origin
app.use(bodyParser.json()); // Parser le body des requêtes en JSON
// Session
const secret = crypto.randomBytes(32).toString('hex');
app.use(session({
secret: secret,
resave: false,
saveUninitialized: true,
cookie: {
secure: process.env.NODE_ENV === 'production' ? true : false,
maxAge: 60 * 60 * 1000
}
}));
// Middleware pour les routes
app.use('/', authRoutes);
// Middleware de gestion des erreurs
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Erreur interne du serveur!');
});
// Middleware pour la gestion des erreurs 404
app.use((req, res) => {
res.status(404).send('Page non trouvée');
});
// Connexion à la base de données sqlite3 au démarrage du serveur
const databaseService = new DatabaseService();
databaseService.connect()
.then(() => {
// Lancer le serveur
app.listen(port, () => {
console.log(`Serveur lancé sur le port ${port}`);
});
})
.catch((err) => {
console.error(err);
});

@ -0,0 +1,231 @@
const sqlite3 = require('sqlite3');
const path = require('path');
const { rejects } = require('assert');
class DatabaseService {
constructor(){
this.db_name = "socialgraph";
}
async connect(client){
const dbPath = path.resolve(__dirname, `../db/${this.db_name}.db`)
return new Promise((resolve, reject) => {
this.client = new sqlite3.Database(dbPath,
sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE,
(err) => {
if(err){
reject(err);
}
else{
resolve();
}
});
});
}
async disconnect(){
return new Promise((resolve, reject) => {
this.client.close((err) => {
if(err){
reject(err);
}
else{
resolve();
}
});
});
}
// Récupère l'utilisateur par son pseudo
async getUserByPseudo(pseudo){
return new Promise((resolve, reject) => {
this.client.get('SELECT * FROM users WHERE pseudo = ?', pseudo, (err, result) => {
if(err){
reject(err);
}
else{
resolve(result);
}
});
});
}
// Récupère l'utilisateur par son id
async getUserByID(id){
return new Promise((resolve, reject) => {
this.client.get('SELECT * FROM users WHERE idUser = ?', id, (err, result) => {
if(err){
reject(err);
}
else{
resolve(result);
}
});
});
}
// Récupère stats solo de l'utilisateur
async getSoloStatsByUserId(userId){
return new Promise((resolve, reject) => {
this.client.get('SELECT * FROM solo_stats WHERE idUser = ?', userId, (err, result) => {
if(err){
reject(err);
}
else{
resolve(result);
}
});
});
}
// Récupère stats online de l'utilisateur
async getOnlineStatsByUserId(userId){
return new Promise((resolve, reject) => {
this.client.get('SELECT * FROM online_stats WHERE idUser = ?', userId, (err, result) => {
if(err){
reject(err);
}
else{
resolve(result);
}
});
});
}
// insère un utilisateur dans la base de données
async insertUser(user) {
return new Promise((resolve, reject) => {
const { pseudo, password } = user;
this.client.run('INSERT INTO users (pseudo, password) VALUES (?, ?)', [pseudo, password], (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
// Mettre à jour l'id de stats solo et online de l'utilisateur
async updateUserIDStats(userId, soloStatsId, onlineStatsId){
return new Promise((resolve, reject) => {
this.client.run('UPDATE users SET idSoloStats = ?, idOnlineStats = ? WHERE idUser = ?', [soloStatsId, onlineStatsId, userId], (err, result) => {
if(err){
reject(err);
}
else{
resolve(result);
}
});
});
}
// Mettre à jour les stats solo de l'utilisateur
async updateSoloStats(userId, nbGames, bestScore, avgNbTry){
return new Promise((resolve, reject) => {
this.client.run('UPDATE solo_stats SET nbGames = ?, bestScore = ?, avgNbTry = ? WHERE idUser = ?', [nbGames, bestScore, avgNbTry, userId], (err, result) => {
if(err){
reject(err);
}
else{
resolve(result);
}
});
});
}
// Mettre à jour les stats online de l'utilisateur
async updateOnlineStats(userId, nbGames, nbWins, ratio){
return new Promise((resolve, reject) => {
this.client.run('UPDATE online_stats SET nbGames = ?, nbWins = ?, ratio = ? WHERE idUser = ?', [nbGames, nbWins, ratio, userId], (err, result) => {
if(err){
reject(err);
}
else{
resolve(result);
}
});
});
}
async initSoloStats(userId) {
return new Promise((resolve, reject) => {
this.client.run('INSERT INTO solo_stats (nbGames, bestScore, avgNbTry, idUser) VALUES (?, ?, ?, ?)', 0, 0, 0.0, userId, (err, result) => {
if(err){
reject(err);
}
else{
resolve(result);
}
});
});
}
async initOnlineStats(userId) {
return new Promise((resolve, reject) => {
this.client.run('INSERT INTO online_stats (nbGames, nbWins, ratio, idUser) VALUES (?, ?, ?, ?)', 0, 0, 0.0, userId, (err, result) => {
if(err){
reject(err);
}
else{
resolve(result);
}
});
});
}
async deleteUser(userId){
return new Promise((resolve, reject) => {
this.client.run('DELETE FROM users WHERE idUser=?', userId, (err, result) => {
if(err){
reject(err);
}
else{
resolve(result);
}
});
});
}
async deleteSoloStat(userId){
return new Promise((resolve, reject) => {
this.client.run('DELETE FROM solo_stats WHERE idUser=?', userId, (err, result) => {
if(err){
reject(err);
}
else{
resolve(result);
}
});
});
}
async deleteOnlineStat(userId){
return new Promise((resolve, reject) => {
this.client.run('DELETE FROM online_stats WHERE idUser=?', userId, (err, result) => {
if(err){
reject(err);
}
else{
resolve(result);
}
});
});
}
async updatePseudo(userId, newPseudo){
return new Promise((resolve, reject) => {
this.client.run('UPDATE users SET pseudo = ? WHERE idUser = ?', newPseudo, userId, (err, result) => {
if(err){
reject(err);
}
else{
resolve(result);
}
});
});
}
}
module.exports = DatabaseService;

@ -0,0 +1,17 @@
const bcrypt = require('bcrypt');
class UserService {
static async createUser(userData) {
// Hacher le mot de passe avant de le stocker dans la base de données
const hashedPassword = await bcrypt.hash(userData.password, 10);
// Retournez l'utilisateur créé
return { pseudo: userData.pseudo, password: hashedPassword };
}
static async initUserStats(idUser) {
return { nbGames: 0, nbWins: 0, ratio: 0.0, idUser: idUser };
}
}
module.exports = UserService;

@ -0,0 +1,113 @@
import VerificationService from './VerificationService';
import {ADRESSE_DBSERVER} from "../AdressSetup"
import User from '../model/User';
class AuthService{
// Méthode pour vérifier les données de connexion
static async validateSignUp(data: any): Promise<{valid: boolean, error: string}> {
return VerificationService.validateSignUpData(data);
}
static async validateSignIn(data: any): Promise<{valid: boolean, error: string}> {
return VerificationService.validateSignInData(data);
}
static async signUp(data: any) {
try {
const response = await fetch(ADRESSE_DBSERVER + '/auth/signup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
credentials: 'include',
});
if (response.ok) {
const result = await response.json();
return result;
} else {
const errorResponse = await response.json();
throw new Error(errorResponse.error);
}
} catch (error) {
console.error(error);
throw error;
}
}
static async signIn(data: any) {
try {
const response = await fetch(ADRESSE_DBSERVER + '/auth/signin', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
credentials: 'include',
});
if (response.ok) {
const result = await response.json();
return result;
} else {
const errorResponse = await response.json();
throw new Error(errorResponse.error);
}
}
catch (error) {
console.error(error);
throw error;
}
}
static async logout() {
try {
const response = await fetch(ADRESSE_DBSERVER + '/auth/logout', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
});
if (response.ok) {
const result = await response.json();
return result;
} else {
const errorResponse = await response.json();
throw new Error(errorResponse.error);
}
} catch (error) {
console.error(error);
throw error;
}
}
static async delAccount(pseudo: string){
try {
const response = await fetch(ADRESSE_DBSERVER + '/auth/delAccount', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ pseudo }),
credentials: 'include',
});
if (response.ok) {
const result = await response.json();
return result;
} else {
const errorResponse = await response.json();
throw new Error(errorResponse.error);
}
} catch (error) {
console.error(error);
throw error;
}
}
}
export default AuthService;

@ -0,0 +1,116 @@
import {ADRESSE_DBSERVER} from "../AdressSetup"
class SessionService {
static async getSession() {
try {
const response = await fetch(ADRESSE_DBSERVER + '/session', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
});
if (response.ok) {
const result = await response.json();
return result;
} else {
const errorResponse = await response.json();
throw new Error(errorResponse.error);
}
} catch (error) {
console.error(error);
throw error;
}
}
static async updateSoloStats(pseudo: string, nbGames: number, bestScore: number, avgNbTry: number){
try {
const response = await fetch(ADRESSE_DBSERVER + '/session/updateSoloStats', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({
pseudo,
nbGames,
bestScore,
avgNbTry
}),
});
if (response.ok) {
const result = await response.json();
return result;
} else {
const errorResponse = await response.json();
throw new Error(errorResponse.error);
}
} catch (error) {
console.error(error);
throw error;
}
}
static async updateOnlineStats(pseudo: string, nbGames: number, nbWins: number, ratio: number){
try {
console.log("updateOnlineStats : ", pseudo, nbGames, nbWins, ratio);
const response = await fetch(ADRESSE_DBSERVER + '/session/updateOnlineStats', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({
pseudo,
nbGames,
nbWins,
ratio
}),
});
if (response.ok) {
const result = await response.json();
return result;
} else {
const errorResponse = await response.json();
throw new Error(errorResponse.error);
}
} catch (error) {
console.error(error);
throw error;
}
}
static async UpdatePseudo(pseudo : string, newPseudo : string) {
console.log("pseudo : " + pseudo + " newpseudo : " + newPseudo)
try {
const response = await fetch(ADRESSE_DBSERVER + '/session/updatePseudo', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
pseudo,
newPseudo
}),
credentials: 'include',
});
if (response.ok) {
const result = await response.json();
return result;
} else {
const errorResponse = await response.json();
throw new Error(errorResponse.error);
}
} catch (error) {
console.error(error);
throw error;
}
}
}
export default SessionService;

@ -0,0 +1,31 @@
class ValidationService {
public static validateSignUpData(data: any): {valid: boolean, error: string} {
if(!data.pseudo || !data.password || !data.Cpassword) {
return {valid: false, error: 'Veuillez remplir tous les champs.'};
}
if(data.password.length < 8) {
return {valid: false, error: 'Le mot de passe doit contenir au moins 8 caractères.'};
}
if(data.password !== data.Cpassword) {
return {valid: false, error: 'Les mots de passe ne correspondent pas.'};
}
return {valid: true, error: ''};
}
public static validateSignInData(data: any): {valid: boolean, error: string} {
if(!data.pseudo || !data.password) {
return {valid: false, error: 'Veuillez remplir tous les champs.'};
}
if(data.password.length < 8) {
return {valid: false, error: 'Le mot de passe doit contenir au moins 8 caractères.'};
}
return {valid: true, error: ''};
}
}
export default ValidationService;

@ -0,0 +1,18 @@
export interface PlayerSoloStats {
nbGames: number;
bestScore: number;
avgNbTry: number;
};
export interface PlayerOnlineStats {
nbGames: number;
nbWins: number;
ratio: number;
};
export interface PlayerProps {
profilePicture: string;
pseudo: string;
soloStats: PlayerSoloStats;
onlineStats: PlayerOnlineStats;
};

File diff suppressed because it is too large Load Diff

@ -1,3 +0,0 @@
\relax
\@writefile{toc}{\contentsline {paragraph}{Première énigme}{2}{}\protected@file@percent }
\gdef \@abspage@last{2}

Binary file not shown.

@ -1,132 +0,0 @@
\documentclass[11pt]{article}
\usepackage{fullpage}
\usepackage{times}
\usepackage{tikz}
\usepackage{paralist}
\usetikzlibrary {shapes.multipart}
\newcommand{\Basketball}{\includegraphics[width=.5cm]{ballon-de-basket.png}}
\newcommand{\Football}{\includegraphics[width=.4cm]{ballon-de-foot.png}}
\newcommand{\Bowling}{\includegraphics[width=.5cm]{bowling.png}}
\newcommand{\Baseball}{\includegraphics[width=.5cm]{baseball.png}}
\newcommand{\Tennis}{\includegraphics[width=.5cm]{tennis.png}}
\begin{document}
\thispagestyle{empty}
Voici le graphe de SocialGraphe
\begin{center}
\begin{tikzpicture}[scale=.9]
\node[draw, circle split] (0) at (0,0) { Alexander \nodepart{lower} \Football{} \Bowling{}};
\node[draw, circle split] (1) at (4,0) { Wyatt \nodepart{lower} \Baseball{} \Tennis{}};
\node[draw, circle split] (2) at (8,0) { Mia \nodepart{lower} \Basketball{}};
\node[draw, circle split] (3) at (12,0) { William \nodepart{lower} \Baseball{} \Football{}};
\node[draw, circle split] (4) at (16,0) { Zoey \nodepart{lower} \Basketball{} \Bowling{} \Tennis{}};
\node[draw, circle split] (5) at (0,4) { Isabella \nodepart{lower} \Tennis{}};
\node[draw, circle split] (6) at (4,4) { Abigail \nodepart{lower} \Baseball{}};
\node[draw, circle split] (7) at (8,4) { Savannah \nodepart{lower} \Bowling{} \Basketball{} \Football{}};
\node[draw, circle split] (8) at (12,4) { Peyton \nodepart{lower} \Football{}};
\node[draw, circle split] (9) at (16,4) { Alice \nodepart{lower} \Tennis{} \Baseball{}};
\node[draw, circle split] (10) at (0,8) { Sophia \nodepart{lower} \Bowling{} \Basketball{} \Bowling{}};
\node[draw, circle split] (11) at (4,8) { Layla \nodepart{lower} \Tennis{} \Baseball{} \Football{}};
\node[draw, circle split] (12) at (8,8) { Ava \nodepart{lower} \Basketball{}};
\node[draw, circle split] (13) at (12,8) { Harper \nodepart{lower} \Bowling{}};
\node[draw, circle split] (14) at (16,8) { Sebastian \nodepart{lower} \Tennis{} \Basketball{} \Baseball{}};
\node[draw, circle split] (15) at (0,12) { Michael \nodepart{lower} \Football{}};
\node[draw, circle split] (16) at (4,12) { Natalie \nodepart{lower} \Bowling{} \Football{} \Baseball{}};
\node[draw, circle split] (17) at (8,12) { Penelope \nodepart{lower} \Basketball{}};
\node[draw, circle split] (18) at (12,12) { Lily \nodepart{lower} \Tennis{} \Tennis{}};
\node[draw, circle split] (19) at (16,12) { Eleanor \nodepart{lower} \Football{}};
\node[draw, circle split] (20) at (0,16) { Henry \nodepart{lower} \Bowling{} \Basketball{}};
\node[draw, circle split] (21) at (4,16) { Claire \nodepart{lower} \Baseball{} \Basketball{}};
\node[draw, circle split] (22) at (8,16) { Caleb \nodepart{lower} \Baseball{}};
\node[draw, circle split] (23) at (12,16) { Charlotte \nodepart{lower} \Bowling{} \Football{} \Tennis{}};
\node[draw, circle split] (24) at (16,16) { Luke \nodepart{lower} \Football{}};
\node[draw, circle split] (25) at (0,20) { Connor \nodepart{lower} \Baseball{} \Tennis{}};
\node[draw, circle split] (26) at (4,20) { Aiden \nodepart{lower} \Basketball{} \Bowling{} \Tennis{}};
\node[draw, circle split] (27) at (8,20) { Aurora \nodepart{lower} \Football{}};
\node[draw, circle split] (28) at (12,20) { Nathan \nodepart{lower} \Bowling{} \Baseball{}};
\node[draw, circle split] (29) at (16,20) { Aurora \nodepart{lower} \Basketball{}};
\draw (0) -- (11);
\draw (0) -- (13);
\draw (0) -- (18);
\draw (1) -- (13);
\draw (1) -- (24);
\draw (2) -- (22);
\draw (2) -- (16);
\draw (2) -- (9);
\draw (2) -- (6);
\draw (3) -- (4);
\draw (3) -- (20);
\draw (4) -- (28);
\draw (4) -- (3);
\draw (5) -- (17);
\draw (5) -- (15);
\draw (5) -- (24);
\draw (6) -- (2);
\draw (7) -- (17);
\draw (7) -- (24);
\draw (7) -- (22);
\draw (7) -- (11);
\draw (8) -- (25);
\draw (8) -- (21);
\draw (8) -- (24);
\draw (8) -- (11);
\draw (9) -- (2);
\draw (10) -- (25);
\draw (10) -- (26);
\draw (10) -- (27);
\draw (11) -- (0);
\draw (11) -- (7);
\draw (11) -- (8);
\draw (12) -- (20);
\draw (12) -- (27);
\draw (13) -- (0);
\draw (13) -- (1);
\draw (14) -- (15);
\draw (15) -- (5);
\draw (15) -- (14);
\draw (15) -- (20);
\draw (15) -- (17);
\draw (16) -- (2);
\draw (16) -- (26);
\draw (17) -- (5);
\draw (17) -- (7);
\draw (17) -- (15);
\draw (17) -- (20);
\draw (18) -- (0);
\draw (19) -- (23);
\draw (20) -- (3);
\draw (20) -- (12);
\draw (20) -- (15);
\draw (20) -- (17);
\draw (21) -- (8);
\draw (22) -- (2);
\draw (22) -- (7);
\draw (22) -- (23);
\draw (23) -- (19);
\draw (23) -- (22);
\draw (24) -- (1);
\draw (24) -- (5);
\draw (24) -- (7);
\draw (24) -- (8);
\draw (25) -- (8);
\draw (25) -- (10);
\draw (26) -- (10);
\draw (26) -- (16);
\draw (26) -- (29);
\draw (27) -- (10);
\draw (27) -- (12);
\draw (28) -- (4);
\draw (29) -- (26);
\end{tikzpicture}
\end{center}
\paragraph{Première énigme}
Trouver qui est le coupable avec les indices suivants.
\begin{compactitem}
\item Indice 1 : Le suspect pratique au moins du Baseball et/ou du Basketball .
\item Indice 2 : Le suspect pratique 2 ou 1 sport(s).
\item Indice 3 : Le suspect a les cheveux Roux ou Blond .
\item Indice 4 : Le suspect a au moins un ami avec les cheveux Roux .
\end{compactitem}
% Solution : Nathan
\end{document}

6
package-lock.json generated

@ -1,6 +0,0 @@
{
"name": "Cryptid",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}
Loading…
Cancel
Save