use an OR in a spot where it will help some tiny amount, set up doc

This commit is contained in:
jackjohn7 2025-11-05 21:29:08 -06:00
parent 4e74d926b0
commit 08e0b11db9
9 changed files with 13632 additions and 5 deletions

387
design/othello.typ Normal file
View file

@ -0,0 +1,387 @@
#let position(..rows) = {
rows.pos()
}
#let othello(pos, labels: true, cell-size: 30pt) = {
let rows = pos
// Colors
let board-color = rgb("#2d8659")
let line-color = rgb("#1a5c3a")
let white-piece = rgb("#f0f0f0")
let black-piece = rgb("#1a1a1a")
let label-color = rgb("#000000")
let flip-color = rgb("#d32f2f")
let mask-color = rgb("#000000").transparentize(60%)
// Calculate total size
let board-size = cell-size * 8
let label-offset = if labels { 15pt } else { 0pt }
let total-size = board-size + label-offset * 2
let piece-radius = cell-size * 0.4
let move-radius = cell-size * 0.25
box(
width: total-size,
height: total-size,
{
place(
dx: label-offset,
dy: label-offset,
// Draw board background
rect(
width: board-size,
height: board-size,
fill: board-color,
stroke: line-color + 2pt
)
)
// Draw grid lines
for i in range(1, 8) {
let offset = cell-size * i
// Vertical lines
place(
dx: label-offset + offset,
dy: label-offset,
line(
start: (0pt, 0pt),
end: (0pt, board-size),
stroke: line-color + 1pt
)
)
// Horizontal lines
place(
dx: label-offset,
dy: label-offset + offset,
line(
start: (0pt, 0pt),
end: (board-size, 0pt),
stroke: line-color + 1pt
)
)
}
// Draw pieces and markers
for (row-idx, row) in rows.enumerate() {
for (col-idx, cell) in row.clusters().enumerate() {
// Regular pieces
if cell == "B" or cell == "W" {
let x = label-offset + col-idx * cell-size + cell-size / 2 - piece-radius
let y = label-offset + row-idx * cell-size + cell-size / 2 - piece-radius
let piece-color = if cell == "B" { black-piece } else { white-piece }
let piece-stroke = if cell == "W" { line-color + 0.5pt } else { none }
place(
dx: x,
dy: y,
circle(
radius: piece-radius,
fill: piece-color,
stroke: piece-stroke
)
)
}
// Pieces to be flipped
else if cell == "b" or cell == "w" {
let x = label-offset + col-idx * cell-size + cell-size / 2 - piece-radius
let y = label-offset + row-idx * cell-size + cell-size / 2 - piece-radius
let piece-color = if cell == "b" { black-piece } else { white-piece }
place(
dx: x,
dy: y,
circle(
radius: piece-radius,
fill: piece-color,
stroke: flip-color + 2pt
)
)
}
// Available moves
else if cell == "0" or cell == "1" {
let x = label-offset + col-idx * cell-size + cell-size / 2 - move-radius
let y = label-offset + row-idx * cell-size + cell-size / 2 - move-radius
let move-color = if cell == "0" {
black-piece.transparentize(50%)
} else {
white-piece.transparentize(50%)
}
place(
dx: x,
dy: y,
circle(
radius: move-radius,
fill: move-color,
stroke: none
)
)
}
// Masked squares
else if cell == "X" {
let x = label-offset + col-idx * cell-size
let y = label-offset + row-idx * cell-size
place(
dx: x,
dy: y,
rect(
width: cell-size,
height: cell-size,
fill: mask-color,
stroke: none
)
)
}
}
}
// Draw labels
if labels {
let files = ("a", "b", "c", "d", "e", "f", "g", "h")
let ranks = ("8", "7", "6", "5", "4", "3", "2", "1").rev()
// File labels (bottom)
for (i, file) in files.enumerate() {
let x = label-offset + i * cell-size + cell-size / 2
place(
dx: x,
dy: board-size + label-offset + 3pt,
align(center, text(size: 10pt, fill: label-color, weight: "bold", file))
)
}
// Rank labels (left)
for (i, rank) in ranks.enumerate() {
let y = label-offset + i * cell-size + cell-size / 2
place(
dx: 3pt,
dy: y,
align(horizon, text(size: 10pt, fill: label-color, weight: "bold", rank))
)
}
}
}
)
}
#let othello-legend(cell-size: 20pt) = {
let board-color = rgb("#2d8659")
let line-color = rgb("#1a5c3a")
let white-piece = rgb("#f0f0f0")
let black-piece = rgb("#1a1a1a")
let flip-color = rgb("#d32f2f")
let mask-color = rgb("#000000").transparentize(60%)
let piece-radius = cell-size * 0.4
let move-radius = cell-size * 0.25
let legend-item(symbol, description) = {
grid(
columns: (cell-size + 10pt, 1fr),
align: (center + horizon, left + horizon),
gutter: 10pt,
// Symbol cell
box(
width: cell-size,
height: cell-size,
{
place(
rect(
width: cell-size,
height: cell-size,
fill: board-color,
stroke: line-color + 1pt
)
)
symbol
}
),
// Description
text(size: 10pt, description)
)
}
stack(
dir: ttb,
spacing: 6pt,
legend-item(
place(
dx: cell-size / 2 - piece-radius,
dy: cell-size / 2 - piece-radius,
circle(radius: piece-radius, fill: black-piece)
),
[Black disc]
),
legend-item(
place(
dx: cell-size / 2 - piece-radius,
dy: cell-size / 2 - piece-radius,
circle(radius: piece-radius, fill: white-piece, stroke: line-color + 0.5pt)
),
[White disc]
),
legend-item(
place(
dx: cell-size / 2 - piece-radius,
dy: cell-size / 2 - piece-radius,
circle(radius: piece-radius, fill: black-piece, stroke: flip-color + 2pt)
),
[Black disc to be flipped]
),
legend-item(
place(
dx: cell-size / 2 - piece-radius,
dy: cell-size / 2 - piece-radius,
circle(radius: piece-radius, fill: white-piece, stroke: flip-color + 2pt)
),
[White disc to be flipped]
),
legend-item(
place(
dx: cell-size / 2 - move-radius,
dy: cell-size / 2 - move-radius,
circle(radius: move-radius, fill: black-piece.transparentize(50%))
),
[Available move for black]
),
legend-item(
place(
dx: cell-size / 2 - move-radius,
dy: cell-size / 2 - move-radius,
circle(radius: move-radius, fill: white-piece.transparentize(50%))
),
[Available move for white]
),
legend-item(
place(
rect(width: cell-size, height: cell-size, fill: mask-color)
),
[Masked square]
),
legend-item(
[],
[Empty square]
),
)
}
#let shift-indicator(values, cell-size: 30pt) = {
assert(values.len() == 9, message: "shift-indicator requires exactly 9 values")
let board-color = rgb("#2d8659")
let line-color = rgb("#1a5c3a")
let white-piece = rgb("#f0f0f0")
let black-piece = rgb("#1a1a1a")
let grid-size = cell-size * 3
let piece-radius = cell-size * 0.4
box(
width: grid-size,
height: grid-size,
{
// Draw background
place(
rect(
width: grid-size,
height: grid-size,
fill: board-color,
stroke: line-color + 2pt
)
)
// Draw grid lines
for i in range(1, 3) {
let offset = cell-size * i
// Vertical lines
place(
dx: offset,
line(
start: (0pt, 0pt),
end: (0pt, grid-size),
stroke: line-color + 1pt
)
)
// Horizontal lines
place(
dy: offset,
line(
start: (0pt, 0pt),
end: (grid-size, 0pt),
stroke: line-color + 1pt
)
)
}
// Draw cells
for row in range(3) {
for col in range(3) {
let idx = row * 3 + col
let value = values.at(idx)
let x = col * cell-size
let y = row * cell-size
if value == 0 {
// Half-white, half-black disc
let center-x = x + cell-size / 2
let center-y = y + cell-size / 2
// Draw full black circle
place(
dx: center-x - piece-radius,
dy: center-y - piece-radius,
circle(radius: piece-radius, fill: black-piece, stroke: none)
)
// Draw white half-circle using clipping
place(
dx: center-x,
dy: center-y - piece-radius,
box(
width: piece-radius,
height: piece-radius * 2,
clip: true,
place(
dx: -piece-radius,
circle(radius: piece-radius, fill: white-piece, stroke: none)
)
)
)
// Draw circle outline
place(
dx: center-x - piece-radius,
dy: center-y - piece-radius,
circle(radius: piece-radius, fill: none, stroke: line-color + 0.5pt)
)
} else {
// Arrow and number
let arrow = if value > 0 { "«" } else { "»" }
let num = str(calc.abs(value))
place(
dx: x,
dy: y,
box(
width: cell-size,
height: cell-size,
align(
center + horizon,
text(fill: white, weight: "bold", size: 10pt, arrow + " " + num)
)
)
)
}
}
}
}
)
}