|
1
|
// Pieces |
|
2
|
|
|
3
|
#let piece-type(p) = { if p == "." { return "." }; upper(p) } |
|
4
|
#let is-white(p) = p != "." and p == upper(p) |
|
5
|
#let is-black(p) = p != "." and p == lower(p) |
|
6
|
|
|
7
|
#let piece-assets = ( |
|
8
|
"K": "wking.png", "k": "bking.png", |
|
9
|
"Q": "wqueen.png", "q": "bqueen.png", |
|
10
|
"R": "wrook.png", "r": "brook.png", |
|
11
|
"N": "wknight.png", "n": "bknight.png", |
|
12
|
"B": "wbishop.png", "b": "bbishop.png", |
|
13
|
"P": "wpawn.png", "p": "bpawn.png", |
|
14
|
) |
|
15
|
|
|
16
|
#let starting-grid = ( |
|
17
|
("r", "n", "b", "q", "k", "b", "n", "r"), |
|
18
|
("p", "p", "p", "p", "p", "p", "p", "p"), |
|
19
|
(".", ".", ".", ".", ".", ".", ".", "."), |
|
20
|
(".", ".", ".", ".", ".", ".", ".", "."), |
|
21
|
(".", ".", ".", ".", ".", ".", ".", "."), |
|
22
|
(".", ".", ".", ".", ".", ".", ".", "."), |
|
23
|
("P", "P", "P", "P", "P", "P", "P", "P"), |
|
24
|
("R", "N", "B", "Q", "K", "B", "N", "R"), |
|
25
|
) |
|
26
|
|
|
27
|
// Coordinates |
|
28
|
|
|
29
|
#let col-to-file = ("a", "b", "c", "d", "e", "f", "g", "h") |
|
30
|
#let file-to-col = ( |
|
31
|
"a": 0, "b": 1, "c": 2, "d": 3, |
|
32
|
"e": 4, "f": 5, "g": 6, "h": 7, |
|
33
|
) |
|
34
|
|
|
35
|
#let coords-to-square(col, row) = col-to-file.at(col - 1) + str(9 - row) |
|
36
|
#let square-to-coords(sq) = ((file-to-col.at(sq.at(0)) + 1), (9 - int(sq.at(1)))) |
|
37
|
|
|
38
|
// Grid |
|
39
|
|
|
40
|
#let copy-grid(g) = g.map(r => r.map(c => c)) |
|
41
|
#let on-board(col, row) = col >= 1 and col <= 8 and row >= 1 and row <= 8 |
|
42
|
#let at(grid, col, row) = { |
|
43
|
if not on-board(col, row) { return "." } |
|
44
|
grid.at(row - 1).at(col - 1) |
|
45
|
} |
|
46
|
|
|
47
|
#let find-pieces(grid, color, type) = { |
|
48
|
let pieces = () |
|
49
|
let test = if color == "white" { is-white } else { is-black } |
|
50
|
for r in range(8) { |
|
51
|
for c in range(8) { |
|
52
|
let p = grid.at(r).at(c) |
|
53
|
if p != "." and piece-type(p) == type and test(p) { |
|
54
|
pieces.push((c + 1, r + 1)) |
|
55
|
} |
|
56
|
} |
|
57
|
} |
|
58
|
pieces |
|
59
|
} |
|
60
|
|
|
61
|
#let apply-move(grid, col, row, dest-col, dest-row, promotion: none, en-passant: none) = { |
|
62
|
let g = copy-grid(grid) |
|
63
|
let piece = g.at(row - 1).at(col - 1) |
|
64
|
g.at(row - 1).at(col - 1) = "." |
|
65
|
|
|
66
|
if en-passant != none { |
|
67
|
g.at(en-passant.at(1) - 1).at(en-passant.at(0) - 1) = "." |
|
68
|
} |
|
69
|
|
|
70
|
if promotion != none { |
|
71
|
let color = if is-white(piece) { upper } else { lower } |
|
72
|
g.at(dest-row - 1).at(dest-col - 1) = color(promotion) |
|
73
|
} else { |
|
74
|
g.at(dest-row - 1).at(dest-col - 1) = piece |
|
75
|
} |
|
76
|
g |
|
77
|
} |
|
78
|
|
|
79
|
// Board |
|
80
|
|
|
81
|
#let is-dark-sq(file, rank) = calc.rem(file + rank, 2) == 1 |
|
82
|
|
|
83
|
#let render-board(board) = { |
|
84
|
let files = ("a", "b", "c", "d", "e", "f", "g", "h") |
|
85
|
let ranks = ("8", "7", "6", "5", "4", "3", "2", "1") |
|
86
|
|
|
87
|
let light-square = rgb("#f0d9b5") |
|
88
|
let dark-square = rgb("#b58863") |
|
89
|
|
|
90
|
let cell-size = 2.8em |
|
91
|
let coord-size = 0.6em |
|
92
|
|
|
93
|
align(center + horizon, grid( |
|
94
|
columns: (coord-size,) + (cell-size,) * 8, |
|
95
|
rows: (cell-size,) * 8 + (coord-size,), |
|
96
|
..{ |
|
97
|
let rows = () |
|
98
|
for rank in range(8) { |
|
99
|
rows.push(text(size: coord-size, ranks.at(rank))) |
|
100
|
for file in range(8) { |
|
101
|
let piece = board.at(rank).at(file) |
|
102
|
let sq-color = if is-dark-sq(file, rank) { dark-square } else { light-square } |
|
103
|
let display = if piece == "." { [] } else { image("assets/" + piece-assets.at(piece), width: cell-size * 0.85) } |
|
104
|
rows.push(box(width: cell-size, |
|
105
|
height: cell-size, |
|
106
|
fill: sq-color, |
|
107
|
align(center + horizon, display))) |
|
108
|
} |
|
109
|
} |
|
110
|
rows.push([]) |
|
111
|
for f in files { rows.push(text(size: coord-size, f)) } |
|
112
|
|
|
113
|
rows |
|
114
|
}, |
|
115
|
)) |
|
116
|
} |