movegen.gno
2.75 Kb · 124 lines
1package minichess
2
3func lastRank(c Color) int {
4 if c == White {
5 return Size - 1
6 }
7 return 0
8}
9
10// Attacked reports whether square sq is attacked by any piece of colour by.
11func (p Position) Attacked(sq int, by Color) bool {
12 return newEngine(&p).attacked(sq, by)
13}
14
15// InCheck reports whether the side to move is in check.
16func (p Position) InCheck() bool {
17 return newEngine(&p).inCheck()
18}
19
20// LegalMoves returns all legal moves for the side to move.
21func (p Position) LegalMoves() []Move {
22 enc := newEngine(&p).legal()
23 out := make([]Move, len(enc))
24 for i, m := range enc {
25 out[i] = decode(m)
26 }
27 return out
28}
29
30// Play applies a move without validating it and returns the new position.
31// Callers must only pass moves obtained from LegalMoves or FindLegal.
32func (p Position) Play(m Move) Position {
33 e := newEngine(&p)
34 e.make(m.encode())
35 full := p.Fullmove
36 if p.Turn == Black {
37 full++
38 }
39 return e.position(full)
40}
41
42// IsIrreversible reports whether m is a capture or pawn move (resets the
43// repetition history and the 50-move counter).
44func (p Position) IsIrreversible(m Move) bool {
45 return p.Board[m.From].Type() == Pawn || p.Board[m.To] != Empty
46}
47
48// FindLegal matches m against the legal moves. A promotion move given
49// without a piece defaults to a queen. Returns false if m is illegal.
50func (p Position) FindLegal(m Move) (Move, bool) {
51 if m.Promo == Empty {
52 pc := p.Board[m.From]
53 if pc.Type() == Pawn && pc.Color() == p.Turn && m.To/Size == lastRank(p.Turn) {
54 m.Promo = Queen
55 }
56 }
57 want := m.encode()
58 for _, lm := range newEngine(&p).legal() {
59 if lm == want {
60 return m, true
61 }
62 }
63 return m, false
64}
65
66// Status describes whether the game is still going and how it ended.
67type Status int
68
69const (
70 Ongoing Status = iota
71 Checkmate
72 Stalemate
73 FiftyMoveDraw
74 InsufficientMaterial
75)
76
77func (s Status) String() string {
78 switch s {
79 case Ongoing:
80 return "ongoing"
81 case Checkmate:
82 return "checkmate"
83 case Stalemate:
84 return "stalemate"
85 case FiftyMoveDraw:
86 return "50-move rule"
87 case InsufficientMaterial:
88 return "insufficient material"
89 }
90 return "unknown"
91}
92
93// Status computes the game status from the position alone. Threefold
94// repetition needs move history and is handled by the caller.
95func (p Position) Status() Status {
96 e := newEngine(&p)
97 if len(e.legal()) == 0 {
98 if e.inCheck() {
99 return Checkmate
100 }
101 return Stalemate
102 }
103 if p.insufficientMaterial() {
104 return InsufficientMaterial
105 }
106 if p.Halfmove >= 100 {
107 return FiftyMoveDraw
108 }
109 return Ongoing
110}
111
112// insufficientMaterial: only kings, or kings plus a single minor piece.
113func (p Position) insufficientMaterial() bool {
114 minors := 0
115 for _, pc := range p.Board {
116 switch pc.Type() {
117 case Pawn, Rook, Queen:
118 return false
119 case Knight, Bishop:
120 minors++
121 }
122 }
123 return minors <= 1
124}