npm ci
PORT_HTTP=4000
PORT_WS=3000
npm run start
npm run start:dev
npm run test
Battleship game backend using websocket.
The backend should be able to do the following:
- Start websocket server
- Handle websocket connection
- Handle player requests
- Handle room requests
- Handle ships requests
- Handle game requests
- Create single play bot
- Only ws,
cross-env
,typescript
,tsx
,ts-node
,ts-node-dev
,nodemon
,dotenv
,eslint
and its plugins,webpack
and its plugins,prettier
,@types/*
and testing tools (for example, Jest, Mocha, AVA, Jasmine, Cypress, Storybook, Puppeteer) are allowed - All requests and responses must be sent as JSON string
- After starting the program displays websocket parameters
- After program work finished the program should end websocket work correctly
- After each received command program should display the command and result
- personal response
- reg - player registration/login
- response for the game room
- create_game - game id and player id (unique id for user in this game)
- start_game - information about game and player's ships positions
- turn - who is shooting now
- attack - coordinates of shot and status
- finish - id of the winner
- response for all
- update_room - list of rooms and players in rooms
- update_winners - send score table to players
- We have inmemory DB with player data (login and password) storage
- Player can create game room or connect to the game room after login
- Player room data (players, game board, ships positions) storages in the server
- Game starts after 2 players are connected to the room and sent ships positions to the server
- Server sends move order
- Players should shoot in theirs turn
- Server send back shot result
- If player hits or kills the ship, player should make one more shoot
- Player wins if he has killed all enemies ships
List of websocket commands (requests/responses) and their syntax (<- - cmd from frontend, -> - answer):
- Player
- Login or create player
<-
{ type: "reg", data: { name: <string>, password: <string>, }, id: 0, }
->
{ type: "reg", data: { name: <string>, index: <number | string>, error: <bool>, errorText: <string>, }, id: 0, }
- Update winners (for all after every winners table update)
->
{ type: "update_winners", data: [ { name: <string>, wins: <number>, } ], id: 0, }
- Login or create player
- Room
- Create new room (create game room and add yourself there)
<-
{ type: "create_room", data: "", id: 0, }
- Add user to room (add yourself to somebodies room, then remove the room from available rooms list)
<-
{ type: "add_user_to_room", data: { indexRoom: <number | string>, }, id: 0, }
->
{ type: "create_game", //send for both players in the room, after they are connected to the room data: { idGame: <number | string>, idPlayer: <number | string>, /* generated by server id for player in the game session, not enemy (unique id for every player) */ }, id: 0, }
- Update room state (send rooms list, where only one player inside)
->
{ type: "update_room", data: [ { roomId: <number | string>, roomUsers: [ { name: <string>, index: <number | string>, } ], }, ], id: 0, }
- Create new room (create game room and add yourself there)
- Ships
- Add ships to the game board
<-
{ type: "add_ships", data: { gameId: <number | string>, ships: [ { position: { x: <number>, y: <number>, }, direction: <boolean>, length: <number>, type: "small"|"medium"|"large"|"huge", } ], indexPlayer: <number | string>, /* id of the player in the current game session */ }, id: 0, }
- Start game (only after server receives both player's ships positions)
->
{ type: "start_game", data: { ships: /* player's ships, not enemy's */ [ { position: { x: <number>, y: <number>, }, direction: <boolean>, length: <number>, type: "small"|"medium"|"large"|"huge", } ], currentPlayerIndex: <number | string>, /* id of the player in the current game session, who have sent his ships */ }, id: 0, }
- Add ships to the game board
- Game
- Attack
<-
{ type: "attack", data: { gameId: <number | string>, x: <number>, y: <number>, indexPlayer: <number | string>, /* id of the player in the current game session */ }, id: 0, }
- Attack feedback (should be sent after every shot, miss and after kill sent miss for all cells around ship too)
->
{ type: "attack", data: { position: { x: <number>, y: <number>, }, currentPlayer: <number | string>, /* id of the player in the current game session */ status: "miss"|"killed"|"shot", }, id: 0, }
- Random attack
<-
{ type: "randomAttack", data: { gameId: <number | string>, indexPlayer: <number | string>, /* id of the player in the current game session */ }, id: 0, }
- Info about player's turn (send after game start and every attack, miss or kill result)
->
{ type: "turn", data: { currentPlayer: <number | string>, /* id of the player in the current game session */ }, id: 0, }
- Finish game
->
{ type: "finish", data: { winPlayer: <number | string>, /* id of the player in the current game session */ }, id: 0, }
- Attack
Player1 Server Player2
reg -->
<-- reg
<-- update_room
<-- update_winners
create_room -->
<-- update_room
<-- reg
reg -->
<-- update_room -->
<-- update_winners -->
<-- add_user_to_room
<-- update_room -->
<-- create_game -->
add_ships -->
<-- add_ships
<-- start_game -->
<-- turn -->
attack (miss) -->
<-- attack -->
<-- turn -->
<-- randomAttack (shoot)
<-- attack -->
<-- turn -->
<-- randomAttack (kill) - send state for all cells around killed ship
<-- attack -->
<-- turn -->
<-- attack -->
<-- turn -->
<-- attack -->
<-- turn -->
<-- attack -->
<-- turn -->
...
<-- randomAttack (miss)
<-- attack -->
<-- turn -->
attack (miss) -->
<-- attack -->
<-- turn -->
...
<-- finish -->
<-- update_winners -->