Add checks
This commit is contained in:
parent
aa072ca4aa
commit
908cc1cfda
261
chess/board.go
261
chess/board.go
|
@ -17,7 +17,7 @@ import (
|
|||
type Board struct {
|
||||
tiles [8][8]*Piece
|
||||
turn Color
|
||||
moves []string
|
||||
Moves []string
|
||||
}
|
||||
|
||||
func NewBoard() *Board {
|
||||
|
@ -240,7 +240,7 @@ func (b *Board) SetPiece(name PieceName, color Color, position string) error {
|
|||
|
||||
func (b *Board) AlgebraicNotation() string {
|
||||
var text string
|
||||
for i, m := range b.moves {
|
||||
for i, m := range b.Moves {
|
||||
if i%2 == 0 {
|
||||
text += fmt.Sprintf("%d. %s", i/2+1, m)
|
||||
} else {
|
||||
|
@ -292,12 +292,9 @@ func (b *Board) Move(move string) error {
|
|||
}
|
||||
|
||||
// TODO: parse ambiguous captures for all pieces
|
||||
// TODO: parse checks e.g. e5+
|
||||
// TODO: parse checkmates e.g. e5#
|
||||
// TODO: parse O-O as kingside castle and O-O-O as queenside castle
|
||||
// TODO: make sure pinned pieces cannot move
|
||||
// TODO: make sure king is not in check after move
|
||||
// ( this avoids moving into check and moving a piece that exposes the king to check e.g. pinned pieces )
|
||||
|
||||
move_ := func() error {
|
||||
|
||||
|
@ -330,13 +327,23 @@ func (b *Board) Move(move string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// is current player in check after move?
|
||||
if b.InCheck() {
|
||||
return fmt.Errorf("invalid move %s: king is in check", move)
|
||||
}
|
||||
|
||||
if b.turn == Light {
|
||||
b.turn = Dark
|
||||
} else {
|
||||
b.turn = Light
|
||||
}
|
||||
|
||||
b.moves = append(b.moves, move)
|
||||
// make sure the move is marked as a check if it was
|
||||
if b.InCheck() && !strings.HasSuffix(move, "+") {
|
||||
move += "+"
|
||||
}
|
||||
|
||||
b.Moves = append(b.Moves, move)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -485,6 +492,248 @@ func (b *Board) validateMove(search PieceName, fromX int, fromY int) func(*Piece
|
|||
}
|
||||
}
|
||||
|
||||
func (b *Board) InCheck() bool {
|
||||
var (
|
||||
kingX, kingY int
|
||||
p *Piece
|
||||
x, y int
|
||||
)
|
||||
|
||||
outerLoop:
|
||||
// find king
|
||||
for y = 0; y < 8; y++ {
|
||||
for x = 0; x < 8; x++ {
|
||||
p := b.getPiece(x, y)
|
||||
if p != nil && p.Name == King && p.Color == b.turn {
|
||||
kingX = x
|
||||
kingY = y
|
||||
break outerLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if any piece can attack king
|
||||
|
||||
// ^
|
||||
x = kingX
|
||||
y = kingY
|
||||
for y = kingY - 1; y >= 0; y-- {
|
||||
p = b.getPiece(x, y)
|
||||
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if p.Color != b.turn && (p.Name == Rook || p.Name == Queen) {
|
||||
return true
|
||||
}
|
||||
|
||||
// some other piece is blocking the way
|
||||
break
|
||||
}
|
||||
|
||||
// ^>
|
||||
x = kingX
|
||||
y = kingY
|
||||
for x, y = kingX+1, kingY-1; x < 8 && y >= 0; x, y = x+1, y-1 {
|
||||
p = b.getPiece(x, y)
|
||||
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if p.Color != b.turn && (p.Name == Bishop || p.Name == Queen) {
|
||||
return true
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// >
|
||||
x = kingX
|
||||
y = kingY
|
||||
for x = kingX + 1; x < 8; x++ {
|
||||
p = b.getPiece(x, y)
|
||||
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if p.Color != b.turn && (p.Name == Rook || p.Name == Queen) {
|
||||
return true
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// v>
|
||||
x = kingX
|
||||
y = kingY
|
||||
for x, y = kingX+1, kingY+1; x < 8 && y < 8; x, y = x+1, y+1 {
|
||||
p = b.getPiece(x, y)
|
||||
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if p.Color != b.turn && (p.Name == Bishop || p.Name == Queen) {
|
||||
return true
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// v
|
||||
x = kingX
|
||||
y = kingY
|
||||
for y = kingY + 1; y < 8; y++ {
|
||||
p = b.getPiece(x, y)
|
||||
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if p.Color != b.turn && (p.Name == Rook || p.Name == Queen) {
|
||||
return true
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// <v
|
||||
x = kingX
|
||||
y = kingY
|
||||
for x, y = kingX-1, kingY+1; x >= 0 && y < 8; x, y = x-1, y+1 {
|
||||
p = b.getPiece(x, y)
|
||||
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if p.Color != b.turn && (p.Name == Bishop || p.Name == Queen) {
|
||||
return true
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// <
|
||||
x = kingX
|
||||
y = kingY
|
||||
for x = kingX - 1; x >= 0; x-- {
|
||||
p = b.getPiece(x, y)
|
||||
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if p.Color != b.turn && (p.Name == Rook || p.Name == Queen) {
|
||||
return true
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// <^
|
||||
x = kingX
|
||||
y = kingY
|
||||
for x, y = kingX-1, kingY-1; x >= 0 && y >= 0; x, y = x-1, y-1 {
|
||||
p = b.getPiece(x, y)
|
||||
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if p.Color != b.turn && (p.Name == Bishop || p.Name == Queen) {
|
||||
return true
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// check for knights
|
||||
x = kingX + 1
|
||||
y = kingY - 2
|
||||
p = b.getPiece(x, y)
|
||||
if p != nil && p.Color != b.turn && p.Name == Knight {
|
||||
return true
|
||||
}
|
||||
|
||||
x = kingX + 2
|
||||
y = kingY - 1
|
||||
p = b.getPiece(x, y)
|
||||
if p != nil && p.Color != b.turn && p.Name == Knight {
|
||||
return true
|
||||
}
|
||||
|
||||
x = kingX + 2
|
||||
y = kingY + 1
|
||||
p = b.getPiece(x, y)
|
||||
if p != nil && p.Color != b.turn && p.Name == Knight {
|
||||
return true
|
||||
}
|
||||
|
||||
x = kingX + 1
|
||||
y = kingY + 2
|
||||
p = b.getPiece(x, y)
|
||||
if p != nil && p.Color != b.turn && p.Name == Knight {
|
||||
return true
|
||||
}
|
||||
|
||||
x = kingX - 1
|
||||
y = kingY + 2
|
||||
p = b.getPiece(x, y)
|
||||
if p != nil && p.Color != b.turn && p.Name == Knight {
|
||||
return true
|
||||
}
|
||||
|
||||
x = kingX - 2
|
||||
y = kingY + 1
|
||||
p = b.getPiece(x, y)
|
||||
if p != nil && p.Color != b.turn && p.Name == Knight {
|
||||
return true
|
||||
}
|
||||
|
||||
x = kingX - 2
|
||||
y = kingY - 1
|
||||
p = b.getPiece(x, y)
|
||||
if p != nil && p.Color != b.turn && p.Name == Knight {
|
||||
return true
|
||||
}
|
||||
|
||||
x = kingX - 1
|
||||
y = kingY - 2
|
||||
p = b.getPiece(x, y)
|
||||
if p != nil && p.Color != b.turn && p.Name == Knight {
|
||||
return true
|
||||
}
|
||||
|
||||
// check for pawns
|
||||
x = kingX - 1
|
||||
if b.turn == Light {
|
||||
y = kingY - 1
|
||||
} else {
|
||||
y = kingY + 1
|
||||
}
|
||||
p = b.getPiece(x, y)
|
||||
if p != nil && p.Color != b.turn && p.Name == Pawn {
|
||||
return true
|
||||
}
|
||||
|
||||
x = kingX + 1
|
||||
if b.turn == Light {
|
||||
y = kingY - 1
|
||||
} else {
|
||||
y = kingY + 1
|
||||
}
|
||||
p = b.getPiece(x, y)
|
||||
if p != nil && p.Color != b.turn && p.Name == Pawn {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Board) movePawn(position string, fromX int, fromY int, promotion string) error {
|
||||
var (
|
||||
toX int
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ekzyis/chessbot/chess"
|
||||
|
@ -453,6 +454,24 @@ func TestBoardMoveKingInvalid(t *testing.T) {
|
|||
assertMoveError(t, b, "Ke3", "no king found that can move to e3")
|
||||
}
|
||||
|
||||
func TestBoardCheck(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
b := chess.NewBoard()
|
||||
|
||||
assert.False(t, b.InCheck())
|
||||
|
||||
assertParse(t, b, "e4 e5 Qh5 Nc6 Qxf7")
|
||||
|
||||
assert.True(t, b.InCheck())
|
||||
assert.True(t, strings.HasSuffix(b.Moves[len(b.Moves)-1], "+"), "check move should end with +")
|
||||
|
||||
assertMoveError(t, b, "Nf6", "invalid move Nf6: king is in check")
|
||||
assertMoveError(t, b, "Ke7", "invalid move Ke7: king is in check")
|
||||
|
||||
assertParse(t, b, "Kxf7")
|
||||
}
|
||||
|
||||
func assertParse(t *testing.T, b *chess.Board, moves string) {
|
||||
assert.NoError(t, b.Parse(moves))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue