all repos

tyess @ 3f3048b6eabb39e39333e63cf4d67ba48587a099

chess in typst

tyess/moves.typ (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
what's creamy, 27 days ago
1
#import "board.typ": at, is-black, is-white, on-board, piece-type
2
3
#let enemy(turn, target) = if turn == "white" { is-black(target) } else { is-white(target) }
4
5
#let pawn-dir(turn) = if turn == "white" { -1 } else { 1 }
6
#let pawn-moves(grid, col, row, turn, ep: none) = { // returns list of (dest-col, dest-row, prom?), ep == en passant
7
  let moves = ()
8
  let start-row = if turn == "white" { 7 } else { 2 }
9
  let dir = pawn-dir(turn)
10
11
  // forward one
12
  let nr = row + dir
13
  if on-board(col, nr) and at(grid, col, nr) == "." {
14
    if nr == 1 or nr == 8 {
15
      for prom in ("Q", "R", "B", "N") { moves.push((col, nr, prom)) }
16
    } else { moves.push((col, nr)) }
17
18
    // forward two from start
19
    let nr2 = row + 2 * dir
20
    if row == start-row and at(grid, col, nr2) == "." { moves.push((col, nr2)) }
21
  }
22
23
  // captures
24
  for dc in (-1, 1) {
25
    let nc = col + dc
26
    let nr = row + dir
27
    if on-board(nc, nr) {
28
      if enemy(turn, at(grid, nc, nr)) {
29
        if nr == 1 or nr == 8 {
30
          for prom in ("Q", "R", "B", "N") { moves.push((nc, nr, prom)) }
31
        } else {
32
          moves.push((nc, nr))
33
        }
34
      }
35
36
      // en passant capture: pawn ends on ep square, captured pawn is one rank behind
37
      if ep != none and nc == ep.at(0) {
38
        let captured-row = ep.at(1) - dir
39
        if enemy(turn, at(grid, nc, captured-row)) { moves.push((ep.at(0), ep.at(1))) }
40
      }
41
    }
42
  }
43
44
  moves
45
}
46
47
#let knight-offsets = ((-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1))
48
#let knight-moves(grid, col, row, turn) = {
49
  let moves = ()
50
  for (dc, dr) in knight-offsets {
51
    let nc = col + dc
52
    let nr = row + dr
53
    if on-board(nc, nr) {
54
      let target = at(grid, nc, nr)
55
      if target == "." or enemy(turn, target) {
56
        moves.push((nc, nr))
57
      }
58
    }
59
  }
60
  moves
61
}
62
63
#let sliding-moves(grid, col, row, turn, dirs) = {
64
  let moves = ()
65
  for (dc, dr) in dirs {
66
    let nc = col + dc
67
    let nr = row + dr
68
    while on-board(nc, nr) {
69
      let target = at(grid, nc, nr)
70
      if target == "." {
71
        moves.push((nc, nr))
72
      } else if enemy(turn, target) {
73
        moves.push((nc, nr))
74
        break
75
      } else { break }
76
      nc = nc + dc
77
      nr = nr + dr
78
    }
79
  }
80
  moves
81
}
82
83
#let bishop-dirs = ((-1, -1), (-1, 1), (1, -1), (1, 1))
84
#let bishop-moves(grid, col, row, turn) = sliding-moves(grid, col, row, turn, bishop-dirs)
85
86
#let rook-dirs = ((-1, 0), (1, 0), (0, -1), (0, 1))
87
#let rook-moves(grid, col, row, turn) = sliding-moves(grid, col, row, turn, rook-dirs)
88
89
#let queen-moves(grid, col, row, turn) = {
90
  let moves = ()
91
  for m in bishop-moves(grid, col, row, turn) { moves.push(m) }
92
  for m in rook-moves(grid, col, row, turn) { moves.push(m) }
93
  moves
94
}
95
96
#let king-dirs = ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))
97
#let king-moves(grid, col, row, turn) = {
98
  let moves = ()
99
  for (dc, dr) in king-dirs {
100
    let nc = col + dc
101
    let nr = row + dr
102
    if on-board(nc, nr) {
103
      if at(grid, nc, nr) == "." or enemy(turn, at(grid, nc, nr)) {
104
        moves.push((nc, nr))
105
      }
106
    }
107
  }
108
  moves
109
}
110
111
#let generate-moves-for(grid, col, row, turn, ep: none) = {
112
  let p = at(grid, col, row)
113
  if p == "." { return () }
114
  if turn == "white" and not is-white(p) { return () }
115
  if turn == "black" and not is-black(p) { return () }
116
117
  let type = piece-type(p)
118
  if type == "P" { return pawn-moves(grid, col, row, turn, ep: ep) }
119
  if type == "N" { return knight-moves(grid, col, row, turn) }
120
  if type == "B" { return bishop-moves(grid, col, row, turn) }
121
  if type == "R" { return rook-moves(grid, col, row, turn) }
122
  if type == "Q" { return queen-moves(grid, col, row, turn) }
123
  if type == "K" { return king-moves(grid, col, row, turn) }
124
  ()
125
}