This write up serves as a guide to the organization of a chess game. In the interview, I discussed using an 8x8 array to store the chess piece objects vs. storing two arrays containing the player's pieces and the pieces being aware of their position. While the latter method would save space, it would also require additional computation to calculate when pieces interact with each other. In the first method, the array location could be checked to see if a piece is there. In the latter method, the array of chess pieces of both players' would have to be checked to see if any of their pieces are at the specific location. In favor of computational speed, I am going to be going with the 8x8 array approach, but this is a worthwhile consideration to note.
Please also note that this is not a comprehensive list of everything necessary to make a successful chess game, but hopefully is enough to demonstrate how I would approach the organization of the project as your last question asked. I did not detail the file structure, the validation functions, the user interactions, what the player object would resemble (note that the player collection is detailed), the driver function, or many other things. If you would be interested in seeing how I would do these functions, I would be more than happy to provide a further write up for these functions.
- Main commands to be used:
- Collections:
- Objects:
- All commands are sent over https
- createHashSalt()
- generateId("player")
- Will create new player object and add to players collection
- Initializes values to:
PLAYERID: //generated by generateId("player")
{
gamesWon: 0,
totalGames: 0,
gameStatus: {
status: "OFFLINE",
gameID: null,
}
- Type is of form
"player"
|"game"
|"tournament"
- Will be generated on sign up using a random number combined with the time stamp - decreases odds of duplicate id drastically
- See: here for implementation
- Uses a prefix allows for differentiation between player, games, and tournaments
- returns unique id of form:
player_96d3d496-2409-41f9-ab91-f424cfbac213
game_96d3d496-2409-41f9-ab91-f424cfbac213
tournament_96d3d496-2409-41f9-ab91-f424cfbac213
- This could be more flushed out in the future to match against similar skill levels, languages, etc. Keeping it simple for now.
- Looks in games.playerSearching[] to find another player who is looking for a match
- If finds a player -> removes other player from the array and initiates a game with initiateGame();
- If doesn't find a player, adds the playerID to the array
- initiateGame();
- Initiates a game between two users
- Creates a new game object and initializes values
GAMEID: //generated by generateId("game")
{
whitePlayer: str, //PLAYER1 id
blackPlayer: str, //PLAYER2 id
// player color can be decided randomly or with other method
// https://chess.stackexchange.com/a/16060 states it is up to organization
// in the future could possibly allow tournaments to be created
// in that case, white and black would be
// decided by the organization
status: str, "INGAME",
winner: null,
moves: [],
board: [], //initliazed with initializeBoard();
}
- generateId("game")
- initializeBoard()
- Creates an 8x8 array with chessPiece objects assigned to the correct initial positions.
- This will need to be broken down into multiple validation functions, but I will omit those for this write up.
- Will move the user's piece in the correct game
- Will need to validate that the move is a valid move.
- Will need to return a new board. The clients will pick up the new board and will then generate a message on what happened. Will return a moveResult object
- If the checkmate will call gameOver(winnerId, gameId);
- gameOver(winnerId, gameId)
- Updates
games[gameId].status
to be"FINISHED"
- Updates
games[gameId].winner
to bewinnerId
- Updates both player's
totalGames
and increments the winner'sgamesWon
- This would need to be more flushed out for a real project
- Will create a session to keep the user logged in
- Session will end when user logs out
- Changes player's gameStatus.status to be
"ONLINE"
- This would need to be more flushed out for a real project
- If the player is currently in a game, send a
moveResult
object withforfeit
set totrue
andwinner
set to the other player's id. - Ends the session and logs the user out
- If the player is
games.playerSearching[]
, remove the id. - Changes player's gameStatus.status to be
"OFFLINE"
- Won't flush this out here. Would need to be flushed out for real project
- Future implementation possibility. Won't flush out fully here. Would create a tournament object. More detail is needed for this.
let players = {
PLAYERID: {
// PLAYERID is a unique identifier for the player.
// Will have the prefix "player_"
// Eg: player_96d3d496-2409-41f9-ab91-f424cfbac213
gamesWon: int, // Can be used for leaderboard purposes
totalGames: int,
// gamesLost can be derived from the
// combination of the previous two
saltedHashedPassword: str,
email: str,
gameStatus: {
status: str, // "INGAME" | "SEARCHING" | "ONLINE" | "OFFLINE"
// "INGAME": currently in a chess match
// "SEARCHING": currently searching to join a match
// "ONLINE": currently on the platform, but not
// actively in a match
// "OFFLINE": not on the platform
gameID: str, // GAMEID | null
// GAMEID of current game or null if no
// game currently being played
// Storing GAMEID here makes it so
// you don't need to constantly search
// the games object to find the cooresponding game
},
PLAYERID: {
gamesWon: int,
totalGames: int,
saltedHashedPassword: str,
email: str,
gameStatus: {
status: str,
against: str
},
},
// ...
PLAYERID: {
gamesWon: int,
totalGames: int,
saltedHashedPassword: str,
email: str,
gameStatus: {
status: str,
against: str
}
}
}
Note that games will persist after the game. This allows games to be looked back on. This could possibly offer a premium option to allow users to look back at a step by step progression through the game so they can see where they did well and where they made a mistake.
let games = {
playerSearching: [str], // Array containing all the players
// who are actively searching for a match
GAMEID: {
//GAMEID generated same way as PLAYERID
// Will have the prefix "game_"
// Eg: game_96d3d496-2409-41f9-ab91-f424cfbac213
whitePlayer: str, //PLAYER1 id
blackPlayer: str, //PLAYER2 id
// player color can be decided randomly or with other method
// https://chess.stackexchange.com/a/16060 states
// it is up to organization
// in the future could possibly allow tournaments to be created
// in that case, white and black would be
// decided by the organization
status: str, // "INGAME" | "FINISHED" | "PENDING"
// "INGAME": currently playing
// "FINISHED": game over
// "PENDING": game to be played
// (in case of a tournament where games are preset up)
winner: str, // playerID of winner
moves: [str],
// array containing string versions of the move() commands sent
// see above function definitions
board: [] //an 8x8 array containing both users pieces
}
}
This could be a possible future implementation if tournaments were desired
let tournaments = {
TOURNAMENTID: {
// Will have the prefix "tournament_"
// Eg: tournament_96d3d496-2409-41f9-ab91-f424cfbac213
gameID = [str], // array of strings containing GAMEIDs
players = [str], // array of strings containing PLAYERIDs
}
}
The leaderboard statistics for the tournament could be calculated by going through all the games and finding the number of wins for each player in the tournament. This way we avoid having to store extra statistics. This approach was not taken for calculating wins for non-tournament games because the total number of games expected to be played would be extremely large compared to the games in a single tournament and it would require too much computation to calculate the number of wins of each player constantly.
This is an object that is sent after a move is executed back to both clients
let moveResult = {
gameId: str, //
playerIdMove: str, // playerId of move initiator
moveAccepted: bool, // if it was a valid move
moveResult: str, // message to be displayed
gameStatus: {
checkmate: bool, // if game is over
forfeit: bool // if other player left game early
winner: str // playerId
}
}
Represents a single chess piece. The chess piece isn't aware of it's position in this set up. The 8x8 array is aware of the pieces.
let chessPiece = {
color: str, // "black" | "white"
type: str // "pawn" | "king" | "queen" | ...
}