Resume the clock on leaving the score sheet, and split the readout in three
Closing the score sheet left the clock paused, so glancing at the score or nudging a clock cost a tap on play. It now picks the turn back up on the way out. openScore() records whether it was the thing that paused the clock, so a pause the players set themselves survives. After a score change there is nothing to resume — newGame() has already moved to IDLE — and resumeNow() only acts on PAUSE, so that case needs no special handling. The scrim behaves the same, since dismissing discards the staged edits and leaves the turn live. togglePause() had the resume arithmetic inline; it splits out as resumeNow() to match pauseNow(), leaving the play button and the sheet on one path. The score line becomes three labelled lines: You: 2 out of 5 Them: 1 Moves: 7 .head is a two-column grid with a max-content label column, so the values line up whatever the system font renders — no hand-tuned em width to drift between iOS and Android. Each line is a display:contents wrapper, which keeps one element per line to hide when a one-point match drops the score. The gap above Moves rides on that row's two cells, since a display:contents row can't carry a margin, and a :not([hidden]) guard drops it when there is no score line above to separate from. Each half reads from its own side of the board, so both players see their own score on the You line. st.score is already indexed by side, so the mirroring costs nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+26
-13
@@ -56,8 +56,10 @@ except installation and the service worker.
|
|||||||
unwinds around it — and a second tap within those two seconds does it. One
|
unwinds around it — and a second tap within those two seconds does it. One
|
||||||
stray thumb can't wipe a live match.
|
stray thumb can't wipe a live match.
|
||||||
- **Pause** freezes mid-turn and resumes exactly where it stopped, delay
|
- **Pause** freezes mid-turn and resumes exactly where it stopped, delay
|
||||||
included. Opening either sheet pauses automatically, and leaves it paused so
|
included. Opening either sheet pauses automatically. The settings sheet leaves
|
||||||
the clock only restarts when both players are ready.
|
it paused — everything in there resets the clocks anyway — while the score
|
||||||
|
sheet picks the turn back up when you close it, so glancing at the score
|
||||||
|
doesn't cost you a tap on play.
|
||||||
|
|
||||||
Backgammon clocks pause between games, and after a cocked die the delay is
|
Backgammon clocks pause between games, and after a cocked die the delay is
|
||||||
normally restarted — use pause and reset for those.
|
normally restarted — use pause and reset for those.
|
||||||
@@ -68,26 +70,37 @@ Set **Match** in the settings to the number of points you're playing to — 1, 3
|
|||||||
5 and up to 29. The time you set is *per point*, so a 5-point match at 2:00 a
|
5 and up to 29. The time you set is *per point*, so a 5-point match at 2:00 a
|
||||||
point puts 10:00 on each clock, and the settings sheet shows you that sum.
|
point puts 10:00 on each clock, and the settings sheet shows you that sum.
|
||||||
|
|
||||||
At **1 point** there's no match to keep score of, so the score line and the
|
Each half reads the state from that player's own side of the board:
|
||||||
score-sheet icon both disappear and you get the plain single-game clock. Set
|
|
||||||
the match to 3 or more and they come back.
|
|
||||||
|
|
||||||
The score sheet is the middle icon in the bar. It reads **You** for whoever is
|
```
|
||||||
holding the phone — the bottom half — and **Them** for the far end, and each
|
You: 1 out of 5
|
||||||
panel shows its own score first, so both players read their own score on the
|
Them: 3
|
||||||
left: `Score: 1 - 3, out of 5`.
|
Moves: 7
|
||||||
|
```
|
||||||
|
|
||||||
|
So "You" always means whoever is sitting at that end — both players read their
|
||||||
|
own score on the top line, and the two halves show mirrored numbers.
|
||||||
|
|
||||||
|
At **1 point** there's no match to keep score of, so the score lines and the
|
||||||
|
score-sheet icon all disappear and you get the plain single-game clock with just
|
||||||
|
its move count. Set the match to 3 or more and they come back.
|
||||||
|
|
||||||
|
The score sheet is the middle icon in the bar. It's filled in from the phone
|
||||||
|
holder's side: **You** is the bottom half, **Them** the far end.
|
||||||
|
|
||||||
Nothing in that sheet takes effect until you press **Done**:
|
Nothing in that sheet takes effect until you press **Done**:
|
||||||
|
|
||||||
- **Done with nothing changed** leaves everything exactly as it was — same turn,
|
- **Done with nothing changed** leaves everything exactly as it was — same turn,
|
||||||
same delay part-spent, same move counts. Tapping outside the sheet is the same
|
same delay part-spent, same move counts — and the clock picks up where it left
|
||||||
as changing nothing, so a stray tap can't restart a game.
|
off. Tapping outside the sheet is the same as changing nothing, so a stray tap
|
||||||
|
can't restart a game. A pause you set yourself before opening the sheet
|
||||||
|
survives; only the sheet's own pause is undone.
|
||||||
- **Change a score** and the game restarts when you press Done: move counters
|
- **Change a score** and the game restarts when you press Done: move counters
|
||||||
back to zero and the turn back to the first tap, waiting on whoever rolls the
|
back to zero and the turn back to the first tap, waiting on whoever rolls the
|
||||||
lower die. The clocks keep whatever they were showing.
|
lower die. The clocks keep whatever they were showing.
|
||||||
- **Change only a clock** — the sheet also lets you correct either player's time
|
- **Change only a clock** — the sheet also lets you correct either player's time
|
||||||
to the second — and the game carries on undisturbed: same turn, same delay
|
to the second — and the game carries on undisturbed and running: same turn,
|
||||||
position, same moves.
|
same delay position, same moves.
|
||||||
|
|
||||||
Shortening the match to fewer points than someone has already won pulls their
|
Shortening the match to fewer points than someone has already won pulls their
|
||||||
score down to fit.
|
score down to fit.
|
||||||
|
|||||||
+54
-30
@@ -75,25 +75,30 @@
|
|||||||
.panel--top .inner{padding:14px 20px max(14px,env(safe-area-inset-top))}
|
.panel--top .inner{padding:14px 20px max(14px,env(safe-area-inset-top))}
|
||||||
.panel--bottom .inner{padding:14px 20px max(14px,env(safe-area-inset-bottom))}
|
.panel--bottom .inner{padding:14px 20px max(14px,env(safe-area-inset-bottom))}
|
||||||
|
|
||||||
|
/* the label column sizes itself to the widest label, so the three values
|
||||||
|
line up whatever the system font turns out to be */
|
||||||
.head{
|
.head{
|
||||||
display:flex;flex-direction:column;align-items:flex-start;
|
display:grid;
|
||||||
gap:3px;
|
grid-template-columns:max-content max-content;
|
||||||
|
justify-content:start;
|
||||||
|
gap:3px 8px;
|
||||||
order:-1;
|
order:-1;
|
||||||
}
|
|
||||||
.score[hidden]{display:none}
|
|
||||||
.score,.moves{
|
|
||||||
font-size:clamp(13px,3.7vw,20px);
|
font-size:clamp(13px,3.7vw,20px);
|
||||||
font-weight:700;
|
font-weight:700;
|
||||||
letter-spacing:.01em;
|
letter-spacing:.01em;
|
||||||
color:var(--idle-mute);
|
color:var(--idle-mute);
|
||||||
white-space:nowrap;
|
white-space:nowrap;
|
||||||
}
|
}
|
||||||
.panel[data-state="active"] .score,
|
.line{display:contents}
|
||||||
.panel[data-state="active"] .moves,
|
.line[hidden]{display:none}
|
||||||
.panel[data-state="flagged"] .score,
|
/* set the moves line apart from the score — a display:contents row can't take
|
||||||
.panel[data-state="flagged"] .moves{color:var(--accent-mute)}
|
a margin itself, so both its cells carry it and the row shifts down evenly.
|
||||||
.panel[data-state="flagged"] .score,
|
The guard drops the space when there's no score line above to separate from. */
|
||||||
.panel[data-state="flagged"] .moves{color:rgba(255,255,255,.55)}
|
.line:not([hidden]) + .line--moves > *{margin-top:7px}
|
||||||
|
|
||||||
|
.panel[data-state="active"] .head,
|
||||||
|
.panel[data-state="flagged"] .head{color:var(--accent-mute)}
|
||||||
|
.panel[data-state="flagged"] .head{color:rgba(255,255,255,.55)}
|
||||||
|
|
||||||
.stack{
|
.stack{
|
||||||
flex:1;
|
flex:1;
|
||||||
@@ -313,8 +318,9 @@
|
|||||||
<section class="panel panel--top" data-side="0" data-state="idle">
|
<section class="panel panel--top" data-side="0" data-state="idle">
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<div class="head">
|
<div class="head">
|
||||||
<div class="score" hidden>Score: <span data-score>0 - 0</span>, out of <span data-target>1</span></div>
|
<span class="line" hidden><span>You:</span><span><span data-you>0</span> out of <span data-target>1</span></span></span>
|
||||||
<div class="moves">Moves: <span data-moves>0</span></div>
|
<span class="line" hidden><span>Them:</span><span data-them>0</span></span>
|
||||||
|
<span class="line line--moves"><span>Moves:</span><span data-moves>0</span></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stack">
|
<div class="stack">
|
||||||
<div class="time" data-time>3:00</div>
|
<div class="time" data-time>3:00</div>
|
||||||
@@ -373,8 +379,9 @@
|
|||||||
<section class="panel panel--bottom" data-side="1" data-state="idle">
|
<section class="panel panel--bottom" data-side="1" data-state="idle">
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<div class="head">
|
<div class="head">
|
||||||
<div class="score" hidden>Score: <span data-score>0 - 0</span>, out of <span data-target>1</span></div>
|
<span class="line" hidden><span>You:</span><span><span data-you>0</span> out of <span data-target>1</span></span></span>
|
||||||
<div class="moves">Moves: <span data-moves>0</span></div>
|
<span class="line" hidden><span>Them:</span><span data-them>0</span></span>
|
||||||
|
<span class="line line--moves"><span>Moves:</span><span data-moves>0</span></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stack">
|
<div class="stack">
|
||||||
<div class="time" data-time>3:00</div>
|
<div class="time" data-time>3:00</div>
|
||||||
@@ -571,9 +578,10 @@
|
|||||||
root: p,
|
root: p,
|
||||||
time: p.querySelector("[data-time]"),
|
time: p.querySelector("[data-time]"),
|
||||||
moves: p.querySelector("[data-moves]"),
|
moves: p.querySelector("[data-moves]"),
|
||||||
score: p.querySelector("[data-score]"),
|
you: p.querySelector("[data-you]"),
|
||||||
|
them: p.querySelector("[data-them]"),
|
||||||
target: p.querySelector("[data-target]"),
|
target: p.querySelector("[data-target]"),
|
||||||
scoreLine: p.querySelector(".score"),
|
scoreLines: [].slice.call(p.querySelectorAll(".line:not(.line--moves)")),
|
||||||
delay: p.querySelector("[data-delay]"),
|
delay: p.querySelector("[data-delay]"),
|
||||||
fill: p.querySelector("[data-fill]"),
|
fill: p.querySelector("[data-fill]"),
|
||||||
dnum: p.querySelector("[data-dnum]")
|
dnum: p.querySelector("[data-dnum]")
|
||||||
@@ -691,7 +699,8 @@
|
|||||||
|
|
||||||
el[i].time.textContent = fmt(main);
|
el[i].time.textContent = fmt(main);
|
||||||
el[i].moves.textContent = st.moves[i];
|
el[i].moves.textContent = st.moves[i];
|
||||||
el[i].score.textContent = st.score[i] + " - " + st.score[1 - i];
|
el[i].you.textContent = st.score[i]; // each panel is "You" to its own player
|
||||||
|
el[i].them.textContent = st.score[1 - i];
|
||||||
el[i].target.textContent = cfg.points;
|
el[i].target.textContent = cfg.points;
|
||||||
|
|
||||||
var showDelay = live && cfg.delay > 0 && dLeft > 0;
|
var showDelay = live && cfg.delay > 0 && dLeft > 0;
|
||||||
@@ -753,18 +762,19 @@
|
|||||||
saveGame();
|
saveGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePause(){
|
function resumeNow(){
|
||||||
if(st.phase === RUN){
|
if(st.phase !== PAUSE) return;
|
||||||
pauseNow();
|
st.turnAt = performance.now() - st.held; // pick the turn up where it stopped
|
||||||
return;
|
st.phase = RUN;
|
||||||
}else if(st.phase === PAUSE){
|
|
||||||
st.turnAt = performance.now() - st.held;
|
|
||||||
st.phase = RUN;
|
|
||||||
}else return;
|
|
||||||
paintPlay();
|
paintPlay();
|
||||||
saveGame();
|
saveGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function togglePause(){
|
||||||
|
if(st.phase === RUN) pauseNow();
|
||||||
|
else resumeNow();
|
||||||
|
}
|
||||||
|
|
||||||
/* SVG elements aren't HTMLElements, so they have no .hidden property —
|
/* SVG elements aren't HTMLElements, so they have no .hidden property —
|
||||||
setting it only makes a JS expando. The attribute has to be set by hand. */
|
setting it only makes a JS expando. The attribute has to be set by hand. */
|
||||||
function showIcon(btn, name, on){
|
function showIcon(btn, name, on){
|
||||||
@@ -902,7 +912,7 @@
|
|||||||
if(many) node.removeAttribute("hidden"); else node.setAttribute("hidden", "");
|
if(many) node.removeAttribute("hidden"); else node.setAttribute("hidden", "");
|
||||||
}
|
}
|
||||||
show(btnScore);
|
show(btnScore);
|
||||||
el.forEach(function(e){ show(e.scoreLine); });
|
el.forEach(function(e){ e.scoreLines.forEach(show); });
|
||||||
}
|
}
|
||||||
|
|
||||||
function clock(ms){ // m:ss, for copy rather than the big readout
|
function clock(ms){ // m:ss, for copy rather than the big readout
|
||||||
@@ -1003,12 +1013,23 @@
|
|||||||
document.getElementById("done").addEventListener("click", closeSheet);
|
document.getElementById("done").addEventListener("click", closeSheet);
|
||||||
// tapping outside the score sheet discards the staged edits rather than
|
// tapping outside the score sheet discards the staged edits rather than
|
||||||
// committing them — a stray tap must never restart a game
|
// committing them — a stray tap must never restart a game
|
||||||
scrim.addEventListener("click", closeSheet);
|
scrim.addEventListener("click", function(){
|
||||||
|
if(openNode === scoreSheet) closeScore(); else closeSheet();
|
||||||
|
});
|
||||||
|
|
||||||
/* ============ score sheet ============ */
|
/* ============ score sheet ============ */
|
||||||
// Edits are staged here and only reach `st` on Done, so opening the sheet,
|
// Edits are staged here and only reach `st` on Done, so opening the sheet,
|
||||||
// fiddling and tapping away changes nothing.
|
// fiddling and tapping away changes nothing.
|
||||||
var edit = null, before = null;
|
var edit = null, before = null, resumeOnClose = false;
|
||||||
|
|
||||||
|
// leaving the sheet with the turn still live carries straight on. After a score
|
||||||
|
// change there is no turn to resume — newGame() has already moved us to IDLE,
|
||||||
|
// which the PAUSE check below quietly skips.
|
||||||
|
function closeScore(){
|
||||||
|
if(resumeOnClose) resumeNow();
|
||||||
|
resumeOnClose = false;
|
||||||
|
closeSheet();
|
||||||
|
}
|
||||||
|
|
||||||
function paintScore(){
|
function paintScore(){
|
||||||
document.getElementById("v-you").textContent = edit.score[1];
|
document.getElementById("v-you").textContent = edit.score[1];
|
||||||
@@ -1021,6 +1042,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openScore(){
|
function openScore(){
|
||||||
|
// only the sheet's own pause gets undone on the way out; a pause the players
|
||||||
|
// set themselves survives
|
||||||
|
resumeOnClose = (st.phase === RUN);
|
||||||
pauseNow();
|
pauseNow();
|
||||||
disarm();
|
disarm();
|
||||||
// seed from what the panels are showing, not from the banked reserve — for
|
// seed from what the panels are showing, not from the banked reserve — for
|
||||||
@@ -1074,7 +1098,7 @@
|
|||||||
document.getElementById("score").addEventListener("click", openScore);
|
document.getElementById("score").addEventListener("click", openScore);
|
||||||
document.getElementById("score-done").addEventListener("click", function(){
|
document.getElementById("score-done").addEventListener("click", function(){
|
||||||
applyScore();
|
applyScore();
|
||||||
closeSheet();
|
closeScore();
|
||||||
});
|
});
|
||||||
|
|
||||||
btnReset.addEventListener("click", armReset);
|
btnReset.addEventListener("click", armReset);
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
/* Cache-first: once installed the clock never touches the network again. */
|
/* Cache-first: once installed the clock never touches the network again. */
|
||||||
const CACHE = "bgclock-v5";
|
const CACHE = "bgclock-v6";
|
||||||
const FILES = [
|
const FILES = [
|
||||||
"./",
|
"./",
|
||||||
"./index.html",
|
"./index.html",
|
||||||
|
|||||||
Reference in New Issue
Block a user