Gitea/workflows #1
@@ -54,6 +54,12 @@ jobs:
|
|||||||
name: site-publish
|
name: site-publish
|
||||||
path: publish
|
path: publish
|
||||||
|
|
||||||
|
# GitHub artifacts don't preserve the Unix executable bit, so Stockfish (the only file
|
||||||
|
# the app spawns as a subprocess) arrives non-executable. Restore 755 here; rsync -a then
|
||||||
|
# carries it to the server, where the service user can run it regardless of file owner.
|
||||||
|
- name: Restore Stockfish executable bit
|
||||||
|
run: chmod 755 publish/Resources/stockfish-ubuntu-x86-64-sse41-popcnt
|
||||||
|
|
||||||
- name: Prepare SSH
|
- name: Prepare SSH
|
||||||
run: |
|
run: |
|
||||||
install -m 700 -d ~/.ssh
|
install -m 700 -d ~/.ssh
|
||||||
|
|||||||
@@ -88,16 +88,29 @@ html, body {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Overlaid on the bottom of the card at game end, so it never adds to the card's height
|
||||||
|
(which would shift the whole grid as games finish and are cleared). */
|
||||||
|
.gameOverlay {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
padding: 0.9rem 1rem;
|
||||||
|
background-color: rgba(20, 20, 22, 0.92);
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.gameResult {
|
.gameResult {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #8cd5ed;
|
color: #8cd5ed;
|
||||||
margin-top: 0.75rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.copyPgnBtn {
|
.copyPgnBtn {
|
||||||
display: block;
|
|
||||||
margin: 0.75rem auto 0;
|
|
||||||
background-color: #8cd5ed;
|
background-color: #8cd5ed;
|
||||||
color: #262626;
|
color: #262626;
|
||||||
border: 0;
|
border: 0;
|
||||||
@@ -154,6 +167,13 @@ body.fullscreen-open {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
|
/* Reserve two lines so the header doesn't reflow as the move count gains digits,
|
||||||
|
the side-to-move text changes, or the check tag toggles. */
|
||||||
|
line-height: 1.3;
|
||||||
|
min-height: 2.6em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Always occupies its space (it's only hidden, not removed) so toggling "check" never
|
/* Always occupies its space (it's only hidden, not removed) so toggling "check" never
|
||||||
|
|||||||
@@ -274,34 +274,42 @@ const Spectate = {
|
|||||||
|
|
||||||
if (!card) return;
|
if (!card) return;
|
||||||
|
|
||||||
let banner = card.querySelector(".gameResult");
|
// The result and Copy PGN button live in an overlay anchored over the board so showing
|
||||||
|
// them at game end never changes the card's height (which would shift the whole grid).
|
||||||
|
let overlay = card.querySelector(".gameOverlay");
|
||||||
|
|
||||||
if (!text) {
|
if (!text) {
|
||||||
banner?.remove();
|
overlay?.remove();
|
||||||
card.querySelector(".copyPgnBtn")?.remove();
|
|
||||||
card.classList.remove("over");
|
card.classList.remove("over");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!banner) {
|
if (!overlay) {
|
||||||
banner = document.createElement("div");
|
overlay = document.createElement("div");
|
||||||
|
overlay.className = "gameOverlay";
|
||||||
|
|
||||||
|
const banner = document.createElement("div");
|
||||||
banner.className = "gameResult";
|
banner.className = "gameResult";
|
||||||
card.appendChild(banner);
|
overlay.appendChild(banner);
|
||||||
|
|
||||||
|
card.appendChild(overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
banner.textContent = text;
|
overlay.querySelector(".gameResult").textContent = text;
|
||||||
card.classList.add("over");
|
card.classList.add("over");
|
||||||
this.addCopyPgn(gameId, card);
|
this.addCopyPgn(gameId, card);
|
||||||
},
|
},
|
||||||
|
|
||||||
addCopyPgn(gameId, card) {
|
addCopyPgn(gameId, card) {
|
||||||
if (card.querySelector(".copyPgnBtn")) return;
|
const overlay = card.querySelector(".gameOverlay");
|
||||||
|
|
||||||
|
if (!overlay || overlay.querySelector(".copyPgnBtn")) return;
|
||||||
|
|
||||||
const btn = document.createElement("button");
|
const btn = document.createElement("button");
|
||||||
btn.className = "copyPgnBtn";
|
btn.className = "copyPgnBtn";
|
||||||
btn.textContent = "Copy PGN";
|
btn.textContent = "Copy PGN";
|
||||||
btn.onclick = (event) => { event.stopPropagation(); this.copyPgn(gameId); };
|
btn.onclick = (event) => { event.stopPropagation(); this.copyPgn(gameId); };
|
||||||
card.appendChild(btn);
|
overlay.appendChild(btn);
|
||||||
|
|
||||||
// Prefetch now (while the game is still in memory) so copy works during the
|
// Prefetch now (while the game is still in memory) so copy works during the
|
||||||
// brief window before the finished game is cleaned up.
|
// brief window before the finished game is cleaned up.
|
||||||
|
|||||||
@@ -87,10 +87,12 @@ static void save_global_weights(); /* defined below; load rewrites stale files
|
|||||||
static void load_global_weights(const char* path) {
|
static void load_global_weights(const char* path) {
|
||||||
WinCounters loaded{};
|
WinCounters loaded{};
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
bool fileExisted = false;
|
||||||
|
|
||||||
if (path && *path) {
|
if (path && *path) {
|
||||||
std::ifstream f(path);
|
std::ifstream f(path);
|
||||||
if (f) {
|
if (f) {
|
||||||
|
fileExisted = true;
|
||||||
int version = 0;
|
int version = 0;
|
||||||
if ((f >> version) && version == LEARNED_VERSION) {
|
if ((f >> version) && version == LEARNED_VERSION) {
|
||||||
auto readTable = [&](double t[chess::PIECE_TYPE_NB][64]) -> bool {
|
auto readTable = [&](double t[chess::PIECE_TYPE_NB][64]) -> bool {
|
||||||
@@ -110,9 +112,12 @@ static void load_global_weights(const char* path) {
|
|||||||
g_counts = ok ? loaded : WinCounters{};
|
g_counts = ok ? loaded : WinCounters{};
|
||||||
recompute_weights();
|
recompute_weights();
|
||||||
|
|
||||||
/* Stale / wrong-version / unreadable: wipe the file's contents (keep the file) by
|
/* Only create a fresh file when none exists yet (first run): write it blank-but-versioned
|
||||||
* rewriting it blank-but-versioned, so the next load matches and we never reread garbage. */
|
* so there's a valid target to persist into. If a file IS present but couldn't be parsed
|
||||||
if (!ok)
|
* (old format, corrupt, or a partial write), leave its bytes untouched — never destroy
|
||||||
|
* accumulated training data on startup. We just play from neutral weights this session;
|
||||||
|
* the next training apply() overwrites the file with a clean, current-format save. */
|
||||||
|
if (!ok && !fileExisted)
|
||||||
save_global_weights();
|
save_global_weights();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user