clone

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

feat/elo-difficulty
puzzletea / games / takuzu / print_adapter.go
print_adapter.go 5.3K
1package takuzu23import (4 "strings"56 "codeberg.org/go-pdf/fpdf"7 "github.com/FelineStateMachine/puzzletea/export/pdf"8)910type printAdapter struct{}1112var PDFPrintAdapter = printAdapter{}1314var defaultTakuzuRules = []string{15 "No three equal adjacent in any row or column.",16 "Each row/column has equal 0 and 1 counts, and rows/columns are unique.",17}1819type takuzuRelationSizing struct {20 fontSize float6421 backdropSize float6422}2324func (printAdapter) CanonicalGameType() string { return "Takuzu" }25func (printAdapter) Aliases() []string { return []string{"takuzu"} }2627func (printAdapter) BuildPDFPayload(save []byte) (any, error) {28 return pdfexport.ParseTakuzuPrintData(save)29}3031func (printAdapter) RenderPDFBody(pdf *fpdf.Fpdf, payload any) error {32 switch data := payload.(type) {33 case *pdfexport.TakuzuData:34 RenderTakuzuPDFBody(pdf, data)35 }36 return nil37}3839func RenderTakuzuPDFBody(pdf *fpdf.Fpdf, data *pdfexport.TakuzuData) {40 RenderTakuzuPDFBodyWithRules(pdf, data, defaultTakuzuRules)41}4243func RenderTakuzuPDFBodyWithRules(pdf *fpdf.Fpdf, data *pdfexport.TakuzuData, rules []string) {44 if data == nil || data.Size <= 0 {45 return46 }47 if len(rules) == 0 {48 rules = defaultTakuzuRules49 }5051 size := data.Size52 pageW, pageH := pdf.GetPageSize()53 pageNo := pdf.PageNo()54 body := pdfexport.PuzzleBodyRect(pageW, pageH, pageNo)55 ruleLines := pdfexport.InstructionLineCount(pdf, body.W, rules)56 area := pdfexport.PuzzleBoardRect(pageW, pageH, pageNo, ruleLines)57 cellSize := pdfexport.FitCompactCellSize(size, size, area)58 if cellSize <= 0 {59 return60 }6162 blockW := float64(size) * cellSize63 blockH := float64(size) * cellSize64 startX, startY := pdfexport.CenteredOrigin(area, size, size, cellSize)6566 pdf.SetDrawColor(60, 60, 60)67 pdf.SetLineWidth(pdfexport.ThinGridLineMM)68 for y := range size {69 for x := range size {70 cellX := startX + float64(x)*cellSize71 cellY := startY + float64(y)*cellSize72 pdf.Rect(cellX, cellY, cellSize, cellSize, "D")73 }74 }7576 if data.GroupEveryTwo {77 pdf.SetDrawColor(130, 130, 130)78 pdf.SetLineWidth(pdfexport.MajorGridLineMM)79 for i := 2; i < size; i += 2 {80 x := startX + float64(i)*cellSize81 y := startY + float64(i)*cellSize82 pdf.Line(x, startY, x, startY+blockH)83 pdf.Line(startX, y, startX+blockW, y)84 }85 }8687 for y := range size {88 for x := range size {89 text := ""90 if y < len(data.Givens) && x < len(data.Givens[y]) {91 text = strings.TrimSpace(data.Givens[y][x])92 }93 if text == "" {94 continue95 }9697 cellX := startX + float64(x)*cellSize98 cellY := startY + float64(y)*cellSize99 drawTakuzuGiven(pdf, cellX, cellY, cellSize, size, text)100 }101 }102103 drawTakuzuRelations(pdf, data, startX, startY, cellSize)104105 pdf.SetDrawColor(35, 35, 35)106 pdf.SetLineWidth(pdfexport.OuterBorderLineMM)107 pdf.Rect(startX, startY, blockW, blockH, "D")108109 ruleY := pdfexport.InstructionY(startY+blockH, pageH, ruleLines)110 pdfexport.RenderInstructions(pdf, body.X, ruleY, body.W, rules)111}112113func drawTakuzuRelations(pdf *fpdf.Fpdf, data *pdfexport.TakuzuData, startX, startY, cellSize float64) {114 if data == nil {115 return116 }117118 pdf.SetTextColor(95, 95, 95)119 sizing := takuzuRelationSizingFor(cellSize, data.Size)120 pdf.SetFont(pdfexport.SansFontFamily, "B", sizing.fontSize)121 pdf.SetFillColor(255, 255, 255)122123 for y, row := range data.HorizontalRelations {124 for x, value := range row {125 if strings.TrimSpace(value) == "" {126 continue127 }128129 centerX := startX + float64(x+1)*cellSize130 centerY := startY + float64(y)*cellSize + cellSize/2131 drawTakuzuRelation(pdf, centerX, centerY, sizing, value)132 }133 }134135 for y, row := range data.VerticalRelations {136 for x, value := range row {137 if strings.TrimSpace(value) == "" {138 continue139 }140141 centerX := startX + float64(x)*cellSize + cellSize/2142 centerY := startY + float64(y+1)*cellSize143 drawTakuzuRelation(pdf, centerX, centerY, sizing, value)144 }145 }146}147148func drawTakuzuRelation(pdf *fpdf.Fpdf, centerX, centerY float64, sizing takuzuRelationSizing, value string) {149 left := centerX - sizing.backdropSize/2150 top := centerY - sizing.backdropSize/2151 lineH := sizing.fontSize * 0.9152153 pdf.Rect(left, top, sizing.backdropSize, sizing.backdropSize, "F")154 pdf.SetXY(left, centerY-lineH/2)155 pdf.CellFormat(sizing.backdropSize, lineH, value, "", 0, "C", false, 0, "")156}157158func drawTakuzuGiven(pdf *fpdf.Fpdf, x, y, cellSize float64, size int, text string) {159 fontSize := takuzuGivenFontSize(cellSize, size)160161 pdf.SetTextColor(pdfexport.PrimaryTextGray, pdfexport.PrimaryTextGray, pdfexport.PrimaryTextGray)162 pdf.SetFont(pdfexport.SansFontFamily, "B", fontSize)163 lineH := fontSize * 0.9164 pdf.SetXY(x, y+(cellSize-lineH)/2)165 pdf.CellFormat(cellSize, lineH, text, "", 0, "C", false, 0, "")166}167168func takuzuGivenFontSize(cellSize float64, size int) float64 {169 fontSize := pdfexport.StandardCellFontSize(cellSize, 0.68)170 switch {171 case size >= 14:172 fontSize *= 0.94173 case size >= 12:174 fontSize *= 0.97175 }176 return pdfexport.ClampStandardCellFontSize(fontSize)177}178179func takuzuRelationSizingFor(cellSize float64, size int) takuzuRelationSizing {180 fontSize := cellSize * 0.48181 if size >= 12 {182 fontSize *= 0.94183 }184 if size >= 14 {185 fontSize *= 0.90186 }187188 if fontSize < 6.2 {189 fontSize = 6.2190 }191 if fontSize > 8.4 {192 fontSize = 8.4193 }194195 backdropSize := fontSize + max(0.25, cellSize*0.04)196 if maxSize := cellSize * 0.58; backdropSize > maxSize {197 backdropSize = maxSize198 }199200 return takuzuRelationSizing{201 fontSize: fontSize,202 backdropSize: backdropSize,203 }204}