This commit is contained in:
Maciej Caderek
2022-04-01 20:52:15 +02:00
parent a003ab6089
commit 2a252b6c69
8 changed files with 136 additions and 44 deletions

View File

@@ -1,5 +1,4 @@
import WebFont from "webfontloader"; import WebFont from "webfontloader";
// import * as Hammer from "hammerjs";
import { render } from "solid-js/web"; import { render } from "solid-js/web";
import { BoardStyle } from "./types"; import { BoardStyle } from "./types";
@@ -17,7 +16,6 @@ import createAnimation from "./encoders/createAnimation";
import readFile from "./utils/readFile"; import readFile from "./utils/readFile";
import download from "./utils/download"; import download from "./utils/download";
import { compressPGN } from "./game/PGNHelpers"; import { compressPGN } from "./game/PGNHelpers";
// import extractUrlData from "./persistance/extractUrlData";
import importFromLink from "./imports/importFromLink"; import importFromLink from "./imports/importFromLink";
import isFEN from "./utils/isFEN"; import isFEN from "./utils/isFEN";
import isPGN from "./utils/isPGN"; import isPGN from "./utils/isPGN";
@@ -230,6 +228,10 @@ const main = async () => {
setState("boardConfig", "sounds", !state.boardConfig.sounds); setState("boardConfig", "sounds", !state.boardConfig.sounds);
saveConfig("board"); saveConfig("board");
}, },
toggleSpeech() {
setState("boardConfig", "speech", !state.boardConfig.speech);
saveConfig("board");
},
}; };
/* Render the page */ /* Render the page */
@@ -332,26 +334,14 @@ const main = async () => {
handlers.loadPGN(content); handlers.loadPGN(content);
} }
}); });
} else {
// const hammer = new Hammer.Manager(board.canvas);
// hammer.add(new Hammer.Swipe());
// hammer.add(new Hammer.Pinch());
// hammer.add(new Hammer.Press({ time: 500 }));
// hammer.add(new Hammer.Tap({ taps: 1 }));
// hammer.on("swiperight", handlers.next);
// hammer.on("swipeleft", handlers.prev);
// hammer.on("swipeup", handlers.first);
// hammer.on("swipedown", handlers.last);
// hammer.on("pinchin", handlers.showBorder);
// hammer.on("pinchout", handlers.hideBorder);
// hammer.on("tap", handlers.next);
// hammer.on("press", handlers.flip);
} }
}; };
/* Boot */ /* Boot */
WebFont.load({ Promise.all([
new Promise((resolve) =>
WebFont.load({
google: { google: {
families: ["Ubuntu:500,700", "Fira Mono:500"], families: ["Ubuntu:500,700", "Fira Mono:500"],
}, },
@@ -359,5 +349,14 @@ WebFont.load({
families: ["Chess"], families: ["Chess"],
urls: ["/fonts.css"], urls: ["/fonts.css"],
}, },
active: main, active: () => resolve(null),
}); })
).catch(() => null),
new Promise((resolve) => {
if (speechSynthesis.getVoices().length > 0) {
resolve(null);
} else {
window.speechSynthesis.onvoiceschanged = resolve;
}
}).catch(() => null),
]).then(() => main());

View File

@@ -3,6 +3,7 @@ import Board from "../board/Board";
import Game from "../game/Game"; import Game from "../game/Game";
import { setState, state } from "../state"; import { setState, state } from "../state";
import sfx from "./sfx"; import sfx from "./sfx";
import Speech, { sanToText } from "./speach";
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -11,11 +12,14 @@ class Player {
private game: Game = new Game(); private game: Game = new Game();
private ply: number = 0; private ply: number = 0;
private callback: (playing: boolean, ply: number) => void = () => {}; private callback: (playing: boolean, ply: number) => void = () => {};
private speech: Speech;
public playing: boolean = false; public playing: boolean = false;
private firstRender: Promise<void>; private firstRender: Promise<void>;
constructor(private board: Board, private config: GameConfig) { constructor(private board: Board, private config: GameConfig) {
this.speech = new Speech();
this.firstRender = this.board this.firstRender = this.board
.frame(this.game.getPosition(0), this.game.header) .frame(this.game.getPosition(0), this.game.header)
.then((_) => this.board.render()); .then((_) => this.board.render());
@@ -109,6 +113,10 @@ class Player {
await this.board.frame(position, this.game.header); await this.board.frame(position, this.game.header);
this.board.render(); this.board.render();
if (state.boardConfig.speech) {
this.speech.say(sanToText(position.move?.san as string));
}
if (this.ply > 0 && state.boardConfig.sounds) { if (this.ply > 0 && state.boardConfig.sounds) {
if (position.mate) { if (position.mate) {
sfx.snap.play(); sfx.snap.play();

66
src/player/speach.ts Normal file
View File

@@ -0,0 +1,66 @@
const words: { [key: string]: string } = {
x: " takes ",
"+": " check!",
"#": " mate!",
"=": " promotes to a ",
P: "pawn ",
R: "rook ",
B: "bishop ",
N: "knight ",
Q: "queen ",
K: "king ",
"O-O": "short castle",
"O-O-O": "long castle",
};
const config = {
volume: 50,
rate: 2,
lang: "en-US",
};
const sanToText = (move: string) => {
if (move === "O-O" || move === "O-O-O") {
move = words[move];
} else {
// Handles special cases like R2a6 ore Nbd2
const special = move.match(/[a-h1-8][a-h][1-8]/);
if (special) {
move = move.replace(
special[0],
`${special[0][0]} ${special[0][1]}${special[0][2]}`
);
}
move = move
.split("")
.map((x) => words[x] ?? x)
.join("")
.replace(/\s{2,}/g, " ");
}
return move;
};
class Speach {
private voice: SpeechSynthesisVoice | undefined;
constructor() {
const voices = speechSynthesis.getVoices();
this.voice = voices.find((voice) => voice.lang === config.lang);
}
say(text: string) {
const info = new SpeechSynthesisUtterance(text);
info.volume = config.volume / 100;
info.lang = config.lang;
if (this.voice) {
info.voice = this.voice;
}
info.rate = 1 + config.rate / 10;
speechSynthesis.speak(info);
}
}
export { sanToText };
export default Speach;

View File

@@ -21,6 +21,7 @@ const initialBoardConfig: BoardConfig = {
showCoords: true, showCoords: true,
showShadows: false, showShadows: false,
sounds: true, sounds: true,
speech: false,
flipped: false, flipped: false,
}; };
@@ -73,8 +74,6 @@ const initialState: State = {
refreshHash: true, refreshHash: true,
}; };
console.log(initialState);
const [state, setState] = createStore(initialState); const [state, setState] = createStore(initialState);
export { state, setState }; export { state, setState };

View File

@@ -90,6 +90,7 @@ export type BoardConfig = {
showCoords: boolean; showCoords: boolean;
showShadows: boolean; showShadows: boolean;
sounds: boolean; sounds: boolean;
speech: boolean;
flipped: boolean; flipped: boolean;
}; };
@@ -173,6 +174,7 @@ export type Handlers = {
downloadImage: () => Promise<void>; downloadImage: () => Promise<void>;
downloadAnimation: () => Promise<void>; downloadAnimation: () => Promise<void>;
toggleSound(): void; toggleSound(): void;
toggleSpeech(): void;
}; };
export type Header = { export type Header = {

View File

@@ -24,7 +24,7 @@
} }
.header__options { .header__options {
padding: 0 2rem; padding: 0 2rem 0;
text-align: right; text-align: right;
} }
@@ -36,8 +36,7 @@
color: var(--color-text); color: var(--color-text);
padding-top: 1rem; padding-top: 1rem;
height: 100%; height: 100%;
margin-left: 10px; margin-left: 0.5rem;
/* background-color: aqua; */
text-align: center; text-align: center;
} }

View File

@@ -1,9 +1,9 @@
import { Component, createSignal } from "solid-js"; import { Component, createSignal } from "solid-js";
import { Handlers } from "../../types"; import { Handlers } from "../../types";
// import { state, setState } from "../../state"; import { state, setState } from "../../state";
import "./Header.css"; import "./Header.css";
const Header: Component<{ handlers: Handlers }> = () => { const Header: Component<{ handlers: Handlers }> = (props) => {
const [darkMode, setDarkMode] = createSignal(true); const [darkMode, setDarkMode] = createSignal(true);
return ( return (
@@ -15,6 +15,36 @@ const Header: Component<{ handlers: Handlers }> = () => {
{/* <div class="header__options-ico" onClick={() => {}}> {/* <div class="header__options-ico" onClick={() => {}}>
<i class="las la-question-circle"></i> <i class="las la-question-circle"></i>
</div> */} </div> */}
<div
class="header__options-ico"
onClick={() => {
props.handlers.toggleSound();
}}
title={state.boardConfig.sounds ? "MUTE" : "SOUND ON"}
>
<i
classList={{
las: true,
"la-volume-mute": !state.boardConfig.sounds,
"la-volume-up": state.boardConfig.sounds,
}}
></i>
</div>
<div
class="header__options-ico"
onClick={() => {
props.handlers.toggleSpeech();
}}
title={state.boardConfig.speech ? "SPEECH OFF" : "SPEECH ON"}
>
<i
classList={{
las: true,
"la-deaf": !state.boardConfig.speech,
"la-assistive-listening-systems": state.boardConfig.speech,
}}
></i>
</div>
<div <div
class="header__options-ico" class="header__options-ico"
onClick={() => { onClick={() => {

View File

@@ -81,17 +81,6 @@ const Share: Component<{ handlers: Handlers; class?: string }> = (props) => {
> >
<i class="las la-cloud"></i> <i class="las la-cloud"></i>
</button> </button>
<button
classList={{
options__button: true,
"options__button--last": true,
"options__button--active": state.boardConfig.sounds,
}}
onClick={props.handlers.toggleSound}
title={state.boardConfig.sounds ? "MUTE" : "SOUND ON"}
>
<i class="las la-volume-up"></i>
</button>
</div> </div>
<hr /> <hr />
<div className="share__fen"> <div className="share__fen">