Šachy vylepšení logiky + přidání možnosti hrát online

This commit is contained in:
Histmy 2025-02-02 11:39:34 +01:00
parent 97349ef57b
commit eb2cff2f54
Signed by: Histmy
GPG Key ID: AC2E43C463D8F329
6 changed files with 191 additions and 52 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "denim_3001",
"version": "3001.61.3",
"version": "3001.62.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "denim_3001",
"version": "3001.61.2",
"version": "3001.62.0",
"license": "ISC",
"dependencies": {
"@discordjs/voice": "^0.17.0",

View File

@ -1,6 +1,6 @@
{
"name": "denim_3001",
"version": "3001.61.3",
"version": "3001.62.0",
"description": "Toto je velmi kvalitní bot.",
"repository": {
"url": "https://github.com/Histmy/Denim-Bot/"

40
res/sachy/game.html Normal file
View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hra - $domena</title>
<style>
table {
border-collapse: collapse;
}
td {
padding: 0;
}
img {
width: 2em;
height: 2em;
display: block;
}
.nic {
width: 2em;
}
</style>
</head>
<body>
<h1>Hra</h1>
<p>$pecko</p>
<table>
$hra
</table>
</body>
</html>

View File

@ -98,7 +98,7 @@ function jeCheck(barva: Hra["hraje"], deska: Deska) {
// od pawnů
[7, 9].forEach(i => {
const pis = deska[index - i];
if (pis?.typ == "pawn" && pis.barva != barva) check = true;
if (pis?.typ == "pawn" && pocetKeStrane[index][i == 7 ? 1 : 3] > 0 && pis.barva != barva) check = true;
});
// od králika
@ -173,6 +173,13 @@ function tahyProPawny(index: number, deska: Deska) {
if (index % 8 != 7 && deska[index - 7] && deska[index - 7]!.barva != barva)
tahy.push(index - 7);
// en croassant
if (pocetKeStrane[index][3] > 0 && deska[index - 1]?.enPassantovatelna)
tahy.push(index - 9);
if (pocetKeStrane[index][1] > 0 && deska[index + 1]?.enPassantovatelna)
tahy.push(index - 7);
// Blokáda
if (deska[index - 8])
return tahy;
@ -183,13 +190,6 @@ function tahyProPawny(index: number, deska: Deska) {
if (deska[index]?.netahnuta && !deska[index - 16])
tahy.push(index - 16);
// en croassant
if (deska[index - 1]?.enPassantovatelna)
tahy.push(index - 9);
if (deska[index + 1]?.enPassantovatelna)
tahy.push(index - 7);
return tahy;
}
@ -218,6 +218,8 @@ function tahyProKrale(index: number, deska: Deska) {
break;
}
if (Math.abs(i) == 3) continue;
const novaDeska = [...deska];
novaDeska[index + i] = deska[index];
novaDeska[index] = undefined;
@ -302,6 +304,45 @@ function renderHra(hra: Hra, konec?: true) {
const deska = [...hra.deska];
const vybranejPis = hra.vybranejPis;
const policka: Policko[] = [];
hra.tahy = [];
if (typeof vybranejPis == "number") hra.tahy = legalniTahy(vybranejPis, deska);
for (let i = 0; i < 64; i++) {
const figurka = deska[i];
const jeTah = hra.tahy.includes(i);
const cernyPozadi = (Math.floor(i / 8) + (i % 8)) % 2;
if (!figurka) {
policka.push(jeTah
? { typ: "tahnutelne", emoutId: cernyPozadi ? "1066376051422404628" : "1066376343807336488" }
: { typ: "prazdne", barva: cernyPozadi ? "cerna" : "bila" }
);
} else {
let klic = (cernyPozadi ? "g" : "w") + (figurka.barva == "bila" ? "w" : "b") + figurka.typ;
if (jeTah) klic += "t";
else if (figurka.typ == "king" && figurka.barva == hra.hraje && jeCheck(figurka.barva, deska)) klic += "c";
const emoutId = fokinLookupTable[klic];
const tahnutelny = !konec && figurka.barva == hra.hraje || jeTah;
policka.push({ emoutId, typ: tahnutelny ? "tahnutelne" : "figurka" });
}
}
return policka;
}
function generateGameEmbed(hra: Hra, konec?: true) {
const embed: APIEmbed = {
fields: [
{
@ -317,51 +358,64 @@ function renderHra(hra: Hra, konec?: true) {
]
};
hra.tahy = [];
if (typeof vybranejPis == "number") hra.tahy = legalniTahy(vybranejPis, deska);
const policka = renderHra(hra, konec);
for (let i = 0; i < 64; i++) {
const figurka = deska[i];
const klikanec = klikance[i];
const policko = policka[i];
const field = embed.fields![(i % 8) < 4 ? 0 : 1];
const prvniDuhy = (i % 8) < 4 ? 0 : 1;
const field = embed.fields![prvniDuhy];
const cernyPozadi = (Math.floor(i / 8) + (i % 8)) % 2;
if (!figurka) {
field.value += hra.tahy.includes(i)
? (cernyPozadi ? `[<:h:1066376051422404628>](http://${sachyDomena}/${klikanec})` : `[<:h:1066376343807336488>](http://${sachyDomena}/${klikanec})`)
: (cernyPozadi ? "🟩" : "⬜");
} else {
let klic = (cernyPozadi ? "g" : "w") + (figurka.barva == "bila" ? "w" : "b") + figurka.typ;
if (hra.tahy.includes(i)) klic += "t";
if (figurka.typ == "king" && figurka.barva == hra.hraje && jeCheck(figurka.barva, deska)) klic += "c";
const emout = fokinLookupTable[klic];
const figurkaStr = `<:h:${emout}>`;
if (!konec && figurka.barva == hra.hraje || hra.tahy.includes(i)) {
field.value += `[${figurkaStr}](http://${sachyDomena}/${klikanec})`;
} else {
field.value += figurkaStr;
if (policko.typ == "prazdne")
field.value += policko.barva == "bila" ? "⬜" : "🟩";
else {
const emout = `<:h:${policko.emoutId}>`;
field.value += policko.typ == "figurka" ? emout : `[${emout}](http://${sachyDomena}/${klikance[i]})`;
}
if ((i % 8 == 3 || i % 8 == 7)) field.value += "\n";
}
if (i % 8 == 3 || i % 8 == 7) field.value += "\n";
if (embed.fields![0].value.length > 1024 || embed.fields![1].value.length > 1024) {
embed.fields = [
{
name: "a je to pici!",
value: `Tuta pozice by vobsahovala moc vodkazu a diky pice diskordu se teda neda vykreslit. Tuten tah budes muset bohuzel vodehrat [na webu](http://${sachyDomena}/play).`
}
];
}
return embed;
}
emiter.on("render", (hrac, respond) => {
const gameID = hraci.get(hrac)!;
if (!gameID) return respond("Však vůbec nehraješ ty magore", true);
const hra = hry.get(gameID)!;
const hraje = hra.hraje == (hra.bila == hrac ? "bila" : "cerna");
const policka = renderHra(hra);
let html = "<tr>";
for (let i = 0; i < 64; i++) {
const policko = policka[i];
if (policko.typ == "prazdne")
html += policko.barva == "bila" ? `<td><img class="nic" src="https://cdn.jsdelivr.net/gh/twitter/twemoji@latest/assets/svg/2b1c.svg"></td>` : `<td><img class="nic" src="https://cdn.jsdelivr.net/gh/twitter/twemoji@latest/assets/svg/1f7e9.svg"></td>`;
else {
const obrazek = `<img src="https://cdn.discordapp.com/emojis/${policko.emoutId}.webp">`;
html += (policko.typ == "figurka" || !hraje) ? `<td>${obrazek}</td>` : `<td><a href="http://${sachyDomena}/play/${klikance[i]}">${obrazek}</a></td>`;
}
if (i % 8 == 7) html += "</tr>";
}
respond(html);
});
function konec(mes: Message, hra: Hra, duvod: string) {
mes.edit({ content: `hra zkoncila ${duvod}`, embeds: [renderHra(hra, true)] });
mes.edit({ content: `hra zkoncila ${duvod}`, embeds: [generateGameEmbed(hra, true)] });
const gameID = hra.id;
@ -399,7 +453,7 @@ function prekreslitHru(hra: Hra) {
if (pat)
return konec(mes, hra, `achylova sytuace`);
mes.edit({ content: "", embeds: [renderHra(hra)] });
mes.edit({ content: "", embeds: [generateGameEmbed(hra)] });
}
emiter.on("tah", (hrac, policko, respond, promo) => {
@ -412,7 +466,7 @@ emiter.on("tah", (hrac, policko, respond, promo) => {
const index = klikance.findIndex(e => e == policko);
if (typeof index != "number") return respond("Co to meleš za hovna?");
if (index == -1) return respond("Co to meleš za hovna?");
// Kliknul na stejnej pis
if (hra.vybranejPis == index) {
@ -485,7 +539,7 @@ const exp: Modul = {
hraci.set(mes.author.id, nextGameID);
hraci.set(druhej, nextGameID);
message.edit({ content: "", embeds: [renderHra(hra)] });
message.edit({ content: "", embeds: [generateGameEmbed(hra)] });
}
},
@ -510,7 +564,7 @@ type Hra = {
bila: string;
cerna: string;
deska: Deska;
hraje: "bila" | "cerna";
hraje: Figurka["barva"];
tahy: number[];
vybranejPis?: number;
message: Message;
@ -524,3 +578,11 @@ type Figurka = {
netahnuta: boolean;
enPassantovatelna: boolean;
};
type Policko = {
typ: "prazdne";
barva: Figurka["barva"];
} | {
typ: "figurka" | "tahnutelne";
emoutId: string;
};

View File

@ -14,11 +14,13 @@ const indexStranka = readFileSync(`${basePath}/index.html`).toString();
const tahStranka = readFileSync(`${basePath}/tah.html`).toString();
const promoStranka = readFileSync(`${basePath}/promotion.html`).toString();
const chybaStranka = readFileSync(`${basePath}/error.html`).toString();
const hraStranka = readFileSync(`${basePath}/game.html`).toString();
export const sachyDomena = process.env.sachyDomena ?? "šach.ml";
interface Eventy {
tah: (playerId: string, square: string, verify: (err?: string) => void, promotionType?: string) => void;
render: (playerId: string, send: (html: string, err?: true) => void) => void;
}
export const emiter = new TypedEmitter<Eventy>();
@ -101,9 +103,9 @@ function loginErr(res: ServerResponse) {
);
}
async function overeni(url: string, session: Session, res: ServerResponse) {
async function overeni(url: URL, session: Session, res: ServerResponse) {
const code = (new URL(url, "h:/")).searchParams.get("code");
const code = url.searchParams.get("code");
const data = await fetch("https://discord.com/api/v10/oauth2/token", { method: "post", body: `client_id=1064269692165963826&client_secret=8HHKODmb1HTLWoJGVyoinSU_9323dEla&grant_type=authorization_code&code=${code}&redirect_uri=http%3A%2F%2F${encodeURIComponent(sachyDomena)}%2Foauth`, headers: { "Content-type": "application/x-www-form-urlencoded" } })
.then(r => r.json())
.catch(err => log("sachy jednotka:", err));
@ -146,16 +148,48 @@ function validateMove(res: ServerResponse, session: Session, url: string) {
emiter.emit("tah", session.discordId!, pismenko, overeni, promoNa);
}
function renderHra(url: URL, session: Session, res: ServerResponse) {
if (!session.logedIn) return redirect(res, `/login?redirect=/play${url.search}`);
let error: string | undefined = undefined;
function serveHTML(html: string, err?: true) {
const vec = hraStranka
.replace("$domena", sachyDomena);
let errForPrint = error ?? err;
if (errForPrint) return res.end(vec.replace("$pecko", html).replace("$hra", ""));
res.end(vec.replace("$hra", html).replace("$pecko", ""));
}
const tah = url.pathname.match(/\/play\/(.)$/);
if (!tah) return emiter.emit("render", session.discordId!, serveHTML);
const promoNa = url.search[1];
function handlePromote(err?: string) {
if (err == "--promote--")
return res.end(promoStranka.replace("$domena", sachyDomena));
error = err;
emiter.emit("render", session.discordId!, serveHTML);
}
emiter.emit("tah", session.discordId!, tah[1], handlePromote, promoNa);
}
const server = createServer((req, res) => {
const session = startSession(req, res);
const url = new URL(req.url!, `http://${sachyDomena}`);
// log(url.pathname, url.search, url.searchParams, req.url);
// Zpracování tahu
if (url.pathname.length == 2 || url.pathname.length == 4) return validateMove(res, session, req.url!);
// Oauth
if (req.url?.startsWith("/oauth")) return overeni(req.url, session, res);
// Hra v prohlížeči
if (url.pathname.startsWith("/play")) return renderHra(url, session, res);
// Jinak
switch (url.pathname) {
@ -172,6 +206,9 @@ const server = createServer((req, res) => {
return login(session, res);
}
case "/oauth":
return overeni(url, session, res);
case "/logout":
return logout(session, res);

View File

@ -55,7 +55,7 @@ const statusTable: SRecord<number> = {
export function statusToDLOG3(status: ClientPresenceStatusData | null) {
if (!status) return "-";
let tovje = [];
const tovje = [];
for (const [key, value] of Object.entries(status)) {
const platform = platformTable[key] ?? key;