Files
8bitMyGoUI/tabletop/tabletop.go
T
ZacharyZhang-NY 81003b21ee Add tabletop components 486-500 and ten game icons
Fifteen pieces of a games night: Dice, DiceTray, PlayingCard, CardHand,
Deck, Chessboard, GoBoard, MahjongTile, Domino, Roulette, CoinFlip,
BingoCard, TicTacToe, ScoreTrack and Meeple. Component number 500 is
a meeple, which seems right.

Dice tumble, coins turn and wheels spin on a cue, a frame at a time,
and land at once under Reduce Motion. Hands lift the card you point
at and play the one drawn on top; pieces and stones pick the lighter
of Lit and Ink for White, so they read in every palette; the computer
plays a fair O; FREE is a star in every language.

New 12x12 icons: Spade, Club, Diamond, Meeple, King, Queen, Rook,
Bishop, Knight and Pawn.
2026-10-12 14:03:58 -04:00

119 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package tabletop is dice, cards, boards and tokens.
package tabletop
import (
"fmt"
"strconv"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
// Suit is a playing card's suit.
type Suit int
const (
Spades Suit = iota
Hearts
Diamonds
Clubs
)
var suits = [...]struct {
msg string
sp *pixel.Sprite
}{{"spades", icon.Spade}, {"hearts", icon.Heart}, {"diamonds", icon.Diamond}, {"clubs", icon.Club}}
// Card's Rank runs from ace, 1, to king, 13.
type Card struct {
Rank int
Suit Suit
}
// check fails on a rank or suit no deck has.
func (cd Card) check(name string) {
if cd.Rank < 1 || cd.Rank > 13 || cd.Suit < Spades || cd.Suit > Clubs {
panic(fmt.Sprintf("tabletop: %s card %+v", name, cd))
}
}
// ink is a card's color: hearts and diamonds are red.
func (cd Card) ink() pixel.Slot {
if cd.Suit == Hearts || cd.Suit == Diamonds {
return pixel.Bad
}
return pixel.Ink
}
// index is the printed rank: A, 2–10, J, Q, K.
func (cd Card) index() string {
return [...]string{"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"}[cd.Rank-1]
}
// name reads a card out, as ACE OF SPADES.
func (cd Card) name(k pixel.Kit) string {
rank := strconv.Itoa(cd.Rank)
switch cd.Rank {
case 1:
rank = k.Msg("ace")
case 11, 12, 13:
rank = k.Msg([...]string{"jack", "queen", "king"}[cd.Rank-11])
}
return fmt.Sprintf(k.Msg("card_name"), rank, k.Msg(suits[cd.Suit].msg))
}
const cardW, cardH = 36, 50
// drawCard paints a card at x, y: face or back.
func drawCard(g pixel.Canvas, x, y int, cd Card, back bool) {
g.Frame(x, y, cardW, cardH, pixel.Flat, pixel.Lit)
if back {
g.Rect(x+3, y+3, cardW-6, cardH-6, pixel.Accent)
for j := 0; j < cardH-10; j += 4 {
for i := j / 4 % 2 * 2; i < cardW-10; i += 4 {
g.Rect(x+5+i, y+5+j, 2, 2, pixel.AccentLit)
}
}
return
}
ink, idx := cd.ink(), cd.index()
g.Micro(x+3, y+3, idx, ink, 1)
g.Micro(x+cardW-3-pixel.MicroWidth(idx), y+cardH-8, idx, ink, 1)
// Every card shows its suit in a corner.
g.Sprite(x+cardW-14, y+2, suits[cd.Suit].sp, 0, ink, 1)
// Court cards show their piece, the rest a big pip.
sp := suits[cd.Suit].sp
if cd.Rank > 10 {
sp = [...]*pixel.Sprite{icon.Knight, icon.Queen, icon.King}[cd.Rank-11]
}
g.Sprite(x+(cardW-24)/2, y+(cardH-24)/2+2, sp, 0, ink, 2)
}
// pips places 0–6 pips on a face of side s.
func pips(g pixel.Canvas, x, y, s, n int, ink pixel.Slot) {
if n < 0 || n > 6 {
panic(fmt.Sprintf("tabletop: %d pips", n))
}
spots := [...][][2]int{{}, {{1, 1}}, {{0, 0}, {2, 2}}, {{0, 0}, {1, 1}, {2, 2}}, {{0, 0}, {2, 0}, {0, 2}, {2, 2}},
{{0, 0}, {2, 0}, {1, 1}, {0, 2}, {2, 2}}, {{0, 0}, {2, 0}, {0, 1}, {2, 1}, {0, 2}, {2, 2}}}[n]
d := max(2, s/7)
for _, p := range spots {
g.Rect(x+s*(p[0]+1)/4-d/2, y+s*(p[1]+1)/4-d/2, d, d, ink)
}
}
// shades are Lit and Ink, lighter first: White looks white.
func shades(k pixel.Kit) (light, dark pixel.Slot) {
lum := func(s pixel.Slot) int {
c := k.C(s)
return 299*int(c.R) + 587*int(c.G) + 114*int(c.B)
}
if lum(pixel.Lit) >= lum(pixel.Ink) {
return pixel.Lit, pixel.Ink
}
return pixel.Ink, pixel.Lit
}
// players color tokens, one slot a player.
var players = []pixel.Slot{pixel.Bad, pixel.Info, pixel.Good, pixel.Warn, pixel.Magic, pixel.Ink}