Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Sign in / Register
Toggle navigation
T
test
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Смирнов Олег
test
Commits
9d2a6016
Commit
9d2a6016
authored
Dec 17, 2024
by
root
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
production debug
parent
ebe4288e
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
96 additions
and
62 deletions
+96
-62
constants.ts
constants.ts
+1
-0
env.local
env.local
+1
-0
next.config.ts
next.config.ts
+1
-0
package.json
package.json
+2
-2
page.tsx
src/app/page.tsx
+28
-10
GameBoard.tsx
src/components/GameBoard.tsx
+0
-1
GameInvite.tsx
src/components/GameInvite.tsx
+3
-1
socket.ts
src/pages/api/socket.ts
+59
-47
game.ts
src/types/game.ts
+1
-1
No files found.
constants.ts
0 → 100644
View file @
9d2a6016
export
const
APP_URL
=
"/games/tictachack"
\ No newline at end of file
env.local
0 → 100644
View file @
9d2a6016
APP_URL=/games/tictachack
\ No newline at end of file
next.config.ts
View file @
9d2a6016
...
...
@@ -2,6 +2,7 @@ import type { NextConfig } from "next";
const
nextConfig
:
NextConfig
=
{
/* config options here */
basePath
:
'/games/tictachack'
};
export
default
nextConfig
;
package.json
View file @
9d2a6016
...
...
@@ -3,9 +3,9 @@
"version"
:
"0.1.0"
,
"private"
:
true
,
"scripts"
:
{
"dev"
:
"next dev --turbopack"
,
"dev"
:
"next dev --turbopack
-p 3001
"
,
"build"
:
"next build"
,
"start"
:
"next start"
,
"start"
:
"next start
-p 3001
"
,
"lint"
:
"next lint"
},
"dependencies"
:
{
...
...
src/app/page.tsx
View file @
9d2a6016
'use client'
;
import
{
useEffect
,
useState
}
from
'react'
;
import
{
useEffect
,
useState
,
Suspense
}
from
'react'
;
import
{
useSearchParams
}
from
'next/navigation'
;
import
{
io
,
Socket
}
from
'socket.io-client'
;
import
GameBoard
from
'../components/GameBoard'
;
import
PlayerInfo
from
'../components/PlayerInfo'
;
import
GameInvite
from
'../components/GameInvite'
;
import
{
GameState
,
Player
,
GameMove
}
from
'../types/game'
;
import
{
GameState
}
from
'../types/game'
;
import
{
APP_URL
}
from
'../../constants'
;
let
socket
:
Socket
;
export
default
function
Home
()
{
function
Home
()
{
const
searchParams
=
useSearchParams
();
const
[
roomId
,
setRoomId
]
=
useState
<
string
>
(
''
);
const
[
showInvite
,
setShowInvite
]
=
useState
(
false
);
...
...
@@ -27,11 +28,12 @@ export default function Home() {
const
[
turnTimeLeft
,
setTurnTimeLeft
]
=
useState
<
number
>
(
20000
);
useEffect
(()
=>
{
const
roomFromUrl
=
searchParams
.
get
(
'room'
);
if
(
!
socket
)
{
socket
=
io
({
path
:
'/api/socket'
,
path
:
`
${
APP_URL
}
/api/socket`
,
});
socket
.
on
(
'connect'
,
()
=>
{
...
...
@@ -57,10 +59,13 @@ export default function Home() {
});
}
if
(
searchParams
)
{
const
roomFromUrl
=
searchParams
.
get
(
'room'
);
if
(
roomFromUrl
)
{
socket
.
emit
(
'joinGame'
,
{
roomId
:
roomFromUrl
});
setRoomId
(
roomFromUrl
);
}
}
return
()
=>
{
if
(
socket
)
{
...
...
@@ -72,8 +77,9 @@ export default function Home() {
useEffect
(()
=>
{
let
timer
:
NodeJS
.
Timeout
;
if
(
gameState
.
isYourTurn
&&
gameState
.
turnStartTime
)
{
const
startTime
=
gameState
.
turnStartTime
;
timer
=
setInterval
(()
=>
{
const
timeLeft
=
gameState
.
turnTimeLimit
-
(
Date
.
now
()
-
gameState
.
turnS
tartTime
);
const
timeLeft
=
gameState
.
turnTimeLimit
-
(
Date
.
now
()
-
s
tartTime
);
setTurnTimeLeft
(
Math
.
max
(
0
,
timeLeft
));
if
(
timeLeft
<=
0
)
{
socket
.
emit
(
'turnTimeout'
,
{
roomId
});
...
...
@@ -130,6 +136,7 @@ export default function Home() {
}
return
(
<
Suspense
>
<
div
className=
"relative min-h-screen bg-gray-900 text-white"
>
{
showInvite
&&
<
GameInvite
roomId=
{
roomId
}
/>
}
...
...
@@ -200,5 +207,16 @@ export default function Home() {
)
}
</
div
>
</
div
>
</
Suspense
>
);
}
const
App
=
()
=>
{
return
(
<
Suspense
fallback=
{
<
div
>
Загрузка...
</
div
>
}
>
<
Home
/>
</
Suspense
>
);
};
export
default
App
;
src/components/GameBoard.tsx
View file @
9d2a6016
...
...
@@ -27,7 +27,6 @@ const GameBoard: React.FC<GameBoardProps> = ({
const
containerRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
cellSize
=
60
;
const
viewportSize
=
800
;
useEffect
(()
=>
{
if
(
containerRef
.
current
)
{
...
...
src/components/GameInvite.tsx
View file @
9d2a6016
import
React
from
'react'
;
import
{
APP_URL
}
from
'../../constants'
;
interface
GameInviteProps
{
roomId
:
string
;
}
const
GameInvite
:
React
.
FC
<
GameInviteProps
>
=
({
roomId
})
=>
{
const
gameUrl
=
typeof
window
!==
'undefined'
?
`
${
window
.
location
.
origin
}
?room=
${
roomId
}
`
:
''
;
const
gameUrl
=
typeof
window
!==
'undefined'
?
`
${
window
.
location
.
origin
}
${
APP_URL
}
?room=
${
roomId
}
`
:
''
;
const
shareText
=
`Присоединяйся ко мне в игре!`
;
...
...
src/pages/api/socket.ts
View file @
9d2a6016
import
{
Server
}
from
'socket.io'
;
import
{
Server
as
NetServer
}
from
'http'
;
import
{
NextApiRequest
}
from
'next'
;
import
{
NextApiResponseWithSocket
}
from
'../../types/next'
;
import
{
GameState
,
GameMove
}
from
'../../types/game'
;
import
{
NextApiRequest
,
NextApiResponse
}
from
'next'
;
// import { NextApiResponseWithSocket } from '../../types/next';
import
{
GameState
,
GameMove
,
CellValue
}
from
'../../types/game'
;
import
{
APP_URL
}
from
'../../../constants'
;
export
type
NextApiResponseWithSocket
=
NextApiResponse
&
{
socket
:
{
server
:
NetServer
&
{
io
:
Server
}
}
}
const
games
=
new
Map
<
string
,
GameState
>
();
...
...
@@ -23,7 +34,7 @@ const createNewGame = (playerId: string): [string, GameState] => {
isYourTurn
:
false
,
status
:
'waiting'
,
turnTimeLimit
:
20000
,
turnStartTime
:
null
,
turnStartTime
:
undefined
,
players
:
{
attacker
:
{
id
:
playerId
,
...
...
@@ -54,44 +65,45 @@ const joinGame = (roomId: string, playerId: string): GameState | null => {
return
game
;
};
const
startNewRound
=
(
game
:
GameState
,
firstReadyPlayerId
:
string
):
GameState
=>
{
// Определяем, кто будет атакующим в новой игре (тот, кто первый нажал "Играть снова")
const
oldAttacker
=
game
.
players
.
attacker
!
;
const
oldDefender
=
game
.
players
.
defender
!
;
// Определяем, кто первый нажал кнопку
const
firstPlayer
=
firstReadyPlayerId
===
oldAttacker
.
id
?
oldAttacker
:
oldDefender
;
const
secondPlayer
=
firstReadyPlayerId
===
oldAttacker
.
id
?
oldDefender
:
oldAttacker
;
// const startNewRound = (game: GameState, firstReadyPlayerId: string): GameState => {
// // Определяем, кто будет атакующим в новой игре (тот, кто первый нажал "Играть снова")
// const oldAttacker = game.players.attacker!;
// const oldDefender = game.players.defender!;
// // Определяем, кто первый нажал кнопку
// const firstPlayer = firstReadyPlayerId === oldAttacker.id ? oldAttacker : oldDefender;
// const secondPlayer = firstReadyPlayerId === oldAttacker.id ? oldDefender : oldAttacker;
// // Сбрасываем состояние игры
// const newGame: GameState = {
// ...game,
// cells: {},
// currentPlayer: 'X',
// winner: null,
// status: 'playing',
// lastMove: null,
// turnStartTime: Date.now(),
// readyForNewGame: {},
// // Первый нажавший становится атакующим
// players: {
// attacker: {
// ...firstPlayer,
// isAttacker: true,
// nickname: 'Heker'
// },
// defender: {
// ...secondPlayer,
// isAttacker: false,
// nickname: 'Beluga'
// }
// }
// };
// return newGame;
// };
const
checkWinner
=
(
cells
:
{
[
key
:
string
]:
CellValue
}
,
lastMove
:
GameMove
):
CellValue
=>
{
// Сбрасываем состояние игры
const
newGame
:
GameState
=
{
...
game
,
cells
:
{},
currentPlayer
:
'X'
,
winner
:
null
,
status
:
'playing'
,
lastMove
:
null
,
turnStartTime
:
Date
.
now
(),
readyForNewGame
:
{},
// Первый нажавший становится атакующим
players
:
{
attacker
:
{
...
firstPlayer
,
isAttacker
:
true
,
nickname
:
'Heker'
},
defender
:
{
...
secondPlayer
,
isAttacker
:
false
,
nickname
:
'Beluga'
}
}
};
return
newGame
;
};
const
checkWinner
=
(
cells
:
{
[
key
:
string
]:
string
},
lastMove
:
GameMove
):
string
|
null
=>
{
const
directions
=
[
[
0
,
1
],
// horizontal
[
1
,
0
],
// vertical
...
...
@@ -126,14 +138,14 @@ const checkWinner = (cells: { [key: string]: string }, lastMove: GameMove): stri
const
handler
=
async
(
req
:
NextApiRequest
,
res
:
NextApiResponseWithSocket
)
=>
{
if
(
!
res
.
socket
.
server
.
io
)
{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const
httpServer
:
NetServer
=
res
.
socket
.
server
as
any
;
const
io
=
new
Server
(
httpServer
,
{
path
:
'/api/socket'
,
path
:
`
${
APP_URL
}
/api/socket`
,
});
io
.
on
(
'connection'
,
(
socket
)
=>
{
console
.
log
(
'Client connected:'
,
socket
.
id
);
socket
.
on
(
'createGame'
,
()
=>
{
const
[
roomId
,
gameState
]
=
createNewGame
(
socket
.
id
);
socket
.
join
(
roomId
);
...
...
@@ -174,7 +186,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponseWithSocket) => {
currentPlayer
:
'X'
,
winner
:
null
,
status
:
'playing'
,
lastMove
:
null
,
lastMove
:
undefined
,
turnStartTime
:
Date
.
now
()
};
...
...
@@ -230,7 +242,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponseWithSocket) => {
game
.
cells
[
cellKey
]
=
player
;
game
.
lastMove
=
{
x
,
y
,
player
};
const
winner
=
checkWinner
(
game
.
cells
,
{
x
,
y
,
player
});
const
winner
=
checkWinner
(
game
.
cells
,
{
x
,
y
,
player
,
roomId
});
if
(
winner
)
{
game
.
winner
=
winner
;
game
.
status
=
'finished'
;
...
...
src/types/game.ts
View file @
9d2a6016
...
...
@@ -43,7 +43,7 @@ export interface GameState {
y
:
number
;
player
:
'X'
|
'O'
;
};
turnStartTime
?:
number
;
turnStartTime
?:
number
|
undefined
|
null
;
turnTimeLimit
:
number
;
// в миллисекундах
readyForNewGame
?:
{
[
playerId
:
string
]:
boolean
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment