Check for collisions with own pieces on target
This commit is contained in:
		
							parent
							
								
									4e12b40478
								
							
						
					
					
						commit
						d3966685db
					
				@ -269,19 +269,22 @@ func (b *Board) Parse(pgn string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Board) Move(position string) error {
 | 
			
		||||
func (b *Board) Move(move string) error {
 | 
			
		||||
	var (
 | 
			
		||||
		err error
 | 
			
		||||
		piece          string
 | 
			
		||||
		targetPosition string
 | 
			
		||||
		// the column from which the piece is captured.
 | 
			
		||||
		// for example, this would be 'e' for exd4 and 'e' for Nexd4.
 | 
			
		||||
		captureFrom string
 | 
			
		||||
		captureFrom    string
 | 
			
		||||
		collisionPiece *Piece
 | 
			
		||||
		err            error
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if strings.Contains(position, "x") {
 | 
			
		||||
	if strings.Contains(move, "x") {
 | 
			
		||||
		// capture move
 | 
			
		||||
		parts := strings.Split(position, "x")
 | 
			
		||||
		parts := strings.Split(move, "x")
 | 
			
		||||
		if len(parts) != 2 {
 | 
			
		||||
			return fmt.Errorf("invalid move: %s", position)
 | 
			
		||||
			return fmt.Errorf("invalid move: %s", move)
 | 
			
		||||
		}
 | 
			
		||||
		if len(parts[0]) == 2 {
 | 
			
		||||
			// example: Nexd4
 | 
			
		||||
@ -295,10 +298,10 @@ func (b *Board) Move(position string) error {
 | 
			
		||||
				captureFrom = ""
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			return fmt.Errorf("invalid move: %s", position)
 | 
			
		||||
			return fmt.Errorf("invalid move: %s", move)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		position = strings.Replace(position, captureFrom+"x", "", 1)
 | 
			
		||||
		move = strings.Replace(move, captureFrom+"x", "", 1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: parse captures e.g. exd5 or Nxd5
 | 
			
		||||
@ -310,25 +313,36 @@ func (b *Board) Move(position string) error {
 | 
			
		||||
	// 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 )
 | 
			
		||||
 | 
			
		||||
	if len(position) == 2 {
 | 
			
		||||
		err = b.movePawn(position, captureFrom)
 | 
			
		||||
	} else if len(position) == 3 {
 | 
			
		||||
		switch strings.ToLower(position[0:1]) {
 | 
			
		||||
	if len(move) == 2 {
 | 
			
		||||
		err = b.movePawn(move, captureFrom)
 | 
			
		||||
	} else if len(move) == 3 {
 | 
			
		||||
 | 
			
		||||
		piece = move[0:1]
 | 
			
		||||
		targetPosition = move[1:3]
 | 
			
		||||
 | 
			
		||||
		// collision detection
 | 
			
		||||
		if collisionPiece, err = b.getCollision(targetPosition); err != nil {
 | 
			
		||||
			return fmt.Errorf("invalid move %s: %v", move, err)
 | 
			
		||||
		} else if collisionPiece != nil {
 | 
			
		||||
			return fmt.Errorf("invalid move %s: position %s blocked by %s", move, targetPosition, collisionPiece)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch strings.ToLower(piece) {
 | 
			
		||||
		case "r":
 | 
			
		||||
			err = b.moveRook(position[1:3], false)
 | 
			
		||||
			err = b.moveRook(targetPosition, false)
 | 
			
		||||
		case "b":
 | 
			
		||||
			err = b.moveBishop(position[1:3], false)
 | 
			
		||||
			err = b.moveBishop(targetPosition, false)
 | 
			
		||||
		case "n":
 | 
			
		||||
			err = b.moveKnight(position[1:3])
 | 
			
		||||
			err = b.moveKnight(targetPosition)
 | 
			
		||||
		case "q":
 | 
			
		||||
			err = b.moveQueen(position[1:3])
 | 
			
		||||
			err = b.moveQueen(targetPosition)
 | 
			
		||||
		case "k":
 | 
			
		||||
			err = b.moveKing(position[1:3])
 | 
			
		||||
			err = b.moveKing(targetPosition)
 | 
			
		||||
		default:
 | 
			
		||||
			err = fmt.Errorf("invalid move: %s", position)
 | 
			
		||||
			err = fmt.Errorf("invalid move %s: %v", move, err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		return fmt.Errorf("invalid move: %s", position)
 | 
			
		||||
		return fmt.Errorf("invalid move %s: %v", move, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@ -341,7 +355,7 @@ func (b *Board) Move(position string) error {
 | 
			
		||||
		b.turn = Light
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.moves = append(b.moves, position)
 | 
			
		||||
	b.moves = append(b.moves, move)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@ -752,6 +766,30 @@ func (b *Board) getPiece(x int, y int) *Piece {
 | 
			
		||||
	return b.tiles[x][y]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Board) getCollision(position string) (*Piece, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		x, y      int
 | 
			
		||||
		p         *Piece
 | 
			
		||||
		collision bool
 | 
			
		||||
		err       error
 | 
			
		||||
	)
 | 
			
		||||
	if x, y, err = getXY(position); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p = b.getPiece(x, y)
 | 
			
		||||
 | 
			
		||||
	// check if position is occupied by own piece
 | 
			
		||||
	collision = p != nil && p.Color == b.turn
 | 
			
		||||
	if collision {
 | 
			
		||||
		return p, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: check if path is blocked by some piece
 | 
			
		||||
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getTileColor(x, y int) Color {
 | 
			
		||||
	if x%2 == y%2 {
 | 
			
		||||
		return Light
 | 
			
		||||
 | 
			
		||||
@ -120,6 +120,26 @@ func TestBoardMoveKnight(t *testing.T) {
 | 
			
		||||
	assertNoPiece(t, b, "g8")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBoardMoveKnightInvalid(t *testing.T) {
 | 
			
		||||
	b := chess.NewBoard()
 | 
			
		||||
 | 
			
		||||
	// out of reach
 | 
			
		||||
	assertMoveError(t, b, "Ng3", "no knight found that can move to g3")
 | 
			
		||||
	assertMoveError(t, b, "Nb3", "no knight found that can move to b3")
 | 
			
		||||
 | 
			
		||||
	// blocked by own piece
 | 
			
		||||
	assertMoveError(t, b, "Nd2", "d2 blocked by white pawn")
 | 
			
		||||
	assertMoveError(t, b, "Ne2", "e2 blocked by white pawn")
 | 
			
		||||
 | 
			
		||||
	b.Move("Nf3")
 | 
			
		||||
 | 
			
		||||
	assertMoveError(t, b, "Ng6", "no knight found that can move to g6")
 | 
			
		||||
	assertMoveError(t, b, "Nb6", "no knight found that can move to b6")
 | 
			
		||||
 | 
			
		||||
	assertMoveError(t, b, "Ne7", "e7 blocked by black pawn")
 | 
			
		||||
	assertMoveError(t, b, "Nd7", "d7 blocked by black pawn")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBoardMoveBishop(t *testing.T) {
 | 
			
		||||
	b := chess.NewBoard()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,34 @@ type Piece struct {
 | 
			
		||||
	Image image.Image
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Piece) String() string {
 | 
			
		||||
	n := ""
 | 
			
		||||
	switch p.Name {
 | 
			
		||||
	case Pawn:
 | 
			
		||||
		n = "pawn"
 | 
			
		||||
	case Knight:
 | 
			
		||||
		n = "knight"
 | 
			
		||||
	case Bishop:
 | 
			
		||||
		n = "bishop"
 | 
			
		||||
	case Rook:
 | 
			
		||||
		n = "rook"
 | 
			
		||||
	case Queen:
 | 
			
		||||
		n = "queen"
 | 
			
		||||
	case King:
 | 
			
		||||
		n = "king"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := ""
 | 
			
		||||
	switch p.Color {
 | 
			
		||||
	case Light:
 | 
			
		||||
		c = "white"
 | 
			
		||||
	case Dark:
 | 
			
		||||
		c = "black"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%s %s", c, n)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type PieceName string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user