class PlayerNotFoundError extends Error { constructor(message) { if (!message) { message = "Player not found"; } super(message); this.name = "PlayerNotFoundError"; } } class PlayerInactiveError extends Error { constructor(message) { if (!message) { message = "Player kicked out due to inactivity"; } super(message); this.name = "PlayerInactiveError"; } } class PositionError extends Error { constructor(message) { if (!message) { message = "Position out of bounds or collision with an object"; } super(message); this.name = "PositionError"; } } const Direction = { LEFT: "left", RIGHT: "right", UP: "up", DOWN: "down", }; class FairHopper { constructor(host, port) { this.host = host; this.port = port; this.defaultHeaders = { Accept: "application/json", "Content-Type": "application/json", }; } formatUrl(path) { return `${this.host}:${this.port}${path}`; } async ping() { const r = await fetch(this.formatUrl("/ping"), { headers: this.defaultHeaders, }); return await r.json(); } async startGame(playerName) { const payload = { player_name: playerName, }; const r = await fetch(this.formatUrl("/game"), { method: "post", headers: this.defaultHeaders, body: JSON.stringify(payload), }); return await r.json(); } async getGameInfo() { const r = await fetch(this.formatUrl("/game"), { headers: this.defaultHeaders, }); return await r.json(); } async getPlayerInfo(playerId) { const r = await fetch(this.formatUrl(`/player/${playerId}`), { headers: this.defaultHeaders, }); switch (r.status) { case 403: throw new PlayerInactiveError(); case 404: throw new PlayerNotFoundError(); } return await r.json(); } async moveLeft(playerId) { return await this.move(playerId, "left"); } async moveRight(playerId) { return await this.move(playerId, "right"); } async moveUp(playerId) { return await this.move(playerId, "up"); } async moveDown(playerId) { return await this.move(playerId, "down"); } async move(playerId, direction) { const url = this.formatUrl(`/player/${playerId}/move/${direction}`); const r = await fetch(url, { method: "post", headers: this.defaultHeaders, }); switch (r.status) { case 403: throw new PlayerInactiveError(); case 404: throw new PlayerNotFoundError(); case 409: throw new PositionError(); } return await r.json(); } async getProducts() { const r = await fetch(this.formatUrl("/products"), { headers: this.defaultHeaders, }); return await r.json(); } async purchaseProduct(playerId, productId) { const url = this.formatUrl(`/player/${playerId}/product/purchase`); const postData = { product_id: productId, }; const r = await fetch(url, { method: "post", headers: this.defaultHeaders, body: JSON.stringify(postData), }); switch (r.status) { case 403: throw new PlayerInactiveError(); case 404: throw new PlayerNotFoundError(); case 409: throw new PositionError(); } return await r.json(); } } module.exports = { FairHopper, PlayerNotFoundError, PlayerInactiveError, PositionError, Direction, };