shaack / cm-chessboard

A JavaScript chessboard without dependencies. Rendered in SVG, coded in ES6. Views FEN, handles move input, animated, responsive, expandable
MIT License
209 stars 63 forks source link

Add a WebSocket example to the demo #136

Open programarivm opened 4 months ago

programarivm commented 4 months ago

👋 Hi there,

Alternatively to chess.js which is a great JavaScript library, the chess functionality can also be provided by a WebSocket chess server using a different approach. This is the case for PHP Chess Server as shown in the example below.

output_trimmed_enhanced_reduced

// index.js

import {INPUT_EVENT_TYPE, COLOR, Chessboard, BORDER_TYPE} from "cm-chessboard";
import {Accessibility} from "../../../vendor/cm-chessboard/src/extensions/accessibility/Accessibility.js";
import {MARKER_TYPE, Markers} from "../../../vendor/cm-chessboard/src/extensions/markers/Markers.js";
import {FEN} from "../../../vendor/cm-chessboard/src/model/Position.js";
import {PromotionDialog} from "../../../vendor/cm-chessboard/src/extensions/promotion-dialog/PromotionDialog.js";
import Ws from './Ws.js';

const inputHandler = (event) => {
  if (event.type === INPUT_EVENT_TYPE.movingOverSquare) {
    return;
  }

  if (event.type !== INPUT_EVENT_TYPE.moveInputFinished) {
    event.chessboard.removeMarkers(MARKER_TYPE.dot);
    event.chessboard.removeMarkers(MARKER_TYPE.bevel);
  }

  if (event.type === INPUT_EVENT_TYPE.moveInputStarted) {
    ws.send(`/legal ${event.square}`);
    return true;
  } else if (event.type === INPUT_EVENT_TYPE.validateMoveInput) {
    ws.send(`/play_lan ${event.piece.charAt(0)} ${event.squareFrom}${event.squareTo}`);
    return true;
  }
}

const board = new Chessboard(document.getElementById("board"), {
  position: FEN.start,
  assetsUrl: "https://cdn.jsdelivr.net/npm/cm-chessboard@8.5.0/assets/",
  style: {borderType: BORDER_TYPE.none, pieces: {file: "pieces/staunty.svg"}, animationDuration: 300},
  orientation: COLOR.white,
  extensions: [
    {class: Markers, props: {autoMarkers: MARKER_TYPE.square}},
    {class: PromotionDialog},
    {class: Accessibility, props: {visuallyHidden: true}}
  ]
});

board.enableMoveInput(inputHandler);

const ws = new Ws(board);
await ws.connect();
await ws.send('/start classical fen');
// Ws.js

import {MARKER_TYPE} from "../../../vendor/cm-chessboard/src/extensions/markers/Markers.js";

export default class Ws {
  constructor(chessboard) {
    this.chessboard = chessboard;
    this.socket = null;
  }

  connect() {
    console.log('Establishing connection...');

    return new Promise((resolve, reject) => {
      this.socket = new WebSocket('wss://async.chesslablab.org:8443');

      this.socket.onopen = () => {
        console.log('Opened connection!');
        resolve();
      };

      this.socket.onmessage = (res) => {
        const data = JSON.parse(res.data);
        const msg = Object.keys(data)[0];
        switch (true) {
          case 'error' === msg:
            if (data['error']) {
              console.log('Whoops! Something went wrong.');
            }
            break;

          case '/start' === msg:
            if (data['/start'].mode === 'fen') {
              if (data['/start'].fen) {
                console.log('Started FEN!');
              } else {
                console.log('Invalid FEN, please try again with a different one.');
              }
            }
            break;

          case '/legal' === msg:
            if (data['/legal']) {
              Object.keys(data['/legal'].fen).forEach(key => {
                this.chessboard.addMarker(MARKER_TYPE.dot, key);
              });
            }
            break;

          case '/play_lan' === msg:
            if (data['/play_lan'].fen) {
              this.chessboard.setPosition(data['/play_lan'].fen, true);
            }
            break;

          default:
            break;
        }
      };

      this.socket.onclose = (err) => {
        console.log('The connection has been lost, please reload the page.');
        reject(err);
      };

      this.socket.onerror = (err) => {
        console.log('The connection has been lost, please reload the page.');
        reject(err);
      };
    });
  }

  send(message) {
    if (this.socket) {
      this.socket.send(message);
    }
  }
}

I was just wondering if a simple WebSocket example like this one could be added to the cm-chessboard demo.

Thank you, and keep up the great work!

shaack commented 2 weeks ago

Hi @programarivm,this sounds like a good idea for me. But how is it guaranteed, that the websocket server is always available?

programarivm commented 2 weeks ago

Well, you may want to play chess online with other players. This is an alternative architecture based on a WebSocket server as opposed to front-end JavaScript code.

shaack commented 2 weeks ago

How about creating a new project together like a small open source live chess server? To and from this we could connect both sides/projects.

programarivm commented 2 weeks ago

Sounds like a plan!

chesslablab/website is an open-source platform that is using this WebSocket server on AWS. It has an estimated cost of about $12 a month on a t2.micro instance.

screencapture-us-east-1-console-aws-amazon-costmanagement-home-2024-06-25-12_04_12