Compare commits

...

4 commits

Author SHA1 Message Date
fd172a48ed
Add code for saving and loading game
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-12-14 20:13:01 +01:00
63a56e5b11
Add missing response to PUT handler 2022-12-14 20:05:10 +01:00
bc16d9607f
Fix format of GUIDs 2022-12-14 19:56:04 +01:00
01fea03112
Allow saving and restoring save games 2022-12-14 13:07:04 +01:00
5 changed files with 185 additions and 4 deletions

View file

@ -9,8 +9,16 @@
<body>
<div id="game">
</div>
<button class="button new-game">
New Game
</button>
<form class="menu-bar">
<button class="button new-game">
New Game
</button>
<button class="button save">
Save Game
</button>
<button class="button load">
Load Game
</button>
</form>
</body>
</html>

View file

@ -73,6 +73,37 @@ export class Game
return this.state.board;
}
/**
* Dumps the state of the game.
*
* @returns {IState}
* The JSON string representing the state.
*/
dump()
{
return {
turnCount: this.state.turnCount,
board: {
...this.state.board
}
};
}
/**
* Loads the game from the specified {@link data `data`}.
*
* @param {IState} data
* The data to load.
*/
load(data)
{
this.state.turnCount = data.turnCount;
this.state.board.splice(0);
this.#state = new State(Game.#width, Game.#height);
Object.assign(this.state.board, data.board);
this.draw();
}
/**
* Initializes the game.
*/

View file

@ -7,6 +7,56 @@ import { Game } from "./Game.js";
*/
let game;
/**
* A value indicating whether a transfer is pending.
*/
let transferPending = false;
/**
* The id of the save game.
*/
let id = "";
/**
* Gets the save button.
*
* @returns {HTMLButtonElement}
* The save button.
*/
function getSaveButton()
{
return document.querySelector(".save");
}
/**
* Gets the load button.
*
* @returns {HTMLButtonElement}
* The load button.
*/
function getLoadButton()
{
return document.querySelector(".load");
}
/**
* Gets an url for storing and loading the save game.
*
* @returns {URL}
* The url for storing and loading the save game.
*/
function getUrl()
{
let result = new URL(
id,
new URL(
"/api/data/",
window.location.origin));
result.searchParams.append("token", "c4game");
return result;
}
/**
* Initializes the board.
*/
@ -19,6 +69,81 @@ function initialize()
{
game.reset();
};
getSaveButton().onclick = async () =>
{
if (!transferPending)
{
transferPending = true;
getSaveButton().disabled = true;
getLoadButton().disabled = true;
try
{
if (id === "")
{
let result = await (await fetch(
getUrl(),
{
method: "POST",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify(game.dump())
})).json();
({ id } = result);
}
else
{
await fetch(
getUrl(),
{
method: "PUT",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify(game.dump())
});
}
}
catch { }
getSaveButton().disabled = false;
getLoadButton().disabled = false;
transferPending = false;
}
else
{
console.log("Already busy");
}
};
getLoadButton().onclick = async () =>
{
if (!transferPending)
{
transferPending = true;
getSaveButton().disabled = true;
getLoadButton().disabled = true;
try
{
game.load(
await (
await fetch(getUrl())).json());
}
catch { }
getSaveButton().disabled = false;
getLoadButton().disabled = false;
transferPending = false;
}
else
{
console.log("Already busy");
}
};
}
initialize();

View file

@ -12,3 +12,19 @@ type Row = CellOwner[];
* Represents a game board.
*/
type Board = Row[];
/**
* Represents the state of a game.
*/
interface IState
{
/**
* The number of rounds that have been played.
*/
turnCount: number;
/**
* The board of the game.
*/
board: Board;
}

View file

@ -31,7 +31,7 @@ let data = {};
*/
function createGuid()
{
return randexp(/[0-9a-f]{8}(-[0-9a-f]){4}[0-9a-f]{8}/);
return randexp(/[0-9a-f]{8}(-[0-9a-f]{4}){4}[0-9a-f]{8}/);
}
app.use(express.static(join(dirname, "..", "..", "game", "lib", "static")));
@ -97,6 +97,7 @@ app.put(
if (id in data)
{
data[id] = request.body;
response.send(data[id]);
}
else
{