add spectator
This commit is contained in:
@@ -12,8 +12,9 @@ ChessServer is just a simple chess server for [Aiursoft](https://www.aiursoft.co
|
|||||||
Requirements about how to run
|
Requirements about how to run
|
||||||
|
|
||||||
1. [.NET 7 SDK](http://dot.net/)
|
1. [.NET 7 SDK](http://dot.net/)
|
||||||
2. Execute `dotnet run` to run the app
|
2. Run `npm i` at directory `/src/Aiursoft.ChessServer/wwwroot/`
|
||||||
3. Use your browser to view [http://localhost:5000](http://localhost:5000)
|
3. Execute `dotnet run` to run the app
|
||||||
|
4. Use your browser to view [http://localhost:5000](http://localhost:5000)
|
||||||
|
|
||||||
## Run in Microsoft Visual Studio
|
## Run in Microsoft Visual Studio
|
||||||
|
|
||||||
|
|||||||
@@ -15,19 +15,50 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
@* Make the following p auto warp *@
|
@* Make the following p auto warp *@
|
||||||
|
|
||||||
<p id="status" class="text-center text-wrap text-primary mt-1"></p>
|
<p id="status" class="text-center text-wrap text-primary mt-1"></p>
|
||||||
<p id="fen" class="text-wrap text-muted hidden"></p>
|
<p id="fen" class="text-wrap text-muted hidden"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="chooseCharacter" tabindex="-1" aria-labelledby="chooseCharacter" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">Choose your character</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<button type="button" id="chooseWhite" class="btn">
|
||||||
|
<img src="/img/chesspieces/wikipedia/wK.png" alt="White">
|
||||||
|
</button>
|
||||||
|
<button type="button" id="chooseBlack" class="btn">
|
||||||
|
<img src="/img/chesspieces/wikipedia/bK.png" alt="Black">
|
||||||
|
</button>
|
||||||
|
<button type="button" data-bs-dismiss="modal" id="choosePeek" class="btn btn-primary">Peek~👀</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
<script type="module" src="~/node_modules/@@chrisoakman/chessboardjs/dist/chessboard-1.0.0.min.js"></script>
|
<script type="module" src="~/node_modules/@@chrisoakman/chessboardjs/dist/chessboard-1.0.0.min.js"></script>
|
||||||
<script type="module" src="~/site.js"></script>
|
<script type="module" src="~/site.js"></script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
// Ask the user to enter w or b:
|
// Ask the user to enter w or b:
|
||||||
import initGameBoard from "/site.js";
|
import Game from "/site.js";
|
||||||
const player = prompt("Please enter your color (w or b):");
|
let game = new Game(@Model);
|
||||||
initGameBoard(player, @Model);
|
game.chooseCharacter();
|
||||||
|
|
||||||
|
$("#chooseWhite").on("click", function () {
|
||||||
|
game.setPlayer('w');
|
||||||
|
game.startGame();
|
||||||
|
});
|
||||||
|
$("#chooseBlack").on("click", function () {
|
||||||
|
game.setPlayer('b');
|
||||||
|
game.startGame();
|
||||||
|
});
|
||||||
|
$('#choosePeek').on('click', function () {
|
||||||
|
game.startGame();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
}
|
}
|
||||||
@@ -1,107 +1,164 @@
|
|||||||
import { Chess } from "/node_modules/chess.js/dist/esm/chess.js";
|
import { Chess } from "/node_modules/chess.js/dist/esm/chess.js";
|
||||||
|
|
||||||
const statusControl = $('#status');
|
const WHITE = "w";
|
||||||
const fenControl = $('#fen');
|
const BLACK = "b";
|
||||||
|
const SPECTATOR = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* representing current Game
|
||||||
|
*
|
||||||
|
* ## example
|
||||||
|
* ```
|
||||||
|
* let gameId = 0;
|
||||||
|
* let game = new Game(gameId);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {number} gameId current id of game
|
||||||
|
*/
|
||||||
|
function Game(gameId) {
|
||||||
|
this.gameId = gameId;
|
||||||
|
this.player = "";
|
||||||
|
this.chooseCharacterModal = new bootstrap.Modal(
|
||||||
|
document.getElementById("chooseCharacter")
|
||||||
|
);
|
||||||
|
|
||||||
|
this.chooseCharacter = () => {
|
||||||
|
this.chooseCharacterModal.show();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setPlayer = (player) => {
|
||||||
|
if ([WHITE, BLACK].includes(player)) {
|
||||||
|
this.player = player;
|
||||||
|
} else {
|
||||||
|
this.player = SPECTATOR;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* let's start to play!
|
||||||
|
* but before, you need to choose a character
|
||||||
|
*/
|
||||||
|
this.startGame = () => {
|
||||||
|
this.chooseCharacterModal.hide();
|
||||||
|
|
||||||
|
initGameBoard(this.player, this.gameId);
|
||||||
|
|
||||||
|
if (this.player === SPECTATOR) {
|
||||||
|
document.getElementById("board").style.cursor = "not-allowed";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusControl = $("#status");
|
||||||
|
const fenControl = $("#fen");
|
||||||
|
|
||||||
const initGameBoard = function (player, gameId) {
|
const initGameBoard = function (player, gameId) {
|
||||||
$.get("/games/" + gameId + ".fen", function (fen) {
|
$.get("/games/" + gameId + ".fen", function (fen) {
|
||||||
let board = null;
|
let board = null;
|
||||||
let game = null;
|
let game = null;
|
||||||
|
|
||||||
// Happens when a player picks up a piece.
|
// Happens when a player picks up a piece.
|
||||||
function onDragStart(source, piece, position, _) {
|
function onDragStart(source, piece, position, _) {
|
||||||
// only allow moving players own pieces
|
// only allow moving players own pieces
|
||||||
if ((game.turn() !== player)) {
|
if (game.turn() !== player) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not pick up pieces if the game is over
|
// do not pick up pieces if the game is over
|
||||||
if (game.isGameOver()) return false
|
if (game.isGameOver()) return false;
|
||||||
|
|
||||||
// only pick up pieces for the side to move
|
// only pick up pieces for the side to move
|
||||||
if ((game.turn() === 'w' && piece.search(/^b/) !== -1) ||
|
if (
|
||||||
(game.turn() === 'b' && piece.search(/^w/) !== -1)) {
|
(game.turn() === "w" && piece.search(/^b/) !== -1) ||
|
||||||
return false
|
(game.turn() === "b" && piece.search(/^w/) !== -1)
|
||||||
}
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDrop(source, target) {
|
||||||
|
try {
|
||||||
|
const move = game.move({
|
||||||
|
from: source,
|
||||||
|
to: target,
|
||||||
|
promotion: "q",
|
||||||
|
});
|
||||||
|
if (move === null) {
|
||||||
|
return "snapback";
|
||||||
}
|
}
|
||||||
|
const lastMove = game.history({ verbose: true }).pop().san;
|
||||||
|
$.post("/games/" + gameId + "/move/" + player + "/" + lastMove);
|
||||||
|
} catch (e) {
|
||||||
|
return "snapback";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onDrop(source, target) {
|
// Hack to make sure castling\promotion works.
|
||||||
try {
|
function onSnapEnd() {
|
||||||
const move = game.move({
|
board.position(game.fen());
|
||||||
from: source,
|
}
|
||||||
to: target,
|
|
||||||
promotion: 'q'
|
function updateStatusText() {
|
||||||
})
|
let status;
|
||||||
if (move === null) {
|
let moveColor = "White";
|
||||||
return 'snapback'
|
if (game.turn() === "b") {
|
||||||
}
|
moveColor = "Black";
|
||||||
const lastMove = game.history({verbose: true}).pop().san;
|
}
|
||||||
$.post("/games/" + gameId + "/move/" + player + "/" + lastMove);
|
if (game.isCheckmate()) {
|
||||||
} catch (e) {
|
status =
|
||||||
return 'snapback';
|
"Game over, " +
|
||||||
}
|
moveColor +
|
||||||
|
" is in checkmate, and winner is " +
|
||||||
|
(game.turn() === "w" ? "Black" : "White");
|
||||||
|
} else if (game.isDraw()) {
|
||||||
|
status = "Game over, drawn position";
|
||||||
|
} else {
|
||||||
|
status = moveColor + " to move";
|
||||||
|
if (game.isCheck()) {
|
||||||
|
status += ", " + moveColor + " is in check";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
statusControl.html(status);
|
||||||
|
fenControl.html(game.fen());
|
||||||
|
}
|
||||||
|
|
||||||
// Hack to make sure castling\promotion works.
|
const config = {
|
||||||
function onSnapEnd() {
|
orientation: player === "w" ? "white" : "black",
|
||||||
board.position(game.fen())
|
draggable: true,
|
||||||
}
|
dragoffBoard: "snapback",
|
||||||
|
position: fen,
|
||||||
|
onDragStart: onDragStart,
|
||||||
|
onSnapEnd: onSnapEnd,
|
||||||
|
onDrop: onDrop,
|
||||||
|
};
|
||||||
|
board = ChessBoard("board", config);
|
||||||
|
|
||||||
function updateStatusText() {
|
function refresh(newFen) {
|
||||||
let status;
|
game = new Chess(newFen);
|
||||||
let moveColor = 'White';
|
board.position(newFen);
|
||||||
if (game.turn() === 'b') {
|
console.log("Got fen " + newFen + ". refreshing board...");
|
||||||
moveColor = 'Black';
|
updateStatusText();
|
||||||
} if (game.isCheckmate()) {
|
}
|
||||||
status = 'Game over, ' + moveColor + ' is in checkmate, and winner is ' + (game.turn() === 'w' ? 'Black' : 'White');
|
|
||||||
} else if (game.isDraw()) {
|
|
||||||
status = 'Game over, drawn position';
|
|
||||||
} else {
|
|
||||||
status = moveColor + ' to move';
|
|
||||||
if (game.isCheck()) {
|
|
||||||
status += ', ' + moveColor + ' is in check';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
statusControl.html(status);
|
|
||||||
fenControl.html(game.fen());
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = {
|
refresh(fen);
|
||||||
orientation: player === "w" ? "white" : "black",
|
|
||||||
draggable: true,
|
|
||||||
dragoffBoard: 'snapback',
|
|
||||||
position: fen,
|
|
||||||
onDragStart: onDragStart,
|
|
||||||
onSnapEnd: onSnapEnd,
|
|
||||||
onDrop: onDrop
|
|
||||||
};
|
|
||||||
board = ChessBoard('board', config);
|
|
||||||
|
|
||||||
function refresh(newFen) {
|
const wsScheme = window.location.protocol === "https:" ? "wss://" : "ws://";
|
||||||
game = new Chess(newFen);
|
const socket = new WebSocket(
|
||||||
board.position(newFen);
|
wsScheme + window.location.host + "/games/" + gameId + ".ws"
|
||||||
console.log("Got fen " + newFen + ". refreshing board...");
|
);
|
||||||
updateStatusText();
|
socket.onmessage = function (event) {
|
||||||
}
|
refresh(event.data);
|
||||||
|
};
|
||||||
refresh(fen);
|
|
||||||
|
|
||||||
const wsScheme = window.location.protocol === "https:" ? "wss://" : "ws://";
|
|
||||||
const socket = new WebSocket(wsScheme + window.location.host + "/games/" + gameId + ".ws");
|
|
||||||
socket.onmessage = function (event) {
|
|
||||||
refresh(event.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Auto reconnect.
|
|
||||||
socket.onclose = function () {
|
|
||||||
alert("Socket closed. Reconnecting...");
|
|
||||||
setTimeout(function () {
|
|
||||||
initGameBoard(player, gameId);
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// Auto reconnect.
|
||||||
|
socket.onclose = function () {
|
||||||
|
alert("Socket closed. Reconnecting...");
|
||||||
|
setTimeout(function () {
|
||||||
|
initGameBoard(player, gameId);
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
// noinspection JSUnusedGlobalSymbols
|
||||||
export default initGameBoard;
|
export default Game;
|
||||||
|
|||||||
Reference in New Issue
Block a user