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
|
||||
|
||||
1. [.NET 7 SDK](http://dot.net/)
|
||||
2. Execute `dotnet run` to run the app
|
||||
3. Use your browser to view [http://localhost:5000](http://localhost:5000)
|
||||
2. Run `npm i` at directory `/src/Aiursoft.ChessServer/wwwroot/`
|
||||
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
|
||||
|
||||
|
||||
@@ -15,19 +15,50 @@
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
@* Make the following p auto warp *@
|
||||
|
||||
|
||||
<p id="status" class="text-center text-wrap text-primary mt-1"></p>
|
||||
<p id="fen" class="text-wrap text-muted hidden"></p>
|
||||
</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 {
|
||||
<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">
|
||||
// Ask the user to enter w or b:
|
||||
import initGameBoard from "/site.js";
|
||||
const player = prompt("Please enter your color (w or b):");
|
||||
initGameBoard(player, @Model);
|
||||
import Game from "/site.js";
|
||||
let game = new Game(@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>
|
||||
}
|
||||
@@ -1,107 +1,164 @@
|
||||
import { Chess } from "/node_modules/chess.js/dist/esm/chess.js";
|
||||
|
||||
const statusControl = $('#status');
|
||||
const fenControl = $('#fen');
|
||||
const WHITE = "w";
|
||||
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) {
|
||||
$.get("/games/" + gameId + ".fen", function (fen) {
|
||||
let board = null;
|
||||
let game = null;
|
||||
$.get("/games/" + gameId + ".fen", function (fen) {
|
||||
let board = null;
|
||||
let game = null;
|
||||
|
||||
// Happens when a player picks up a piece.
|
||||
function onDragStart(source, piece, position, _) {
|
||||
// only allow moving players own pieces
|
||||
if ((game.turn() !== player)) {
|
||||
return false
|
||||
}
|
||||
// Happens when a player picks up a piece.
|
||||
function onDragStart(source, piece, position, _) {
|
||||
// only allow moving players own pieces
|
||||
if (game.turn() !== player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// do not pick up pieces if the game is over
|
||||
if (game.isGameOver()) return false
|
||||
// do not pick up pieces if the game is over
|
||||
if (game.isGameOver()) return false;
|
||||
|
||||
// only pick up pieces for the side to move
|
||||
if ((game.turn() === 'w' && piece.search(/^b/) !== -1) ||
|
||||
(game.turn() === 'b' && piece.search(/^w/) !== -1)) {
|
||||
return false
|
||||
}
|
||||
// only pick up pieces for the side to move
|
||||
if (
|
||||
(game.turn() === "w" && piece.search(/^b/) !== -1) ||
|
||||
(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) {
|
||||
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';
|
||||
}
|
||||
// Hack to make sure castling\promotion works.
|
||||
function onSnapEnd() {
|
||||
board.position(game.fen());
|
||||
}
|
||||
|
||||
function updateStatusText() {
|
||||
let status;
|
||||
let moveColor = "White";
|
||||
if (game.turn() === "b") {
|
||||
moveColor = "Black";
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
// Hack to make sure castling\promotion works.
|
||||
function onSnapEnd() {
|
||||
board.position(game.fen())
|
||||
}
|
||||
const config = {
|
||||
orientation: player === "w" ? "white" : "black",
|
||||
draggable: true,
|
||||
dragoffBoard: "snapback",
|
||||
position: fen,
|
||||
onDragStart: onDragStart,
|
||||
onSnapEnd: onSnapEnd,
|
||||
onDrop: onDrop,
|
||||
};
|
||||
board = ChessBoard("board", config);
|
||||
|
||||
function updateStatusText() {
|
||||
let status;
|
||||
let moveColor = 'White';
|
||||
if (game.turn() === 'b') {
|
||||
moveColor = 'Black';
|
||||
} 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());
|
||||
}
|
||||
function refresh(newFen) {
|
||||
game = new Chess(newFen);
|
||||
board.position(newFen);
|
||||
console.log("Got fen " + newFen + ". refreshing board...");
|
||||
updateStatusText();
|
||||
}
|
||||
|
||||
const config = {
|
||||
orientation: player === "w" ? "white" : "black",
|
||||
draggable: true,
|
||||
dragoffBoard: 'snapback',
|
||||
position: fen,
|
||||
onDragStart: onDragStart,
|
||||
onSnapEnd: onSnapEnd,
|
||||
onDrop: onDrop
|
||||
};
|
||||
board = ChessBoard('board', config);
|
||||
refresh(fen);
|
||||
|
||||
function refresh(newFen) {
|
||||
game = new Chess(newFen);
|
||||
board.position(newFen);
|
||||
console.log("Got fen " + newFen + ". refreshing board...");
|
||||
updateStatusText();
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
});
|
||||
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);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
export default initGameBoard;
|
||||
export default Game;
|
||||
|
||||
Reference in New Issue
Block a user