clone

fifteen puzzle games for the terminal, with daily puzzles, weekly ladders and xp, built on bubble tea

bug/architecture-test-trim-path
puzzletea / games / rippleeffect / print_adapter.go
print_adapter.go 3.5K
1package rippleeffect23import (4 "strconv"56 "codeberg.org/go-pdf/fpdf"7 "github.com/FelineStateMachine/puzzletea/export/pdf"8)910type printAdapter struct{}1112var PDFPrintAdapter = printAdapter{}1314func (printAdapter) CanonicalGameType() string { return "Ripple Effect" }15func (printAdapter) Aliases() []string { return []string{"ripple effect", "rippleeffect", "ripple"} }1617func (printAdapter) BuildPDFPayload(save []byte) (any, error) {18 return pdfexport.ParseRippleEffectPrintData(save)19}2021func (printAdapter) RenderPDFBody(pdf *fpdf.Fpdf, payload any) error {22 switch data := payload.(type) {23 case *pdfexport.RippleEffectData:24 renderRippleEffectPage(pdf, data)25 }26 return nil27}2829func renderRippleEffectPage(pdf *fpdf.Fpdf, data *pdfexport.RippleEffectData) {30 if data == nil || data.Width <= 0 || data.Height <= 0 {31 return32 }3334 pageW, pageH := pdf.GetPageSize()35 pageNo := pdf.PageNo()36 body := pdfexport.PuzzleBodyRect(pageW, pageH, pageNo)37 rules := []string{"Each cage uses 1..n once; equal digits must be at least their value apart in rows and columns."}38 ruleLines := pdfexport.InstructionLineCount(pdf, body.W, rules)39 area := pdfexport.PuzzleBoardRect(pageW, pageH, pageNo, ruleLines)40 cellSize := pdfexport.FitCompactCellSize(data.Width, data.Height, area)41 if cellSize <= 0 {42 return43 }4445 blockW := float64(data.Width) * cellSize46 blockH := float64(data.Height) * cellSize47 startX, startY := pdfexport.CenteredOrigin(area, data.Width, data.Height, cellSize)4849 cageGrid := make([][]int, data.Height)50 for y := 0; y < data.Height; y++ {51 cageGrid[y] = make([]int, data.Width)52 for x := 0; x < data.Width; x++ {53 cageGrid[y][x] = -154 }55 }56 for cageIdx, cage := range data.Cages {57 for _, cell := range cage.Cells {58 if cell.X >= 0 && cell.X < data.Width && cell.Y >= 0 && cell.Y < data.Height {59 cageGrid[cell.Y][cell.X] = cageIdx60 }61 }62 }63 masks := buildBoundaryMasks(cageGrid)6465 pdf.SetDrawColor(150, 150, 150)66 pdf.SetLineWidth(pdfexport.ThinGridLineMM)67 for y := 0; y <= data.Height; y++ {68 lineY := startY + float64(y)*cellSize69 pdf.Line(startX, lineY, startX+blockW, lineY)70 }71 for x := 0; x <= data.Width; x++ {72 lineX := startX + float64(x)*cellSize73 pdf.Line(lineX, startY, lineX, startY+blockH)74 }7576 pdf.SetDrawColor(35, 35, 35)77 pdf.SetLineWidth(pdfexport.MajorGridLineMM)78 for y := 0; y < data.Height; y++ {79 for x := 0; x < data.Width; x++ {80 cellX := startX + float64(x)*cellSize81 cellY := startY + float64(y)*cellSize82 mask := masks[y][x]83 if mask.has(boundaryTop) {84 pdf.Line(cellX, cellY, cellX+cellSize, cellY)85 }86 if mask.has(boundaryLeft) {87 pdf.Line(cellX, cellY, cellX, cellY+cellSize)88 }89 if mask.has(boundaryRight) {90 pdf.Line(cellX+cellSize, cellY, cellX+cellSize, cellY+cellSize)91 }92 if mask.has(boundaryBottom) {93 pdf.Line(cellX, cellY+cellSize, cellX+cellSize, cellY+cellSize)94 }9596 if data.Givens[y][x] == 0 {97 continue98 }99 drawRippleEffectGiven(pdf, cellX, cellY, cellSize, strconv.Itoa(data.Givens[y][x]))100 }101 }102103 ruleY := pdfexport.InstructionY(startY+blockH, pageH, ruleLines)104 pdfexport.RenderInstructions(pdf, body.X, ruleY, body.W, rules)105}106107func drawRippleEffectGiven(pdf *fpdf.Fpdf, x, y, cellSize float64, text string) {108 fontSize := pdfexport.ClampStandardCellFontSize(pdfexport.StandardCellFontSize(cellSize, 0.58))109 pdf.SetTextColor(pdfexport.PrimaryTextGray, pdfexport.PrimaryTextGray, pdfexport.PrimaryTextGray)110 pdf.SetFont(pdfexport.SansFontFamily, "B", fontSize)111 lineH := fontSize * 0.9112 pdf.SetXY(x, y+(cellSize-lineH)/2)113 pdf.CellFormat(cellSize, lineH, text, "", 0, "C", false, 0, "")114}