2024-03-02 18:28:33 +01:00
export class GameInfo {
constructor ( redis , io ) {
this . redis = redis ;
this . io = io ;
}
2024-03-08 23:32:55 +01:00
async timer ( tId , time , callback ) {
2024-04-06 21:18:38 +02:00
await this . redis . json . set ( ` timer: ${ tId } ` , '$' , { lastUpdate : new Date ( ) . getTime ( ) / 1000 , end : new Date ( ) . getTime ( ) / 1000 + time } ) ;
let localLastUpdate = await this . redis . json . get ( ` timer: ${ tId } ` , { path : ".lastUpdate" } ) ;
2024-03-08 23:32:55 +01:00
let timeout = setTimeout ( callback , time * 1000 ) ;
let interval = setInterval ( async ( ) => {
if ( timeout . _destroyed ) {
// timer is finished, stop monitoring turn changes
clearInterval ( interval ) ;
return ;
}
2024-04-06 21:18:38 +02:00
let lastUpdate = await this . redis . json . get ( ` timer: ${ tId } ` , { path : ".lastUpdate" } ) ;
2024-03-08 23:32:55 +01:00
if ( localLastUpdate != lastUpdate ) {
// timer has been reset
clearTimeout ( timeout ) ;
clearInterval ( interval ) ;
return ;
}
} , 200 ) ;
}
2024-04-06 21:18:38 +02:00
async timerLeft ( tId ) {
let end = await this . redis . json . get ( ` timer: ${ tId } ` , { path : ".end" } ) ;
let left = end - new Date ( ) . getTime ( ) / 1000 ;
return left ;
}
2024-03-08 23:32:55 +01:00
async resetTimer ( tId ) {
2024-04-06 21:18:38 +02:00
let lastUpdate = await this . redis . json . get ( ` timer: ${ tId } ` , { path : ".end" } ) ;
await this . redis . json . set ( ` timer: ${ tId } ` , '.lastUpdate' , - lastUpdate ) ;
2024-03-08 23:32:55 +01:00
}
2024-03-02 18:28:33 +01:00
async isPlayerInGame ( socket ) {
const game = await this . redis . json . get ( ` game: ${ socket . session . activeGame } ` ) ;
return game != null ;
}
async getPlayerGameData ( socket ) {
const game = await this . redis . json . get ( ` game: ${ socket . session . activeGame } ` ) ;
return game == null ? null : { id : socket . session . activeGame , data : game } ;
}
2024-03-18 12:41:50 +01:00
async getStats ( socket ) {
const boards = await this . redis . json . get ( ` game: ${ socket . session . activeGame } ` , { path : ".boards" } ) ;
let stats = [ ] ;
boards . forEach ( board => {
stats . push ( board . stats ) ;
} ) ;
return stats ;
}
async incrStat ( socket , statKey , by = 1 ) {
const game = await this . redis . json . get ( ` game: ${ socket . session . activeGame } ` ) ;
2024-04-07 00:21:09 +02:00
const idx = socket . request . session . userId === game . hostId ? 0 : 1 ;
2024-03-18 12:41:50 +01:00
this . redis . json . numIncrBy ( ` game: ${ socket . session . activeGame } ` , ` .boards[ ${ idx } ].stats. ${ statKey } ` , by ) ;
}
2024-03-03 01:29:11 +01:00
async getPlayerShips ( socket ) {
const game = await this . redis . json . get ( ` game: ${ socket . session . activeGame } ` ) ;
2024-04-07 00:21:09 +02:00
const idx = socket . request . session . userId === game . hostId ? 0 : 1 ;
2024-03-03 01:29:11 +01:00
return game . boards [ idx ] . ships ;
}
2024-03-02 18:28:33 +01:00
async endPrepPhase ( socket ) {
const gameId = socket . session . activeGame ;
const key = ` game: ${ gameId } ` ;
await this . redis . json . set ( key , '$.state' , 'action' ) ;
await this . redis . json . set ( key , '$.nextPlayer' , 0 ) ;
const UTCTs = Math . floor ( ( new Date ( ) ) . getTime ( ) / 1000 + 30 ) ;
this . io . to ( gameId ) . emit ( 'turn update' , { turn : 0 , phase : "action" , timerToUTC : UTCTs } ) ;
}
async passTurn ( socket ) {
const gameId = socket . session . activeGame ;
const key = ` game: ${ gameId } ` ;
2024-03-03 16:55:38 +01:00
await this . redis . json . set ( key , '.state' , 'action' ) ;
2024-03-07 21:56:44 +01:00
let nextPlayer = await this . redis . json . get ( key , { path : '.nextPlayer' } ) ;
2024-03-08 19:18:53 +01:00
nextPlayer = ! nextPlayer ? 1 : 0 ;
2024-03-03 16:55:38 +01:00
await this . redis . json . set ( key , '.nextPlayer' , nextPlayer ) ;
2024-03-02 18:28:33 +01:00
const UTCTs = Math . floor ( ( new Date ( ) ) . getTime ( ) / 1000 + 30 ) ;
2024-03-07 21:56:44 +01:00
this . io . to ( gameId ) . emit ( 'turn update' , { turn : nextPlayer , phase : "action" , timerToUTC : UTCTs } ) ;
2024-03-02 18:28:33 +01:00
}
2024-03-03 16:55:38 +01:00
async placeShip ( socket , shipData ) {
const gameId = socket . session . activeGame ;
const key = ` game: ${ gameId } ` ;
2024-03-18 12:41:50 +01:00
const hostId = ( await this . redis . json . get ( key , { path : '.hostId' } ) ) ;
2024-03-03 16:55:38 +01:00
2024-04-07 00:21:09 +02:00
const playerIdx = socket . request . session . userId === hostId ? 0 : 1 ;
2024-03-03 22:59:52 +01:00
await this . redis . json . arrAppend ( key , ` .boards[ ${ playerIdx } ].ships ` , shipData ) ;
}
2024-04-06 00:05:11 +02:00
async depleteShips ( socket ) {
const gameId = socket . session . activeGame ;
const key = ` game: ${ gameId } ` ;
const hostId = ( await this . redis . json . get ( key , { path : '.hostId' } ) ) ;
2024-04-07 00:21:09 +02:00
const playerIdx = socket . request . session . userId === hostId ? 0 : 1 ;
2024-04-06 00:05:11 +02:00
var playerShips = ( await this . redis . json . get ( key , { path : ` .boards[ ${ playerIdx } ].ships ` } ) ) ;
const availableShips = getShipsAvailable ( playerShips ) ;
const boardRender = [ ] ;
const subtrahents = [ [ 0 , 0 ] , [ 0 , 1 ] , [ 1 , 0 ] , [ 0 , - 1 ] , [ - 1 , 0 ] , [ 1 , 1 ] , [ - 1 , - 1 ] , [ 1 , - 1 ] , [ - 1 , 1 ] ] ;
for ( let i = 0 ; i < 10 ; i ++ ) {
var array = [ ] ;
for ( let i = 0 ; i < 10 ; i ++ ) {
array . push ( false ) ;
}
boardRender . push ( array ) ;
}
playerShips . forEach ( ship => {
let multips ;
switch ( ship . rot ) {
case 0 :
multips = [ 1 , 0 ] ;
break ;
case 1 :
multips = [ 0 , 1 ] ;
break ;
case 2 :
multips = [ - 1 , 0 ] ;
break ;
case 3 :
multips = [ 0 , - 1 ] ;
break ;
}
for ( let i = 0 ; i <= ship . type ; i ++ ) {
for ( let l = 0 ; l < subtrahents . length ; l ++ ) {
const idxX = ship . posX - subtrahents [ l ] [ 0 ] + multips [ 0 ] * i ;
const idxY = ship . posY - subtrahents [ l ] [ 1 ] + multips [ 1 ] * i ;
if ( ! ( idxX < 0 || idxX > 9 || idxY < 0 || idxY > 9 ) ) {
boardRender [ idxX ] [ idxY ] = true ;
}
}
}
} ) ;
const placedShips = [ ] ;
for ( let i = 0 ; i < availableShips . length ; i ++ ) {
let availableShipsOfType = availableShips [ i ] ;
for ( let j = 0 ; j < availableShipsOfType ; j ++ ) {
playerShips = ( await this . redis . json . get ( key , { path : ` .boards[ ${ playerIdx } ].ships ` } ) ) ;
2024-04-06 21:18:38 +02:00
let print = "" ;
2024-04-06 00:05:11 +02:00
for ( let y = 0 ; y < 10 ; y ++ ) {
let row = "" ;
for ( let x = 0 ; x < 10 ; x ++ ) {
row += ` ${ boardRender [ x ] [ y ] ? "\x1b[31m" : "\x1b[32m" } ${ boardRender [ x ] [ y ] } \x 1b[0m \t ` ;
}
2024-04-06 21:18:38 +02:00
print += row + "\n" ;
2024-04-06 00:05:11 +02:00
}
2024-04-06 21:18:38 +02:00
const search = findEmptyFields ( boardRender , i + 1 ) ;
2024-04-06 00:05:11 +02:00
const rPos = search [ Math . floor ( Math . random ( ) * search . length ) ] ;
placedShips . push ( { type : i , posX : rPos . posX , posY : rPos . posY , rot : rPos . rot } ) ;
await this . redis . json . arrAppend ( key , ` .boards[ ${ playerIdx } ].ships ` , { type : i , posX : rPos . posX , posY : rPos . posY , rot : rPos . rot , hits : Array . from ( new Array ( i + 1 ) , ( ) => false ) } ) ;
let multips ;
switch ( rPos . rot ) {
case 0 :
multips = [ 1 , 0 ] ;
break ;
case 1 :
multips = [ 0 , 1 ] ;
break ;
case 2 :
multips = [ - 1 , 0 ] ;
break ;
case 3 :
multips = [ 0 , - 1 ] ;
break ;
}
for ( let k = 0 ; k <= i ; k ++ ) {
for ( let l = 0 ; l < subtrahents . length ; l ++ ) {
const idxX = rPos . posX - subtrahents [ l ] [ 0 ] + multips [ 0 ] * k ;
const idxY = rPos . posY - subtrahents [ l ] [ 1 ] + multips [ 1 ] * k ;
if ( ! ( idxX < 0 || idxX > 9 || idxY < 0 || idxY > 9 ) ) {
boardRender [ idxX ] [ idxY ] = true ;
}
}
}
}
}
return placedShips ;
}
2024-03-03 22:59:52 +01:00
async removeShip ( socket , posX , posY ) {
const gameId = socket . session . activeGame ;
const key = ` game: ${ gameId } ` ;
2024-03-04 12:23:47 +01:00
const hostId = ( await this . redis . json . get ( key , { path : '.hostId' } ) ) ;
2024-03-03 22:59:52 +01:00
2024-04-07 00:21:09 +02:00
const playerIdx = socket . request . session . userId === hostId ? 0 : 1 ;
2024-03-03 22:59:52 +01:00
let playerShips = await this . redis . json . get ( key , { path : ` .boards[ ${ playerIdx } ].ships ` } ) ;
var deletedShip ;
playerShips = playerShips . filter ( function ( ship ) {
2024-03-05 21:34:16 +01:00
if ( ship . posX == posX && ship . posY == posY ) {
2024-03-03 22:59:52 +01:00
deletedShip = ship ;
}
return ship . posX != posX || ship . posY != posY
} ) ;
await this . redis . json . set ( key , ` .boards[ ${ playerIdx } ].ships ` , playerShips ) ;
return deletedShip ;
2024-03-03 16:55:38 +01:00
}
2024-03-07 21:56:44 +01:00
async shootShip ( socket , posX , posY ) {
const gameId = socket . session . activeGame ;
const key = ` game: ${ gameId } ` ;
const hostId = ( await this . redis . json . get ( key , { path : '.hostId' } ) ) ;
2024-04-07 00:21:09 +02:00
const enemyIdx = socket . request . session . userId === hostId ? 1 : 0 ;
2024-03-08 19:18:53 +01:00
// const playerIdx = enemyIdx ? 0 : 1;
2024-03-07 21:56:44 +01:00
2024-03-10 11:59:18 +01:00
let playerBoard = await this . redis . json . get ( key , { path : ` .boards[ ${ enemyIdx } ] ` } ) ;
2024-03-07 21:56:44 +01:00
2024-03-10 11:59:18 +01:00
let shot = playerBoard . shots . find ( ( shot ) => shot . posX === posX && shot . posY === posY ) ;
if ( shot ) {
return { status : - 1 }
}
var check = checkHit ( playerBoard . ships , posX , posY ) ;
2024-03-07 21:56:44 +01:00
if ( ! check ) {
2024-03-08 19:18:53 +01:00
return { status : 0 } ;
2024-03-07 21:56:44 +01:00
}
var shotShip ;
2024-03-10 11:59:18 +01:00
for ( let i = 0 ; i < playerBoard . ships . length ; i ++ ) {
const ship = playerBoard . ships [ i ] ;
2024-03-07 21:56:44 +01:00
if ( ship . posX === check . originPosX & ship . posY === check . originPosY ) {
shotShip = ship ;
2024-03-10 11:59:18 +01:00
playerBoard . ships [ i ] . hits [ check . fieldIdx ] = true ;
if ( ! playerBoard . ships [ i ] . hits . includes ( false ) ) {
2024-03-08 19:18:53 +01:00
let gameFinished = true ;
2024-03-10 11:59:18 +01:00
await this . redis . json . set ( key , ` .boards[ ${ enemyIdx } ] ` , playerBoard ) ;
playerBoard . ships . every ( ship => {
2024-03-08 19:18:53 +01:00
if ( ship . hits . includes ( false ) ) {
gameFinished = false ;
return false ;
} else {
return true ;
}
} ) ;
return { status : 2 , ship : ship , gameFinished : gameFinished } ;
}
2024-03-07 21:56:44 +01:00
}
}
2024-03-10 11:59:18 +01:00
await this . redis . json . set ( key , ` .boards[ ${ enemyIdx } ] ` , playerBoard ) ;
2024-03-08 19:18:53 +01:00
return { status : 1 , ship : shotShip } ;
2024-03-07 21:56:44 +01:00
}
2024-04-06 21:18:38 +02:00
async setReady ( socket ) {
const gameId = socket . session . activeGame ;
const key = ` game: ${ gameId } ` ;
const hostId = ( await this . redis . json . get ( key , { path : '.hostId' } ) ) ;
2024-04-07 00:21:09 +02:00
const playerIdx = socket . request . session . userId === hostId ? 0 : 1 ;
2024-04-06 21:18:38 +02:00
await this . redis . json . set ( key , ` .ready[ ${ playerIdx } ] ` , true ) ;
}
2024-03-02 18:28:33 +01:00
}
export function isPlayerInRoom ( socket ) {
return ! socket . rooms . size === 1 ;
}
2024-03-03 16:55:38 +01:00
export function getShipsAvailable ( ships ) {
2024-03-02 18:28:33 +01:00
let shipsLeft = [ 4 , 3 , 2 , 1 ] ;
2024-03-03 16:55:38 +01:00
ships . forEach ( ship => {
2024-03-02 18:28:33 +01:00
shipsLeft [ ship . type ] -- ;
} ) ;
return shipsLeft ;
}
2024-03-07 21:56:44 +01:00
export function checkHit ( ships , posX , posY ) {
2024-03-02 21:45:24 +01:00
let boardRender = [ ] ;
for ( let i = 0 ; i < 10 ; i ++ ) {
var array = [ ] ;
for ( let i = 0 ; i < 10 ; i ++ ) {
array . push ( false ) ;
}
boardRender . push ( array ) ;
}
2024-03-07 21:56:44 +01:00
ships . forEach ( ship => {
2024-03-02 21:45:24 +01:00
let multips ;
switch ( ship . rot ) {
case 0 :
multips = [ 1 , 0 ] ;
break ;
case 1 :
multips = [ 0 , 1 ] ;
break ;
case 2 :
multips = [ - 1 , 0 ] ;
break ;
case 3 :
multips = [ 0 , - 1 ] ;
break ;
}
2024-03-09 22:12:36 +01:00
for ( let i = 0 ; i <= ship . type ; i ++ ) {
2024-03-08 19:18:53 +01:00
let x = clamp ( ship . posX + multips [ 0 ] * i , 0 , 9 ) ;
let y = clamp ( ship . posY + multips [ 1 ] * i , 0 , 9 ) ;
boardRender [ x ] [ y ] = { fieldIdx : i , originPosX : ship . posX , originPosY : ship . posY } ;
2024-03-02 21:45:24 +01:00
}
} ) ;
2024-03-03 22:59:52 +01:00
return boardRender [ posX ] [ posY ] ;
2024-03-02 21:45:24 +01:00
}
2024-03-03 01:29:11 +01:00
export function validateShipPosition ( ships , type , posX , posY , rot ) {
2024-03-07 21:56:44 +01:00
if ( type < 0 || type > 3 || rot < 0 || rot > 3 ) {
return false ;
}
2024-03-03 01:29:11 +01:00
let boardRender = [ ] ;
for ( let i = 0 ; i < 10 ; i ++ ) {
var array = [ ] ;
for ( let i = 0 ; i < 10 ; i ++ ) {
array . push ( false ) ;
}
boardRender . push ( array ) ;
}
ships . forEach ( ship => {
let multips ;
switch ( ship . rot ) {
case 0 :
2024-03-04 12:23:47 +01:00
multips = [ 1 , 0 ] ;
2024-03-03 01:29:11 +01:00
break ;
case 1 :
2024-03-04 12:23:47 +01:00
multips = [ 0 , 1 ] ;
2024-03-03 01:29:11 +01:00
break ;
case 2 :
2024-03-04 12:23:47 +01:00
multips = [ - 1 , 0 ] ;
2024-03-03 01:29:11 +01:00
break ;
case 3 :
2024-03-04 12:23:47 +01:00
multips = [ 0 , - 1 ] ;
2024-03-03 01:29:11 +01:00
break ;
}
2024-03-09 22:12:36 +01:00
for ( let i = 0 ; i <= ship . type ; i ++ ) {
2024-03-04 12:23:47 +01:00
boardRender [ ship . posX + multips [ 0 ] * i ] [ ship . posY + multips [ 1 ] * i ] = true ;
2024-03-03 01:29:11 +01:00
}
} ) ;
2024-03-03 16:55:38 +01:00
let multips ;
2024-03-02 21:45:24 +01:00
switch ( rot ) {
case 0 :
2024-03-04 12:23:47 +01:00
multips = [ 1 , 0 ] ;
2024-03-02 21:45:24 +01:00
break ;
case 1 :
2024-03-04 12:23:47 +01:00
multips = [ 0 , 1 ] ;
2024-03-02 21:45:24 +01:00
break ;
case 2 :
2024-03-04 12:23:47 +01:00
multips = [ - 1 , 0 ] ;
2024-03-02 21:45:24 +01:00
break ;
case 3 :
2024-03-04 12:23:47 +01:00
multips = [ 0 , - 1 ] ;
2024-03-02 21:45:24 +01:00
break ;
}
2024-03-03 16:55:38 +01:00
for ( let x = 0 ; x <= type ; x ++ ) {
2024-03-04 12:23:47 +01:00
if ( posX + multips [ 0 ] * x > 9 || posX + multips [ 0 ] * x < 0 || posY + multips [ 1 ] * x > 9 || posY + multips [ 1 ] * x < 0 ) {
2024-03-02 21:45:24 +01:00
return false ;
}
2024-03-03 16:55:38 +01:00
let subtrahents = [ [ 0 , 0 ] , [ 0 , 1 ] , [ 1 , 0 ] , [ 0 , - 1 ] , [ - 1 , 0 ] , [ 1 , 1 ] , [ - 1 , - 1 ] , [ 1 , - 1 ] , [ - 1 , 1 ] ] ; // Usuń cztery ostatnie elementy jeżeli chcesz by statki mogły się stykać rogami
for ( let y = 0 ; y < subtrahents . length ; y ++ ) {
2024-03-04 12:23:47 +01:00
const idxX = posX - subtrahents [ y ] [ 0 ] + multips [ 0 ] * x ;
const idxY = posY - subtrahents [ y ] [ 1 ] + multips [ 1 ] * x ;
2024-03-03 22:59:52 +01:00
if ( ! ( idxX < 0 || idxX > 9 || idxY < 0 || idxY > 9 ) && boardRender [ idxX ] [ idxY ] ) {
2024-03-03 16:55:38 +01:00
return false ;
}
2024-03-03 01:29:11 +01:00
}
2024-03-02 21:45:24 +01:00
}
return true ;
2024-03-02 18:28:33 +01:00
}
export function checkTurn ( data , playerId ) {
// Check if it's player's turn
if ( playerId == data . hostId ) {
return data . nextPlayer === 0 ;
} else {
return data . nextPlayer === 1 ;
}
}
2024-04-06 00:05:11 +02:00
function findEmptyFields ( grid , len ) {
2024-04-06 21:18:38 +02:00
const shipPlacements = [ ] ;
2024-04-06 00:05:11 +02:00
// Helper function to check if a row can be placed horizontally at a given position
function canPlaceHorizontally ( x , y ) {
2024-04-06 21:18:38 +02:00
// Check if the ship exceeds the board boundaries horizontally
if ( x + len > grid . length ) {
return false ;
2024-04-06 00:05:11 +02:00
}
2024-04-06 21:18:38 +02:00
// Check if any field within the ship's length is already occupied
for ( let i = x ; i < x + len ; i ++ ) {
2024-04-06 00:05:11 +02:00
if ( grid [ i ] [ y ] ) {
2024-04-06 21:18:38 +02:00
return false ;
2024-04-06 00:05:11 +02:00
}
}
return true ;
}
// Helper function to check if a row can be placed vertically at a given position
function canPlaceVertically ( x , y ) {
2024-04-06 21:18:38 +02:00
// Check if the ship exceeds the board boundaries vertically
if ( y + len > grid [ 0 ] . length ) {
return false ;
2024-04-06 00:05:11 +02:00
}
2024-04-06 21:18:38 +02:00
// Check if any field within the ship's length is already occupied
for ( let i = y ; i < y + len ; i ++ ) {
2024-04-06 00:05:11 +02:00
if ( grid [ x ] [ i ] ) {
2024-04-06 21:18:38 +02:00
return false ;
2024-04-06 00:05:11 +02:00
}
}
return true ;
}
2024-04-06 21:18:38 +02:00
// Loop through the grid to find empty places
2024-04-06 00:05:11 +02:00
for ( let i = 0 ; i < grid . length ; i ++ ) {
for ( let j = 0 ; j < grid [ 0 ] . length ; j ++ ) {
2024-04-06 21:18:38 +02:00
if ( ! grid [ i ] [ j ] ) { // Check if the current position is empty
if ( canPlaceHorizontally ( i , j ) ) {
shipPlacements . push ( { posX : i , posY : j , rot : 0 } ) ;
2024-04-06 00:05:11 +02:00
}
2024-04-06 21:18:38 +02:00
if ( canPlaceVertically ( i , j ) ) {
shipPlacements . push ( { posX : i , posY : j , rot : 1 } ) ;
2024-04-06 00:05:11 +02:00
}
}
}
}
2024-04-06 21:18:38 +02:00
return shipPlacements ;
2024-04-06 00:05:11 +02:00
}
2024-03-08 19:18:53 +01:00
function clamp ( n , min , max ) {
return Math . min ( Math . max ( n , min ) , max ) ;
}