This commit is contained in:
parent
154646a124
commit
65a31cac78
10 changed files with 40 additions and 37 deletions
|
@ -15,18 +15,18 @@ jobs:
|
||||||
|
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
run: |
|
run: |
|
||||||
docker build -t git.wilmoredynamics.com/ab/ab01:${GITHUB_SHA::8} .
|
docker build -t git.wilmoredynamics.com/ab/logica:${GITHUB_SHA::8} .
|
||||||
# Tag the image with 'latest'
|
# Tag the image with 'latest'
|
||||||
docker tag git.wilmoredynamics.com/ab/ab01:${GITHUB_SHA::8} git.wilmoredynamics.com/ab/ab01:latest
|
docker tag git.wilmoredynamics.com/ab/logica:${GITHUB_SHA::8} git.wilmoredynamics.com/ab/logica:latest
|
||||||
|
|
||||||
- name: Log in to Forgejo Container Registry
|
- name: Log in to Forgejo Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: git.wilmoredynamics.com
|
registry: git.wilmoredynamics.com
|
||||||
username: AB # Votre nom d'utilisateur ou organisation Forgejo
|
username: ab # Votre nom d'utilisateur ou organisation Forgejo
|
||||||
password: ${{ secrets.FORGEJO_TOKEN }} # Votre jeton d'accès personnel Forgejo
|
password: ${{ secrets.FORGEJO_TOKEN }} # Votre jeton d'accès personnel Forgejo
|
||||||
|
|
||||||
- name: Push Docker image to Forgejo Container Registry
|
- name: Push Docker image to Forgejo Container Registry
|
||||||
run: |
|
run: |
|
||||||
docker push git.wilmoredynamics.com/ab/ab01:${GITHUB_SHA::8}
|
docker push git.wilmoredynamics.com/ab/logica:${GITHUB_SHA::8}
|
||||||
docker push git.wilmoredynamics.com/ab/ab01:latest
|
docker push git.wilmoredynamics.com/ab/logica:latest
|
|
@ -1,12 +1,12 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Box, Container, Typography, Button, useTheme, ButtonGroup, Alert, Snackbar, IconButton, Tooltip, Paper, Divider, Dialog, DialogTitle, DialogContent, LinearProgress, Badge } from '@mui/material';
|
import { Box, Container, Typography, Button, useTheme, ButtonGroup, Alert, Snackbar, IconButton, Tooltip, Paper, Divider, Dialog, DialogTitle, DialogContent, LinearProgress } from '@mui/material';
|
||||||
import { BineroGrid } from '@/components/games/binero/BineroGrid';
|
import { BineroGrid } from '@/components/games/binero/BineroGrid';
|
||||||
import { Timer } from '@/components/games/sudoku/Timer';
|
import { Timer } from '@/components/games/sudoku/Timer';
|
||||||
import { generateBinero, validateGrid, isGridComplete, calculateScore } from '@/components/games/binero/bineroLogic';
|
import { generateBinero, validateGrid, isGridComplete, calculateScore } from '@/components/games/binero/bineroLogic';
|
||||||
import { BineroRules } from '@/components/games/binero/BineroRules';
|
import { BineroRules } from '@/components/games/binero/BineroRules';
|
||||||
import { useState, useCallback, useEffect, useMemo } from 'react';
|
import { useState, useCallback, useEffect, useMemo } from 'react';
|
||||||
import { Help, Refresh, CheckCircle, Timer as TimerIcon, EmojiEvents, Undo, Save, Settings } from '@mui/icons-material';
|
import { Help, Refresh, CheckCircle, Timer as TimerIcon, EmojiEvents, Undo, Settings } from '@mui/icons-material';
|
||||||
import { useLocalStorage } from '@/hooks/useLocalStorage';
|
import { useLocalStorage } from '@/hooks/useLocalStorage';
|
||||||
|
|
||||||
type CellValue = 0 | 1 | null;
|
type CellValue = 0 | 1 | null;
|
||||||
|
@ -22,12 +22,6 @@ interface GameState {
|
||||||
gridSize: { rows: number; cols: number };
|
gridSize: { rows: number; cols: number };
|
||||||
}
|
}
|
||||||
|
|
||||||
const DIFFICULTY_MULTIPLIER = {
|
|
||||||
easy: 1,
|
|
||||||
medium: 1.5,
|
|
||||||
hard: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
const GRID_SIZES = [
|
const GRID_SIZES = [
|
||||||
{ rows: 6, cols: 6, label: '6×6' },
|
{ rows: 6, cols: 6, label: '6×6' },
|
||||||
{ rows: 8, cols: 8, label: '8×8' },
|
{ rows: 8, cols: 8, label: '8×8' },
|
||||||
|
|
|
@ -26,7 +26,7 @@ export default function RootLayout({
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<html lang="fr">
|
<html lang="fr" className={`${geistSans.variable} ${geistMono.variable}`}>
|
||||||
<body>
|
<body>
|
||||||
<ColorModeProvider>
|
<ColorModeProvider>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Box, Container, Typography, Grid, Card, CardContent, CardActions, Button, useTheme } from '@mui/material';
|
import { Box, Container, Typography, Card, CardContent, CardActions, Button, useTheme } from '@mui/material';
|
||||||
import { Extension, Grid3x3, GridOn } from '@mui/icons-material';
|
import { Extension, Grid3x3, GridOn } from '@mui/icons-material';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,8 @@ const calculateScore = (difficulty: 'easy' | 'medium' | 'hard', timeInSeconds: n
|
||||||
export default function SudokuPage() {
|
export default function SudokuPage() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [gameState, setGameState] = useState<GameState>(() => {
|
const [gameState, setGameState] = useState<GameState>(() => {
|
||||||
const savedState = localStorage.getItem('sudokuGameState');
|
if (typeof window !== 'undefined') {
|
||||||
const savedBestScores = localStorage.getItem('sudokuBestScores');
|
const savedBestScores = localStorage.getItem('sudokuBestScores');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
grid: generateSudoku('easy'),
|
grid: generateSudoku('easy'),
|
||||||
difficulty: 'easy',
|
difficulty: 'easy',
|
||||||
|
@ -39,12 +38,22 @@ export default function SudokuPage() {
|
||||||
score: 0,
|
score: 0,
|
||||||
bestScores: savedBestScores ? JSON.parse(savedBestScores) : { easy: 0, medium: 0, hard: 0 },
|
bestScores: savedBestScores ? JSON.parse(savedBestScores) : { easy: 0, medium: 0, hard: 0 },
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
grid: generateSudoku('easy'),
|
||||||
|
difficulty: 'easy',
|
||||||
|
isPlaying: false,
|
||||||
|
score: 0,
|
||||||
|
bestScores: { easy: 0, medium: 0, hard: 0 },
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const [message, setMessage] = useState<{ text: string; severity: 'success' | 'error' | 'info' }>({ text: '', severity: 'info' });
|
const [message, setMessage] = useState<{ text: string; severity: 'success' | 'error' | 'info' }>({ text: '', severity: 'info' });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
localStorage.setItem('sudokuBestScores', JSON.stringify(gameState.bestScores));
|
localStorage.setItem('sudokuBestScores', JSON.stringify(gameState.bestScores));
|
||||||
|
}
|
||||||
}, [gameState.bestScores]);
|
}, [gameState.bestScores]);
|
||||||
|
|
||||||
const handleCellChange = (row: number, col: number, value: number | null) => {
|
const handleCellChange = (row: number, col: number, value: number | null) => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Box, Paper, Typography, useTheme } from '@mui/material';
|
import { Box, Paper, Typography, useTheme, Theme } from '@mui/material';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
type CellValue = 0 | 1 | null;
|
type CellValue = 0 | 1 | null;
|
||||||
|
@ -25,13 +25,13 @@ const BineroCell = memo(({
|
||||||
isConflict: boolean;
|
isConflict: boolean;
|
||||||
showHints: boolean;
|
showHints: boolean;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
theme: any;
|
theme: Theme;
|
||||||
}) => (
|
}) => (
|
||||||
<Box
|
<Box
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
sx={{
|
sx={{
|
||||||
width: 40,
|
width: { xs: 32, sm: 40 },
|
||||||
height: 40,
|
height: { xs: 32, sm: 40 },
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
@ -50,6 +50,7 @@ const BineroCell = memo(({
|
||||||
<Typography
|
<Typography
|
||||||
variant="h6"
|
variant="h6"
|
||||||
sx={{
|
sx={{
|
||||||
|
fontSize: { xs: '1rem', sm: '1.25rem' },
|
||||||
color: value === null
|
color: value === null
|
||||||
? 'transparent'
|
? 'transparent'
|
||||||
: isConflict && showHints
|
: isConflict && showHints
|
||||||
|
@ -134,10 +135,12 @@ export const BineroGrid = memo(({ grid, onCellClick, showHints = false, difficul
|
||||||
<Paper
|
<Paper
|
||||||
elevation={3}
|
elevation={3}
|
||||||
sx={{
|
sx={{
|
||||||
p: 2,
|
p: { xs: 1, sm: 2 },
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
margin: '0 auto',
|
margin: '0 auto',
|
||||||
|
maxWidth: '100%',
|
||||||
|
overflow: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={gridStyle}>
|
<Box sx={gridStyle}>
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Box, Typography, List, ListItem, ListItemIcon, ListItemText, Paper, useTheme } from '@mui/material';
|
import { Box, Typography } from '@mui/material';
|
||||||
import { Check, Close, Info } from '@mui/icons-material';
|
|
||||||
|
|
||||||
export const BineroRules = () => {
|
export const BineroRules = () => {
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ py: 2 }}>
|
<Box sx={{ py: 2 }}>
|
||||||
<Typography variant="body1" paragraph>
|
<Typography variant="body1" paragraph>
|
||||||
|
|
|
@ -116,7 +116,6 @@ export const generateBinero = (size: number, difficulty: 'easy' | 'medium' | 'ha
|
||||||
solve();
|
solve();
|
||||||
|
|
||||||
// Retirer des cellules aléatoirement selon la difficulté
|
// Retirer des cellules aléatoirement selon la difficulté
|
||||||
const cellsToKeep = size * size - filledCells;
|
|
||||||
let removed = 0;
|
let removed = 0;
|
||||||
|
|
||||||
while (removed < filledCells) {
|
while (removed < filledCells) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ interface TimerProps {
|
||||||
|
|
||||||
export const Timer = ({ isRunning, onTimeUpdate }: TimerProps) => {
|
export const Timer = ({ isRunning, onTimeUpdate }: TimerProps) => {
|
||||||
const [seconds, setSeconds] = useState(0);
|
const [seconds, setSeconds] = useState(0);
|
||||||
const intervalRef = useRef<NodeJS.Timeout>();
|
const intervalRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||||
const lastUpdateRef = useRef<number>(0);
|
const lastUpdateRef = useRef<number>(0);
|
||||||
const isFirstRender = useRef(true);
|
const isFirstRender = useRef(true);
|
||||||
const secondsRef = useRef(0);
|
const secondsRef = useRef(0);
|
||||||
|
@ -22,8 +22,11 @@ export const Timer = ({ isRunning, onTimeUpdate }: TimerProps) => {
|
||||||
lastUpdateRef.current = now;
|
lastUpdateRef.current = now;
|
||||||
secondsRef.current += delta;
|
secondsRef.current += delta;
|
||||||
setSeconds(secondsRef.current);
|
setSeconds(secondsRef.current);
|
||||||
|
if (onTimeUpdate) {
|
||||||
|
onTimeUpdate(secondsRef.current);
|
||||||
}
|
}
|
||||||
}, []);
|
}
|
||||||
|
}, [onTimeUpdate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isFirstRender.current) {
|
if (isFirstRender.current) {
|
||||||
|
@ -48,10 +51,8 @@ export const Timer = ({ isRunning, onTimeUpdate }: TimerProps) => {
|
||||||
}, [isRunning, updateTimer]);
|
}, [isRunning, updateTimer]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (onTimeUpdate) {
|
setSeconds(secondsRef.current);
|
||||||
onTimeUpdate(secondsRef.current);
|
}, [secondsRef.current]);
|
||||||
}
|
|
||||||
}, [seconds, onTimeUpdate]);
|
|
||||||
|
|
||||||
const formatTime = (totalSeconds: number) => {
|
const formatTime = (totalSeconds: number) => {
|
||||||
const hours = Math.floor(totalSeconds / 3600);
|
const hours = Math.floor(totalSeconds / 3600);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
export function useLocalStorage<T>(key: string, initialValue: T) {
|
export function useLocalStorage<T>(key: string, initialValue: T) {
|
||||||
// État pour stocker notre valeur
|
// État pour stocker notre valeur
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue