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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user