172 lines
3.8 KiB
TypeScript
172 lines
3.8 KiB
TypeScript
import { Coordinates } from "./boardState";
|
|
import { Selected } from "./Board";
|
|
import BoardState, { fromWireFormat } from "./boardState";
|
|
import { COLOR, PIECE } from "./primitives";
|
|
import validateBoardResponse from "./boardResponse.validator";
|
|
import validateStartMessage from "./startMessage.validator";
|
|
import GAME_STATE from "./gameState";
|
|
|
|
export type ValidBoardResponse = {
|
|
boardState: BoardState;
|
|
gameState: GAME_STATE;
|
|
};
|
|
|
|
export type StartGame = (
|
|
playerColor: COLOR
|
|
) => Promise<[string, BoardState] | null>;
|
|
|
|
export type MakeMove = (
|
|
selected: Selected,
|
|
destination: Coordinates,
|
|
gameId: string,
|
|
kind?: PIECE
|
|
) => Promise<void>;
|
|
|
|
export type Poll = (gameId: string) => Promise<ValidBoardResponse | null>;
|
|
|
|
export type Api = {
|
|
startGame: StartGame;
|
|
makeMove: MakeMove;
|
|
poll: Poll;
|
|
};
|
|
|
|
export type ApiMessage<T> = {
|
|
data: T;
|
|
gameId: string;
|
|
};
|
|
|
|
const apiMessage = <T>(gameId: string, data: T): ApiMessage<T> => {
|
|
return {
|
|
data,
|
|
gameId,
|
|
};
|
|
};
|
|
|
|
const makeBody = <T>(gameId: string, data: T): string => {
|
|
return JSON.stringify(apiMessage(gameId, data));
|
|
};
|
|
|
|
const init = (baseUri: string): Api => {
|
|
return {
|
|
startGame: startGame(baseUri),
|
|
makeMove: makeMove(baseUri),
|
|
poll: poll(baseUri),
|
|
};
|
|
};
|
|
|
|
const startGame = (baseUri: string): StartGame => {
|
|
return async (playerColor: COLOR): Promise<[string, BoardState] | null> => {
|
|
const startUri = `${baseUri}/start`;
|
|
|
|
const payload = {
|
|
playerColor,
|
|
};
|
|
|
|
const fetchOptions = {
|
|
method: "POST",
|
|
headers: {
|
|
Accept: "application/json",
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(payload),
|
|
};
|
|
|
|
try {
|
|
const response = await fetch(startUri, fetchOptions);
|
|
|
|
if (!response.ok) {
|
|
console.error("Failed to start game,", response.status);
|
|
return null;
|
|
}
|
|
|
|
const json: unknown = await response.json();
|
|
|
|
const startMessage = validateStartMessage(json);
|
|
|
|
return [startMessage.gameId, fromWireFormat(startMessage.board)];
|
|
} catch (error) {
|
|
console.error("Failed to start game,", error);
|
|
return null;
|
|
}
|
|
};
|
|
};
|
|
|
|
const makeMove = (baseUri: string): MakeMove => {
|
|
return async (
|
|
selected: Selected,
|
|
destination: Coordinates,
|
|
gameId: string,
|
|
kind?: PIECE
|
|
): Promise<void> => {
|
|
const moveUri = `${baseUri}/move`;
|
|
|
|
const body = makeBody(gameId, {
|
|
piece: selected.piece,
|
|
from: selected.location,
|
|
to: destination,
|
|
kind,
|
|
});
|
|
|
|
const fetchOptions = {
|
|
method: "POST",
|
|
headers: {
|
|
Accept: "application/json",
|
|
"Content-Type": "application/json",
|
|
},
|
|
body,
|
|
};
|
|
|
|
try {
|
|
await fetch(moveUri, fetchOptions);
|
|
} catch (error) {
|
|
console.error("Failed to make move,", error);
|
|
}
|
|
};
|
|
};
|
|
|
|
const poll = (baseUri: string): Poll => {
|
|
return async (gameId: string) => {
|
|
const pollUri = `${baseUri}/poll/${gameId}`;
|
|
|
|
const fetchOptions = {
|
|
method: "GET",
|
|
headers: {
|
|
Accept: "application/json",
|
|
},
|
|
};
|
|
|
|
try {
|
|
const result = await fetch(pollUri, fetchOptions);
|
|
|
|
if (!result.ok) {
|
|
if (result.status >= 400 && result.status < 500) {
|
|
throw new Error("Invalid status for long poll");
|
|
}
|
|
|
|
console.error("Failed to long poll,", result.status);
|
|
return null;
|
|
}
|
|
|
|
if (result.status === 204) {
|
|
return null;
|
|
}
|
|
|
|
const json: unknown = await result.json();
|
|
|
|
const boardResponse = validateBoardResponse(json);
|
|
|
|
const boardState = fromWireFormat(boardResponse.boardState);
|
|
|
|
return {
|
|
boardState,
|
|
gameState: boardResponse.gameState,
|
|
};
|
|
} catch (error) {
|
|
console.error("Failed to long poll,", error);
|
|
throw new Error("Failed to long poll");
|
|
}
|
|
};
|
|
};
|
|
|
|
export { init };
|