VERSION HISTORY
v3.3.16
Bugfix — rights issue could push a company past the 200,000 share limit (available went negative): all three paths (AI play, human max calculation, human execution) now cap the rights purchase at shares actually available. Also: the end-of-game score submission is now much more prominent (full-width gold panel) and leaving the game-over screen via New Game or Rematch without submitting now asks for confirmation.
v3.3.15
Investor Rating: removed the per-round bonus (was 100 pts/round). Playing more rounds already raises wealth through compounding, so the separate round bonus double-counted and unfairly favoured long games over efficient short ones. Score is now (wealth/1000 + humanOpponents×500) × (1 + efficiency/100) − rights penalty. Rounds played is still recorded and shown on the leaderboard, just no longer scored.
v3.3.14
Windfall tax escalation to restore late-game jeopardy: when 5+ companies have a Chairman (any player holding 100,000+ shares), the per-round windfall chance rises from 8% to 90% until one fires. After firing there is a one-round breather at 0%, then two rounds at 8%, before escalation can re-apply. The £5 / 150,000-share qualifying thresholds are unchanged (a triggered windfall with no qualifying holders still does nothing — which is why it can appear not to fire). Verified base 8% behaviour is unchanged when the board is not concentrated.
v3.3.13
Bugfix — AI Debenture paid out £0 on bankrupt companies: it used co.basePrice, which is overwritten to the current price each round (so once a company hit £0, basePrice was £0). Now uses co.originalPrice (the fixed IPO price), matching the human debenture path. Also set originalPrice explicitly in the company setup path so it is always present.
v3.3.12
Activity log fixes: (1) renderLog was only ever displaying the last 20 entries and addLog was trimming retained history to 50, so round markers and earlier activity scrolled out of reach — now the full retained history (up to 200 entries) is shown and scrollable; (2) this also fixes the "Round N begins" marker appearing to be missing — with the more active AI generating many entries per round it had simply been pushed beyond the visible 20.
v3.3.11
Bugfix — AI Share Suspend was being logged (and applied) multiple times: handleSuspendPhaseUI could be called repeatedly by state syncs/re-renders, each queuing a 1500ms timer that acted and logged. Added a re-entry guard (_aiSuspendPending) so only one AI suspend action is scheduled at a time, with a re-check that the holder is still pending before acting. Guard reset at the start of each suspend phase.
v3.3.10
Activity log now persists across rounds instead of being wiped at the start of each new round, so players can scroll back through the whole game's history. Each round is still clearly marked with a "Round N begins" separator. Log is trimmed to the most recent 200 entries to keep its size bounded.
v3.3.9
Hard & Medium AI now take profits and reallocate: when holding a large position that has risen 25%+ above its round-start price, with own cards no longer favouring it and a cheap company (≤£1.50) available to rotate into, the AI sells down to the next shareholding status floor — Chairman (100k) or Director (50k) or zero — never carelessly selling through a level and losing the power to discard opponents' negative cards. Card-aware so it won't dump a position its cards still favour. Validated with a 12-case automated test harness.
v3.3.8
AI made far more competitive: removed the restrictive per-trade share caps (was 15,000 hard / 10,000 medium) that left the AI sitting on idle cash. Hard AI now invests up to 95% of its cash per buy, Medium up to 80%, limited only by shares available. This lets the AI deploy capital and compound at a rate closer to an aggressive human player. Easy AI unchanged. Validated with an automated test harness confirming no overspend and correct 500-block sizing.
v3.3.7
Hard AI now plays Share Suspend cards strategically: (1) suspends the first company (board order) it holds shares in that fell that round — reverting the price up to recover value; (2) failing that, suspends the first company it holds no shares in that another player holds and that rose — reverting the price down to hurt the opponent; (3) otherwise skips. Respects event-blocked companies. Medium and Easy AI continue to skip. Validated with a 10-case automated test harness.
v3.3.6
AI improvements: (1) all difficulties never sell a holding priced below £1 — better to risk bankruptcy and recover via a future debenture; (2) AI never plays a rights issue on a company priced below £2; (3) Hard AI now plays aggressively — if its cards would raise a company's price by 50%+ of current value it buys the maximum it can afford in that company (highest priority). Changes isolated to AI decision logic; validated with an automated test harness.
v3.3.5
Bugfix — Investor Rating efficiency always showed 0%: newRound was resetting gameStats every round, wiping the accumulated roundStarts snapshots needed for the theoretical-max calculation, leaving only the final round's data. roundStarts is now preserved across the per-round gameStats reset so efficiency is calculated from every round.
v3.3.4
Investor Rating now tracks Rights Issue cards dealt across the whole game (counted whether or not played, as a luck factor) and deducts 250 points per card from the final score, floored at zero. New "RI" column on the leaderboard shows the count. Rules and submit-modal breakdown updated.
v3.3.3
Added a Date column to the Investor Rating leaderboard showing when each score was achieved (e.g. 4 Jun 26).
v3.3.2
Round summary now shows the logged-in player's running Investor Rating at the top — current score, trading efficiency %, rounds played and wealth — so players can track their rating as the game progresses. Shown to human players only.
v3.3.1
Added a Leaderboard (🏆) button to the in-game board header, alongside Settings and Rules, so the global Investor Rating leaderboard can be viewed mid-game.
v3.3.0
New feature — Investor Rating & global leaderboards: opt-in score submission after game over. Score = (wealth/1000 + rounds×100 + humanOpponents×500) × (1 + efficiency/100), where efficiency compares actual profit to a theoretical-max "perfect play" model (liquidate at round-start prices, buy best 1-2 gaining companies capped at available shares). Four separate public leaderboards by human opponent count (solo/1/2/3+). Only player name and game stats stored — no personal data. Leaderboard viewable from lobby and game-over screen.
v3.2.16
Windfall tax shares sold in blocks of 500: when shares must be sold to cover a levy shortfall they are now rounded up to the nearest 500 (matching the standard trading rule); any surplus proceeds are returned to the player as cash.
v3.2.15
Bugfix — state divergence and game freeze when windfall tax fires: acknowledgeWindfall was calling pushState, racing with other devices doing the same and corrupting game state; windfall modifications are already included in newRound's pushState so acknowledgeWindfall now just closes the modal and re-renders. Also added windfall modal to finally-block AI blocking check.
v3.2.14
New feature — Windfall Tax: 8% chance per round from round 5 onwards (never same round as market crash); any player holding 150,000+ shares in a company priced £5+ pays a 15% levy; cash first then shares sold to cover shortfall; sold shares return to pool; dramatic modal with rotating crisis headlines shown to all players; mutual exclusion with market crash; AI players fully affected.
v3.2.13
Bugfix — company prices reset to pre-card values after round end: co_prices (written to Firebase when a company event fires mid-round) was being re-applied by the listener during summary/leaderboard phases, overwriting the correct post-card prices from the full G serialization; co_prices application now guarded to phase=playing only.
v3.2.12
Bugfix — company event modal missing on active player's device: _isMyTurn guard correctly skipped G replacement but did not sync pendingEvents, so the modal never showed when an event fired during that player's turn. pendingEvents now synced inside the guard with modal trigger, matching the existing pendingBoardroomNews pattern.
v3.2.11
Bugfix — round summary ack deadlock in 2-player games: full pushState in acknowledgeSummary overwrote other players' acks; fixed by atomic Firebase write per player. Host listener checks all-acked on every incoming ack. 60-second host timeout added as final safeguard. Bugfix — Share Suspend showing stale prices on non-host device: co.price holds pre-card value; dropdown and playSuspendCard now use roundSummary.after as authoritative post-card price.
v3.2.10
Stable release — 17 rounds confirmed, multiple company events, market crash, share suspend, no freezes. Fixes: AI start-of-round delay (newRound now explicitly triggers AI after clearing _newRoundInProgress). Diagnostic logging removed.
v3.2.9
Bugfix — newRound increments G.round then Firebase listener fires during setup and replaces G with old state resetting round back; _newRoundInProgress flag now blocks all listener G-replacement during newRound execution
v3.2.8
Bugfix — game freezes after round with company event: stale pendingEvent with acked:{0:true} persisted into new round; _isMyTurn guard blocked newRound pushState when human goes first; fixed by adding sequenceId nonce to G (changes each newRound) so guard can distinguish new-round push from same-turn echo; also resets _lastEventCheckedSeq in newRound
v3.2.7
Bugfix — AI freezes after company event: event modal has 400ms delay so was not yet visible when AI trigger ran; all trigger points now check pendingEvents and pendingBoardroomNews directly, not just modal CSS; acknowledgeCompanyEvent now re-triggers AI after last event is dismissed
v3.2.6
AI turn system rewritten with try/finally mutex: _aiTurnInProgress held for entire execution, released in finally block which also explicitly triggers the next AI turn if needed; eliminates all double-fire and missed-trigger race conditions
v3.2.5
Bugfix — AI double-fire causing skipped turns: Firebase listener could re-trigger runAITurn while aiFinishTurn was still awaiting confirmTurn, advancing seqIndex twice; _aiTurnInProgress now stays true until confirmTurn fully completes, and all AI trigger points check it before firing
v3.2.4
Bugfix — human timer killed by stale AI turn: runAITurn called stopTimer() before checking if it was still an AI's turn; if the turn advanced to a human in the 1.5s AI think delay the human's timer was silently killed; stopTimer now only called after re-verifying it's still an AI turn
v3.2.3
Bugfix — timer freeze on human turn: if a Firebase update nulled G.loggedInPlayer just as the countdown hit zero, confirmTurn's guard silently returned and the game froze; passTrade and both timer expiry paths now restore loggedInPlayer from _myPlayerIdx before checking
v3.2.2
AI watchdog — host device polls every 8 seconds; if it's an AI's turn and no modal is blocking and the turn hasn't advanced, runAITurn is re-fired automatically; watchdog starts with game, restarts each round, stops at game over
v3.2.1
Bugfix — AI skips turn after market crash: crash modal was showing while pushState immediately triggered the AI; AI trigger now suppressed while crash/boardroom/event modals are open, and re-checked when crash modal is dismissed
v3.2.0
Host recovery — if host is logged out while waiting for players, they can rejoin the room using their name and the code to reclaim host status and start the game; any player can also claim host if the original host is gone
v3.2
Bug fixes — boardroom/company event acknowledgements now use string keys (was re-showing up to 8x with multiple players); triggerNewRound made async (was returning players to previous round); rematch button added to final leaderboard and company stories; stale co_prices cleared on new round start; 12-player support with scaled card dealing (9 cards at 7 players down to 5 at 12); market crash now revives bankrupt companies to £0.25 with explanatory text; share suspend uses roundSummary.before price for reliable display
v3.1.5
Bugfix — Share Suspend showing zero price movement: Firebase converts roundSummary.prices array to string-keyed object on roundtrip, making prices[ci] return undefined; deserializer now restores prices, cash and discards sub-arrays
v3.1.4
Bugfix — freeze after leaderboard when player 1 is first in both rounds: isMyTurn guard now includes round-number check, preventing new-round state from being blocked when seqIndex resets to 0
v3.1.3
Round summary now per-player acknowledged — each human player must tap 'I've Seen This' before the leaderboard appears; waiting list shows who hasn't confirmed yet; AI players auto-ack
v3.1.2
Bugfix — ghost re-announcements fixed: active player's device now syncs pendingBoardroomNews and boardroomAnnounced from Firebase during their turn (isMyTurn guard was leaving stale acked items in local G, which got re-pushed to Firebase on confirmTurn)
v3.1.1
Bugfix — boardroom news stack overflow fixed: Firebase acked array corruption repaired in deserializer; re-entry guards added to acknowledgeBoardroomNews and acknowledgeCompanyEvent; showNextBoardroomNews now skips if modal already showing
v3.1
Bug fixes — loan card now saves to Firebase before confirmTurn (no more lost loans); all boardroom functions restored after silent file loss; timer setting locked during gameplay to prevent mid-game state wipe; active timer button now highlighted; sell minimum corrected to 500 shares; G.viewingPlayer guard removed from executeTrade (was silently blocking trades); originalPrice stored correctly for Debenture card; setTimer no longer pushes full game state (was wiping active player trades); pre-deploy audit added; Nelson Myatt URL corrected; confirmed stable reference build; Sell All button added; rights issue max now capped by cash; timer synced to all players on game start; 24h timer shows HH:MM format; Apply button always visible for rights issue/loan/debenture; company event price update fixed (now applied after deserialize); rules updated with correct buy minimum, debenture price and Sell All
v3.0
Round and game statistics — biggest trade, most active player, loans, interest, crashes; MAX button on share slider; version checking with refresh banner; rejoin in-progress game; AI improvements — social investing, smarter loan logic, faster repayment
v2.9
Company Events — 6 types (scandal, product launch, partnership, profit warning, strike, award); turns 1-2 only; max once per round; async acknowledgement; show/hide % setting; rules fully rewritten
v2.8
Boardroom News — Director (50k) and Chairman (100k) announcements with generated story; per-player acknowledgement; stale room auto-cleanup (7 days)
v2.7
Fireworks animation on final leaderboard — full-screen burst for 2 seconds then drops behind modal for remaining display
v2.6
Sound effects — buy (cash register), sell (coin drop), round end (bell), market crash (alarm siren), winner fanfare
v2.5
Share price history heat-map table — colour-coded cells showing price movement per round with net change per company
v2.4
Multiple AI players (up to 3) — Claude, HAL, Deep Blue; Easy/Medium/Hard difficulty; Hard mode tracks wealth race and Director/Chairman thresholds
v2.3
Market Crash event — random 20-40% price drop; max once per 10 rounds; dramatic modal shown to all players
v2.2
Claude AI player — three randomised strategies; uses all special cards; Director/Chairman aware; End Game final settlement with interest; company stories screen; final leaderboard with winner celebration
v2.1
BT replaced by ASEI; 24-hour async timer mode (Firebase timestamp-based); loan interest at 10% compound per round; voluntary loan repayment button (doesn't use turn); End Game button with final loan settlement and confirmation modal
v2.0
Stable multiplayer release — activity log real-time sync; turn passing reliable; currency cards persist to next round; Share Suspend modal fixed; Director/Chairman card discard rules; sponsor logos; splash screen & lobby layout improved
v1.9
Turn timer (5 min / off) with 1-min warning sound; price-change tip cards info-only; company logos reliable
v1.8
Holdings sync fix; log race condition resolved; loan card fix; duplicate HTML removed
v1.7
Share Suspend phase Firebase-driven with per-player UI; suspend card info-only during bargaining
v1.6
Log sync; cards sorted by company; loan card fixed; end-of-round price calc; Share Suspend phase; version log
v1.5
Card logos added; All Holdings stacked bar chart
v1.4
iPad/iPhone compatibility — replaced prompt() with in-page name inputs
v1.3
Activity log sync fix — real-time updates across all devices
v1.2
Card dealing fixed — one hand of 10 per player per round (156 card deck)
v1.1
Player name persistence across Firebase syncs
v1.0
Initial release — 6-player stock market game with Firebase multiplayer