Commit Graph
160 Commits
Author SHA1 Message Date
Claude 3b0b307997 Katan: no roads past an opponent's settlement, piece limits, stable layout
CI / Build and check (pull_request) Successful in 7m22s
CI / Deploy to Cloudflare Pages (pull_request) Skipped
Rules
- A road may only start from a crossing where the player has a building, or
  where one of their roads ends AND no opponent's settlement or city stands.
  roadConnects ignored opponents' buildings, so a road (the computer's included)
  could be laid straight past a settlement. Also applies to Road Building.
- Enforce the piece supply: 15 roads, 5 settlements, 4 cities. It was
  unlimited.
- Tests: building past a settlement and a city, free roads, own settlement not
  blocking, and running out of each piece.

UI
- The layout rescaled the board whenever it became the player's turn, because
  the action buttons only existed then. Everything outside the board now has a
  fixed height: the action bar always shows every button (disabled, not absent),
  the prompt is pinned to two lines, player cards always draw every line, and
  the dice total is always present. Rule recorded in docs/UI-catan.md.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-15 13:55:03 +00:00
devadminandClaude Sonnet 5.5 87b00ad84f Merge pull request 'Katan: playable screen, setup options, dev-preview gate' (#3) from feature/catan-ui into dev
CI / Build and check (push) Successful in 19m17s
CI / Deploy to Cloudflare Pages (push) Successful in 31s
Katan: playable screen, setup options, dev-preview gate

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 20:52:24 -06:00
Claude 30dcf026bd Katan: playable screen, setup options, dev-preview catalogue gate
CI / Build and check (pull_request) Successful in 20m42s
CI / Deploy to Cloudflare Pages (pull_request) Skipped
Adds the Katan screen on top of the WALA-119 engine, built in separable layers
so the look, wording, layout and interaction can each be changed alone:

- CatanStyle: every colour and proportion (light/dark), via LocalCatanStyle
- CatanText: every word on screen (pure functions of state)
- CatanGeometry: lattice -> pixels and tap hit-testing (pure, unit-tested)
- CatanInteraction: what is tappable and what a tap means, derived only from the
  engine's legal-move list (pure, unit-tested)
- CatanBoardView / CatanPanels / CatanDialogs: stateless composables
- CatanScreen: wiring to MatchController, portrait and landscape layouts
- CatanPreviews: Android Studio previews of real positions
- docs/UI-catan.md: how to adapt each layer, rules of the road, known gaps

Wiring: PlayScreen branch, GameSetupScreen options (3-4 players, game to
5/8/10/12), and a GameDescriptor.devPreview flag so dev builds can open Katan
while production still hides it until two-device play is verified.

Tests: CatanGeometryTest, CatanInteractionTest, CatanTextTest, CatalogTest.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 22:24:45 +00:00
devadmin a778e4732f Merge pull request 'Merge feature/catan-engine into dev (Katan rules engine + AI, WALA-119)' (#2) from catan-into-dev into dev
CI / Build and check (push) Successful in 15m38s
CI / Deploy to Cloudflare Pages (push) Failing after 13s
Reviewed-on: #2
2026-10-14 15:57:04 -06:00
Claude fb59372802 Restore the Katan catalogue entry the engine depends on
CI / Build and check (pull_request) Successful in 13m46s
CI / Deploy to Cloudflare Pages (pull_request) Skipped
The Option 2 migration dropped Gitea's 5c797c8, which added GameCatalog.CATAN
and a Katan entry (available = false: Coming soon on dev, hidden from
production). CatanRules.kt references GameCatalog.CATAN, so the merge did not
compile without it. Taken verbatim from feature/catan-engine, with the
matching CatalogTest assertions.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 20:55:38 +00:00
Claude 653e7a17a3 Merge feature/catan-engine into dev
Brings in the Katan rules engine, board, model and AI (WALA-119), its 51 unit
tests and docs/RULES-catan.md. Only conflict was docs/README.md, which the
GitHub-lineage migration had dropped: kept the engine branch's index and
pointed the CI link (docs/CI.md no longer exists) at the root README.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 20:50:28 +00:00
rootandClaude Sonnet 5.5 33a253d80c CI: keep JVM crash logs and try C1-only JIT on the runner
CI / Build and check (push) Successful in 13m29s
CI / Deploy to Cloudflare Pages (push) Successful in 45s
Runs on the Gitea runner crash intermittently on three JDK vendors. Upload
hs_err/replay logs on failure so the failing frame survives the job
container, and drop C2 (TieredStopAtLevel=1) for the Gradle and Kotlin
daemons to test whether the optimising JIT is the trigger.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YGRgChhwbwWr5yYrHTd9pL
2026-10-14 14:34:10 -06:00
Claude 4ffe817d5f Stage script: use node instead of jq
CI / Build and check (push) Failing after 8m40s
CI / Deploy to Cloudflare Pages (push) Skipped
The Gitea runner's container image has node but no jq, so the first dev deploy
died with exit 127 in stage-pages-site.sh. Same behaviour, tested against mock
servers for: other channel published, 200-with-HTML, 404, and unreachable.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 20:13:21 +00:00
Claude fd352b4d14 Back to Temurin JDK 17 now that api.adoptium.net is reachable from the runner
CI / Build and check (push) Successful in 17m2s
CI / Deploy to Cloudflare Pages (push) Failing after 6s
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 19:43:13 +00:00
Claude 7b5b041c42 Test: wait for the guest's sheet before comparing it
CI / Build and check (push) Failing after 7m34s
CI / Deploy to Cloudflare Pages (push) Skipped
The sheet arrives over the socket just after connected flips, so on a slower
runner the comparison raced it (ScorekeeperSessionTest, line 167). Same
pattern the sibling test already uses.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 17:23:42 +00:00
Claude e63c2cf7ac Use Zulu JDK 17: the Gitea runner cannot reach api.adoptium.net
setup-java's temurin distribution resolves versions through api.adoptium.net,
which fails TLS from the runner (EPROTO) while api.azul.com is reachable.
Any JDK 17 builds the project.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 17:02:54 +00:00
Claude 2314f5f842 Temporary: probe JDK hosts 2026-10-14 17:01:57 +00:00
Claude 2a193f8d5b Temporary: runner egress probe (to be removed) 2026-10-14 17:01:05 +00:00
Claude ad30ab9dea Move CI to Gitea: run-number offset, Cloudflare-only publishing
- plgVersionRunOffset=1000: stamped build number is run number + offset, so
  Gitea's restarted run counter cannot produce a versionCode below an
  installed one (GitHub reached prod 30, dev ~172). Tags and versionName use
  the same number.
- Drop the GitHub release steps; plg.wallacenet.ca is the only channel.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 17:00:01 +00:00
Claude 4c603f6d3d Adopt the GitHub lineage; supersede Gitea-only bootstrap commits
Keeps Gitea's history reachable (also preserved as gitea-pre-github-main)
while the tree stays the GitHub one: package org.prolibertate.games, OTA
from plg.wallacenet.ca. Gitea's package rename and Gitea-releases updater
are intentionally not carried over.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 16:59:38 +00:00
adrianoftyriel 0927660c15 Merge pull request #43 from adrianoftyriel/dev
Dev
2026-10-14 10:42:42 -06:00
adrianoftyriel 5d0f95361f Merge pull request #42 from adrianoftyriel/cf-real-404
Fix Pages carry-over: real 404 and tolerate 200-with-HTML
2026-10-14 10:18:01 -06:00
Claude 6fbe63f787 Fix carry-over on Pages: add 404.html and tolerate 200-with-HTML
Pages serves index.html with 200 for unknown paths when there is no
404.html, so a never-published channel looked published and jq failed on
the HTML. Add a real 404 page and treat a 200 without a latest.json as
absent.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 16:08:28 +00:00
adrianoftyriel 12d20565bd Merge pull request #41 from adrianoftyriel/cf-treat-1014-as-absent
Treat Cloudflare 1014 as 'channel not published yet'
2026-10-14 10:02:26 -06:00
Claude 2df140a3c1 Treat Cloudflare error 1014 as 'channel not published yet'
A push-triggered first deploy cannot set the first_deploy input, and the
domain answers 403/1014 until the Pages project exists. 1014 means nothing
exists to preserve, so handle it like a 404. Other 403s still abort.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 15:53:15 +00:00
adrianoftyriel 0b74b1b2b1 Merge pull request #40 from adrianoftyriel/cf-first-deploy-fix
Fix first Cloudflare deploy: add first_deploy dispatch input
2026-10-14 09:39:58 -06:00
Claude 4369d15cec Add first_deploy dispatch input so the first Cloudflare deploy can bootstrap
The domain answers 403 (error 1014) until the Pages project exists, which
made the carry-over fetch abort the very deploy that would create it.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 15:39:03 +00:00
adrianoftyriel 1e9908569d Merge pull request #39 from adrianoftyriel/cloudflare-pages-deploy
Publish dev and prod builds to plg.wallacenet.ca (Cloudflare Pages)
2026-10-14 09:32:31 -06:00
Claude c0fc4fe7d6 Publish dev and prod builds to plg.wallacenet.ca (Cloudflare Pages)
Both workflows gain a deploy-pages job that stages their channel into one
Pages site, carrying the other channel over from the live site. The in-app
updater now reads /prod|/dev/latest.json from plg.wallacenet.ca instead of
the GitHub API. GitHub releases are still published for one transition
deployment so existing installs can fetch the new updater.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdMNbY5hLR7FjtfEE712xG
2026-10-14 15:20:47 +00:00
adrianoftyrielandPaperclip 3a930f5423 Katan rules engine + AI (WALA-119)
CI / Build and check (pull_request) Failing after 8s
The island game as a pure-Kotlin state machine under game/catan/, wired to
the upstream GameRules / GameAi contract and fully unit-tested on the JVM.

- game/catan/: CatanModel (state, moves, phases), CatanBoard (19-hex
  geometry, seeded island generation with the 6/8 separation rule, shared
  vertex/edge graph), CatanRules (setup, production, the seven and the
  robber, domestic and maritime trade, building, development cards,
  Longest Road, Largest Army, win detection), CatanAi (a heuristic opponent
  that reads only public information).
- GameRules.isLegalMove: a defaulted structural legality check so a game
  with a combinatorial move space (Catan's trade offers) is gated the same
  way as one with an enumerated move list; MatchController uses it.
- GameCatalog.CATAN: the catalogue entry, listed but not yet available
  (descriptor and screen land with the screen task).
- Tests: 51 across CatanBoardTest, CatanRulesTest and CatanAiTest —
  geometry invariants, generation balance, setup, production, the
  seven/robber, both trade types, development-card timing, Longest Road
  edge cases, Largest Army, win detection, and AI games played to a result.
- docs/RULES-catan.md, in the per-game style, with the rule choices and
  the known gaps called out.

Two real engine bugs found and fixed while getting the suite green:
VERTEX_EDGES counted each shared edge once per adjacent hex, so an
interior vertex looked like it had six edges (which also double-counted
segments in the Longest Road search and duplicated setup moves); and the
applyRoll seven-transition was a discarded expression, so a seven never
left AWAIT_ROLL. Three test fixtures rolled from the PLAYING phase and two
assumed a two-seat rotation; corrected to the phases and seat order the
engine actually uses.

Verified: ./gradlew :app:testDebugUnitTest --offline — 679 tests, 0 failures.

Co-Authored-By: Paperclip <[email protected]>
2026-10-14 15:13:36 +00:00
adrianoftyrielandPaperclip 4d7cd6182f Rename the installed Android package to ca.wallacenet.prolibertategames
Build and Release APK / Build APK and publish release (push) Failing after 7s
Follows the Gitea repo rename: the applicationId (and its .dev variant)
now matches the repo, the launcher name and the docs, instead of carrying
the abandoned Pilgrims of Katan identity.

The JVM namespace stays org.prolibertate.games, so no source file moves;
only the installed identity changes.

Co-Authored-By: Paperclip <[email protected]>
v1.4.1002-dev
2026-10-14 14:47:13 +00:00
adrianoftyrielandPaperclip b58b4251dc Rename the Gitea repo references to ProLibertateGames
Build and Release APK / Build APK and publish release (push) Canceled after 0s
The repo was renamed from PilgrimsOfKatan to ProLibertateGames on the
Gitea instance. Update every reference so nothing points at the old name:

- OTA updater: the repo constant and the User-Agent
- publish-release.sh: the default repo, the asset name, the release title
- CI/release workflows: the artifact names and the staged APK name
- settings.gradle.kts: the Gradle project name
- Protocol.kt / LanTransport.kt: the mDNS service label and the Wi-Fi lock tag
- docs (README, CLAUDE.md, docs/CI.md) and the update-policy test fixtures

The installed applicationId is left unchanged in this commit.

Co-Authored-By: Paperclip <[email protected]>
2026-10-14 14:21:43 +00:00
adrianoftyrielandPaperclip 5c797c8807 Restore the Pro Libertate Games title and splash; list Katan under Board Games
Build and Release APK / Build APK and publish release (push) Canceled after 0s
The bootstrap rebranded the app to Pilgrims of Katan (WALA-118 review);
the decision is to keep the upstream's own identity instead.

- Title and splash: the upstream originals restored exactly — the Wallace
  tartan splash under the "Pro Libertate Games" uncial wordmark
  (SplashScreen.kt, Tartan.kt), the tartan launcher icon, and the
  Theme.ProLibertateGames window theme. The game screens go back to the
  upstream WallaceGold/WallaceRed accents the tartan is woven from.
- Names and colours: launcher names back to "Pro Libertate Games" /
  "Pro Libertate Games DEV"; the channel colours back to the clan red /
  hunting green setts (resValue tartan_field and friends). The installed
  identity stays ca.wallacenet.pilgrimsofkatan (+ .dev) — that is the
  package, not the brand.
- Catalogue: Katan is listed under Board Games (3-4 players, original
  blurb, no third-party marks) with available = false, so dev builds show
  it as "Coming soon" and a production build hides it until its engine
  and screen land.
- Docs: README/CLAUDE.md/docs/CI.md updated to the restored identity;
  CatalogTest's invariant now names Katan as the one deliberate exception
  rather than asserting nothing is unfinished.

Structural bootstrap untouched: applicationId, OTA repointed at the Gitea
releases, CI workflows, version stamping.

Verified: ./gradlew assembleDebug and testDebugUnitTest green — 628 tests,
0 failures.

Co-Authored-By: Paperclip <[email protected]>
2026-10-14 02:15:30 +00:00
adrianoftyrielandPaperclip 05c443a4b5 Publish: verify the release's stored state after attaching the APK
Build and Release APK / Build APK and publish release (push) Canceled after 0s
A live publish on git.wallacenet.ca kept coming back invisible to the updater:
the create accepted "draft": false, and the release was still stored as a
draft once the APK was attached — so the anonymous list the app reads came
back empty even though every publish step reported success.

The script now attaches first, then reads the release back and PATCHes the
state if it did not stick, failing loudly if the correction is refused. A
draft is not a channel a preview build can silently become.

Also lists all 22 catalogue games in the README's table; seven were missing,
which read as "the app has 15 games".

Verified against the live instance: anonymous list shows
v1.4.1001-dev as a published prerelease with a tag-named asset, the
production /releases/latest correctly returns 404 while only a prerelease
exists, and an anonymous download through the URL the app builds matches the
built APK byte for byte.

Co-Authored-By: Paperclip <[email protected]>
2026-10-13 22:27:33 +00:00
adrianoftyrielandPaperclip a827a38ae6 OTA: name release assets after their tag, download from one host
Build and Release APK / Build APK and publish release (push) Canceled after 0s
Two things the first live release check on git.wallacenet.ca showed:

- The publish script uploaded the APK under whatever the build called it
  (app-debug.apk), which is the name the updater only finds by its fallback
  rule. The script now names the asset after its tag, so the primary rule —
  "the asset whose name contains the tag" — is the one that fires.
- The instance answers for two host names and reports asset URLs on the one
  it treats as canonical, so the app would have fetched its release list from
  git.wallacenet.ca and its APK from elsewhere. onReportedHost() now keeps the
  path the server chose and puts the download on the host the app already
  trusts, tested in UpdatePolicyTest.

Co-Authored-By: Paperclip <[email protected]>
v1.4.1001-dev
2026-10-13 22:13:09 +00:00
adrianoftyrielandPaperclip fad4778ad8 Bootstrap Pilgrims of Katan: rebrand, Gitea CI and OTA
Build and Release APK / Build APK and publish release (push) Canceled after 0s
Import of ProLibertateGames (upstream main, GPL-3.0) rebranded for
Wallacenet and repointed at git.wallacenet.ca.

- Identity: applicationId ca.wallacenet.pilgrimsofkatan (+ .dev for the dev
  channel), launcher names "Pilgrims of Katan" / "Pilgrims of Katan DEV",
  neutral hexagon branding replacing the Wallace tartan. Uncial Antiqua keeps
  its OFL licence, and the GPL-3.0 LICENSE plus upstream attribution stay.
- OTA: Updater.kt now reads this repo's own Gitea releases
  (/api/v1/repos/devadmin/PilgrimsOfKatan/releases...) and BuildConfig.
  OTA_ENABLED is on; Gitea answers the same JSON shape the code already parsed.
- Build numbers: versionCode = CI run number + plgVersionRunOffset (1000),
  with the tag and versionName derived from the same figure. The offset is
  permanent, because Gitea Actions restarts run numbers at 1 and a reset would
  otherwise make Android reject the next build as a downgrade.
- CI: ci.yml publishes a dev prerelease from dev, release.yml a production
  release from main, both through scripts/publish-release.sh (Gitea REST API,
  runnable by hand until an Actions runner is registered).
- Docs: docs/CI.md records the channel, numbering and runner decisions;
  docs/README.md seeds the per-game RULES-* conventions; CLAUDE.md carries the
  agent ground rules.

Verified: ./gradlew assembleDebug and testDebugUnitTest green on JDK 17 /
compileSdk 34, both variants stamped, installing side by side under the new
identity.

Co-Authored-By: Paperclip <[email protected]>
2026-10-13 22:03:27 +00:00
adrianoftyriel d27c2db894 Merge pull request #38 from adrianoftyriel/dev
Release: multi-device scorekeeper sharing and tally scoring
2026-10-12 22:04:30 -06:00
adrianoftyriel 4037f75136 Merge pull request #37 from adrianoftyriel/feature/tally-number-box
CI / Build and check (push) Failing after 1m40s
Tally scoring: inline number box instead of dialog
2026-10-12 21:24:56 -06:00
devadminandClaude Sonnet 5.5 9df9fcd556 Tally: replace the custom-amount dialog with an inline number box
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 03:24:33 +00:00
adrianoftyriel 4ae16c7ca4 Merge pull request #36 from adrianoftyriel/feature/tally-scoring
Scorekeeper: round-based or tally scoring
2026-10-12 17:15:56 -06:00
devadminandClaude Sonnet 5.5 5c0a44c11d Scorekeeper: choose round-based or tally scoring when starting a game
Tally sheets let any player score at any moment (+1, -1 or a custom amount)
with a log of entries that can be taken back. Entries are stored as one-player
rows so totals, undo and sharing work unchanged.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-12 21:43:37 +00:00
adrianoftyriel 6030e62bbe Merge pull request #35 from adrianoftyriel/fix/scorekeeper-join-diagnostics
Scorekeeper sharing: join from any sheet, show waiting requests, diagnostics
2026-10-09 21:29:09 -06:00
James WallaceandClaude Sonnet 5.5 d9fdb598ac Scorekeeper sharing: join from any sheet, show waiting requests, diagnostics
A guest was timing out with nothing ever shown on the host. Without a
device to look at, this makes the next test say where it stops, and fixes
what was found on the way.

- The host's Sharing panel shows links received and join requests read, which
  tells "nobody reached this phone" from "reached it and not understood".
- The guest says when its link is up and it is waiting for an answer.
- A request waiting shows on the host's top bar ("Sharing . 1 waiting"), and
  the Sharing panel steps aside while one is up so two dialogs never stack.
- Join is a top-bar action on a sheet already under way; it was only offered
  on an empty one.
- LanTransport: a host being cleaned up no longer unregisters the
  advertisement or clears the address of a newer host started in its place.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CBG7URddsUZjQ6KE1Kqf6G
2026-10-10 02:56:36 +00:00
adrianoftyriel 3e277754e9 Merge pull request #34 from adrianoftyriel/feature/multidevice-scorekeeper
Scorekeeper: share a sheet between devices
2026-10-09 19:55:50 -06:00
James WallaceandClaude Sonnet 5.5 89b19a894a Scorekeeper: share a sheet between devices
A host can share its score sheet and others nearby can join it, found the
same way games are, or by typed address over a hotspot.

- A joining guest is not let in on its own say-so. The host is prompted with
  the guest's name and answers Allow or Deny. A device already let in that
  drops out (locked phone) is let straight back; the host can remove anyone.
- The host's sheet is authoritative. What travels is what was done (SheetOp:
  add round, rename, move column, ...), not whole sheets, so changes made at
  the same moment all land. A guest's own changes show at once and are laid
  over the host's sheet until confirmed (SheetReplica).
- Sync asks the host for the sheet as it stands, and runs on its own when
  the app returns to the front, as it does for a game table.
- Hello carries a purpose and hosts advertise one, so a game join screen does
  not list score sheets and a game guest typing a sheet's address is told so.
- The Scorekeeper screen now expresses every edit as a SheetOp, so one grid
  serves a local sheet, a hosted one and a joined one.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CBG7URddsUZjQ6KE1Kqf6G
2026-10-10 01:27:40 +00:00
adrianoftyriel e6ce1aebd5 Merge pull request #33 from adrianoftyriel/dev
Release 1.4
2026-10-09 14:13:52 -06:00
adrianoftyriel 16f9321ca3 Merge pull request #32 from adrianoftyriel/fix/ci-setup-android-v4
CI: bump setup-android to v4
2026-10-09 14:02:08 -06:00
James WallaceandClaude Sonnet 5.5 c89c8d15c7 CI: bump setup-android to v4
v3 runs sdkmanager with the tools package, which Google's repository no
longer has, so the Set up Android SDK step failed before any build.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CBG7URddsUZjQ6KE1Kqf6G
2026-10-09 19:34:12 +00:00
James Wallace f72405f28f Merge branch 'claude/card-game-physics-animations-5b6bfi' into dev
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

# Conflicts:
#	app/src/main/java/org/prolibertate/games/net/LanTransport.kt
2026-10-09 15:47:47 +00:00
James WallaceandClaude Sonnet 5.5 45bc16594e Bump version series to 1.4
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-02 21:23:38 +00:00
James Wallace 6913ee2fe9 Merge branch 'fix/scorekeeper-zero-and-cell-edit' into dev 2026-10-02 21:19:59 +00:00
James WallaceandClaude Sonnet 5.5 b98d464954 Scorekeeper: show an entered 0, tap a past score to edit it
Zeros are now stored so a typed 0 shows as 0 while a blank cell stays blank.
Tapping a score in an earlier round opens that round for correction with the
cursor in the tapped cell, instead of needing the row number first.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-02 21:19:58 +00:00
adrianoftyriel 26ae3fecaa Merge pull request #31: get back into a game a locked phone dropped out of
Get back into a game a locked phone dropped out of
2026-08-17 09:45:40 -06:00
Claude 5d1d2e204e Merge remote-tracking branch 'origin/dev' into claude/locked-phone-turn-issue-x9pvbv 2026-08-17 15:33:41 +00:00
Claude f0c8895747 Get back into a game a locked phone dropped out of
A phone locked when its turn came round could not play once it was
unlocked. The game was never wrong — the host still had the position and
the seat was still the player's — but the only route to it was gone, and
nothing rebuilt it. The board came back with the state it had before the
turn arrived, and every tap on it went nowhere.

A locked phone puts its Wi-Fi radio into power save, and a link carrying
nothing between one turn and the next is exactly what that drops. Four
things were missing, and all four are needed:

The radio is held awake for as long as there is a game on, which stops
most of these drops happening at all. FULL_HIGH_PERF rather than the
LOW_LATENCY its deprecation notice points at: that one only applies with
the screen on and the app in front, which is the case that worked.

A link with nothing on it is pinged, and answered. TCP does not report a
link that died this way — the read never returns and the next write goes
into a socket that leads nowhere — so without traffic of its own the app
could not tell a player who was thinking from one who was gone. Both ends
have to speak it or a quiet link would be dropped as dead, hence the
protocol bump; the lobby already turns a mismatch away with "update both
devices".

A write that fails now reports the link as broken instead of closing it as
though this end had meant to. That silence was the reason a dead board
answered nothing and said nothing about why.

And a guest that loses its link dials the host again — which is still
listening, on the same port, for the whole match — and is put back in the
seat it never actually left, by the device id it is already known by. The
table then resyncs, which is what fills the screen back in. Coming back
to the front asks for the same thing, since a client draws nothing but
what it was sent and anything pushed while the link was down was pushed
at nothing.

Sends are serialised while I am here: a message is a line, a write is
three calls, and the heartbeat writes on its own schedule.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01GarwvAPvWHELNspro4foFY
2026-08-16 00:28:05 +00:00