games: the money moves
The table dealt cards but settled money by editing a number. So the felt got the two things it was missing: a bet spot in front of you, and the house's rack beside the shoe. Every chip is now always travelling between one of those and the other. You build a bet by throwing chips onto the spot — the chip you clicked is the chip that flies. The stake sits there through the hand. The house pays out of its rack into the spot, and the pile is then swept back to your stack. A loss goes to the rack and does not come back. Two rules hold it together. The number under the pile is a readout of the pile, never the other way round: the bet starts at nothing rather than at a default nobody put down, and a settled hand leaves your stake back up on the spot, because otherwise the panel prints "your bet: 300" over an empty circle. And the chip bar does not move until the chips that justify it have landed — a counter that pays you before the dealer turns over is a counter that has told you the ending. casino-fx.js is the engine underneath: chips fly on an arc, out of a fixed overlay so no container clips one crossing from a button to the felt. It knows nothing about blackjack. Also: cards land with weight and a degree or two of tilt, so a hand looks dealt rather than typeset; the dealer takes a beat before drawing out; and a natural gets confetti, which is the only thing in the room that does. Driven in a real browser, which is the only way to review an animation — and which is what caught the verdict pill rendering white on white in a dark room, a chip rack sitting on top of the dealer, and Hit being offered over a table that was still being paid out. devcasino_test.go is that harness, kept.
This commit is contained in:
71
internal/web/devcasino_test.go
Normal file
71
internal/web/devcasino_test.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestDevCasino is not a test. It is the casino, running, on a port, with one
|
||||
// signed-in player who has chips — so the table can be driven in a real browser,
|
||||
// which is the only honest way to review an animation.
|
||||
//
|
||||
// Skipped unless you ask for it:
|
||||
//
|
||||
// PETE_DEV_CASINO=:7788 go test ./internal/web -run TestDevCasino -timeout 0
|
||||
//
|
||||
// It prints the session cookie to plant. The routes are wired here rather than
|
||||
// taken from New(), because New() decides whether the casino exists at the
|
||||
// moment it builds the mux, and the test rig only signs the player in afterwards.
|
||||
func TestDevCasino(t *testing.T) {
|
||||
addr := os.Getenv("PETE_DEV_CASINO")
|
||||
if addr == "" {
|
||||
t.Skip("set PETE_DEV_CASINO=:port to run the casino for a browser")
|
||||
}
|
||||
|
||||
s := newCasino(t)
|
||||
fund(t, 5000)
|
||||
|
||||
payload, _ := json.Marshal(SessionUser{
|
||||
Sub: "sub-1", Username: "reala", Name: "Reala",
|
||||
Exp: time.Now().Add(24 * time.Hour).Unix(),
|
||||
})
|
||||
cookie := s.auth.sign(payload)
|
||||
|
||||
staticSub, err := fs.Sub(staticFS, "static")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticSub))))
|
||||
mux.HandleFunc("GET /games", s.handleLobby)
|
||||
mux.HandleFunc("GET /games/blackjack", s.handleBlackjack)
|
||||
mux.HandleFunc("GET /api/games/table", s.handleTable)
|
||||
mux.HandleFunc("POST /api/games/buyin", s.handleBuyIn)
|
||||
mux.HandleFunc("POST /api/games/cashout", s.handleCashOut)
|
||||
mux.HandleFunc("POST /api/games/blackjack/deal", s.handleDeal)
|
||||
mux.HandleFunc("POST /api/games/blackjack/move", s.handleMove)
|
||||
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Written to a file, not printed: `go test` buffers stdout, and the browser
|
||||
// driver needs the cookie while the server is still running.
|
||||
if out := os.Getenv("PETE_DEV_COOKIE_FILE"); out != "" {
|
||||
if err := os.WriteFile(out, []byte(cookie), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
fmt.Printf("\nCASINO http://localhost%s/games/blackjack\nCOOKIE %s=%s\n\n", addr, sessionCookie, cookie)
|
||||
|
||||
srv := &http.Server{Handler: mux, ReadHeaderTimeout: 5 * time.Second}
|
||||
t.Cleanup(func() { _ = srv.Close() })
|
||||
_ = srv.Serve(ln)
|
||||
}
|
||||
Reference in New Issue
Block a user